Merge from rust-lang/rust
This commit is contained in:
commit
88223c56d9
3427 changed files with 107813 additions and 37739 deletions
20
.github/workflows/ci.yml
vendored
20
.github/workflows/ci.yml
vendored
|
|
@ -34,7 +34,7 @@ concurrency:
|
|||
# For a given workflow, if we push to the same branch, cancel all previous builds on that branch.
|
||||
# We add an exception for try builds (try branch) and unrolled rollup builds (try-perf), which
|
||||
# are all triggered on the same branch, but which should be able to run concurrently.
|
||||
group: ${{ github.workflow }}-${{ ((github.ref == 'refs/heads/try' || github.ref == 'refs/heads/try-perf') && github.sha) || github.ref }}
|
||||
group: ${{ github.workflow }}-${{ ((github.ref == 'refs/heads/try' || github.ref == 'refs/heads/try-perf' || github.ref == 'refs/heads/automation/bors/try') && github.sha) || github.ref }}
|
||||
cancel-in-progress: true
|
||||
env:
|
||||
TOOLSTATE_REPO: "https://github.com/rust-lang-nursery/rust-toolstate"
|
||||
|
|
@ -46,7 +46,7 @@ jobs:
|
|||
# If you want to modify CI jobs, take a look at src/ci/github-actions/jobs.yml.
|
||||
calculate_matrix:
|
||||
name: Calculate job matrix
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-24.04-arm
|
||||
outputs:
|
||||
jobs: ${{ steps.jobs.outputs.jobs }}
|
||||
run_type: ${{ steps.jobs.outputs.run_type }}
|
||||
|
|
@ -73,6 +73,14 @@ jobs:
|
|||
needs: [ calculate_matrix ]
|
||||
runs-on: "${{ matrix.os }}"
|
||||
timeout-minutes: 360
|
||||
# The bors environment contains secrets required for elevated workflows (try and auto builds),
|
||||
# which need to access e.g. S3 and upload artifacts. We want to provide access to that
|
||||
# environment only on the try/auto branches, which are only accessible to bors.
|
||||
# This also ensures that PR CI (which doesn't get write access to S3) works, as it cannot
|
||||
# access the environment.
|
||||
#
|
||||
# We only enable the environment for the rust-lang/rust repository, so that CI works on forks.
|
||||
environment: ${{ ((github.repository == 'rust-lang/rust' && (github.ref == 'refs/heads/try' || github.ref == 'refs/heads/try-perf' || github.ref == 'refs/heads/automation/bors/try' || github.ref == 'refs/heads/auto')) && 'bors') || '' }}
|
||||
env:
|
||||
CI_JOB_NAME: ${{ matrix.name }}
|
||||
CI_JOB_DOC_URL: ${{ matrix.doc_url }}
|
||||
|
|
@ -225,8 +233,8 @@ jobs:
|
|||
fi
|
||||
exit ${STATUS}
|
||||
env:
|
||||
AWS_ACCESS_KEY_ID: ${{ env.CACHES_AWS_ACCESS_KEY_ID }}
|
||||
AWS_SECRET_ACCESS_KEY: ${{ secrets[format('AWS_SECRET_ACCESS_KEY_{0}', env.CACHES_AWS_ACCESS_KEY_ID)] }}
|
||||
AWS_ACCESS_KEY_ID: ${{ secrets.CACHES_AWS_ACCESS_KEY_ID }}
|
||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.CACHES_AWS_SECRET_ACCESS_KEY }}
|
||||
|
||||
- name: create github artifacts
|
||||
run: src/ci/scripts/create-doc-artifacts.sh
|
||||
|
|
@ -248,8 +256,8 @@ jobs:
|
|||
- name: upload artifacts to S3
|
||||
run: src/ci/scripts/upload-artifacts.sh
|
||||
env:
|
||||
AWS_ACCESS_KEY_ID: ${{ env.ARTIFACTS_AWS_ACCESS_KEY_ID }}
|
||||
AWS_SECRET_ACCESS_KEY: ${{ secrets[format('AWS_SECRET_ACCESS_KEY_{0}', env.ARTIFACTS_AWS_ACCESS_KEY_ID)] }}
|
||||
AWS_ACCESS_KEY_ID: ${{ secrets.ARTIFACTS_AWS_ACCESS_KEY_ID }}
|
||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.ARTIFACTS_AWS_SECRET_ACCESS_KEY }}
|
||||
# Adding a condition on DEPLOY=1 or DEPLOY_ALT=1 is not needed as all deploy
|
||||
# builders *should* have the AWS credentials available. Still, explicitly
|
||||
# adding the condition is helpful as this way CI will not silently skip
|
||||
|
|
|
|||
2
.mailmap
2
.mailmap
|
|
@ -653,7 +653,7 @@ Torsten Weber <TorstenWeber12@gmail.com> <torstenweber12@gmail.com>
|
|||
Trevor Gross <tmgross@umich.edu> <t.gross35@gmail.com>
|
||||
Trevor Gross <tmgross@umich.edu> <tgross@intrepidcs.com>
|
||||
Trevor Spiteri <tspiteri@ieee.org> <trevor.spiteri@um.edu.mt>
|
||||
Tshepang Mbambo <tshepang@gmail.com>
|
||||
Tshepang Mbambo <hopsi@tuta.io> <tshepang@gmail.com>
|
||||
Ty Overby <ty@pre-alpha.com>
|
||||
Tyler Mandry <tmandry@gmail.com> <tmandry@google.com>
|
||||
Tyler Ruckinger <t.ruckinger@gmail.com>
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ and we appreciate all of them.
|
|||
|
||||
The best way to get started is by asking for help in the [#new
|
||||
members](https://rust-lang.zulipchat.com/#narrow/stream/122652-new-members)
|
||||
Zulip stream. We have lots of docs below of how to get started on your own, but
|
||||
Zulip stream. We have a lot of documentation below on how to get started on your own, but
|
||||
the Zulip stream is the best place to *ask* for help.
|
||||
|
||||
Documentation for contributing to the compiler or tooling is located in the [Guide to Rustc
|
||||
|
|
@ -14,7 +14,7 @@ standard library in the [Standard library developers Guide][std-dev-guide], comm
|
|||
|
||||
## Making changes to subtrees and submodules
|
||||
|
||||
For submodules, changes need to be made against the repository corresponding the
|
||||
For submodules, changes need to be made against the repository corresponding to the
|
||||
submodule, and not the main `rust-lang/rust` repository.
|
||||
|
||||
For subtrees, prefer sending a PR against the subtree's repository if it does
|
||||
|
|
@ -25,7 +25,7 @@ rustc-dev-guide change that does not accompany a compiler change).
|
|||
|
||||
The [rustc-dev-guide] is meant to help document how rustc –the Rust compiler– works,
|
||||
as well as to help new contributors get involved in rustc development. It is recommended
|
||||
to read and understand the [rustc-dev-guide] before making a contribution. This guide
|
||||
that you read and understand the [rustc-dev-guide] before making a contribution. This guide
|
||||
talks about the different bots in the Rust ecosystem, the Rust development tools,
|
||||
bootstrapping, the compiler architecture, source code representation, and more.
|
||||
|
||||
|
|
@ -33,7 +33,7 @@ bootstrapping, the compiler architecture, source code representation, and more.
|
|||
|
||||
There are many ways you can get help when you're stuck. Rust has many platforms for this:
|
||||
[internals], [rust-zulip], and [rust-discord]. It is recommended to ask for help on
|
||||
the [rust-zulip], but any of these platforms are a great way to seek help and even
|
||||
the [rust-zulip], but any of these platforms are great ways to seek help and even
|
||||
find a mentor! You can learn more about asking questions and getting help in the
|
||||
[Asking Questions](https://rustc-dev-guide.rust-lang.org/getting-started.html#asking-questions) chapter of the [rustc-dev-guide].
|
||||
|
||||
|
|
|
|||
113
Cargo.lock
113
Cargo.lock
|
|
@ -162,7 +162,7 @@ version = "0.4.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "01667f6f40216b9a0b2945e05fed5f1ad0ab6470e69cb9378001e37b1c0668e4"
|
||||
dependencies = [
|
||||
"object",
|
||||
"object 0.36.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -179,9 +179,9 @@ checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
|
|||
|
||||
[[package]]
|
||||
name = "askama"
|
||||
version = "0.13.1"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d4744ed2eef2645831b441d8f5459689ade2ab27c854488fbab1fbe94fce1a7"
|
||||
checksum = "f75363874b771be265f4ffe307ca705ef6f3baa19011c149da8674a87f1b75c4"
|
||||
dependencies = [
|
||||
"askama_derive",
|
||||
"itoa",
|
||||
|
|
@ -192,9 +192,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "askama_derive"
|
||||
version = "0.13.1"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d661e0f57be36a5c14c48f78d09011e67e0cb618f269cca9f2fd8d15b68c46ac"
|
||||
checksum = "129397200fe83088e8a68407a8e2b1f826cf0086b21ccdb866a722c8bcd3a94f"
|
||||
dependencies = [
|
||||
"askama_parser",
|
||||
"basic-toml",
|
||||
|
|
@ -209,9 +209,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "askama_parser"
|
||||
version = "0.13.0"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf315ce6524c857bb129ff794935cf6d42c82a6cff60526fe2a63593de4d0d4f"
|
||||
checksum = "d6ab5630b3d5eaf232620167977f95eb51f3432fc76852328774afbd242d4358"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"serde",
|
||||
|
|
@ -235,7 +235,7 @@ dependencies = [
|
|||
"cfg-if",
|
||||
"libc",
|
||||
"miniz_oxide",
|
||||
"object",
|
||||
"object 0.36.7",
|
||||
"rustc-demangle",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
|
@ -582,13 +582,11 @@ dependencies = [
|
|||
name = "clippy_dev"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"chrono",
|
||||
"clap",
|
||||
"indoc",
|
||||
"itertools",
|
||||
"opener",
|
||||
"shell-escape",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
|
|
@ -1219,16 +1217,16 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fluent-bundle"
|
||||
version = "0.15.3"
|
||||
version = "0.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7fe0a21ee80050c678013f82edf4b705fe2f26f1f9877593d13198612503f493"
|
||||
checksum = "01203cb8918f5711e73891b347816d932046f95f54207710bda99beaeb423bf4"
|
||||
dependencies = [
|
||||
"fluent-langneg",
|
||||
"fluent-syntax",
|
||||
"intl-memoizer",
|
||||
"intl_pluralrules",
|
||||
"rustc-hash 1.1.0",
|
||||
"self_cell 0.10.3",
|
||||
"rustc-hash 2.1.1",
|
||||
"self_cell",
|
||||
"smallvec",
|
||||
"unic-langid",
|
||||
]
|
||||
|
|
@ -1244,11 +1242,12 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fluent-syntax"
|
||||
version = "0.11.1"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a530c4694a6a8d528794ee9bbd8ba0122e779629ac908d15ad5a7ae7763a33d"
|
||||
checksum = "54f0d287c53ffd184d04d8677f590f4ac5379785529e5e08b1c8083acdd5c198"
|
||||
dependencies = [
|
||||
"thiserror 1.0.69",
|
||||
"memchr",
|
||||
"thiserror 2.0.12",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1894,9 +1893,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "intl-memoizer"
|
||||
version = "0.5.2"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fe22e020fce238ae18a6d5d8c502ee76a52a6e880d99477657e6acc30ec57bda"
|
||||
checksum = "310da2e345f5eb861e7a07ee182262e94975051db9e4223e909ba90f392f163f"
|
||||
dependencies = [
|
||||
"type-map",
|
||||
"unic-langid",
|
||||
|
|
@ -2130,6 +2129,7 @@ dependencies = [
|
|||
name = "lint-docs"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"rustc-literal-escaper",
|
||||
"serde_json",
|
||||
"tempfile",
|
||||
"walkdir",
|
||||
|
|
@ -2509,7 +2509,19 @@ dependencies = [
|
|||
"indexmap",
|
||||
"memchr",
|
||||
"ruzstd",
|
||||
"wasmparser 0.222.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.37.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6273adb7096cf9ab4335f258e627d8230e69d40d45567d678f552dcec6245215"
|
||||
dependencies = [
|
||||
"crc32fast",
|
||||
"hashbrown",
|
||||
"indexmap",
|
||||
"memchr",
|
||||
"wasmparser 0.232.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -3109,7 +3121,7 @@ dependencies = [
|
|||
"build_helper",
|
||||
"gimli",
|
||||
"libc",
|
||||
"object",
|
||||
"object 0.36.7",
|
||||
"regex",
|
||||
"serde_json",
|
||||
"similar",
|
||||
|
|
@ -3118,9 +3130,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rustc-build-sysroot"
|
||||
version = "0.5.5"
|
||||
version = "0.5.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fb332121f7845c6bd016f9655cf22f03c2999df936694b624a88669a78667d98"
|
||||
checksum = "10edc2e4393515193bd766e2f6c050b0536a68e56f2b6d56c07ababfdc114ff0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"rustc_version",
|
||||
|
|
@ -3404,6 +3416,7 @@ dependencies = [
|
|||
"rustc_macros",
|
||||
"rustc_parse",
|
||||
"rustc_parse_format",
|
||||
"rustc_proc_macro",
|
||||
"rustc_session",
|
||||
"rustc_span",
|
||||
"rustc_target",
|
||||
|
|
@ -3421,7 +3434,7 @@ dependencies = [
|
|||
"itertools",
|
||||
"libc",
|
||||
"measureme",
|
||||
"object",
|
||||
"object 0.37.0",
|
||||
"rustc-demangle",
|
||||
"rustc_abi",
|
||||
"rustc_ast",
|
||||
|
|
@ -3462,7 +3475,7 @@ dependencies = [
|
|||
"either",
|
||||
"itertools",
|
||||
"libc",
|
||||
"object",
|
||||
"object 0.37.0",
|
||||
"pathdiff",
|
||||
"regex",
|
||||
"rustc_abi",
|
||||
|
|
@ -3639,6 +3652,7 @@ dependencies = [
|
|||
"rustc_macros",
|
||||
"rustc_serialize",
|
||||
"rustc_span",
|
||||
"smallvec",
|
||||
"tracing",
|
||||
"unic-langid",
|
||||
]
|
||||
|
|
@ -3693,6 +3707,7 @@ dependencies = [
|
|||
"rustc_lint_defs",
|
||||
"rustc_macros",
|
||||
"rustc_parse",
|
||||
"rustc_proc_macro",
|
||||
"rustc_serialize",
|
||||
"rustc_session",
|
||||
"rustc_span",
|
||||
|
|
@ -3784,6 +3799,7 @@ dependencies = [
|
|||
"rustc_middle",
|
||||
"rustc_session",
|
||||
"rustc_span",
|
||||
"rustc_target",
|
||||
"rustc_trait_selection",
|
||||
"smallvec",
|
||||
"tracing",
|
||||
|
|
@ -3809,6 +3825,7 @@ dependencies = [
|
|||
"rustc_abi",
|
||||
"rustc_ast",
|
||||
"rustc_attr_data_structures",
|
||||
"rustc_attr_parsing",
|
||||
"rustc_data_structures",
|
||||
"rustc_errors",
|
||||
"rustc_fluent_macro",
|
||||
|
|
@ -4040,6 +4057,7 @@ dependencies = [
|
|||
"rustc_index",
|
||||
"rustc_macros",
|
||||
"rustc_middle",
|
||||
"rustc_proc_macro",
|
||||
"rustc_serialize",
|
||||
"rustc_session",
|
||||
"rustc_span",
|
||||
|
|
@ -4296,6 +4314,13 @@ dependencies = [
|
|||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc_proc_macro"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"rustc-literal-escaper",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc_query_impl"
|
||||
version = "0.0.0"
|
||||
|
|
@ -4484,7 +4509,7 @@ name = "rustc_target"
|
|||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"object",
|
||||
"object 0.37.0",
|
||||
"rustc_abi",
|
||||
"rustc_data_structures",
|
||||
"rustc_fs_util",
|
||||
|
|
@ -4662,6 +4687,7 @@ dependencies = [
|
|||
"bincode",
|
||||
"rustc-hash 2.1.1",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
|
|
@ -4791,15 +4817,6 @@ version = "1.2.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "self_cell"
|
||||
version = "0.10.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e14e4d63b804dc0c7ec4a1e52bcb63f02c7ac94476755aa579edac21e01f915d"
|
||||
dependencies = [
|
||||
"self_cell 1.2.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "self_cell"
|
||||
version = "1.2.0"
|
||||
|
|
@ -4887,12 +4904,6 @@ dependencies = [
|
|||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shell-escape"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "45bb67a18fa91266cc7807181f62f9178a6873bfad7dc788c42e6430db40184f"
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.3.0"
|
||||
|
|
@ -5250,7 +5261,7 @@ checksum = "9e9c1e705f82a260173f3eec93f2ff6d7807f23ad5a8cc2e7316a891733ea7a1"
|
|||
dependencies = [
|
||||
"gimli",
|
||||
"hashbrown",
|
||||
"object",
|
||||
"object 0.36.7",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
|
|
@ -5911,15 +5922,6 @@ dependencies = [
|
|||
"indexmap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasmparser"
|
||||
version = "0.222.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa210fd1788e6b37a1d1930f3389c48e1d6ebd1a013d34fa4b7f9e3e3bf03146"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasmparser"
|
||||
version = "0.229.0"
|
||||
|
|
@ -5944,6 +5946,15 @@ dependencies = [
|
|||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasmparser"
|
||||
version = "0.232.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "917739b33bb1eb0e9a49bcd2637a351931be4578d0cc4d37b908d7a797784fbb"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wast"
|
||||
version = "230.0.0"
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ members = [
|
|||
# tidy-alphabetical-start
|
||||
"compiler/rustc",
|
||||
"src/build_helper",
|
||||
"src/etc/test-float-parse",
|
||||
"src/rustc-std-workspace/rustc-std-workspace-alloc",
|
||||
"src/rustc-std-workspace/rustc-std-workspace-core",
|
||||
"src/rustc-std-workspace/rustc-std-workspace-std",
|
||||
|
|
@ -41,6 +40,7 @@ members = [
|
|||
"src/tools/rustdoc-themes",
|
||||
"src/tools/rustfmt",
|
||||
"src/tools/suggest-tests",
|
||||
"src/tools/test-float-parse",
|
||||
"src/tools/tidy",
|
||||
"src/tools/tier-check",
|
||||
"src/tools/unicode-table-generator",
|
||||
|
|
|
|||
129
compiler/rustc_abi/src/canon_abi.rs
Normal file
129
compiler/rustc_abi/src/canon_abi.rs
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
use std::fmt;
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
use rustc_macros::HashStable_Generic;
|
||||
|
||||
use crate::ExternAbi;
|
||||
|
||||
/// Calling convention to determine codegen
|
||||
///
|
||||
/// CanonAbi erases certain distinctions ExternAbi preserves, but remains target-dependent.
|
||||
/// There are still both target-specific variants and aliasing variants, though much fewer.
|
||||
/// The reason for this step is the frontend may wish to show an ExternAbi but implement that ABI
|
||||
/// using a different ABI than the string per se, or describe irrelevant differences, e.g.
|
||||
/// - extern "system"
|
||||
/// - extern "cdecl"
|
||||
/// - extern "C-unwind"
|
||||
/// In that sense, this erases mere syntactic distinctions to create a canonical *directive*,
|
||||
/// rather than picking the "actual" ABI.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[derive(PartialOrd, Ord, PartialEq, Eq, Hash)]
|
||||
#[cfg_attr(feature = "nightly", derive(HashStable_Generic))]
|
||||
pub enum CanonAbi {
|
||||
// NOTE: the use of nested variants for some ABIs is for many targets they don't matter,
|
||||
// and this pushes the complexity of their reasoning to target-specific code,
|
||||
// allowing a `match` to easily exhaustively ignore these subcategories of variants.
|
||||
// Otherwise it is very tempting to avoid matching exhaustively!
|
||||
C,
|
||||
Rust,
|
||||
RustCold,
|
||||
|
||||
/// ABIs relevant to 32-bit Arm targets
|
||||
Arm(ArmCall),
|
||||
/// ABI relevant to GPUs: the entry point for a GPU kernel
|
||||
GpuKernel,
|
||||
|
||||
/// ABIs relevant to bare-metal interrupt targets
|
||||
// FIXME(workingjubilee): a particular reason for this nesting is we might not need these?
|
||||
// interrupt ABIs should have the same properties:
|
||||
// - uncallable by Rust calls, as LLVM rejects it in most cases
|
||||
// - uses a preserve-all-registers *callee* convention
|
||||
// - should always return `-> !` (effectively... it can't use normal `ret`)
|
||||
// what differs between targets is
|
||||
// - allowed arguments: x86 differs slightly, having 2-3 arguments which are handled magically
|
||||
// - may need special prologues/epilogues for some interrupts, without affecting "call ABI"
|
||||
Interrupt(InterruptKind),
|
||||
|
||||
/// ABIs relevant to Windows or x86 targets
|
||||
X86(X86Call),
|
||||
}
|
||||
|
||||
impl fmt::Display for CanonAbi {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
// convert to the ExternAbi that *shares a string* with this CanonAbi.
|
||||
// FIXME: ideally we'd avoid printing `CanonAbi`, and preserve `ExternAbi` everywhere
|
||||
// that we need to generate error messages.
|
||||
let erased_abi = match self {
|
||||
CanonAbi::C => ExternAbi::C { unwind: false },
|
||||
CanonAbi::Rust => ExternAbi::Rust,
|
||||
CanonAbi::RustCold => ExternAbi::RustCold,
|
||||
CanonAbi::Arm(arm_call) => match arm_call {
|
||||
ArmCall::Aapcs => ExternAbi::Aapcs { unwind: false },
|
||||
ArmCall::CCmseNonSecureCall => ExternAbi::CCmseNonSecureCall,
|
||||
ArmCall::CCmseNonSecureEntry => ExternAbi::CCmseNonSecureEntry,
|
||||
},
|
||||
CanonAbi::GpuKernel => ExternAbi::GpuKernel,
|
||||
CanonAbi::Interrupt(interrupt_kind) => match interrupt_kind {
|
||||
InterruptKind::Avr => ExternAbi::AvrInterrupt,
|
||||
InterruptKind::AvrNonBlocking => ExternAbi::AvrNonBlockingInterrupt,
|
||||
InterruptKind::Msp430 => ExternAbi::Msp430Interrupt,
|
||||
InterruptKind::RiscvMachine => ExternAbi::RiscvInterruptM,
|
||||
InterruptKind::RiscvSupervisor => ExternAbi::RiscvInterruptS,
|
||||
InterruptKind::X86 => ExternAbi::X86Interrupt,
|
||||
},
|
||||
CanonAbi::X86(x86_call) => match x86_call {
|
||||
X86Call::Fastcall => ExternAbi::Fastcall { unwind: false },
|
||||
X86Call::Stdcall => ExternAbi::Stdcall { unwind: false },
|
||||
X86Call::SysV64 => ExternAbi::SysV64 { unwind: false },
|
||||
X86Call::Thiscall => ExternAbi::Thiscall { unwind: false },
|
||||
X86Call::Vectorcall => ExternAbi::Vectorcall { unwind: false },
|
||||
X86Call::Win64 => ExternAbi::Win64 { unwind: false },
|
||||
},
|
||||
};
|
||||
erased_abi.as_str().fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
/// Callee codegen for interrupts
|
||||
///
|
||||
/// This is named differently from the "Call" enums because it is different:
|
||||
/// these "ABI" differences are not relevant to callers, since there is "no caller".
|
||||
/// These only affect callee codegen. making their categorization as distinct ABIs a bit peculiar.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[derive(PartialOrd, Ord, PartialEq, Eq, Hash)]
|
||||
#[cfg_attr(feature = "nightly", derive(HashStable_Generic))]
|
||||
pub enum InterruptKind {
|
||||
Avr,
|
||||
AvrNonBlocking,
|
||||
Msp430,
|
||||
RiscvMachine,
|
||||
RiscvSupervisor,
|
||||
X86,
|
||||
}
|
||||
|
||||
/// ABIs defined for x86-{32,64}
|
||||
///
|
||||
/// One of SysV64 or Win64 may alias the C ABI, and arguably Win64 is cross-platform now?
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[derive(PartialOrd, Ord, PartialEq, Eq, Hash)]
|
||||
#[cfg_attr(feature = "nightly", derive(HashStable_Generic))]
|
||||
pub enum X86Call {
|
||||
/// "fastcall" has both GNU and Windows variants
|
||||
Fastcall,
|
||||
/// "stdcall" has both GNU and Windows variants
|
||||
Stdcall,
|
||||
SysV64,
|
||||
Thiscall,
|
||||
Vectorcall,
|
||||
Win64,
|
||||
}
|
||||
|
||||
/// ABIs defined for 32-bit Arm
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[derive(PartialOrd, Ord, PartialEq, Eq, Hash)]
|
||||
#[cfg_attr(feature = "nightly", derive(HashStable_Generic))]
|
||||
pub enum ArmCall {
|
||||
Aapcs,
|
||||
CCmseNonSecureCall,
|
||||
CCmseNonSecureEntry,
|
||||
}
|
||||
|
|
@ -7,69 +7,98 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher, StableOrd};
|
|||
#[cfg(feature = "nightly")]
|
||||
use rustc_macros::{Decodable, Encodable};
|
||||
|
||||
use crate::AbiFromStrErr;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
use ExternAbi as Abi;
|
||||
|
||||
/// ABI we expect to see within `extern "{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,
|
||||
/* universal */
|
||||
/// presumed C ABI for the platform
|
||||
C {
|
||||
unwind: bool,
|
||||
},
|
||||
/// ABI of the "system" interface, e.g. the Win32 API, always "aliasing"
|
||||
System {
|
||||
unwind: bool,
|
||||
},
|
||||
|
||||
/// that's us!
|
||||
Rust,
|
||||
/// the mostly-unused `unboxed_closures` ABI, effectively now an impl detail unless someone
|
||||
/// puts in the work to make it viable again... but would we need a special ABI?
|
||||
RustCall,
|
||||
/// 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,
|
||||
|
||||
/// Unstable impl detail that directly uses Rust types to describe the ABI to LLVM.
|
||||
/// Even normally-compatible Rust types can become ABI-incompatible with this ABI!
|
||||
Unadjusted,
|
||||
|
||||
/// UEFI ABI, usually an alias of C, but sometimes an arch-specific alias
|
||||
/// and only valid on platforms that have a UEFI standard
|
||||
EfiApi,
|
||||
|
||||
/* arm */
|
||||
/// Arm Architecture Procedure Call Standard, sometimes `ExternAbi::C` is an alias for this
|
||||
Aapcs {
|
||||
unwind: bool,
|
||||
},
|
||||
/// extremely constrained barely-C ABI for TrustZone
|
||||
CCmseNonSecureCall,
|
||||
/// extremely constrained barely-C ABI for TrustZone
|
||||
CCmseNonSecureEntry,
|
||||
|
||||
/* gpu */
|
||||
/// 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,
|
||||
/// An entry-point function called by the GPU's host
|
||||
// FIXME: why do we have two of these?
|
||||
PtxKernel,
|
||||
|
||||
/* interrupt */
|
||||
AvrInterrupt,
|
||||
AvrNonBlockingInterrupt,
|
||||
Msp430Interrupt,
|
||||
RiscvInterruptM,
|
||||
RiscvInterruptS,
|
||||
X86Interrupt,
|
||||
|
||||
/* x86 */
|
||||
/// `ExternAbi::C` but spelled funny because x86
|
||||
Cdecl {
|
||||
unwind: bool,
|
||||
},
|
||||
/// gnu-stdcall on "unix" and win-stdcall on "windows"
|
||||
Stdcall {
|
||||
unwind: bool,
|
||||
},
|
||||
/// gnu-fastcall on "unix" and win-fastcall on "windows"
|
||||
Fastcall {
|
||||
unwind: bool,
|
||||
},
|
||||
Vectorcall {
|
||||
unwind: bool,
|
||||
},
|
||||
/// windows C++ ABI
|
||||
Thiscall {
|
||||
unwind: bool,
|
||||
},
|
||||
Aapcs {
|
||||
/// uses AVX and stuff
|
||||
Vectorcall {
|
||||
unwind: bool,
|
||||
},
|
||||
|
||||
/* x86_64 */
|
||||
SysV64 {
|
||||
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,
|
||||
},
|
||||
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 {
|
||||
|
|
@ -99,11 +128,6 @@ macro_rules! abi_impls {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum AbiFromStrErr {
|
||||
Unknown,
|
||||
}
|
||||
|
||||
abi_impls! {
|
||||
ExternAbi = {
|
||||
C { unwind: false } =><= "C",
|
||||
|
|
@ -227,7 +251,7 @@ pub fn all_names() -> Vec<&'static str> {
|
|||
|
||||
impl ExternAbi {
|
||||
/// Default ABI chosen for `extern fn` declarations without an explicit ABI.
|
||||
pub const FALLBACK: Abi = Abi::C { unwind: false };
|
||||
pub const FALLBACK: ExternAbi = ExternAbi::C { unwind: false };
|
||||
|
||||
pub fn name(self) -> &'static str {
|
||||
self.as_str()
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ use rustc_index::bit_set::BitMatrix;
|
|||
use tracing::debug;
|
||||
|
||||
use crate::{
|
||||
AbiAndPrefAlign, Align, BackendRepr, FieldsShape, HasDataLayout, IndexSlice, IndexVec, Integer,
|
||||
AbiAlign, Align, BackendRepr, FieldsShape, HasDataLayout, IndexSlice, IndexVec, Integer,
|
||||
LayoutData, Niche, NonZeroUsize, Primitive, ReprOptions, Scalar, Size, StructKind, TagEncoding,
|
||||
Variants, WrappingRange,
|
||||
};
|
||||
|
|
@ -173,13 +173,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
|
|||
// 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_aligned_factor(size),
|
||||
pref: dl.llvmlike_vector_align(size).pref,
|
||||
},
|
||||
)
|
||||
(BackendRepr::Memory { sized: true }, AbiAlign { abi: Align::max_aligned_factor(size) })
|
||||
} else {
|
||||
(BackendRepr::SimdVector { element: e_repr, count }, dl.llvmlike_vector_align(size))
|
||||
};
|
||||
|
|
@ -435,13 +429,13 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
|
|||
}
|
||||
|
||||
if let Some(pack) = repr.pack {
|
||||
align = align.min(AbiAndPrefAlign::new(pack));
|
||||
align = align.min(AbiAlign::new(pack));
|
||||
}
|
||||
// The unadjusted ABI alignment does not include repr(align), but does include repr(pack).
|
||||
// See documentation on `LayoutS::unadjusted_abi_align`.
|
||||
let unadjusted_abi_align = align.abi;
|
||||
if let Some(repr_align) = repr.align {
|
||||
align = align.max(AbiAndPrefAlign::new(repr_align));
|
||||
align = align.max(AbiAlign::new(repr_align));
|
||||
}
|
||||
// `align` must not be modified after this, or `unadjusted_abi_align` could be inaccurate.
|
||||
let align = align;
|
||||
|
|
@ -758,7 +752,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
|
|||
niche_variants,
|
||||
niche_start,
|
||||
},
|
||||
tag_field: 0,
|
||||
tag_field: FieldIdx::new(0),
|
||||
variants: IndexVec::new(),
|
||||
},
|
||||
fields: FieldsShape::Arbitrary {
|
||||
|
|
@ -1072,7 +1066,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
|
|||
variants: Variants::Multiple {
|
||||
tag,
|
||||
tag_encoding: TagEncoding::Direct,
|
||||
tag_field: 0,
|
||||
tag_field: FieldIdx::new(0),
|
||||
variants: IndexVec::new(),
|
||||
},
|
||||
fields: FieldsShape::Arbitrary {
|
||||
|
|
@ -1289,7 +1283,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
|
|||
if let StructKind::Prefixed(prefix_size, prefix_align) = kind {
|
||||
let prefix_align =
|
||||
if let Some(pack) = pack { prefix_align.min(pack) } else { prefix_align };
|
||||
align = align.max(AbiAndPrefAlign::new(prefix_align));
|
||||
align = align.max(AbiAlign::new(prefix_align));
|
||||
offset = prefix_size.align_to(prefix_align);
|
||||
}
|
||||
for &i in &inverse_memory_index {
|
||||
|
|
@ -1308,7 +1302,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
|
|||
|
||||
// Invariant: offset < dl.obj_size_bound() <= 1<<61
|
||||
let field_align = if let Some(pack) = pack {
|
||||
field.align.min(AbiAndPrefAlign::new(pack))
|
||||
field.align.min(AbiAlign::new(pack))
|
||||
} else {
|
||||
field.align
|
||||
};
|
||||
|
|
@ -1342,7 +1336,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
|
|||
// See documentation on `LayoutS::unadjusted_abi_align`.
|
||||
let unadjusted_abi_align = align.abi;
|
||||
if let Some(repr_align) = repr.align {
|
||||
align = align.max(AbiAndPrefAlign::new(repr_align));
|
||||
align = align.max(AbiAlign::new(repr_align));
|
||||
}
|
||||
// `align` must not be modified after this point, or `unadjusted_abi_align` could be inaccurate.
|
||||
let align = align;
|
||||
|
|
|
|||
|
|
@ -158,7 +158,7 @@ pub(super) fn layout<
|
|||
// Build a prefix layout, including "promoting" all ineligible
|
||||
// locals as part of the prefix. We compute the layout of all of
|
||||
// these fields at once to get optimal packing.
|
||||
let tag_index = prefix_layouts.len();
|
||||
let tag_index = prefix_layouts.next_index();
|
||||
|
||||
// `variant_fields` already accounts for the reserved variants, so no need to add them.
|
||||
let max_discr = (variant_fields.len() - 1) as u128;
|
||||
|
|
@ -187,7 +187,7 @@ pub(super) fn layout<
|
|||
|
||||
// "a" (`0..b_start`) and "b" (`b_start..`) correspond to
|
||||
// "outer" and "promoted" fields respectively.
|
||||
let b_start = FieldIdx::new(tag_index + 1);
|
||||
let b_start = tag_index.plus(1);
|
||||
let offsets_b = IndexVec::from_raw(offsets.raw.split_off(b_start.index()));
|
||||
let offsets_a = offsets;
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use rustc_data_structures::intern::Interned;
|
|||
use rustc_macros::HashStable_Generic;
|
||||
|
||||
use crate::{
|
||||
AbiAndPrefAlign, Align, BackendRepr, FieldsShape, Float, HasDataLayout, LayoutData, Niche,
|
||||
AbiAlign, Align, BackendRepr, FieldsShape, Float, HasDataLayout, LayoutData, Niche,
|
||||
PointeeInfo, Primitive, Scalar, Size, TargetDataLayout, Variants,
|
||||
};
|
||||
|
||||
|
|
@ -39,6 +39,13 @@ rustc_index::newtype_index! {
|
|||
pub struct FieldIdx {}
|
||||
}
|
||||
|
||||
impl FieldIdx {
|
||||
/// The second field, at index 1.
|
||||
///
|
||||
/// For use alongside [`FieldIdx::ZERO`], particularly with scalar pairs.
|
||||
pub const ONE: FieldIdx = FieldIdx::from_u32(1);
|
||||
}
|
||||
|
||||
rustc_index::newtype_index! {
|
||||
/// The *source-order* index of a variant in a type.
|
||||
///
|
||||
|
|
@ -93,7 +100,7 @@ impl<'a> Layout<'a> {
|
|||
self.0.0.largest_niche
|
||||
}
|
||||
|
||||
pub fn align(self) -> AbiAndPrefAlign {
|
||||
pub fn align(self) -> AbiAlign {
|
||||
self.0.0.align
|
||||
}
|
||||
|
||||
|
|
@ -274,7 +281,7 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
|
|||
|
||||
/// Finds the one field that is not a 1-ZST.
|
||||
/// Returns `None` if there are multiple non-1-ZST fields or only 1-ZST-fields.
|
||||
pub fn non_1zst_field<C>(&self, cx: &C) -> Option<(usize, Self)>
|
||||
pub fn non_1zst_field<C>(&self, cx: &C) -> Option<(FieldIdx, Self)>
|
||||
where
|
||||
Ty: TyAbiInterface<'a, C> + Copy,
|
||||
{
|
||||
|
|
@ -288,7 +295,7 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
|
|||
// More than one non-1-ZST field.
|
||||
return None;
|
||||
}
|
||||
found = Some((field_idx, field));
|
||||
found = Some((FieldIdx::from_usize(field_idx), field));
|
||||
}
|
||||
found
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ use std::fmt;
|
|||
#[cfg(feature = "nightly")]
|
||||
use std::iter::Step;
|
||||
use std::num::{NonZeroUsize, ParseIntError};
|
||||
use std::ops::{Add, AddAssign, Mul, RangeInclusive, Sub};
|
||||
use std::ops::{Add, AddAssign, Deref, Mul, RangeInclusive, Sub};
|
||||
use std::str::FromStr;
|
||||
|
||||
use bitflags::bitflags;
|
||||
|
|
@ -55,13 +55,14 @@ use rustc_index::{Idx, IndexSlice, IndexVec};
|
|||
use rustc_macros::{Decodable_NoContext, Encodable_NoContext, HashStable_Generic};
|
||||
|
||||
mod callconv;
|
||||
mod canon_abi;
|
||||
mod extern_abi;
|
||||
mod layout;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
mod extern_abi;
|
||||
|
||||
pub use callconv::{Heterogeneous, HomogeneousAggregate, Reg, RegKind};
|
||||
pub use canon_abi::{ArmCall, CanonAbi, InterruptKind, X86Call};
|
||||
pub use extern_abi::{ExternAbi, all_names};
|
||||
#[cfg(feature = "nightly")]
|
||||
pub use layout::{FIRST_VARIANT, FieldIdx, Layout, TyAbiInterface, TyAndLayout, VariantIdx};
|
||||
|
|
@ -225,22 +226,22 @@ pub const MAX_SIMD_LANES: u64 = 1 << 0xF;
|
|||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct TargetDataLayout {
|
||||
pub endian: Endian,
|
||||
pub i1_align: AbiAndPrefAlign,
|
||||
pub i8_align: AbiAndPrefAlign,
|
||||
pub i16_align: AbiAndPrefAlign,
|
||||
pub i32_align: AbiAndPrefAlign,
|
||||
pub i64_align: AbiAndPrefAlign,
|
||||
pub i128_align: AbiAndPrefAlign,
|
||||
pub f16_align: AbiAndPrefAlign,
|
||||
pub f32_align: AbiAndPrefAlign,
|
||||
pub f64_align: AbiAndPrefAlign,
|
||||
pub f128_align: AbiAndPrefAlign,
|
||||
pub i1_align: AbiAlign,
|
||||
pub i8_align: AbiAlign,
|
||||
pub i16_align: AbiAlign,
|
||||
pub i32_align: AbiAlign,
|
||||
pub i64_align: AbiAlign,
|
||||
pub i128_align: AbiAlign,
|
||||
pub f16_align: AbiAlign,
|
||||
pub f32_align: AbiAlign,
|
||||
pub f64_align: AbiAlign,
|
||||
pub f128_align: AbiAlign,
|
||||
pub pointer_size: Size,
|
||||
pub pointer_align: AbiAndPrefAlign,
|
||||
pub aggregate_align: AbiAndPrefAlign,
|
||||
pub pointer_align: AbiAlign,
|
||||
pub aggregate_align: AbiAlign,
|
||||
|
||||
/// Alignments for vector types.
|
||||
pub vector_align: Vec<(Size, AbiAndPrefAlign)>,
|
||||
pub vector_align: Vec<(Size, AbiAlign)>,
|
||||
|
||||
pub instruction_address_space: AddressSpace,
|
||||
|
||||
|
|
@ -256,22 +257,22 @@ impl Default for TargetDataLayout {
|
|||
let align = |bits| Align::from_bits(bits).unwrap();
|
||||
TargetDataLayout {
|
||||
endian: Endian::Big,
|
||||
i1_align: AbiAndPrefAlign::new(align(8)),
|
||||
i8_align: AbiAndPrefAlign::new(align(8)),
|
||||
i16_align: AbiAndPrefAlign::new(align(16)),
|
||||
i32_align: AbiAndPrefAlign::new(align(32)),
|
||||
i64_align: AbiAndPrefAlign { abi: align(32), pref: align(64) },
|
||||
i128_align: AbiAndPrefAlign { abi: align(32), pref: align(64) },
|
||||
f16_align: AbiAndPrefAlign::new(align(16)),
|
||||
f32_align: AbiAndPrefAlign::new(align(32)),
|
||||
f64_align: AbiAndPrefAlign::new(align(64)),
|
||||
f128_align: AbiAndPrefAlign::new(align(128)),
|
||||
i1_align: AbiAlign::new(align(8)),
|
||||
i8_align: AbiAlign::new(align(8)),
|
||||
i16_align: AbiAlign::new(align(16)),
|
||||
i32_align: AbiAlign::new(align(32)),
|
||||
i64_align: AbiAlign::new(align(32)),
|
||||
i128_align: AbiAlign::new(align(32)),
|
||||
f16_align: AbiAlign::new(align(16)),
|
||||
f32_align: AbiAlign::new(align(32)),
|
||||
f64_align: AbiAlign::new(align(64)),
|
||||
f128_align: AbiAlign::new(align(128)),
|
||||
pointer_size: Size::from_bits(64),
|
||||
pointer_align: AbiAndPrefAlign::new(align(64)),
|
||||
aggregate_align: AbiAndPrefAlign { abi: align(0), pref: align(64) },
|
||||
pointer_align: AbiAlign::new(align(64)),
|
||||
aggregate_align: AbiAlign { abi: align(8) },
|
||||
vector_align: vec![
|
||||
(Size::from_bits(64), AbiAndPrefAlign::new(align(64))),
|
||||
(Size::from_bits(128), AbiAndPrefAlign::new(align(128))),
|
||||
(Size::from_bits(64), AbiAlign::new(align(64))),
|
||||
(Size::from_bits(128), AbiAlign::new(align(128))),
|
||||
],
|
||||
instruction_address_space: AddressSpace::DATA,
|
||||
c_enum_min_size: Integer::I32,
|
||||
|
|
@ -329,8 +330,7 @@ impl TargetDataLayout {
|
|||
.map_err(|err| TargetDataLayoutErrors::InvalidAlignment { cause, err })
|
||||
};
|
||||
let abi = parse_bits(s[0], "alignment", cause)?;
|
||||
let pref = s.get(1).map_or(Ok(abi), |pref| parse_bits(pref, "alignment", cause))?;
|
||||
Ok(AbiAndPrefAlign { abi: align_from_bits(abi)?, pref: align_from_bits(pref)? })
|
||||
Ok(AbiAlign::new(align_from_bits(abi)?))
|
||||
};
|
||||
|
||||
let mut dl = TargetDataLayout::default();
|
||||
|
|
@ -425,7 +425,7 @@ impl TargetDataLayout {
|
|||
|
||||
/// psABI-mandated alignment for a vector type, if any
|
||||
#[inline]
|
||||
fn cabi_vector_align(&self, vec_size: Size) -> Option<AbiAndPrefAlign> {
|
||||
fn cabi_vector_align(&self, vec_size: Size) -> Option<AbiAlign> {
|
||||
self.vector_align
|
||||
.iter()
|
||||
.find(|(size, _align)| *size == vec_size)
|
||||
|
|
@ -434,8 +434,8 @@ impl TargetDataLayout {
|
|||
|
||||
/// an alignment resembling the one LLVM would pick for a vector
|
||||
#[inline]
|
||||
pub fn llvmlike_vector_align(&self, vec_size: Size) -> AbiAndPrefAlign {
|
||||
self.cabi_vector_align(vec_size).unwrap_or(AbiAndPrefAlign::new(
|
||||
pub fn llvmlike_vector_align(&self, vec_size: Size) -> AbiAlign {
|
||||
self.cabi_vector_align(vec_size).unwrap_or(AbiAlign::new(
|
||||
Align::from_bytes(vec_size.bytes().next_power_of_two()).unwrap(),
|
||||
))
|
||||
}
|
||||
|
|
@ -863,25 +863,32 @@ impl Align {
|
|||
/// It is of effectively no consequence for layout in structs and on the stack.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
#[cfg_attr(feature = "nightly", derive(HashStable_Generic))]
|
||||
pub struct AbiAndPrefAlign {
|
||||
pub struct AbiAlign {
|
||||
pub abi: Align,
|
||||
pub pref: Align,
|
||||
}
|
||||
|
||||
impl AbiAndPrefAlign {
|
||||
impl AbiAlign {
|
||||
#[inline]
|
||||
pub fn new(align: Align) -> AbiAndPrefAlign {
|
||||
AbiAndPrefAlign { abi: align, pref: align }
|
||||
pub fn new(align: Align) -> AbiAlign {
|
||||
AbiAlign { abi: align }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn min(self, other: AbiAndPrefAlign) -> AbiAndPrefAlign {
|
||||
AbiAndPrefAlign { abi: self.abi.min(other.abi), pref: self.pref.min(other.pref) }
|
||||
pub fn min(self, other: AbiAlign) -> AbiAlign {
|
||||
AbiAlign { abi: self.abi.min(other.abi) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn max(self, other: AbiAndPrefAlign) -> AbiAndPrefAlign {
|
||||
AbiAndPrefAlign { abi: self.abi.max(other.abi), pref: self.pref.max(other.pref) }
|
||||
pub fn max(self, other: AbiAlign) -> AbiAlign {
|
||||
AbiAlign { abi: self.abi.max(other.abi) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for AbiAlign {
|
||||
type Target = Align;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.abi
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -944,7 +951,7 @@ impl Integer {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn align<C: HasDataLayout>(self, cx: &C) -> AbiAndPrefAlign {
|
||||
pub fn align<C: HasDataLayout>(self, cx: &C) -> AbiAlign {
|
||||
use Integer::*;
|
||||
let dl = cx.data_layout();
|
||||
|
||||
|
|
@ -1057,7 +1064,7 @@ impl Float {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn align<C: HasDataLayout>(self, cx: &C) -> AbiAndPrefAlign {
|
||||
pub fn align<C: HasDataLayout>(self, cx: &C) -> AbiAlign {
|
||||
use Float::*;
|
||||
let dl = cx.data_layout();
|
||||
|
||||
|
|
@ -1101,7 +1108,7 @@ impl Primitive {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn align<C: HasDataLayout>(self, cx: &C) -> AbiAndPrefAlign {
|
||||
pub fn align<C: HasDataLayout>(self, cx: &C) -> AbiAlign {
|
||||
use Primitive::*;
|
||||
let dl = cx.data_layout();
|
||||
|
||||
|
|
@ -1224,7 +1231,7 @@ impl Scalar {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn align(self, cx: &impl HasDataLayout) -> AbiAndPrefAlign {
|
||||
pub fn align(self, cx: &impl HasDataLayout) -> AbiAlign {
|
||||
self.primitive().align(cx)
|
||||
}
|
||||
|
||||
|
|
@ -1572,7 +1579,7 @@ pub enum Variants<FieldIdx: Idx, VariantIdx: Idx> {
|
|||
Multiple {
|
||||
tag: Scalar,
|
||||
tag_encoding: TagEncoding<VariantIdx>,
|
||||
tag_field: usize,
|
||||
tag_field: FieldIdx,
|
||||
variants: IndexVec<VariantIdx, LayoutData<FieldIdx, VariantIdx>>,
|
||||
},
|
||||
}
|
||||
|
|
@ -1730,7 +1737,7 @@ pub struct LayoutData<FieldIdx: Idx, VariantIdx: Idx> {
|
|||
/// especially in the case of by-pointer struct returns, which allocate stack even when unused.
|
||||
pub uninhabited: bool,
|
||||
|
||||
pub align: AbiAndPrefAlign,
|
||||
pub align: AbiAlign,
|
||||
pub size: Size,
|
||||
|
||||
/// The largest alignment explicitly requested with `repr(align)` on this type or any field.
|
||||
|
|
@ -1895,3 +1902,11 @@ pub enum StructKind {
|
|||
/// A univariant, but with a prefix of an arbitrary size & alignment (e.g., enum tag).
|
||||
Prefixed(Size, Align),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum AbiFromStrErr {
|
||||
/// not a known ABI
|
||||
Unknown,
|
||||
/// no "-unwind" variant can be used here
|
||||
NoExplicitUnwind,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ 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};
|
||||
use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol, kw, sym};
|
||||
use rustc_span::{DUMMY_SP, ErrorGuaranteed, Ident, Span, Symbol, kw, sym};
|
||||
use thin_vec::{ThinVec, thin_vec};
|
||||
|
||||
pub use crate::format::*;
|
||||
|
|
@ -99,8 +99,15 @@ pub struct Path {
|
|||
|
||||
impl PartialEq<Symbol> for Path {
|
||||
#[inline]
|
||||
fn eq(&self, symbol: &Symbol) -> bool {
|
||||
matches!(&self.segments[..], [segment] if segment.ident.name == *symbol)
|
||||
fn eq(&self, name: &Symbol) -> bool {
|
||||
if let [segment] = self.segments.as_ref()
|
||||
&& segment.args.is_none()
|
||||
&& segment.ident.name == *name
|
||||
{
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -120,17 +127,6 @@ impl Path {
|
|||
Path { segments: thin_vec![PathSegment::from_ident(ident)], span: ident.span, tokens: None }
|
||||
}
|
||||
|
||||
pub fn is_ident(&self, name: Symbol) -> bool {
|
||||
if let [segment] = self.segments.as_ref()
|
||||
&& segment.args.is_none()
|
||||
&& segment.ident.name == name
|
||||
{
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_global(&self) -> bool {
|
||||
self.segments.first().is_some_and(|segment| segment.ident.name == kw::PathRoot)
|
||||
}
|
||||
|
|
@ -1123,10 +1119,9 @@ impl Stmt {
|
|||
pub fn add_trailing_semicolon(mut self) -> Self {
|
||||
self.kind = match self.kind {
|
||||
StmtKind::Expr(expr) => StmtKind::Semi(expr),
|
||||
StmtKind::MacCall(mac) => {
|
||||
StmtKind::MacCall(mac.map(|MacCallStmt { mac, style: _, attrs, tokens }| {
|
||||
MacCallStmt { mac, style: MacStmtStyle::Semicolon, attrs, tokens }
|
||||
}))
|
||||
StmtKind::MacCall(mut mac) => {
|
||||
mac.style = MacStmtStyle::Semicolon;
|
||||
StmtKind::MacCall(mac)
|
||||
}
|
||||
kind => kind,
|
||||
};
|
||||
|
|
@ -1526,6 +1521,19 @@ impl Expr {
|
|||
| ExprKind::Struct(_)
|
||||
)
|
||||
}
|
||||
|
||||
/// Creates a dummy `P<Expr>`.
|
||||
///
|
||||
/// Should only be used when it will be replaced afterwards or as a return value when an error was encountered.
|
||||
pub fn dummy() -> P<Expr> {
|
||||
P(Expr {
|
||||
id: DUMMY_NODE_ID,
|
||||
kind: ExprKind::Dummy,
|
||||
span: DUMMY_SP,
|
||||
attrs: ThinVec::new(),
|
||||
tokens: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Encodable, Decodable, Debug)]
|
||||
|
|
@ -1715,7 +1723,7 @@ pub enum ExprKind {
|
|||
///
|
||||
/// Usually not written directly in user code but
|
||||
/// indirectly via the macro `core::mem::offset_of!(...)`.
|
||||
OffsetOf(P<Ty>, P<[Ident]>),
|
||||
OffsetOf(P<Ty>, Vec<Ident>),
|
||||
|
||||
/// A macro invocation; pre-expansion.
|
||||
MacCall(P<MacCall>),
|
||||
|
|
@ -2452,6 +2460,39 @@ impl TyKind {
|
|||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if this type is considered a scalar primitive (e.g.,
|
||||
/// `i32`, `u8`, `bool`, etc).
|
||||
///
|
||||
/// This check is based on **symbol equality** and does **not** remove any
|
||||
/// path prefixes or references. If a type alias or shadowing is present
|
||||
/// (e.g., `type i32 = CustomType;`), this method will still return `true`
|
||||
/// for `i32`, even though it may not refer to the primitive type.
|
||||
pub fn maybe_scalar(&self) -> bool {
|
||||
let Some(ty_sym) = self.is_simple_path() else {
|
||||
// unit type
|
||||
return self.is_unit();
|
||||
};
|
||||
matches!(
|
||||
ty_sym,
|
||||
sym::i8
|
||||
| sym::i16
|
||||
| sym::i32
|
||||
| sym::i64
|
||||
| sym::i128
|
||||
| sym::u8
|
||||
| sym::u16
|
||||
| sym::u32
|
||||
| sym::u64
|
||||
| sym::u128
|
||||
| sym::f16
|
||||
| sym::f32
|
||||
| sym::f64
|
||||
| sym::f128
|
||||
| sym::char
|
||||
| sym::bool
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// A pattern type pattern.
|
||||
|
|
@ -3417,9 +3458,9 @@ impl Item {
|
|||
ItemKind::Fn(i) => Some(&i.generics),
|
||||
ItemKind::TyAlias(i) => Some(&i.generics),
|
||||
ItemKind::TraitAlias(_, generics, _)
|
||||
| ItemKind::Enum(_, _, generics)
|
||||
| ItemKind::Struct(_, _, generics)
|
||||
| ItemKind::Union(_, _, generics) => Some(&generics),
|
||||
| ItemKind::Enum(_, generics, _)
|
||||
| ItemKind::Struct(_, generics, _)
|
||||
| ItemKind::Union(_, generics, _) => Some(&generics),
|
||||
ItemKind::Trait(i) => Some(&i.generics),
|
||||
ItemKind::Impl(i) => Some(&i.generics),
|
||||
}
|
||||
|
|
@ -3663,15 +3704,15 @@ pub enum ItemKind {
|
|||
/// An enum definition (`enum`).
|
||||
///
|
||||
/// E.g., `enum Foo<A, B> { C<A>, D<B> }`.
|
||||
Enum(Ident, EnumDef, Generics),
|
||||
Enum(Ident, Generics, EnumDef),
|
||||
/// A struct definition (`struct`).
|
||||
///
|
||||
/// E.g., `struct Foo<A> { x: A }`.
|
||||
Struct(Ident, VariantData, Generics),
|
||||
Struct(Ident, Generics, VariantData),
|
||||
/// A union definition (`union`).
|
||||
///
|
||||
/// E.g., `union Foo<A, B> { x: A, y: B }`.
|
||||
Union(Ident, VariantData, Generics),
|
||||
Union(Ident, Generics, VariantData),
|
||||
/// A trait declaration (`trait`).
|
||||
///
|
||||
/// E.g., `trait Foo { .. }`, `trait Foo<T> { .. }` or `auto trait Foo {}`.
|
||||
|
|
@ -3688,10 +3729,8 @@ pub enum ItemKind {
|
|||
///
|
||||
/// E.g., `foo!(..)`.
|
||||
MacCall(P<MacCall>),
|
||||
|
||||
/// A macro definition.
|
||||
MacroDef(Ident, MacroDef),
|
||||
|
||||
/// A single delegation item (`reuse`).
|
||||
///
|
||||
/// E.g. `reuse <Type as Trait>::name { target_expr_template }`.
|
||||
|
|
@ -3767,9 +3806,9 @@ impl ItemKind {
|
|||
Self::Fn(box Fn { generics, .. })
|
||||
| Self::TyAlias(box TyAlias { generics, .. })
|
||||
| Self::Const(box ConstItem { generics, .. })
|
||||
| Self::Enum(_, _, generics)
|
||||
| Self::Struct(_, _, generics)
|
||||
| Self::Union(_, _, generics)
|
||||
| Self::Enum(_, generics, _)
|
||||
| Self::Struct(_, generics, _)
|
||||
| Self::Union(_, generics, _)
|
||||
| Self::Trait(box Trait { generics, .. })
|
||||
| Self::TraitAlias(_, generics, _)
|
||||
| Self::Impl(box Impl { generics, .. }) => Some(generics),
|
||||
|
|
|
|||
|
|
@ -304,6 +304,7 @@ impl HasAttrs for Stmt {
|
|||
}
|
||||
|
||||
/// A newtype around an AST node that implements the traits above if the node implements them.
|
||||
#[repr(transparent)]
|
||||
pub struct AstNodeWrapper<Wrapped, Tag> {
|
||||
pub wrapped: Wrapped,
|
||||
pub tag: PhantomData<Tag>,
|
||||
|
|
@ -313,6 +314,11 @@ impl<Wrapped, Tag> AstNodeWrapper<Wrapped, Tag> {
|
|||
pub fn new(wrapped: Wrapped, _tag: Tag) -> AstNodeWrapper<Wrapped, Tag> {
|
||||
AstNodeWrapper { wrapped, tag: Default::default() }
|
||||
}
|
||||
|
||||
pub fn from_mut(wrapped: &mut Wrapped, _tag: Tag) -> &mut AstNodeWrapper<Wrapped, Tag> {
|
||||
// SAFETY: `AstNodeWrapper` is `repr(transparent)` w.r.t `Wrapped`
|
||||
unsafe { &mut *<*mut Wrapped>::cast(wrapped) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<Wrapped: HasNodeId, Tag> HasNodeId for AstNodeWrapper<Wrapped, Tag> {
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ impl Attribute {
|
|||
|
||||
pub fn unwrap_normal_item(self) -> AttrItem {
|
||||
match self.kind {
|
||||
AttrKind::Normal(normal) => normal.into_inner().item,
|
||||
AttrKind::Normal(normal) => normal.item,
|
||||
AttrKind::DocComment(..) => panic!("unexpected doc comment"),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,209 +1,11 @@
|
|||
//! The AST pointer.
|
||||
//!
|
||||
//! Provides [`P<T>`][struct@P], an owned smart pointer.
|
||||
//!
|
||||
//! # Motivations and benefits
|
||||
//!
|
||||
//! * **Identity**: sharing AST nodes is problematic for the various analysis
|
||||
//! passes (e.g., one may be able to bypass the borrow checker with a shared
|
||||
//! `ExprKind::AddrOf` node taking a mutable borrow).
|
||||
//!
|
||||
//! * **Efficiency**: folding can reuse allocation space for `P<T>` and `Vec<T>`,
|
||||
//! the latter even when the input and output types differ (as it would be the
|
||||
//! case with arenas or a GADT AST using type parameters to toggle features).
|
||||
//!
|
||||
//! * **Maintainability**: `P<T>` provides an interface, which can remain fully
|
||||
//! functional even if the implementation changes (using a special thread-local
|
||||
//! heap, for example). Moreover, a switch to, e.g., `P<'a, T>` would be easy
|
||||
//! and mostly automated.
|
||||
|
||||
use std::fmt::{self, Debug, Display};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::{slice, vec};
|
||||
|
||||
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
|
||||
use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
|
||||
/// An owned smart pointer.
|
||||
/// A pointer type that uniquely owns a heap allocation of type T.
|
||||
///
|
||||
/// See the [module level documentation][crate::ptr] for details.
|
||||
pub struct P<T: ?Sized> {
|
||||
ptr: Box<T>,
|
||||
}
|
||||
/// This used to be its own type, but now it's just a typedef for `Box` and we are planning to
|
||||
/// remove it soon.
|
||||
pub type P<T> = Box<T>;
|
||||
|
||||
/// Construct a `P<T>` from a `T` value.
|
||||
#[allow(non_snake_case)]
|
||||
pub fn P<T: 'static>(value: T) -> P<T> {
|
||||
P { ptr: Box::new(value) }
|
||||
}
|
||||
|
||||
impl<T: 'static> P<T> {
|
||||
/// Move out of the pointer.
|
||||
/// Intended for chaining transformations not covered by `map`.
|
||||
pub fn and_then<U, F>(self, f: F) -> U
|
||||
where
|
||||
F: FnOnce(T) -> U,
|
||||
{
|
||||
f(*self.ptr)
|
||||
}
|
||||
|
||||
/// Equivalent to `and_then(|x| x)`.
|
||||
pub fn into_inner(self) -> T {
|
||||
*self.ptr
|
||||
}
|
||||
|
||||
/// Produce a new `P<T>` from `self` without reallocating.
|
||||
pub fn map<F>(mut self, f: F) -> P<T>
|
||||
where
|
||||
F: FnOnce(T) -> T,
|
||||
{
|
||||
let x = f(*self.ptr);
|
||||
*self.ptr = x;
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Optionally produce a new `P<T>` from `self` without reallocating.
|
||||
pub fn filter_map<F>(mut self, f: F) -> Option<P<T>>
|
||||
where
|
||||
F: FnOnce(T) -> Option<T>,
|
||||
{
|
||||
*self.ptr = f(*self.ptr)?;
|
||||
Some(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Deref for P<T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &T {
|
||||
&self.ptr
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> DerefMut for P<T> {
|
||||
fn deref_mut(&mut self) -> &mut T {
|
||||
&mut self.ptr
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static + Clone> Clone for P<T> {
|
||||
fn clone(&self) -> P<T> {
|
||||
P((**self).clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + Debug> Debug for P<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
Debug::fmt(&self.ptr, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Display> Display for P<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
Display::fmt(&**self, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> fmt::Pointer for P<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Pointer::fmt(&self.ptr, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: Decoder, T: 'static + Decodable<D>> Decodable<D> for P<T> {
|
||||
fn decode(d: &mut D) -> P<T> {
|
||||
P(Decodable::decode(d))
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: Encoder, T: Encodable<S>> Encodable<S> for P<T> {
|
||||
fn encode(&self, s: &mut S) {
|
||||
(**self).encode(s);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> P<[T]> {
|
||||
// FIXME(const-hack) make this const again
|
||||
pub fn new() -> P<[T]> {
|
||||
P { ptr: Box::default() }
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
pub fn from_vec(v: Vec<T>) -> P<[T]> {
|
||||
P { ptr: v.into_boxed_slice() }
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
pub fn into_vec(self) -> Vec<T> {
|
||||
self.ptr.into_vec()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Default for P<[T]> {
|
||||
/// Creates an empty `P<[T]>`.
|
||||
fn default() -> P<[T]> {
|
||||
P::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone> Clone for P<[T]> {
|
||||
fn clone(&self) -> P<[T]> {
|
||||
P::from_vec(self.to_vec())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<Vec<T>> for P<[T]> {
|
||||
fn from(v: Vec<T>) -> Self {
|
||||
P::from_vec(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<P<[T]>> for Vec<T> {
|
||||
fn from(val: P<[T]>) -> Self {
|
||||
val.into_vec()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> FromIterator<T> for P<[T]> {
|
||||
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> P<[T]> {
|
||||
P::from_vec(iter.into_iter().collect())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> IntoIterator for P<[T]> {
|
||||
type Item = T;
|
||||
type IntoIter = vec::IntoIter<T>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.into_vec().into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> IntoIterator for &'a P<[T]> {
|
||||
type Item = &'a T;
|
||||
type IntoIter = slice::Iter<'a, T>;
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.ptr.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: Encoder, T: Encodable<S>> Encodable<S> for P<[T]> {
|
||||
fn encode(&self, s: &mut S) {
|
||||
Encodable::encode(&**self, s);
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: Decoder, T: Decodable<D>> Decodable<D> for P<[T]> {
|
||||
fn decode(d: &mut D) -> P<[T]> {
|
||||
P::from_vec(Decodable::decode(d))
|
||||
}
|
||||
}
|
||||
|
||||
impl<CTX, T> HashStable<CTX> for P<T>
|
||||
where
|
||||
T: ?Sized + HashStable<CTX>,
|
||||
{
|
||||
fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) {
|
||||
(**self).hash_stable(hcx, hasher);
|
||||
}
|
||||
pub fn P<T>(value: T) -> P<T> {
|
||||
Box::new(value)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,7 +57,9 @@ impl TokenTree {
|
|||
match (self, other) {
|
||||
(TokenTree::Token(token, _), TokenTree::Token(token2, _)) => token.kind == token2.kind,
|
||||
(TokenTree::Delimited(.., delim, tts), TokenTree::Delimited(.., delim2, tts2)) => {
|
||||
delim == delim2 && tts.eq_unspanned(tts2)
|
||||
delim == delim2
|
||||
&& tts.len() == tts2.len()
|
||||
&& tts.iter().zip(tts2.iter()).all(|(a, b)| a.eq_unspanned(b))
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
|
|
@ -694,18 +696,6 @@ impl TokenStream {
|
|||
TokenStreamIter::new(self)
|
||||
}
|
||||
|
||||
/// Compares two `TokenStream`s, checking equality without regarding span information.
|
||||
pub fn eq_unspanned(&self, other: &TokenStream) -> bool {
|
||||
let mut iter1 = self.iter();
|
||||
let mut iter2 = other.iter();
|
||||
for (tt1, tt2) in iter::zip(&mut iter1, &mut iter2) {
|
||||
if !tt1.eq_unspanned(tt2) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
iter1.next().is_none() && iter2.next().is_none()
|
||||
}
|
||||
|
||||
/// Create a token stream containing a single token with alone spacing. The
|
||||
/// spacing used for the final token in a constructed stream doesn't matter
|
||||
/// because it's never used. In practice we arbitrarily use
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -446,13 +446,7 @@ impl<'a> SelfResolver<'a> {
|
|||
}
|
||||
|
||||
impl<'ast, 'a> Visitor<'ast> for SelfResolver<'a> {
|
||||
fn visit_path(&mut self, path: &'ast Path, id: NodeId) {
|
||||
fn visit_id(&mut self, id: NodeId) {
|
||||
self.try_replace_id(id);
|
||||
visit::walk_path(self, path);
|
||||
}
|
||||
|
||||
fn visit_path_segment(&mut self, seg: &'ast PathSegment) {
|
||||
self.try_replace_id(seg.id);
|
||||
visit::walk_path_segment(self, seg);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
use std::assert_matches::assert_matches;
|
||||
use std::ops::ControlFlow;
|
||||
use std::sync::Arc;
|
||||
|
||||
|
|
@ -1199,11 +1198,13 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
let closure_def_id = self.local_def_id(closure_id);
|
||||
let (binder_clause, generic_params) = self.lower_closure_binder(binder);
|
||||
|
||||
assert_matches!(
|
||||
coroutine_kind,
|
||||
CoroutineKind::Async { .. },
|
||||
"only async closures are supported currently"
|
||||
);
|
||||
let coroutine_desugaring = match coroutine_kind {
|
||||
CoroutineKind::Async { .. } => hir::CoroutineDesugaring::Async,
|
||||
CoroutineKind::Gen { .. } => hir::CoroutineDesugaring::Gen,
|
||||
CoroutineKind::AsyncGen { span, .. } => {
|
||||
span_bug!(span, "only async closures and `iter!` closures are supported currently")
|
||||
}
|
||||
};
|
||||
|
||||
let body = self.with_new_scopes(fn_decl_span, |this| {
|
||||
let inner_decl =
|
||||
|
|
@ -1247,7 +1248,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
// Lower this as a `CoroutineClosure`. That will ensure that HIR typeck
|
||||
// knows that a `FnDecl` output type like `-> &str` actually means
|
||||
// "coroutine that returns &str", rather than directly returning a `&str`.
|
||||
kind: hir::ClosureKind::CoroutineClosure(hir::CoroutineDesugaring::Async),
|
||||
kind: hir::ClosureKind::CoroutineClosure(coroutine_desugaring),
|
||||
constness: hir::Constness::NotConst,
|
||||
});
|
||||
hir::ExprKind::Closure(c)
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use rustc_ast::*;
|
|||
use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_hir as hir;
|
||||
use rustc_session::config::FmtDebug;
|
||||
use rustc_span::{Ident, Span, Symbol, kw, sym};
|
||||
use rustc_span::{Ident, Span, Symbol, sym};
|
||||
|
||||
use super::LoweringContext;
|
||||
|
||||
|
|
@ -418,7 +418,7 @@ fn expand_format_args<'hir>(
|
|||
&FormatArgsPiece::Placeholder(_) => {
|
||||
// Inject empty string before placeholders when not already preceded by a literal piece.
|
||||
if i == 0 || matches!(fmt.template[i - 1], FormatArgsPiece::Placeholder(_)) {
|
||||
Some(ctx.expr_str(fmt.span, kw::Empty))
|
||||
Some(ctx.expr_str(fmt.span, sym::empty))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
|
|
|||
|
|
@ -164,7 +164,7 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> {
|
|||
fn visit_item(&mut self, i: &'hir Item<'hir>) {
|
||||
debug_assert_eq!(i.owner_id, self.owner);
|
||||
self.with_parent(i.hir_id(), |this| {
|
||||
if let ItemKind::Struct(_, struct_def, _) = &i.kind {
|
||||
if let ItemKind::Struct(_, _, struct_def) = &i.kind {
|
||||
// If this is a tuple or unit-like struct, register the constructor.
|
||||
if let Some(ctor_hir_id) = struct_def.ctor_hir_id() {
|
||||
this.insert(i.span, ctor_hir_id, Node::Ctor(struct_def));
|
||||
|
|
|
|||
|
|
@ -3,10 +3,11 @@ use rustc_ast::ptr::P;
|
|||
use rustc_ast::visit::AssocCtxt;
|
||||
use rustc_ast::*;
|
||||
use rustc_errors::ErrorGuaranteed;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::def::{DefKind, PerNS, Res};
|
||||
use rustc_hir::def_id::{CRATE_DEF_ID, LocalDefId};
|
||||
use rustc_hir::{self as hir, HirId, LifetimeSource, PredicateOrigin};
|
||||
use rustc_index::{IndexSlice, IndexVec};
|
||||
use rustc_middle::span_bug;
|
||||
use rustc_middle::ty::{ResolverAstLowering, TyCtxt};
|
||||
use rustc_span::edit_distance::find_best_match_for_name;
|
||||
use rustc_span::{DUMMY_SP, DesugaringKind, Ident, Span, Symbol, kw, sym};
|
||||
|
|
@ -70,44 +71,32 @@ impl<'a, 'hir> ItemLowerer<'a, 'hir> {
|
|||
}
|
||||
}
|
||||
|
||||
pub(super) fn lower_node(&mut self, def_id: LocalDefId) -> hir::MaybeOwner<'hir> {
|
||||
pub(super) fn lower_node(&mut self, def_id: LocalDefId) {
|
||||
let owner = self.owners.ensure_contains_elem(def_id, || hir::MaybeOwner::Phantom);
|
||||
if let hir::MaybeOwner::Phantom = owner {
|
||||
let node = self.ast_index[def_id];
|
||||
match node {
|
||||
AstOwner::NonOwner => {}
|
||||
AstOwner::Crate(c) => self.lower_crate(c),
|
||||
AstOwner::Item(item) => self.lower_item(item),
|
||||
AstOwner::AssocItem(item, ctxt) => self.lower_assoc_item(item, ctxt),
|
||||
AstOwner::ForeignItem(item) => self.lower_foreign_item(item),
|
||||
AstOwner::Crate(c) => {
|
||||
debug_assert_eq!(self.resolver.node_id_to_def_id[&CRATE_NODE_ID], CRATE_DEF_ID);
|
||||
self.with_lctx(CRATE_NODE_ID, |lctx| {
|
||||
let module = lctx.lower_mod(&c.items, &c.spans);
|
||||
// FIXME(jdonszelman): is dummy span ever a problem here?
|
||||
lctx.lower_attrs(hir::CRATE_HIR_ID, &c.attrs, DUMMY_SP);
|
||||
hir::OwnerNode::Crate(module)
|
||||
})
|
||||
}
|
||||
AstOwner::Item(item) => {
|
||||
self.with_lctx(item.id, |lctx| hir::OwnerNode::Item(lctx.lower_item(item)))
|
||||
}
|
||||
AstOwner::AssocItem(item, ctxt) => {
|
||||
self.with_lctx(item.id, |lctx| lctx.lower_assoc_item(item, ctxt))
|
||||
}
|
||||
AstOwner::ForeignItem(item) => self.with_lctx(item.id, |lctx| {
|
||||
hir::OwnerNode::ForeignItem(lctx.lower_foreign_item(item))
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
self.owners[def_id]
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self, c))]
|
||||
fn lower_crate(&mut self, c: &Crate) {
|
||||
debug_assert_eq!(self.resolver.node_id_to_def_id[&CRATE_NODE_ID], CRATE_DEF_ID);
|
||||
self.with_lctx(CRATE_NODE_ID, |lctx| {
|
||||
let module = lctx.lower_mod(&c.items, &c.spans);
|
||||
// FIXME(jdonszelman): is dummy span ever a problem here?
|
||||
lctx.lower_attrs(hir::CRATE_HIR_ID, &c.attrs, DUMMY_SP);
|
||||
hir::OwnerNode::Crate(module)
|
||||
})
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
fn lower_item(&mut self, item: &Item) {
|
||||
self.with_lctx(item.id, |lctx| hir::OwnerNode::Item(lctx.lower_item(item)))
|
||||
}
|
||||
|
||||
fn lower_assoc_item(&mut self, item: &AssocItem, ctxt: AssocCtxt) {
|
||||
self.with_lctx(item.id, |lctx| lctx.lower_assoc_item(item, ctxt))
|
||||
}
|
||||
|
||||
fn lower_foreign_item(&mut self, item: &ForeignItem) {
|
||||
self.with_lctx(item.id, |lctx| hir::OwnerNode::ForeignItem(lctx.lower_foreign_item(item)))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -192,7 +181,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
let (ty, body_id) =
|
||||
self.lower_const_item(t, span, e.as_deref(), ImplTraitPosition::StaticTy);
|
||||
self.lower_define_opaque(hir_id, define_opaque);
|
||||
hir::ItemKind::Static(ident, ty, *m, body_id)
|
||||
hir::ItemKind::Static(*m, ident, ty, body_id)
|
||||
}
|
||||
ItemKind::Const(box ast::ConstItem {
|
||||
ident,
|
||||
|
|
@ -212,7 +201,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
},
|
||||
);
|
||||
self.lower_define_opaque(hir_id, &define_opaque);
|
||||
hir::ItemKind::Const(ident, ty, generics, body_id)
|
||||
hir::ItemKind::Const(ident, generics, ty, body_id)
|
||||
}
|
||||
ItemKind::Fn(box Fn {
|
||||
sig: FnSig { decl, header, span: fn_sig_span },
|
||||
|
|
@ -316,9 +305,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
),
|
||||
},
|
||||
);
|
||||
hir::ItemKind::TyAlias(ident, ty, generics)
|
||||
hir::ItemKind::TyAlias(ident, generics, ty)
|
||||
}
|
||||
ItemKind::Enum(ident, enum_definition, generics) => {
|
||||
ItemKind::Enum(ident, generics, enum_definition) => {
|
||||
let ident = self.lower_ident(*ident);
|
||||
let (generics, variants) = self.lower_generics(
|
||||
generics,
|
||||
|
|
@ -330,9 +319,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
)
|
||||
},
|
||||
);
|
||||
hir::ItemKind::Enum(ident, hir::EnumDef { variants }, generics)
|
||||
hir::ItemKind::Enum(ident, generics, hir::EnumDef { variants })
|
||||
}
|
||||
ItemKind::Struct(ident, struct_def, generics) => {
|
||||
ItemKind::Struct(ident, generics, struct_def) => {
|
||||
let ident = self.lower_ident(*ident);
|
||||
let (generics, struct_def) = self.lower_generics(
|
||||
generics,
|
||||
|
|
@ -340,9 +329,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
|
||||
|this| this.lower_variant_data(hir_id, struct_def),
|
||||
);
|
||||
hir::ItemKind::Struct(ident, struct_def, generics)
|
||||
hir::ItemKind::Struct(ident, generics, struct_def)
|
||||
}
|
||||
ItemKind::Union(ident, vdata, generics) => {
|
||||
ItemKind::Union(ident, generics, vdata) => {
|
||||
let ident = self.lower_ident(*ident);
|
||||
let (generics, vdata) = self.lower_generics(
|
||||
generics,
|
||||
|
|
@ -350,7 +339,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
|
||||
|this| this.lower_variant_data(hir_id, vdata),
|
||||
);
|
||||
hir::ItemKind::Union(ident, vdata, generics)
|
||||
hir::ItemKind::Union(ident, generics, vdata)
|
||||
}
|
||||
ItemKind::Impl(box Impl {
|
||||
safety,
|
||||
|
|
@ -479,8 +468,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
ItemKind::Delegation(box delegation) => {
|
||||
let delegation_results = self.lower_delegation(delegation, id, false);
|
||||
hir::ItemKind::Fn {
|
||||
ident: delegation_results.ident,
|
||||
sig: delegation_results.sig,
|
||||
ident: delegation_results.ident,
|
||||
generics: delegation_results.generics,
|
||||
body: delegation_results.body_id,
|
||||
has_body: true,
|
||||
|
|
@ -539,7 +528,22 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
}
|
||||
UseTreeKind::Glob => {
|
||||
let res = self.expect_full_res(id);
|
||||
let res = smallvec![self.lower_res(res)];
|
||||
let res = self.lower_res(res);
|
||||
// Put the result in the appropriate namespace.
|
||||
let res = match res {
|
||||
Res::Def(DefKind::Mod | DefKind::Trait, _) => {
|
||||
PerNS { type_ns: Some(res), value_ns: None, macro_ns: None }
|
||||
}
|
||||
Res::Def(DefKind::Enum, _) => {
|
||||
PerNS { type_ns: None, value_ns: Some(res), macro_ns: None }
|
||||
}
|
||||
Res::Err => {
|
||||
// Propagate the error to all namespaces, just to be sure.
|
||||
let err = Some(Res::Err);
|
||||
PerNS { type_ns: err, value_ns: err, macro_ns: err }
|
||||
}
|
||||
_ => span_bug!(path.span, "bad glob res {:?}", res),
|
||||
};
|
||||
let path = Path { segments, span: path.span, tokens: None };
|
||||
let path = self.lower_use_path(res, &path, ParamMode::Explicit);
|
||||
hir::ItemKind::Use(path, hir::UseKind::Glob)
|
||||
|
|
@ -613,7 +617,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
} else {
|
||||
// For non-empty lists we can just drop all the data, the prefix is already
|
||||
// present in HIR as a part of nested imports.
|
||||
self.arena.alloc(hir::UsePath { res: smallvec![], segments: &[], span })
|
||||
self.arena.alloc(hir::UsePath { res: PerNS::default(), segments: &[], span })
|
||||
};
|
||||
hir::ItemKind::Use(path, hir::UseKind::ListStem)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,9 +33,7 @@
|
|||
// tidy-alphabetical-start
|
||||
#![allow(internal_features)]
|
||||
#![doc(rust_logo)]
|
||||
#![feature(assert_matches)]
|
||||
#![feature(box_patterns)]
|
||||
#![feature(exact_size_is_empty)]
|
||||
#![feature(if_let_guard)]
|
||||
#![feature(rustdoc_internals)]
|
||||
// tidy-alphabetical-end
|
||||
|
|
@ -64,7 +62,7 @@ use rustc_middle::ty::{ResolverAstLowering, TyCtxt};
|
|||
use rustc_session::parse::{add_feature_diagnostics, feature_err};
|
||||
use rustc_span::symbol::{Ident, Symbol, kw, sym};
|
||||
use rustc_span::{DUMMY_SP, DesugaringKind, Span};
|
||||
use smallvec::{SmallVec, smallvec};
|
||||
use smallvec::SmallVec;
|
||||
use thin_vec::ThinVec;
|
||||
use tracing::{debug, instrument, trace};
|
||||
|
||||
|
|
@ -444,14 +442,14 @@ pub fn lower_to_hir(tcx: TyCtxt<'_>, (): ()) -> hir::Crate<'_> {
|
|||
tcx.definitions_untracked().def_index_count(),
|
||||
);
|
||||
|
||||
let mut lowerer = item::ItemLowerer {
|
||||
tcx,
|
||||
resolver: &mut resolver,
|
||||
ast_index: &ast_index,
|
||||
owners: &mut owners,
|
||||
};
|
||||
for def_id in ast_index.indices() {
|
||||
item::ItemLowerer {
|
||||
tcx,
|
||||
resolver: &mut resolver,
|
||||
ast_index: &ast_index,
|
||||
owners: &mut owners,
|
||||
}
|
||||
.lower_node(def_id);
|
||||
lowerer.lower_node(def_id);
|
||||
}
|
||||
|
||||
drop(ast_index);
|
||||
|
|
@ -705,14 +703,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
self.resolver.get_partial_res(id).map_or(Res::Err, |pr| pr.expect_full_res())
|
||||
}
|
||||
|
||||
fn lower_import_res(&mut self, id: NodeId, span: Span) -> SmallVec<[Res; 3]> {
|
||||
let res = self.resolver.get_import_res(id).present_items();
|
||||
let res: SmallVec<_> = res.map(|res| self.lower_res(res)).collect();
|
||||
if res.is_empty() {
|
||||
fn lower_import_res(&mut self, id: NodeId, span: Span) -> PerNS<Option<Res>> {
|
||||
let per_ns = self.resolver.get_import_res(id);
|
||||
let per_ns = per_ns.map(|res| res.map(|res| self.lower_res(res)));
|
||||
if per_ns.is_empty() {
|
||||
// Propagate the error to all namespaces, just to be sure.
|
||||
self.dcx().span_delayed_bug(span, "no resolution for an import");
|
||||
return smallvec![Res::Err];
|
||||
let err = Some(Res::Err);
|
||||
return PerNS { type_ns: err, value_ns: err, macro_ns: err };
|
||||
}
|
||||
res
|
||||
per_ns
|
||||
}
|
||||
|
||||
fn make_lang_item_qpath(
|
||||
|
|
@ -730,7 +730,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
span: Span,
|
||||
args: Option<&'hir hir::GenericArgs<'hir>>,
|
||||
) -> &'hir hir::Path<'hir> {
|
||||
let def_id = self.tcx.require_lang_item(lang_item, Some(span));
|
||||
let def_id = self.tcx.require_lang_item(lang_item, span);
|
||||
let def_kind = self.tcx.def_kind(def_id);
|
||||
let res = Res::Def(def_kind, def_id);
|
||||
self.arena.alloc(hir::Path {
|
||||
|
|
@ -1404,7 +1404,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
};
|
||||
let span = self.tcx.sess.source_map().start_point(t.span).shrink_to_hi();
|
||||
let region = Lifetime { ident: Ident::new(kw::UnderscoreLifetime, span), id };
|
||||
(region, LifetimeSyntax::Hidden)
|
||||
(region, LifetimeSyntax::Implicit)
|
||||
}
|
||||
};
|
||||
self.lower_lifetime(®ion, LifetimeSource::Reference, syntax)
|
||||
|
|
@ -1788,7 +1788,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
id,
|
||||
Ident::new(kw::UnderscoreLifetime, span),
|
||||
LifetimeSource::Path { angle_brackets },
|
||||
LifetimeSyntax::Hidden,
|
||||
LifetimeSyntax::Implicit,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -2420,7 +2420,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
Ident::new(kw::UnderscoreLifetime, self.lower_span(span)),
|
||||
hir::LifetimeKind::ImplicitObjectLifetimeDefault,
|
||||
LifetimeSource::Other,
|
||||
LifetimeSyntax::Hidden,
|
||||
LifetimeSyntax::Implicit,
|
||||
);
|
||||
debug!("elided_dyn_bound: r={:?}", r);
|
||||
self.arena.alloc(r)
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use rustc_ast::{self as ast, *};
|
||||
use rustc_hir::def::{DefKind, PartialRes, Res};
|
||||
use rustc_hir::def::{DefKind, PartialRes, PerNS, Res};
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::{self as hir, GenericArg};
|
||||
use rustc_middle::{span_bug, ty};
|
||||
use rustc_session::parse::add_feature_diagnostics;
|
||||
use rustc_span::{BytePos, DUMMY_SP, DesugaringKind, Ident, Span, Symbol, sym};
|
||||
use smallvec::{SmallVec, smallvec};
|
||||
use smallvec::smallvec;
|
||||
use tracing::{debug, instrument};
|
||||
|
||||
use super::errors::{
|
||||
|
|
@ -226,11 +226,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
|
||||
pub(crate) fn lower_use_path(
|
||||
&mut self,
|
||||
res: SmallVec<[Res; 3]>,
|
||||
res: PerNS<Option<Res>>,
|
||||
p: &Path,
|
||||
param_mode: ParamMode,
|
||||
) -> &'hir hir::UsePath<'hir> {
|
||||
assert!((1..=3).contains(&res.len()));
|
||||
assert!(!res.is_empty());
|
||||
self.arena.alloc(hir::UsePath {
|
||||
res,
|
||||
segments: self.arena.alloc_from_iter(p.segments.iter().map(|segment| {
|
||||
|
|
|
|||
|
|
@ -1010,7 +1010,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
|||
});
|
||||
self.extern_mod_span = old_item;
|
||||
}
|
||||
ItemKind::Enum(_, def, _) => {
|
||||
ItemKind::Enum(_, _, def) => {
|
||||
for variant in &def.variants {
|
||||
self.visibility_not_permitted(
|
||||
&variant.vis,
|
||||
|
|
@ -1061,7 +1061,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
|||
}
|
||||
visit::walk_item(self, item)
|
||||
}
|
||||
ItemKind::Struct(ident, vdata, generics) => match vdata {
|
||||
ItemKind::Struct(ident, generics, vdata) => match vdata {
|
||||
VariantData::Struct { fields, .. } => {
|
||||
self.visit_attrs_vis_ident(&item.attrs, &item.vis, ident);
|
||||
self.visit_generics(generics);
|
||||
|
|
@ -1070,7 +1070,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
|||
}
|
||||
_ => visit::walk_item(self, item),
|
||||
},
|
||||
ItemKind::Union(ident, vdata, generics) => {
|
||||
ItemKind::Union(ident, generics, vdata) => {
|
||||
if vdata.fields().is_empty() {
|
||||
self.dcx().emit_err(errors::FieldlessUnion { span: item.span });
|
||||
}
|
||||
|
|
|
|||
|
|
@ -477,11 +477,12 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
|
|||
for span in spans {
|
||||
if (!visitor.features.coroutines() && !span.allows_unstable(sym::coroutines))
|
||||
&& (!visitor.features.gen_blocks() && !span.allows_unstable(sym::gen_blocks))
|
||||
&& (!visitor.features.yield_expr() && !span.allows_unstable(sym::yield_expr))
|
||||
{
|
||||
#[allow(rustc::untranslatable_diagnostic)]
|
||||
// Don't know which of the two features to include in the
|
||||
// error message, so I am arbitrarily picking one.
|
||||
feature_err(&visitor.sess, sym::coroutines, *span, "yield syntax is experimental")
|
||||
// Emit yield_expr as the error, since that will be sufficient. You can think of it
|
||||
// as coroutines and gen_blocks imply yield_expr.
|
||||
feature_err(&visitor.sess, sym::yield_expr, *span, "yield syntax is experimental")
|
||||
.emit();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -298,14 +298,14 @@ impl<'a> State<'a> {
|
|||
*defaultness,
|
||||
);
|
||||
}
|
||||
ast::ItemKind::Enum(ident, enum_definition, params) => {
|
||||
self.print_enum_def(enum_definition, params, *ident, item.span, &item.vis);
|
||||
ast::ItemKind::Enum(ident, generics, enum_definition) => {
|
||||
self.print_enum_def(enum_definition, generics, *ident, item.span, &item.vis);
|
||||
}
|
||||
ast::ItemKind::Struct(ident, struct_def, generics) => {
|
||||
ast::ItemKind::Struct(ident, generics, struct_def) => {
|
||||
let (cb, ib) = self.head(visibility_qualified(&item.vis, "struct"));
|
||||
self.print_struct(struct_def, generics, *ident, item.span, true, cb, ib);
|
||||
}
|
||||
ast::ItemKind::Union(ident, struct_def, generics) => {
|
||||
ast::ItemKind::Union(ident, generics, struct_def) => {
|
||||
let (cb, ib) = self.head(visibility_qualified(&item.vis, "union"));
|
||||
self.print_struct(struct_def, generics, *ident, item.span, true, cb, ib);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
use std::fmt::{self, Display};
|
||||
use std::sync::OnceLock;
|
||||
|
||||
use rustc_macros::{
|
||||
Decodable, Encodable, HashStable_Generic, PrintAttribute, current_rustc_version,
|
||||
|
|
@ -16,8 +17,29 @@ pub struct RustcVersion {
|
|||
|
||||
impl RustcVersion {
|
||||
pub const CURRENT: Self = current_rustc_version!();
|
||||
pub fn current_overridable() -> Self {
|
||||
*CURRENT_OVERRIDABLE.get_or_init(|| {
|
||||
if let Ok(override_var) = std::env::var("RUSTC_OVERRIDE_VERSION_STRING")
|
||||
&& let Some(override_) = Self::parse_str(&override_var)
|
||||
{
|
||||
override_
|
||||
} else {
|
||||
Self::CURRENT
|
||||
}
|
||||
})
|
||||
}
|
||||
fn parse_str(value: &str) -> Option<Self> {
|
||||
// Ignore any suffixes such as "-dev" or "-nightly".
|
||||
let mut components = value.split('-').next().unwrap().splitn(3, '.');
|
||||
let major = components.next()?.parse().ok()?;
|
||||
let minor = components.next()?.parse().ok()?;
|
||||
let patch = components.next().unwrap_or("0").parse().ok()?;
|
||||
Some(RustcVersion { major, minor, patch })
|
||||
}
|
||||
}
|
||||
|
||||
static CURRENT_OVERRIDABLE: OnceLock<RustcVersion> = OnceLock::new();
|
||||
|
||||
impl Display for RustcVersion {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(formatter, "{}.{}.{}", self.major, self.minor, self.patch)
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ use crate::session_diagnostics;
|
|||
|
||||
pub(crate) struct AllowInternalUnstableParser;
|
||||
impl CombineAttributeParser for AllowInternalUnstableParser {
|
||||
const PATH: &'static [rustc_span::Symbol] = &[sym::allow_internal_unstable];
|
||||
const PATH: &'static [Symbol] = &[sym::allow_internal_unstable];
|
||||
type Item = (Symbol, Span);
|
||||
const CONVERT: ConvertFn<Self::Item> = AttributeKind::AllowInternalUnstable;
|
||||
|
||||
|
|
@ -24,7 +24,7 @@ impl CombineAttributeParser for AllowInternalUnstableParser {
|
|||
|
||||
pub(crate) struct AllowConstFnUnstableParser;
|
||||
impl CombineAttributeParser for AllowConstFnUnstableParser {
|
||||
const PATH: &'static [rustc_span::Symbol] = &[sym::rustc_allow_const_fn_unstable];
|
||||
const PATH: &'static [Symbol] = &[sym::rustc_allow_const_fn_unstable];
|
||||
type Item = Symbol;
|
||||
const CONVERT: ConvertFn<Self::Item> = AttributeKind::AllowConstFnUnstable;
|
||||
|
||||
|
|
@ -53,7 +53,7 @@ fn parse_unstable<'a>(
|
|||
|
||||
for param in list.mixed() {
|
||||
let param_span = param.span();
|
||||
if let Some(ident) = param.meta_item().and_then(|i| i.path_without_args().word()) {
|
||||
if let Some(ident) = param.meta_item().and_then(|i| i.path().word()) {
|
||||
res.push(ident.name);
|
||||
} else {
|
||||
cx.emit_err(session_diagnostics::ExpectsFeatures {
|
||||
|
|
|
|||
|
|
@ -4,14 +4,29 @@ use rustc_attr_data_structures::RustcVersion;
|
|||
use rustc_feature::{Features, GatedCfg, find_gated_cfg};
|
||||
use rustc_session::Session;
|
||||
use rustc_session::config::ExpectedValues;
|
||||
use rustc_session::lint::BuiltinLintDiag;
|
||||
use rustc_session::lint::builtin::UNEXPECTED_CFGS;
|
||||
use rustc_session::lint::{BuiltinLintDiag, Lint};
|
||||
use rustc_session::parse::feature_err;
|
||||
use rustc_span::{Span, Symbol, sym};
|
||||
|
||||
use crate::session_diagnostics::{self, UnsupportedLiteralReason};
|
||||
use crate::{fluent_generated, parse_version};
|
||||
|
||||
/// Emitter of a builtin lint from `cfg_matches`.
|
||||
///
|
||||
/// Used to support emiting a lint (currently on check-cfg), either:
|
||||
/// - as an early buffered lint (in `rustc`)
|
||||
/// - or has a "normal" lint from HIR (in `rustdoc`)
|
||||
pub trait CfgMatchesLintEmitter {
|
||||
fn emit_span_lint(&self, sess: &Session, lint: &'static Lint, sp: Span, diag: BuiltinLintDiag);
|
||||
}
|
||||
|
||||
impl CfgMatchesLintEmitter for NodeId {
|
||||
fn emit_span_lint(&self, sess: &Session, lint: &'static Lint, sp: Span, diag: BuiltinLintDiag) {
|
||||
sess.psess.buffer_lint(lint, sp, *self, diag);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Condition {
|
||||
pub name: Symbol,
|
||||
|
|
@ -25,17 +40,17 @@ pub struct Condition {
|
|||
pub fn cfg_matches(
|
||||
cfg: &MetaItemInner,
|
||||
sess: &Session,
|
||||
lint_node_id: NodeId,
|
||||
lint_emitter: impl CfgMatchesLintEmitter,
|
||||
features: Option<&Features>,
|
||||
) -> bool {
|
||||
eval_condition(cfg, sess, features, &mut |cfg| {
|
||||
try_gate_cfg(cfg.name, cfg.span, sess, features);
|
||||
match sess.psess.check_config.expecteds.get(&cfg.name) {
|
||||
Some(ExpectedValues::Some(values)) if !values.contains(&cfg.value) => {
|
||||
sess.psess.buffer_lint(
|
||||
lint_emitter.emit_span_lint(
|
||||
sess,
|
||||
UNEXPECTED_CFGS,
|
||||
cfg.span,
|
||||
lint_node_id,
|
||||
BuiltinLintDiag::UnexpectedCfgValue(
|
||||
(cfg.name, cfg.name_span),
|
||||
cfg.value.map(|v| (v, cfg.value_span.unwrap())),
|
||||
|
|
@ -43,10 +58,10 @@ pub fn cfg_matches(
|
|||
);
|
||||
}
|
||||
None if sess.psess.check_config.exhaustive_names => {
|
||||
sess.psess.buffer_lint(
|
||||
lint_emitter.emit_span_lint(
|
||||
sess,
|
||||
UNEXPECTED_CFGS,
|
||||
cfg.span,
|
||||
lint_node_id,
|
||||
BuiltinLintDiag::UnexpectedCfgName(
|
||||
(cfg.name, cfg.name_span),
|
||||
cfg.value.map(|v| (v, cfg.value_span.unwrap())),
|
||||
|
|
@ -129,9 +144,9 @@ pub fn eval_condition(
|
|||
|
||||
// See https://github.com/rust-lang/rust/issues/64796#issuecomment-640851454 for details
|
||||
if sess.psess.assume_incomplete_release {
|
||||
RustcVersion::CURRENT > min_version
|
||||
RustcVersion::current_overridable() > min_version
|
||||
} else {
|
||||
RustcVersion::CURRENT >= min_version
|
||||
RustcVersion::current_overridable() >= min_version
|
||||
}
|
||||
}
|
||||
MetaItemKind::List(mis) => {
|
||||
|
|
|
|||
|
|
@ -42,9 +42,9 @@ fn get(
|
|||
}
|
||||
|
||||
impl SingleAttributeParser for DeprecationParser {
|
||||
const PATH: &'static [rustc_span::Symbol] = &[sym::deprecated];
|
||||
const PATH: &'static [Symbol] = &[sym::deprecated];
|
||||
|
||||
fn on_duplicate(cx: &AcceptContext<'_>, first_span: rustc_span::Span) {
|
||||
fn on_duplicate(cx: &AcceptContext<'_>, first_span: Span) {
|
||||
// FIXME(jdonszelmann): merge with errors from check_attrs.rs
|
||||
cx.emit_err(session_diagnostics::UnusedMultiple {
|
||||
this: cx.attr_span,
|
||||
|
|
@ -79,7 +79,7 @@ impl SingleAttributeParser for DeprecationParser {
|
|||
return None;
|
||||
};
|
||||
|
||||
let ident_name = param.path_without_args().word_sym();
|
||||
let ident_name = param.path().word_sym();
|
||||
|
||||
match ident_name {
|
||||
Some(name @ sym::since) => {
|
||||
|
|
@ -102,7 +102,7 @@ impl SingleAttributeParser for DeprecationParser {
|
|||
_ => {
|
||||
cx.emit_err(session_diagnostics::UnknownMetaItem {
|
||||
span: param_span,
|
||||
item: param.path_without_args().to_string(),
|
||||
item: param.path().to_string(),
|
||||
expected: if features.deprecated_suggestion() {
|
||||
&["since", "note", "suggestion"]
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
use std::marker::PhantomData;
|
||||
|
||||
use rustc_attr_data_structures::AttributeKind;
|
||||
use rustc_span::Span;
|
||||
use rustc_span::{Span, Symbol};
|
||||
use thin_vec::ThinVec;
|
||||
|
||||
use crate::context::{AcceptContext, FinalizeContext};
|
||||
|
|
@ -33,7 +33,7 @@ pub(crate) mod transparency;
|
|||
pub(crate) mod util;
|
||||
|
||||
type AcceptFn<T> = fn(&mut T, &AcceptContext<'_>, &ArgParser<'_>);
|
||||
type AcceptMapping<T> = &'static [(&'static [rustc_span::Symbol], AcceptFn<T>)];
|
||||
type AcceptMapping<T> = &'static [(&'static [Symbol], AcceptFn<T>)];
|
||||
|
||||
/// An [`AttributeParser`] is a type which searches for syntactic attributes.
|
||||
///
|
||||
|
|
@ -72,9 +72,9 @@ pub(crate) trait AttributeParser: Default + 'static {
|
|||
/// [`SingleAttributeParser`] can only convert attributes one-to-one, and cannot combine multiple
|
||||
/// attributes together like is necessary for `#[stable()]` and `#[unstable()]` for example.
|
||||
pub(crate) trait SingleAttributeParser: 'static {
|
||||
const PATH: &'static [rustc_span::Symbol];
|
||||
const PATH: &'static [Symbol];
|
||||
|
||||
/// Caled when a duplicate attribute is found.
|
||||
/// Called when a duplicate attribute is found.
|
||||
///
|
||||
/// `first_span` is the span of the first occurrence of this attribute.
|
||||
// FIXME(jdonszelmann): default error
|
||||
|
|
@ -119,7 +119,7 @@ type ConvertFn<E> = fn(ThinVec<E>) -> AttributeKind;
|
|||
/// [`CombineAttributeParser`] can only convert a single kind of attribute, and cannot combine multiple
|
||||
/// attributes together like is necessary for `#[stable()]` and `#[unstable()]` for example.
|
||||
pub(crate) trait CombineAttributeParser: 'static {
|
||||
const PATH: &'static [rustc_span::Symbol];
|
||||
const PATH: &'static [Symbol];
|
||||
|
||||
type Item;
|
||||
const CONVERT: ConvertFn<Self::Item>;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use rustc_abi::Align;
|
||||
use rustc_ast::{IntTy, LitIntType, LitKind, UintTy};
|
||||
use rustc_attr_data_structures::{AttributeKind, IntType, ReprAttr};
|
||||
use rustc_span::{Span, Symbol, sym};
|
||||
use rustc_span::{DUMMY_SP, Span, Symbol, sym};
|
||||
|
||||
use super::{CombineAttributeParser, ConvertFn};
|
||||
use crate::context::AcceptContext;
|
||||
|
|
@ -21,7 +21,7 @@ pub(crate) struct ReprParser;
|
|||
|
||||
impl CombineAttributeParser for ReprParser {
|
||||
type Item = (ReprAttr, Span);
|
||||
const PATH: &'static [rustc_span::Symbol] = &[sym::repr];
|
||||
const PATH: &'static [Symbol] = &[sym::repr];
|
||||
const CONVERT: ConvertFn<Self::Item> = AttributeKind::Repr;
|
||||
|
||||
fn extend<'a>(
|
||||
|
|
@ -96,10 +96,10 @@ fn parse_repr(cx: &AcceptContext<'_>, param: &MetaItemParser<'_>) -> Option<Repr
|
|||
|
||||
// FIXME(jdonszelmann): invert the parsing here to match on the word first and then the
|
||||
// structure.
|
||||
let (name, ident_span) = if let Some(ident) = param.path_without_args().word() {
|
||||
let (name, ident_span) = if let Some(ident) = param.path().word() {
|
||||
(Some(ident.name), ident.span)
|
||||
} else {
|
||||
(None, rustc_span::DUMMY_SP)
|
||||
(None, DUMMY_SP)
|
||||
};
|
||||
|
||||
let args = param.args();
|
||||
|
|
|
|||
|
|
@ -117,7 +117,7 @@ impl AttributeParser for BodyStabilityParser {
|
|||
pub(crate) struct ConstStabilityIndirectParser;
|
||||
// FIXME(jdonszelmann): single word attribute group when we have these
|
||||
impl SingleAttributeParser for ConstStabilityIndirectParser {
|
||||
const PATH: &'static [rustc_span::Symbol] = &[sym::rustc_const_stable_indirect];
|
||||
const PATH: &'static [Symbol] = &[sym::rustc_const_stable_indirect];
|
||||
|
||||
// ignore
|
||||
fn on_duplicate(_cx: &AcceptContext<'_>, _first_span: Span) {}
|
||||
|
|
@ -204,7 +204,7 @@ fn insert_value_into_option_or_error(
|
|||
if item.is_some() {
|
||||
cx.emit_err(session_diagnostics::MultipleItem {
|
||||
span: param.span(),
|
||||
item: param.path_without_args().to_string(),
|
||||
item: param.path().to_string(),
|
||||
});
|
||||
None
|
||||
} else if let Some(v) = param.args().name_value()
|
||||
|
|
@ -242,13 +242,13 @@ pub(crate) fn parse_stability(
|
|||
return None;
|
||||
};
|
||||
|
||||
match param.path_without_args().word_sym() {
|
||||
match param.path().word_sym() {
|
||||
Some(sym::feature) => insert_value_into_option_or_error(cx, ¶m, &mut feature)?,
|
||||
Some(sym::since) => insert_value_into_option_or_error(cx, ¶m, &mut since)?,
|
||||
_ => {
|
||||
cx.emit_err(session_diagnostics::UnknownMetaItem {
|
||||
span: param_span,
|
||||
item: param.path_without_args().to_string(),
|
||||
item: param.path().to_string(),
|
||||
expected: &["feature", "since"],
|
||||
});
|
||||
return None;
|
||||
|
|
@ -310,7 +310,7 @@ pub(crate) fn parse_unstability(
|
|||
return None;
|
||||
};
|
||||
|
||||
match param.path_without_args().word_sym() {
|
||||
match param.path().word_sym() {
|
||||
Some(sym::feature) => insert_value_into_option_or_error(cx, ¶m, &mut feature)?,
|
||||
Some(sym::reason) => insert_value_into_option_or_error(cx, ¶m, &mut reason)?,
|
||||
Some(sym::issue) => {
|
||||
|
|
@ -349,7 +349,7 @@ pub(crate) fn parse_unstability(
|
|||
_ => {
|
||||
cx.emit_err(session_diagnostics::UnknownMetaItem {
|
||||
span: param.span(),
|
||||
item: param.path_without_args().to_string(),
|
||||
item: param.path().to_string(),
|
||||
expected: &["feature", "reason", "issue", "soft", "implied_by"],
|
||||
});
|
||||
return None;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use rustc_attr_data_structures::AttributeKind;
|
||||
use rustc_span::hygiene::Transparency;
|
||||
use rustc_span::sym;
|
||||
use rustc_span::{Span, Symbol, sym};
|
||||
|
||||
use super::{AcceptContext, SingleAttributeParser};
|
||||
use crate::parser::ArgParser;
|
||||
|
|
@ -11,9 +11,9 @@ pub(crate) struct TransparencyParser;
|
|||
#[allow(rustc::untranslatable_diagnostic)]
|
||||
#[allow(rustc::diagnostic_outside_of_impl)]
|
||||
impl SingleAttributeParser for TransparencyParser {
|
||||
const PATH: &'static [rustc_span::Symbol] = &[sym::rustc_macro_transparency];
|
||||
const PATH: &'static [Symbol] = &[sym::rustc_macro_transparency];
|
||||
|
||||
fn on_duplicate(cx: &crate::context::AcceptContext<'_>, first_span: rustc_span::Span) {
|
||||
fn on_duplicate(cx: &crate::context::AcceptContext<'_>, first_span: Span) {
|
||||
cx.dcx().span_err(vec![first_span, cx.attr_span], "multiple macro transparency attributes");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -26,3 +26,33 @@ pub fn is_builtin_attr(attr: &impl AttributeExt) -> bool {
|
|||
pub fn find_crate_name(attrs: &[impl AttributeExt]) -> Option<Symbol> {
|
||||
first_attr_value_str_by_name(attrs, sym::crate_name)
|
||||
}
|
||||
|
||||
pub fn is_doc_alias_attrs_contain_symbol<'tcx, T: AttributeExt + 'tcx>(
|
||||
attrs: impl Iterator<Item = &'tcx T>,
|
||||
symbol: Symbol,
|
||||
) -> bool {
|
||||
let doc_attrs = attrs.filter(|attr| attr.has_name(sym::doc));
|
||||
for attr in doc_attrs {
|
||||
let Some(values) = attr.meta_item_list() else {
|
||||
continue;
|
||||
};
|
||||
let alias_values = values.iter().filter(|v| v.has_name(sym::alias));
|
||||
for v in alias_values {
|
||||
if let Some(nested) = v.meta_item_list() {
|
||||
// #[doc(alias("foo", "bar"))]
|
||||
let mut iter = nested.iter().filter_map(|item| item.lit()).map(|item| item.symbol);
|
||||
if iter.any(|s| s == symbol) {
|
||||
return true;
|
||||
}
|
||||
} else if let Some(meta) = v.meta_item()
|
||||
&& let Some(lit) = meta.name_value_literal()
|
||||
{
|
||||
// #[doc(alias = "foo")]
|
||||
if lit.symbol == symbol {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,12 +26,16 @@ macro_rules! attribute_groups {
|
|||
(
|
||||
pub(crate) static $name: ident = [$($names: ty),* $(,)?];
|
||||
) => {
|
||||
pub(crate) static $name: LazyLock<(
|
||||
BTreeMap<&'static [Symbol], Vec<Box<dyn Fn(&AcceptContext<'_>, &ArgParser<'_>) + Send + Sync>>>,
|
||||
Vec<Box<dyn Send + Sync + Fn(&FinalizeContext<'_>) -> Option<AttributeKind>>>
|
||||
)> = LazyLock::new(|| {
|
||||
let mut accepts = BTreeMap::<_, Vec<Box<dyn Fn(&AcceptContext<'_>, &ArgParser<'_>) + Send + Sync>>>::new();
|
||||
let mut finalizes = Vec::<Box<dyn Send + Sync + Fn(&FinalizeContext<'_>) -> Option<AttributeKind>>>::new();
|
||||
type Accepts = BTreeMap<
|
||||
&'static [Symbol],
|
||||
Box<dyn Send + Sync + Fn(&AcceptContext<'_>, &ArgParser<'_>)>
|
||||
>;
|
||||
type Finalizes = Vec<
|
||||
Box<dyn Send + Sync + Fn(&FinalizeContext<'_>) -> Option<AttributeKind>>
|
||||
>;
|
||||
pub(crate) static $name: LazyLock<(Accepts, Finalizes)> = LazyLock::new(|| {
|
||||
let mut accepts = Accepts::new();
|
||||
let mut finalizes = Finalizes::new();
|
||||
$(
|
||||
{
|
||||
thread_local! {
|
||||
|
|
@ -39,11 +43,12 @@ macro_rules! attribute_groups {
|
|||
};
|
||||
|
||||
for (k, v) in <$names>::ATTRIBUTES {
|
||||
accepts.entry(*k).or_default().push(Box::new(|cx, args| {
|
||||
let old = accepts.insert(*k, Box::new(|cx, args| {
|
||||
STATE_OBJECT.with_borrow_mut(|s| {
|
||||
v(s, cx, args)
|
||||
})
|
||||
}));
|
||||
assert!(old.is_none());
|
||||
}
|
||||
|
||||
finalizes.push(Box::new(|cx| {
|
||||
|
|
@ -110,7 +115,8 @@ impl<'a> Deref for AcceptContext<'a> {
|
|||
|
||||
/// Context given to every attribute parser during finalization.
|
||||
///
|
||||
/// Gives [`AttributeParser`](crate::attributes::AttributeParser)s enough information to create errors, for example.
|
||||
/// Gives [`AttributeParser`](crate::attributes::AttributeParser)s enough information to create
|
||||
/// errors, for example.
|
||||
pub(crate) struct FinalizeContext<'a> {
|
||||
/// The parse context, gives access to the session and the
|
||||
/// diagnostics context.
|
||||
|
|
@ -141,10 +147,9 @@ pub struct AttributeParser<'sess> {
|
|||
sess: &'sess Session,
|
||||
features: Option<&'sess Features>,
|
||||
|
||||
/// *only* parse attributes with this symbol.
|
||||
/// *Only* parse attributes with this symbol.
|
||||
///
|
||||
/// Used in cases where we want the lowering infrastructure for
|
||||
/// parse just a single attribute.
|
||||
/// Used in cases where we want the lowering infrastructure for parse just a single attribute.
|
||||
parse_only: Option<Symbol>,
|
||||
|
||||
/// Can be used to instruct parsers to reduce the number of diagnostics it emits.
|
||||
|
|
@ -157,9 +162,9 @@ impl<'sess> AttributeParser<'sess> {
|
|||
/// One example where this is necessary, is to parse `feature` attributes themselves for
|
||||
/// example.
|
||||
///
|
||||
/// Try to use this as little as possible. Attributes *should* be lowered during `rustc_ast_lowering`.
|
||||
/// Some attributes require access to features to parse, which would crash if you tried to do so
|
||||
/// through [`parse_limited`](Self::parse_limited).
|
||||
/// Try to use this as little as possible. Attributes *should* be lowered during
|
||||
/// `rustc_ast_lowering`. Some attributes require access to features to parse, which would
|
||||
/// crash if you tried to do so through [`parse_limited`](Self::parse_limited).
|
||||
///
|
||||
/// To make sure use is limited, supply a `Symbol` you'd like to parse. Only attributes with
|
||||
/// that symbol are picked out of the list of instructions and parsed. Those are returned.
|
||||
|
|
@ -217,19 +222,18 @@ impl<'sess> AttributeParser<'sess> {
|
|||
let group_cx = FinalizeContext { cx: self, target_span };
|
||||
|
||||
for attr in attrs {
|
||||
// if we're only looking for a single attribute,
|
||||
// skip all the ones we don't care about
|
||||
// If we're only looking for a single attribute, skip all the ones we don't care about.
|
||||
if let Some(expected) = self.parse_only {
|
||||
if !attr.has_name(expected) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// sometimes, for example for `#![doc = include_str!("readme.md")]`,
|
||||
// Sometimes, for example for `#![doc = include_str!("readme.md")]`,
|
||||
// doc still contains a non-literal. You might say, when we're lowering attributes
|
||||
// that's expanded right? But no, sometimes, when parsing attributes on macros,
|
||||
// we already use the lowering logic and these are still there. So, when `omit_doc`
|
||||
// is set we *also* want to ignore these
|
||||
// is set we *also* want to ignore these.
|
||||
if omit_doc == OmitDoc::Skip && attr.has_name(sym::doc) {
|
||||
continue;
|
||||
}
|
||||
|
|
@ -260,24 +264,21 @@ impl<'sess> AttributeParser<'sess> {
|
|||
// }
|
||||
ast::AttrKind::Normal(n) => {
|
||||
let parser = MetaItemParser::from_attr(n, self.dcx());
|
||||
let (path, args) = parser.deconstruct();
|
||||
let path = parser.path();
|
||||
let args = parser.args();
|
||||
let parts = path.segments().map(|i| i.name).collect::<Vec<_>>();
|
||||
|
||||
if let Some(accepts) = ATTRIBUTE_MAPPING.0.get(parts.as_slice()) {
|
||||
for f in accepts {
|
||||
let cx = AcceptContext {
|
||||
group_cx: &group_cx,
|
||||
attr_span: lower_span(attr.span),
|
||||
};
|
||||
if let Some(accept) = ATTRIBUTE_MAPPING.0.get(parts.as_slice()) {
|
||||
let cx =
|
||||
AcceptContext { group_cx: &group_cx, attr_span: lower_span(attr.span) };
|
||||
|
||||
f(&cx, &args)
|
||||
}
|
||||
accept(&cx, &args)
|
||||
} else {
|
||||
// if we're here, we must be compiling a tool attribute... Or someone forgot to
|
||||
// parse their fancy new attribute. Let's warn them in any case. If you are that
|
||||
// person, and you really your attribute should remain unparsed, carefully read the
|
||||
// documentation in this module and if you still think so you can add an exception
|
||||
// to this assertion.
|
||||
// If we're here, we must be compiling a tool attribute... Or someone
|
||||
// forgot to parse their fancy new attribute. Let's warn them in any case.
|
||||
// If you are that person, and you really think your attribute should
|
||||
// remain unparsed, carefully read the documentation in this module and if
|
||||
// you still think so you can add an exception to this assertion.
|
||||
|
||||
// FIXME(jdonszelmann): convert other attributes, and check with this that
|
||||
// we caught em all
|
||||
|
|
|
|||
|
|
@ -1,31 +1,38 @@
|
|||
//! Centralized logic for parsing and attributes.
|
||||
//!
|
||||
//! Part of a series of crates:
|
||||
//! - rustc_attr_data_structures: contains types that the parsers parse into
|
||||
//! - rustc_attr_parsing: this crate
|
||||
//! - (in the future): rustc_attr_validation
|
||||
//! ## Architecture
|
||||
//! This crate is part of a series of crates that handle attribute processing.
|
||||
//! - [rustc_attr_data_structures](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_attr_data_structures/index.html): Defines the data structures that store parsed attributes
|
||||
//! - [rustc_attr_parsing](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_attr_parsing/index.html): This crate, handles the parsing of attributes
|
||||
//! - (planned) rustc_attr_validation: Will handle attribute validation
|
||||
//!
|
||||
//! History: Check out [#131229](https://github.com/rust-lang/rust/issues/131229).
|
||||
//! There used to be only one definition of attributes in the compiler: `ast::Attribute`.
|
||||
//! These were then parsed or validated or both in places distributed all over the compiler.
|
||||
//! This was a mess...
|
||||
//! The separation between data structures and parsing follows the principle of separation of concerns.
|
||||
//! Data structures (`rustc_attr_data_structures`) define what attributes look like after parsing.
|
||||
//! This crate (`rustc_attr_parsing`) handles how to convert raw tokens into those structures.
|
||||
//! This split allows other parts of the compiler to use the data structures without needing
|
||||
//! the parsing logic, making the codebase more modular and maintainable.
|
||||
//!
|
||||
//! Attributes are markers on items.
|
||||
//! Many of them are actually attribute-like proc-macros, and are expanded to some other rust syntax.
|
||||
//! This could either be a user provided proc macro, or something compiler provided.
|
||||
//! `derive` is an example of one that the compiler provides.
|
||||
//! These are built-in, but they have a valid expansion to Rust tokens and are thus called "active".
|
||||
//! I personally like calling these *active* compiler-provided attributes, built-in *macros*,
|
||||
//! because they still expand, and this helps to differentiate them from built-in *attributes*.
|
||||
//! However, I'll be the first to admit that the naming here can be confusing.
|
||||
//! ## Background
|
||||
//! Previously, the compiler had a single attribute definition (`ast::Attribute`) with parsing and
|
||||
//! validation scattered throughout the codebase. This was reorganized for better maintainability
|
||||
//! (see [#131229](https://github.com/rust-lang/rust/issues/131229)).
|
||||
//!
|
||||
//! The alternative to active attributes, are inert attributes.
|
||||
//! These can occur in user code (proc-macro helper attributes).
|
||||
//! But what's important is, many built-in attributes are inert like this.
|
||||
//! There is nothing they expand to during the macro expansion process,
|
||||
//! sometimes because they literally cannot expand to something that is valid Rust.
|
||||
//! They are really just markers to guide the compilation process.
|
||||
//! An example is `#[inline(...)]` which changes how code for functions is generated.
|
||||
//! ## Types of Attributes
|
||||
//! In Rust, attributes are markers that can be attached to items. They come in two main categories.
|
||||
//!
|
||||
//! ### 1. Active Attributes
|
||||
//! These are attribute-like proc-macros that expand into other Rust code.
|
||||
//! They can be either user-defined or compiler-provided. Examples of compiler-provided active attributes:
|
||||
//! - `#[derive(...)]`: Expands into trait implementations
|
||||
//! - `#[cfg()]`: Expands based on configuration
|
||||
//! - `#[cfg_attr()]`: Conditional attribute application
|
||||
//!
|
||||
//! ### 2. Inert Attributes
|
||||
//! These are pure markers that don't expand into other code. They guide the compilation process.
|
||||
//! They can be user-defined (in proc-macro helpers) or built-in. Examples of built-in inert attributes:
|
||||
//! - `#[stable()]`: Marks stable API items
|
||||
//! - `#[inline()]`: Suggests function inlining
|
||||
//! - `#[repr()]`: Controls type representation
|
||||
//!
|
||||
//! ```text
|
||||
//! Active Inert
|
||||
|
|
@ -33,27 +40,21 @@
|
|||
//! │ (mostly in) │ these are parsed │
|
||||
//! │ rustc_builtin_macros │ here! │
|
||||
//! │ │ │
|
||||
//! │ │ │
|
||||
//! │ #[derive(...)] │ #[stable()] │
|
||||
//! Built-in │ #[cfg()] │ #[inline()] │
|
||||
//! │ #[cfg_attr()] │ #[repr()] │
|
||||
//! │ │ │
|
||||
//! │ │ │
|
||||
//! │ │ │
|
||||
//! ├──────────────────────┼──────────────────────┤
|
||||
//! │ │ │
|
||||
//! │ │ │
|
||||
//! │ │ `b` in │
|
||||
//! │ │ #[proc_macro_derive( │
|
||||
//! User created │ #[proc_macro_attr()] │ a, │
|
||||
//! │ │ attributes(b) │
|
||||
//! │ │ ] │
|
||||
//! │ │ │
|
||||
//! │ │ │
|
||||
//! │ │ │
|
||||
//! └──────────────────────┴──────────────────────┘
|
||||
//! ```
|
||||
//!
|
||||
//! ## How This Crate Works
|
||||
//! In this crate, syntactical attributes (sequences of tokens that look like
|
||||
//! `#[something(something else)]`) are parsed into more semantic attributes, markers on items.
|
||||
//! Multiple syntactic attributes might influence a single semantic attribute. For example,
|
||||
|
|
@ -63,18 +64,17 @@
|
|||
//! and `#[unstable()]` syntactic attributes, and at the end produce a single
|
||||
//! [`AttributeKind::Stability`](rustc_attr_data_structures::AttributeKind::Stability).
|
||||
//!
|
||||
//! As a rule of thumb, when a syntactical attribute can be applied more than once, they should be
|
||||
//! combined into a single semantic attribute. For example:
|
||||
//! When multiple instances of the same attribute are allowed, they're combined into a single
|
||||
//! semantic attribute. For example:
|
||||
//!
|
||||
//! ```
|
||||
//! ```rust
|
||||
//! #[repr(C)]
|
||||
//! #[repr(packed)]
|
||||
//! struct Meow {}
|
||||
//! ```
|
||||
//!
|
||||
//! should result in a single `AttributeKind::Repr` containing a list of repr annotations, in this
|
||||
//! case `C` and `packed`. This is equivalent to writing `#[repr(C, packed)]` in a single
|
||||
//! syntactical annotation.
|
||||
//! This is equivalent to `#[repr(C, packed)]` and results in a single `AttributeKind::Repr`
|
||||
//! containing both `C` and `packed` annotations.
|
||||
|
||||
// tidy-alphabetical-start
|
||||
#![allow(internal_features)]
|
||||
|
|
@ -90,7 +90,9 @@ pub mod parser;
|
|||
mod session_diagnostics;
|
||||
|
||||
pub use attributes::cfg::*;
|
||||
pub use attributes::util::{find_crate_name, is_builtin_attr, parse_version};
|
||||
pub use attributes::util::{
|
||||
find_crate_name, is_builtin_attr, is_doc_alias_attrs_contain_symbol, parse_version,
|
||||
};
|
||||
pub use context::{AttributeParser, OmitDoc};
|
||||
|
||||
rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
|
||||
|
|
|
|||
|
|
@ -12,8 +12,7 @@ use rustc_ast::{AttrArgs, DelimArgs, Expr, ExprKind, LitKind, MetaItemLit, Norma
|
|||
use rustc_ast_pretty::pprust;
|
||||
use rustc_errors::DiagCtxtHandle;
|
||||
use rustc_hir::{self as hir, AttrPath};
|
||||
use rustc_span::symbol::{Ident, kw, sym};
|
||||
use rustc_span::{ErrorGuaranteed, Span, Symbol};
|
||||
use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol, kw, sym};
|
||||
|
||||
pub struct SegmentIterator<'a> {
|
||||
offset: usize,
|
||||
|
|
@ -253,9 +252,13 @@ impl<'a> MetaItemParser<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Gets just the path, without the args.
|
||||
pub fn path_without_args(&self) -> PathParser<'a> {
|
||||
self.path.clone()
|
||||
/// Gets just the path, without the args. Some examples:
|
||||
///
|
||||
/// - `#[rustfmt::skip]`: `rustfmt::skip` is a path
|
||||
/// - `#[allow(clippy::complexity)]`: `clippy::complexity` is a path
|
||||
/// - `#[inline]`: `inline` is a single segment path
|
||||
pub fn path(&self) -> &PathParser<'a> {
|
||||
&self.path
|
||||
}
|
||||
|
||||
/// Gets just the args parser, without caring about the path.
|
||||
|
|
@ -263,50 +266,14 @@ impl<'a> MetaItemParser<'a> {
|
|||
&self.args
|
||||
}
|
||||
|
||||
pub fn deconstruct(&self) -> (PathParser<'a>, &ArgParser<'a>) {
|
||||
(self.path_without_args(), self.args())
|
||||
}
|
||||
|
||||
/// Asserts that this MetaItem starts with a path. Some examples:
|
||||
///
|
||||
/// - `#[rustfmt::skip]`: `rustfmt::skip` is a path
|
||||
/// - `#[allow(clippy::complexity)]`: `clippy::complexity` is a path
|
||||
/// - `#[inline]`: `inline` is a single segment path
|
||||
pub fn path(&self) -> (PathParser<'a>, &ArgParser<'a>) {
|
||||
self.deconstruct()
|
||||
}
|
||||
|
||||
/// Asserts that this MetaItem starts with a word, or single segment path.
|
||||
/// Doesn't return the args parser.
|
||||
///
|
||||
/// For examples. see [`Self::word`]
|
||||
pub fn word_without_args(&self) -> Option<Ident> {
|
||||
Some(self.word()?.0)
|
||||
}
|
||||
|
||||
/// Asserts that this MetaItem starts with a word, or single segment path.
|
||||
///
|
||||
/// Some examples:
|
||||
/// - `#[inline]`: `inline` is a word
|
||||
/// - `#[rustfmt::skip]`: `rustfmt::skip` is a path,
|
||||
/// and not a word and should instead be parsed using [`path`](Self::path)
|
||||
pub fn word(&self) -> Option<(Ident, &ArgParser<'a>)> {
|
||||
let (path, args) = self.deconstruct();
|
||||
Some((path.word()?, args))
|
||||
}
|
||||
|
||||
/// Asserts that this MetaItem starts with some specific word.
|
||||
///
|
||||
/// See [`word`](Self::word) for examples of what a word is.
|
||||
pub fn word_is(&self, sym: Symbol) -> Option<&ArgParser<'a>> {
|
||||
self.path_without_args().word_is(sym).then(|| self.args())
|
||||
}
|
||||
|
||||
/// Asserts that this MetaItem starts with some specific path.
|
||||
///
|
||||
/// See [`word`](Self::path) for examples of what a word is.
|
||||
pub fn path_is(&self, segments: &[Symbol]) -> Option<&ArgParser<'a>> {
|
||||
self.path_without_args().segments_is(segments).then(|| self.args())
|
||||
self.path().word_is(sym).then(|| self.args())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -549,7 +516,7 @@ impl<'a> MetaItemListParser<'a> {
|
|||
}
|
||||
|
||||
/// Lets you pick and choose as what you want to parse each element in the list
|
||||
pub fn mixed<'s>(&'s self) -> impl Iterator<Item = &'s MetaItemOrLitParser<'a>> + 's {
|
||||
pub fn mixed(&self) -> impl Iterator<Item = &MetaItemOrLitParser<'a>> {
|
||||
self.sub_parsers.iter()
|
||||
}
|
||||
|
||||
|
|
@ -561,20 +528,6 @@ impl<'a> MetaItemListParser<'a> {
|
|||
self.len() == 0
|
||||
}
|
||||
|
||||
/// Asserts that every item in the list is another list starting with a word.
|
||||
///
|
||||
/// See [`MetaItemParser::word`] for examples of words.
|
||||
pub fn all_word_list<'s>(&'s self) -> Option<Vec<(Ident, &'s ArgParser<'a>)>> {
|
||||
self.mixed().map(|i| i.meta_item()?.word()).collect()
|
||||
}
|
||||
|
||||
/// Asserts that every item in the list is another list starting with a full path.
|
||||
///
|
||||
/// See [`MetaItemParser::path`] for examples of paths.
|
||||
pub fn all_path_list<'s>(&'s self) -> Option<Vec<(PathParser<'a>, &'s ArgParser<'a>)>> {
|
||||
self.mixed().map(|i| Some(i.meta_item()?.path())).collect()
|
||||
}
|
||||
|
||||
/// Returns Some if the list contains only a single element.
|
||||
///
|
||||
/// Inside the Some is the parser to parse this single element.
|
||||
|
|
|
|||
|
|
@ -5,11 +5,9 @@ use rustc_index::{IndexSlice, IndexVec};
|
|||
use rustc_middle::mir::ConstraintCategory;
|
||||
use rustc_middle::ty::{RegionVid, TyCtxt, VarianceDiagInfo};
|
||||
use rustc_span::Span;
|
||||
use tracing::{debug, instrument};
|
||||
use tracing::debug;
|
||||
|
||||
use crate::region_infer::{AnnotatedSccs, ConstraintSccs, RegionDefinition, SccAnnotations};
|
||||
use crate::type_check::Locations;
|
||||
use crate::universal_regions::UniversalRegions;
|
||||
|
||||
pub(crate) mod graph;
|
||||
|
||||
|
|
@ -53,112 +51,6 @@ impl<'tcx> OutlivesConstraintSet<'tcx> {
|
|||
) -> &IndexSlice<OutlivesConstraintIndex, OutlivesConstraint<'tcx>> {
|
||||
&self.outlives
|
||||
}
|
||||
|
||||
/// Computes cycles (SCCs) in the graph of regions. In particular,
|
||||
/// find all regions R1, R2 such that R1: R2 and R2: R1 and group
|
||||
/// them into an SCC, and find the relationships between SCCs.
|
||||
pub(crate) fn compute_sccs(
|
||||
&self,
|
||||
static_region: RegionVid,
|
||||
definitions: &IndexVec<RegionVid, RegionDefinition<'tcx>>,
|
||||
) -> AnnotatedSccs {
|
||||
let constraint_graph = self.graph(definitions.len());
|
||||
let region_graph = &constraint_graph.region_graph(self, static_region);
|
||||
let mut annotation_visitor = SccAnnotations::new(definitions);
|
||||
(
|
||||
ConstraintSccs::new_with_annotation(®ion_graph, &mut annotation_visitor),
|
||||
annotation_visitor.scc_to_annotation,
|
||||
)
|
||||
}
|
||||
|
||||
/// This method handles Universe errors by rewriting the constraint
|
||||
/// graph. For each strongly connected component in the constraint
|
||||
/// graph such that there is a series of constraints
|
||||
/// A: B: C: ... : X where
|
||||
/// A's universe is smaller than X's and A is a placeholder,
|
||||
/// add a constraint that A: 'static. This is a safe upper bound
|
||||
/// in the face of borrow checker/trait solver limitations that will
|
||||
/// eventually go away.
|
||||
///
|
||||
/// For a more precise definition, see the documentation for
|
||||
/// [`crate::region_infer::RegionTracker`].
|
||||
///
|
||||
/// This edge case used to be handled during constraint propagation
|
||||
/// by iterating over the strongly connected components in the constraint
|
||||
/// graph while maintaining a set of bookkeeping mappings similar
|
||||
/// to what is stored in `RegionTracker` and manually adding 'static as
|
||||
/// needed.
|
||||
///
|
||||
/// It was rewritten as part of the Polonius project with the goal of moving
|
||||
/// higher-kindedness concerns out of the path of the borrow checker,
|
||||
/// for two reasons:
|
||||
///
|
||||
/// 1. Implementing Polonius is difficult enough without also
|
||||
/// handling them.
|
||||
/// 2. The long-term goal is to handle higher-kinded concerns
|
||||
/// in the trait solver, where they belong. This avoids
|
||||
/// logic duplication and allows future trait solvers
|
||||
/// to compute better bounds than for example our
|
||||
/// "must outlive 'static" here.
|
||||
///
|
||||
/// This code is a stop-gap measure in preparation for the future trait solver.
|
||||
///
|
||||
/// Every constraint added by this method is an
|
||||
/// internal `IllegalUniverse` constraint.
|
||||
#[instrument(skip(self, universal_regions, definitions))]
|
||||
pub(crate) fn add_outlives_static(
|
||||
&mut self,
|
||||
universal_regions: &UniversalRegions<'tcx>,
|
||||
definitions: &IndexVec<RegionVid, RegionDefinition<'tcx>>,
|
||||
) -> AnnotatedSccs {
|
||||
let fr_static = universal_regions.fr_static;
|
||||
let (sccs, annotations) = self.compute_sccs(fr_static, definitions);
|
||||
|
||||
// Changed to `true` if we added any constraints to `self` and need to
|
||||
// recompute SCCs.
|
||||
let mut added_constraints = false;
|
||||
|
||||
for scc in sccs.all_sccs() {
|
||||
// No point in adding 'static: 'static!
|
||||
// This micro-optimisation makes somewhat sense
|
||||
// because static outlives *everything*.
|
||||
if scc == sccs.scc(fr_static) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let annotation = annotations[scc];
|
||||
|
||||
// If this SCC participates in a universe violation,
|
||||
// e.g. if it reaches a region with a universe smaller than
|
||||
// the largest region reached, add a requirement that it must
|
||||
// outlive `'static`.
|
||||
if annotation.has_incompatible_universes() {
|
||||
// Optimisation opportunity: this will add more constraints than
|
||||
// needed for correctness, since an SCC upstream of another with
|
||||
// a universe violation will "infect" its downstream SCCs to also
|
||||
// outlive static.
|
||||
added_constraints = true;
|
||||
let scc_representative_outlives_static = OutlivesConstraint {
|
||||
sup: annotation.representative,
|
||||
sub: fr_static,
|
||||
category: ConstraintCategory::IllegalUniverse,
|
||||
locations: Locations::All(rustc_span::DUMMY_SP),
|
||||
span: rustc_span::DUMMY_SP,
|
||||
variance_info: VarianceDiagInfo::None,
|
||||
from_closure: false,
|
||||
};
|
||||
self.push(scc_representative_outlives_static);
|
||||
}
|
||||
}
|
||||
|
||||
if added_constraints {
|
||||
// We changed the constraint set and so must recompute SCCs.
|
||||
self.compute_sccs(fr_static, definitions)
|
||||
} else {
|
||||
// If we didn't add any back-edges; no more work needs doing
|
||||
(sccs, annotations)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Index<OutlivesConstraintIndex> for OutlivesConstraintSet<'tcx> {
|
||||
|
|
|
|||
|
|
@ -263,7 +263,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
// something that already has `Fn`-like bounds (or is a closure), so we can't
|
||||
// restrict anyways.
|
||||
} else {
|
||||
let copy_did = self.infcx.tcx.require_lang_item(LangItem::Copy, Some(span));
|
||||
let copy_did = self.infcx.tcx.require_lang_item(LangItem::Copy, span);
|
||||
self.suggest_adding_bounds(&mut err, ty, copy_did, span);
|
||||
}
|
||||
|
||||
|
|
@ -1915,7 +1915,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
|
||||
let local_ty = self.body.local_decls[place.local].ty;
|
||||
let typeck_results = tcx.typeck(self.mir_def_id());
|
||||
let clone = tcx.require_lang_item(LangItem::Clone, Some(body.span));
|
||||
let clone = tcx.require_lang_item(LangItem::Clone, body.span);
|
||||
for expr in expr_finder.clones {
|
||||
if let hir::ExprKind::MethodCall(_, rcvr, _, span) = expr.kind
|
||||
&& let Some(rcvr_ty) = typeck_results.node_type_opt(rcvr.hir_id)
|
||||
|
|
@ -3314,7 +3314,13 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
"function parameter".to_string(),
|
||||
"function parameter borrowed here".to_string(),
|
||||
),
|
||||
LocalKind::Temp if self.body.local_decls[local].is_user_variable() => {
|
||||
LocalKind::Temp
|
||||
if self.body.local_decls[local].is_user_variable()
|
||||
&& !self.body.local_decls[local]
|
||||
.source_info
|
||||
.span
|
||||
.in_external_macro(self.infcx.tcx.sess.source_map()) =>
|
||||
{
|
||||
("local binding".to_string(), "local binding introduced here".to_string())
|
||||
}
|
||||
LocalKind::ReturnPointer | LocalKind::Temp => {
|
||||
|
|
|
|||
|
|
@ -840,14 +840,22 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
} else {
|
||||
bug!("not an upvar")
|
||||
};
|
||||
err.span_label(
|
||||
*span,
|
||||
format!(
|
||||
"calling `{}` requires mutable binding due to {}",
|
||||
self.describe_place(the_place_err).unwrap(),
|
||||
reason
|
||||
),
|
||||
);
|
||||
// sometimes we deliberately don't store the name of a place when coming from a macro in
|
||||
// another crate. We generally want to limit those diagnostics a little, to hide
|
||||
// implementation details (such as those from pin!() or format!()). In that case show a
|
||||
// slightly different error message, or none at all if something else happened. In other
|
||||
// cases the message is likely not useful.
|
||||
if let Some(place_name) = self.describe_place(the_place_err) {
|
||||
err.span_label(
|
||||
*span,
|
||||
format!("calling `{place_name}` requires mutable binding due to {reason}"),
|
||||
);
|
||||
} else if span.from_expansion() {
|
||||
err.span_label(
|
||||
*span,
|
||||
format!("a call in this macro requires a mutable binding due to {reason}",),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -606,8 +606,8 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
|
|||
hir_args: &'hir hir::GenericArgs<'hir>,
|
||||
search_stack: &mut Vec<(Ty<'tcx>, &'hir hir::Ty<'hir>)>,
|
||||
) -> Option<&'hir hir::Lifetime> {
|
||||
for (kind, hir_arg) in iter::zip(args, hir_args.args) {
|
||||
match (kind.unpack(), hir_arg) {
|
||||
for (arg, hir_arg) in iter::zip(args, hir_args.args) {
|
||||
match (arg.kind(), hir_arg) {
|
||||
(GenericArgKind::Lifetime(r), hir::GenericArg::Lifetime(lt)) => {
|
||||
if r.as_var() == needle_fr {
|
||||
return Some(lt);
|
||||
|
|
@ -631,7 +631,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
|
|||
) => {
|
||||
self.dcx().span_delayed_bug(
|
||||
hir_arg.span(),
|
||||
format!("unmatched arg and hir arg: found {kind:?} vs {hir_arg:?}"),
|
||||
format!("unmatched arg and hir arg: found {arg:?} vs {hir_arg:?}"),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -997,7 +997,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
|
|||
) -> bool {
|
||||
let tcx = self.infcx.tcx;
|
||||
ty.walk().any(|arg| {
|
||||
if let ty::GenericArgKind::Type(ty) = arg.unpack()
|
||||
if let ty::GenericArgKind::Type(ty) = arg.kind()
|
||||
&& let ty::Param(_) = ty.kind()
|
||||
{
|
||||
clauses.iter().any(|pred| {
|
||||
|
|
|
|||
348
compiler/rustc_borrowck/src/handle_placeholders.rs
Normal file
348
compiler/rustc_borrowck/src/handle_placeholders.rs
Normal file
|
|
@ -0,0 +1,348 @@
|
|||
//! Logic for lowering higher-kinded outlives constraints
|
||||
//! (with placeholders and universes) and turn them into regular
|
||||
//! outlives constraints.
|
||||
|
||||
use rustc_data_structures::frozen::Frozen;
|
||||
use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_data_structures::graph::scc;
|
||||
use rustc_data_structures::graph::scc::Sccs;
|
||||
use rustc_index::IndexVec;
|
||||
use rustc_infer::infer::RegionVariableOrigin;
|
||||
use rustc_middle::mir::ConstraintCategory;
|
||||
use rustc_middle::ty::{RegionVid, UniverseIndex};
|
||||
use tracing::debug;
|
||||
|
||||
use crate::constraints::{ConstraintSccIndex, OutlivesConstraintSet};
|
||||
use crate::consumers::OutlivesConstraint;
|
||||
use crate::diagnostics::UniverseInfo;
|
||||
use crate::member_constraints::MemberConstraintSet;
|
||||
use crate::region_infer::values::{LivenessValues, PlaceholderIndices};
|
||||
use crate::region_infer::{ConstraintSccs, RegionDefinition, Representative, TypeTest};
|
||||
use crate::ty::VarianceDiagInfo;
|
||||
use crate::type_check::free_region_relations::UniversalRegionRelations;
|
||||
use crate::type_check::{Locations, MirTypeckRegionConstraints};
|
||||
use crate::universal_regions::UniversalRegions;
|
||||
use crate::{BorrowckInferCtxt, NllRegionVariableOrigin};
|
||||
|
||||
/// A set of outlives constraints after rewriting to remove
|
||||
/// higher-kinded constraints.
|
||||
pub(crate) struct LoweredConstraints<'tcx> {
|
||||
pub(crate) constraint_sccs: Sccs<RegionVid, ConstraintSccIndex>,
|
||||
pub(crate) definitions: Frozen<IndexVec<RegionVid, RegionDefinition<'tcx>>>,
|
||||
pub(crate) scc_annotations: IndexVec<ConstraintSccIndex, RegionTracker>,
|
||||
pub(crate) member_constraints: MemberConstraintSet<'tcx, RegionVid>,
|
||||
pub(crate) outlives_constraints: Frozen<OutlivesConstraintSet<'tcx>>,
|
||||
pub(crate) type_tests: Vec<TypeTest<'tcx>>,
|
||||
pub(crate) liveness_constraints: LivenessValues,
|
||||
pub(crate) universe_causes: FxIndexMap<UniverseIndex, UniverseInfo<'tcx>>,
|
||||
pub(crate) placeholder_indices: PlaceholderIndices,
|
||||
}
|
||||
|
||||
impl<'d, 'tcx, A: scc::Annotation> SccAnnotations<'d, 'tcx, A> {
|
||||
pub(crate) fn init(definitions: &'d IndexVec<RegionVid, RegionDefinition<'tcx>>) -> Self {
|
||||
Self { scc_to_annotation: IndexVec::new(), definitions }
|
||||
}
|
||||
}
|
||||
|
||||
/// A Visitor for SCC annotation construction.
|
||||
pub(crate) struct SccAnnotations<'d, 'tcx, A: scc::Annotation> {
|
||||
pub(crate) scc_to_annotation: IndexVec<ConstraintSccIndex, A>,
|
||||
definitions: &'d IndexVec<RegionVid, RegionDefinition<'tcx>>,
|
||||
}
|
||||
|
||||
impl scc::Annotations<RegionVid> for SccAnnotations<'_, '_, RegionTracker> {
|
||||
fn new(&self, element: RegionVid) -> RegionTracker {
|
||||
RegionTracker::new(element, &self.definitions[element])
|
||||
}
|
||||
|
||||
fn annotate_scc(&mut self, scc: ConstraintSccIndex, annotation: RegionTracker) {
|
||||
let idx = self.scc_to_annotation.push(annotation);
|
||||
assert!(idx == scc);
|
||||
}
|
||||
|
||||
type Ann = RegionTracker;
|
||||
type SccIdx = ConstraintSccIndex;
|
||||
}
|
||||
|
||||
/// An annotation for region graph SCCs that tracks
|
||||
/// the values of its elements. This annotates a single SCC.
|
||||
#[derive(Copy, Debug, Clone)]
|
||||
pub(crate) struct RegionTracker {
|
||||
/// The largest universe of a placeholder reached from this SCC.
|
||||
/// This includes placeholders within this SCC.
|
||||
max_placeholder_universe_reached: UniverseIndex,
|
||||
|
||||
/// The largest universe nameable from this SCC.
|
||||
/// It is the smallest nameable universes of all
|
||||
/// existential regions reachable from it.
|
||||
max_nameable_universe: UniverseIndex,
|
||||
|
||||
/// The representative Region Variable Id for this SCC.
|
||||
pub(crate) representative: Representative,
|
||||
}
|
||||
|
||||
impl RegionTracker {
|
||||
pub(crate) fn new(rvid: RegionVid, definition: &RegionDefinition<'_>) -> Self {
|
||||
let placeholder_universe =
|
||||
if matches!(definition.origin, NllRegionVariableOrigin::Placeholder(_)) {
|
||||
definition.universe
|
||||
} else {
|
||||
UniverseIndex::ROOT
|
||||
};
|
||||
|
||||
Self {
|
||||
max_placeholder_universe_reached: placeholder_universe,
|
||||
max_nameable_universe: definition.universe,
|
||||
representative: Representative::new(rvid, definition),
|
||||
}
|
||||
}
|
||||
|
||||
/// The largest universe this SCC can name. It's the smallest
|
||||
/// largest nameable uninverse of any reachable region.
|
||||
pub(crate) fn max_nameable_universe(self) -> UniverseIndex {
|
||||
self.max_nameable_universe
|
||||
}
|
||||
|
||||
fn merge_min_max_seen(&mut self, other: &Self) {
|
||||
self.max_placeholder_universe_reached = std::cmp::max(
|
||||
self.max_placeholder_universe_reached,
|
||||
other.max_placeholder_universe_reached,
|
||||
);
|
||||
|
||||
self.max_nameable_universe =
|
||||
std::cmp::min(self.max_nameable_universe, other.max_nameable_universe);
|
||||
}
|
||||
|
||||
/// Returns `true` if during the annotated SCC reaches a placeholder
|
||||
/// with a universe larger than the smallest nameable universe of any
|
||||
/// reachable existential region.
|
||||
pub(crate) fn has_incompatible_universes(&self) -> bool {
|
||||
self.max_nameable_universe().cannot_name(self.max_placeholder_universe_reached)
|
||||
}
|
||||
|
||||
/// Determine if the tracked universes of the two SCCs are compatible.
|
||||
pub(crate) fn universe_compatible_with(&self, other: Self) -> bool {
|
||||
self.max_nameable_universe().can_name(other.max_nameable_universe())
|
||||
|| self.max_nameable_universe().can_name(other.max_placeholder_universe_reached)
|
||||
}
|
||||
}
|
||||
|
||||
impl scc::Annotation for RegionTracker {
|
||||
fn merge_scc(mut self, other: Self) -> Self {
|
||||
self.representative = self.representative.merge_scc(other.representative);
|
||||
self.merge_min_max_seen(&other);
|
||||
self
|
||||
}
|
||||
|
||||
fn merge_reached(mut self, other: Self) -> Self {
|
||||
// No update to in-component values, only add seen values.
|
||||
self.merge_min_max_seen(&other);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Determines if the region variable definitions contain
|
||||
/// placeholders, and compute them for later use.
|
||||
fn region_definitions<'tcx>(
|
||||
universal_regions: &UniversalRegions<'tcx>,
|
||||
infcx: &BorrowckInferCtxt<'tcx>,
|
||||
) -> (Frozen<IndexVec<RegionVid, RegionDefinition<'tcx>>>, bool) {
|
||||
let var_infos = infcx.get_region_var_infos();
|
||||
// Create a RegionDefinition for each inference variable. This happens here because
|
||||
// it allows us to sneak in a cheap check for placeholders. Otherwise, its proper home
|
||||
// is in `RegionInferenceContext::new()`, probably.
|
||||
let mut definitions = IndexVec::with_capacity(var_infos.len());
|
||||
let mut has_placeholders = false;
|
||||
|
||||
for info in var_infos.iter() {
|
||||
let origin = match info.origin {
|
||||
RegionVariableOrigin::Nll(origin) => origin,
|
||||
_ => NllRegionVariableOrigin::Existential { from_forall: false },
|
||||
};
|
||||
|
||||
let definition = RegionDefinition { origin, universe: info.universe, external_name: None };
|
||||
|
||||
has_placeholders |= matches!(origin, NllRegionVariableOrigin::Placeholder(_));
|
||||
definitions.push(definition);
|
||||
}
|
||||
|
||||
// Add external names from universal regions in fun function definitions.
|
||||
// FIXME: this two-step method is annoying, but I don't know how to avoid it.
|
||||
for (external_name, variable) in universal_regions.named_universal_regions_iter() {
|
||||
debug!("region {:?} has external name {:?}", variable, external_name);
|
||||
definitions[variable].external_name = Some(external_name);
|
||||
}
|
||||
(Frozen::freeze(definitions), has_placeholders)
|
||||
}
|
||||
|
||||
/// This method handles placeholders by rewriting the constraint
|
||||
/// graph. For each strongly connected component in the constraint
|
||||
/// graph such that there is a series of constraints
|
||||
/// A: B: C: ... : X where
|
||||
/// A contains a placeholder whose universe cannot be named by X,
|
||||
/// add a constraint that A: 'static. This is a safe upper bound
|
||||
/// in the face of borrow checker/trait solver limitations that will
|
||||
/// eventually go away.
|
||||
///
|
||||
/// For a more precise definition, see the documentation for
|
||||
/// [`RegionTracker`] and its methods!
|
||||
///
|
||||
/// This edge case used to be handled during constraint propagation.
|
||||
/// It was rewritten as part of the Polonius project with the goal of moving
|
||||
/// higher-kindedness concerns out of the path of the borrow checker,
|
||||
/// for two reasons:
|
||||
///
|
||||
/// 1. Implementing Polonius is difficult enough without also
|
||||
/// handling them.
|
||||
/// 2. The long-term goal is to handle higher-kinded concerns
|
||||
/// in the trait solver, where they belong. This avoids
|
||||
/// logic duplication and allows future trait solvers
|
||||
/// to compute better bounds than for example our
|
||||
/// "must outlive 'static" here.
|
||||
///
|
||||
/// This code is a stop-gap measure in preparation for the future trait solver.
|
||||
///
|
||||
/// Every constraint added by this method is an internal `IllegalUniverse` constraint.
|
||||
pub(crate) fn compute_sccs_applying_placeholder_outlives_constraints<'tcx>(
|
||||
constraints: MirTypeckRegionConstraints<'tcx>,
|
||||
universal_region_relations: &Frozen<UniversalRegionRelations<'tcx>>,
|
||||
infcx: &BorrowckInferCtxt<'tcx>,
|
||||
) -> LoweredConstraints<'tcx> {
|
||||
let universal_regions = &universal_region_relations.universal_regions;
|
||||
let (definitions, has_placeholders) = region_definitions(universal_regions, infcx);
|
||||
|
||||
let MirTypeckRegionConstraints {
|
||||
placeholder_indices,
|
||||
placeholder_index_to_region: _,
|
||||
liveness_constraints,
|
||||
mut outlives_constraints,
|
||||
mut member_constraints,
|
||||
universe_causes,
|
||||
type_tests,
|
||||
} = constraints;
|
||||
|
||||
if let Some(guar) = universal_regions.tainted_by_errors() {
|
||||
debug!("Universal regions tainted by errors; removing constraints!");
|
||||
// Suppress unhelpful extra errors in `infer_opaque_types` by clearing out all
|
||||
// outlives bounds that we may end up checking.
|
||||
outlives_constraints = Default::default();
|
||||
member_constraints = Default::default();
|
||||
|
||||
// Also taint the entire scope.
|
||||
infcx.set_tainted_by_errors(guar);
|
||||
}
|
||||
|
||||
let fr_static = universal_regions.fr_static;
|
||||
let compute_sccs =
|
||||
|constraints: &OutlivesConstraintSet<'tcx>,
|
||||
annotations: &mut SccAnnotations<'_, 'tcx, RegionTracker>| {
|
||||
ConstraintSccs::new_with_annotation(
|
||||
&constraints.graph(definitions.len()).region_graph(constraints, fr_static),
|
||||
annotations,
|
||||
)
|
||||
};
|
||||
|
||||
let mut scc_annotations = SccAnnotations::init(&definitions);
|
||||
let constraint_sccs = compute_sccs(&outlives_constraints, &mut scc_annotations);
|
||||
|
||||
// This code structure is a bit convoluted because it allows for a planned
|
||||
// future change where the early return here has a different type of annotation
|
||||
// that does much less work.
|
||||
if !has_placeholders {
|
||||
debug!("No placeholder regions found; skipping rewriting logic!");
|
||||
|
||||
return LoweredConstraints {
|
||||
type_tests,
|
||||
member_constraints,
|
||||
constraint_sccs,
|
||||
scc_annotations: scc_annotations.scc_to_annotation,
|
||||
definitions,
|
||||
outlives_constraints: Frozen::freeze(outlives_constraints),
|
||||
liveness_constraints,
|
||||
universe_causes,
|
||||
placeholder_indices,
|
||||
};
|
||||
}
|
||||
debug!("Placeholders present; activating placeholder handling logic!");
|
||||
|
||||
let added_constraints = rewrite_placeholder_outlives(
|
||||
&constraint_sccs,
|
||||
&scc_annotations,
|
||||
fr_static,
|
||||
&mut outlives_constraints,
|
||||
);
|
||||
|
||||
let (constraint_sccs, scc_annotations) = if added_constraints {
|
||||
let mut annotations = SccAnnotations::init(&definitions);
|
||||
|
||||
// We changed the constraint set and so must recompute SCCs.
|
||||
// Optimisation opportunity: if we can add them incrementally (and that's
|
||||
// possible because edges to 'static always only merge SCCs into 'static),
|
||||
// we would potentially save a lot of work here.
|
||||
(compute_sccs(&outlives_constraints, &mut annotations), annotations.scc_to_annotation)
|
||||
} else {
|
||||
// If we didn't add any back-edges; no more work needs doing
|
||||
debug!("No constraints rewritten!");
|
||||
(constraint_sccs, scc_annotations.scc_to_annotation)
|
||||
};
|
||||
|
||||
LoweredConstraints {
|
||||
constraint_sccs,
|
||||
definitions,
|
||||
scc_annotations,
|
||||
member_constraints,
|
||||
outlives_constraints: Frozen::freeze(outlives_constraints),
|
||||
type_tests,
|
||||
liveness_constraints,
|
||||
universe_causes,
|
||||
placeholder_indices,
|
||||
}
|
||||
}
|
||||
|
||||
fn rewrite_placeholder_outlives<'tcx>(
|
||||
sccs: &Sccs<RegionVid, ConstraintSccIndex>,
|
||||
annotations: &SccAnnotations<'_, '_, RegionTracker>,
|
||||
fr_static: RegionVid,
|
||||
outlives_constraints: &mut OutlivesConstraintSet<'tcx>,
|
||||
) -> bool {
|
||||
// Changed to `true` if we added any constraints and need to
|
||||
// recompute SCCs.
|
||||
let mut added_constraints = false;
|
||||
|
||||
let annotations = &annotations.scc_to_annotation;
|
||||
|
||||
for scc in sccs.all_sccs() {
|
||||
// No point in adding 'static: 'static!
|
||||
// This micro-optimisation makes somewhat sense
|
||||
// because static outlives *everything*.
|
||||
if scc == sccs.scc(fr_static) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let annotation = annotations[scc];
|
||||
|
||||
// If this SCC participates in a universe violation,
|
||||
// e.g. if it reaches a region with a universe smaller than
|
||||
// the largest region reached, add a requirement that it must
|
||||
// outlive `'static`.
|
||||
if annotation.has_incompatible_universes() {
|
||||
// Optimisation opportunity: this will add more constraints than
|
||||
// needed for correctness, since an SCC upstream of another with
|
||||
// a universe violation will "infect" its downstream SCCs to also
|
||||
// outlive static.
|
||||
let scc_representative_outlives_static = OutlivesConstraint {
|
||||
sup: annotation.representative.rvid(),
|
||||
sub: fr_static,
|
||||
category: ConstraintCategory::IllegalUniverse,
|
||||
locations: Locations::All(rustc_span::DUMMY_SP),
|
||||
span: rustc_span::DUMMY_SP,
|
||||
variance_info: VarianceDiagInfo::None,
|
||||
from_closure: false,
|
||||
};
|
||||
outlives_constraints.push(scc_representative_outlives_static);
|
||||
added_constraints = true;
|
||||
debug!("Added {:?}: 'static!", annotation.representative.rvid());
|
||||
}
|
||||
}
|
||||
added_constraints
|
||||
}
|
||||
|
|
@ -40,9 +40,7 @@ use rustc_middle::ty::{
|
|||
self, ParamEnv, RegionVid, Ty, TyCtxt, TypeFoldable, TypeVisitable, TypingMode, fold_regions,
|
||||
};
|
||||
use rustc_middle::{bug, span_bug};
|
||||
use rustc_mir_dataflow::impls::{
|
||||
EverInitializedPlaces, MaybeInitializedPlaces, MaybeUninitializedPlaces,
|
||||
};
|
||||
use rustc_mir_dataflow::impls::{EverInitializedPlaces, MaybeUninitializedPlaces};
|
||||
use rustc_mir_dataflow::move_paths::{
|
||||
InitIndex, InitLocation, LookupResult, MoveData, MovePathIndex,
|
||||
};
|
||||
|
|
@ -74,6 +72,7 @@ mod constraints;
|
|||
mod dataflow;
|
||||
mod def_use;
|
||||
mod diagnostics;
|
||||
mod handle_placeholders;
|
||||
mod member_constraints;
|
||||
mod nll;
|
||||
mod path_utils;
|
||||
|
|
@ -324,10 +323,6 @@ fn do_mir_borrowck<'tcx>(
|
|||
|
||||
let move_data = MoveData::gather_moves(body, tcx, |_| true);
|
||||
|
||||
let flow_inits = MaybeInitializedPlaces::new(tcx, body, &move_data)
|
||||
.iterate_to_fixpoint(tcx, body, Some("borrowck"))
|
||||
.into_results_cursor(body);
|
||||
|
||||
let locals_are_invalidated_at_exit = tcx.hir_body_owner_kind(def).is_fn_or_closure();
|
||||
let borrow_set = BorrowSet::build(tcx, body, locals_are_invalidated_at_exit, &move_data);
|
||||
|
||||
|
|
@ -346,7 +341,6 @@ fn do_mir_borrowck<'tcx>(
|
|||
body,
|
||||
&promoted,
|
||||
&location_table,
|
||||
flow_inits,
|
||||
&move_data,
|
||||
&borrow_set,
|
||||
consumer_options,
|
||||
|
|
|
|||
|
|
@ -11,8 +11,6 @@ use rustc_middle::mir::pretty::{PrettyPrintMirOptions, dump_mir_with_options};
|
|||
use rustc_middle::mir::{Body, PassWhere, Promoted, create_dump_file, dump_enabled, dump_mir};
|
||||
use rustc_middle::ty::print::with_no_trimmed_paths;
|
||||
use rustc_middle::ty::{self, TyCtxt};
|
||||
use rustc_mir_dataflow::ResultsCursor;
|
||||
use rustc_mir_dataflow::impls::MaybeInitializedPlaces;
|
||||
use rustc_mir_dataflow::move_paths::MoveData;
|
||||
use rustc_mir_dataflow::points::DenseLocationMap;
|
||||
use rustc_session::config::MirIncludeSpans;
|
||||
|
|
@ -22,6 +20,7 @@ use tracing::{debug, instrument};
|
|||
use crate::borrow_set::BorrowSet;
|
||||
use crate::consumers::ConsumerOptions;
|
||||
use crate::diagnostics::RegionErrors;
|
||||
use crate::handle_placeholders::compute_sccs_applying_placeholder_outlives_constraints;
|
||||
use crate::polonius::PoloniusDiagnosticsContext;
|
||||
use crate::polonius::legacy::{
|
||||
PoloniusFacts, PoloniusFactsExt, PoloniusLocationTable, PoloniusOutput,
|
||||
|
|
@ -75,14 +74,13 @@ pub(crate) fn replace_regions_in_mir<'tcx>(
|
|||
/// Computes the (non-lexical) regions from the input MIR.
|
||||
///
|
||||
/// This may result in errors being reported.
|
||||
pub(crate) fn compute_regions<'a, 'tcx>(
|
||||
pub(crate) fn compute_regions<'tcx>(
|
||||
root_cx: &mut BorrowCheckRootCtxt<'tcx>,
|
||||
infcx: &BorrowckInferCtxt<'tcx>,
|
||||
universal_regions: UniversalRegions<'tcx>,
|
||||
body: &Body<'tcx>,
|
||||
promoted: &IndexSlice<Promoted, Body<'tcx>>,
|
||||
location_table: &PoloniusLocationTable,
|
||||
flow_inits: ResultsCursor<'a, 'tcx, MaybeInitializedPlaces<'a, 'tcx>>,
|
||||
move_data: &MoveData<'tcx>,
|
||||
borrow_set: &BorrowSet<'tcx>,
|
||||
consumer_options: Option<ConsumerOptions>,
|
||||
|
|
@ -112,11 +110,16 @@ pub(crate) fn compute_regions<'a, 'tcx>(
|
|||
location_table,
|
||||
borrow_set,
|
||||
&mut polonius_facts,
|
||||
flow_inits,
|
||||
move_data,
|
||||
Rc::clone(&location_map),
|
||||
);
|
||||
|
||||
let lowered_constraints = compute_sccs_applying_placeholder_outlives_constraints(
|
||||
constraints,
|
||||
&universal_region_relations,
|
||||
infcx,
|
||||
);
|
||||
|
||||
// If requested, emit legacy polonius facts.
|
||||
polonius::legacy::emit_facts(
|
||||
&mut polonius_facts,
|
||||
|
|
@ -126,11 +129,15 @@ pub(crate) fn compute_regions<'a, 'tcx>(
|
|||
borrow_set,
|
||||
move_data,
|
||||
&universal_region_relations,
|
||||
&constraints,
|
||||
&lowered_constraints,
|
||||
);
|
||||
|
||||
let mut regioncx =
|
||||
RegionInferenceContext::new(infcx, constraints, universal_region_relations, location_map);
|
||||
let mut regioncx = RegionInferenceContext::new(
|
||||
infcx,
|
||||
lowered_constraints,
|
||||
universal_region_relations,
|
||||
location_map,
|
||||
);
|
||||
|
||||
// If requested for `-Zpolonius=next`, convert NLL constraints to localized outlives constraints
|
||||
// and use them to compute loan liveness.
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ use tracing::debug;
|
|||
|
||||
use crate::borrow_set::BorrowSet;
|
||||
use crate::constraints::OutlivesConstraint;
|
||||
use crate::type_check::MirTypeckRegionConstraints;
|
||||
use crate::handle_placeholders::LoweredConstraints;
|
||||
use crate::type_check::free_region_relations::UniversalRegionRelations;
|
||||
use crate::universal_regions::UniversalRegions;
|
||||
|
||||
|
|
@ -43,7 +43,7 @@ pub(crate) fn emit_facts<'tcx>(
|
|||
borrow_set: &BorrowSet<'tcx>,
|
||||
move_data: &MoveData<'tcx>,
|
||||
universal_region_relations: &UniversalRegionRelations<'tcx>,
|
||||
constraints: &MirTypeckRegionConstraints<'tcx>,
|
||||
constraints: &LoweredConstraints<'tcx>,
|
||||
) {
|
||||
let Some(facts) = facts else {
|
||||
// We don't do anything if there are no facts to fill.
|
||||
|
|
@ -203,7 +203,7 @@ pub(crate) fn emit_drop_facts<'tcx>(
|
|||
fn emit_outlives_facts<'tcx>(
|
||||
facts: &mut PoloniusFacts,
|
||||
location_table: &PoloniusLocationTable,
|
||||
constraints: &MirTypeckRegionConstraints<'tcx>,
|
||||
constraints: &LoweredConstraints<'tcx>,
|
||||
) {
|
||||
facts.subset_base.extend(constraints.outlives_constraints.outlives().iter().flat_map(
|
||||
|constraint: &OutlivesConstraint<'_>| {
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
"| {r:rw$?} | {ui:4?} | {v}",
|
||||
r = region,
|
||||
rw = REGION_WIDTH,
|
||||
ui = self.region_universe(region),
|
||||
ui = self.max_nameable_universe(self.constraint_sccs.scc(region)),
|
||||
v = self.region_value_str(region),
|
||||
)?;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
use std::cell::OnceCell;
|
||||
use std::collections::VecDeque;
|
||||
use std::rc::Rc;
|
||||
|
||||
|
|
@ -10,7 +11,7 @@ use rustc_hir::def_id::CRATE_DEF_ID;
|
|||
use rustc_index::IndexVec;
|
||||
use rustc_infer::infer::outlives::test_type_match;
|
||||
use rustc_infer::infer::region_constraints::{GenericKind, VerifyBound, VerifyIfEq};
|
||||
use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin, RegionVariableOrigin};
|
||||
use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin};
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::mir::{
|
||||
AnnotationSource, BasicBlock, Body, ConstraintCategory, Local, Location, ReturnConstraint,
|
||||
|
|
@ -27,13 +28,14 @@ use crate::constraints::graph::{self, NormalConstraintGraph, RegionGraph};
|
|||
use crate::constraints::{ConstraintSccIndex, OutlivesConstraint, OutlivesConstraintSet};
|
||||
use crate::dataflow::BorrowIndex;
|
||||
use crate::diagnostics::{RegionErrorKind, RegionErrors, UniverseInfo};
|
||||
use crate::handle_placeholders::{LoweredConstraints, RegionTracker};
|
||||
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};
|
||||
use crate::type_check::Locations;
|
||||
use crate::type_check::free_region_relations::UniversalRegionRelations;
|
||||
use crate::type_check::{Locations, MirTypeckRegionConstraints};
|
||||
use crate::universal_regions::UniversalRegions;
|
||||
use crate::{
|
||||
BorrowckInferCtxt, ClosureOutlivesRequirement, ClosureOutlivesSubject,
|
||||
|
|
@ -47,125 +49,48 @@ mod reverse_sccs;
|
|||
|
||||
pub(crate) mod values;
|
||||
|
||||
pub(crate) type ConstraintSccs = Sccs<RegionVid, ConstraintSccIndex>;
|
||||
pub(crate) type AnnotatedSccs = (ConstraintSccs, IndexVec<ConstraintSccIndex, RegionTracker>);
|
||||
|
||||
/// An annotation for region graph SCCs that tracks
|
||||
/// the values of its elements. This annotates a single SCC.
|
||||
#[derive(Copy, Debug, Clone)]
|
||||
pub(crate) struct RegionTracker {
|
||||
/// The largest universe of a placeholder reached from this SCC.
|
||||
/// This includes placeholders within this SCC.
|
||||
max_placeholder_universe_reached: UniverseIndex,
|
||||
|
||||
/// The smallest universe index reachable form the nodes of this SCC.
|
||||
min_reachable_universe: UniverseIndex,
|
||||
|
||||
/// The representative Region Variable Id for this SCC. We prefer
|
||||
/// placeholders over existentially quantified variables, otherwise
|
||||
/// it's the one with the smallest Region Variable ID.
|
||||
pub(crate) representative: RegionVid,
|
||||
|
||||
/// Is the current representative a placeholder?
|
||||
representative_is_placeholder: bool,
|
||||
|
||||
/// Is the current representative existentially quantified?
|
||||
representative_is_existential: bool,
|
||||
/// The representative region variable for an SCC, tagged by its origin.
|
||||
/// We prefer placeholders over existentially quantified variables, otherwise
|
||||
/// it's the one with the smallest Region Variable ID. In other words,
|
||||
/// the order of this enumeration really matters!
|
||||
#[derive(Copy, Debug, Clone, PartialEq, PartialOrd, Eq, Ord)]
|
||||
pub(crate) enum Representative {
|
||||
FreeRegion(RegionVid),
|
||||
Placeholder(RegionVid),
|
||||
Existential(RegionVid),
|
||||
}
|
||||
|
||||
impl scc::Annotation for RegionTracker {
|
||||
fn merge_scc(mut self, mut other: Self) -> Self {
|
||||
// Prefer any placeholder over any existential
|
||||
if other.representative_is_placeholder && self.representative_is_existential {
|
||||
other.merge_min_max_seen(&self);
|
||||
return other;
|
||||
impl Representative {
|
||||
pub(crate) fn rvid(self) -> RegionVid {
|
||||
match self {
|
||||
Representative::FreeRegion(region_vid)
|
||||
| Representative::Placeholder(region_vid)
|
||||
| Representative::Existential(region_vid) => region_vid,
|
||||
}
|
||||
|
||||
if self.representative_is_placeholder && other.representative_is_existential
|
||||
|| (self.representative <= other.representative)
|
||||
{
|
||||
self.merge_min_max_seen(&other);
|
||||
return self;
|
||||
}
|
||||
other.merge_min_max_seen(&self);
|
||||
other
|
||||
}
|
||||
|
||||
fn merge_reached(mut self, other: Self) -> Self {
|
||||
// No update to in-component values, only add seen values.
|
||||
self.merge_min_max_seen(&other);
|
||||
pub(crate) fn new(r: RegionVid, definition: &RegionDefinition<'_>) -> Self {
|
||||
match definition.origin {
|
||||
NllRegionVariableOrigin::FreeRegion => Representative::FreeRegion(r),
|
||||
NllRegionVariableOrigin::Placeholder(_) => Representative::Placeholder(r),
|
||||
NllRegionVariableOrigin::Existential { .. } => Representative::Existential(r),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl scc::Annotation for Representative {
|
||||
fn merge_scc(self, other: Self) -> Self {
|
||||
// Just pick the smallest one. Note that we order by tag first!
|
||||
std::cmp::min(self, other)
|
||||
}
|
||||
|
||||
// For reachability, we do nothing since the representative doesn't change.
|
||||
fn merge_reached(self, _other: Self) -> Self {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// A Visitor for SCC annotation construction.
|
||||
pub(crate) struct SccAnnotations<'d, 'tcx, A: scc::Annotation> {
|
||||
pub(crate) scc_to_annotation: IndexVec<ConstraintSccIndex, A>,
|
||||
definitions: &'d IndexVec<RegionVid, RegionDefinition<'tcx>>,
|
||||
}
|
||||
|
||||
impl<'d, 'tcx, A: scc::Annotation> SccAnnotations<'d, 'tcx, A> {
|
||||
pub(crate) fn new(definitions: &'d IndexVec<RegionVid, RegionDefinition<'tcx>>) -> Self {
|
||||
Self { scc_to_annotation: IndexVec::new(), definitions }
|
||||
}
|
||||
}
|
||||
|
||||
impl scc::Annotations<RegionVid> for SccAnnotations<'_, '_, RegionTracker> {
|
||||
fn new(&self, element: RegionVid) -> RegionTracker {
|
||||
RegionTracker::new(element, &self.definitions[element])
|
||||
}
|
||||
|
||||
fn annotate_scc(&mut self, scc: ConstraintSccIndex, annotation: RegionTracker) {
|
||||
let idx = self.scc_to_annotation.push(annotation);
|
||||
assert!(idx == scc);
|
||||
}
|
||||
|
||||
type Ann = RegionTracker;
|
||||
type SccIdx = ConstraintSccIndex;
|
||||
}
|
||||
|
||||
impl RegionTracker {
|
||||
pub(crate) fn new(rvid: RegionVid, definition: &RegionDefinition<'_>) -> Self {
|
||||
let (representative_is_placeholder, representative_is_existential) = match definition.origin
|
||||
{
|
||||
NllRegionVariableOrigin::FreeRegion => (false, false),
|
||||
NllRegionVariableOrigin::Placeholder(_) => (true, false),
|
||||
NllRegionVariableOrigin::Existential { .. } => (false, true),
|
||||
};
|
||||
|
||||
let placeholder_universe =
|
||||
if representative_is_placeholder { definition.universe } else { UniverseIndex::ROOT };
|
||||
|
||||
Self {
|
||||
max_placeholder_universe_reached: placeholder_universe,
|
||||
min_reachable_universe: definition.universe,
|
||||
representative: rvid,
|
||||
representative_is_placeholder,
|
||||
representative_is_existential,
|
||||
}
|
||||
}
|
||||
|
||||
/// The smallest-indexed universe reachable from and/or in this SCC.
|
||||
fn min_universe(self) -> UniverseIndex {
|
||||
self.min_reachable_universe
|
||||
}
|
||||
|
||||
fn merge_min_max_seen(&mut self, other: &Self) {
|
||||
self.max_placeholder_universe_reached = std::cmp::max(
|
||||
self.max_placeholder_universe_reached,
|
||||
other.max_placeholder_universe_reached,
|
||||
);
|
||||
|
||||
self.min_reachable_universe =
|
||||
std::cmp::min(self.min_reachable_universe, other.min_reachable_universe);
|
||||
}
|
||||
|
||||
/// Returns `true` if during the annotated SCC reaches a placeholder
|
||||
/// with a universe larger than the smallest reachable one, `false` otherwise.
|
||||
pub(crate) fn has_incompatible_universes(&self) -> bool {
|
||||
self.min_universe().cannot_name(self.max_placeholder_universe_reached)
|
||||
}
|
||||
}
|
||||
pub(crate) type ConstraintSccs = Sccs<RegionVid, ConstraintSccIndex>;
|
||||
|
||||
pub struct RegionInferenceContext<'tcx> {
|
||||
/// Contains the definition for every region variable. Region
|
||||
|
|
@ -197,8 +122,8 @@ pub struct RegionInferenceContext<'tcx> {
|
|||
|
||||
/// Reverse of the SCC constraint graph -- i.e., an edge `A -> B` exists if
|
||||
/// `B: A`. This is used to compute the universal regions that are required
|
||||
/// to outlive a given SCC. Computed lazily.
|
||||
rev_scc_graph: Option<ReverseSccGraph>,
|
||||
/// to outlive a given SCC.
|
||||
rev_scc_graph: OnceCell<ReverseSccGraph>,
|
||||
|
||||
/// The "R0 member of [R1..Rn]" constraints, indexed by SCC.
|
||||
member_constraints: Rc<MemberConstraintSet<'tcx, ConstraintSccIndex>>,
|
||||
|
|
@ -413,26 +338,6 @@ fn sccs_info<'tcx>(infcx: &BorrowckInferCtxt<'tcx>, sccs: &ConstraintSccs) {
|
|||
debug!("SCC edges {:#?}", scc_node_to_edges);
|
||||
}
|
||||
|
||||
fn create_definitions<'tcx>(
|
||||
infcx: &BorrowckInferCtxt<'tcx>,
|
||||
universal_regions: &UniversalRegions<'tcx>,
|
||||
) -> Frozen<IndexVec<RegionVid, RegionDefinition<'tcx>>> {
|
||||
// Create a RegionDefinition for each inference variable.
|
||||
let mut definitions: IndexVec<_, _> = infcx
|
||||
.get_region_var_infos()
|
||||
.iter()
|
||||
.map(|info| RegionDefinition::new(info.universe, info.origin))
|
||||
.collect();
|
||||
|
||||
// Add the external name for all universal regions.
|
||||
for (external_name, variable) in universal_regions.named_universal_regions_iter() {
|
||||
debug!("region {variable:?} has external name {external_name:?}");
|
||||
definitions[variable].external_name = Some(external_name);
|
||||
}
|
||||
|
||||
Frozen::freeze(definitions)
|
||||
}
|
||||
|
||||
impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
/// Creates a new region inference context with a total of
|
||||
/// `num_region_variables` valid inference variables; the first N
|
||||
|
|
@ -443,42 +348,30 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
/// of constraints produced by the MIR type check.
|
||||
pub(crate) fn new(
|
||||
infcx: &BorrowckInferCtxt<'tcx>,
|
||||
constraints: MirTypeckRegionConstraints<'tcx>,
|
||||
lowered_constraints: LoweredConstraints<'tcx>,
|
||||
universal_region_relations: Frozen<UniversalRegionRelations<'tcx>>,
|
||||
location_map: Rc<DenseLocationMap>,
|
||||
) -> Self {
|
||||
let universal_regions = &universal_region_relations.universal_regions;
|
||||
let MirTypeckRegionConstraints {
|
||||
placeholder_indices,
|
||||
placeholder_index_to_region: _,
|
||||
liveness_constraints,
|
||||
mut outlives_constraints,
|
||||
mut member_constraints,
|
||||
universe_causes,
|
||||
|
||||
let LoweredConstraints {
|
||||
constraint_sccs,
|
||||
definitions,
|
||||
outlives_constraints,
|
||||
scc_annotations,
|
||||
type_tests,
|
||||
} = constraints;
|
||||
liveness_constraints,
|
||||
universe_causes,
|
||||
placeholder_indices,
|
||||
member_constraints,
|
||||
} = lowered_constraints;
|
||||
|
||||
debug!("universal_regions: {:#?}", universal_region_relations.universal_regions);
|
||||
debug!("outlives constraints: {:#?}", outlives_constraints);
|
||||
debug!("placeholder_indices: {:#?}", placeholder_indices);
|
||||
debug!("type tests: {:#?}", type_tests);
|
||||
|
||||
if let Some(guar) = universal_region_relations.universal_regions.tainted_by_errors() {
|
||||
// Suppress unhelpful extra errors in `infer_opaque_types` by clearing out all
|
||||
// outlives bounds that we may end up checking.
|
||||
outlives_constraints = Default::default();
|
||||
member_constraints = Default::default();
|
||||
|
||||
// Also taint the entire scope.
|
||||
infcx.set_tainted_by_errors(guar);
|
||||
}
|
||||
|
||||
let definitions = create_definitions(infcx, &universal_regions);
|
||||
|
||||
let (constraint_sccs, scc_annotations) =
|
||||
outlives_constraints.add_outlives_static(&universal_regions, &definitions);
|
||||
let constraints = Frozen::freeze(outlives_constraints);
|
||||
let constraint_graph = Frozen::freeze(constraints.graph(definitions.len()));
|
||||
let constraint_graph = Frozen::freeze(outlives_constraints.graph(definitions.len()));
|
||||
|
||||
if cfg!(debug_assertions) {
|
||||
sccs_info(infcx, &constraint_sccs);
|
||||
|
|
@ -498,11 +391,11 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
let mut result = Self {
|
||||
definitions,
|
||||
liveness_constraints,
|
||||
constraints,
|
||||
constraints: outlives_constraints,
|
||||
constraint_graph,
|
||||
constraint_sccs,
|
||||
scc_annotations,
|
||||
rev_scc_graph: None,
|
||||
rev_scc_graph: OnceCell::new(),
|
||||
member_constraints,
|
||||
member_constraints_applied: Vec::new(),
|
||||
universe_causes,
|
||||
|
|
@ -657,11 +550,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
self.scc_values.placeholders_contained_in(scc)
|
||||
}
|
||||
|
||||
/// Returns access to the value of `r` for debugging purposes.
|
||||
pub(crate) fn region_universe(&self, r: RegionVid) -> ty::UniverseIndex {
|
||||
self.scc_universe(self.constraint_sccs.scc(r))
|
||||
}
|
||||
|
||||
/// Once region solving has completed, this function will return the member constraints that
|
||||
/// were applied to the value of a given SCC `scc`. See `AppliedMemberConstraint`.
|
||||
pub(crate) fn applied_member_constraints(
|
||||
|
|
@ -809,9 +697,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
member_constraint_index: NllMemberConstraintIndex,
|
||||
choice_regions: &[ty::RegionVid],
|
||||
) {
|
||||
// Lazily compute the reverse graph, we'll need it later.
|
||||
self.compute_reverse_scc_graph();
|
||||
|
||||
// Create a mutable vector of the options. We'll try to winnow
|
||||
// them down.
|
||||
let mut choice_regions: Vec<ty::RegionVid> = choice_regions.to_vec();
|
||||
|
|
@ -828,7 +713,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
|
||||
// If the member region lives in a higher universe, we currently choose
|
||||
// the most conservative option by leaving it unchanged.
|
||||
if !self.scc_universe(scc).is_root() {
|
||||
if !self.max_nameable_universe(scc).is_root() {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -849,7 +734,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
// R0`). Therefore, we need only keep an option `O` if `UB: O`
|
||||
// for all UB.
|
||||
let universal_region_relations = &self.universal_region_relations;
|
||||
for ub in self.rev_scc_graph.as_ref().unwrap().upper_bounds(scc) {
|
||||
for ub in self.reverse_scc_graph().upper_bounds(scc) {
|
||||
debug!(?ub);
|
||||
choice_regions.retain(|&o_r| universal_region_relations.outlives(ub, o_r));
|
||||
}
|
||||
|
|
@ -904,20 +789,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
/// in `scc_a`. Used during constraint propagation, and only once
|
||||
/// the value of `scc_b` has been computed.
|
||||
fn universe_compatible(&self, scc_b: ConstraintSccIndex, scc_a: ConstraintSccIndex) -> bool {
|
||||
let a_annotation = self.scc_annotations[scc_a];
|
||||
let b_annotation = self.scc_annotations[scc_b];
|
||||
let a_universe = a_annotation.min_universe();
|
||||
|
||||
// If scc_b's declared universe is a subset of
|
||||
// scc_a's declared universe (typically, both are ROOT), then
|
||||
// it cannot contain any problematic universe elements.
|
||||
if a_universe.can_name(b_annotation.min_universe()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Otherwise, there can be no placeholder in `b` with a too high
|
||||
// universe index to name from `a`.
|
||||
a_universe.can_name(b_annotation.max_placeholder_universe_reached)
|
||||
self.scc_annotations[scc_a].universe_compatible_with(self.scc_annotations[scc_b])
|
||||
}
|
||||
|
||||
/// Once regions have been propagated, this method is used to see
|
||||
|
|
@ -1021,7 +893,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
"lower_bound = {:?} r_scc={:?} universe={:?}",
|
||||
lower_bound,
|
||||
r_scc,
|
||||
self.scc_universe(r_scc)
|
||||
self.max_nameable_universe(r_scc)
|
||||
);
|
||||
// If the type test requires that `T: 'a` where `'a` is a
|
||||
// placeholder from another universe, that effectively requires
|
||||
|
|
@ -1499,10 +1371,9 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
/// The minimum universe of any variable reachable from this
|
||||
/// SCC, inside or outside of it.
|
||||
fn scc_universe(&self, scc: ConstraintSccIndex) -> UniverseIndex {
|
||||
self.scc_annotations[scc].min_universe()
|
||||
/// The largest universe of any region nameable from this SCC.
|
||||
fn max_nameable_universe(&self, scc: ConstraintSccIndex) -> UniverseIndex {
|
||||
self.scc_annotations[scc].max_nameable_universe()
|
||||
}
|
||||
|
||||
/// Checks the final value for the free region `fr` to see if it
|
||||
|
|
@ -1524,7 +1395,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
|
||||
// Because this free region must be in the ROOT universe, we
|
||||
// know it cannot contain any bound universes.
|
||||
assert!(self.scc_universe(longer_fr_scc).is_root());
|
||||
assert!(self.max_nameable_universe(longer_fr_scc).is_root());
|
||||
|
||||
// Only check all of the relations for the main representative of each
|
||||
// SCC, otherwise just check that we outlive said representative. This
|
||||
|
|
@ -1915,7 +1786,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
#[instrument(skip(self), level = "trace", ret)]
|
||||
pub(crate) fn find_sub_region_live_at(&self, fr1: RegionVid, location: Location) -> RegionVid {
|
||||
trace!(scc = ?self.constraint_sccs.scc(fr1));
|
||||
trace!(universe = ?self.region_universe(fr1));
|
||||
trace!(universe = ?self.max_nameable_universe(self.constraint_sccs.scc(fr1)));
|
||||
self.find_constraint_paths_between_regions(fr1, |r| {
|
||||
// First look for some `r` such that `fr1: r` and `r` is live at `location`
|
||||
trace!(?r, liveness_constraints=?self.liveness_constraints.pretty_print_live_points(r));
|
||||
|
|
@ -2246,7 +2117,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
/// they *must* be equal (though not having the same repr does not
|
||||
/// mean they are unequal).
|
||||
fn scc_representative(&self, scc: ConstraintSccIndex) -> RegionVid {
|
||||
self.scc_annotations[scc].representative
|
||||
self.scc_annotations[scc].representative.rvid()
|
||||
}
|
||||
|
||||
pub(crate) fn liveness_constraints(&self) -> &LivenessValues {
|
||||
|
|
@ -2268,21 +2139,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'tcx> RegionDefinition<'tcx> {
|
||||
fn new(universe: ty::UniverseIndex, rv_origin: RegionVariableOrigin) -> Self {
|
||||
// Create a new region definition. Note that, for free
|
||||
// regions, the `external_name` field gets updated later in
|
||||
// `init_free_and_bound_regions`.
|
||||
|
||||
let origin = match rv_origin {
|
||||
RegionVariableOrigin::Nll(origin) => origin,
|
||||
_ => NllRegionVariableOrigin::Existential { from_forall: false },
|
||||
};
|
||||
|
||||
Self { origin, universe, external_name: None }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct BlameConstraint<'tcx> {
|
||||
pub category: ConstraintCategory<'tcx>,
|
||||
|
|
|
|||
|
|
@ -191,7 +191,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
let scc = self.constraint_sccs.scc(vid);
|
||||
|
||||
// Special handling of higher-ranked regions.
|
||||
if !self.scc_universe(scc).is_root() {
|
||||
if !self.max_nameable_universe(scc).is_root() {
|
||||
match self.scc_values.placeholders_contained_in(scc).enumerate().last() {
|
||||
// If the region contains a single placeholder then they're equal.
|
||||
Some((0, placeholder)) => {
|
||||
|
|
@ -215,9 +215,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
// FIXME: We could probably compute the LUB if there is one.
|
||||
let scc = self.constraint_sccs.scc(vid);
|
||||
let upper_bounds: Vec<_> = self
|
||||
.rev_scc_graph
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.reverse_scc_graph()
|
||||
.upper_bounds(scc)
|
||||
.filter_map(|vid| self.definitions[vid].external_name)
|
||||
.filter(|r| !r.is_static())
|
||||
|
|
|
|||
|
|
@ -59,13 +59,10 @@ impl ReverseSccGraph {
|
|||
}
|
||||
|
||||
impl RegionInferenceContext<'_> {
|
||||
/// Compute the reverse SCC-based constraint graph (lazily).
|
||||
pub(super) fn compute_reverse_scc_graph(&mut self) {
|
||||
if self.rev_scc_graph.is_some() {
|
||||
return;
|
||||
}
|
||||
|
||||
self.rev_scc_graph =
|
||||
Some(ReverseSccGraph::compute(&self.constraint_sccs, self.universal_regions()));
|
||||
/// Return the reverse graph of the region SCCs, initialising it if needed.
|
||||
pub(super) fn reverse_scc_graph(&self) -> &ReverseSccGraph {
|
||||
self.rev_scc_graph.get_or_init(|| {
|
||||
ReverseSccGraph::compute(&self.constraint_sccs, self.universal_regions())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -148,7 +148,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
|
|||
|
||||
let mut next_outlives_predicates = vec![];
|
||||
for (ty::OutlivesPredicate(k1, r2), constraint_category) in outlives_predicates {
|
||||
match k1.unpack() {
|
||||
match k1.kind() {
|
||||
GenericArgKind::Lifetime(r1) => {
|
||||
let r1_vid = self.to_region_vid(r1);
|
||||
let r2_vid = self.to_region_vid(r2);
|
||||
|
|
|
|||
|
|
@ -131,7 +131,8 @@ impl UniversalRegionRelations<'_> {
|
|||
assert!(self.universal_regions.is_universal_region(fr0));
|
||||
|
||||
let mut external_parents = vec![];
|
||||
let mut queue = vec![fr0];
|
||||
|
||||
let mut queue = vec![relation.minimal_scc_representative(fr0)];
|
||||
|
||||
// Keep expanding `fr` into its parents until we reach
|
||||
// non-local regions.
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
|||
assert_matches!(
|
||||
self.tcx().coroutine_kind(self.tcx().coroutine_for_closure(mir_def_id)),
|
||||
Some(hir::CoroutineKind::Desugared(
|
||||
hir::CoroutineDesugaring::Async,
|
||||
hir::CoroutineDesugaring::Async | hir::CoroutineDesugaring::Gen,
|
||||
hir::CoroutineSource::Closure
|
||||
)),
|
||||
"this needs to be modified if we're lowering non-async closures"
|
||||
|
|
|
|||
|
|
@ -5,8 +5,6 @@ use rustc_middle::mir::{Body, Local, Location, SourceInfo};
|
|||
use rustc_middle::span_bug;
|
||||
use rustc_middle::ty::relate::Relate;
|
||||
use rustc_middle::ty::{GenericArgsRef, Region, RegionVid, Ty, TyCtxt, TypeVisitable};
|
||||
use rustc_mir_dataflow::ResultsCursor;
|
||||
use rustc_mir_dataflow::impls::MaybeInitializedPlaces;
|
||||
use rustc_mir_dataflow::move_paths::MoveData;
|
||||
use rustc_mir_dataflow::points::DenseLocationMap;
|
||||
use tracing::debug;
|
||||
|
|
@ -28,10 +26,9 @@ mod trace;
|
|||
///
|
||||
/// N.B., this computation requires normalization; therefore, it must be
|
||||
/// performed before
|
||||
pub(super) fn generate<'a, 'tcx>(
|
||||
pub(super) fn generate<'tcx>(
|
||||
typeck: &mut TypeChecker<'_, 'tcx>,
|
||||
location_map: &DenseLocationMap,
|
||||
flow_inits: ResultsCursor<'a, 'tcx, MaybeInitializedPlaces<'a, 'tcx>>,
|
||||
move_data: &MoveData<'tcx>,
|
||||
) {
|
||||
debug!("liveness::generate");
|
||||
|
|
@ -58,7 +55,7 @@ pub(super) fn generate<'a, 'tcx>(
|
|||
let (relevant_live_locals, boring_locals) =
|
||||
compute_relevant_live_locals(typeck.tcx(), &free_regions, typeck.body);
|
||||
|
||||
trace::trace(typeck, location_map, flow_inits, move_data, relevant_live_locals, boring_locals);
|
||||
trace::trace(typeck, location_map, move_data, relevant_live_locals, boring_locals);
|
||||
|
||||
// Mark regions that should be live where they appear within rvalues or within a call: like
|
||||
// args, regions, and types.
|
||||
|
|
|
|||
|
|
@ -7,10 +7,10 @@ use rustc_middle::mir::{BasicBlock, Body, ConstraintCategory, HasLocalDecls, Loc
|
|||
use rustc_middle::traits::query::DropckOutlivesResult;
|
||||
use rustc_middle::ty::relate::Relate;
|
||||
use rustc_middle::ty::{Ty, TyCtxt, TypeVisitable, TypeVisitableExt};
|
||||
use rustc_mir_dataflow::ResultsCursor;
|
||||
use rustc_mir_dataflow::impls::MaybeInitializedPlaces;
|
||||
use rustc_mir_dataflow::move_paths::{HasMoveData, MoveData, MovePathIndex};
|
||||
use rustc_mir_dataflow::points::{DenseLocationMap, PointIndex};
|
||||
use rustc_mir_dataflow::{Analysis, ResultsCursor};
|
||||
use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span};
|
||||
use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
|
||||
use rustc_trait_selection::traits::ObligationCtxt;
|
||||
|
|
@ -37,10 +37,9 @@ use crate::type_check::{NormalizeLocation, TypeChecker};
|
|||
/// DROP-LIVE set are to the liveness sets for regions found in the
|
||||
/// `dropck_outlives` result of the variable's type (in particular,
|
||||
/// this respects `#[may_dangle]` annotations).
|
||||
pub(super) fn trace<'a, 'tcx>(
|
||||
pub(super) fn trace<'tcx>(
|
||||
typeck: &mut TypeChecker<'_, 'tcx>,
|
||||
location_map: &DenseLocationMap,
|
||||
flow_inits: ResultsCursor<'a, 'tcx, MaybeInitializedPlaces<'a, 'tcx>>,
|
||||
move_data: &MoveData<'tcx>,
|
||||
relevant_live_locals: Vec<Local>,
|
||||
boring_locals: Vec<Local>,
|
||||
|
|
@ -48,7 +47,7 @@ pub(super) fn trace<'a, 'tcx>(
|
|||
let local_use_map = &LocalUseMap::build(&relevant_live_locals, location_map, typeck.body);
|
||||
let cx = LivenessContext {
|
||||
typeck,
|
||||
flow_inits,
|
||||
flow_inits: None,
|
||||
location_map,
|
||||
local_use_map,
|
||||
move_data,
|
||||
|
|
@ -65,7 +64,7 @@ pub(super) fn trace<'a, 'tcx>(
|
|||
}
|
||||
|
||||
/// Contextual state for the type-liveness coroutine.
|
||||
struct LivenessContext<'a, 'typeck, 'b, 'tcx> {
|
||||
struct LivenessContext<'a, 'typeck, 'tcx> {
|
||||
/// Current type-checker, giving us our inference context etc.
|
||||
///
|
||||
/// This also stores the body we're currently analyzing.
|
||||
|
|
@ -81,8 +80,8 @@ struct LivenessContext<'a, 'typeck, 'b, 'tcx> {
|
|||
drop_data: FxIndexMap<Ty<'tcx>, DropData<'tcx>>,
|
||||
|
||||
/// Results of dataflow tracking which variables (and paths) have been
|
||||
/// initialized.
|
||||
flow_inits: ResultsCursor<'b, 'tcx, MaybeInitializedPlaces<'b, 'tcx>>,
|
||||
/// initialized. Computed lazily when needed by drop-liveness.
|
||||
flow_inits: Option<ResultsCursor<'a, 'tcx, MaybeInitializedPlaces<'a, 'tcx>>>,
|
||||
|
||||
/// Index indicating where each variable is assigned, used, or
|
||||
/// dropped.
|
||||
|
|
@ -94,8 +93,8 @@ struct DropData<'tcx> {
|
|||
region_constraint_data: Option<&'tcx QueryRegionConstraints<'tcx>>,
|
||||
}
|
||||
|
||||
struct LivenessResults<'a, 'typeck, 'b, 'tcx> {
|
||||
cx: LivenessContext<'a, 'typeck, 'b, 'tcx>,
|
||||
struct LivenessResults<'a, 'typeck, 'tcx> {
|
||||
cx: LivenessContext<'a, 'typeck, 'tcx>,
|
||||
|
||||
/// Set of points that define the current local.
|
||||
defs: DenseBitSet<PointIndex>,
|
||||
|
|
@ -116,8 +115,8 @@ struct LivenessResults<'a, 'typeck, 'b, 'tcx> {
|
|||
stack: Vec<PointIndex>,
|
||||
}
|
||||
|
||||
impl<'a, 'typeck, 'b, 'tcx> LivenessResults<'a, 'typeck, 'b, 'tcx> {
|
||||
fn new(cx: LivenessContext<'a, 'typeck, 'b, 'tcx>) -> Self {
|
||||
impl<'a, 'typeck, 'tcx> LivenessResults<'a, 'typeck, 'tcx> {
|
||||
fn new(cx: LivenessContext<'a, 'typeck, 'tcx>) -> Self {
|
||||
let num_points = cx.location_map.num_points();
|
||||
LivenessResults {
|
||||
cx,
|
||||
|
|
@ -459,20 +458,56 @@ impl<'a, 'typeck, 'b, 'tcx> LivenessResults<'a, 'typeck, 'b, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> {
|
||||
impl<'a, 'typeck, 'tcx> LivenessContext<'a, 'typeck, 'tcx> {
|
||||
/// Computes the `MaybeInitializedPlaces` dataflow analysis if it hasn't been done already.
|
||||
///
|
||||
/// In practice, the results of this dataflow analysis are rarely needed but can be expensive to
|
||||
/// compute on big functions, so we compute them lazily as a fast path when:
|
||||
/// - there are relevant live locals
|
||||
/// - there are drop points for these relevant live locals.
|
||||
///
|
||||
/// This happens as part of the drop-liveness computation: it's the only place checking for
|
||||
/// maybe-initializedness of `MovePathIndex`es.
|
||||
fn flow_inits(&mut self) -> &mut ResultsCursor<'a, 'tcx, MaybeInitializedPlaces<'a, 'tcx>> {
|
||||
self.flow_inits.get_or_insert_with(|| {
|
||||
let tcx = self.typeck.tcx();
|
||||
let body = self.typeck.body;
|
||||
// FIXME: reduce the `MaybeInitializedPlaces` domain to the useful `MovePath`s.
|
||||
//
|
||||
// This dataflow analysis computes maybe-initializedness of all move paths, which
|
||||
// explains why it can be expensive on big functions. But this data is only used in
|
||||
// drop-liveness. Therefore, most of the move paths computed here are ultimately unused,
|
||||
// even if the results are computed lazily and "no relevant live locals with drop
|
||||
// points" is the common case.
|
||||
//
|
||||
// So we only need the ones for 1) relevant live locals 2) that have drop points. That's
|
||||
// a much, much smaller domain: in our benchmarks, when it's not zero (the most likely
|
||||
// case), there are a few dozens compared to e.g. thousands or tens of thousands of
|
||||
// locals and move paths.
|
||||
let flow_inits = MaybeInitializedPlaces::new(tcx, body, self.move_data)
|
||||
.iterate_to_fixpoint(tcx, body, Some("borrowck"))
|
||||
.into_results_cursor(body);
|
||||
flow_inits
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> LivenessContext<'_, '_, 'tcx> {
|
||||
fn body(&self) -> &Body<'tcx> {
|
||||
self.typeck.body
|
||||
}
|
||||
|
||||
/// Returns `true` if the local variable (or some part of it) is initialized at the current
|
||||
/// cursor position. Callers should call one of the `seek` methods immediately before to point
|
||||
/// the cursor to the desired location.
|
||||
fn initialized_at_curr_loc(&self, mpi: MovePathIndex) -> bool {
|
||||
let state = self.flow_inits.get();
|
||||
fn initialized_at_curr_loc(&mut self, mpi: MovePathIndex) -> bool {
|
||||
let flow_inits = self.flow_inits();
|
||||
let state = flow_inits.get();
|
||||
if state.contains(mpi) {
|
||||
return true;
|
||||
}
|
||||
|
||||
let move_paths = &self.flow_inits.analysis().move_data().move_paths;
|
||||
let move_paths = &flow_inits.analysis().move_data().move_paths;
|
||||
move_paths[mpi].find_descendant(move_paths, |mpi| state.contains(mpi)).is_some()
|
||||
}
|
||||
|
||||
|
|
@ -481,7 +516,8 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> {
|
|||
/// DROP of some local variable will have an effect -- note that
|
||||
/// drops, as they may unwind, are always terminators.
|
||||
fn initialized_at_terminator(&mut self, block: BasicBlock, mpi: MovePathIndex) -> bool {
|
||||
self.flow_inits.seek_before_primary_effect(self.body().terminator_loc(block));
|
||||
let terminator_location = self.body().terminator_loc(block);
|
||||
self.flow_inits().seek_before_primary_effect(terminator_location);
|
||||
self.initialized_at_curr_loc(mpi)
|
||||
}
|
||||
|
||||
|
|
@ -491,7 +527,8 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> {
|
|||
/// **Warning:** Does not account for the result of `Call`
|
||||
/// instructions.
|
||||
fn initialized_at_exit(&mut self, block: BasicBlock, mpi: MovePathIndex) -> bool {
|
||||
self.flow_inits.seek_after_primary_effect(self.body().terminator_loc(block));
|
||||
let terminator_location = self.body().terminator_loc(block);
|
||||
self.flow_inits().seek_after_primary_effect(terminator_location);
|
||||
self.initialized_at_curr_loc(mpi)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -30,8 +30,6 @@ use rustc_middle::ty::{
|
|||
TypeVisitableExt, UserArgs, UserTypeAnnotationIndex, fold_regions,
|
||||
};
|
||||
use rustc_middle::{bug, span_bug};
|
||||
use rustc_mir_dataflow::ResultsCursor;
|
||||
use rustc_mir_dataflow::impls::MaybeInitializedPlaces;
|
||||
use rustc_mir_dataflow::move_paths::MoveData;
|
||||
use rustc_mir_dataflow::points::DenseLocationMap;
|
||||
use rustc_span::def_id::CRATE_DEF_ID;
|
||||
|
|
@ -97,10 +95,9 @@ mod relate_tys;
|
|||
/// - `location_table` -- for datalog polonius, the map between `Location`s and `RichLocation`s
|
||||
/// - `borrow_set` -- information about borrows occurring in `body`
|
||||
/// - `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
|
||||
/// - `location_map` -- map between MIR `Location` and `PointIndex`
|
||||
pub(crate) fn type_check<'a, 'tcx>(
|
||||
pub(crate) fn type_check<'tcx>(
|
||||
root_cx: &mut BorrowCheckRootCtxt<'tcx>,
|
||||
infcx: &BorrowckInferCtxt<'tcx>,
|
||||
body: &Body<'tcx>,
|
||||
|
|
@ -109,7 +106,6 @@ pub(crate) fn type_check<'a, 'tcx>(
|
|||
location_table: &PoloniusLocationTable,
|
||||
borrow_set: &BorrowSet<'tcx>,
|
||||
polonius_facts: &mut Option<PoloniusFacts>,
|
||||
flow_inits: ResultsCursor<'a, 'tcx, MaybeInitializedPlaces<'a, 'tcx>>,
|
||||
move_data: &MoveData<'tcx>,
|
||||
location_map: Rc<DenseLocationMap>,
|
||||
) -> MirTypeckResults<'tcx> {
|
||||
|
|
@ -167,7 +163,7 @@ pub(crate) fn type_check<'a, 'tcx>(
|
|||
typeck.equate_inputs_and_outputs(&normalized_inputs_and_output);
|
||||
typeck.check_signature_annotation();
|
||||
|
||||
liveness::generate(&mut typeck, &location_map, flow_inits, move_data);
|
||||
liveness::generate(&mut typeck, &location_map, move_data);
|
||||
|
||||
let opaque_type_values =
|
||||
opaque_types::take_opaques_and_register_member_constraints(&mut typeck);
|
||||
|
|
@ -474,17 +470,18 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
|||
let projected_ty = curr_projected_ty.projection_ty_core(
|
||||
tcx,
|
||||
proj,
|
||||
|this, field, ()| {
|
||||
let ty = this.field_ty(tcx, field);
|
||||
self.structurally_resolve(ty, locations)
|
||||
},
|
||||
|_, _| unreachable!(),
|
||||
|ty| self.structurally_resolve(ty, locations),
|
||||
|ty, variant_index, field, ()| PlaceTy::field_ty(tcx, ty, variant_index, field),
|
||||
|_| unreachable!(),
|
||||
);
|
||||
curr_projected_ty = projected_ty;
|
||||
}
|
||||
trace!(?curr_projected_ty);
|
||||
|
||||
let ty = curr_projected_ty.ty;
|
||||
// Need to renormalize `a` as typecheck may have failed to normalize
|
||||
// higher-ranked aliases if normalization was ambiguous due to inference.
|
||||
let a = self.normalize(a, locations);
|
||||
let ty = self.normalize(curr_projected_ty.ty, locations);
|
||||
self.relate_types(ty, v.xform(ty::Contravariant), a, locations, category)?;
|
||||
|
||||
Ok(())
|
||||
|
|
@ -691,7 +688,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
|||
if !self.unsized_feature_enabled() {
|
||||
let trait_ref = ty::TraitRef::new(
|
||||
tcx,
|
||||
tcx.require_lang_item(LangItem::Sized, Some(self.last_span)),
|
||||
tcx.require_lang_item(LangItem::Sized, self.last_span),
|
||||
[place_ty],
|
||||
);
|
||||
self.prove_trait_ref(
|
||||
|
|
@ -1013,7 +1010,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
|||
let ty = place.ty(self.body, tcx).ty;
|
||||
let trait_ref = ty::TraitRef::new(
|
||||
tcx,
|
||||
tcx.require_lang_item(LangItem::Copy, Some(span)),
|
||||
tcx.require_lang_item(LangItem::Copy, span),
|
||||
[ty],
|
||||
);
|
||||
|
||||
|
|
@ -1028,11 +1025,8 @@ impl<'a, 'tcx> Visitor<'tcx> for 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, span), [ty]);
|
||||
|
||||
self.prove_trait_ref(
|
||||
trait_ref,
|
||||
|
|
@ -1044,11 +1038,8 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
|||
&Rvalue::NullaryOp(NullOp::UbChecks, _) => {}
|
||||
|
||||
Rvalue::ShallowInitBox(_operand, 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, span), [*ty]);
|
||||
|
||||
self.prove_trait_ref(
|
||||
trait_ref,
|
||||
|
|
@ -1225,7 +1216,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
|||
let &ty = ty;
|
||||
let trait_ref = ty::TraitRef::new(
|
||||
tcx,
|
||||
tcx.require_lang_item(LangItem::CoerceUnsized, Some(span)),
|
||||
tcx.require_lang_item(LangItem::CoerceUnsized, span),
|
||||
[op.ty(self.body, tcx), ty],
|
||||
);
|
||||
|
||||
|
|
@ -1814,7 +1805,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
|||
if let PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy) = context {
|
||||
let trait_ref = ty::TraitRef::new(
|
||||
tcx,
|
||||
tcx.require_lang_item(LangItem::Copy, Some(self.last_span)),
|
||||
tcx.require_lang_item(LangItem::Copy, self.last_span),
|
||||
[place_ty.ty],
|
||||
);
|
||||
|
||||
|
|
@ -1852,7 +1843,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
|||
| ProjectionElem::Downcast(..) => {}
|
||||
ProjectionElem::Field(field, fty) => {
|
||||
let fty = self.normalize(fty, location);
|
||||
let ty = base_ty.field_ty(tcx, field);
|
||||
let ty = PlaceTy::field_ty(tcx, base_ty.ty, base_ty.variant_index, field);
|
||||
let ty = self.normalize(ty, location);
|
||||
debug!(?fty, ?ty);
|
||||
|
||||
|
|
|
|||
|
|
@ -221,7 +221,7 @@ fn register_member_constraints<'tcx>(
|
|||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(i, _)| variances[*i] == ty::Invariant)
|
||||
.filter_map(|(_, arg)| match arg.unpack() {
|
||||
.filter_map(|(_, arg)| match arg.kind() {
|
||||
GenericArgKind::Lifetime(r) => Some(typeck.to_region_vid(r)),
|
||||
GenericArgKind::Type(_) | GenericArgKind::Const(_) => None,
|
||||
})
|
||||
|
|
|
|||
|
|
@ -544,10 +544,10 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> {
|
|||
// (as it's created inside the body itself, not passed in from outside).
|
||||
if let DefiningTy::FnDef(def_id, _) = defining_ty {
|
||||
if self.infcx.tcx.fn_sig(def_id).skip_binder().c_variadic() {
|
||||
let va_list_did = self.infcx.tcx.require_lang_item(
|
||||
LangItem::VaList,
|
||||
Some(self.infcx.tcx.def_span(self.mir_def)),
|
||||
);
|
||||
let va_list_did = self
|
||||
.infcx
|
||||
.tcx
|
||||
.require_lang_item(LangItem::VaList, self.infcx.tcx.def_span(self.mir_def));
|
||||
|
||||
let reg_vid = self
|
||||
.infcx
|
||||
|
|
|
|||
|
|
@ -24,6 +24,9 @@ rustc_lint_defs = { path = "../rustc_lint_defs" }
|
|||
rustc_macros = { path = "../rustc_macros" }
|
||||
rustc_parse = { path = "../rustc_parse" }
|
||||
rustc_parse_format = { path = "../rustc_parse_format" }
|
||||
# We must use the proc_macro version that we will compile proc-macros against,
|
||||
# not the one from our own sysroot.
|
||||
rustc_proc_macro = { path = "../rustc_proc_macro" }
|
||||
rustc_session = { path = "../rustc_session" }
|
||||
rustc_span = { path = "../rustc_span" }
|
||||
rustc_target = { path = "../rustc_target" }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,11 @@
|
|||
builtin_macros_alloc_error_must_be_fn = alloc_error_handler must be a function
|
||||
builtin_macros_alloc_must_statics = allocators must be statics
|
||||
|
||||
builtin_macros_asm_attribute_not_supported =
|
||||
this attribute is not supported on assembly
|
||||
builtin_macros_asm_cfg =
|
||||
the `#[cfg(/* ... */)]` and `#[cfg_attr(/* ... */)]` attributes on assembly are unstable
|
||||
|
||||
builtin_macros_asm_clobber_abi = clobber_abi
|
||||
builtin_macros_asm_clobber_no_reg = asm with `clobber_abi` must specify explicit registers for outputs
|
||||
builtin_macros_asm_clobber_outputs = generic outputs
|
||||
|
|
@ -9,17 +14,6 @@ builtin_macros_asm_duplicate_arg = duplicate argument named `{$name}`
|
|||
.label = previously here
|
||||
.arg = duplicate argument
|
||||
|
||||
builtin_macros_asm_expected_comma = expected token: `,`
|
||||
.label = expected `,`
|
||||
|
||||
builtin_macros_asm_expected_other = expected operand, {$is_inline_asm ->
|
||||
[false] options
|
||||
*[true] clobber_abi, options
|
||||
}, or additional template string
|
||||
|
||||
builtin_macros_asm_expected_string_literal = expected string literal
|
||||
.label = not a string literal
|
||||
|
||||
builtin_macros_asm_explicit_register_name = explicit register arguments cannot have names
|
||||
|
||||
builtin_macros_asm_mayunwind = asm labels are not allowed with the `may_unwind` option
|
||||
|
|
@ -45,17 +39,8 @@ builtin_macros_asm_pure_combine = the `pure` option must be combined with either
|
|||
|
||||
builtin_macros_asm_pure_no_output = asm with the `pure` option must have at least one output
|
||||
|
||||
builtin_macros_asm_requires_template = requires at least a template string argument
|
||||
|
||||
builtin_macros_asm_sym_no_path = expected a path for argument to `sym`
|
||||
|
||||
builtin_macros_asm_underscore_input = _ cannot be used for input operands
|
||||
|
||||
builtin_macros_asm_unsupported_clobber_abi = `clobber_abi` cannot be used with `{$macro_name}!`
|
||||
|
||||
builtin_macros_asm_unsupported_operand = the `{$symbol}` operand cannot be used with `{$macro_name}!`
|
||||
.label = the `{$symbol}` operand is not meaningful for global-scoped inline assembly, remove it
|
||||
|
||||
builtin_macros_asm_unsupported_option = the `{$symbol}` option cannot be used with `{$macro_name}!`
|
||||
.label = the `{$symbol}` option is not meaningful for global-scoped inline assembly
|
||||
.suggestion = remove this option
|
||||
|
|
@ -71,7 +56,6 @@ builtin_macros_assert_requires_expression = macro requires an expression as an a
|
|||
|
||||
builtin_macros_autodiff = autodiff must be applied to function
|
||||
builtin_macros_autodiff_missing_config = autodiff requires at least a name and mode
|
||||
builtin_macros_autodiff_mode = unknown Mode: `{$mode}`. Use `Forward` or `Reverse`
|
||||
builtin_macros_autodiff_mode_activity = {$act} can not be used in {$mode} Mode
|
||||
builtin_macros_autodiff_not_build = this rustc version does not support autodiff
|
||||
builtin_macros_autodiff_number_activities = expected {$expected} activities, but found {$found}
|
||||
|
|
@ -162,7 +146,10 @@ builtin_macros_expected_comma_in_list = expected token: `,`
|
|||
|
||||
builtin_macros_expected_one_cfg_pattern = expected 1 cfg-pattern
|
||||
|
||||
builtin_macros_expected_register_class_or_explicit_register = expected register class or explicit register
|
||||
builtin_macros_expected_other = expected operand, {$is_inline_asm ->
|
||||
[false] options
|
||||
*[true] clobber_abi, options
|
||||
}, or additional template string
|
||||
|
||||
builtin_macros_export_macro_rules = cannot export macro_rules! macros from a `proc-macro` crate type currently
|
||||
|
||||
|
|
@ -255,8 +242,6 @@ builtin_macros_no_default_variant = `#[derive(Default)]` on enum with no `#[defa
|
|||
.label = this enum needs a unit variant marked with `#[default]`
|
||||
.suggestion = make this unit variant default by placing `#[default]` on it
|
||||
|
||||
builtin_macros_non_abi = at least one abi must be provided as an argument to `clobber_abi`
|
||||
|
||||
builtin_macros_non_exhaustive_default = default variant must be exhaustive
|
||||
.label = declared `#[non_exhaustive]` here
|
||||
.help = consider a manual implementation of `Default`
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
use ast::token::IdentIsRaw;
|
||||
use lint::BuiltinLintDiag;
|
||||
use rustc_ast::ptr::P;
|
||||
use rustc_ast::tokenstream::TokenStream;
|
||||
|
|
@ -7,39 +6,16 @@ use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
|
|||
use rustc_errors::PResult;
|
||||
use rustc_expand::base::*;
|
||||
use rustc_index::bit_set::GrowableBitSet;
|
||||
use rustc_parse::exp;
|
||||
use rustc_parse::parser::{ExpKeywordPair, Parser};
|
||||
use rustc_parse::parser::asm::*;
|
||||
use rustc_session::lint;
|
||||
use rustc_span::{ErrorGuaranteed, InnerSpan, Span, Symbol, kw};
|
||||
use rustc_session::parse::feature_err;
|
||||
use rustc_span::{ErrorGuaranteed, InnerSpan, Span, Symbol, sym};
|
||||
use rustc_target::asm::InlineAsmArch;
|
||||
use smallvec::smallvec;
|
||||
use {rustc_ast as ast, rustc_parse_format as parse};
|
||||
|
||||
use crate::errors;
|
||||
use crate::util::{ExprToSpannedString, expr_to_spanned_string};
|
||||
|
||||
/// An argument to one of the `asm!` macros. The argument is syntactically valid, but is otherwise
|
||||
/// not validated at all.
|
||||
pub struct AsmArg {
|
||||
pub kind: AsmArgKind,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
pub enum AsmArgKind {
|
||||
Template(P<ast::Expr>),
|
||||
Operand(Option<Symbol>, ast::InlineAsmOperand),
|
||||
Options(Vec<AsmOption>),
|
||||
ClobberAbi(Vec<(Symbol, Span)>),
|
||||
}
|
||||
|
||||
pub struct AsmOption {
|
||||
pub symbol: Symbol,
|
||||
pub span: Span,
|
||||
// A bitset, with only the bit for this option's symbol set.
|
||||
pub options: ast::InlineAsmOptions,
|
||||
// Used when suggesting to remove an option.
|
||||
pub span_with_comma: Span,
|
||||
}
|
||||
use crate::{errors, fluent_generated as fluent};
|
||||
|
||||
/// Validated assembly arguments, ready for macro expansion.
|
||||
struct ValidatedAsmArgs {
|
||||
|
|
@ -52,215 +28,6 @@ struct ValidatedAsmArgs {
|
|||
pub options_spans: Vec<Span>,
|
||||
}
|
||||
|
||||
/// Used for better error messages when operand types are used that are not
|
||||
/// supported by the current macro (e.g. `in` or `out` for `global_asm!`)
|
||||
///
|
||||
/// returns
|
||||
///
|
||||
/// - `Ok(true)` if the current token matches the keyword, and was expected
|
||||
/// - `Ok(false)` if the current token does not match the keyword
|
||||
/// - `Err(_)` if the current token matches the keyword, but was not expected
|
||||
fn eat_operand_keyword<'a>(
|
||||
p: &mut Parser<'a>,
|
||||
exp: ExpKeywordPair,
|
||||
asm_macro: AsmMacro,
|
||||
) -> PResult<'a, bool> {
|
||||
if matches!(asm_macro, AsmMacro::Asm) {
|
||||
Ok(p.eat_keyword(exp))
|
||||
} else {
|
||||
let span = p.token.span;
|
||||
if p.eat_keyword_noexpect(exp.kw) {
|
||||
// in gets printed as `r#in` otherwise
|
||||
let symbol = if exp.kw == kw::In { "in" } else { exp.kw.as_str() };
|
||||
Err(p.dcx().create_err(errors::AsmUnsupportedOperand {
|
||||
span,
|
||||
symbol,
|
||||
macro_name: asm_macro.macro_name(),
|
||||
}))
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_asm_operand<'a>(
|
||||
p: &mut Parser<'a>,
|
||||
asm_macro: AsmMacro,
|
||||
) -> PResult<'a, Option<ast::InlineAsmOperand>> {
|
||||
let dcx = p.dcx();
|
||||
|
||||
Ok(Some(if eat_operand_keyword(p, exp!(In), asm_macro)? {
|
||||
let reg = parse_reg(p)?;
|
||||
if p.eat_keyword(exp!(Underscore)) {
|
||||
let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span });
|
||||
return Err(err);
|
||||
}
|
||||
let expr = p.parse_expr()?;
|
||||
ast::InlineAsmOperand::In { reg, expr }
|
||||
} else if eat_operand_keyword(p, exp!(Out), asm_macro)? {
|
||||
let reg = parse_reg(p)?;
|
||||
let expr = if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) };
|
||||
ast::InlineAsmOperand::Out { reg, expr, late: false }
|
||||
} else if eat_operand_keyword(p, exp!(Lateout), asm_macro)? {
|
||||
let reg = parse_reg(p)?;
|
||||
let expr = if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) };
|
||||
ast::InlineAsmOperand::Out { reg, expr, late: true }
|
||||
} else if eat_operand_keyword(p, exp!(Inout), asm_macro)? {
|
||||
let reg = parse_reg(p)?;
|
||||
if p.eat_keyword(exp!(Underscore)) {
|
||||
let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span });
|
||||
return Err(err);
|
||||
}
|
||||
let expr = p.parse_expr()?;
|
||||
if p.eat(exp!(FatArrow)) {
|
||||
let out_expr =
|
||||
if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) };
|
||||
ast::InlineAsmOperand::SplitInOut { reg, in_expr: expr, out_expr, late: false }
|
||||
} else {
|
||||
ast::InlineAsmOperand::InOut { reg, expr, late: false }
|
||||
}
|
||||
} else if eat_operand_keyword(p, exp!(Inlateout), asm_macro)? {
|
||||
let reg = parse_reg(p)?;
|
||||
if p.eat_keyword(exp!(Underscore)) {
|
||||
let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span });
|
||||
return Err(err);
|
||||
}
|
||||
let expr = p.parse_expr()?;
|
||||
if p.eat(exp!(FatArrow)) {
|
||||
let out_expr =
|
||||
if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) };
|
||||
ast::InlineAsmOperand::SplitInOut { reg, in_expr: expr, out_expr, late: true }
|
||||
} else {
|
||||
ast::InlineAsmOperand::InOut { reg, expr, late: true }
|
||||
}
|
||||
} else if eat_operand_keyword(p, exp!(Label), asm_macro)? {
|
||||
let block = p.parse_block()?;
|
||||
ast::InlineAsmOperand::Label { block }
|
||||
} else if p.eat_keyword(exp!(Const)) {
|
||||
let anon_const = p.parse_expr_anon_const()?;
|
||||
ast::InlineAsmOperand::Const { anon_const }
|
||||
} else if p.eat_keyword(exp!(Sym)) {
|
||||
let expr = p.parse_expr()?;
|
||||
let ast::ExprKind::Path(qself, path) = &expr.kind else {
|
||||
let err = dcx.create_err(errors::AsmSymNoPath { span: expr.span });
|
||||
return Err(err);
|
||||
};
|
||||
let sym =
|
||||
ast::InlineAsmSym { id: ast::DUMMY_NODE_ID, qself: qself.clone(), path: path.clone() };
|
||||
ast::InlineAsmOperand::Sym { sym }
|
||||
} else {
|
||||
return Ok(None);
|
||||
}))
|
||||
}
|
||||
|
||||
// Public for rustfmt.
|
||||
pub fn parse_asm_args<'a>(
|
||||
p: &mut Parser<'a>,
|
||||
sp: Span,
|
||||
asm_macro: AsmMacro,
|
||||
) -> PResult<'a, Vec<AsmArg>> {
|
||||
let dcx = p.dcx();
|
||||
|
||||
if p.token == token::Eof {
|
||||
return Err(dcx.create_err(errors::AsmRequiresTemplate { span: sp }));
|
||||
}
|
||||
|
||||
let mut args = Vec::new();
|
||||
|
||||
let first_template = p.parse_expr()?;
|
||||
args.push(AsmArg { span: first_template.span, kind: AsmArgKind::Template(first_template) });
|
||||
|
||||
let mut allow_templates = true;
|
||||
|
||||
while p.token != token::Eof {
|
||||
if !p.eat(exp!(Comma)) {
|
||||
if allow_templates {
|
||||
// After a template string, we always expect *only* a comma...
|
||||
return Err(dcx.create_err(errors::AsmExpectedComma { span: p.token.span }));
|
||||
} else {
|
||||
// ...after that delegate to `expect` to also include the other expected tokens.
|
||||
return Err(p.expect(exp!(Comma)).err().unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
// Accept trailing commas.
|
||||
if p.token == token::Eof {
|
||||
break;
|
||||
}
|
||||
|
||||
let span_start = p.token.span;
|
||||
|
||||
// Parse `clobber_abi`.
|
||||
if p.eat_keyword(exp!(ClobberAbi)) {
|
||||
allow_templates = false;
|
||||
|
||||
args.push(AsmArg {
|
||||
kind: AsmArgKind::ClobberAbi(parse_clobber_abi(p)?),
|
||||
span: span_start.to(p.prev_token.span),
|
||||
});
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Parse `options`.
|
||||
if p.eat_keyword(exp!(Options)) {
|
||||
allow_templates = false;
|
||||
|
||||
args.push(AsmArg {
|
||||
kind: AsmArgKind::Options(parse_options(p, asm_macro)?),
|
||||
span: span_start.to(p.prev_token.span),
|
||||
});
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Parse operand names.
|
||||
let name = if p.token.is_ident() && p.look_ahead(1, |t| *t == token::Eq) {
|
||||
let (ident, _) = p.token.ident().unwrap();
|
||||
p.bump();
|
||||
p.expect(exp!(Eq))?;
|
||||
allow_templates = false;
|
||||
Some(ident.name)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if let Some(op) = parse_asm_operand(p, asm_macro)? {
|
||||
allow_templates = false;
|
||||
|
||||
args.push(AsmArg {
|
||||
span: span_start.to(p.prev_token.span),
|
||||
kind: AsmArgKind::Operand(name, op),
|
||||
});
|
||||
} else if allow_templates {
|
||||
let template = p.parse_expr()?;
|
||||
// If it can't possibly expand to a string, provide diagnostics here to include other
|
||||
// things it could have been.
|
||||
match template.kind {
|
||||
ast::ExprKind::Lit(token_lit)
|
||||
if matches!(
|
||||
token_lit.kind,
|
||||
token::LitKind::Str | token::LitKind::StrRaw(_)
|
||||
) => {}
|
||||
ast::ExprKind::MacCall(..) => {}
|
||||
_ => {
|
||||
let err = dcx.create_err(errors::AsmExpectedOther {
|
||||
span: template.span,
|
||||
is_inline_asm: matches!(asm_macro, AsmMacro::Asm),
|
||||
});
|
||||
return Err(err);
|
||||
}
|
||||
}
|
||||
|
||||
args.push(AsmArg { span: template.span, kind: AsmArgKind::Template(template) });
|
||||
} else {
|
||||
p.unexpected_any()?
|
||||
}
|
||||
}
|
||||
|
||||
Ok(args)
|
||||
}
|
||||
|
||||
fn parse_args<'a>(
|
||||
ecx: &ExtCtxt<'a>,
|
||||
sp: Span,
|
||||
|
|
@ -278,6 +45,13 @@ fn validate_asm_args<'a>(
|
|||
) -> PResult<'a, ValidatedAsmArgs> {
|
||||
let dcx = ecx.dcx();
|
||||
|
||||
let strip_unconfigured = rustc_expand::config::StripUnconfigured {
|
||||
sess: ecx.sess,
|
||||
features: Some(ecx.ecfg.features),
|
||||
config_tokens: false,
|
||||
lint_node_id: ecx.current_expansion.lint_node_id,
|
||||
};
|
||||
|
||||
let mut validated = ValidatedAsmArgs {
|
||||
templates: vec![],
|
||||
operands: vec![],
|
||||
|
|
@ -291,6 +65,26 @@ fn validate_asm_args<'a>(
|
|||
let mut allow_templates = true;
|
||||
|
||||
for arg in args {
|
||||
for attr in arg.attributes.0.iter() {
|
||||
match attr.name() {
|
||||
Some(sym::cfg | sym::cfg_attr) => {
|
||||
if !ecx.ecfg.features.asm_cfg() {
|
||||
let span = attr.span();
|
||||
feature_err(ecx.sess, sym::asm_cfg, span, fluent::builtin_macros_asm_cfg)
|
||||
.emit();
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
ecx.dcx().emit_err(errors::AsmAttributeNotSupported { span: attr.span() });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Skip arguments that are configured out.
|
||||
if ecx.ecfg.features.asm_cfg() && strip_unconfigured.configure(arg.attributes).is_none() {
|
||||
continue;
|
||||
}
|
||||
|
||||
match arg.kind {
|
||||
AsmArgKind::Template(template) => {
|
||||
// The error for the first template is delayed.
|
||||
|
|
@ -479,103 +273,6 @@ fn validate_asm_args<'a>(
|
|||
Ok(validated)
|
||||
}
|
||||
|
||||
fn parse_options<'a>(p: &mut Parser<'a>, asm_macro: AsmMacro) -> PResult<'a, Vec<AsmOption>> {
|
||||
p.expect(exp!(OpenParen))?;
|
||||
|
||||
let mut asm_options = Vec::new();
|
||||
|
||||
while !p.eat(exp!(CloseParen)) {
|
||||
const OPTIONS: [(ExpKeywordPair, ast::InlineAsmOptions); ast::InlineAsmOptions::COUNT] = [
|
||||
(exp!(Pure), ast::InlineAsmOptions::PURE),
|
||||
(exp!(Nomem), ast::InlineAsmOptions::NOMEM),
|
||||
(exp!(Readonly), ast::InlineAsmOptions::READONLY),
|
||||
(exp!(PreservesFlags), ast::InlineAsmOptions::PRESERVES_FLAGS),
|
||||
(exp!(Noreturn), ast::InlineAsmOptions::NORETURN),
|
||||
(exp!(Nostack), ast::InlineAsmOptions::NOSTACK),
|
||||
(exp!(MayUnwind), ast::InlineAsmOptions::MAY_UNWIND),
|
||||
(exp!(AttSyntax), ast::InlineAsmOptions::ATT_SYNTAX),
|
||||
(exp!(Raw), ast::InlineAsmOptions::RAW),
|
||||
];
|
||||
|
||||
'blk: {
|
||||
for (exp, options) in OPTIONS {
|
||||
// Gives a more accurate list of expected next tokens.
|
||||
let kw_matched = if asm_macro.is_supported_option(options) {
|
||||
p.eat_keyword(exp)
|
||||
} else {
|
||||
p.eat_keyword_noexpect(exp.kw)
|
||||
};
|
||||
|
||||
if kw_matched {
|
||||
let span = p.prev_token.span;
|
||||
let span_with_comma =
|
||||
if p.token == token::Comma { span.to(p.token.span) } else { span };
|
||||
|
||||
asm_options.push(AsmOption { symbol: exp.kw, span, options, span_with_comma });
|
||||
break 'blk;
|
||||
}
|
||||
}
|
||||
|
||||
return p.unexpected_any();
|
||||
}
|
||||
|
||||
// Allow trailing commas.
|
||||
if p.eat(exp!(CloseParen)) {
|
||||
break;
|
||||
}
|
||||
p.expect(exp!(Comma))?;
|
||||
}
|
||||
|
||||
Ok(asm_options)
|
||||
}
|
||||
|
||||
fn parse_clobber_abi<'a>(p: &mut Parser<'a>) -> PResult<'a, Vec<(Symbol, Span)>> {
|
||||
p.expect(exp!(OpenParen))?;
|
||||
|
||||
if p.eat(exp!(CloseParen)) {
|
||||
return Err(p.dcx().create_err(errors::NonABI { span: p.token.span }));
|
||||
}
|
||||
|
||||
let mut new_abis = Vec::new();
|
||||
while !p.eat(exp!(CloseParen)) {
|
||||
match p.parse_str_lit() {
|
||||
Ok(str_lit) => {
|
||||
new_abis.push((str_lit.symbol_unescaped, str_lit.span));
|
||||
}
|
||||
Err(opt_lit) => {
|
||||
let span = opt_lit.map_or(p.token.span, |lit| lit.span);
|
||||
return Err(p.dcx().create_err(errors::AsmExpectedStringLiteral { span }));
|
||||
}
|
||||
};
|
||||
|
||||
// Allow trailing commas
|
||||
if p.eat(exp!(CloseParen)) {
|
||||
break;
|
||||
}
|
||||
p.expect(exp!(Comma))?;
|
||||
}
|
||||
|
||||
Ok(new_abis)
|
||||
}
|
||||
|
||||
fn parse_reg<'a>(p: &mut Parser<'a>) -> PResult<'a, ast::InlineAsmRegOrRegClass> {
|
||||
p.expect(exp!(OpenParen))?;
|
||||
let result = match p.token.uninterpolate().kind {
|
||||
token::Ident(name, IdentIsRaw::No) => ast::InlineAsmRegOrRegClass::RegClass(name),
|
||||
token::Literal(token::Lit { kind: token::LitKind::Str, symbol, suffix: _ }) => {
|
||||
ast::InlineAsmRegOrRegClass::Reg(symbol)
|
||||
}
|
||||
_ => {
|
||||
return Err(p.dcx().create_err(errors::ExpectedRegisterClassOrExplicitRegister {
|
||||
span: p.token.span,
|
||||
}));
|
||||
}
|
||||
};
|
||||
p.bump();
|
||||
p.expect(exp!(CloseParen))?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn expand_preparsed_asm(
|
||||
ecx: &mut ExtCtxt<'_>,
|
||||
asm_macro: AsmMacro,
|
||||
|
|
|
|||
|
|
@ -86,27 +86,23 @@ mod llvm_enzyme {
|
|||
ecx: &mut ExtCtxt<'_>,
|
||||
meta_item: &ThinVec<MetaItemInner>,
|
||||
has_ret: bool,
|
||||
mode: DiffMode,
|
||||
) -> AutoDiffAttrs {
|
||||
let dcx = ecx.sess.dcx();
|
||||
let mode = name(&meta_item[1]);
|
||||
let Ok(mode) = DiffMode::from_str(&mode) else {
|
||||
dcx.emit_err(errors::AutoDiffInvalidMode { span: meta_item[1].span(), mode });
|
||||
return AutoDiffAttrs::error();
|
||||
};
|
||||
|
||||
// Now we check, whether the user wants autodiff in batch/vector mode, or scalar mode.
|
||||
// If he doesn't specify an integer (=width), we default to scalar mode, thus width=1.
|
||||
let mut first_activity = 2;
|
||||
let mut first_activity = 1;
|
||||
|
||||
let width = if let [_, _, x, ..] = &meta_item[..]
|
||||
let width = if let [_, x, ..] = &meta_item[..]
|
||||
&& let Some(x) = width(x)
|
||||
{
|
||||
first_activity = 3;
|
||||
first_activity = 2;
|
||||
match x.try_into() {
|
||||
Ok(x) => x,
|
||||
Err(_) => {
|
||||
dcx.emit_err(errors::AutoDiffInvalidWidth {
|
||||
span: meta_item[2].span(),
|
||||
span: meta_item[1].span(),
|
||||
width: x,
|
||||
});
|
||||
return AutoDiffAttrs::error();
|
||||
|
|
@ -165,6 +161,24 @@ mod llvm_enzyme {
|
|||
ts.push(TokenTree::Token(comma.clone(), Spacing::Alone));
|
||||
}
|
||||
|
||||
pub(crate) fn expand_forward(
|
||||
ecx: &mut ExtCtxt<'_>,
|
||||
expand_span: Span,
|
||||
meta_item: &ast::MetaItem,
|
||||
item: Annotatable,
|
||||
) -> Vec<Annotatable> {
|
||||
expand_with_mode(ecx, expand_span, meta_item, item, DiffMode::Forward)
|
||||
}
|
||||
|
||||
pub(crate) fn expand_reverse(
|
||||
ecx: &mut ExtCtxt<'_>,
|
||||
expand_span: Span,
|
||||
meta_item: &ast::MetaItem,
|
||||
item: Annotatable,
|
||||
) -> Vec<Annotatable> {
|
||||
expand_with_mode(ecx, expand_span, meta_item, item, DiffMode::Reverse)
|
||||
}
|
||||
|
||||
/// We expand the autodiff macro to generate a new placeholder function which passes
|
||||
/// type-checking and can be called by users. The function body of the placeholder function will
|
||||
/// later be replaced on LLVM-IR level, so the design of the body is less important and for now
|
||||
|
|
@ -198,11 +212,12 @@ mod llvm_enzyme {
|
|||
/// ```
|
||||
/// FIXME(ZuseZ4): Once autodiff is enabled by default, make this a doc comment which is checked
|
||||
/// in CI.
|
||||
pub(crate) fn expand(
|
||||
pub(crate) fn expand_with_mode(
|
||||
ecx: &mut ExtCtxt<'_>,
|
||||
expand_span: Span,
|
||||
meta_item: &ast::MetaItem,
|
||||
mut item: Annotatable,
|
||||
mode: DiffMode,
|
||||
) -> Vec<Annotatable> {
|
||||
if cfg!(not(llvm_enzyme)) {
|
||||
ecx.sess.dcx().emit_err(errors::AutoDiffSupportNotBuild { span: meta_item.span });
|
||||
|
|
@ -245,29 +260,41 @@ mod llvm_enzyme {
|
|||
// create TokenStream from vec elemtents:
|
||||
// meta_item doesn't have a .tokens field
|
||||
let mut ts: Vec<TokenTree> = vec![];
|
||||
if meta_item_vec.len() < 2 {
|
||||
// At the bare minimum, we need a fnc name and a mode, even for a dummy function with no
|
||||
// input and output args.
|
||||
if meta_item_vec.len() < 1 {
|
||||
// At the bare minimum, we need a fnc name.
|
||||
dcx.emit_err(errors::AutoDiffMissingConfig { span: item.span() });
|
||||
return vec![item];
|
||||
}
|
||||
|
||||
meta_item_inner_to_ts(&meta_item_vec[1], &mut ts);
|
||||
let mode_symbol = match mode {
|
||||
DiffMode::Forward => sym::Forward,
|
||||
DiffMode::Reverse => sym::Reverse,
|
||||
_ => unreachable!("Unsupported mode: {:?}", mode),
|
||||
};
|
||||
|
||||
// Insert mode token
|
||||
let mode_token = Token::new(TokenKind::Ident(mode_symbol, false.into()), Span::default());
|
||||
ts.insert(0, TokenTree::Token(mode_token, Spacing::Joint));
|
||||
ts.insert(
|
||||
1,
|
||||
TokenTree::Token(Token::new(TokenKind::Comma, Span::default()), Spacing::Alone),
|
||||
);
|
||||
|
||||
// Now, if the user gave a width (vector aka batch-mode ad), then we copy it.
|
||||
// If it is not given, we default to 1 (scalar mode).
|
||||
let start_position;
|
||||
let kind: LitKind = LitKind::Integer;
|
||||
let symbol;
|
||||
if meta_item_vec.len() >= 3
|
||||
&& let Some(width) = width(&meta_item_vec[2])
|
||||
if meta_item_vec.len() >= 2
|
||||
&& let Some(width) = width(&meta_item_vec[1])
|
||||
{
|
||||
start_position = 3;
|
||||
start_position = 2;
|
||||
symbol = Symbol::intern(&width.to_string());
|
||||
} else {
|
||||
start_position = 2;
|
||||
start_position = 1;
|
||||
symbol = sym::integer(1);
|
||||
}
|
||||
|
||||
let l: Lit = Lit { kind, symbol, suffix: None };
|
||||
let t = Token::new(TokenKind::Literal(l), Span::default());
|
||||
let comma = Token::new(TokenKind::Comma, Span::default());
|
||||
|
|
@ -289,7 +316,7 @@ mod llvm_enzyme {
|
|||
ts.pop();
|
||||
let ts: TokenStream = TokenStream::from_iter(ts);
|
||||
|
||||
let x: AutoDiffAttrs = from_ast(ecx, &meta_item_vec, has_ret);
|
||||
let x: AutoDiffAttrs = from_ast(ecx, &meta_item_vec, has_ret, mode);
|
||||
if !x.is_active() {
|
||||
// We encountered an error, so we return the original item.
|
||||
// This allows us to potentially parse other attributes.
|
||||
|
|
@ -1017,4 +1044,4 @@ mod llvm_enzyme {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) use llvm_enzyme::expand;
|
||||
pub(crate) use llvm_enzyme::{expand_forward, expand_reverse};
|
||||
|
|
|
|||
|
|
@ -34,8 +34,8 @@ pub(crate) fn expand_deriving_clone(
|
|||
let is_simple;
|
||||
match item {
|
||||
Annotatable::Item(annitem) => match &annitem.kind {
|
||||
ItemKind::Struct(_, _, Generics { params, .. })
|
||||
| ItemKind::Enum(_, _, Generics { params, .. }) => {
|
||||
ItemKind::Struct(_, Generics { params, .. }, _)
|
||||
| ItemKind::Enum(_, Generics { params, .. }, _) => {
|
||||
let container_id = cx.current_expansion.id.expn_data().parent.expect_local();
|
||||
let has_derive_copy = cx.resolver.has_derive_copy(container_id);
|
||||
if has_derive_copy
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ use crate::deriving::generic::ty::*;
|
|||
use crate::deriving::generic::*;
|
||||
use crate::deriving::{path_local, path_std};
|
||||
|
||||
/// Expands a `#[derive(PartialEq)]` attribute into an implementation for the
|
||||
/// target item.
|
||||
pub(crate) fn expand_deriving_partial_eq(
|
||||
cx: &ExtCtxt<'_>,
|
||||
span: Span,
|
||||
|
|
@ -16,62 +18,6 @@ pub(crate) fn expand_deriving_partial_eq(
|
|||
push: &mut dyn FnMut(Annotatable),
|
||||
is_const: bool,
|
||||
) {
|
||||
fn cs_eq(cx: &ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> BlockOrExpr {
|
||||
let base = true;
|
||||
let expr = cs_fold(
|
||||
true, // use foldl
|
||||
cx,
|
||||
span,
|
||||
substr,
|
||||
|cx, fold| match fold {
|
||||
CsFold::Single(field) => {
|
||||
let [other_expr] = &field.other_selflike_exprs[..] else {
|
||||
cx.dcx()
|
||||
.span_bug(field.span, "not exactly 2 arguments in `derive(PartialEq)`");
|
||||
};
|
||||
|
||||
// We received arguments of type `&T`. Convert them to type `T` by stripping
|
||||
// any leading `&`. This isn't necessary for type checking, but
|
||||
// it results in better error messages if something goes wrong.
|
||||
//
|
||||
// Note: for arguments that look like `&{ x }`, which occur with packed
|
||||
// structs, this would cause expressions like `{ self.x } == { other.x }`,
|
||||
// which isn't valid Rust syntax. This wouldn't break compilation because these
|
||||
// AST nodes are constructed within the compiler. But it would mean that code
|
||||
// printed by `-Zunpretty=expanded` (or `cargo expand`) would have invalid
|
||||
// syntax, which would be suboptimal. So we wrap these in parens, giving
|
||||
// `({ self.x }) == ({ other.x })`, which is valid syntax.
|
||||
let convert = |expr: &P<Expr>| {
|
||||
if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) =
|
||||
&expr.kind
|
||||
{
|
||||
if let ExprKind::Block(..) = &inner.kind {
|
||||
// `&{ x }` form: remove the `&`, add parens.
|
||||
cx.expr_paren(field.span, inner.clone())
|
||||
} else {
|
||||
// `&x` form: remove the `&`.
|
||||
inner.clone()
|
||||
}
|
||||
} else {
|
||||
expr.clone()
|
||||
}
|
||||
};
|
||||
cx.expr_binary(
|
||||
field.span,
|
||||
BinOpKind::Eq,
|
||||
convert(&field.self_expr),
|
||||
convert(other_expr),
|
||||
)
|
||||
}
|
||||
CsFold::Combine(span, expr1, expr2) => {
|
||||
cx.expr_binary(span, BinOpKind::And, expr1, expr2)
|
||||
}
|
||||
CsFold::Fieldless => cx.expr_bool(span, base),
|
||||
},
|
||||
);
|
||||
BlockOrExpr::new_expr(expr)
|
||||
}
|
||||
|
||||
let structural_trait_def = TraitDef {
|
||||
span,
|
||||
path: path_std!(marker::StructuralPartialEq),
|
||||
|
|
@ -97,7 +43,9 @@ pub(crate) fn expand_deriving_partial_eq(
|
|||
ret_ty: Path(path_local!(bool)),
|
||||
attributes: thin_vec![cx.attr_word(sym::inline, span)],
|
||||
fieldless_variants_strategy: FieldlessVariantsStrategy::Unify,
|
||||
combine_substructure: combine_substructure(Box::new(|a, b, c| cs_eq(a, b, c))),
|
||||
combine_substructure: combine_substructure(Box::new(|a, b, c| {
|
||||
BlockOrExpr::new_expr(get_substructure_equality_expr(a, b, c))
|
||||
})),
|
||||
}];
|
||||
|
||||
let trait_def = TraitDef {
|
||||
|
|
@ -113,3 +61,156 @@ pub(crate) fn expand_deriving_partial_eq(
|
|||
};
|
||||
trait_def.expand(cx, mitem, item, push)
|
||||
}
|
||||
|
||||
/// Generates the equality expression for a struct or enum variant when deriving
|
||||
/// `PartialEq`.
|
||||
///
|
||||
/// This function generates an expression that checks if all fields of a struct
|
||||
/// or enum variant are equal.
|
||||
/// - Scalar fields are compared first for efficiency, followed by compound
|
||||
/// fields.
|
||||
/// - If there are no fields, returns `true` (fieldless types are always equal).
|
||||
///
|
||||
/// Whether a field is considered "scalar" is determined by comparing the symbol
|
||||
/// of its type to a set of known scalar type symbols (e.g., `i32`, `u8`, etc).
|
||||
/// This check is based on the type's symbol.
|
||||
///
|
||||
/// ### Example 1
|
||||
/// ```
|
||||
/// #[derive(PartialEq)]
|
||||
/// struct i32;
|
||||
///
|
||||
/// // Here, `field_2` is of type `i32`, but since it's a user-defined type (not
|
||||
/// // the primitive), it will not be treated as scalar. The function will still
|
||||
/// // check equality of `field_2` first because the symbol matches `i32`.
|
||||
/// #[derive(PartialEq)]
|
||||
/// struct Struct {
|
||||
/// field_1: &'static str,
|
||||
/// field_2: i32,
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ### Example 2
|
||||
/// ```
|
||||
/// mod ty {
|
||||
/// pub type i32 = i32;
|
||||
/// }
|
||||
///
|
||||
/// // Here, `field_2` is of type `ty::i32`, which is a type alias for `i32`.
|
||||
/// // However, the function will not reorder the fields because the symbol for
|
||||
/// // `ty::i32` does not match the symbol for the primitive `i32`
|
||||
/// // ("ty::i32" != "i32").
|
||||
/// #[derive(PartialEq)]
|
||||
/// struct Struct {
|
||||
/// field_1: &'static str,
|
||||
/// field_2: ty::i32,
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// For enums, the discriminant is compared first, then the rest of the fields.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// If called on static or all-fieldless enums/structs, which should not occur
|
||||
/// during derive expansion.
|
||||
fn get_substructure_equality_expr(
|
||||
cx: &ExtCtxt<'_>,
|
||||
span: Span,
|
||||
substructure: &Substructure<'_>,
|
||||
) -> P<Expr> {
|
||||
use SubstructureFields::*;
|
||||
|
||||
match substructure.fields {
|
||||
EnumMatching(.., fields) | Struct(.., fields) => {
|
||||
let combine = move |acc, field| {
|
||||
let rhs = get_field_equality_expr(cx, field);
|
||||
if let Some(lhs) = acc {
|
||||
// Combine the previous comparison with the current field
|
||||
// using logical AND.
|
||||
return Some(cx.expr_binary(field.span, BinOpKind::And, lhs, rhs));
|
||||
}
|
||||
// Start the chain with the first field's comparison.
|
||||
Some(rhs)
|
||||
};
|
||||
|
||||
// First compare scalar fields, then compound fields, combining all
|
||||
// with logical AND.
|
||||
return fields
|
||||
.iter()
|
||||
.filter(|field| !field.maybe_scalar)
|
||||
.fold(fields.iter().filter(|field| field.maybe_scalar).fold(None, combine), combine)
|
||||
// If there are no fields, treat as always equal.
|
||||
.unwrap_or_else(|| cx.expr_bool(span, true));
|
||||
}
|
||||
EnumDiscr(disc, match_expr) => {
|
||||
let lhs = get_field_equality_expr(cx, disc);
|
||||
let Some(match_expr) = match_expr else {
|
||||
return lhs;
|
||||
};
|
||||
// Compare the discriminant first (cheaper), then the rest of the
|
||||
// fields.
|
||||
return cx.expr_binary(disc.span, BinOpKind::And, lhs, match_expr.clone());
|
||||
}
|
||||
StaticEnum(..) => cx.dcx().span_bug(
|
||||
span,
|
||||
"unexpected static enum encountered during `derive(PartialEq)` expansion",
|
||||
),
|
||||
StaticStruct(..) => cx.dcx().span_bug(
|
||||
span,
|
||||
"unexpected static struct encountered during `derive(PartialEq)` expansion",
|
||||
),
|
||||
AllFieldlessEnum(..) => cx.dcx().span_bug(
|
||||
span,
|
||||
"unexpected all-fieldless enum encountered during `derive(PartialEq)` expansion",
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
/// Generates an equality comparison expression for a single struct or enum
|
||||
/// field.
|
||||
///
|
||||
/// This function produces an AST expression that compares the `self` and
|
||||
/// `other` values for a field using `==`. It removes any leading references
|
||||
/// from both sides for readability. If the field is a block expression, it is
|
||||
/// wrapped in parentheses to ensure valid syntax.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if there are not exactly two arguments to compare (should be `self`
|
||||
/// and `other`).
|
||||
fn get_field_equality_expr(cx: &ExtCtxt<'_>, field: &FieldInfo) -> P<Expr> {
|
||||
let [rhs] = &field.other_selflike_exprs[..] else {
|
||||
cx.dcx().span_bug(field.span, "not exactly 2 arguments in `derive(PartialEq)`");
|
||||
};
|
||||
|
||||
cx.expr_binary(
|
||||
field.span,
|
||||
BinOpKind::Eq,
|
||||
wrap_block_expr(cx, peel_refs(&field.self_expr)),
|
||||
wrap_block_expr(cx, peel_refs(rhs)),
|
||||
)
|
||||
}
|
||||
|
||||
/// Removes all leading immutable references from an expression.
|
||||
///
|
||||
/// This is used to strip away any number of leading `&` from an expression
|
||||
/// (e.g., `&&&T` becomes `T`). Only removes immutable references; mutable
|
||||
/// references are preserved.
|
||||
fn peel_refs(mut expr: &P<Expr>) -> P<Expr> {
|
||||
while let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = &expr.kind {
|
||||
expr = &inner;
|
||||
}
|
||||
expr.clone()
|
||||
}
|
||||
|
||||
/// Wraps a block expression in parentheses to ensure valid AST in macro
|
||||
/// expansion output.
|
||||
///
|
||||
/// If the given expression is a block, it is wrapped in parentheses; otherwise,
|
||||
/// it is returned unchanged.
|
||||
fn wrap_block_expr(cx: &ExtCtxt<'_>, expr: P<Expr>) -> P<Expr> {
|
||||
if matches!(&expr.kind, ExprKind::Block(..)) {
|
||||
return cx.expr_paren(expr.span, expr);
|
||||
}
|
||||
expr
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ pub(crate) fn expand_deriving_partial_ord(
|
|||
|
||||
// Order in which to perform matching
|
||||
let discr_then_data = if let Annotatable::Item(item) = item
|
||||
&& let ItemKind::Enum(_, def, _) = &item.kind
|
||||
&& let ItemKind::Enum(_, _, def) = &item.kind
|
||||
{
|
||||
let dataful: Vec<bool> = def.variants.iter().map(|v| !v.data.fields().is_empty()).collect();
|
||||
match dataful.iter().filter(|&&b| b).count() {
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ pub(crate) fn expand_deriving_coerce_pointee(
|
|||
item.visit_with(&mut DetectNonGenericPointeeAttr { cx });
|
||||
|
||||
let (name_ident, generics) = if let Annotatable::Item(aitem) = item
|
||||
&& let ItemKind::Struct(ident, struct_data, g) = &aitem.kind
|
||||
&& let ItemKind::Struct(ident, g, struct_data) = &aitem.kind
|
||||
{
|
||||
if !matches!(
|
||||
struct_data,
|
||||
|
|
|
|||
|
|
@ -284,6 +284,7 @@ pub(crate) struct FieldInfo {
|
|||
/// The expressions corresponding to references to this field in
|
||||
/// the other selflike arguments.
|
||||
pub other_selflike_exprs: Vec<P<Expr>>,
|
||||
pub maybe_scalar: bool,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
|
|
@ -488,7 +489,7 @@ impl<'a> TraitDef<'a> {
|
|||
);
|
||||
|
||||
let newitem = match &item.kind {
|
||||
ast::ItemKind::Struct(ident, struct_def, generics) => self.expand_struct_def(
|
||||
ast::ItemKind::Struct(ident, generics, struct_def) => self.expand_struct_def(
|
||||
cx,
|
||||
struct_def,
|
||||
*ident,
|
||||
|
|
@ -496,7 +497,7 @@ impl<'a> TraitDef<'a> {
|
|||
from_scratch,
|
||||
is_packed,
|
||||
),
|
||||
ast::ItemKind::Enum(ident, enum_def, generics) => {
|
||||
ast::ItemKind::Enum(ident, generics, enum_def) => {
|
||||
// We ignore `is_packed` here, because `repr(packed)`
|
||||
// enums cause an error later on.
|
||||
//
|
||||
|
|
@ -504,7 +505,7 @@ impl<'a> TraitDef<'a> {
|
|||
// downstream in blatantly illegal code, so it is fine.
|
||||
self.expand_enum_def(cx, enum_def, *ident, generics, from_scratch)
|
||||
}
|
||||
ast::ItemKind::Union(ident, struct_def, generics) => {
|
||||
ast::ItemKind::Union(ident, generics, struct_def) => {
|
||||
if self.supports_unions {
|
||||
self.expand_struct_def(
|
||||
cx,
|
||||
|
|
@ -1220,7 +1221,8 @@ impl<'a> MethodDef<'a> {
|
|||
|
||||
let self_expr = discr_exprs.remove(0);
|
||||
let other_selflike_exprs = discr_exprs;
|
||||
let discr_field = FieldInfo { span, name: None, self_expr, other_selflike_exprs };
|
||||
let discr_field =
|
||||
FieldInfo { span, name: None, self_expr, other_selflike_exprs, maybe_scalar: true };
|
||||
|
||||
let discr_let_stmts: ThinVec<_> = iter::zip(&discr_idents, &selflike_args)
|
||||
.map(|(&ident, selflike_arg)| {
|
||||
|
|
@ -1533,6 +1535,7 @@ impl<'a> TraitDef<'a> {
|
|||
name: struct_field.ident,
|
||||
self_expr,
|
||||
other_selflike_exprs,
|
||||
maybe_scalar: struct_field.ty.peel_refs().kind.maybe_scalar(),
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ impl MultiItemModifier for BuiltinDerive {
|
|||
let mut items = Vec::new();
|
||||
match item {
|
||||
Annotatable::Stmt(stmt) => {
|
||||
if let ast::StmtKind::Item(item) = stmt.into_inner().kind {
|
||||
if let ast::StmtKind::Item(item) = stmt.kind {
|
||||
(self.0)(
|
||||
ecx,
|
||||
span,
|
||||
|
|
|
|||
|
|
@ -109,13 +109,6 @@ pub(crate) struct ProcMacro {
|
|||
pub(crate) span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(builtin_macros_non_abi)]
|
||||
pub(crate) struct NonABI {
|
||||
#[primary_span]
|
||||
pub(crate) span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(builtin_macros_trace_macros)]
|
||||
pub(crate) struct TraceMacros {
|
||||
|
|
@ -187,14 +180,6 @@ mod autodiff {
|
|||
pub(crate) act: String,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(builtin_macros_autodiff_mode)]
|
||||
pub(crate) struct AutoDiffInvalidMode {
|
||||
#[primary_span]
|
||||
pub(crate) span: Span,
|
||||
pub(crate) mode: String,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(builtin_macros_autodiff_width)]
|
||||
pub(crate) struct AutoDiffInvalidWidth {
|
||||
|
|
@ -789,51 +774,12 @@ pub(crate) struct AsmModifierInvalid {
|
|||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(builtin_macros_asm_requires_template)]
|
||||
pub(crate) struct AsmRequiresTemplate {
|
||||
#[diag(builtin_macros_asm_attribute_not_supported)]
|
||||
pub(crate) struct AsmAttributeNotSupported {
|
||||
#[primary_span]
|
||||
pub(crate) span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(builtin_macros_asm_expected_comma)]
|
||||
pub(crate) struct AsmExpectedComma {
|
||||
#[primary_span]
|
||||
#[label]
|
||||
pub(crate) span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(builtin_macros_asm_expected_string_literal)]
|
||||
pub(crate) struct AsmExpectedStringLiteral {
|
||||
#[primary_span]
|
||||
#[label]
|
||||
pub(crate) span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(builtin_macros_asm_underscore_input)]
|
||||
pub(crate) struct AsmUnderscoreInput {
|
||||
#[primary_span]
|
||||
pub(crate) span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(builtin_macros_asm_sym_no_path)]
|
||||
pub(crate) struct AsmSymNoPath {
|
||||
#[primary_span]
|
||||
pub(crate) span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(builtin_macros_asm_expected_other)]
|
||||
pub(crate) struct AsmExpectedOther {
|
||||
#[primary_span]
|
||||
#[label(builtin_macros_asm_expected_other)]
|
||||
pub(crate) span: Span,
|
||||
pub(crate) is_inline_asm: bool,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(builtin_macros_asm_duplicate_arg)]
|
||||
pub(crate) struct AsmDuplicateArg {
|
||||
|
|
@ -925,16 +871,6 @@ pub(crate) struct AsmUnsupportedOption {
|
|||
pub(crate) macro_name: &'static str,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(builtin_macros_asm_unsupported_operand)]
|
||||
pub(crate) struct AsmUnsupportedOperand<'a> {
|
||||
#[primary_span]
|
||||
#[label]
|
||||
pub(crate) span: Span,
|
||||
pub(crate) symbol: &'a str,
|
||||
pub(crate) macro_name: &'static str,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(builtin_macros_asm_unsupported_clobber_abi)]
|
||||
pub(crate) struct AsmUnsupportedClobberAbi {
|
||||
|
|
@ -957,13 +893,6 @@ pub(crate) struct TestRunnerNargs {
|
|||
pub(crate) span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(builtin_macros_expected_register_class_or_explicit_register)]
|
||||
pub(crate) struct ExpectedRegisterClassOrExplicitRegister {
|
||||
#[primary_span]
|
||||
pub(crate) span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(builtin_macros_expected_comma_in_list)]
|
||||
pub(crate) struct ExpectedCommaInList {
|
||||
|
|
@ -1027,3 +956,12 @@ pub(crate) struct NonGenericPointee {
|
|||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(builtin_macros_expected_other)]
|
||||
pub(crate) struct AsmExpectedOther {
|
||||
#[primary_span]
|
||||
#[label(builtin_macros_expected_other)]
|
||||
pub(crate) span: Span,
|
||||
pub(crate) is_inline_asm: bool,
|
||||
}
|
||||
|
|
|
|||
53
compiler/rustc_builtin_macros/src/iter.rs
Normal file
53
compiler/rustc_builtin_macros/src/iter.rs
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
use rustc_ast::ptr::P;
|
||||
use rustc_ast::tokenstream::TokenStream;
|
||||
use rustc_ast::{CoroutineKind, DUMMY_NODE_ID, Expr, ast, token};
|
||||
use rustc_errors::PResult;
|
||||
use rustc_expand::base::{self, DummyResult, ExpandResult, ExtCtxt, MacroExpanderResult};
|
||||
use rustc_span::Span;
|
||||
|
||||
pub(crate) fn expand<'cx>(
|
||||
cx: &'cx mut ExtCtxt<'_>,
|
||||
sp: Span,
|
||||
tts: TokenStream,
|
||||
) -> MacroExpanderResult<'cx> {
|
||||
let closure = match parse_closure(cx, sp, tts) {
|
||||
Ok(parsed) => parsed,
|
||||
Err(err) => {
|
||||
return ExpandResult::Ready(DummyResult::any(sp, err.emit()));
|
||||
}
|
||||
};
|
||||
|
||||
ExpandResult::Ready(base::MacEager::expr(closure))
|
||||
}
|
||||
|
||||
fn parse_closure<'a>(
|
||||
cx: &mut ExtCtxt<'a>,
|
||||
span: Span,
|
||||
stream: TokenStream,
|
||||
) -> PResult<'a, P<Expr>> {
|
||||
let mut closure_parser = cx.new_parser_from_tts(stream);
|
||||
|
||||
let coroutine_kind = Some(CoroutineKind::Gen {
|
||||
span,
|
||||
closure_id: DUMMY_NODE_ID,
|
||||
return_impl_trait_id: DUMMY_NODE_ID,
|
||||
});
|
||||
|
||||
let mut closure = closure_parser.parse_expr()?;
|
||||
match &mut closure.kind {
|
||||
ast::ExprKind::Closure(c) => {
|
||||
if let Some(kind) = c.coroutine_kind {
|
||||
cx.dcx().span_err(kind.span(), "only plain closures allowed in `iter!`");
|
||||
}
|
||||
c.coroutine_kind = coroutine_kind;
|
||||
if closure_parser.token != token::Eof {
|
||||
closure_parser.unexpected()?;
|
||||
}
|
||||
Ok(closure)
|
||||
}
|
||||
_ => {
|
||||
cx.dcx().span_err(closure.span, "`iter!` body must be a closure");
|
||||
Err(closure_parser.unexpected().unwrap_err())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5,23 +5,20 @@
|
|||
#![allow(internal_features)]
|
||||
#![allow(rustc::diagnostic_outside_of_impl)]
|
||||
#![allow(rustc::untranslatable_diagnostic)]
|
||||
#![cfg_attr(not(bootstrap), feature(autodiff))]
|
||||
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
|
||||
#![doc(rust_logo)]
|
||||
#![feature(assert_matches)]
|
||||
#![feature(autodiff)]
|
||||
#![feature(box_patterns)]
|
||||
#![feature(decl_macro)]
|
||||
#![feature(if_let_guard)]
|
||||
#![feature(proc_macro_internals)]
|
||||
#![feature(proc_macro_quote)]
|
||||
#![feature(rustdoc_internals)]
|
||||
#![feature(string_from_utf8_lossy_owned)]
|
||||
#![feature(try_blocks)]
|
||||
#![recursion_limit = "256"]
|
||||
// tidy-alphabetical-end
|
||||
|
||||
extern crate proc_macro;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use rustc_expand::base::{MacroExpanderFn, ResolverExpand, SyntaxExtensionKind};
|
||||
|
|
@ -49,6 +46,7 @@ mod errors;
|
|||
mod format;
|
||||
mod format_foreign;
|
||||
mod global_allocator;
|
||||
mod iter;
|
||||
mod log_syntax;
|
||||
mod pattern_type;
|
||||
mod source_util;
|
||||
|
|
@ -97,6 +95,7 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) {
|
|||
include: source_util::expand_include,
|
||||
include_bytes: source_util::expand_include_bytes,
|
||||
include_str: source_util::expand_include_str,
|
||||
iter: iter::expand,
|
||||
line: source_util::expand_line,
|
||||
log_syntax: log_syntax::expand_log_syntax,
|
||||
module_path: source_util::expand_mod,
|
||||
|
|
@ -112,7 +111,8 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) {
|
|||
|
||||
register_attr! {
|
||||
alloc_error_handler: alloc_error_handler::expand,
|
||||
autodiff: autodiff::expand,
|
||||
autodiff_forward: autodiff::expand_forward,
|
||||
autodiff_reverse: autodiff::expand_reverse,
|
||||
bench: test::expand_bench,
|
||||
cfg_accessible: cfg_accessible::Expander,
|
||||
cfg_eval: cfg_eval::expand,
|
||||
|
|
@ -139,7 +139,7 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) {
|
|||
CoercePointee: coerce_pointee::expand_deriving_coerce_pointee,
|
||||
}
|
||||
|
||||
let client = proc_macro::bridge::client::Client::expand1(proc_macro::quote);
|
||||
let client = rustc_proc_macro::bridge::client::Client::expand1(rustc_proc_macro::quote);
|
||||
register(sym::quote, SyntaxExtensionKind::Bang(Arc::new(BangProcMacro { client })));
|
||||
let requires = SyntaxExtensionKind::Attr(Arc::new(contracts::ExpandRequires));
|
||||
register(sym::contracts_requires, requires);
|
||||
|
|
|
|||
|
|
@ -30,14 +30,12 @@ fn parse_pat_ty<'a>(cx: &mut ExtCtxt<'a>, stream: TokenStream) -> PResult<'a, (P
|
|||
|
||||
let pat = pat_to_ty_pat(
|
||||
cx,
|
||||
parser
|
||||
.parse_pat_no_top_guard(
|
||||
None,
|
||||
RecoverComma::No,
|
||||
RecoverColon::No,
|
||||
CommaRecoveryMode::EitherTupleOrPipe,
|
||||
)?
|
||||
.into_inner(),
|
||||
*parser.parse_pat_no_top_guard(
|
||||
None,
|
||||
RecoverComma::No,
|
||||
RecoverColon::No,
|
||||
CommaRecoveryMode::EitherTupleOrPipe,
|
||||
)?,
|
||||
);
|
||||
|
||||
if parser.token != token::Eof {
|
||||
|
|
@ -58,9 +56,9 @@ fn pat_to_ty_pat(cx: &mut ExtCtxt<'_>, pat: ast::Pat) -> P<TyPat> {
|
|||
end.map(|value| P(AnonConst { id: DUMMY_NODE_ID, value })),
|
||||
include_end,
|
||||
),
|
||||
ast::PatKind::Or(variants) => TyPatKind::Or(
|
||||
variants.into_iter().map(|pat| pat_to_ty_pat(cx, pat.into_inner())).collect(),
|
||||
),
|
||||
ast::PatKind::Or(variants) => {
|
||||
TyPatKind::Or(variants.into_iter().map(|pat| pat_to_ty_pat(cx, *pat)).collect())
|
||||
}
|
||||
ast::PatKind::Err(guar) => TyPatKind::Err(guar),
|
||||
_ => TyPatKind::Err(cx.dcx().span_err(pat.span, "pattern not supported in pattern types")),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -354,30 +354,28 @@ fn mk_decls(cx: &mut ExtCtxt<'_>, macros: &[ProcMacro]) -> P<ast::Item> {
|
|||
})
|
||||
.collect();
|
||||
|
||||
let decls_static = cx
|
||||
.item_static(
|
||||
let mut decls_static = cx.item_static(
|
||||
span,
|
||||
Ident::new(sym::_DECLS, span),
|
||||
cx.ty_ref(
|
||||
span,
|
||||
Ident::new(sym::_DECLS, span),
|
||||
cx.ty_ref(
|
||||
cx.ty(
|
||||
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,
|
||||
),
|
||||
None,
|
||||
ast::Mutability::Not,
|
||||
cx.expr_array_ref(span, decls),
|
||||
)
|
||||
.map(|mut i| {
|
||||
i.attrs.push(cx.attr_word(sym::rustc_proc_macro_decls, span));
|
||||
i.attrs.push(cx.attr_word(sym::used, span));
|
||||
i.attrs.push(cx.attr_nested_word(sym::allow, sym::deprecated, span));
|
||||
i
|
||||
});
|
||||
),
|
||||
ast::Mutability::Not,
|
||||
cx.expr_array_ref(span, decls),
|
||||
);
|
||||
decls_static.attrs.extend([
|
||||
cx.attr_word(sym::rustc_proc_macro_decls, span),
|
||||
cx.attr_word(sym::used, span),
|
||||
cx.attr_nested_word(sym::allow, sym::deprecated, span),
|
||||
]);
|
||||
|
||||
let block = cx.expr_block(
|
||||
cx.block(span, thin_vec![cx.stmt_item(span, krate), cx.stmt_item(span, decls_static)]),
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ pub(crate) fn expand_test_case(
|
|||
let (mut item, is_stmt) = match anno_item {
|
||||
Annotatable::Item(item) => (item, false),
|
||||
Annotatable::Stmt(stmt) if let ast::StmtKind::Item(_) = stmt.kind => {
|
||||
if let ast::StmtKind::Item(i) = stmt.into_inner().kind {
|
||||
if let ast::StmtKind::Item(i) = stmt.kind {
|
||||
(i, true)
|
||||
} else {
|
||||
unreachable!()
|
||||
|
|
@ -120,11 +120,7 @@ pub(crate) fn expand_test_or_bench(
|
|||
Annotatable::Item(i) => (i, false),
|
||||
Annotatable::Stmt(stmt) if matches!(stmt.kind, ast::StmtKind::Item(_)) => {
|
||||
// FIXME: Use an 'if let' guard once they are implemented
|
||||
if let ast::StmtKind::Item(i) = stmt.into_inner().kind {
|
||||
(i, true)
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
if let ast::StmtKind::Item(i) = stmt.kind { (i, true) } else { unreachable!() }
|
||||
}
|
||||
other => {
|
||||
not_testable_error(cx, attr_sp, None);
|
||||
|
|
@ -381,10 +377,7 @@ pub(crate) fn expand_test_or_bench(
|
|||
.into(),
|
||||
),
|
||||
);
|
||||
test_const = test_const.map(|mut tc| {
|
||||
tc.vis.kind = ast::VisibilityKind::Public;
|
||||
tc
|
||||
});
|
||||
test_const.vis.kind = ast::VisibilityKind::Public;
|
||||
|
||||
// extern crate test
|
||||
let test_extern =
|
||||
|
|
|
|||
|
|
@ -128,9 +128,7 @@ impl<'a> MutVisitor for TestHarnessGenerator<'a> {
|
|||
c.items.push(mk_main(&mut self.cx));
|
||||
}
|
||||
|
||||
fn visit_item(&mut self, item: &mut P<ast::Item>) {
|
||||
let item = &mut **item;
|
||||
|
||||
fn visit_item(&mut self, item: &mut ast::Item) {
|
||||
if let Some(name) = get_test_name(&item) {
|
||||
debug!("this is a test item");
|
||||
|
||||
|
|
@ -193,7 +191,7 @@ struct EntryPointCleaner<'a> {
|
|||
}
|
||||
|
||||
impl<'a> MutVisitor for EntryPointCleaner<'a> {
|
||||
fn visit_item(&mut self, item: &mut P<ast::Item>) {
|
||||
fn visit_item(&mut self, item: &mut ast::Item) {
|
||||
self.depth += 1;
|
||||
ast::mut_visit::walk_item(self, item);
|
||||
self.depth -= 1;
|
||||
|
|
|
|||
|
|
@ -34,9 +34,9 @@ jobs:
|
|||
- name: Rustfmt
|
||||
run: |
|
||||
cargo fmt --check
|
||||
rustfmt --check build_system/main.rs
|
||||
rustfmt --check example/*
|
||||
rustfmt --check scripts/*.rs
|
||||
rustfmt --check --edition 2024 build_system/main.rs
|
||||
rustfmt --check --edition 2024 example/*
|
||||
rustfmt --check --edition 2024 scripts/*.rs
|
||||
|
||||
|
||||
test:
|
||||
|
|
|
|||
|
|
@ -43,39 +43,42 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
|||
|
||||
[[package]]
|
||||
name = "cranelift-assembler-x64"
|
||||
version = "0.118.0"
|
||||
version = "0.120.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e4b56ebe316895d3fa37775d0a87b0c889cc933f5c8b253dbcc7c7bcb7fe7e4"
|
||||
checksum = "9ff8e35182c7372df00447cb90a04e584e032c42b9b9b6e8c50ddaaf0d7900d5"
|
||||
dependencies = [
|
||||
"cranelift-assembler-x64-meta",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cranelift-assembler-x64-meta"
|
||||
version = "0.118.0"
|
||||
version = "0.120.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95cabbc01dfbd7dcd6c329ca44f0212910309c221797ac736a67a5bc8857fe1b"
|
||||
checksum = "14220f9c2698015c3b94dc6b84ae045c1c45509ddc406e43c6139252757fdb7a"
|
||||
dependencies = [
|
||||
"cranelift-srcgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cranelift-bforest"
|
||||
version = "0.118.0"
|
||||
version = "0.120.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "76ffe46df300a45f1dc6f609dc808ce963f0e3a2e971682c479a2d13e3b9b8ef"
|
||||
checksum = "d372ef2777ceefd75829e1390211ac240e9196bc60699218f7ea2419038288ee"
|
||||
dependencies = [
|
||||
"cranelift-entity",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cranelift-bitset"
|
||||
version = "0.118.0"
|
||||
version = "0.120.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b265bed7c51e1921fdae6419791d31af77d33662ee56d7b0fa0704dc8d231cab"
|
||||
checksum = "56323783e423818fa89ce8078e90a3913d2a6e0810399bfce8ebd7ee87baa81f"
|
||||
|
||||
[[package]]
|
||||
name = "cranelift-codegen"
|
||||
version = "0.118.0"
|
||||
version = "0.120.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e606230a7e3a6897d603761baee0d19f88d077f17b996bb5089488a29ae96e41"
|
||||
checksum = "74ffb780aab6186c6e9ba26519654b1ac55a09c0a866f6088a4efbbd84da68ed"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"cranelift-assembler-x64",
|
||||
|
|
@ -98,43 +101,44 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "cranelift-codegen-meta"
|
||||
version = "0.118.0"
|
||||
version = "0.120.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a63bffafc23bc60969ad528e138788495999d935f0adcfd6543cb151ca8637d"
|
||||
checksum = "c23ef13814d3b39c869650d5961128cbbecad83fbdff4e6836a03ecf6862d7ed"
|
||||
dependencies = [
|
||||
"cranelift-assembler-x64",
|
||||
"cranelift-assembler-x64-meta",
|
||||
"cranelift-codegen-shared",
|
||||
"cranelift-srcgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cranelift-codegen-shared"
|
||||
version = "0.118.0"
|
||||
version = "0.120.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af50281b67324b58e843170a6a5943cf6d387c06f7eeacc9f5696e4ab7ae7d7e"
|
||||
checksum = "b9f623300657679f847803ce80811454bfff89cea4f6bf684be5c468d4a73631"
|
||||
|
||||
[[package]]
|
||||
name = "cranelift-control"
|
||||
version = "0.118.0"
|
||||
version = "0.120.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c20c1b38d1abfbcebb0032e497e71156c0e3b8dcb3f0a92b9863b7bcaec290c"
|
||||
checksum = "31f4168af69989aa6b91fab46799ed4df6096f3209f4a6c8fb4358f49c60188f"
|
||||
dependencies = [
|
||||
"arbitrary",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cranelift-entity"
|
||||
version = "0.118.0"
|
||||
version = "0.120.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c2c67d95507c51b4a1ff3f3555fe4bfec36b9e13c1b684ccc602736f5d5f4a2"
|
||||
checksum = "ca6fa9bae1c8de26d71ac2162f069447610fd91e7780cb480ee0d76ac81eabb8"
|
||||
dependencies = [
|
||||
"cranelift-bitset",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cranelift-frontend"
|
||||
version = "0.118.0"
|
||||
version = "0.120.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4e002691cc69c38b54fc7ec93e5be5b744f627d027031d991cc845d1d512d0ce"
|
||||
checksum = "b8219205608aa0b0e6769b580284a7e055c7e0c323c1041cde7ca078add3e412"
|
||||
dependencies = [
|
||||
"cranelift-codegen",
|
||||
"log",
|
||||
|
|
@ -144,15 +148,15 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "cranelift-isle"
|
||||
version = "0.118.0"
|
||||
version = "0.120.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e93588ed1796cbcb0e2ad160403509e2c5d330d80dd6e0014ac6774c7ebac496"
|
||||
checksum = "588d0c5964f10860b04043e55aab26d7f7a206b0fd4f10c5260e8aa5773832bd"
|
||||
|
||||
[[package]]
|
||||
name = "cranelift-jit"
|
||||
version = "0.118.0"
|
||||
version = "0.120.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17f6682f0b193d6b7873cc8e7ed67e8776a8a26f50eeabf88534e9be618b9a03"
|
||||
checksum = "56bd917ddc524f84f4066f954062875bdfc0dffea068ee94e906d98de5ac7c33"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cranelift-codegen",
|
||||
|
|
@ -170,9 +174,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "cranelift-module"
|
||||
version = "0.118.0"
|
||||
version = "0.120.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ff19784c6de05116e63e6a34791012bd927b2a4eac56233039c46f1b6a4edac8"
|
||||
checksum = "68a03c057d8a992e06596c871341e446af43ff9224f941e5b8adea39137a5391"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cranelift-codegen",
|
||||
|
|
@ -181,9 +185,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "cranelift-native"
|
||||
version = "0.118.0"
|
||||
version = "0.120.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5b09bdd6407bf5d89661b80cf926ce731c9e8cc184bf49102267a2369a8358e"
|
||||
checksum = "19ed3c94cb97b14f92b6a94a1d45ef8c851f6a2ad9114e5d91d233f7da638fed"
|
||||
dependencies = [
|
||||
"cranelift-codegen",
|
||||
"libc",
|
||||
|
|
@ -192,9 +196,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "cranelift-object"
|
||||
version = "0.118.0"
|
||||
version = "0.120.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "685e8661a30d1cb69509f589ac643adeee79c5f63c0da316431b9fad29e6d3b4"
|
||||
checksum = "a64dacef362a69375a604f6636e5e9a174fb96dba3b273646fcd9fa85c1d0997"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cranelift-codegen",
|
||||
|
|
@ -205,6 +209,12 @@ dependencies = [
|
|||
"target-lexicon",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cranelift-srcgen"
|
||||
version = "0.120.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85256fac1519a7d25a040c1d850fba67478f3f021ad5fdf738ba4425ee862dbf"
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.4.2"
|
||||
|
|
@ -331,9 +341,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "regalloc2"
|
||||
version = "0.11.1"
|
||||
version = "0.12.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "145c1c267e14f20fb0f88aa76a1c5ffec42d592c1d28b3cd9148ae35916158d3"
|
||||
checksum = "5216b1837de2149f8bc8e6d5f88a9326b63b8c836ed58ce4a0a29ec736a59734"
|
||||
dependencies = [
|
||||
"allocator-api2",
|
||||
"bumpalo",
|
||||
|
|
@ -436,9 +446,9 @@ checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
|
|||
|
||||
[[package]]
|
||||
name = "wasmtime-jit-icache-coherence"
|
||||
version = "31.0.0"
|
||||
version = "33.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a54f6c6c7e9d7eeee32dfcc10db7f29d505ee7dd28d00593ea241d5f70698e64"
|
||||
checksum = "175e924dbc944c185808466d1e90b5a7feb610f3b9abdfe26f8ee25fd1086d1c"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cfg-if",
|
||||
|
|
|
|||
|
|
@ -8,12 +8,12 @@ crate-type = ["dylib"]
|
|||
|
||||
[dependencies]
|
||||
# These have to be in sync with each other
|
||||
cranelift-codegen = { version = "0.118.0", default-features = false, features = ["std", "timing", "unwind", "all-native-arch"] }
|
||||
cranelift-frontend = { version = "0.118.0" }
|
||||
cranelift-module = { version = "0.118.0" }
|
||||
cranelift-native = { version = "0.118.0" }
|
||||
cranelift-jit = { version = "0.118.0", optional = true }
|
||||
cranelift-object = { version = "0.118.0" }
|
||||
cranelift-codegen = { version = "0.120.0", default-features = false, features = ["std", "timing", "unwind", "all-native-arch"] }
|
||||
cranelift-frontend = { version = "0.120.0" }
|
||||
cranelift-module = { version = "0.120.0" }
|
||||
cranelift-native = { version = "0.120.0" }
|
||||
cranelift-jit = { version = "0.120.0", optional = true }
|
||||
cranelift-object = { version = "0.120.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"] }
|
||||
|
|
@ -24,12 +24,12 @@ 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-31.0.0", version = "0.118.0" }
|
||||
#cranelift-frontend = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-31.0.0", version = "0.118.0" }
|
||||
#cranelift-module = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-31.0.0", version = "0.118.0" }
|
||||
#cranelift-native = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-31.0.0", version = "0.118.0" }
|
||||
#cranelift-jit = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-31.0.0", version = "0.118.0" }
|
||||
#cranelift-object = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-31.0.0", version = "0.118.0" }
|
||||
#cranelift-codegen = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-33.0.0", version = "0.120.0" }
|
||||
#cranelift-frontend = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-33.0.0", version = "0.120.0" }
|
||||
#cranelift-module = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-33.0.0", version = "0.120.0" }
|
||||
#cranelift-native = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-33.0.0", version = "0.120.0" }
|
||||
#cranelift-jit = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-33.0.0", version = "0.120.0" }
|
||||
#cranelift-object = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-33.0.0", version = "0.120.0" }
|
||||
|
||||
# Uncomment to use local checkout of cranelift
|
||||
#cranelift-codegen = { path = "../wasmtime/cranelift/codegen" }
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ If not please open an issue.
|
|||
|
||||
## Download using Rustup
|
||||
|
||||
The Cranelift codegen backend is distributed in nightly builds on Linux and x86_64 macOS. If you want to
|
||||
The Cranelift codegen backend is distributed in nightly builds on Linux, macOS and x86_64 Windows. If you want to
|
||||
install it using Rustup, you can do that by running:
|
||||
|
||||
```bash
|
||||
|
|
@ -79,7 +79,7 @@ For more docs on how to build and test see [build_system/usage.txt](build_system
|
|||
Not all targets are available as rustup component for nightly. See notes in the platform support matrix.
|
||||
|
||||
[^xcoff]: XCOFF object file format is not supported.
|
||||
[^no-rustup]: Not available as rustup component for nightly. You can build it yourself.
|
||||
[^no-rustup]: Not available as [rustup component for nightly](https://rust-lang.github.io/rustup-components-history/). You can build it yourself.
|
||||
|
||||
## Usage
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ pub(crate) fn build_backend(
|
|||
let mut cmd = CG_CLIF.build(&bootstrap_host_compiler, dirs);
|
||||
|
||||
let mut rustflags = rustflags_from_env("RUSTFLAGS");
|
||||
rustflags.push("-Zallow-features=rustc_private".to_owned());
|
||||
rustflags.push("-Zallow-features=rustc_private,f16,f128".to_owned());
|
||||
rustflags_to_cmd_env(&mut cmd, "RUSTFLAGS", &rustflags);
|
||||
|
||||
if env::var("CG_CLIF_EXPENSIVE_CHECKS").is_ok() {
|
||||
|
|
|
|||
|
|
@ -168,7 +168,8 @@ fn build_llvm_sysroot_for_triple(compiler: Compiler) -> SysrootTarget {
|
|||
let file_name_str = file.file_name().unwrap().to_str().unwrap();
|
||||
if (file_name_str.contains("rustc_")
|
||||
&& !file_name_str.contains("rustc_std_workspace_")
|
||||
&& !file_name_str.contains("rustc_demangle"))
|
||||
&& !file_name_str.contains("rustc_demangle")
|
||||
&& !file_name_str.contains("rustc_literal_escaper"))
|
||||
|| file_name_str.contains("chalk")
|
||||
|| file_name_str.contains("tracing")
|
||||
|| file_name_str.contains("regex")
|
||||
|
|
@ -234,7 +235,7 @@ fn build_clif_sysroot_for_triple(
|
|||
compiler.rustflags.extend(rustflags);
|
||||
let mut build_cmd = STANDARD_LIBRARY.build(&compiler, dirs);
|
||||
build_cmd.arg("--release");
|
||||
build_cmd.arg("--features").arg("backtrace panic-unwind compiler-builtins-no-f16-f128");
|
||||
build_cmd.arg("--features").arg("backtrace panic-unwind");
|
||||
build_cmd.arg(format!("-Zroot-dir={}", STDLIB_SRC.to_path(dirs).display()));
|
||||
build_cmd.env("CARGO_PROFILE_RELEASE_DEBUG", "true");
|
||||
build_cmd.env("__CARGO_DEFAULT_LIB_METADATA", "cg_clif");
|
||||
|
|
|
|||
|
|
@ -213,11 +213,13 @@ pub(crate) fn copy_dir_recursively(from: &Path, to: &Path) {
|
|||
if filename == "." || filename == ".." {
|
||||
continue;
|
||||
}
|
||||
let src = from.join(&filename);
|
||||
let dst = to.join(&filename);
|
||||
if entry.metadata().unwrap().is_dir() {
|
||||
fs::create_dir(to.join(&filename)).unwrap();
|
||||
copy_dir_recursively(&from.join(&filename), &to.join(&filename));
|
||||
fs::create_dir(&dst).unwrap_or_else(|e| panic!("failed to create {dst:?}: {e}"));
|
||||
copy_dir_recursively(&src, &dst);
|
||||
} else {
|
||||
fs::copy(from.join(&filename), to.join(&filename)).unwrap();
|
||||
fs::copy(&src, &dst).unwrap_or_else(|e| panic!("failed to copy {src:?}->{dst:?}: {e}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ These are a few functions that allow you to easily run rust code from the shell
|
|||
|
||||
```bash
|
||||
function jit_naked() {
|
||||
echo "$@" | $cg_clif_dir/dist/rustc-clif - -Zunstable-options -Cllvm-args=jit-mode-Cprefer-dynamic
|
||||
echo "$@" | $cg_clif_dir/dist/rustc-clif - -Zunstable-options -Cllvm-args=jit-mode -Cprefer-dynamic
|
||||
}
|
||||
|
||||
function jit() {
|
||||
|
|
|
|||
|
|
@ -521,10 +521,28 @@ fn panic_cannot_unwind() -> ! {
|
|||
}
|
||||
|
||||
#[lang = "eh_personality"]
|
||||
fn eh_personality() -> ! {
|
||||
// FIXME personality signature depends on target
|
||||
fn eh_personality(
|
||||
_version: i32,
|
||||
_actions: i32,
|
||||
_exception_class: u64,
|
||||
_exception_object: *mut (),
|
||||
_context: *mut (),
|
||||
) -> i32 {
|
||||
loop {}
|
||||
}
|
||||
|
||||
#[lang = "panic_in_cleanup"]
|
||||
fn panic_in_cleanup() -> ! {
|
||||
loop {}
|
||||
}
|
||||
|
||||
#[cfg(all(unix, not(target_vendor = "apple")))]
|
||||
#[link(name = "gcc_s")]
|
||||
extern "C" {
|
||||
fn _Unwind_Resume(exc: *mut ()) -> !;
|
||||
}
|
||||
|
||||
#[lang = "drop_in_place"]
|
||||
#[allow(unconditional_recursion)]
|
||||
pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) {
|
||||
|
|
|
|||
|
|
@ -233,7 +233,7 @@ unsafe fn test_vaddvq_f32() {
|
|||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
unsafe fn test_vrndnq_f32() {
|
||||
// AArch64 llvm intrinsic: llvm.aarch64.neon.frintn.v4f32
|
||||
// llvm intrinsic: llvm.roundeven.v4f32
|
||||
let a = f32x4::from([0.1, -1.9, 4.5, 5.5]);
|
||||
let e = f32x4::from([0., -2., 4., 6.]);
|
||||
let r: f32x4 = transmute(vrndnq_f32(transmute(a)));
|
||||
|
|
|
|||
|
|
@ -166,6 +166,7 @@ fn main() {
|
|||
enum Never {}
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "s390x"))] // s390x doesn't have vector instructions enabled by default
|
||||
foo(I64X2([0, 0]));
|
||||
|
||||
transmute_wide_pointer();
|
||||
|
|
@ -203,9 +204,11 @@ fn rust_call_abi() {
|
|||
rust_call_abi_callee((1, 2));
|
||||
}
|
||||
|
||||
#[cfg_attr(target_arch = "s390x", allow(dead_code))]
|
||||
#[repr(simd)]
|
||||
struct I64X2([i64; 2]);
|
||||
|
||||
#[cfg_attr(target_arch = "s390x", allow(dead_code))]
|
||||
#[allow(improper_ctypes_definitions)]
|
||||
extern "C" fn foo(_a: I64X2) {}
|
||||
|
||||
|
|
|
|||
|
|
@ -37,6 +37,15 @@ diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs
|
|||
index bf2b6d59f88..d5ccce03bbf 100644
|
||||
--- a/library/core/src/sync/atomic.rs
|
||||
+++ b/library/core/src/sync/atomic.rs
|
||||
@@ -300,8 +300,6 @@ impl_atomic_primitive!(AtomicI32(i32), size("32"), align(4));
|
||||
impl_atomic_primitive!(AtomicU32(u32), size("32"), align(4));
|
||||
impl_atomic_primitive!(AtomicI64(i64), size("64"), align(8));
|
||||
impl_atomic_primitive!(AtomicU64(u64), size("64"), align(8));
|
||||
-impl_atomic_primitive!(AtomicI128(i128), size("128"), align(16));
|
||||
-impl_atomic_primitive!(AtomicU128(u128), size("128"), align(16));
|
||||
|
||||
#[cfg(target_pointer_width = "16")]
|
||||
impl_atomic_primitive!(AtomicIsize(isize), size("ptr"), align(2));
|
||||
@@ -3585,44 +3585,6 @@ pub const fn as_ptr(&self) -> *mut $int_type {
|
||||
8,
|
||||
u64 AtomicU64
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ index 1e336bf..35e6f54 100644
|
|||
@@ -2,5 +2,4 @@
|
||||
// tidy-alphabetical-start
|
||||
-#![cfg_attr(target_has_atomic = "128", feature(integer_atomics))]
|
||||
#![cfg_attr(test, feature(cfg_match))]
|
||||
#![cfg_attr(test, feature(cfg_select))]
|
||||
#![feature(alloc_layout_extra)]
|
||||
#![feature(array_chunks)]
|
||||
diff --git a/coretests/tests/atomic.rs b/coretests/tests/atomic.rs
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
[toolchain]
|
||||
channel = "nightly-2025-03-30"
|
||||
channel = "nightly-2025-05-25"
|
||||
components = ["rust-src", "rustc-dev", "llvm-tools"]
|
||||
profile = "minimal"
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ verbose-tests = false
|
|||
# disabled bootstrap will crash trying to copy llvm tools for the bootstrap
|
||||
# compiler.
|
||||
llvm-tools = false
|
||||
std-features = ["panic-unwind", "compiler-builtins-no-f16-f128"]
|
||||
std-features = ["panic-unwind"]
|
||||
|
||||
EOF
|
||||
|
||||
|
|
|
|||
|
|
@ -53,6 +53,8 @@ rm -r tests/run-make/split-debuginfo # same
|
|||
rm -r tests/run-make/target-specs # i686 not supported by Cranelift
|
||||
rm -r tests/run-make/mismatching-target-triples # same
|
||||
rm tests/ui/asm/x86_64/issue-96797.rs # const and sym inline asm operands don't work entirely correctly
|
||||
rm tests/ui/asm/global-asm-mono-sym-fn.rs # same
|
||||
rm tests/ui/asm/naked-asm-mono-sym-fn.rs # same
|
||||
rm tests/ui/asm/x86_64/goto.rs # inline asm labels not supported
|
||||
rm tests/ui/simd/simd-bitmask-notpow2.rs # non-pow-of-2 simd vector sizes
|
||||
rm -r tests/run-make/embed-source-dwarf # embedding sources in debuginfo
|
||||
|
|
@ -70,19 +72,13 @@ rm tests/ui/consts/precise-drop-with-coverage.rs
|
|||
rm tests/ui/issues/issue-85461.rs
|
||||
rm -r tests/ui/instrument-coverage/
|
||||
|
||||
# missing f16/f128 support
|
||||
rm tests/ui/half-open-range-patterns/half-open-range-pats-semantics.rs
|
||||
rm tests/ui/asm/aarch64/type-f16.rs
|
||||
rm tests/ui/float/conv-bits-runtime-const.rs
|
||||
rm tests/ui/consts/const-eval/float_methods.rs
|
||||
rm tests/ui/match/match-float.rs
|
||||
|
||||
# optimization tests
|
||||
# ==================
|
||||
rm tests/ui/codegen/issue-28950.rs # depends on stack size optimizations
|
||||
rm tests/ui/codegen/init-large-type.rs # same
|
||||
rm -r tests/run-make/fmt-write-bloat/ # tests an optimization
|
||||
rm tests/ui/statics/const_generics.rs # same
|
||||
rm tests/ui/linking/executable-no-mangle-strip.rs # requires --gc-sections to work for statics
|
||||
|
||||
# backend specific tests
|
||||
# ======================
|
||||
|
|
@ -96,6 +92,7 @@ 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
|
||||
rm -r tests/ui/optimization-remark.rs # same
|
||||
rm -r tests/run-make/print-to-output # requires --print relocation-models
|
||||
|
||||
# requires asm, llvm-ir and/or llvm-bc emit support
|
||||
|
|
@ -118,7 +115,6 @@ rm tests/ui/mir/mir_raw_fat_ptr.rs # same
|
|||
rm tests/ui/consts/issue-33537.rs # same
|
||||
rm tests/ui/consts/const-mut-refs-crate.rs # same
|
||||
rm tests/ui/abi/large-byval-align.rs # exceeds implementation limit of Cranelift
|
||||
rm tests/ui/abi/simd-abi-checks-avx.rs # attempts to declare function with two different signatures
|
||||
|
||||
# doesn't work due to the way the rustc test suite is invoked.
|
||||
# should work when using ./x.py test the way it is intended
|
||||
|
|
@ -129,7 +125,11 @@ rm -r tests/run-make/compiler-builtins # Expects lib/rustlib/src/rust to contain
|
|||
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/print-request-help-stable-unstable # same
|
||||
rm -r tests/run-make/incr-add-rust-src-component
|
||||
rm tests/ui/errors/remap-path-prefix-sysroot.rs # different sysroot source path
|
||||
rm -r tests/run-make/export/extern-opt # something about rustc version mismatches
|
||||
rm -r tests/run-make/export # same
|
||||
|
||||
# genuine bugs
|
||||
# ============
|
||||
|
|
@ -141,6 +141,7 @@ rm -r tests/run-make/panic-abort-eh_frame # .eh_frame emitted with panic=abort
|
|||
rm tests/ui/process/nofile-limit.rs # TODO some AArch64 linking issue
|
||||
rm tests/ui/backtrace/synchronized-panic-handler.rs # missing needs-unwind annotation
|
||||
rm tests/ui/lint/non-snake-case/lint-non-snake-case-crate.rs # same
|
||||
rm tests/ui/async-await/async-drop/async-drop-initial.rs # same (rust-lang/rust#140493)
|
||||
rm -r tests/ui/codegen/equal-pointers-unequal # make incorrect assumptions about the location of stack variables
|
||||
|
||||
rm tests/ui/stdio-is-blocking.rs # really slow with unoptimized libstd
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ use std::mem;
|
|||
use cranelift_codegen::ir::{ArgumentPurpose, SigRef};
|
||||
use cranelift_codegen::isa::CallConv;
|
||||
use cranelift_module::ModuleError;
|
||||
use rustc_abi::ExternAbi;
|
||||
use rustc_abi::{CanonAbi, ExternAbi, X86Call};
|
||||
use rustc_codegen_ssa::base::is_call_from_compiler_builtins_to_upstream_monomorphization;
|
||||
use rustc_codegen_ssa::errors::CompilerBuiltinsCannotCall;
|
||||
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
|
||||
|
|
@ -19,7 +19,8 @@ use rustc_middle::ty::layout::FnAbiOf;
|
|||
use rustc_middle::ty::print::with_no_trimmed_paths;
|
||||
use rustc_session::Session;
|
||||
use rustc_span::source_map::Spanned;
|
||||
use rustc_target::callconv::{Conv, FnAbi, PassMode};
|
||||
use rustc_target::callconv::{FnAbi, PassMode};
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use self::pass_mode::*;
|
||||
pub(crate) use self::returning::codegen_return;
|
||||
|
|
@ -41,32 +42,27 @@ fn clif_sig_from_fn_abi<'tcx>(
|
|||
Signature { params, returns, call_conv }
|
||||
}
|
||||
|
||||
pub(crate) fn conv_to_call_conv(sess: &Session, c: Conv, default_call_conv: CallConv) -> CallConv {
|
||||
pub(crate) fn conv_to_call_conv(
|
||||
sess: &Session,
|
||||
c: CanonAbi,
|
||||
default_call_conv: CallConv,
|
||||
) -> CallConv {
|
||||
match c {
|
||||
Conv::Rust | Conv::C => default_call_conv,
|
||||
Conv::Cold | Conv::PreserveMost | Conv::PreserveAll => CallConv::Cold,
|
||||
Conv::X86_64SysV => CallConv::SystemV,
|
||||
Conv::X86_64Win64 => CallConv::WindowsFastcall,
|
||||
CanonAbi::Rust | CanonAbi::C => default_call_conv,
|
||||
CanonAbi::RustCold => CallConv::Cold,
|
||||
|
||||
// Should already get a back compat warning
|
||||
Conv::X86Fastcall | Conv::X86Stdcall | Conv::X86ThisCall | Conv::X86VectorCall => {
|
||||
default_call_conv
|
||||
}
|
||||
CanonAbi::X86(x86_call) => match x86_call {
|
||||
X86Call::SysV64 => CallConv::SystemV,
|
||||
X86Call::Win64 => CallConv::WindowsFastcall,
|
||||
// Should already get a back compat warning
|
||||
_ => default_call_conv,
|
||||
},
|
||||
|
||||
Conv::X86Intr | Conv::RiscvInterrupt { .. } => {
|
||||
sess.dcx().fatal(format!("interrupt call conv {c:?} not yet implemented"))
|
||||
CanonAbi::Interrupt(_) | CanonAbi::Arm(_) => {
|
||||
sess.dcx().fatal("call conv {c:?} is not yet implemented")
|
||||
}
|
||||
|
||||
Conv::ArmAapcs => sess.dcx().fatal("aapcs call conv not yet implemented"),
|
||||
Conv::CCmseNonSecureCall => {
|
||||
sess.dcx().fatal("C-cmse-nonsecure-call call conv is not yet implemented");
|
||||
}
|
||||
Conv::CCmseNonSecureEntry => {
|
||||
sess.dcx().fatal("C-cmse-nonsecure-entry call conv is not yet implemented");
|
||||
}
|
||||
|
||||
Conv::Msp430Intr | Conv::GpuKernel | Conv::AvrInterrupt | Conv::AvrNonBlockingInterrupt => {
|
||||
unreachable!("tried to use {c:?} call conv which only exists on an unsupported target");
|
||||
CanonAbi::GpuKernel => {
|
||||
unreachable!("tried to use {c:?} call conv which only exists on an unsupported target")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -153,10 +149,7 @@ impl<'tcx> FunctionCx<'_, '_, 'tcx> {
|
|||
|
||||
let ret = self.lib_call_unadjusted(name, params, returns, &args)[0];
|
||||
|
||||
// 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())])
|
||||
Cow::Owned(vec![codegen_bitcast(self, types::I128, ret)])
|
||||
} else if ret_single_i128 && self.tcx.sess.target.arch == "s390x" {
|
||||
// Return i128 using a return area pointer on s390x.
|
||||
let mut params = params;
|
||||
|
|
@ -184,11 +177,9 @@ impl<'tcx> FunctionCx<'_, '_, 'tcx> {
|
|||
let sig = Signature { params, returns, call_conv: self.target_config.default_call_conv };
|
||||
let func_id = self.module.declare_function(name, Linkage::Import, &sig).unwrap();
|
||||
let func_ref = self.module.declare_func_in_func(func_id, &mut self.bcx.func);
|
||||
if self.clif_comments.enabled() {
|
||||
self.add_comment(func_ref, format!("{:?}", name));
|
||||
}
|
||||
let call_inst = self.bcx.ins().call(func_ref, args);
|
||||
if self.clif_comments.enabled() {
|
||||
self.add_comment(func_ref, format!("{:?}", name));
|
||||
self.add_comment(call_inst, format!("lib_call {}", name));
|
||||
}
|
||||
let results = self.bcx.inst_results(call_inst);
|
||||
|
|
@ -384,6 +375,7 @@ pub(crate) fn codegen_terminator_call<'tcx>(
|
|||
args: &[Spanned<Operand<'tcx>>],
|
||||
destination: Place<'tcx>,
|
||||
target: Option<BasicBlock>,
|
||||
_unwind: UnwindAction,
|
||||
) {
|
||||
let func = codegen_operand(fx, func);
|
||||
let fn_sig = func.layout().ty.fn_sig(fx.tcx);
|
||||
|
|
@ -529,7 +521,7 @@ pub(crate) fn codegen_terminator_call<'tcx>(
|
|||
Some(Instance { def: InstanceKind::Virtual(_, idx), .. }) => {
|
||||
if fx.clif_comments.enabled() {
|
||||
let nop_inst = fx.bcx.ins().nop();
|
||||
fx.add_comment(
|
||||
fx.add_post_comment(
|
||||
nop_inst,
|
||||
with_no_trimmed_paths!(format!(
|
||||
"virtual call; self arg pass mode: {:?}",
|
||||
|
|
@ -555,7 +547,7 @@ pub(crate) fn codegen_terminator_call<'tcx>(
|
|||
None => {
|
||||
if fx.clif_comments.enabled() {
|
||||
let nop_inst = fx.bcx.ins().nop();
|
||||
fx.add_comment(nop_inst, "indirect call");
|
||||
fx.add_post_comment(nop_inst, "indirect call");
|
||||
}
|
||||
|
||||
let func = func.load_scalar(fx);
|
||||
|
|
@ -585,17 +577,18 @@ pub(crate) fn codegen_terminator_call<'tcx>(
|
|||
adjust_call_for_c_variadic(fx, &fn_abi, source_info, func_ref, &mut call_args);
|
||||
}
|
||||
|
||||
if fx.clif_comments.enabled() {
|
||||
let nop_inst = fx.bcx.ins().nop();
|
||||
with_no_trimmed_paths!(fx.add_comment(nop_inst, format!("abi: {:?}", fn_abi)));
|
||||
}
|
||||
|
||||
match func_ref {
|
||||
let call_inst = match func_ref {
|
||||
CallTarget::Direct(func_ref) => fx.bcx.ins().call(func_ref, &call_args),
|
||||
CallTarget::Indirect(sig, func_ptr) => {
|
||||
fx.bcx.ins().call_indirect(sig, func_ptr, &call_args)
|
||||
}
|
||||
};
|
||||
|
||||
if fx.clif_comments.enabled() {
|
||||
with_no_trimmed_paths!(fx.add_comment(call_inst, format!("abi: {:?}", fn_abi)));
|
||||
}
|
||||
|
||||
fx.bcx.func.dfg.inst_results(call_inst).iter().copied().collect::<SmallVec<[Value; 2]>>()
|
||||
});
|
||||
|
||||
if let Some(dest) = target {
|
||||
|
|
@ -612,7 +605,7 @@ pub(crate) fn codegen_terminator_call<'tcx>(
|
|||
target: CallTarget,
|
||||
call_args: &mut Vec<Value>,
|
||||
) {
|
||||
if fn_abi.conv != Conv::C {
|
||||
if fn_abi.conv != CanonAbi::C {
|
||||
fx.tcx.dcx().span_fatal(
|
||||
source_info.span,
|
||||
format!("Variadic call for non-C abi {:?}", fn_abi.conv),
|
||||
|
|
@ -705,13 +698,16 @@ pub(crate) fn codegen_drop<'tcx>(
|
|||
source_info: mir::SourceInfo,
|
||||
drop_place: CPlace<'tcx>,
|
||||
target: BasicBlock,
|
||||
_unwind: UnwindAction,
|
||||
) {
|
||||
let ty = drop_place.layout().ty;
|
||||
let drop_instance = Instance::resolve_drop_in_place(fx.tcx, ty);
|
||||
let ret_block = fx.get_block(target);
|
||||
|
||||
// AsyncDropGlueCtorShim can't be here
|
||||
if let ty::InstanceKind::DropGlue(_, None) = drop_instance.def {
|
||||
// we don't actually need to drop anything
|
||||
fx.bcx.ins().jump(ret_block, &[]);
|
||||
} else {
|
||||
match ty.kind() {
|
||||
ty::Dynamic(_, _, ty::Dyn) => {
|
||||
|
|
@ -748,7 +744,9 @@ pub(crate) fn codegen_drop<'tcx>(
|
|||
|
||||
let sig = clif_sig_from_fn_abi(fx.tcx, fx.target_config.default_call_conv, &fn_abi);
|
||||
let sig = fx.bcx.import_signature(sig);
|
||||
// FIXME implement cleanup on exceptions
|
||||
fx.bcx.ins().call_indirect(sig, drop_fn, &[ptr]);
|
||||
fx.bcx.ins().jump(ret_block, &[]);
|
||||
}
|
||||
ty::Dynamic(_, _, ty::DynStar) => {
|
||||
// IN THIS ARM, WE HAVE:
|
||||
|
|
@ -792,6 +790,8 @@ pub(crate) fn codegen_drop<'tcx>(
|
|||
let sig = clif_sig_from_fn_abi(fx.tcx, fx.target_config.default_call_conv, &fn_abi);
|
||||
let sig = fx.bcx.import_signature(sig);
|
||||
fx.bcx.ins().call_indirect(sig, drop_fn, &[data]);
|
||||
// FIXME implement cleanup on exceptions
|
||||
fx.bcx.ins().jump(ret_block, &[]);
|
||||
}
|
||||
_ => {
|
||||
assert!(!matches!(drop_instance.def, InstanceKind::Virtual(_, _)));
|
||||
|
|
@ -817,10 +817,37 @@ pub(crate) fn codegen_drop<'tcx>(
|
|||
|
||||
let func_ref = fx.get_function_ref(drop_instance);
|
||||
fx.bcx.ins().call(func_ref, &call_args);
|
||||
// FIXME implement cleanup on exceptions
|
||||
fx.bcx.ins().jump(ret_block, &[]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let target_block = fx.get_block(target);
|
||||
fx.bcx.ins().jump(target_block, &[]);
|
||||
}
|
||||
|
||||
pub(crate) fn lib_call_arg_param(tcx: TyCtxt<'_>, ty: Type, is_signed: bool) -> AbiParam {
|
||||
let param = AbiParam::new(ty);
|
||||
if ty.is_int() && u64::from(ty.bits()) < tcx.data_layout.pointer_size.bits() {
|
||||
match (&*tcx.sess.target.arch, &*tcx.sess.target.vendor) {
|
||||
("x86_64", _) | ("aarch64", "apple") => match (ty, is_signed) {
|
||||
(types::I8 | types::I16, true) => param.sext(),
|
||||
(types::I8 | types::I16, false) => param.uext(),
|
||||
_ => param,
|
||||
},
|
||||
("aarch64", _) => param,
|
||||
("riscv64", _) => match (ty, is_signed) {
|
||||
(types::I32, _) | (_, true) => param.sext(),
|
||||
_ => param.uext(),
|
||||
},
|
||||
("s390x", _) => {
|
||||
if is_signed {
|
||||
param.sext()
|
||||
} else {
|
||||
param.uext()
|
||||
}
|
||||
}
|
||||
_ => unimplemented!("{:?}", tcx.sess.target.arch),
|
||||
}
|
||||
} else {
|
||||
param
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,8 +22,10 @@ fn reg_to_abi_param(reg: Reg) -> AbiParam {
|
|||
(RegKind::Integer, 3..=4) => types::I32,
|
||||
(RegKind::Integer, 5..=8) => types::I64,
|
||||
(RegKind::Integer, 9..=16) => types::I128,
|
||||
(RegKind::Float, 2) => types::F16,
|
||||
(RegKind::Float, 4) => types::F32,
|
||||
(RegKind::Float, 8) => types::F64,
|
||||
(RegKind::Float, 16) => types::F128,
|
||||
(RegKind::Vector, size) => types::I8.by(u32::try_from(size).unwrap()).unwrap(),
|
||||
_ => unreachable!("{:?}", reg),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ pub(super) fn codegen_with_call_return_arg<'tcx>(
|
|||
fx: &mut FunctionCx<'_, '_, 'tcx>,
|
||||
ret_arg_abi: &ArgAbi<'tcx, Ty<'tcx>>,
|
||||
ret_place: CPlace<'tcx>,
|
||||
f: impl FnOnce(&mut FunctionCx<'_, '_, 'tcx>, Option<Value>) -> Inst,
|
||||
f: impl FnOnce(&mut FunctionCx<'_, '_, 'tcx>, Option<Value>) -> SmallVec<[Value; 2]>,
|
||||
) {
|
||||
let (ret_temp_place, return_ptr) = match ret_arg_abi.mode {
|
||||
PassMode::Ignore => (None, None),
|
||||
|
|
@ -67,23 +67,21 @@ pub(super) fn codegen_with_call_return_arg<'tcx>(
|
|||
PassMode::Direct(_) | PassMode::Pair(_, _) | PassMode::Cast { .. } => (None, None),
|
||||
};
|
||||
|
||||
let call_inst = f(fx, return_ptr);
|
||||
let results = f(fx, return_ptr);
|
||||
|
||||
match ret_arg_abi.mode {
|
||||
PassMode::Ignore => {}
|
||||
PassMode::Direct(_) => {
|
||||
let ret_val = fx.bcx.inst_results(call_inst)[0];
|
||||
let ret_val = results[0];
|
||||
ret_place.write_cvalue(fx, CValue::by_val(ret_val, ret_arg_abi.layout));
|
||||
}
|
||||
PassMode::Pair(_, _) => {
|
||||
let ret_val_a = fx.bcx.inst_results(call_inst)[0];
|
||||
let ret_val_b = fx.bcx.inst_results(call_inst)[1];
|
||||
let ret_val_a = results[0];
|
||||
let ret_val_b = results[1];
|
||||
ret_place
|
||||
.write_cvalue(fx, CValue::by_val_pair(ret_val_a, ret_val_b, ret_arg_abi.layout));
|
||||
}
|
||||
PassMode::Cast { ref cast, .. } => {
|
||||
let results =
|
||||
fx.bcx.inst_results(call_inst).iter().copied().collect::<SmallVec<[Value; 2]>>();
|
||||
let result =
|
||||
super::pass_mode::from_casted_value(fx, &results, ret_place.layout(), cast);
|
||||
ret_place.write_cvalue(fx, result);
|
||||
|
|
|
|||
|
|
@ -15,9 +15,9 @@ use rustc_middle::ty::print::with_no_trimmed_paths;
|
|||
|
||||
use crate::constant::ConstantCx;
|
||||
use crate::debuginfo::{FunctionDebugContext, TypeDebugContext};
|
||||
use crate::enable_verifier;
|
||||
use crate::prelude::*;
|
||||
use crate::pretty_clif::CommentWriter;
|
||||
use crate::{codegen_f16_f128, enable_verifier};
|
||||
|
||||
pub(crate) struct CodegenedFunction {
|
||||
symbol_name: String,
|
||||
|
|
@ -193,6 +193,18 @@ pub(crate) fn compile_fn(
|
|||
name = codegened_func.symbol_name
|
||||
));
|
||||
}
|
||||
Err(ModuleError::Compilation(CodegenError::Verifier(err))) => {
|
||||
let early_dcx = rustc_session::EarlyDiagCtxt::new(
|
||||
rustc_session::config::ErrorOutputType::default(),
|
||||
);
|
||||
let _ = early_dcx.early_err(format!("{:?}", err));
|
||||
let pretty_error = cranelift_codegen::print_errors::pretty_verifier_error(
|
||||
&context.func,
|
||||
Some(Box::new(&clif_comments)),
|
||||
err,
|
||||
);
|
||||
early_dcx.early_fatal(format!("cranelift verify error:\n{}", pretty_error));
|
||||
}
|
||||
Err(err) => {
|
||||
panic!("Error while defining {name}: {err:?}", name = codegened_func.symbol_name);
|
||||
}
|
||||
|
|
@ -303,7 +315,7 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) {
|
|||
bb_data.terminator().kind.fmt_head(&mut terminator_head).unwrap();
|
||||
});
|
||||
let inst = fx.bcx.func.layout.last_inst(block).unwrap();
|
||||
fx.add_comment(inst, terminator_head);
|
||||
fx.add_post_comment(inst, terminator_head);
|
||||
}
|
||||
|
||||
let source_info = bb_data.terminator().source_info;
|
||||
|
|
@ -337,7 +349,7 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) {
|
|||
TerminatorKind::Return => {
|
||||
crate::abi::codegen_return(fx);
|
||||
}
|
||||
TerminatorKind::Assert { cond, expected, msg, target, unwind: _ } => {
|
||||
TerminatorKind::Assert { cond, expected, msg, target, unwind } => {
|
||||
if !fx.tcx.sess.overflow_checks() && msg.is_optional_overflow_check() {
|
||||
let target = fx.get_block(*target);
|
||||
fx.bcx.ins().jump(target, &[]);
|
||||
|
|
@ -367,7 +379,8 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) {
|
|||
fx,
|
||||
rustc_hir::LangItem::PanicBoundsCheck,
|
||||
&[index, len, location],
|
||||
Some(source_info.span),
|
||||
*unwind,
|
||||
source_info.span,
|
||||
);
|
||||
}
|
||||
AssertKind::MisalignedPointerDereference { ref required, ref found } => {
|
||||
|
|
@ -379,7 +392,8 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) {
|
|||
fx,
|
||||
rustc_hir::LangItem::PanicMisalignedPointerDereference,
|
||||
&[required, found, location],
|
||||
Some(source_info.span),
|
||||
*unwind,
|
||||
source_info.span,
|
||||
);
|
||||
}
|
||||
AssertKind::NullPointerDereference => {
|
||||
|
|
@ -389,7 +403,8 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) {
|
|||
fx,
|
||||
rustc_hir::LangItem::PanicNullPointerDereference,
|
||||
&[location],
|
||||
Some(source_info.span),
|
||||
*unwind,
|
||||
source_info.span,
|
||||
)
|
||||
}
|
||||
_ => {
|
||||
|
|
@ -399,7 +414,8 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) {
|
|||
fx,
|
||||
msg.panic_function(),
|
||||
&[location],
|
||||
Some(source_info.span),
|
||||
*unwind,
|
||||
source_info.span,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -457,7 +473,7 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) {
|
|||
destination,
|
||||
target,
|
||||
fn_span,
|
||||
unwind: _,
|
||||
unwind,
|
||||
call_source: _,
|
||||
} => {
|
||||
fx.tcx.prof.generic_activity("codegen call").run(|| {
|
||||
|
|
@ -468,6 +484,7 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) {
|
|||
args,
|
||||
*destination,
|
||||
*target,
|
||||
*unwind,
|
||||
)
|
||||
});
|
||||
}
|
||||
|
|
@ -514,7 +531,7 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) {
|
|||
);
|
||||
}
|
||||
TerminatorKind::UnwindTerminate(reason) => {
|
||||
codegen_unwind_terminate(fx, source_info, *reason);
|
||||
codegen_unwind_terminate(fx, source_info.span, *reason);
|
||||
}
|
||||
TerminatorKind::UnwindResume => {
|
||||
// FIXME implement unwinding
|
||||
|
|
@ -530,23 +547,19 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) {
|
|||
| TerminatorKind::CoroutineDrop => {
|
||||
bug!("shouldn't exist at codegen {:?}", bb_data.terminator());
|
||||
}
|
||||
TerminatorKind::Drop { place, target, unwind: _, replace: _, drop, async_fut } => {
|
||||
TerminatorKind::Drop { place, target, unwind, replace: _, drop, async_fut } => {
|
||||
assert!(
|
||||
async_fut.is_none() && drop.is_none(),
|
||||
"Async Drop must be expanded or reset to sync before codegen"
|
||||
);
|
||||
let drop_place = codegen_place(fx, *place);
|
||||
crate::abi::codegen_drop(fx, source_info, drop_place, *target);
|
||||
crate::abi::codegen_drop(fx, source_info, drop_place, *target, *unwind);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn codegen_stmt<'tcx>(
|
||||
fx: &mut FunctionCx<'_, '_, 'tcx>,
|
||||
#[allow(unused_variables)] cur_block: Block,
|
||||
stmt: &Statement<'tcx>,
|
||||
) {
|
||||
fn codegen_stmt<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, cur_block: Block, stmt: &Statement<'tcx>) {
|
||||
let _print_guard = crate::PrintOnPanic(|| format!("stmt {:?}", stmt));
|
||||
|
||||
fx.set_debug_loc(stmt.source_info);
|
||||
|
|
@ -557,7 +570,7 @@ fn codegen_stmt<'tcx>(
|
|||
if fx.clif_comments.enabled() {
|
||||
let inst = fx.bcx.func.layout.last_inst(cur_block).unwrap();
|
||||
with_no_trimmed_paths!({
|
||||
fx.add_comment(inst, format!("{:?}", stmt));
|
||||
fx.add_post_comment(inst, format!("{:?}", stmt));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -622,6 +635,15 @@ fn codegen_stmt<'tcx>(
|
|||
let val = operand.load_scalar(fx);
|
||||
match layout.ty.kind() {
|
||||
ty::Int(_) => CValue::by_val(fx.bcx.ins().ineg(val), layout),
|
||||
// FIXME(bytecodealliance/wasmtime#8312): Remove
|
||||
// once backend lowerings have been added to
|
||||
// Cranelift.
|
||||
ty::Float(FloatTy::F16) => {
|
||||
CValue::by_val(codegen_f16_f128::neg_f16(fx, val), layout)
|
||||
}
|
||||
ty::Float(FloatTy::F128) => {
|
||||
CValue::by_val(codegen_f16_f128::neg_f128(fx, val), layout)
|
||||
}
|
||||
ty::Float(_) => CValue::by_val(fx.bcx.ins().fneg(val), layout),
|
||||
_ => unreachable!("un op Neg for {:?}", layout.ty),
|
||||
}
|
||||
|
|
@ -793,7 +815,7 @@ fn codegen_stmt<'tcx>(
|
|||
let done_block = fx.bcx.create_block();
|
||||
let index = fx.bcx.append_block_param(loop_block, fx.pointer_type);
|
||||
let zero = fx.bcx.ins().iconst(fx.pointer_type, 0);
|
||||
fx.bcx.ins().jump(loop_block, &[zero]);
|
||||
fx.bcx.ins().jump(loop_block, &[zero.into()]);
|
||||
|
||||
fx.bcx.switch_to_block(loop_block);
|
||||
let done = fx.bcx.ins().icmp_imm(IntCC::Equal, index, times as i64);
|
||||
|
|
@ -803,7 +825,7 @@ fn codegen_stmt<'tcx>(
|
|||
let to = lval.place_index(fx, index);
|
||||
to.write_cvalue(fx, operand);
|
||||
let index = fx.bcx.ins().iadd_imm(index, 1);
|
||||
fx.bcx.ins().jump(loop_block, &[index]);
|
||||
fx.bcx.ins().jump(loop_block, &[index.into()]);
|
||||
|
||||
fx.bcx.switch_to_block(done_block);
|
||||
fx.bcx.ins().nop();
|
||||
|
|
@ -1052,30 +1074,35 @@ pub(crate) fn codegen_operand<'tcx>(
|
|||
pub(crate) fn codegen_panic_nounwind<'tcx>(
|
||||
fx: &mut FunctionCx<'_, '_, 'tcx>,
|
||||
msg_str: &str,
|
||||
span: Option<Span>,
|
||||
span: Span,
|
||||
) {
|
||||
let msg_ptr = fx.anonymous_str(msg_str);
|
||||
let msg_len = fx.bcx.ins().iconst(fx.pointer_type, i64::try_from(msg_str.len()).unwrap());
|
||||
let args = [msg_ptr, msg_len];
|
||||
|
||||
codegen_panic_inner(fx, rustc_hir::LangItem::PanicNounwind, &args, span);
|
||||
codegen_panic_inner(
|
||||
fx,
|
||||
rustc_hir::LangItem::PanicNounwind,
|
||||
&args,
|
||||
UnwindAction::Terminate(UnwindTerminateReason::Abi),
|
||||
span,
|
||||
);
|
||||
}
|
||||
|
||||
pub(crate) fn codegen_unwind_terminate<'tcx>(
|
||||
fx: &mut FunctionCx<'_, '_, 'tcx>,
|
||||
source_info: mir::SourceInfo,
|
||||
span: Span,
|
||||
reason: UnwindTerminateReason,
|
||||
) {
|
||||
let args = [];
|
||||
|
||||
codegen_panic_inner(fx, reason.lang_item(), &args, Some(source_info.span));
|
||||
codegen_panic_inner(fx, reason.lang_item(), &[], UnwindAction::Unreachable, span);
|
||||
}
|
||||
|
||||
fn codegen_panic_inner<'tcx>(
|
||||
fx: &mut FunctionCx<'_, '_, 'tcx>,
|
||||
lang_item: rustc_hir::LangItem,
|
||||
args: &[Value],
|
||||
span: Option<Span>,
|
||||
_unwind: UnwindAction,
|
||||
span: Span,
|
||||
) {
|
||||
fx.bcx.set_cold_block(fx.bcx.current_block().unwrap());
|
||||
|
||||
|
|
@ -1090,6 +1117,8 @@ fn codegen_panic_inner<'tcx>(
|
|||
|
||||
let symbol_name = fx.tcx.symbol_name(instance).name;
|
||||
|
||||
// FIXME implement cleanup on exceptions
|
||||
|
||||
fx.lib_call(
|
||||
symbol_name,
|
||||
args.iter().map(|&arg| AbiParam::new(fx.bcx.func.dfg.value_type(arg))).collect(),
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
//! Various number casting functions
|
||||
|
||||
use crate::codegen_f16_f128;
|
||||
use crate::prelude::*;
|
||||
|
||||
pub(crate) fn clif_intcast(
|
||||
|
|
@ -36,6 +37,14 @@ pub(crate) fn clif_int_or_float_cast(
|
|||
) -> Value {
|
||||
let from_ty = fx.bcx.func.dfg.value_type(from);
|
||||
|
||||
// FIXME(bytecodealliance/wasmtime#8312): Remove in favour of native
|
||||
// Cranelift operations once Cranelift backends have lowerings for them.
|
||||
if matches!(from_ty, types::F16 | types::F128)
|
||||
|| matches!(to_ty, types::F16 | types::F128) && from_ty != to_ty
|
||||
{
|
||||
return codegen_f16_f128::codegen_cast(fx, from, from_signed, to_ty, to_signed);
|
||||
}
|
||||
|
||||
if from_ty.is_int() && to_ty.is_int() {
|
||||
// int-like -> int-like
|
||||
clif_intcast(
|
||||
|
|
@ -58,8 +67,10 @@ pub(crate) fn clif_int_or_float_cast(
|
|||
"__float{sign}ti{flt}f",
|
||||
sign = if from_signed { "" } else { "un" },
|
||||
flt = match to_ty {
|
||||
types::F16 => "h",
|
||||
types::F32 => "s",
|
||||
types::F64 => "d",
|
||||
types::F128 => "t",
|
||||
_ => unreachable!("{:?}", to_ty),
|
||||
},
|
||||
);
|
||||
|
|
@ -90,8 +101,10 @@ pub(crate) fn clif_int_or_float_cast(
|
|||
"__fix{sign}{flt}fti",
|
||||
sign = if to_signed { "" } else { "uns" },
|
||||
flt = match from_ty {
|
||||
types::F16 => "h",
|
||||
types::F32 => "s",
|
||||
types::F64 => "d",
|
||||
types::F128 => "t",
|
||||
_ => unreachable!("{:?}", to_ty),
|
||||
},
|
||||
);
|
||||
|
|
@ -145,8 +158,12 @@ pub(crate) fn clif_int_or_float_cast(
|
|||
} else if from_ty.is_float() && to_ty.is_float() {
|
||||
// float -> float
|
||||
match (from_ty, to_ty) {
|
||||
(types::F32, types::F64) => fx.bcx.ins().fpromote(types::F64, from),
|
||||
(types::F64, types::F32) => fx.bcx.ins().fdemote(types::F32, from),
|
||||
(types::F16, types::F32 | types::F64 | types::F128)
|
||||
| (types::F32, types::F64 | types::F128)
|
||||
| (types::F64, types::F128) => fx.bcx.ins().fpromote(to_ty, from),
|
||||
(types::F128, types::F64 | types::F32 | types::F16)
|
||||
| (types::F64, types::F32 | types::F16)
|
||||
| (types::F32, types::F16) => fx.bcx.ins().fdemote(to_ty, from),
|
||||
_ => from,
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
284
compiler/rustc_codegen_cranelift/src/codegen_f16_f128.rs
Normal file
284
compiler/rustc_codegen_cranelift/src/codegen_f16_f128.rs
Normal file
|
|
@ -0,0 +1,284 @@
|
|||
use crate::prelude::*;
|
||||
|
||||
pub(crate) fn f16_to_f32(fx: &mut FunctionCx<'_, '_, '_>, value: Value) -> Value {
|
||||
let (value, arg_ty) =
|
||||
if fx.tcx.sess.target.vendor == "apple" && fx.tcx.sess.target.arch == "x86_64" {
|
||||
(
|
||||
fx.bcx.ins().bitcast(types::I16, MemFlags::new(), value),
|
||||
lib_call_arg_param(fx.tcx, types::I16, false),
|
||||
)
|
||||
} else {
|
||||
(value, AbiParam::new(types::F16))
|
||||
};
|
||||
fx.lib_call("__extendhfsf2", vec![arg_ty], vec![AbiParam::new(types::F32)], &[value])[0]
|
||||
}
|
||||
|
||||
fn f16_to_f64(fx: &mut FunctionCx<'_, '_, '_>, value: Value) -> Value {
|
||||
let ret = f16_to_f32(fx, value);
|
||||
fx.bcx.ins().fpromote(types::F64, ret)
|
||||
}
|
||||
|
||||
pub(crate) fn f32_to_f16(fx: &mut FunctionCx<'_, '_, '_>, value: Value) -> Value {
|
||||
let ret_ty = if fx.tcx.sess.target.vendor == "apple" && fx.tcx.sess.target.arch == "x86_64" {
|
||||
types::I16
|
||||
} else {
|
||||
types::F16
|
||||
};
|
||||
let ret = fx.lib_call(
|
||||
"__truncsfhf2",
|
||||
vec![AbiParam::new(types::F32)],
|
||||
vec![AbiParam::new(ret_ty)],
|
||||
&[value],
|
||||
)[0];
|
||||
if ret_ty == types::I16 { fx.bcx.ins().bitcast(types::F16, MemFlags::new(), ret) } else { ret }
|
||||
}
|
||||
|
||||
fn f64_to_f16(fx: &mut FunctionCx<'_, '_, '_>, value: Value) -> Value {
|
||||
let ret_ty = if fx.tcx.sess.target.vendor == "apple" && fx.tcx.sess.target.arch == "x86_64" {
|
||||
types::I16
|
||||
} else {
|
||||
types::F16
|
||||
};
|
||||
let ret = fx.lib_call(
|
||||
"__truncdfhf2",
|
||||
vec![AbiParam::new(types::F64)],
|
||||
vec![AbiParam::new(ret_ty)],
|
||||
&[value],
|
||||
)[0];
|
||||
if ret_ty == types::I16 { fx.bcx.ins().bitcast(types::F16, MemFlags::new(), ret) } else { ret }
|
||||
}
|
||||
|
||||
pub(crate) fn fcmp(fx: &mut FunctionCx<'_, '_, '_>, cc: FloatCC, lhs: Value, rhs: Value) -> Value {
|
||||
let ty = fx.bcx.func.dfg.value_type(lhs);
|
||||
match ty {
|
||||
types::F32 | types::F64 => fx.bcx.ins().fcmp(cc, lhs, rhs),
|
||||
types::F16 => {
|
||||
let lhs = f16_to_f32(fx, lhs);
|
||||
let rhs = f16_to_f32(fx, rhs);
|
||||
fx.bcx.ins().fcmp(cc, lhs, rhs)
|
||||
}
|
||||
types::F128 => {
|
||||
let (name, int_cc) = match cc {
|
||||
FloatCC::Equal => ("__eqtf2", IntCC::Equal),
|
||||
FloatCC::NotEqual => ("__netf2", IntCC::NotEqual),
|
||||
FloatCC::LessThan => ("__lttf2", IntCC::SignedLessThan),
|
||||
FloatCC::LessThanOrEqual => ("__letf2", IntCC::SignedLessThanOrEqual),
|
||||
FloatCC::GreaterThan => ("__gttf2", IntCC::SignedGreaterThan),
|
||||
FloatCC::GreaterThanOrEqual => ("__getf2", IntCC::SignedGreaterThanOrEqual),
|
||||
_ => unreachable!("not currently used in rustc_codegen_cranelift: {cc:?}"),
|
||||
};
|
||||
let res = fx.lib_call(
|
||||
name,
|
||||
vec![AbiParam::new(types::F128), AbiParam::new(types::F128)],
|
||||
// FIXME(rust-lang/compiler-builtins#919): This should be `I64` on non-AArch64
|
||||
// architectures, but switching it before compiler-builtins is fixed causes test
|
||||
// failures.
|
||||
vec![AbiParam::new(types::I32)],
|
||||
&[lhs, rhs],
|
||||
)[0];
|
||||
let zero = fx.bcx.ins().iconst(types::I32, 0);
|
||||
let res = fx.bcx.ins().icmp(int_cc, res, zero);
|
||||
res
|
||||
}
|
||||
_ => unreachable!("{ty:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn codegen_f128_binop(
|
||||
fx: &mut FunctionCx<'_, '_, '_>,
|
||||
bin_op: BinOp,
|
||||
lhs: Value,
|
||||
rhs: Value,
|
||||
) -> Value {
|
||||
let name = match bin_op {
|
||||
BinOp::Add => "__addtf3",
|
||||
BinOp::Sub => "__subtf3",
|
||||
BinOp::Mul => "__multf3",
|
||||
BinOp::Div => "__divtf3",
|
||||
_ => unreachable!("handled in `codegen_float_binop`"),
|
||||
};
|
||||
fx.lib_call(
|
||||
name,
|
||||
vec![AbiParam::new(types::F128), AbiParam::new(types::F128)],
|
||||
vec![AbiParam::new(types::F128)],
|
||||
&[lhs, rhs],
|
||||
)[0]
|
||||
}
|
||||
|
||||
pub(crate) fn neg_f16(fx: &mut FunctionCx<'_, '_, '_>, value: Value) -> Value {
|
||||
let bits = fx.bcx.ins().bitcast(types::I16, MemFlags::new(), value);
|
||||
let bits = fx.bcx.ins().bxor_imm(bits, 0x8000);
|
||||
fx.bcx.ins().bitcast(types::F16, MemFlags::new(), bits)
|
||||
}
|
||||
|
||||
pub(crate) fn neg_f128(fx: &mut FunctionCx<'_, '_, '_>, value: Value) -> Value {
|
||||
let bits = fx.bcx.ins().bitcast(types::I128, MemFlags::new(), value);
|
||||
let (low, high) = fx.bcx.ins().isplit(bits);
|
||||
let high = fx.bcx.ins().bxor_imm(high, 0x8000_0000_0000_0000_u64 as i64);
|
||||
let bits = fx.bcx.ins().iconcat(low, high);
|
||||
fx.bcx.ins().bitcast(types::F128, MemFlags::new(), bits)
|
||||
}
|
||||
|
||||
pub(crate) fn abs_f16(fx: &mut FunctionCx<'_, '_, '_>, value: Value) -> Value {
|
||||
let bits = fx.bcx.ins().bitcast(types::I16, MemFlags::new(), value);
|
||||
let bits = fx.bcx.ins().band_imm(bits, 0x7fff);
|
||||
fx.bcx.ins().bitcast(types::F16, MemFlags::new(), bits)
|
||||
}
|
||||
|
||||
pub(crate) fn abs_f128(fx: &mut FunctionCx<'_, '_, '_>, value: Value) -> Value {
|
||||
let bits = fx.bcx.ins().bitcast(types::I128, MemFlags::new(), value);
|
||||
let (low, high) = fx.bcx.ins().isplit(bits);
|
||||
let high = fx.bcx.ins().band_imm(high, 0x7fff_ffff_ffff_ffff_u64 as i64);
|
||||
let bits = fx.bcx.ins().iconcat(low, high);
|
||||
fx.bcx.ins().bitcast(types::F128, MemFlags::new(), bits)
|
||||
}
|
||||
|
||||
pub(crate) fn copysign_f16(fx: &mut FunctionCx<'_, '_, '_>, lhs: Value, rhs: Value) -> Value {
|
||||
let lhs = fx.bcx.ins().bitcast(types::I16, MemFlags::new(), lhs);
|
||||
let rhs = fx.bcx.ins().bitcast(types::I16, MemFlags::new(), rhs);
|
||||
let res = fx.bcx.ins().band_imm(lhs, 0x7fff);
|
||||
let sign = fx.bcx.ins().band_imm(rhs, 0x8000);
|
||||
let res = fx.bcx.ins().bor(res, sign);
|
||||
fx.bcx.ins().bitcast(types::F16, MemFlags::new(), res)
|
||||
}
|
||||
|
||||
pub(crate) fn copysign_f128(fx: &mut FunctionCx<'_, '_, '_>, lhs: Value, rhs: Value) -> Value {
|
||||
let lhs = fx.bcx.ins().bitcast(types::I128, MemFlags::new(), lhs);
|
||||
let rhs = fx.bcx.ins().bitcast(types::I128, MemFlags::new(), rhs);
|
||||
let (low, lhs_high) = fx.bcx.ins().isplit(lhs);
|
||||
let (_, rhs_high) = fx.bcx.ins().isplit(rhs);
|
||||
let high = fx.bcx.ins().band_imm(lhs_high, 0x7fff_ffff_ffff_ffff_u64 as i64);
|
||||
let sign = fx.bcx.ins().band_imm(rhs_high, 0x8000_0000_0000_0000_u64 as i64);
|
||||
let high = fx.bcx.ins().bor(high, sign);
|
||||
let res = fx.bcx.ins().iconcat(low, high);
|
||||
fx.bcx.ins().bitcast(types::F128, MemFlags::new(), res)
|
||||
}
|
||||
|
||||
pub(crate) fn codegen_cast(
|
||||
fx: &mut FunctionCx<'_, '_, '_>,
|
||||
from: Value,
|
||||
from_signed: bool,
|
||||
to_ty: Type,
|
||||
to_signed: bool,
|
||||
) -> Value {
|
||||
let from_ty = fx.bcx.func.dfg.value_type(from);
|
||||
if from_ty.is_float() && to_ty.is_float() {
|
||||
let name = match (from_ty, to_ty) {
|
||||
(types::F16, types::F32) => return f16_to_f32(fx, from),
|
||||
(types::F16, types::F64) => return f16_to_f64(fx, from),
|
||||
(types::F16, types::F128) => "__extendhftf2",
|
||||
(types::F32, types::F128) => "__extendsftf2",
|
||||
(types::F64, types::F128) => "__extenddftf2",
|
||||
(types::F128, types::F64) => "__trunctfdf2",
|
||||
(types::F128, types::F32) => "__trunctfsf2",
|
||||
(types::F128, types::F16) => "__trunctfhf2",
|
||||
(types::F64, types::F16) => return f64_to_f16(fx, from),
|
||||
(types::F32, types::F16) => return f32_to_f16(fx, from),
|
||||
_ => unreachable!("{from_ty:?} -> {to_ty:?}"),
|
||||
};
|
||||
fx.lib_call(name, vec![AbiParam::new(from_ty)], vec![AbiParam::new(to_ty)], &[from])[0]
|
||||
} else if from_ty.is_int() && to_ty == types::F16 {
|
||||
let res = clif_int_or_float_cast(fx, from, from_signed, types::F32, false);
|
||||
f32_to_f16(fx, res)
|
||||
} else if from_ty == types::F16 && to_ty.is_int() {
|
||||
let from = f16_to_f32(fx, from);
|
||||
clif_int_or_float_cast(fx, from, false, to_ty, to_signed)
|
||||
} else if from_ty.is_int() && to_ty == types::F128 {
|
||||
let (from, from_ty) = if from_ty.bits() < 32 {
|
||||
(clif_int_or_float_cast(fx, from, from_signed, types::I32, from_signed), types::I32)
|
||||
} else {
|
||||
(from, from_ty)
|
||||
};
|
||||
let name = format!(
|
||||
"__float{sign}{size}itf",
|
||||
sign = if from_signed { "" } else { "un" },
|
||||
size = match from_ty {
|
||||
types::I32 => 's',
|
||||
types::I64 => 'd',
|
||||
types::I128 => 't',
|
||||
_ => unreachable!("{from_ty:?}"),
|
||||
},
|
||||
);
|
||||
fx.lib_call(
|
||||
&name,
|
||||
vec![lib_call_arg_param(fx.tcx, from_ty, from_signed)],
|
||||
vec![AbiParam::new(to_ty)],
|
||||
&[from],
|
||||
)[0]
|
||||
} else if from_ty == types::F128 && to_ty.is_int() {
|
||||
let ret_ty = if to_ty.bits() < 32 { types::I32 } else { to_ty };
|
||||
let name = format!(
|
||||
"__fix{sign}tf{size}i",
|
||||
sign = if from_signed { "" } else { "un" },
|
||||
size = match ret_ty {
|
||||
types::I32 => 's',
|
||||
types::I64 => 'd',
|
||||
types::I128 => 't',
|
||||
_ => unreachable!("{from_ty:?}"),
|
||||
},
|
||||
);
|
||||
let ret =
|
||||
fx.lib_call(&name, vec![AbiParam::new(from_ty)], vec![AbiParam::new(to_ty)], &[from])
|
||||
[0];
|
||||
let val = if ret_ty == to_ty {
|
||||
ret
|
||||
} else {
|
||||
let (min, max) = match (to_ty, to_signed) {
|
||||
(types::I8, false) => (0, i64::from(u8::MAX)),
|
||||
(types::I16, false) => (0, i64::from(u16::MAX)),
|
||||
(types::I8, true) => (i64::from(i8::MIN as u32), i64::from(i8::MAX as u32)),
|
||||
(types::I16, true) => (i64::from(i16::MIN as u32), i64::from(i16::MAX as u32)),
|
||||
_ => unreachable!("{to_ty:?}"),
|
||||
};
|
||||
let min_val = fx.bcx.ins().iconst(types::I32, min);
|
||||
let max_val = fx.bcx.ins().iconst(types::I32, max);
|
||||
|
||||
let val = if to_signed {
|
||||
let has_underflow = fx.bcx.ins().icmp_imm(IntCC::SignedLessThan, ret, min);
|
||||
let has_overflow = fx.bcx.ins().icmp_imm(IntCC::SignedGreaterThan, ret, max);
|
||||
let bottom_capped = fx.bcx.ins().select(has_underflow, min_val, ret);
|
||||
fx.bcx.ins().select(has_overflow, max_val, bottom_capped)
|
||||
} else {
|
||||
let has_overflow = fx.bcx.ins().icmp_imm(IntCC::UnsignedGreaterThan, ret, max);
|
||||
fx.bcx.ins().select(has_overflow, max_val, ret)
|
||||
};
|
||||
fx.bcx.ins().ireduce(to_ty, val)
|
||||
};
|
||||
|
||||
if let Some(false) = fx.tcx.sess.opts.unstable_opts.saturating_float_casts {
|
||||
return val;
|
||||
}
|
||||
|
||||
let is_not_nan = fcmp(fx, FloatCC::Equal, from, from);
|
||||
let zero = type_zero_value(&mut fx.bcx, to_ty);
|
||||
fx.bcx.ins().select(is_not_nan, val, zero)
|
||||
} else {
|
||||
unreachable!("{from_ty:?} -> {to_ty:?}");
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn fma_f16(fx: &mut FunctionCx<'_, '_, '_>, x: Value, y: Value, z: Value) -> Value {
|
||||
let x = f16_to_f64(fx, x);
|
||||
let y = f16_to_f64(fx, y);
|
||||
let z = f16_to_f64(fx, z);
|
||||
let res = fx.bcx.ins().fma(x, y, z);
|
||||
f64_to_f16(fx, res)
|
||||
}
|
||||
|
||||
pub(crate) fn fmin_f128(fx: &mut FunctionCx<'_, '_, '_>, a: Value, b: Value) -> Value {
|
||||
fx.lib_call(
|
||||
"fminimumf128",
|
||||
vec![AbiParam::new(types::F128), AbiParam::new(types::F128)],
|
||||
vec![AbiParam::new(types::F128)],
|
||||
&[a, b],
|
||||
)[0]
|
||||
}
|
||||
|
||||
pub(crate) fn fmax_f128(fx: &mut FunctionCx<'_, '_, '_>, a: Value, b: Value) -> Value {
|
||||
fx.lib_call(
|
||||
"fmaximumf128",
|
||||
vec![AbiParam::new(types::F128), AbiParam::new(types::F128)],
|
||||
vec![AbiParam::new(types::F128)],
|
||||
&[a, b],
|
||||
)[0]
|
||||
}
|
||||
|
|
@ -33,10 +33,10 @@ pub(crate) fn scalar_to_clif_type(tcx: TyCtxt<'_>, scalar: Scalar) -> Type {
|
|||
Integer::I128 => types::I128,
|
||||
},
|
||||
Primitive::Float(float) => match float {
|
||||
Float::F16 => unimplemented!("f16_f128"),
|
||||
Float::F16 => types::F16,
|
||||
Float::F32 => types::F32,
|
||||
Float::F64 => types::F64,
|
||||
Float::F128 => unimplemented!("f16_f128"),
|
||||
Float::F128 => types::F128,
|
||||
},
|
||||
// FIXME(erikdesjardins): handle non-default addrspace ptr sizes
|
||||
Primitive::Pointer(_) => pointer_ty(tcx),
|
||||
|
|
@ -64,10 +64,10 @@ fn clif_type_from_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option<types::Typ
|
|||
},
|
||||
ty::Char => types::I32,
|
||||
ty::Float(size) => match size {
|
||||
FloatTy::F16 => unimplemented!("f16_f128"),
|
||||
FloatTy::F16 => types::F16,
|
||||
FloatTy::F32 => types::F32,
|
||||
FloatTy::F64 => types::F64,
|
||||
FloatTy::F128 => unimplemented!("f16_f128"),
|
||||
FloatTy::F128 => types::F128,
|
||||
},
|
||||
ty::FnPtr(..) => pointer_ty(tcx),
|
||||
ty::RawPtr(pointee_ty, _) | ty::Ref(_, pointee_ty, _) => {
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue