Merge from rust-lang/rust

This commit is contained in:
Laurențiu Nicola 2025-06-09 15:44:40 +03:00
commit 88223c56d9
3427 changed files with 107813 additions and 37739 deletions

View file

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

View file

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

View file

@ -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].

View file

@ -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"

View file

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

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

View file

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

View file

@ -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;

View file

@ -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;

View file

@ -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
}

View file

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

View file

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

View file

@ -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> {

View file

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

View file

@ -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)
}

View file

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

View file

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

View file

@ -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)

View file

@ -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
}

View file

@ -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));

View file

@ -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)
}

View file

@ -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(&region, 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)

View file

@ -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| {

View file

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

View file

@ -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();
}
}

View file

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

View file

@ -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)

View file

@ -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 {

View file

@ -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) => {

View file

@ -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 {

View file

@ -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>;

View file

@ -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();

View file

@ -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, &param, &mut feature)?,
Some(sym::since) => insert_value_into_option_or_error(cx, &param, &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, &param, &mut feature)?,
Some(sym::reason) => insert_value_into_option_or_error(cx, &param, &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;

View file

@ -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");
}

View file

@ -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
}

View file

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

View file

@ -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" }

View file

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

View file

@ -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(&region_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> {

View file

@ -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 => {

View file

@ -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}",),
);
}
}
}

View file

@ -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| {

View 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
}

View file

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

View file

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

View file

@ -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<'_>| {

View file

@ -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),
)?;
}

View file

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

View file

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

View file

@ -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())
})
}
}

View file

@ -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);

View file

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

View file

@ -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"

View file

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

View file

@ -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)
}

View file

@ -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);

View file

@ -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,
})

View file

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

View file

@ -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" }

View file

@ -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`

View file

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

View file

@ -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};

View file

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

View file

@ -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
}

View file

@ -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() {

View file

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

View file

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

View file

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

View file

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

View 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())
}
}
}

View file

@ -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);

View file

@ -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")),
};

View file

@ -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)]),

View file

@ -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 =

View file

@ -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;

View file

@ -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:

View file

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

View file

@ -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" }

View file

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

View file

@ -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() {

View file

@ -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");

View file

@ -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}"));
}
}
}

View file

@ -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() {

View file

@ -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) {

View file

@ -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)));

View file

@ -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) {}

View file

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

View file

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

View file

@ -1,4 +1,4 @@
[toolchain]
channel = "nightly-2025-03-30"
channel = "nightly-2025-05-25"
components = ["rust-src", "rustc-dev", "llvm-tools"]
profile = "minimal"

View file

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

View file

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

View file

@ -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
}
}

View file

@ -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),
};

View file

@ -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);

View file

@ -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(),

View file

@ -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 {

View 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]
}

View file

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