Merge from rustc
This commit is contained in:
commit
7bde176305
6080 changed files with 88693 additions and 54139 deletions
32
.github/workflows/ci.yml
vendored
32
.github/workflows/ci.yml
vendored
|
|
@ -1,8 +1,8 @@
|
|||
# This file defines our primary CI workflow that runs on pull requests
|
||||
# and also on pushes to special branches (auto, try).
|
||||
#
|
||||
# The actual definition of the executed jobs is calculated by a Python
|
||||
# script located at src/ci/github-actions/ci.py, which
|
||||
# The actual definition of the executed jobs is calculated by the
|
||||
# `src/ci/citool` crate, which
|
||||
# uses job definition data from src/ci/github-actions/jobs.yml.
|
||||
# You should primarily modify the `jobs.yml` file if you want to modify
|
||||
# what jobs are executed in CI.
|
||||
|
|
@ -56,7 +56,10 @@ jobs:
|
|||
- name: Calculate the CI job matrix
|
||||
env:
|
||||
COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
|
||||
run: python3 src/ci/github-actions/ci.py calculate-job-matrix >> $GITHUB_OUTPUT
|
||||
run: |
|
||||
cd src/ci/citool
|
||||
CARGO_INCREMENTAL=0 cargo test
|
||||
CARGO_INCREMENTAL=0 cargo run calculate-job-matrix >> $GITHUB_OUTPUT
|
||||
id: jobs
|
||||
job:
|
||||
name: ${{ matrix.full_name }}
|
||||
|
|
@ -179,6 +182,13 @@ jobs:
|
|||
- name: show the current environment
|
||||
run: src/ci/scripts/dump-environment.sh
|
||||
|
||||
# Pre-build citool before the following step uninstalls rustup
|
||||
# Build it into the build directory, to avoid modifying sources
|
||||
- name: build citool
|
||||
run: |
|
||||
cd src/ci/citool
|
||||
CARGO_INCREMENTAL=0 CARGO_TARGET_DIR=../../../build/citool cargo build
|
||||
|
||||
- name: run the build
|
||||
# Redirect stderr to stdout to avoid reordering the two streams in the GHA logs.
|
||||
run: src/ci/scripts/run-build-from-ci.sh 2>&1
|
||||
|
|
@ -215,16 +225,22 @@ jobs:
|
|||
# erroring about invalid credentials instead.
|
||||
if: github.event_name == 'push' || env.DEPLOY == '1' || env.DEPLOY_ALT == '1'
|
||||
|
||||
- name: postprocess metrics into the summary
|
||||
run: |
|
||||
if [ -f build/metrics.json ]; then
|
||||
./build/citool/debug/citool postprocess-metrics build/metrics.json ${GITHUB_STEP_SUMMARY}
|
||||
elif [ -f obj/build/metrics.json ]; then
|
||||
./build/citool/debug/citool postprocess-metrics obj/build/metrics.json ${GITHUB_STEP_SUMMARY}
|
||||
else
|
||||
echo "No metrics.json found"
|
||||
fi
|
||||
|
||||
- name: upload job metrics to DataDog
|
||||
if: needs.calculate_matrix.outputs.run_type != 'pr'
|
||||
env:
|
||||
DATADOG_SITE: datadoghq.com
|
||||
DATADOG_API_KEY: ${{ secrets.DATADOG_API_KEY }}
|
||||
DD_GITHUB_JOB_NAME: ${{ matrix.full_name }}
|
||||
run: |
|
||||
cd src/ci
|
||||
npm ci
|
||||
python3 scripts/upload-build-metrics.py ../../build/cpu-usage.csv
|
||||
run: ./build/citool/debug/citool upload-build-metrics build/cpu-usage.csv
|
||||
|
||||
# This job isused to tell bors the final status of the build, as there is no practical way to detect
|
||||
# when a workflow is successful listening to webhooks only in our current bors implementation (homu).
|
||||
|
|
|
|||
42
.github/workflows/post-merge.yml
vendored
Normal file
42
.github/workflows/post-merge.yml
vendored
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
# Workflow that runs after a merge to master, analyses changes in test executions
|
||||
# and posts the result to the merged PR.
|
||||
|
||||
name: Post merge analysis
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
analysis:
|
||||
runs-on: ubuntu-24.04
|
||||
if: github.repository == 'rust-lang/rust'
|
||||
permissions:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
# Make sure that we have enough commits to find the parent merge commit.
|
||||
# Since all merges should be through merge commits, fetching two commits
|
||||
# should be enough to get the parent bors merge commit.
|
||||
fetch-depth: 2
|
||||
- name: Perform analysis and send PR
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
# Get closest bors merge commit
|
||||
PARENT_COMMIT=`git rev-list --author='bors <bors@rust-lang.org>' -n1 --first-parent HEAD^1`
|
||||
echo "Parent: ${PARENT_COMMIT}"
|
||||
|
||||
# Find PR for the current commit
|
||||
HEAD_PR=`gh pr list --search "${{ github.sha }}" --state merged --json number --jq '.[0].number'`
|
||||
echo "HEAD: ${{ github.sha }} (#${HEAD_PR})"
|
||||
|
||||
cd src/ci/citool
|
||||
|
||||
echo "Post-merge analysis result" > output.log
|
||||
cargo run --release post-merge-report ${PARENT_COMMIT} ${{ github.sha }} >> output.log
|
||||
cat output.log
|
||||
|
||||
gh pr comment ${HEAD_PR} -F output.log
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -53,6 +53,7 @@ no_llvm_build
|
|||
/target
|
||||
/library/target
|
||||
/src/bootstrap/target
|
||||
/src/ci/citool/target
|
||||
/src/tools/x/target
|
||||
# Created by `x vendor`
|
||||
/vendor
|
||||
|
|
|
|||
3
.mailmap
3
.mailmap
|
|
@ -292,6 +292,9 @@ James Hinshelwood <jameshinshelwood1@gmail.com> <james.hinshelwood@bigpayme.com>
|
|||
James Miller <bladeon@gmail.com> <james@aatch.net>
|
||||
James Perry <james.austin.perry@gmail.com>
|
||||
James Sanderson <zofrex@gmail.com>
|
||||
Jana Dönszelmann <jana@donsz.nl>
|
||||
Jana Dönszelmann <jana@donsz.nl> <jonathan@donsz.nl>
|
||||
Jana Dönszelmann <jana@donsz.nl> <jonabent@gmail.com>
|
||||
Jan-Erik Rediger <janerik@fnordig.de> <badboy@archlinux.us>
|
||||
Jaro Fietz <jaro.fietz@gmx.de>
|
||||
Jason Fager <jfager@gmail.com>
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ For submodules, changes need to be made against the repository corresponding the
|
|||
submodule, and not the main `rust-lang/rust` repository.
|
||||
|
||||
For subtrees, prefer sending a PR against the subtree's repository if it does
|
||||
not need to be made against the main `rust-lang/rust` repostory (e.g. a
|
||||
not need to be made against the main `rust-lang/rust` repository (e.g. a
|
||||
rustc-dev-guide change that does not accompany a compiler change).
|
||||
|
||||
## About the [rustc-dev-guide]
|
||||
|
|
|
|||
58
Cargo.lock
58
Cargo.lock
|
|
@ -358,7 +358,7 @@ dependencies = [
|
|||
"cargo_metadata 0.18.1",
|
||||
"directories",
|
||||
"rustc-build-sysroot",
|
||||
"rustc_tools_util",
|
||||
"rustc_tools_util 0.4.0",
|
||||
"rustc_version",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
|
@ -407,9 +407,9 @@ version = "0.1.0"
|
|||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.13"
|
||||
version = "1.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7777341816418c02e033934a09f20dc0ccaf65a5201ef8a450ae0105a573fda"
|
||||
checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c"
|
||||
dependencies = [
|
||||
"shlex",
|
||||
]
|
||||
|
|
@ -522,7 +522,7 @@ checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
|
|||
|
||||
[[package]]
|
||||
name = "clippy"
|
||||
version = "0.1.86"
|
||||
version = "0.1.87"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"cargo_metadata 0.18.1",
|
||||
|
|
@ -539,7 +539,7 @@ dependencies = [
|
|||
"quote",
|
||||
"regex",
|
||||
"rinja",
|
||||
"rustc_tools_util",
|
||||
"rustc_tools_util 0.4.2",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"syn 2.0.96",
|
||||
|
|
@ -553,7 +553,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clippy_config"
|
||||
version = "0.1.86"
|
||||
version = "0.1.87"
|
||||
dependencies = [
|
||||
"clippy_utils",
|
||||
"itertools",
|
||||
|
|
@ -578,7 +578,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clippy_lints"
|
||||
version = "0.1.86"
|
||||
version = "0.1.87"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"cargo_metadata 0.18.1",
|
||||
|
|
@ -601,7 +601,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clippy_utils"
|
||||
version = "0.1.86"
|
||||
version = "0.1.87"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"itertools",
|
||||
|
|
@ -1491,6 +1491,7 @@ version = "0.15.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
|
||||
dependencies = [
|
||||
"allocator-api2",
|
||||
"foldhash",
|
||||
"serde",
|
||||
]
|
||||
|
|
@ -2526,6 +2527,16 @@ version = "0.2.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
|
||||
|
||||
[[package]]
|
||||
name = "os_pipe"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ffd2b0a5634335b135d5728d84c5e0fd726954b87111f7506a61c502280d982"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "overload"
|
||||
version = "0.1.1"
|
||||
|
|
@ -2871,11 +2882,11 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rand_xoshiro"
|
||||
version = "0.6.0"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa"
|
||||
checksum = "f703f4665700daf5512dcca5f43afa6af89f09db47fb56be587f80636bda2d41"
|
||||
dependencies = [
|
||||
"rand_core 0.6.4",
|
||||
"rand_core 0.9.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -3050,6 +3061,7 @@ dependencies = [
|
|||
"gimli 0.31.1",
|
||||
"libc",
|
||||
"object 0.36.7",
|
||||
"os_pipe",
|
||||
"regex",
|
||||
"serde_json",
|
||||
"similar",
|
||||
|
|
@ -3144,7 +3156,7 @@ name = "rustc_abi"
|
|||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"rand 0.8.5",
|
||||
"rand 0.9.0",
|
||||
"rand_xoshiro",
|
||||
"rustc_data_structures",
|
||||
"rustc_hashes",
|
||||
|
|
@ -3286,6 +3298,7 @@ dependencies = [
|
|||
"rustc_hir",
|
||||
"rustc_lexer",
|
||||
"rustc_macros",
|
||||
"rustc_middle",
|
||||
"rustc_serialize",
|
||||
"rustc_session",
|
||||
"rustc_span",
|
||||
|
|
@ -3480,6 +3493,7 @@ dependencies = [
|
|||
"either",
|
||||
"elsa",
|
||||
"ena",
|
||||
"hashbrown 0.15.2",
|
||||
"indexmap",
|
||||
"jobserver",
|
||||
"libc",
|
||||
|
|
@ -3741,7 +3755,7 @@ dependencies = [
|
|||
"rustc_abi",
|
||||
"rustc_ast",
|
||||
"rustc_ast_pretty",
|
||||
"rustc_attr_parsing",
|
||||
"rustc_attr_data_structures",
|
||||
"rustc_hir",
|
||||
"rustc_span",
|
||||
]
|
||||
|
|
@ -3777,7 +3791,7 @@ dependencies = [
|
|||
name = "rustc_incremental"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"rand 0.8.5",
|
||||
"rand 0.9.0",
|
||||
"rustc_ast",
|
||||
"rustc_data_structures",
|
||||
"rustc_errors",
|
||||
|
|
@ -3916,6 +3930,7 @@ dependencies = [
|
|||
"rustc_target",
|
||||
"rustc_trait_selection",
|
||||
"rustc_type_ir",
|
||||
"smallvec",
|
||||
"tracing",
|
||||
"unicode-security",
|
||||
]
|
||||
|
|
@ -4008,7 +4023,8 @@ dependencies = [
|
|||
"rustc_apfloat",
|
||||
"rustc_arena",
|
||||
"rustc_ast",
|
||||
"rustc_attr_parsing",
|
||||
"rustc_ast_ir",
|
||||
"rustc_attr_data_structures",
|
||||
"rustc_data_structures",
|
||||
"rustc_error_messages",
|
||||
"rustc_errors",
|
||||
|
|
@ -4453,6 +4469,10 @@ version = "0.4.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3316159ab19e19d1065ecc49278e87f767a9dae9fae80348d2b4d4fa4ae02d4d"
|
||||
|
||||
[[package]]
|
||||
name = "rustc_tools_util"
|
||||
version = "0.4.2"
|
||||
|
||||
[[package]]
|
||||
name = "rustc_trait_selection"
|
||||
version = "0.0.0"
|
||||
|
|
@ -4500,8 +4520,6 @@ dependencies = [
|
|||
"rustc_abi",
|
||||
"rustc_data_structures",
|
||||
"rustc_hir",
|
||||
"rustc_infer",
|
||||
"rustc_macros",
|
||||
"rustc_middle",
|
||||
"rustc_span",
|
||||
"tracing",
|
||||
|
|
@ -4513,6 +4531,7 @@ version = "0.0.0"
|
|||
dependencies = [
|
||||
"itertools",
|
||||
"rustc_abi",
|
||||
"rustc_attr_parsing",
|
||||
"rustc_data_structures",
|
||||
"rustc_errors",
|
||||
"rustc_fluent_macro",
|
||||
|
|
@ -5160,8 +5179,8 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"indicatif",
|
||||
"num",
|
||||
"rand 0.8.5",
|
||||
"rand_chacha 0.3.1",
|
||||
"rand 0.9.0",
|
||||
"rand_chacha 0.9.0",
|
||||
"rayon",
|
||||
]
|
||||
|
||||
|
|
@ -5257,6 +5276,7 @@ dependencies = [
|
|||
"serde",
|
||||
"similar",
|
||||
"termcolor",
|
||||
"toml 0.7.8",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
|
|
|
|||
10
INSTALL.md
10
INSTALL.md
|
|
@ -210,9 +210,13 @@ itself back on after some time).
|
|||
|
||||
### MSVC
|
||||
|
||||
MSVC builds of Rust additionally require an installation of Visual Studio 2017
|
||||
(or later) so `rustc` can use its linker. The simplest way is to get
|
||||
[Visual Studio], check the "C++ build tools" and "Windows 10 SDK" workload.
|
||||
MSVC builds of Rust additionally requires an installation of:
|
||||
|
||||
- Visual Studio 2022 (or later) build tools so `rustc` can use its linker. Older
|
||||
Visual Studio versions such as 2019 *may* work but aren't actively tested.
|
||||
- A recent Windows 10 or 11 SDK.
|
||||
|
||||
The simplest way is to get [Visual Studio], check the "C++ build tools".
|
||||
|
||||
[Visual Studio]: https://visualstudio.microsoft.com/downloads/
|
||||
|
||||
|
|
|
|||
|
|
@ -6,13 +6,13 @@ edition = "2024"
|
|||
[dependencies]
|
||||
# tidy-alphabetical-start
|
||||
bitflags = "2.4.1"
|
||||
rand = { version = "0.8.4", default-features = false, optional = true }
|
||||
rand_xoshiro = { version = "0.6.0", optional = true }
|
||||
rustc_data_structures = { path = "../rustc_data_structures", optional = true }
|
||||
rand = { version = "0.9.0", default-features = false, optional = true }
|
||||
rand_xoshiro = { version = "0.7.0", optional = true }
|
||||
rustc_data_structures = { path = "../rustc_data_structures", optional = true }
|
||||
rustc_hashes = { path = "../rustc_hashes" }
|
||||
rustc_index = { path = "../rustc_index", default-features = false }
|
||||
rustc_macros = { path = "../rustc_macros", optional = true }
|
||||
rustc_serialize = { path = "../rustc_serialize", optional = true }
|
||||
rustc_serialize = { path = "../rustc_serialize", optional = true }
|
||||
rustc_span = { path = "../rustc_span", optional = true }
|
||||
tracing = "0.1"
|
||||
# tidy-alphabetical-end
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
|
|||
Ok(HomogeneousAggregate::Homogeneous(Reg { kind, size: self.size }))
|
||||
}
|
||||
|
||||
BackendRepr::Vector { .. } => {
|
||||
BackendRepr::SimdVector { .. } => {
|
||||
assert!(!self.is_zst());
|
||||
Ok(HomogeneousAggregate::Homogeneous(Reg {
|
||||
kind: RegKind::Vector,
|
||||
|
|
|
|||
|
|
@ -191,6 +191,17 @@ impl StableOrd for ExternAbi {
|
|||
}
|
||||
|
||||
impl ExternAbi {
|
||||
/// An ABI "like Rust"
|
||||
///
|
||||
/// These ABIs are fully controlled by the Rust compiler, which means they
|
||||
/// - support unwinding with `-Cpanic=unwind`, unlike `extern "C"`
|
||||
/// - often diverge from the C ABI
|
||||
/// - are subject to change between compiler versions
|
||||
pub fn is_rustic_abi(self) -> bool {
|
||||
use ExternAbi::*;
|
||||
matches!(self, Rust | RustCall | RustIntrinsic | RustCold)
|
||||
}
|
||||
|
||||
pub fn supports_varargs(self) -> bool {
|
||||
// * C and Cdecl obviously support varargs.
|
||||
// * C can be based on Aapcs, SysV64 or Win64, so they must support varargs.
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ use std::{cmp, iter};
|
|||
|
||||
use rustc_hashes::Hash64;
|
||||
use rustc_index::Idx;
|
||||
use rustc_index::bit_set::BitMatrix;
|
||||
use tracing::debug;
|
||||
|
||||
use crate::{
|
||||
|
|
@ -12,6 +13,9 @@ use crate::{
|
|||
Variants, WrappingRange,
|
||||
};
|
||||
|
||||
mod coroutine;
|
||||
mod simple;
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
mod ty;
|
||||
|
||||
|
|
@ -60,17 +64,28 @@ pub enum LayoutCalculatorError<F> {
|
|||
|
||||
/// The fields or variants have irreconcilable reprs
|
||||
ReprConflict,
|
||||
|
||||
/// The length of an SIMD type is zero
|
||||
ZeroLengthSimdType,
|
||||
|
||||
/// The length of an SIMD type exceeds the maximum number of lanes
|
||||
OversizedSimdType { max_lanes: u64 },
|
||||
|
||||
/// An element type of an SIMD type isn't a primitive
|
||||
NonPrimitiveSimdType(F),
|
||||
}
|
||||
|
||||
impl<F> LayoutCalculatorError<F> {
|
||||
pub fn without_payload(&self) -> LayoutCalculatorError<()> {
|
||||
match self {
|
||||
LayoutCalculatorError::UnexpectedUnsized(_) => {
|
||||
LayoutCalculatorError::UnexpectedUnsized(())
|
||||
}
|
||||
LayoutCalculatorError::SizeOverflow => LayoutCalculatorError::SizeOverflow,
|
||||
LayoutCalculatorError::EmptyUnion => LayoutCalculatorError::EmptyUnion,
|
||||
LayoutCalculatorError::ReprConflict => LayoutCalculatorError::ReprConflict,
|
||||
use LayoutCalculatorError::*;
|
||||
match *self {
|
||||
UnexpectedUnsized(_) => UnexpectedUnsized(()),
|
||||
SizeOverflow => SizeOverflow,
|
||||
EmptyUnion => EmptyUnion,
|
||||
ReprConflict => ReprConflict,
|
||||
ZeroLengthSimdType => ZeroLengthSimdType,
|
||||
OversizedSimdType { max_lanes } => OversizedSimdType { max_lanes },
|
||||
NonPrimitiveSimdType(_) => NonPrimitiveSimdType(()),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -78,13 +93,15 @@ impl<F> LayoutCalculatorError<F> {
|
|||
///
|
||||
/// Intended for use by rust-analyzer, as neither it nor `rustc_abi` depend on fluent infra.
|
||||
pub fn fallback_fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
use LayoutCalculatorError::*;
|
||||
f.write_str(match self {
|
||||
LayoutCalculatorError::UnexpectedUnsized(_) => {
|
||||
"an unsized type was found where a sized type was expected"
|
||||
UnexpectedUnsized(_) => "an unsized type was found where a sized type was expected",
|
||||
SizeOverflow => "size overflow",
|
||||
EmptyUnion => "type is a union with no fields",
|
||||
ReprConflict => "type has an invalid repr",
|
||||
ZeroLengthSimdType | OversizedSimdType { .. } | NonPrimitiveSimdType(_) => {
|
||||
"invalid simd type definition"
|
||||
}
|
||||
LayoutCalculatorError::SizeOverflow => "size overflow",
|
||||
LayoutCalculatorError::EmptyUnion => "type is a union with no fields",
|
||||
LayoutCalculatorError::ReprConflict => "type has an invalid repr",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -102,41 +119,115 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
|
|||
Self { cx }
|
||||
}
|
||||
|
||||
pub fn scalar_pair<FieldIdx: Idx, VariantIdx: Idx>(
|
||||
pub fn array_like<FieldIdx: Idx, VariantIdx: Idx, F>(
|
||||
&self,
|
||||
a: Scalar,
|
||||
b: Scalar,
|
||||
) -> LayoutData<FieldIdx, VariantIdx> {
|
||||
let dl = self.cx.data_layout();
|
||||
let b_align = b.align(dl);
|
||||
let align = a.align(dl).max(b_align).max(dl.aggregate_align);
|
||||
let b_offset = a.size(dl).align_to(b_align.abi);
|
||||
let size = (b_offset + b.size(dl)).align_to(align.abi);
|
||||
element: &LayoutData<FieldIdx, VariantIdx>,
|
||||
count_if_sized: Option<u64>, // None for slices
|
||||
) -> LayoutCalculatorResult<FieldIdx, VariantIdx, F> {
|
||||
let count = count_if_sized.unwrap_or(0);
|
||||
let size =
|
||||
element.size.checked_mul(count, &self.cx).ok_or(LayoutCalculatorError::SizeOverflow)?;
|
||||
|
||||
// HACK(nox): We iter on `b` and then `a` because `max_by_key`
|
||||
// returns the last maximum.
|
||||
let largest_niche = Niche::from_scalar(dl, b_offset, b)
|
||||
.into_iter()
|
||||
.chain(Niche::from_scalar(dl, Size::ZERO, a))
|
||||
.max_by_key(|niche| niche.available(dl));
|
||||
|
||||
let combined_seed = a.size(&self.cx).bytes().wrapping_add(b.size(&self.cx).bytes());
|
||||
|
||||
LayoutData {
|
||||
Ok(LayoutData {
|
||||
variants: Variants::Single { index: VariantIdx::new(0) },
|
||||
fields: FieldsShape::Arbitrary {
|
||||
offsets: [Size::ZERO, b_offset].into(),
|
||||
memory_index: [0, 1].into(),
|
||||
},
|
||||
backend_repr: BackendRepr::ScalarPair(a, b),
|
||||
largest_niche,
|
||||
uninhabited: false,
|
||||
align,
|
||||
fields: FieldsShape::Array { stride: element.size, count },
|
||||
backend_repr: BackendRepr::Memory { sized: count_if_sized.is_some() },
|
||||
largest_niche: element.largest_niche.filter(|_| count != 0),
|
||||
uninhabited: element.uninhabited && count != 0,
|
||||
align: element.align,
|
||||
size,
|
||||
max_repr_align: None,
|
||||
unadjusted_abi_align: align.abi,
|
||||
randomization_seed: Hash64::new(combined_seed),
|
||||
unadjusted_abi_align: element.align.abi,
|
||||
randomization_seed: element.randomization_seed.wrapping_add(Hash64::new(count)),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn simd_type<
|
||||
FieldIdx: Idx,
|
||||
VariantIdx: Idx,
|
||||
F: AsRef<LayoutData<FieldIdx, VariantIdx>> + fmt::Debug,
|
||||
>(
|
||||
&self,
|
||||
element: F,
|
||||
count: u64,
|
||||
repr_packed: bool,
|
||||
) -> LayoutCalculatorResult<FieldIdx, VariantIdx, F> {
|
||||
let elt = element.as_ref();
|
||||
if count == 0 {
|
||||
return Err(LayoutCalculatorError::ZeroLengthSimdType);
|
||||
} else if count > crate::MAX_SIMD_LANES {
|
||||
return Err(LayoutCalculatorError::OversizedSimdType {
|
||||
max_lanes: crate::MAX_SIMD_LANES,
|
||||
});
|
||||
}
|
||||
|
||||
let BackendRepr::Scalar(e_repr) = elt.backend_repr else {
|
||||
return Err(LayoutCalculatorError::NonPrimitiveSimdType(element));
|
||||
};
|
||||
|
||||
// Compute the size and alignment of the vector
|
||||
let dl = self.cx.data_layout();
|
||||
let size =
|
||||
elt.size.checked_mul(count, dl).ok_or_else(|| LayoutCalculatorError::SizeOverflow)?;
|
||||
let (repr, align) = if repr_packed && !count.is_power_of_two() {
|
||||
// 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,
|
||||
},
|
||||
)
|
||||
} else {
|
||||
(BackendRepr::SimdVector { element: e_repr, count }, dl.llvmlike_vector_align(size))
|
||||
};
|
||||
let size = size.align_to(align.abi);
|
||||
|
||||
Ok(LayoutData {
|
||||
variants: Variants::Single { index: VariantIdx::new(0) },
|
||||
fields: FieldsShape::Arbitrary {
|
||||
offsets: [Size::ZERO].into(),
|
||||
memory_index: [0].into(),
|
||||
},
|
||||
backend_repr: repr,
|
||||
largest_niche: elt.largest_niche,
|
||||
uninhabited: false,
|
||||
size,
|
||||
align,
|
||||
max_repr_align: None,
|
||||
unadjusted_abi_align: elt.align.abi,
|
||||
randomization_seed: elt.randomization_seed.wrapping_add(Hash64::new(count)),
|
||||
})
|
||||
}
|
||||
|
||||
/// Compute the layout for a coroutine.
|
||||
///
|
||||
/// This uses dedicated code instead of [`Self::layout_of_struct_or_enum`], as coroutine
|
||||
/// fields may be shared between multiple variants (see the [`coroutine`] module for details).
|
||||
pub fn coroutine<
|
||||
'a,
|
||||
F: Deref<Target = &'a LayoutData<FieldIdx, VariantIdx>> + fmt::Debug + Copy,
|
||||
VariantIdx: Idx,
|
||||
FieldIdx: Idx,
|
||||
LocalIdx: Idx,
|
||||
>(
|
||||
&self,
|
||||
local_layouts: &IndexSlice<LocalIdx, F>,
|
||||
prefix_layouts: IndexVec<FieldIdx, F>,
|
||||
variant_fields: &IndexSlice<VariantIdx, IndexVec<FieldIdx, LocalIdx>>,
|
||||
storage_conflicts: &BitMatrix<LocalIdx, LocalIdx>,
|
||||
tag_to_layout: impl Fn(Scalar) -> F,
|
||||
) -> LayoutCalculatorResult<FieldIdx, VariantIdx, F> {
|
||||
coroutine::layout(
|
||||
self,
|
||||
local_layouts,
|
||||
prefix_layouts,
|
||||
variant_fields,
|
||||
storage_conflicts,
|
||||
tag_to_layout,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn univariant<
|
||||
|
|
@ -214,25 +305,6 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
|
|||
layout
|
||||
}
|
||||
|
||||
pub fn layout_of_never_type<FieldIdx: Idx, VariantIdx: Idx>(
|
||||
&self,
|
||||
) -> LayoutData<FieldIdx, VariantIdx> {
|
||||
let dl = self.cx.data_layout();
|
||||
// This is also used for uninhabited enums, so we use `Variants::Empty`.
|
||||
LayoutData {
|
||||
variants: Variants::Empty,
|
||||
fields: FieldsShape::Primitive,
|
||||
backend_repr: BackendRepr::Memory { sized: true },
|
||||
largest_niche: None,
|
||||
uninhabited: true,
|
||||
align: dl.i8_align,
|
||||
size: Size::ZERO,
|
||||
max_repr_align: None,
|
||||
unadjusted_abi_align: dl.i8_align.abi,
|
||||
randomization_seed: Hash64::ZERO,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn layout_of_struct_or_enum<
|
||||
'a,
|
||||
FieldIdx: Idx,
|
||||
|
|
@ -260,7 +332,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
|
|||
Some(present_first) => present_first,
|
||||
// Uninhabited because it has no variants, or only absent ones.
|
||||
None if is_enum => {
|
||||
return Ok(self.layout_of_never_type());
|
||||
return Ok(LayoutData::never_type(&self.cx));
|
||||
}
|
||||
// If it's a struct, still compute a layout so that we can still compute the
|
||||
// field offsets.
|
||||
|
|
@ -386,13 +458,15 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
|
|||
BackendRepr::Memory { sized: true }
|
||||
}
|
||||
// Vectors require at least element alignment, else disable the opt
|
||||
BackendRepr::Vector { element, count: _ } if element.align(dl).abi > align.abi => {
|
||||
BackendRepr::SimdVector { element, count: _ }
|
||||
if element.align(dl).abi > align.abi =>
|
||||
{
|
||||
BackendRepr::Memory { sized: true }
|
||||
}
|
||||
// the alignment tests passed and we can use this
|
||||
BackendRepr::Scalar(..)
|
||||
| BackendRepr::ScalarPair(..)
|
||||
| BackendRepr::Vector { .. }
|
||||
| BackendRepr::SimdVector { .. }
|
||||
| BackendRepr::Memory { .. } => repr,
|
||||
},
|
||||
};
|
||||
|
|
@ -464,7 +538,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
|
|||
hide_niches(a);
|
||||
hide_niches(b);
|
||||
}
|
||||
BackendRepr::Vector { element, count: _ } => hide_niches(element),
|
||||
BackendRepr::SimdVector { element, count: _ } => hide_niches(element),
|
||||
BackendRepr::Memory { sized: _ } => {}
|
||||
}
|
||||
st.largest_niche = None;
|
||||
|
|
@ -947,7 +1021,8 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
|
|||
// Common prim might be uninit.
|
||||
Scalar::Union { value: prim }
|
||||
};
|
||||
let pair = self.scalar_pair::<FieldIdx, VariantIdx>(tag, prim_scalar);
|
||||
let pair =
|
||||
LayoutData::<FieldIdx, VariantIdx>::scalar_pair(&self.cx, tag, prim_scalar);
|
||||
let pair_offsets = match pair.fields {
|
||||
FieldsShape::Arbitrary { ref offsets, ref memory_index } => {
|
||||
assert_eq!(memory_index.raw, [0, 1]);
|
||||
|
|
@ -1314,7 +1389,9 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
|
|||
match field.backend_repr {
|
||||
// For plain scalars, or vectors of them, we can't unpack
|
||||
// newtypes for `#[repr(C)]`, as that affects C ABIs.
|
||||
BackendRepr::Scalar(_) | BackendRepr::Vector { .. } if optimize_abi => {
|
||||
BackendRepr::Scalar(_) | BackendRepr::SimdVector { .. }
|
||||
if optimize_abi =>
|
||||
{
|
||||
abi = field.backend_repr;
|
||||
}
|
||||
// But scalar pairs are Rust-specific and get
|
||||
|
|
@ -1337,7 +1414,8 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
|
|||
} else {
|
||||
((j, b), (i, a))
|
||||
};
|
||||
let pair = self.scalar_pair::<FieldIdx, VariantIdx>(a, b);
|
||||
let pair =
|
||||
LayoutData::<FieldIdx, VariantIdx>::scalar_pair(&self.cx, a, b);
|
||||
let pair_offsets = match pair.fields {
|
||||
FieldsShape::Arbitrary { ref offsets, ref memory_index } => {
|
||||
assert_eq!(memory_index.raw, [0, 1]);
|
||||
|
|
|
|||
320
compiler/rustc_abi/src/layout/coroutine.rs
Normal file
320
compiler/rustc_abi/src/layout/coroutine.rs
Normal file
|
|
@ -0,0 +1,320 @@
|
|||
//! Coroutine layout logic.
|
||||
//!
|
||||
//! When laying out coroutines, we divide our saved local fields into two
|
||||
//! categories: overlap-eligible and overlap-ineligible.
|
||||
//!
|
||||
//! Those fields which are ineligible for overlap go in a "prefix" at the
|
||||
//! beginning of the layout, and always have space reserved for them.
|
||||
//!
|
||||
//! Overlap-eligible fields are only assigned to one variant, so we lay
|
||||
//! those fields out for each variant and put them right after the
|
||||
//! prefix.
|
||||
//!
|
||||
//! Finally, in the layout details, we point to the fields from the
|
||||
//! variants they are assigned to. It is possible for some fields to be
|
||||
//! included in multiple variants. No field ever "moves around" in the
|
||||
//! layout; its offset is always the same.
|
||||
//!
|
||||
//! Also included in the layout are the upvars and the discriminant.
|
||||
//! These are included as fields on the "outer" layout; they are not part
|
||||
//! of any variant.
|
||||
|
||||
use std::iter;
|
||||
|
||||
use rustc_index::bit_set::{BitMatrix, DenseBitSet};
|
||||
use rustc_index::{Idx, IndexSlice, IndexVec};
|
||||
use tracing::{debug, trace};
|
||||
|
||||
use crate::{
|
||||
BackendRepr, FieldsShape, HasDataLayout, Integer, LayoutData, Primitive, ReprOptions, Scalar,
|
||||
StructKind, TagEncoding, Variants, WrappingRange,
|
||||
};
|
||||
|
||||
/// Overlap eligibility and variant assignment for each CoroutineSavedLocal.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
enum SavedLocalEligibility<VariantIdx, FieldIdx> {
|
||||
Unassigned,
|
||||
Assigned(VariantIdx),
|
||||
Ineligible(Option<FieldIdx>),
|
||||
}
|
||||
|
||||
/// Compute the eligibility and assignment of each local.
|
||||
fn coroutine_saved_local_eligibility<VariantIdx: Idx, FieldIdx: Idx, LocalIdx: Idx>(
|
||||
nb_locals: usize,
|
||||
variant_fields: &IndexSlice<VariantIdx, IndexVec<FieldIdx, LocalIdx>>,
|
||||
storage_conflicts: &BitMatrix<LocalIdx, LocalIdx>,
|
||||
) -> (DenseBitSet<LocalIdx>, IndexVec<LocalIdx, SavedLocalEligibility<VariantIdx, FieldIdx>>) {
|
||||
use SavedLocalEligibility::*;
|
||||
|
||||
let mut assignments: IndexVec<LocalIdx, _> = IndexVec::from_elem_n(Unassigned, nb_locals);
|
||||
|
||||
// The saved locals not eligible for overlap. These will get
|
||||
// "promoted" to the prefix of our coroutine.
|
||||
let mut ineligible_locals = DenseBitSet::new_empty(nb_locals);
|
||||
|
||||
// Figure out which of our saved locals are fields in only
|
||||
// one variant. The rest are deemed ineligible for overlap.
|
||||
for (variant_index, fields) in variant_fields.iter_enumerated() {
|
||||
for local in fields {
|
||||
match assignments[*local] {
|
||||
Unassigned => {
|
||||
assignments[*local] = Assigned(variant_index);
|
||||
}
|
||||
Assigned(idx) => {
|
||||
// We've already seen this local at another suspension
|
||||
// point, so it is no longer a candidate.
|
||||
trace!(
|
||||
"removing local {:?} in >1 variant ({:?}, {:?})",
|
||||
local, variant_index, idx
|
||||
);
|
||||
ineligible_locals.insert(*local);
|
||||
assignments[*local] = Ineligible(None);
|
||||
}
|
||||
Ineligible(_) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Next, check every pair of eligible locals to see if they
|
||||
// conflict.
|
||||
for local_a in storage_conflicts.rows() {
|
||||
let conflicts_a = storage_conflicts.count(local_a);
|
||||
if ineligible_locals.contains(local_a) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for local_b in storage_conflicts.iter(local_a) {
|
||||
// local_a and local_b are storage live at the same time, therefore they
|
||||
// cannot overlap in the coroutine layout. The only way to guarantee
|
||||
// this is if they are in the same variant, or one is ineligible
|
||||
// (which means it is stored in every variant).
|
||||
if ineligible_locals.contains(local_b) || assignments[local_a] == assignments[local_b] {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If they conflict, we will choose one to make ineligible.
|
||||
// This is not always optimal; it's just a greedy heuristic that
|
||||
// seems to produce good results most of the time.
|
||||
let conflicts_b = storage_conflicts.count(local_b);
|
||||
let (remove, other) =
|
||||
if conflicts_a > conflicts_b { (local_a, local_b) } else { (local_b, local_a) };
|
||||
ineligible_locals.insert(remove);
|
||||
assignments[remove] = Ineligible(None);
|
||||
trace!("removing local {:?} due to conflict with {:?}", remove, other);
|
||||
}
|
||||
}
|
||||
|
||||
// Count the number of variants in use. If only one of them, then it is
|
||||
// impossible to overlap any locals in our layout. In this case it's
|
||||
// always better to make the remaining locals ineligible, so we can
|
||||
// lay them out with the other locals in the prefix and eliminate
|
||||
// unnecessary padding bytes.
|
||||
{
|
||||
let mut used_variants = DenseBitSet::new_empty(variant_fields.len());
|
||||
for assignment in &assignments {
|
||||
if let Assigned(idx) = assignment {
|
||||
used_variants.insert(*idx);
|
||||
}
|
||||
}
|
||||
if used_variants.count() < 2 {
|
||||
for assignment in assignments.iter_mut() {
|
||||
*assignment = Ineligible(None);
|
||||
}
|
||||
ineligible_locals.insert_all();
|
||||
}
|
||||
}
|
||||
|
||||
// Write down the order of our locals that will be promoted to the prefix.
|
||||
{
|
||||
for (idx, local) in ineligible_locals.iter().enumerate() {
|
||||
assignments[local] = Ineligible(Some(FieldIdx::new(idx)));
|
||||
}
|
||||
}
|
||||
debug!("coroutine saved local assignments: {:?}", assignments);
|
||||
|
||||
(ineligible_locals, assignments)
|
||||
}
|
||||
|
||||
/// Compute the full coroutine layout.
|
||||
pub(super) fn layout<
|
||||
'a,
|
||||
F: core::ops::Deref<Target = &'a LayoutData<FieldIdx, VariantIdx>> + core::fmt::Debug + Copy,
|
||||
VariantIdx: Idx,
|
||||
FieldIdx: Idx,
|
||||
LocalIdx: Idx,
|
||||
>(
|
||||
calc: &super::LayoutCalculator<impl HasDataLayout>,
|
||||
local_layouts: &IndexSlice<LocalIdx, F>,
|
||||
mut prefix_layouts: IndexVec<FieldIdx, F>,
|
||||
variant_fields: &IndexSlice<VariantIdx, IndexVec<FieldIdx, LocalIdx>>,
|
||||
storage_conflicts: &BitMatrix<LocalIdx, LocalIdx>,
|
||||
tag_to_layout: impl Fn(Scalar) -> F,
|
||||
) -> super::LayoutCalculatorResult<FieldIdx, VariantIdx, F> {
|
||||
use SavedLocalEligibility::*;
|
||||
|
||||
let (ineligible_locals, assignments) =
|
||||
coroutine_saved_local_eligibility(local_layouts.len(), variant_fields, storage_conflicts);
|
||||
|
||||
// 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();
|
||||
|
||||
// `variant_fields` already accounts for the reserved variants, so no need to add them.
|
||||
let max_discr = (variant_fields.len() - 1) as u128;
|
||||
let discr_int = Integer::fit_unsigned(max_discr);
|
||||
let tag = Scalar::Initialized {
|
||||
value: Primitive::Int(discr_int, /* signed = */ false),
|
||||
valid_range: WrappingRange { start: 0, end: max_discr },
|
||||
};
|
||||
|
||||
let promoted_layouts = ineligible_locals.iter().map(|local| local_layouts[local]);
|
||||
prefix_layouts.push(tag_to_layout(tag));
|
||||
prefix_layouts.extend(promoted_layouts);
|
||||
let prefix =
|
||||
calc.univariant(&prefix_layouts, &ReprOptions::default(), StructKind::AlwaysSized)?;
|
||||
|
||||
let (prefix_size, prefix_align) = (prefix.size, prefix.align);
|
||||
|
||||
// Split the prefix layout into the "outer" fields (upvars and
|
||||
// discriminant) and the "promoted" fields. Promoted fields will
|
||||
// get included in each variant that requested them in
|
||||
// CoroutineLayout.
|
||||
debug!("prefix = {:#?}", prefix);
|
||||
let (outer_fields, promoted_offsets, promoted_memory_index) = match prefix.fields {
|
||||
FieldsShape::Arbitrary { mut offsets, memory_index } => {
|
||||
let mut inverse_memory_index = memory_index.invert_bijective_mapping();
|
||||
|
||||
// "a" (`0..b_start`) and "b" (`b_start..`) correspond to
|
||||
// "outer" and "promoted" fields respectively.
|
||||
let b_start = FieldIdx::new(tag_index + 1);
|
||||
let offsets_b = IndexVec::from_raw(offsets.raw.split_off(b_start.index()));
|
||||
let offsets_a = offsets;
|
||||
|
||||
// Disentangle the "a" and "b" components of `inverse_memory_index`
|
||||
// by preserving the order but keeping only one disjoint "half" each.
|
||||
// FIXME(eddyb) build a better abstraction for permutations, if possible.
|
||||
let inverse_memory_index_b: IndexVec<u32, FieldIdx> = inverse_memory_index
|
||||
.iter()
|
||||
.filter_map(|&i| i.index().checked_sub(b_start.index()).map(FieldIdx::new))
|
||||
.collect();
|
||||
inverse_memory_index.raw.retain(|&i| i.index() < b_start.index());
|
||||
let inverse_memory_index_a = inverse_memory_index;
|
||||
|
||||
// Since `inverse_memory_index_{a,b}` each only refer to their
|
||||
// respective fields, they can be safely inverted
|
||||
let memory_index_a = inverse_memory_index_a.invert_bijective_mapping();
|
||||
let memory_index_b = inverse_memory_index_b.invert_bijective_mapping();
|
||||
|
||||
let outer_fields =
|
||||
FieldsShape::Arbitrary { offsets: offsets_a, memory_index: memory_index_a };
|
||||
(outer_fields, offsets_b, memory_index_b)
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let mut size = prefix.size;
|
||||
let mut align = prefix.align;
|
||||
let variants = variant_fields
|
||||
.iter_enumerated()
|
||||
.map(|(index, variant_fields)| {
|
||||
// Only include overlap-eligible fields when we compute our variant layout.
|
||||
let variant_only_tys = variant_fields
|
||||
.iter()
|
||||
.filter(|local| match assignments[**local] {
|
||||
Unassigned => unreachable!(),
|
||||
Assigned(v) if v == index => true,
|
||||
Assigned(_) => unreachable!("assignment does not match variant"),
|
||||
Ineligible(_) => false,
|
||||
})
|
||||
.map(|local| local_layouts[*local]);
|
||||
|
||||
let mut variant = calc.univariant(
|
||||
&variant_only_tys.collect::<IndexVec<_, _>>(),
|
||||
&ReprOptions::default(),
|
||||
StructKind::Prefixed(prefix_size, prefix_align.abi),
|
||||
)?;
|
||||
variant.variants = Variants::Single { index };
|
||||
|
||||
let FieldsShape::Arbitrary { offsets, memory_index } = variant.fields else {
|
||||
unreachable!();
|
||||
};
|
||||
|
||||
// Now, stitch the promoted and variant-only fields back together in
|
||||
// the order they are mentioned by our CoroutineLayout.
|
||||
// Because we only use some subset (that can differ between variants)
|
||||
// of the promoted fields, we can't just pick those elements of the
|
||||
// `promoted_memory_index` (as we'd end up with gaps).
|
||||
// So instead, we build an "inverse memory_index", as if all of the
|
||||
// promoted fields were being used, but leave the elements not in the
|
||||
// subset as `invalid_field_idx`, which we can filter out later to
|
||||
// obtain a valid (bijective) mapping.
|
||||
let invalid_field_idx = promoted_memory_index.len() + memory_index.len();
|
||||
let mut combined_inverse_memory_index =
|
||||
IndexVec::from_elem_n(FieldIdx::new(invalid_field_idx), invalid_field_idx);
|
||||
|
||||
let mut offsets_and_memory_index = iter::zip(offsets, memory_index);
|
||||
let combined_offsets = variant_fields
|
||||
.iter_enumerated()
|
||||
.map(|(i, local)| {
|
||||
let (offset, memory_index) = match assignments[*local] {
|
||||
Unassigned => unreachable!(),
|
||||
Assigned(_) => {
|
||||
let (offset, memory_index) = offsets_and_memory_index.next().unwrap();
|
||||
(offset, promoted_memory_index.len() as u32 + memory_index)
|
||||
}
|
||||
Ineligible(field_idx) => {
|
||||
let field_idx = field_idx.unwrap();
|
||||
(promoted_offsets[field_idx], promoted_memory_index[field_idx])
|
||||
}
|
||||
};
|
||||
combined_inverse_memory_index[memory_index] = i;
|
||||
offset
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Remove the unused slots and invert the mapping to obtain the
|
||||
// combined `memory_index` (also see previous comment).
|
||||
combined_inverse_memory_index.raw.retain(|&i| i.index() != invalid_field_idx);
|
||||
let combined_memory_index = combined_inverse_memory_index.invert_bijective_mapping();
|
||||
|
||||
variant.fields = FieldsShape::Arbitrary {
|
||||
offsets: combined_offsets,
|
||||
memory_index: combined_memory_index,
|
||||
};
|
||||
|
||||
size = size.max(variant.size);
|
||||
align = align.max(variant.align);
|
||||
Ok(variant)
|
||||
})
|
||||
.collect::<Result<IndexVec<VariantIdx, _>, _>>()?;
|
||||
|
||||
size = size.align_to(align.abi);
|
||||
|
||||
let uninhabited = prefix.uninhabited || variants.iter().all(|v| v.is_uninhabited());
|
||||
let abi = BackendRepr::Memory { sized: true };
|
||||
|
||||
Ok(LayoutData {
|
||||
variants: Variants::Multiple {
|
||||
tag,
|
||||
tag_encoding: TagEncoding::Direct,
|
||||
tag_field: tag_index,
|
||||
variants,
|
||||
},
|
||||
fields: outer_fields,
|
||||
backend_repr: abi,
|
||||
// Suppress niches inside coroutines. If the niche is inside a field that is aliased (due to
|
||||
// self-referentiality), getting the discriminant can cause aliasing violations.
|
||||
// `UnsafeCell` blocks niches for the same reason, but we don't yet have `UnsafePinned` that
|
||||
// would do the same for us here.
|
||||
// See <https://github.com/rust-lang/rust/issues/63818>, <https://github.com/rust-lang/miri/issues/3780>.
|
||||
// FIXME: Remove when <https://github.com/rust-lang/rust/issues/125735> is implemented and aliased coroutine fields are wrapped in `UnsafePinned`.
|
||||
largest_niche: None,
|
||||
uninhabited,
|
||||
size,
|
||||
align,
|
||||
max_repr_align: None,
|
||||
unadjusted_abi_align: align.abi,
|
||||
randomization_seed: Default::default(),
|
||||
})
|
||||
}
|
||||
148
compiler/rustc_abi/src/layout/simple.rs
Normal file
148
compiler/rustc_abi/src/layout/simple.rs
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
use std::num::NonZero;
|
||||
|
||||
use rustc_hashes::Hash64;
|
||||
use rustc_index::{Idx, IndexVec};
|
||||
|
||||
use crate::{
|
||||
BackendRepr, FieldsShape, HasDataLayout, LayoutData, Niche, Primitive, Scalar, Size, Variants,
|
||||
};
|
||||
|
||||
/// "Simple" layout constructors that cannot fail.
|
||||
impl<FieldIdx: Idx, VariantIdx: Idx> LayoutData<FieldIdx, VariantIdx> {
|
||||
pub fn unit<C: HasDataLayout>(cx: &C, sized: bool) -> Self {
|
||||
let dl = cx.data_layout();
|
||||
LayoutData {
|
||||
variants: Variants::Single { index: VariantIdx::new(0) },
|
||||
fields: FieldsShape::Arbitrary {
|
||||
offsets: IndexVec::new(),
|
||||
memory_index: IndexVec::new(),
|
||||
},
|
||||
backend_repr: BackendRepr::Memory { sized },
|
||||
largest_niche: None,
|
||||
uninhabited: false,
|
||||
align: dl.i8_align,
|
||||
size: Size::ZERO,
|
||||
max_repr_align: None,
|
||||
unadjusted_abi_align: dl.i8_align.abi,
|
||||
randomization_seed: Hash64::new(0),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn never_type<C: HasDataLayout>(cx: &C) -> Self {
|
||||
let dl = cx.data_layout();
|
||||
// This is also used for uninhabited enums, so we use `Variants::Empty`.
|
||||
LayoutData {
|
||||
variants: Variants::Empty,
|
||||
fields: FieldsShape::Primitive,
|
||||
backend_repr: BackendRepr::Memory { sized: true },
|
||||
largest_niche: None,
|
||||
uninhabited: true,
|
||||
align: dl.i8_align,
|
||||
size: Size::ZERO,
|
||||
max_repr_align: None,
|
||||
unadjusted_abi_align: dl.i8_align.abi,
|
||||
randomization_seed: Hash64::ZERO,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn scalar<C: HasDataLayout>(cx: &C, scalar: Scalar) -> Self {
|
||||
let largest_niche = Niche::from_scalar(cx, Size::ZERO, scalar);
|
||||
let size = scalar.size(cx);
|
||||
let align = scalar.align(cx);
|
||||
|
||||
let range = scalar.valid_range(cx);
|
||||
|
||||
// All primitive types for which we don't have subtype coercions should get a distinct seed,
|
||||
// so that types wrapping them can use randomization to arrive at distinct layouts.
|
||||
//
|
||||
// Some type information is already lost at this point, so as an approximation we derive
|
||||
// the seed from what remains. For example on 64-bit targets usize and u64 can no longer
|
||||
// be distinguished.
|
||||
let randomization_seed = size
|
||||
.bytes()
|
||||
.wrapping_add(
|
||||
match scalar.primitive() {
|
||||
Primitive::Int(_, true) => 1,
|
||||
Primitive::Int(_, false) => 2,
|
||||
Primitive::Float(_) => 3,
|
||||
Primitive::Pointer(_) => 4,
|
||||
} << 32,
|
||||
)
|
||||
// distinguishes references from pointers
|
||||
.wrapping_add((range.start as u64).rotate_right(16))
|
||||
// distinguishes char from u32 and bool from u8
|
||||
.wrapping_add((range.end as u64).rotate_right(16));
|
||||
|
||||
LayoutData {
|
||||
variants: Variants::Single { index: VariantIdx::new(0) },
|
||||
fields: FieldsShape::Primitive,
|
||||
backend_repr: BackendRepr::Scalar(scalar),
|
||||
largest_niche,
|
||||
uninhabited: false,
|
||||
size,
|
||||
align,
|
||||
max_repr_align: None,
|
||||
unadjusted_abi_align: align.abi,
|
||||
randomization_seed: Hash64::new(randomization_seed),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn scalar_pair<C: HasDataLayout>(cx: &C, a: Scalar, b: Scalar) -> Self {
|
||||
let dl = cx.data_layout();
|
||||
let b_align = b.align(dl);
|
||||
let align = a.align(dl).max(b_align).max(dl.aggregate_align);
|
||||
let b_offset = a.size(dl).align_to(b_align.abi);
|
||||
let size = (b_offset + b.size(dl)).align_to(align.abi);
|
||||
|
||||
// HACK(nox): We iter on `b` and then `a` because `max_by_key`
|
||||
// returns the last maximum.
|
||||
let largest_niche = Niche::from_scalar(dl, b_offset, b)
|
||||
.into_iter()
|
||||
.chain(Niche::from_scalar(dl, Size::ZERO, a))
|
||||
.max_by_key(|niche| niche.available(dl));
|
||||
|
||||
let combined_seed = a.size(dl).bytes().wrapping_add(b.size(dl).bytes());
|
||||
|
||||
LayoutData {
|
||||
variants: Variants::Single { index: VariantIdx::new(0) },
|
||||
fields: FieldsShape::Arbitrary {
|
||||
offsets: [Size::ZERO, b_offset].into(),
|
||||
memory_index: [0, 1].into(),
|
||||
},
|
||||
backend_repr: BackendRepr::ScalarPair(a, b),
|
||||
largest_niche,
|
||||
uninhabited: false,
|
||||
align,
|
||||
size,
|
||||
max_repr_align: None,
|
||||
unadjusted_abi_align: align.abi,
|
||||
randomization_seed: Hash64::new(combined_seed),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a dummy layout for an uninhabited variant.
|
||||
///
|
||||
/// Uninhabited variants get pruned as part of the layout calculation,
|
||||
/// so this can be used after the fact to reconstitute a layout.
|
||||
pub fn uninhabited_variant<C: HasDataLayout>(cx: &C, index: VariantIdx, fields: usize) -> Self {
|
||||
let dl = cx.data_layout();
|
||||
LayoutData {
|
||||
variants: Variants::Single { index },
|
||||
fields: match NonZero::new(fields) {
|
||||
Some(fields) => FieldsShape::Union(fields),
|
||||
None => FieldsShape::Arbitrary {
|
||||
offsets: IndexVec::new(),
|
||||
memory_index: IndexVec::new(),
|
||||
},
|
||||
},
|
||||
backend_repr: BackendRepr::Memory { sized: true },
|
||||
largest_niche: None,
|
||||
uninhabited: true,
|
||||
align: dl.i8_align,
|
||||
size: Size::ZERO,
|
||||
max_repr_align: None,
|
||||
unadjusted_abi_align: dl.i8_align.abi,
|
||||
randomization_seed: Hash64::ZERO,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -150,6 +150,12 @@ impl<'a, Ty> Deref for TyAndLayout<'a, Ty> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, Ty> AsRef<LayoutData<FieldIdx, VariantIdx>> for TyAndLayout<'a, Ty> {
|
||||
fn as_ref(&self) -> &LayoutData<FieldIdx, VariantIdx> {
|
||||
&*self.layout.0.0
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait that needs to be implemented by the higher-level type representation
|
||||
/// (e.g. `rustc_middle::ty::Ty`), to provide `rustc_target::abi` functionality.
|
||||
pub trait TyAbiInterface<'a, C>: Sized + std::fmt::Debug {
|
||||
|
|
@ -219,7 +225,7 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
|
|||
C: HasDataLayout,
|
||||
{
|
||||
match self.backend_repr {
|
||||
BackendRepr::Vector { .. } => self.size == expected_size,
|
||||
BackendRepr::SimdVector { .. } => self.size == expected_size,
|
||||
BackendRepr::Memory { .. } => {
|
||||
if self.fields.count() == 1 && self.fields.offset(0).bytes() == 0 {
|
||||
self.field(cx, 0).is_single_vector_element(cx, expected_size)
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@
|
|||
#![cfg_attr(feature = "nightly", feature(rustc_attrs))]
|
||||
#![cfg_attr(feature = "nightly", feature(rustdoc_internals))]
|
||||
#![cfg_attr(feature = "nightly", feature(step_trait))]
|
||||
#![warn(unreachable_pub)]
|
||||
// tidy-alphabetical-end
|
||||
|
||||
/*! ABI handling for rustc
|
||||
|
|
@ -205,6 +204,13 @@ impl ReprOptions {
|
|||
}
|
||||
}
|
||||
|
||||
/// The maximum supported number of lanes in a SIMD vector.
|
||||
///
|
||||
/// This value is selected based on backend support:
|
||||
/// * LLVM does not appear to have a vector width limit.
|
||||
/// * Cranelift stores the base-2 log of the lane count in a 4 bit integer.
|
||||
pub const MAX_SIMD_LANES: u64 = 1 << 0xF;
|
||||
|
||||
/// Parsed [Data layout](https://llvm.org/docs/LangRef.html#data-layout)
|
||||
/// for a target, which contains everything needed to compute layouts.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
|
|
@ -1410,7 +1416,7 @@ impl AddressSpace {
|
|||
pub enum BackendRepr {
|
||||
Scalar(Scalar),
|
||||
ScalarPair(Scalar, Scalar),
|
||||
Vector {
|
||||
SimdVector {
|
||||
element: Scalar,
|
||||
count: u64,
|
||||
},
|
||||
|
|
@ -1426,9 +1432,9 @@ impl BackendRepr {
|
|||
#[inline]
|
||||
pub fn is_unsized(&self) -> bool {
|
||||
match *self {
|
||||
BackendRepr::Scalar(_) | BackendRepr::ScalarPair(..) | BackendRepr::Vector { .. } => {
|
||||
false
|
||||
}
|
||||
BackendRepr::Scalar(_)
|
||||
| BackendRepr::ScalarPair(..)
|
||||
| BackendRepr::SimdVector { .. } => false,
|
||||
BackendRepr::Memory { sized } => !sized,
|
||||
}
|
||||
}
|
||||
|
|
@ -1467,7 +1473,7 @@ impl BackendRepr {
|
|||
BackendRepr::Scalar(s) => Some(s.align(cx).abi),
|
||||
BackendRepr::ScalarPair(s1, s2) => Some(s1.align(cx).max(s2.align(cx)).abi),
|
||||
// The align of a Vector can vary in surprising ways
|
||||
BackendRepr::Vector { .. } | BackendRepr::Memory { .. } => None,
|
||||
BackendRepr::SimdVector { .. } | BackendRepr::Memory { .. } => None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1489,7 +1495,7 @@ impl BackendRepr {
|
|||
Some(size)
|
||||
}
|
||||
// The size of a Vector can vary in surprising ways
|
||||
BackendRepr::Vector { .. } | BackendRepr::Memory { .. } => None,
|
||||
BackendRepr::SimdVector { .. } | BackendRepr::Memory { .. } => None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1500,8 +1506,8 @@ impl BackendRepr {
|
|||
BackendRepr::ScalarPair(s1, s2) => {
|
||||
BackendRepr::ScalarPair(s1.to_union(), s2.to_union())
|
||||
}
|
||||
BackendRepr::Vector { element, count } => {
|
||||
BackendRepr::Vector { element: element.to_union(), count }
|
||||
BackendRepr::SimdVector { element, count } => {
|
||||
BackendRepr::SimdVector { element: element.to_union(), count }
|
||||
}
|
||||
BackendRepr::Memory { .. } => BackendRepr::Memory { sized: true },
|
||||
}
|
||||
|
|
@ -1513,8 +1519,8 @@ impl BackendRepr {
|
|||
// We do *not* ignore the sign since it matters for some ABIs (e.g. s390x).
|
||||
(BackendRepr::Scalar(l), BackendRepr::Scalar(r)) => l.primitive() == r.primitive(),
|
||||
(
|
||||
BackendRepr::Vector { element: element_l, count: count_l },
|
||||
BackendRepr::Vector { element: element_r, count: count_r },
|
||||
BackendRepr::SimdVector { element: element_l, count: count_l },
|
||||
BackendRepr::SimdVector { element: element_r, count: count_r },
|
||||
) => element_l.primitive() == element_r.primitive() && count_l == count_r,
|
||||
(BackendRepr::ScalarPair(l1, l2), BackendRepr::ScalarPair(r1, r2)) => {
|
||||
l1.primitive() == r1.primitive() && l2.primitive() == r2.primitive()
|
||||
|
|
@ -1735,7 +1741,7 @@ impl<FieldIdx: Idx, VariantIdx: Idx> LayoutData<FieldIdx, VariantIdx> {
|
|||
/// Returns `true` if this is an aggregate type (including a ScalarPair!)
|
||||
pub fn is_aggregate(&self) -> bool {
|
||||
match self.backend_repr {
|
||||
BackendRepr::Scalar(_) | BackendRepr::Vector { .. } => false,
|
||||
BackendRepr::Scalar(_) | BackendRepr::SimdVector { .. } => false,
|
||||
BackendRepr::ScalarPair(..) | BackendRepr::Memory { .. } => true,
|
||||
}
|
||||
}
|
||||
|
|
@ -1744,48 +1750,6 @@ impl<FieldIdx: Idx, VariantIdx: Idx> LayoutData<FieldIdx, VariantIdx> {
|
|||
pub fn is_uninhabited(&self) -> bool {
|
||||
self.uninhabited
|
||||
}
|
||||
|
||||
pub fn scalar<C: HasDataLayout>(cx: &C, scalar: Scalar) -> Self {
|
||||
let largest_niche = Niche::from_scalar(cx, Size::ZERO, scalar);
|
||||
let size = scalar.size(cx);
|
||||
let align = scalar.align(cx);
|
||||
|
||||
let range = scalar.valid_range(cx);
|
||||
|
||||
// All primitive types for which we don't have subtype coercions should get a distinct seed,
|
||||
// so that types wrapping them can use randomization to arrive at distinct layouts.
|
||||
//
|
||||
// Some type information is already lost at this point, so as an approximation we derive
|
||||
// the seed from what remains. For example on 64-bit targets usize and u64 can no longer
|
||||
// be distinguished.
|
||||
let randomization_seed = size
|
||||
.bytes()
|
||||
.wrapping_add(
|
||||
match scalar.primitive() {
|
||||
Primitive::Int(_, true) => 1,
|
||||
Primitive::Int(_, false) => 2,
|
||||
Primitive::Float(_) => 3,
|
||||
Primitive::Pointer(_) => 4,
|
||||
} << 32,
|
||||
)
|
||||
// distinguishes references from pointers
|
||||
.wrapping_add((range.start as u64).rotate_right(16))
|
||||
// distinguishes char from u32 and bool from u8
|
||||
.wrapping_add((range.end as u64).rotate_right(16));
|
||||
|
||||
LayoutData {
|
||||
variants: Variants::Single { index: VariantIdx::new(0) },
|
||||
fields: FieldsShape::Primitive,
|
||||
backend_repr: BackendRepr::Scalar(scalar),
|
||||
largest_niche,
|
||||
uninhabited: false,
|
||||
size,
|
||||
align,
|
||||
max_repr_align: None,
|
||||
unadjusted_abi_align: align.abi,
|
||||
randomization_seed: Hash64::new(randomization_seed),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<FieldIdx: Idx, VariantIdx: Idx> fmt::Debug for LayoutData<FieldIdx, VariantIdx>
|
||||
|
|
@ -1812,7 +1776,7 @@ where
|
|||
f.debug_struct("Layout")
|
||||
.field("size", size)
|
||||
.field("align", align)
|
||||
.field("abi", backend_repr)
|
||||
.field("backend_repr", backend_repr)
|
||||
.field("fields", fields)
|
||||
.field("largest_niche", largest_niche)
|
||||
.field("uninhabited", uninhabited)
|
||||
|
|
@ -1877,9 +1841,9 @@ impl<FieldIdx: Idx, VariantIdx: Idx> LayoutData<FieldIdx, VariantIdx> {
|
|||
/// non-trivial alignment constraints. You probably want to use `is_1zst` instead.
|
||||
pub fn is_zst(&self) -> bool {
|
||||
match self.backend_repr {
|
||||
BackendRepr::Scalar(_) | BackendRepr::ScalarPair(..) | BackendRepr::Vector { .. } => {
|
||||
false
|
||||
}
|
||||
BackendRepr::Scalar(_)
|
||||
| BackendRepr::ScalarPair(..)
|
||||
| BackendRepr::SimdVector { .. } => false,
|
||||
BackendRepr::Memory { sized } => sized && self.size.bytes() == 0,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@
|
|||
#![feature(maybe_uninit_slice)]
|
||||
#![feature(rustc_attrs)]
|
||||
#![feature(rustdoc_internals)]
|
||||
#![warn(unreachable_pub)]
|
||||
// tidy-alphabetical-end
|
||||
|
||||
use std::alloc::Layout;
|
||||
|
|
@ -93,7 +92,7 @@ impl<T> ArenaChunk<T> {
|
|||
#[inline]
|
||||
fn end(&mut self) -> *mut T {
|
||||
unsafe {
|
||||
if mem::size_of::<T>() == 0 {
|
||||
if size_of::<T>() == 0 {
|
||||
// A pointer as large as possible for zero-sized elements.
|
||||
ptr::without_provenance_mut(!0)
|
||||
} else {
|
||||
|
|
@ -151,7 +150,7 @@ impl<T> TypedArena<T> {
|
|||
}
|
||||
|
||||
unsafe {
|
||||
if mem::size_of::<T>() == 0 {
|
||||
if size_of::<T>() == 0 {
|
||||
self.ptr.set(self.ptr.get().wrapping_byte_add(1));
|
||||
let ptr = ptr::NonNull::<T>::dangling().as_ptr();
|
||||
// Don't drop the object. This `write` is equivalent to `forget`.
|
||||
|
|
@ -173,13 +172,13 @@ impl<T> TypedArena<T> {
|
|||
// FIXME: this should *likely* use `offset_from`, but more
|
||||
// investigation is needed (including running tests in miri).
|
||||
let available_bytes = self.end.get().addr() - self.ptr.get().addr();
|
||||
let additional_bytes = additional.checked_mul(mem::size_of::<T>()).unwrap();
|
||||
let additional_bytes = additional.checked_mul(size_of::<T>()).unwrap();
|
||||
available_bytes >= additional_bytes
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn alloc_raw_slice(&self, len: usize) -> *mut T {
|
||||
assert!(mem::size_of::<T>() != 0);
|
||||
assert!(size_of::<T>() != 0);
|
||||
assert!(len != 0);
|
||||
|
||||
// Ensure the current chunk can fit `len` objects.
|
||||
|
|
@ -213,7 +212,7 @@ impl<T> TypedArena<T> {
|
|||
// So we collect all the elements beforehand, which takes care of reentrancy and panic
|
||||
// safety. This function is much less hot than `DroplessArena::alloc_from_iter`, so it
|
||||
// doesn't need to be hyper-optimized.
|
||||
assert!(mem::size_of::<T>() != 0);
|
||||
assert!(size_of::<T>() != 0);
|
||||
|
||||
let mut vec: SmallVec<[_; 8]> = iter.into_iter().collect();
|
||||
if vec.is_empty() {
|
||||
|
|
@ -236,7 +235,7 @@ impl<T> TypedArena<T> {
|
|||
unsafe {
|
||||
// We need the element size to convert chunk sizes (ranging from
|
||||
// PAGE to HUGE_PAGE bytes) to element counts.
|
||||
let elem_size = cmp::max(1, mem::size_of::<T>());
|
||||
let elem_size = cmp::max(1, size_of::<T>());
|
||||
let mut chunks = self.chunks.borrow_mut();
|
||||
let mut new_cap;
|
||||
if let Some(last_chunk) = chunks.last_mut() {
|
||||
|
|
@ -246,7 +245,7 @@ impl<T> TypedArena<T> {
|
|||
// FIXME: this should *likely* use `offset_from`, but more
|
||||
// investigation is needed (including running tests in miri).
|
||||
let used_bytes = self.ptr.get().addr() - last_chunk.start().addr();
|
||||
last_chunk.entries = used_bytes / mem::size_of::<T>();
|
||||
last_chunk.entries = used_bytes / size_of::<T>();
|
||||
}
|
||||
|
||||
// If the previous chunk's len is less than HUGE_PAGE
|
||||
|
|
@ -276,7 +275,7 @@ impl<T> TypedArena<T> {
|
|||
let end = self.ptr.get().addr();
|
||||
// We then calculate the number of elements to be dropped in the last chunk,
|
||||
// which is the filled area's length.
|
||||
let diff = if mem::size_of::<T>() == 0 {
|
||||
let diff = if size_of::<T>() == 0 {
|
||||
// `T` is ZST. It can't have a drop flag, so the value here doesn't matter. We get
|
||||
// the number of zero-sized values in the last and only chunk, just out of caution.
|
||||
// Recall that `end` was incremented for each allocated value.
|
||||
|
|
@ -284,7 +283,7 @@ impl<T> TypedArena<T> {
|
|||
} else {
|
||||
// FIXME: this should *likely* use `offset_from`, but more
|
||||
// investigation is needed (including running tests in miri).
|
||||
(end - start) / mem::size_of::<T>()
|
||||
(end - start) / size_of::<T>()
|
||||
};
|
||||
// Pass that to the `destroy` method.
|
||||
unsafe {
|
||||
|
|
@ -329,7 +328,7 @@ fn align_up(val: usize, align: usize) -> usize {
|
|||
|
||||
// Pointer alignment is common in compiler types, so keep `DroplessArena` aligned to them
|
||||
// to optimize away alignment code.
|
||||
const DROPLESS_ALIGNMENT: usize = mem::align_of::<usize>();
|
||||
const DROPLESS_ALIGNMENT: usize = align_of::<usize>();
|
||||
|
||||
/// An arena that can hold objects of multiple different types that impl `Copy`
|
||||
/// and/or satisfy `!mem::needs_drop`.
|
||||
|
|
@ -447,7 +446,7 @@ impl DroplessArena {
|
|||
#[inline]
|
||||
pub fn alloc<T>(&self, object: T) -> &mut T {
|
||||
assert!(!mem::needs_drop::<T>());
|
||||
assert!(mem::size_of::<T>() != 0);
|
||||
assert!(size_of::<T>() != 0);
|
||||
|
||||
let mem = self.alloc_raw(Layout::new::<T>()) as *mut T;
|
||||
|
||||
|
|
@ -471,7 +470,7 @@ impl DroplessArena {
|
|||
T: Copy,
|
||||
{
|
||||
assert!(!mem::needs_drop::<T>());
|
||||
assert!(mem::size_of::<T>() != 0);
|
||||
assert!(size_of::<T>() != 0);
|
||||
assert!(!slice.is_empty());
|
||||
|
||||
let mem = self.alloc_raw(Layout::for_value::<[T]>(slice)) as *mut T;
|
||||
|
|
@ -546,7 +545,7 @@ impl DroplessArena {
|
|||
// Warning: this function is reentrant: `iter` could hold a reference to `&self` and
|
||||
// allocate additional elements while we're iterating.
|
||||
let iter = iter.into_iter();
|
||||
assert!(mem::size_of::<T>() != 0);
|
||||
assert!(size_of::<T>() != 0);
|
||||
assert!(!mem::needs_drop::<T>());
|
||||
|
||||
let size_hint = iter.size_hint();
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ pub use crate::format::*;
|
|||
use crate::ptr::P;
|
||||
use crate::token::{self, CommentKind, Delimiter};
|
||||
use crate::tokenstream::{DelimSpan, LazyAttrTokenStream, TokenStream};
|
||||
use crate::util::parser::{AssocOp, ExprPrecedence};
|
||||
use crate::util::parser::{ExprPrecedence, Fixity};
|
||||
|
||||
/// A "Label" is an identifier of some point in sources,
|
||||
/// e.g. in the following code:
|
||||
|
|
@ -124,10 +124,19 @@ impl Path {
|
|||
self.segments.first().is_some_and(|segment| segment.ident.name == kw::PathRoot)
|
||||
}
|
||||
|
||||
/// If this path is a single identifier with no arguments, does not ensure
|
||||
/// that the path resolves to a const param, the caller should check this.
|
||||
pub fn is_potential_trivial_const_arg(&self) -> bool {
|
||||
matches!(self.segments[..], [PathSegment { args: None, .. }])
|
||||
/// Check if this path is potentially a trivial const arg, i.e., one that can _potentially_
|
||||
/// be represented without an anon const in the HIR.
|
||||
///
|
||||
/// If `allow_mgca_arg` is true (as should be the case in most situations when
|
||||
/// `#![feature(min_generic_const_args)]` is enabled), then this always returns true
|
||||
/// because all paths are valid.
|
||||
///
|
||||
/// Otherwise, it returns true iff the path has exactly one segment, and it has no generic args
|
||||
/// (i.e., it is _potentially_ a const parameter).
|
||||
#[tracing::instrument(level = "debug", ret)]
|
||||
pub fn is_potential_trivial_const_arg(&self, allow_mgca_arg: bool) -> bool {
|
||||
allow_mgca_arg
|
||||
|| self.segments.len() == 1 && self.segments.iter().all(|seg| seg.args.is_none())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -417,9 +426,11 @@ impl WhereClause {
|
|||
/// A single predicate in a where-clause.
|
||||
#[derive(Clone, Encodable, Decodable, Debug)]
|
||||
pub struct WherePredicate {
|
||||
pub attrs: AttrVec,
|
||||
pub kind: WherePredicateKind,
|
||||
pub id: NodeId,
|
||||
pub span: Span,
|
||||
pub is_placeholder: bool,
|
||||
}
|
||||
|
||||
/// Predicate kind in where-clause.
|
||||
|
|
@ -937,8 +948,37 @@ impl BinOpKind {
|
|||
matches!(self, BinOpKind::And | BinOpKind::Or)
|
||||
}
|
||||
|
||||
pub fn precedence(&self) -> ExprPrecedence {
|
||||
use BinOpKind::*;
|
||||
match *self {
|
||||
Mul | Div | Rem => ExprPrecedence::Product,
|
||||
Add | Sub => ExprPrecedence::Sum,
|
||||
Shl | Shr => ExprPrecedence::Shift,
|
||||
BitAnd => ExprPrecedence::BitAnd,
|
||||
BitXor => ExprPrecedence::BitXor,
|
||||
BitOr => ExprPrecedence::BitOr,
|
||||
Lt | Gt | Le | Ge | Eq | Ne => ExprPrecedence::Compare,
|
||||
And => ExprPrecedence::LAnd,
|
||||
Or => ExprPrecedence::LOr,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fixity(&self) -> Fixity {
|
||||
use BinOpKind::*;
|
||||
match self {
|
||||
Eq | Ne | Lt | Le | Gt | Ge => Fixity::None,
|
||||
Add | Sub | Mul | Div | Rem | And | Or | BitXor | BitAnd | BitOr | Shl | Shr => {
|
||||
Fixity::Left
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_comparison(self) -> bool {
|
||||
crate::util::parser::AssocOp::from_ast_binop(self).is_comparison()
|
||||
use BinOpKind::*;
|
||||
match self {
|
||||
Eq | Ne | Lt | Le | Gt | Ge => true,
|
||||
Add | Sub | Mul | Div | Rem | And | Or | BitXor | BitAnd | BitOr | Shl | Shr => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the binary operator takes its arguments by value.
|
||||
|
|
@ -1177,22 +1217,31 @@ pub struct Expr {
|
|||
}
|
||||
|
||||
impl Expr {
|
||||
/// Could this expr be either `N`, or `{ N }`, where `N` is a const parameter.
|
||||
/// Check if this expression is potentially a trivial const arg, i.e., one that can _potentially_
|
||||
/// be represented without an anon const in the HIR.
|
||||
///
|
||||
/// If this is not the case, name resolution does not resolve `N` when using
|
||||
/// `min_const_generics` as more complex expressions are not supported.
|
||||
/// This will unwrap at most one block level (curly braces). After that, if the expression
|
||||
/// is a path, it mostly dispatches to [`Path::is_potential_trivial_const_arg`].
|
||||
/// See there for more info about `allow_mgca_arg`.
|
||||
///
|
||||
/// Does not ensure that the path resolves to a const param, the caller should check this.
|
||||
/// The only additional thing to note is that when `allow_mgca_arg` is false, this function
|
||||
/// will only allow paths with no qself, before dispatching to the `Path` function of
|
||||
/// the same name.
|
||||
///
|
||||
/// Does not ensure that the path resolves to a const param/item, the caller should check this.
|
||||
/// This also does not consider macros, so it's only correct after macro-expansion.
|
||||
pub fn is_potential_trivial_const_arg(&self) -> bool {
|
||||
pub fn is_potential_trivial_const_arg(&self, allow_mgca_arg: bool) -> bool {
|
||||
let this = self.maybe_unwrap_block();
|
||||
|
||||
if let ExprKind::Path(None, path) = &this.kind
|
||||
&& path.is_potential_trivial_const_arg()
|
||||
{
|
||||
true
|
||||
if allow_mgca_arg {
|
||||
matches!(this.kind, ExprKind::Path(..))
|
||||
} else {
|
||||
false
|
||||
if let ExprKind::Path(None, path) = &this.kind
|
||||
&& path.is_potential_trivial_const_arg(allow_mgca_arg)
|
||||
{
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1332,7 +1381,7 @@ impl Expr {
|
|||
ExprKind::Range(..) => ExprPrecedence::Range,
|
||||
|
||||
// Binop-like expr kinds, handled by `AssocOp`.
|
||||
ExprKind::Binary(op, ..) => AssocOp::from_ast_binop(op.node).precedence(),
|
||||
ExprKind::Binary(op, ..) => op.node.precedence(),
|
||||
ExprKind::Cast(..) => ExprPrecedence::Cast,
|
||||
|
||||
ExprKind::Assign(..) |
|
||||
|
|
@ -1350,6 +1399,7 @@ impl Expr {
|
|||
// Never need parens
|
||||
ExprKind::Array(_)
|
||||
| ExprKind::Await(..)
|
||||
| ExprKind::Use(..)
|
||||
| ExprKind::Block(..)
|
||||
| ExprKind::Call(..)
|
||||
| ExprKind::ConstBlock(_)
|
||||
|
|
@ -1424,6 +1474,15 @@ pub enum RangeLimits {
|
|||
Closed,
|
||||
}
|
||||
|
||||
impl RangeLimits {
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
RangeLimits::HalfOpen => "..",
|
||||
RangeLimits::Closed => "..=",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A method call (e.g. `x.foo::<Bar, Baz>(a, b, c)`).
|
||||
#[derive(Clone, Encodable, Decodable, Debug)]
|
||||
pub struct MethodCall {
|
||||
|
|
@ -1530,6 +1589,8 @@ pub enum ExprKind {
|
|||
Gen(CaptureBy, P<Block>, GenBlockKind, Span),
|
||||
/// An await expression (`my_future.await`). Span is of await keyword.
|
||||
Await(P<Expr>, Span),
|
||||
/// A use expression (`x.use`). Span is of use keyword.
|
||||
Use(P<Expr>, Span),
|
||||
|
||||
/// A try block (`try { ... }`).
|
||||
TryBlock(P<Block>),
|
||||
|
|
@ -1699,8 +1760,17 @@ pub enum CaptureBy {
|
|||
/// The span of the `move` keyword.
|
||||
move_kw: Span,
|
||||
},
|
||||
/// `move` keyword was not specified.
|
||||
/// `move` or `use` keywords were not specified.
|
||||
Ref,
|
||||
/// `use |x| y + x`.
|
||||
///
|
||||
/// Note that if you have a regular closure like `|| x.use`, this will *not* result
|
||||
/// in a `Use` capture. Instead, the `ExprUseVisitor` will look at the type
|
||||
/// of `x` and treat `x.use` as either a copy/clone/move as appropriate.
|
||||
Use {
|
||||
/// The span of the `use` keyword.
|
||||
use_kw: Span,
|
||||
},
|
||||
}
|
||||
|
||||
/// Closure lifetime binder, `for<'a, 'b>` in `for<'a, 'b> |_: &'a (), _: &'b ()|`.
|
||||
|
|
@ -2583,6 +2653,8 @@ pub enum SelfKind {
|
|||
Value(Mutability),
|
||||
/// `&'lt self`, `&'lt mut self`
|
||||
Region(Option<Lifetime>, Mutability),
|
||||
/// `&'lt pin const self`, `&'lt pin mut self`
|
||||
Pinned(Option<Lifetime>, Mutability),
|
||||
/// `self: TYPE`, `mut self: TYPE`
|
||||
Explicit(P<Ty>, Mutability),
|
||||
}
|
||||
|
|
@ -2592,6 +2664,8 @@ impl SelfKind {
|
|||
match self {
|
||||
SelfKind::Region(None, mutbl) => mutbl.ref_prefix_str().to_string(),
|
||||
SelfKind::Region(Some(lt), mutbl) => format!("&{lt} {}", mutbl.prefix_str()),
|
||||
SelfKind::Pinned(None, mutbl) => format!("&pin {}", mutbl.ptr_str()),
|
||||
SelfKind::Pinned(Some(lt), mutbl) => format!("&{lt} pin {}", mutbl.ptr_str()),
|
||||
SelfKind::Value(_) | SelfKind::Explicit(_, _) => {
|
||||
unreachable!("if we had an explicit self, we wouldn't be here")
|
||||
}
|
||||
|
|
@ -2608,11 +2682,13 @@ impl Param {
|
|||
if ident.name == kw::SelfLower {
|
||||
return match self.ty.kind {
|
||||
TyKind::ImplicitSelf => Some(respan(self.pat.span, SelfKind::Value(mutbl))),
|
||||
TyKind::Ref(lt, MutTy { ref ty, mutbl })
|
||||
| TyKind::PinnedRef(lt, MutTy { ref ty, mutbl })
|
||||
TyKind::Ref(lt, MutTy { ref ty, mutbl }) if ty.kind.is_implicit_self() => {
|
||||
Some(respan(self.pat.span, SelfKind::Region(lt, mutbl)))
|
||||
}
|
||||
TyKind::PinnedRef(lt, MutTy { ref ty, mutbl })
|
||||
if ty.kind.is_implicit_self() =>
|
||||
{
|
||||
Some(respan(self.pat.span, SelfKind::Region(lt, mutbl)))
|
||||
Some(respan(self.pat.span, SelfKind::Pinned(lt, mutbl)))
|
||||
}
|
||||
_ => Some(respan(
|
||||
self.pat.span.to(self.ty.span),
|
||||
|
|
@ -2654,6 +2730,15 @@ impl Param {
|
|||
tokens: None,
|
||||
}),
|
||||
),
|
||||
SelfKind::Pinned(lt, mutbl) => (
|
||||
mutbl,
|
||||
P(Ty {
|
||||
id: DUMMY_NODE_ID,
|
||||
kind: TyKind::PinnedRef(lt, MutTy { ty: infer_ty, mutbl }),
|
||||
span,
|
||||
tokens: None,
|
||||
}),
|
||||
),
|
||||
};
|
||||
Param {
|
||||
attrs,
|
||||
|
|
@ -3381,6 +3466,7 @@ pub struct Fn {
|
|||
pub generics: Generics,
|
||||
pub sig: FnSig,
|
||||
pub contract: Option<P<FnContract>>,
|
||||
pub define_opaque: Option<ThinVec<(NodeId, Path)>>,
|
||||
pub body: Option<P<Block>>,
|
||||
}
|
||||
|
||||
|
|
@ -3678,7 +3764,7 @@ mod size_asserts {
|
|||
static_assert_size!(Block, 32);
|
||||
static_assert_size!(Expr, 72);
|
||||
static_assert_size!(ExprKind, 40);
|
||||
static_assert_size!(Fn, 168);
|
||||
static_assert_size!(Fn, 176);
|
||||
static_assert_size!(ForeignItem, 88);
|
||||
static_assert_size!(ForeignItemKind, 16);
|
||||
static_assert_size!(GenericArg, 24);
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ use crate::tokenstream::LazyAttrTokenStream;
|
|||
use crate::{
|
||||
Arm, AssocItem, AttrItem, AttrKind, AttrVec, Attribute, Block, Crate, Expr, ExprField,
|
||||
FieldDef, ForeignItem, GenericParam, Item, NodeId, Param, Pat, PatField, Path, Stmt, StmtKind,
|
||||
Ty, Variant, Visibility,
|
||||
Ty, Variant, Visibility, WherePredicate,
|
||||
};
|
||||
|
||||
/// A utility trait to reduce boilerplate.
|
||||
|
|
@ -79,6 +79,7 @@ impl_has_node_id!(
|
|||
Stmt,
|
||||
Ty,
|
||||
Variant,
|
||||
WherePredicate,
|
||||
);
|
||||
|
||||
impl<T: AstDeref<Target: HasNodeId>> HasNodeId for T {
|
||||
|
|
@ -127,7 +128,16 @@ macro_rules! impl_has_tokens_none {
|
|||
}
|
||||
|
||||
impl_has_tokens!(AssocItem, AttrItem, Block, Expr, ForeignItem, Item, Pat, Path, Ty, Visibility);
|
||||
impl_has_tokens_none!(Arm, ExprField, FieldDef, GenericParam, Param, PatField, Variant);
|
||||
impl_has_tokens_none!(
|
||||
Arm,
|
||||
ExprField,
|
||||
FieldDef,
|
||||
GenericParam,
|
||||
Param,
|
||||
PatField,
|
||||
Variant,
|
||||
WherePredicate
|
||||
);
|
||||
|
||||
impl<T: AstDeref<Target: HasTokens>> HasTokens for T {
|
||||
fn tokens(&self) -> Option<&LazyAttrTokenStream> {
|
||||
|
|
@ -199,23 +209,13 @@ impl HasTokens for Attribute {
|
|||
impl HasTokens for Nonterminal {
|
||||
fn tokens(&self) -> Option<&LazyAttrTokenStream> {
|
||||
match self {
|
||||
Nonterminal::NtItem(item) => item.tokens(),
|
||||
Nonterminal::NtStmt(stmt) => stmt.tokens(),
|
||||
Nonterminal::NtExpr(expr) | Nonterminal::NtLiteral(expr) => expr.tokens(),
|
||||
Nonterminal::NtPat(pat) => pat.tokens(),
|
||||
Nonterminal::NtMeta(attr_item) => attr_item.tokens(),
|
||||
Nonterminal::NtPath(path) => path.tokens(),
|
||||
Nonterminal::NtBlock(block) => block.tokens(),
|
||||
}
|
||||
}
|
||||
fn tokens_mut(&mut self) -> Option<&mut Option<LazyAttrTokenStream>> {
|
||||
match self {
|
||||
Nonterminal::NtItem(item) => item.tokens_mut(),
|
||||
Nonterminal::NtStmt(stmt) => stmt.tokens_mut(),
|
||||
Nonterminal::NtExpr(expr) | Nonterminal::NtLiteral(expr) => expr.tokens_mut(),
|
||||
Nonterminal::NtPat(pat) => pat.tokens_mut(),
|
||||
Nonterminal::NtMeta(attr_item) => attr_item.tokens_mut(),
|
||||
Nonterminal::NtPath(path) => path.tokens_mut(),
|
||||
Nonterminal::NtBlock(block) => block.tokens_mut(),
|
||||
}
|
||||
}
|
||||
|
|
@ -285,6 +285,7 @@ impl_has_attrs!(
|
|||
Param,
|
||||
PatField,
|
||||
Variant,
|
||||
WherePredicate,
|
||||
);
|
||||
impl_has_attrs_none!(Attribute, AttrItem, Block, Pat, Path, Ty, Visibility);
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ use crate::ast::{
|
|||
PathSegment, Safety,
|
||||
};
|
||||
use crate::ptr::P;
|
||||
use crate::token::{self, CommentKind, Delimiter, Token};
|
||||
use crate::token::{self, CommentKind, Delimiter, InvisibleOrigin, MetaVarKind, Token};
|
||||
use crate::tokenstream::{
|
||||
DelimSpan, LazyAttrTokenStream, Spacing, TokenStream, TokenStreamIter, TokenTree,
|
||||
};
|
||||
|
|
@ -405,11 +405,17 @@ impl MetaItem {
|
|||
let span = span.with_hi(segments.last().unwrap().ident.span.hi());
|
||||
Path { span, segments, tokens: None }
|
||||
}
|
||||
Some(TokenTree::Token(Token { kind: token::Interpolated(nt), .. }, _)) => match &**nt {
|
||||
token::Nonterminal::NtMeta(item) => return item.meta(item.path.span),
|
||||
token::Nonterminal::NtPath(path) => (**path).clone(),
|
||||
_ => return None,
|
||||
},
|
||||
Some(TokenTree::Delimited(
|
||||
_span,
|
||||
_spacing,
|
||||
Delimiter::Invisible(InvisibleOrigin::MetaVar(
|
||||
MetaVarKind::Meta { .. } | MetaVarKind::Path,
|
||||
)),
|
||||
_stream,
|
||||
)) => {
|
||||
// This path is currently unreachable in the test suite.
|
||||
unreachable!()
|
||||
}
|
||||
Some(TokenTree::Token(
|
||||
Token { kind: token::OpenDelim(_) | token::CloseDelim(_), .. },
|
||||
_,
|
||||
|
|
|
|||
|
|
@ -266,7 +266,7 @@ pub enum FormatAlignment {
|
|||
#[derive(Clone, Encodable, Decodable, Debug, PartialEq, Eq)]
|
||||
pub enum FormatCount {
|
||||
/// `{:5}` or `{:.5}`
|
||||
Literal(usize),
|
||||
Literal(u16),
|
||||
/// `{:.*}`, `{:.5$}`, or `{:a$}`, etc.
|
||||
Argument(FormatArgPosition),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
// tidy-alphabetical-start
|
||||
#![allow(internal_features)]
|
||||
#![cfg_attr(doc, recursion_limit = "256")] // FIXME(nnethercote): will be removed by #124141
|
||||
#![doc(
|
||||
html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/",
|
||||
test(attr(deny(warnings)))
|
||||
|
|
@ -19,7 +20,6 @@
|
|||
#![feature(never_type)]
|
||||
#![feature(rustdoc_internals)]
|
||||
#![feature(stmt_expr_attributes)]
|
||||
#![warn(unreachable_pub)]
|
||||
// tidy-alphabetical-end
|
||||
|
||||
pub mod util {
|
||||
|
|
|
|||
|
|
@ -338,8 +338,11 @@ pub trait MutVisitor: Sized {
|
|||
walk_where_clause(self, where_clause);
|
||||
}
|
||||
|
||||
fn visit_where_predicate(&mut self, where_predicate: &mut WherePredicate) {
|
||||
walk_where_predicate(self, where_predicate)
|
||||
fn flat_map_where_predicate(
|
||||
&mut self,
|
||||
where_predicate: WherePredicate,
|
||||
) -> SmallVec<[WherePredicate; 1]> {
|
||||
walk_flat_map_where_predicate(self, where_predicate)
|
||||
}
|
||||
|
||||
fn visit_where_predicate_kind(&mut self, kind: &mut WherePredicateKind) {
|
||||
|
|
@ -892,29 +895,9 @@ pub fn visit_token<T: MutVisitor>(vis: &mut T, t: &mut Token) {
|
|||
// multiple items there....
|
||||
fn visit_nonterminal<T: MutVisitor>(vis: &mut T, nt: &mut token::Nonterminal) {
|
||||
match nt {
|
||||
token::NtItem(item) => visit_clobber(item, |item| {
|
||||
// This is probably okay, because the only visitors likely to
|
||||
// peek inside interpolated nodes will be renamings/markings,
|
||||
// which map single items to single items.
|
||||
vis.flat_map_item(item).expect_one("expected visitor to produce exactly one item")
|
||||
}),
|
||||
token::NtBlock(block) => vis.visit_block(block),
|
||||
token::NtStmt(stmt) => visit_clobber(stmt, |stmt| {
|
||||
// See reasoning above.
|
||||
stmt.map(|stmt| {
|
||||
vis.flat_map_stmt(stmt).expect_one("expected visitor to produce exactly one item")
|
||||
})
|
||||
}),
|
||||
token::NtPat(pat) => vis.visit_pat(pat),
|
||||
token::NtExpr(expr) => vis.visit_expr(expr),
|
||||
token::NtLiteral(expr) => vis.visit_expr(expr),
|
||||
token::NtMeta(item) => {
|
||||
let AttrItem { unsafety: _, path, args, tokens } = item.deref_mut();
|
||||
vis.visit_path(path);
|
||||
visit_attr_args(vis, args);
|
||||
visit_lazy_tts(vis, tokens);
|
||||
}
|
||||
token::NtPath(path) => vis.visit_path(path),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -978,7 +961,14 @@ fn walk_fn<T: MutVisitor>(vis: &mut T, kind: FnKind<'_>) {
|
|||
_ctxt,
|
||||
_ident,
|
||||
_vis,
|
||||
Fn { defaultness, generics, contract, body, sig: FnSig { header, decl, span } },
|
||||
Fn {
|
||||
defaultness,
|
||||
generics,
|
||||
contract,
|
||||
body,
|
||||
sig: FnSig { header, decl, span },
|
||||
define_opaque,
|
||||
},
|
||||
) => {
|
||||
// Identifier and visibility are visited as a part of the item.
|
||||
visit_defaultness(vis, defaultness);
|
||||
|
|
@ -992,6 +982,11 @@ fn walk_fn<T: MutVisitor>(vis: &mut T, kind: FnKind<'_>) {
|
|||
vis.visit_block(body);
|
||||
}
|
||||
vis.visit_span(span);
|
||||
|
||||
for (id, path) in define_opaque.iter_mut().flatten() {
|
||||
vis.visit_id(id);
|
||||
vis.visit_path(path)
|
||||
}
|
||||
}
|
||||
FnKind::Closure(binder, coroutine_kind, decl, body) => {
|
||||
vis.visit_closure_binder(binder);
|
||||
|
|
@ -1105,15 +1100,20 @@ fn walk_ty_alias_where_clauses<T: MutVisitor>(vis: &mut T, tawcs: &mut TyAliasWh
|
|||
|
||||
fn walk_where_clause<T: MutVisitor>(vis: &mut T, wc: &mut WhereClause) {
|
||||
let WhereClause { has_where_token: _, predicates, span } = wc;
|
||||
visit_thin_vec(predicates, |predicate| vis.visit_where_predicate(predicate));
|
||||
predicates.flat_map_in_place(|predicate| vis.flat_map_where_predicate(predicate));
|
||||
vis.visit_span(span);
|
||||
}
|
||||
|
||||
pub fn walk_where_predicate<T: MutVisitor>(vis: &mut T, pred: &mut WherePredicate) {
|
||||
let WherePredicate { kind, id, span } = pred;
|
||||
pub fn walk_flat_map_where_predicate<T: MutVisitor>(
|
||||
vis: &mut T,
|
||||
mut pred: WherePredicate,
|
||||
) -> SmallVec<[WherePredicate; 1]> {
|
||||
let WherePredicate { attrs, kind, id, span, is_placeholder: _ } = &mut pred;
|
||||
vis.visit_id(id);
|
||||
visit_attrs(vis, attrs);
|
||||
vis.visit_where_predicate_kind(kind);
|
||||
vis.visit_span(span);
|
||||
smallvec![pred]
|
||||
}
|
||||
|
||||
pub fn walk_where_predicate_kind<T: MutVisitor>(vis: &mut T, kind: &mut WherePredicateKind) {
|
||||
|
|
@ -1745,6 +1745,10 @@ pub fn walk_expr<T: MutVisitor>(vis: &mut T, Expr { kind, id, span, attrs, token
|
|||
vis.visit_expr(expr);
|
||||
vis.visit_span(await_kw_span);
|
||||
}
|
||||
ExprKind::Use(expr, use_kw_span) => {
|
||||
vis.visit_expr(expr);
|
||||
vis.visit_span(use_kw_span);
|
||||
}
|
||||
ExprKind::Assign(el, er, span) => {
|
||||
vis.visit_expr(el);
|
||||
vis.visit_expr(er);
|
||||
|
|
@ -1895,6 +1899,9 @@ fn walk_capture_by<T: MutVisitor>(vis: &mut T, capture_by: &mut CaptureBy) {
|
|||
CaptureBy::Value { move_kw } => {
|
||||
vis.visit_span(move_kw);
|
||||
}
|
||||
CaptureBy::Use { use_kw } => {
|
||||
vis.visit_span(use_kw);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1929,7 +1936,7 @@ impl DummyAstNode for Item {
|
|||
span: Default::default(),
|
||||
tokens: Default::default(),
|
||||
},
|
||||
ident: Ident::empty(),
|
||||
ident: Ident::dummy(),
|
||||
kind: ItemKind::ExternCrate(None),
|
||||
tokens: Default::default(),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ use std::borrow::Cow;
|
|||
use std::fmt;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub use BinOpToken::*;
|
||||
pub use LitKind::*;
|
||||
pub use Nonterminal::*;
|
||||
pub use NtExprKind::*;
|
||||
|
|
@ -26,21 +25,6 @@ pub enum CommentKind {
|
|||
Block,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Encodable, Decodable, Hash, Debug, Copy)]
|
||||
#[derive(HashStable_Generic)]
|
||||
pub enum BinOpToken {
|
||||
Plus,
|
||||
Minus,
|
||||
Star,
|
||||
Slash,
|
||||
Percent,
|
||||
Caret,
|
||||
And,
|
||||
Or,
|
||||
Shl,
|
||||
Shr,
|
||||
}
|
||||
|
||||
// This type must not implement `Hash` due to the unusual `PartialEq` impl below.
|
||||
#[derive(Copy, Clone, Debug, Encodable, Decodable, HashStable_Generic)]
|
||||
pub enum InvisibleOrigin {
|
||||
|
|
@ -90,7 +74,10 @@ pub enum MetaVarKind {
|
|||
Ident,
|
||||
Lifetime,
|
||||
Literal,
|
||||
Meta,
|
||||
Meta {
|
||||
/// Will `AttrItem::meta` succeed on this, if reparsed?
|
||||
has_meta_form: bool,
|
||||
},
|
||||
Path,
|
||||
Vis,
|
||||
TT,
|
||||
|
|
@ -110,7 +97,7 @@ impl fmt::Display for MetaVarKind {
|
|||
MetaVarKind::Ident => sym::ident,
|
||||
MetaVarKind::Lifetime => sym::lifetime,
|
||||
MetaVarKind::Literal => sym::literal,
|
||||
MetaVarKind::Meta => sym::meta,
|
||||
MetaVarKind::Meta { .. } => sym::meta,
|
||||
MetaVarKind::Path => sym::path,
|
||||
MetaVarKind::Vis => sym::vis,
|
||||
MetaVarKind::TT => sym::tt,
|
||||
|
|
@ -373,11 +360,49 @@ pub enum TokenKind {
|
|||
/// `||`
|
||||
OrOr,
|
||||
/// `!`
|
||||
Not,
|
||||
Bang,
|
||||
/// `~`
|
||||
Tilde,
|
||||
BinOp(BinOpToken),
|
||||
BinOpEq(BinOpToken),
|
||||
// `+`
|
||||
Plus,
|
||||
// `-`
|
||||
Minus,
|
||||
// `*`
|
||||
Star,
|
||||
// `/`
|
||||
Slash,
|
||||
// `%`
|
||||
Percent,
|
||||
// `^`
|
||||
Caret,
|
||||
// `&`
|
||||
And,
|
||||
// `|`
|
||||
Or,
|
||||
// `<<`
|
||||
Shl,
|
||||
// `>>`
|
||||
Shr,
|
||||
// `+=`
|
||||
PlusEq,
|
||||
// `-=`
|
||||
MinusEq,
|
||||
// `*=`
|
||||
StarEq,
|
||||
// `/=`
|
||||
SlashEq,
|
||||
// `%=`
|
||||
PercentEq,
|
||||
// `^=`
|
||||
CaretEq,
|
||||
// `&=`
|
||||
AndEq,
|
||||
// `|=`
|
||||
OrEq,
|
||||
// `<<=`
|
||||
ShlEq,
|
||||
// `>>=`
|
||||
ShrEq,
|
||||
|
||||
/* Structural symbols */
|
||||
/// `@`
|
||||
|
|
@ -497,31 +522,31 @@ impl TokenKind {
|
|||
Some(match (self, n) {
|
||||
(Le, 1) => (Lt, Eq),
|
||||
(EqEq, 1) => (Eq, Eq),
|
||||
(Ne, 1) => (Not, Eq),
|
||||
(Ne, 1) => (Bang, Eq),
|
||||
(Ge, 1) => (Gt, Eq),
|
||||
(AndAnd, 1) => (BinOp(And), BinOp(And)),
|
||||
(OrOr, 1) => (BinOp(Or), BinOp(Or)),
|
||||
(BinOp(Shl), 1) => (Lt, Lt),
|
||||
(BinOp(Shr), 1) => (Gt, Gt),
|
||||
(BinOpEq(Plus), 1) => (BinOp(Plus), Eq),
|
||||
(BinOpEq(Minus), 1) => (BinOp(Minus), Eq),
|
||||
(BinOpEq(Star), 1) => (BinOp(Star), Eq),
|
||||
(BinOpEq(Slash), 1) => (BinOp(Slash), Eq),
|
||||
(BinOpEq(Percent), 1) => (BinOp(Percent), Eq),
|
||||
(BinOpEq(Caret), 1) => (BinOp(Caret), Eq),
|
||||
(BinOpEq(And), 1) => (BinOp(And), Eq),
|
||||
(BinOpEq(Or), 1) => (BinOp(Or), Eq),
|
||||
(BinOpEq(Shl), 1) => (Lt, Le), // `<` + `<=`
|
||||
(BinOpEq(Shl), 2) => (BinOp(Shl), Eq), // `<<` + `=`
|
||||
(BinOpEq(Shr), 1) => (Gt, Ge), // `>` + `>=`
|
||||
(BinOpEq(Shr), 2) => (BinOp(Shr), Eq), // `>>` + `=`
|
||||
(AndAnd, 1) => (And, And),
|
||||
(OrOr, 1) => (Or, Or),
|
||||
(Shl, 1) => (Lt, Lt),
|
||||
(Shr, 1) => (Gt, Gt),
|
||||
(PlusEq, 1) => (Plus, Eq),
|
||||
(MinusEq, 1) => (Minus, Eq),
|
||||
(StarEq, 1) => (Star, Eq),
|
||||
(SlashEq, 1) => (Slash, Eq),
|
||||
(PercentEq, 1) => (Percent, Eq),
|
||||
(CaretEq, 1) => (Caret, Eq),
|
||||
(AndEq, 1) => (And, Eq),
|
||||
(OrEq, 1) => (Or, Eq),
|
||||
(ShlEq, 1) => (Lt, Le), // `<` + `<=`
|
||||
(ShlEq, 2) => (Shl, Eq), // `<<` + `=`
|
||||
(ShrEq, 1) => (Gt, Ge), // `>` + `>=`
|
||||
(ShrEq, 2) => (Shr, Eq), // `>>` + `=`
|
||||
(DotDot, 1) => (Dot, Dot),
|
||||
(DotDotDot, 1) => (Dot, DotDot), // `.` + `..`
|
||||
(DotDotDot, 2) => (DotDot, Dot), // `..` + `.`
|
||||
(DotDotEq, 2) => (DotDot, Eq),
|
||||
(PathSep, 1) => (Colon, Colon),
|
||||
(RArrow, 1) => (BinOp(Minus), Gt),
|
||||
(LArrow, 1) => (Lt, BinOp(Minus)),
|
||||
(RArrow, 1) => (Minus, Gt),
|
||||
(LArrow, 1) => (Lt, Minus),
|
||||
(FatArrow, 1) => (Eq, Gt),
|
||||
_ => return None,
|
||||
})
|
||||
|
|
@ -540,7 +565,7 @@ impl TokenKind {
|
|||
}
|
||||
|
||||
pub fn should_end_const_arg(&self) -> bool {
|
||||
matches!(self, Gt | Ge | BinOp(Shr) | BinOpEq(Shr))
|
||||
matches!(self, Gt | Ge | Shr | ShrEq)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -579,11 +604,11 @@ impl Token {
|
|||
|
||||
pub fn is_punct(&self) -> bool {
|
||||
match self.kind {
|
||||
Eq | Lt | Le | EqEq | Ne | Ge | Gt | AndAnd | OrOr | Not | Tilde | BinOp(_)
|
||||
| BinOpEq(_) | At | Dot | DotDot | DotDotDot | DotDotEq | Comma | Semi | Colon
|
||||
| PathSep | RArrow | LArrow | FatArrow | Pound | Dollar | Question | SingleQuote => {
|
||||
true
|
||||
}
|
||||
Eq | Lt | Le | EqEq | Ne | Ge | Gt | AndAnd | OrOr | Bang | Tilde | Plus | Minus
|
||||
| Star | Slash | Percent | Caret | And | Or | Shl | Shr | PlusEq | MinusEq | StarEq
|
||||
| SlashEq | PercentEq | CaretEq | AndEq | OrEq | ShlEq | ShrEq | At | Dot | DotDot
|
||||
| DotDotDot | DotDotEq | Comma | Semi | Colon | PathSep | RArrow | LArrow
|
||||
| FatArrow | Pound | Dollar | Question | SingleQuote => true,
|
||||
|
||||
OpenDelim(..) | CloseDelim(..) | Literal(..) | DocComment(..) | Ident(..)
|
||||
| NtIdent(..) | Lifetime(..) | NtLifetime(..) | Interpolated(..) | Eof => false,
|
||||
|
|
@ -591,7 +616,7 @@ impl Token {
|
|||
}
|
||||
|
||||
pub fn is_like_plus(&self) -> bool {
|
||||
matches!(self.kind, BinOp(Plus) | BinOpEq(Plus))
|
||||
matches!(self.kind, Plus | PlusEq)
|
||||
}
|
||||
|
||||
/// Returns `true` if the token can appear at the start of an expression.
|
||||
|
|
@ -605,15 +630,15 @@ impl Token {
|
|||
ident_can_begin_expr(name, self.span, is_raw), // value name or keyword
|
||||
OpenDelim(Parenthesis | Brace | Bracket) | // tuple, array or block
|
||||
Literal(..) | // literal
|
||||
Not | // operator not
|
||||
BinOp(Minus) | // unary minus
|
||||
BinOp(Star) | // dereference
|
||||
BinOp(Or) | OrOr | // closure
|
||||
BinOp(And) | // reference
|
||||
Bang | // operator not
|
||||
Minus | // unary minus
|
||||
Star | // dereference
|
||||
Or | OrOr | // closure
|
||||
And | // reference
|
||||
AndAnd | // double reference
|
||||
// DotDotDot is no longer supported, but we need some way to display the error
|
||||
DotDot | DotDotDot | DotDotEq | // range notation
|
||||
Lt | BinOp(Shl) | // associated path
|
||||
Lt | Shl | // associated path
|
||||
PathSep | // global path
|
||||
Lifetime(..) | // labeled loop
|
||||
Pound => true, // expression attributes
|
||||
|
|
@ -621,8 +646,7 @@ impl Token {
|
|||
matches!(&**nt,
|
||||
NtBlock(..) |
|
||||
NtExpr(..) |
|
||||
NtLiteral(..) |
|
||||
NtPath(..)
|
||||
NtLiteral(..)
|
||||
),
|
||||
OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(
|
||||
MetaVarKind::Block |
|
||||
|
|
@ -643,29 +667,25 @@ impl Token {
|
|||
Ident(..) | NtIdent(..) |
|
||||
OpenDelim(Delimiter::Parenthesis) | // tuple pattern
|
||||
OpenDelim(Delimiter::Bracket) | // slice pattern
|
||||
BinOp(And) | // reference
|
||||
BinOp(Minus) | // negative literal
|
||||
AndAnd | // double reference
|
||||
Literal(_) | // literal
|
||||
DotDot | // range pattern (future compat)
|
||||
DotDotDot | // range pattern (future compat)
|
||||
PathSep | // path
|
||||
Lt | // path (UFCS constant)
|
||||
BinOp(Shl) => true, // path (double UFCS)
|
||||
// leading vert `|` or-pattern
|
||||
BinOp(Or) => matches!(pat_kind, PatWithOr),
|
||||
And | // reference
|
||||
Minus | // negative literal
|
||||
AndAnd | // double reference
|
||||
Literal(_) | // literal
|
||||
DotDot | // range pattern (future compat)
|
||||
DotDotDot | // range pattern (future compat)
|
||||
PathSep | // path
|
||||
Lt | // path (UFCS constant)
|
||||
Shl => true, // path (double UFCS)
|
||||
Or => matches!(pat_kind, PatWithOr), // leading vert `|` or-pattern
|
||||
Interpolated(nt) =>
|
||||
matches!(&**nt,
|
||||
| NtExpr(..)
|
||||
| NtLiteral(..)
|
||||
| NtMeta(..)
|
||||
| NtPat(..)
|
||||
| NtPath(..)
|
||||
),
|
||||
OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(
|
||||
MetaVarKind::Expr { .. } |
|
||||
MetaVarKind::Literal |
|
||||
MetaVarKind::Meta |
|
||||
MetaVarKind::Meta { .. } |
|
||||
MetaVarKind::Pat(_) |
|
||||
MetaVarKind::Path |
|
||||
MetaVarKind::Ty { .. }
|
||||
|
|
@ -677,19 +697,18 @@ impl Token {
|
|||
/// Returns `true` if the token can appear at the start of a type.
|
||||
pub fn can_begin_type(&self) -> bool {
|
||||
match self.uninterpolate().kind {
|
||||
Ident(name, is_raw) =>
|
||||
Ident(name, is_raw) =>
|
||||
ident_can_begin_type(name, self.span, is_raw), // type name or keyword
|
||||
OpenDelim(Delimiter::Parenthesis) | // tuple
|
||||
OpenDelim(Delimiter::Bracket) | // array
|
||||
Not | // never
|
||||
BinOp(Star) | // raw pointer
|
||||
BinOp(And) | // reference
|
||||
AndAnd | // double reference
|
||||
Question | // maybe bound in trait object
|
||||
Lifetime(..) | // lifetime bound in trait object
|
||||
Lt | BinOp(Shl) | // associated path
|
||||
PathSep => true, // global path
|
||||
Interpolated(ref nt) => matches!(&**nt, NtPath(..)),
|
||||
Bang | // never
|
||||
Star | // raw pointer
|
||||
And | // reference
|
||||
AndAnd | // double reference
|
||||
Question | // maybe bound in trait object
|
||||
Lifetime(..) | // lifetime bound in trait object
|
||||
Lt | Shl | // associated path
|
||||
PathSep => true, // global path
|
||||
OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(
|
||||
MetaVarKind::Ty { .. } |
|
||||
MetaVarKind::Path
|
||||
|
|
@ -703,7 +722,7 @@ impl Token {
|
|||
/// Returns `true` if the token can appear at the start of a const param.
|
||||
pub fn can_begin_const_arg(&self) -> bool {
|
||||
match self.kind {
|
||||
OpenDelim(Delimiter::Brace) | Literal(..) | BinOp(Minus) => true,
|
||||
OpenDelim(Delimiter::Brace) | Literal(..) | Minus => true,
|
||||
Ident(name, IdentIsRaw::No) if name.is_bool_lit() => true,
|
||||
Interpolated(ref nt) => matches!(&**nt, NtExpr(..) | NtBlock(..) | NtLiteral(..)),
|
||||
OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(
|
||||
|
|
@ -752,7 +771,7 @@ impl Token {
|
|||
/// Keep this in sync with and `Lit::from_token`, excluding unary negation.
|
||||
pub fn can_begin_literal_maybe_minus(&self) -> bool {
|
||||
match self.uninterpolate().kind {
|
||||
Literal(..) | BinOp(Minus) => true,
|
||||
Literal(..) | Minus => true,
|
||||
Ident(name, IdentIsRaw::No) if name.is_bool_lit() => true,
|
||||
Interpolated(ref nt) => match &**nt {
|
||||
NtLiteral(_) => true,
|
||||
|
|
@ -848,27 +867,17 @@ impl Token {
|
|||
self.ident().is_some_and(|(ident, _)| ident.name == name)
|
||||
}
|
||||
|
||||
/// Returns `true` if the token is an interpolated path.
|
||||
fn is_whole_path(&self) -> bool {
|
||||
if let Interpolated(nt) = &self.kind
|
||||
&& let NtPath(..) = &**nt
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
/// Is this a pre-parsed expression dropped into the token stream
|
||||
/// (which happens while parsing the result of macro expansion)?
|
||||
pub fn is_whole_expr(&self) -> bool {
|
||||
#[allow(irrefutable_let_patterns)] // FIXME: temporary
|
||||
if let Interpolated(nt) = &self.kind
|
||||
&& let NtExpr(_) | NtLiteral(_) | NtPath(_) | NtBlock(_) = &**nt
|
||||
&& let NtExpr(_) | NtLiteral(_) | NtBlock(_) = &**nt
|
||||
{
|
||||
return true;
|
||||
true
|
||||
} else {
|
||||
matches!(self.is_metavar_seq(), Some(MetaVarKind::Path))
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
/// Is the token an interpolated block (`$b:block`)?
|
||||
|
|
@ -888,13 +897,13 @@ impl Token {
|
|||
}
|
||||
|
||||
pub fn is_qpath_start(&self) -> bool {
|
||||
self == &Lt || self == &BinOp(Shl)
|
||||
self == &Lt || self == &Shl
|
||||
}
|
||||
|
||||
pub fn is_path_start(&self) -> bool {
|
||||
self == &PathSep
|
||||
|| self.is_qpath_start()
|
||||
|| self.is_whole_path()
|
||||
|| matches!(self.is_metavar_seq(), Some(MetaVarKind::Path))
|
||||
|| self.is_path_segment_keyword()
|
||||
|| self.is_ident() && !self.is_reserved_ident()
|
||||
}
|
||||
|
|
@ -980,59 +989,82 @@ impl Token {
|
|||
}
|
||||
|
||||
pub fn glue(&self, joint: &Token) -> Option<Token> {
|
||||
let kind = match self.kind {
|
||||
Eq => match joint.kind {
|
||||
Eq => EqEq,
|
||||
Gt => FatArrow,
|
||||
_ => return None,
|
||||
},
|
||||
Lt => match joint.kind {
|
||||
Eq => Le,
|
||||
Lt => BinOp(Shl),
|
||||
Le => BinOpEq(Shl),
|
||||
BinOp(Minus) => LArrow,
|
||||
_ => return None,
|
||||
},
|
||||
Gt => match joint.kind {
|
||||
Eq => Ge,
|
||||
Gt => BinOp(Shr),
|
||||
Ge => BinOpEq(Shr),
|
||||
_ => return None,
|
||||
},
|
||||
Not => match joint.kind {
|
||||
Eq => Ne,
|
||||
_ => return None,
|
||||
},
|
||||
BinOp(op) => match joint.kind {
|
||||
Eq => BinOpEq(op),
|
||||
BinOp(And) if op == And => AndAnd,
|
||||
BinOp(Or) if op == Or => OrOr,
|
||||
Gt if op == Minus => RArrow,
|
||||
_ => return None,
|
||||
},
|
||||
Dot => match joint.kind {
|
||||
Dot => DotDot,
|
||||
DotDot => DotDotDot,
|
||||
_ => return None,
|
||||
},
|
||||
DotDot => match joint.kind {
|
||||
Dot => DotDotDot,
|
||||
Eq => DotDotEq,
|
||||
_ => return None,
|
||||
},
|
||||
Colon => match joint.kind {
|
||||
Colon => PathSep,
|
||||
_ => return None,
|
||||
},
|
||||
SingleQuote => match joint.kind {
|
||||
Ident(name, is_raw) => Lifetime(Symbol::intern(&format!("'{name}")), is_raw),
|
||||
_ => return None,
|
||||
},
|
||||
let kind = match (&self.kind, &joint.kind) {
|
||||
(Eq, Eq) => EqEq,
|
||||
(Eq, Gt) => FatArrow,
|
||||
(Eq, _) => return None,
|
||||
|
||||
Le | EqEq | Ne | Ge | AndAnd | OrOr | Tilde | BinOpEq(..) | At | DotDotDot
|
||||
| DotDotEq | Comma | Semi | PathSep | RArrow | LArrow | FatArrow | Pound | Dollar
|
||||
| Question | OpenDelim(..) | CloseDelim(..) | Literal(..) | Ident(..) | NtIdent(..)
|
||||
| Lifetime(..) | NtLifetime(..) | Interpolated(..) | DocComment(..) | Eof => {
|
||||
(Lt, Eq) => Le,
|
||||
(Lt, Lt) => Shl,
|
||||
(Lt, Le) => ShlEq,
|
||||
(Lt, Minus) => LArrow,
|
||||
(Lt, _) => return None,
|
||||
|
||||
(Gt, Eq) => Ge,
|
||||
(Gt, Gt) => Shr,
|
||||
(Gt, Ge) => ShrEq,
|
||||
(Gt, _) => return None,
|
||||
|
||||
(Bang, Eq) => Ne,
|
||||
(Bang, _) => return None,
|
||||
|
||||
(Plus, Eq) => PlusEq,
|
||||
(Plus, _) => return None,
|
||||
|
||||
(Minus, Eq) => MinusEq,
|
||||
(Minus, Gt) => RArrow,
|
||||
(Minus, _) => return None,
|
||||
|
||||
(Star, Eq) => StarEq,
|
||||
(Star, _) => return None,
|
||||
|
||||
(Slash, Eq) => SlashEq,
|
||||
(Slash, _) => return None,
|
||||
|
||||
(Percent, Eq) => PercentEq,
|
||||
(Percent, _) => return None,
|
||||
|
||||
(Caret, Eq) => CaretEq,
|
||||
(Caret, _) => return None,
|
||||
|
||||
(And, Eq) => AndEq,
|
||||
(And, And) => AndAnd,
|
||||
(And, _) => return None,
|
||||
|
||||
(Or, Eq) => OrEq,
|
||||
(Or, Or) => OrOr,
|
||||
(Or, _) => return None,
|
||||
|
||||
(Shl, Eq) => ShlEq,
|
||||
(Shl, _) => return None,
|
||||
|
||||
(Shr, Eq) => ShrEq,
|
||||
(Shr, _) => return None,
|
||||
|
||||
(Dot, Dot) => DotDot,
|
||||
(Dot, DotDot) => DotDotDot,
|
||||
(Dot, _) => return None,
|
||||
|
||||
(DotDot, Dot) => DotDotDot,
|
||||
(DotDot, Eq) => DotDotEq,
|
||||
(DotDot, _) => return None,
|
||||
|
||||
(Colon, Colon) => PathSep,
|
||||
(Colon, _) => return None,
|
||||
|
||||
(SingleQuote, Ident(name, is_raw)) => {
|
||||
Lifetime(Symbol::intern(&format!("'{name}")), *is_raw)
|
||||
}
|
||||
(SingleQuote, _) => return None,
|
||||
|
||||
(
|
||||
Le | EqEq | Ne | Ge | AndAnd | OrOr | Tilde | PlusEq | MinusEq | StarEq | SlashEq
|
||||
| PercentEq | CaretEq | AndEq | OrEq | ShlEq | ShrEq | At | DotDotDot | DotDotEq
|
||||
| Comma | Semi | PathSep | RArrow | LArrow | FatArrow | Pound | Dollar | Question
|
||||
| OpenDelim(..) | CloseDelim(..) | Literal(..) | Ident(..) | NtIdent(..)
|
||||
| Lifetime(..) | NtLifetime(..) | Interpolated(..) | DocComment(..) | Eof,
|
||||
_,
|
||||
) => {
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
|
@ -1072,15 +1104,9 @@ pub enum NtExprKind {
|
|||
#[derive(Clone, Encodable, Decodable)]
|
||||
/// For interpolation during macro expansion.
|
||||
pub enum Nonterminal {
|
||||
NtItem(P<ast::Item>),
|
||||
NtBlock(P<ast::Block>),
|
||||
NtStmt(P<ast::Stmt>),
|
||||
NtPat(P<ast::Pat>),
|
||||
NtExpr(P<ast::Expr>),
|
||||
NtLiteral(P<ast::Expr>),
|
||||
/// Stuff inside brackets for attributes
|
||||
NtMeta(P<ast::AttrItem>),
|
||||
NtPath(P<ast::Path>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Encodable, Decodable, Hash, HashStable_Generic)]
|
||||
|
|
@ -1169,26 +1195,16 @@ impl fmt::Display for NonterminalKind {
|
|||
impl Nonterminal {
|
||||
pub fn use_span(&self) -> Span {
|
||||
match self {
|
||||
NtItem(item) => item.span,
|
||||
NtBlock(block) => block.span,
|
||||
NtStmt(stmt) => stmt.span,
|
||||
NtPat(pat) => pat.span,
|
||||
NtExpr(expr) | NtLiteral(expr) => expr.span,
|
||||
NtMeta(attr_item) => attr_item.span(),
|
||||
NtPath(path) => path.span,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn descr(&self) -> &'static str {
|
||||
match self {
|
||||
NtItem(..) => "item",
|
||||
NtBlock(..) => "block",
|
||||
NtStmt(..) => "statement",
|
||||
NtPat(..) => "pattern",
|
||||
NtExpr(..) => "expression",
|
||||
NtLiteral(..) => "literal",
|
||||
NtMeta(..) => "attribute",
|
||||
NtPath(..) => "path",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1206,14 +1222,9 @@ impl PartialEq for Nonterminal {
|
|||
impl fmt::Debug for Nonterminal {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match *self {
|
||||
NtItem(..) => f.pad("NtItem(..)"),
|
||||
NtBlock(..) => f.pad("NtBlock(..)"),
|
||||
NtStmt(..) => f.pad("NtStmt(..)"),
|
||||
NtPat(..) => f.pad("NtPat(..)"),
|
||||
NtExpr(..) => f.pad("NtExpr(..)"),
|
||||
NtLiteral(..) => f.pad("NtLiteral(..)"),
|
||||
NtMeta(..) => f.pad("NtMeta(..)"),
|
||||
NtPath(..) => f.pad("NtPath(..)"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ use rustc_macros::{Decodable, Encodable, HashStable_Generic};
|
|||
use rustc_serialize::{Decodable, Encodable};
|
||||
use rustc_span::{DUMMY_SP, Span, SpanDecoder, SpanEncoder, Symbol, sym};
|
||||
|
||||
use crate::ast::{AttrStyle, StmtKind};
|
||||
use crate::ast::AttrStyle;
|
||||
use crate::ast_traits::{HasAttrs, HasTokens};
|
||||
use crate::token::{self, Delimiter, InvisibleOrigin, Nonterminal, Token, TokenKind};
|
||||
use crate::{AttrVec, Attribute};
|
||||
|
|
@ -461,16 +461,7 @@ impl TokenStream {
|
|||
|
||||
pub fn from_nonterminal_ast(nt: &Nonterminal) -> TokenStream {
|
||||
match nt {
|
||||
Nonterminal::NtItem(item) => TokenStream::from_ast(item),
|
||||
Nonterminal::NtBlock(block) => TokenStream::from_ast(block),
|
||||
Nonterminal::NtStmt(stmt) if let StmtKind::Empty = stmt.kind => {
|
||||
// FIXME: Properly collect tokens for empty statements.
|
||||
TokenStream::token_alone(token::Semi, stmt.span)
|
||||
}
|
||||
Nonterminal::NtStmt(stmt) => TokenStream::from_ast(stmt),
|
||||
Nonterminal::NtPat(pat) => TokenStream::from_ast(pat),
|
||||
Nonterminal::NtMeta(attr) => TokenStream::from_ast(attr),
|
||||
Nonterminal::NtPath(path) => TokenStream::from_ast(path),
|
||||
Nonterminal::NtExpr(expr) | Nonterminal::NtLiteral(expr) => TokenStream::from_ast(expr),
|
||||
}
|
||||
}
|
||||
|
|
@ -654,7 +645,7 @@ impl TokenStream {
|
|||
if attr_style == AttrStyle::Inner {
|
||||
vec![
|
||||
TokenTree::token_joint(token::Pound, span),
|
||||
TokenTree::token_joint_hidden(token::Not, span),
|
||||
TokenTree::token_joint_hidden(token::Bang, span),
|
||||
body,
|
||||
]
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -108,6 +108,7 @@ pub fn leading_labeled_expr(mut expr: &ast::Expr) -> bool {
|
|||
Assign(e, _, _)
|
||||
| AssignOp(_, e, _)
|
||||
| Await(e, _)
|
||||
| Use(e, _)
|
||||
| Binary(_, e, _)
|
||||
| Call(e, _)
|
||||
| Cast(e, _)
|
||||
|
|
@ -224,6 +225,7 @@ pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option<TrailingBrace<'_>> {
|
|||
| Lit(_)
|
||||
| Type(_, _)
|
||||
| Await(_, _)
|
||||
| Use(_, _)
|
||||
| Field(_, _)
|
||||
| Index(_, _, _)
|
||||
| Underscore
|
||||
|
|
|
|||
|
|
@ -1,59 +1,21 @@
|
|||
use rustc_span::kw;
|
||||
|
||||
use crate::ast::{self, BinOpKind};
|
||||
use crate::token::{self, BinOpToken, Token};
|
||||
use crate::ast::{self, BinOpKind, RangeLimits};
|
||||
use crate::token::{self, Token};
|
||||
|
||||
/// Associative operator with precedence.
|
||||
///
|
||||
/// This is the enum which specifies operator precedence and fixity to the parser.
|
||||
/// Associative operator.
|
||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||
pub enum AssocOp {
|
||||
/// `+`
|
||||
Add,
|
||||
/// `-`
|
||||
Subtract,
|
||||
/// `*`
|
||||
Multiply,
|
||||
/// `/`
|
||||
Divide,
|
||||
/// `%`
|
||||
Modulus,
|
||||
/// `&&`
|
||||
LAnd,
|
||||
/// `||`
|
||||
LOr,
|
||||
/// `^`
|
||||
BitXor,
|
||||
/// `&`
|
||||
BitAnd,
|
||||
/// `|`
|
||||
BitOr,
|
||||
/// `<<`
|
||||
ShiftLeft,
|
||||
/// `>>`
|
||||
ShiftRight,
|
||||
/// `==`
|
||||
Equal,
|
||||
/// `<`
|
||||
Less,
|
||||
/// `<=`
|
||||
LessEqual,
|
||||
/// `!=`
|
||||
NotEqual,
|
||||
/// `>`
|
||||
Greater,
|
||||
/// `>=`
|
||||
GreaterEqual,
|
||||
/// A binary op.
|
||||
Binary(BinOpKind),
|
||||
/// `?=` where ? is one of the assignable BinOps
|
||||
AssignOp(BinOpKind),
|
||||
/// `=`
|
||||
Assign,
|
||||
/// `?=` where ? is one of the BinOpToken
|
||||
AssignOp(BinOpToken),
|
||||
/// `as`
|
||||
As,
|
||||
/// `..` range
|
||||
DotDot,
|
||||
/// `..=` range
|
||||
DotDotEq,
|
||||
Cast,
|
||||
/// `..` or `..=` range
|
||||
Range(RangeLimits),
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
|
|
@ -67,81 +29,56 @@ pub enum Fixity {
|
|||
}
|
||||
|
||||
impl AssocOp {
|
||||
/// Creates a new AssocOP from a token
|
||||
/// Creates a new AssocOp from a token.
|
||||
pub fn from_token(t: &Token) -> Option<AssocOp> {
|
||||
use AssocOp::*;
|
||||
match t.kind {
|
||||
token::BinOpEq(k) => Some(AssignOp(k)),
|
||||
token::Eq => Some(Assign),
|
||||
token::BinOp(BinOpToken::Star) => Some(Multiply),
|
||||
token::BinOp(BinOpToken::Slash) => Some(Divide),
|
||||
token::BinOp(BinOpToken::Percent) => Some(Modulus),
|
||||
token::BinOp(BinOpToken::Plus) => Some(Add),
|
||||
token::BinOp(BinOpToken::Minus) => Some(Subtract),
|
||||
token::BinOp(BinOpToken::Shl) => Some(ShiftLeft),
|
||||
token::BinOp(BinOpToken::Shr) => Some(ShiftRight),
|
||||
token::BinOp(BinOpToken::And) => Some(BitAnd),
|
||||
token::BinOp(BinOpToken::Caret) => Some(BitXor),
|
||||
token::BinOp(BinOpToken::Or) => Some(BitOr),
|
||||
token::Lt => Some(Less),
|
||||
token::Le => Some(LessEqual),
|
||||
token::Ge => Some(GreaterEqual),
|
||||
token::Gt => Some(Greater),
|
||||
token::EqEq => Some(Equal),
|
||||
token::Ne => Some(NotEqual),
|
||||
token::AndAnd => Some(LAnd),
|
||||
token::OrOr => Some(LOr),
|
||||
token::DotDot => Some(DotDot),
|
||||
token::DotDotEq => Some(DotDotEq),
|
||||
token::Plus => Some(Binary(BinOpKind::Add)),
|
||||
token::Minus => Some(Binary(BinOpKind::Sub)),
|
||||
token::Star => Some(Binary(BinOpKind::Mul)),
|
||||
token::Slash => Some(Binary(BinOpKind::Div)),
|
||||
token::Percent => Some(Binary(BinOpKind::Rem)),
|
||||
token::Caret => Some(Binary(BinOpKind::BitXor)),
|
||||
token::And => Some(Binary(BinOpKind::BitAnd)),
|
||||
token::Or => Some(Binary(BinOpKind::BitOr)),
|
||||
token::Shl => Some(Binary(BinOpKind::Shl)),
|
||||
token::Shr => Some(Binary(BinOpKind::Shr)),
|
||||
token::PlusEq => Some(AssignOp(BinOpKind::Add)),
|
||||
token::MinusEq => Some(AssignOp(BinOpKind::Sub)),
|
||||
token::StarEq => Some(AssignOp(BinOpKind::Mul)),
|
||||
token::SlashEq => Some(AssignOp(BinOpKind::Div)),
|
||||
token::PercentEq => Some(AssignOp(BinOpKind::Rem)),
|
||||
token::CaretEq => Some(AssignOp(BinOpKind::BitXor)),
|
||||
token::AndEq => Some(AssignOp(BinOpKind::BitAnd)),
|
||||
token::OrEq => Some(AssignOp(BinOpKind::BitOr)),
|
||||
token::ShlEq => Some(AssignOp(BinOpKind::Shl)),
|
||||
token::ShrEq => Some(AssignOp(BinOpKind::Shr)),
|
||||
token::Lt => Some(Binary(BinOpKind::Lt)),
|
||||
token::Le => Some(Binary(BinOpKind::Le)),
|
||||
token::Ge => Some(Binary(BinOpKind::Ge)),
|
||||
token::Gt => Some(Binary(BinOpKind::Gt)),
|
||||
token::EqEq => Some(Binary(BinOpKind::Eq)),
|
||||
token::Ne => Some(Binary(BinOpKind::Ne)),
|
||||
token::AndAnd => Some(Binary(BinOpKind::And)),
|
||||
token::OrOr => Some(Binary(BinOpKind::Or)),
|
||||
token::DotDot => Some(Range(RangeLimits::HalfOpen)),
|
||||
// DotDotDot is no longer supported, but we need some way to display the error
|
||||
token::DotDotDot => Some(DotDotEq),
|
||||
token::DotDotEq | token::DotDotDot => Some(Range(RangeLimits::Closed)),
|
||||
// `<-` should probably be `< -`
|
||||
token::LArrow => Some(Less),
|
||||
_ if t.is_keyword(kw::As) => Some(As),
|
||||
token::LArrow => Some(Binary(BinOpKind::Lt)),
|
||||
_ if t.is_keyword(kw::As) => Some(Cast),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new AssocOp from ast::BinOpKind.
|
||||
pub fn from_ast_binop(op: BinOpKind) -> Self {
|
||||
use AssocOp::*;
|
||||
match op {
|
||||
BinOpKind::Lt => Less,
|
||||
BinOpKind::Gt => Greater,
|
||||
BinOpKind::Le => LessEqual,
|
||||
BinOpKind::Ge => GreaterEqual,
|
||||
BinOpKind::Eq => Equal,
|
||||
BinOpKind::Ne => NotEqual,
|
||||
BinOpKind::Mul => Multiply,
|
||||
BinOpKind::Div => Divide,
|
||||
BinOpKind::Rem => Modulus,
|
||||
BinOpKind::Add => Add,
|
||||
BinOpKind::Sub => Subtract,
|
||||
BinOpKind::Shl => ShiftLeft,
|
||||
BinOpKind::Shr => ShiftRight,
|
||||
BinOpKind::BitAnd => BitAnd,
|
||||
BinOpKind::BitXor => BitXor,
|
||||
BinOpKind::BitOr => BitOr,
|
||||
BinOpKind::And => LAnd,
|
||||
BinOpKind::Or => LOr,
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the precedence of this operator
|
||||
pub fn precedence(&self) -> ExprPrecedence {
|
||||
use AssocOp::*;
|
||||
match *self {
|
||||
As => ExprPrecedence::Cast,
|
||||
Multiply | Divide | Modulus => ExprPrecedence::Product,
|
||||
Add | Subtract => ExprPrecedence::Sum,
|
||||
ShiftLeft | ShiftRight => ExprPrecedence::Shift,
|
||||
BitAnd => ExprPrecedence::BitAnd,
|
||||
BitXor => ExprPrecedence::BitXor,
|
||||
BitOr => ExprPrecedence::BitOr,
|
||||
Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual => ExprPrecedence::Compare,
|
||||
LAnd => ExprPrecedence::LAnd,
|
||||
LOr => ExprPrecedence::LOr,
|
||||
DotDot | DotDotEq => ExprPrecedence::Range,
|
||||
Cast => ExprPrecedence::Cast,
|
||||
Binary(bin_op) => bin_op.precedence(),
|
||||
Range(_) => ExprPrecedence::Range,
|
||||
Assign | AssignOp(_) => ExprPrecedence::Assign,
|
||||
}
|
||||
}
|
||||
|
|
@ -152,22 +89,17 @@ impl AssocOp {
|
|||
// NOTE: it is a bug to have an operators that has same precedence but different fixities!
|
||||
match *self {
|
||||
Assign | AssignOp(_) => Fixity::Right,
|
||||
As | Multiply | Divide | Modulus | Add | Subtract | ShiftLeft | ShiftRight | BitAnd
|
||||
| BitXor | BitOr | LAnd | LOr => Fixity::Left,
|
||||
Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual | DotDot | DotDotEq => {
|
||||
Fixity::None
|
||||
}
|
||||
Binary(binop) => binop.fixity(),
|
||||
Cast => Fixity::Left,
|
||||
Range(_) => Fixity::None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_comparison(&self) -> bool {
|
||||
use AssocOp::*;
|
||||
match *self {
|
||||
Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual => true,
|
||||
Assign | AssignOp(_) | As | Multiply | Divide | Modulus | Add | Subtract
|
||||
| ShiftLeft | ShiftRight | BitAnd | BitXor | BitOr | LAnd | LOr | DotDot | DotDotEq => {
|
||||
false
|
||||
}
|
||||
Binary(binop) => binop.is_comparison(),
|
||||
Assign | AssignOp(_) | Cast | Range(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -175,34 +107,7 @@ impl AssocOp {
|
|||
use AssocOp::*;
|
||||
match *self {
|
||||
Assign | AssignOp(_) => true,
|
||||
Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual | As | Multiply
|
||||
| Divide | Modulus | Add | Subtract | ShiftLeft | ShiftRight | BitAnd | BitXor
|
||||
| BitOr | LAnd | LOr | DotDot | DotDotEq => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_ast_binop(&self) -> Option<BinOpKind> {
|
||||
use AssocOp::*;
|
||||
match *self {
|
||||
Less => Some(BinOpKind::Lt),
|
||||
Greater => Some(BinOpKind::Gt),
|
||||
LessEqual => Some(BinOpKind::Le),
|
||||
GreaterEqual => Some(BinOpKind::Ge),
|
||||
Equal => Some(BinOpKind::Eq),
|
||||
NotEqual => Some(BinOpKind::Ne),
|
||||
Multiply => Some(BinOpKind::Mul),
|
||||
Divide => Some(BinOpKind::Div),
|
||||
Modulus => Some(BinOpKind::Rem),
|
||||
Add => Some(BinOpKind::Add),
|
||||
Subtract => Some(BinOpKind::Sub),
|
||||
ShiftLeft => Some(BinOpKind::Shl),
|
||||
ShiftRight => Some(BinOpKind::Shr),
|
||||
BitAnd => Some(BinOpKind::BitAnd),
|
||||
BitXor => Some(BinOpKind::BitXor),
|
||||
BitOr => Some(BinOpKind::BitOr),
|
||||
LAnd => Some(BinOpKind::And),
|
||||
LOr => Some(BinOpKind::Or),
|
||||
Assign | AssignOp(_) | As | DotDot | DotDotEq => None,
|
||||
Cast | Binary(_) | Range(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -212,20 +117,23 @@ impl AssocOp {
|
|||
/// parentheses while having a high degree of confidence on the correctness of the suggestion.
|
||||
pub fn can_continue_expr_unambiguously(&self) -> bool {
|
||||
use AssocOp::*;
|
||||
use BinOpKind::*;
|
||||
matches!(
|
||||
self,
|
||||
BitXor | // `{ 42 } ^ 3`
|
||||
Assign | // `{ 42 } = { 42 }`
|
||||
Divide | // `{ 42 } / 42`
|
||||
Modulus | // `{ 42 } % 2`
|
||||
ShiftRight | // `{ 42 } >> 2`
|
||||
LessEqual | // `{ 42 } <= 3`
|
||||
Greater | // `{ 42 } > 3`
|
||||
GreaterEqual | // `{ 42 } >= 3`
|
||||
Binary(
|
||||
BitXor | // `{ 42 } ^ 3`
|
||||
Div | // `{ 42 } / 42`
|
||||
Rem | // `{ 42 } % 2`
|
||||
Shr | // `{ 42 } >> 2`
|
||||
Le | // `{ 42 } <= 3`
|
||||
Gt | // `{ 42 } > 3`
|
||||
Ge // `{ 42 } >= 3`
|
||||
) |
|
||||
AssignOp(_) | // `{ 42 } +=`
|
||||
// Equal | // `{ 42 } == { 42 }` Accepting these here would regress incorrect
|
||||
// NotEqual | // `{ 42 } != { 42 } struct literals parser recovery.
|
||||
As // `{ 42 } as usize`
|
||||
Cast // `{ 42 } as usize`
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -833,7 +833,8 @@ pub fn walk_where_predicate<'a, V: Visitor<'a>>(
|
|||
visitor: &mut V,
|
||||
predicate: &'a WherePredicate,
|
||||
) -> V::Result {
|
||||
let WherePredicate { kind, id: _, span: _ } = predicate;
|
||||
let WherePredicate { attrs, kind, id: _, span: _, is_placeholder: _ } = predicate;
|
||||
walk_list!(visitor, visit_attribute, attrs);
|
||||
visitor.visit_where_predicate_kind(kind)
|
||||
}
|
||||
|
||||
|
|
@ -891,7 +892,14 @@ pub fn walk_fn<'a, V: Visitor<'a>>(visitor: &mut V, kind: FnKind<'a>) -> V::Resu
|
|||
_ctxt,
|
||||
_ident,
|
||||
_vis,
|
||||
Fn { defaultness: _, sig: FnSig { header, decl, span: _ }, generics, contract, body },
|
||||
Fn {
|
||||
defaultness: _,
|
||||
sig: FnSig { header, decl, span: _ },
|
||||
generics,
|
||||
contract,
|
||||
body,
|
||||
define_opaque,
|
||||
},
|
||||
) => {
|
||||
// Identifier and visibility are visited as a part of the item.
|
||||
try_visit!(visitor.visit_fn_header(header));
|
||||
|
|
@ -899,6 +907,9 @@ pub fn walk_fn<'a, V: Visitor<'a>>(visitor: &mut V, kind: FnKind<'a>) -> V::Resu
|
|||
try_visit!(visitor.visit_fn_decl(decl));
|
||||
visit_opt!(visitor, visit_contract, contract);
|
||||
visit_opt!(visitor, visit_block, body);
|
||||
for (id, path) in define_opaque.iter().flatten() {
|
||||
try_visit!(visitor.visit_path(path, *id))
|
||||
}
|
||||
}
|
||||
FnKind::Closure(binder, coroutine_kind, decl, body) => {
|
||||
try_visit!(visitor.visit_closure_binder(binder));
|
||||
|
|
@ -1202,7 +1213,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) -> V
|
|||
FnKind::Closure(binder, coroutine_kind, fn_decl, body),
|
||||
*span,
|
||||
*id
|
||||
))
|
||||
));
|
||||
}
|
||||
ExprKind::Block(block, opt_label) => {
|
||||
visit_opt!(visitor, visit_label, opt_label);
|
||||
|
|
@ -1210,6 +1221,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) -> V
|
|||
}
|
||||
ExprKind::Gen(_capt, body, _kind, _decl_span) => try_visit!(visitor.visit_block(body)),
|
||||
ExprKind::Await(expr, _span) => try_visit!(visitor.visit_expr(expr)),
|
||||
ExprKind::Use(expr, _span) => try_visit!(visitor.visit_expr(expr)),
|
||||
ExprKind::Assign(lhs, rhs, _span) => {
|
||||
try_visit!(visitor.visit_expr(lhs));
|
||||
try_visit!(visitor.visit_expr(rhs));
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@
|
|||
#![cfg_attr(feature = "nightly", allow(internal_features))]
|
||||
#![cfg_attr(feature = "nightly", feature(never_type))]
|
||||
#![cfg_attr(feature = "nightly", feature(rustc_attrs))]
|
||||
#![warn(unreachable_pub)]
|
||||
// tidy-alphabetical-end
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
|
|
|
|||
|
|
@ -37,11 +37,11 @@ ast_lowering_bad_return_type_notation_inputs =
|
|||
.suggestion = remove the input types
|
||||
|
||||
ast_lowering_bad_return_type_notation_needs_dots = return type notation arguments must be elided with `..`
|
||||
.suggestion = add `..`
|
||||
.suggestion = use the correct syntax by adding `..` to the arguments
|
||||
|
||||
ast_lowering_bad_return_type_notation_output =
|
||||
return type not allowed with return type notation
|
||||
.suggestion = remove the return type
|
||||
ast_lowering_bad_return_type_notation_output_suggestion = use the right argument notation and remove the return type
|
||||
|
||||
ast_lowering_bad_return_type_notation_position = return type notation not allowed in this position yet
|
||||
|
||||
|
|
@ -88,7 +88,7 @@ ast_lowering_invalid_abi_clobber_abi =
|
|||
invalid ABI for `clobber_abi`
|
||||
.note = the following ABIs are supported on this target: {$supported_abis}
|
||||
|
||||
ast_lowering_invalid_abi_suggestion = did you mean
|
||||
ast_lowering_invalid_abi_suggestion = there's a similarly named valid ABI `{$suggestion}`
|
||||
|
||||
ast_lowering_invalid_asm_template_modifier_const =
|
||||
asm template modifiers are not allowed for `const` arguments
|
||||
|
|
|
|||
|
|
@ -195,7 +195,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
}
|
||||
}
|
||||
InlineAsmOperand::Const { anon_const } => hir::InlineAsmOperand::Const {
|
||||
anon_const: self.lower_anon_const_to_anon_const(anon_const),
|
||||
anon_const: self.lower_const_block(anon_const),
|
||||
},
|
||||
InlineAsmOperand::Sym { sym } => {
|
||||
let static_def_id = self
|
||||
|
|
|
|||
|
|
@ -60,25 +60,27 @@ pub(crate) struct DelegationResults<'hir> {
|
|||
}
|
||||
|
||||
impl<'hir> LoweringContext<'_, 'hir> {
|
||||
pub(crate) fn delegation_has_self(&self, item_id: NodeId, path_id: NodeId, span: Span) -> bool {
|
||||
/// Defines whether the delegatee is an associated function whose first parameter is `self`.
|
||||
pub(crate) fn delegatee_is_method(&self, item_id: NodeId, path_id: NodeId, span: Span) -> bool {
|
||||
let sig_id = self.get_delegation_sig_id(item_id, path_id, span);
|
||||
let Ok(sig_id) = sig_id else {
|
||||
return false;
|
||||
};
|
||||
self.has_self(sig_id, span)
|
||||
self.is_method(sig_id, span)
|
||||
}
|
||||
|
||||
fn has_self(&self, def_id: DefId, span: Span) -> bool {
|
||||
if let Some(local_sig_id) = def_id.as_local() {
|
||||
// The value may be missing due to recursive delegation.
|
||||
// Error will be emitted later during HIR ty lowering.
|
||||
self.resolver.delegation_fn_sigs.get(&local_sig_id).is_some_and(|sig| sig.has_self)
|
||||
} else {
|
||||
match self.tcx.def_kind(def_id) {
|
||||
DefKind::Fn => false,
|
||||
DefKind::AssocFn => self.tcx.associated_item(def_id).fn_has_self_parameter,
|
||||
_ => span_bug!(span, "unexpected DefKind for delegation item"),
|
||||
}
|
||||
fn is_method(&self, def_id: DefId, span: Span) -> bool {
|
||||
match self.tcx.def_kind(def_id) {
|
||||
DefKind::Fn => false,
|
||||
DefKind::AssocFn => match def_id.as_local() {
|
||||
Some(local_def_id) => self
|
||||
.resolver
|
||||
.delegation_fn_sigs
|
||||
.get(&local_def_id)
|
||||
.is_some_and(|sig| sig.has_self),
|
||||
None => self.tcx.associated_item(def_id).fn_has_self_parameter,
|
||||
},
|
||||
_ => span_bug!(span, "unexpected DefKind for delegation item"),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -324,10 +326,11 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
|
||||
let call = if self
|
||||
.get_resolution_id(delegation.id, span)
|
||||
.and_then(|def_id| Ok(self.has_self(def_id, span)))
|
||||
.and_then(|def_id| Ok(self.is_method(def_id, span)))
|
||||
.unwrap_or_default()
|
||||
&& delegation.qself.is_none()
|
||||
&& !has_generic_args
|
||||
&& !args.is_empty()
|
||||
{
|
||||
let ast_segment = delegation.path.segments.last().unwrap();
|
||||
let segment = self.lower_path_segment(
|
||||
|
|
|
|||
|
|
@ -46,8 +46,9 @@ pub(crate) struct TupleStructWithDefault {
|
|||
#[derive(Subdiagnostic)]
|
||||
#[suggestion(
|
||||
ast_lowering_invalid_abi_suggestion,
|
||||
code = "{suggestion}",
|
||||
applicability = "maybe-incorrect"
|
||||
code = "\"{suggestion}\"",
|
||||
applicability = "maybe-incorrect",
|
||||
style = "verbose"
|
||||
)]
|
||||
pub(crate) struct InvalidAbiSuggestion {
|
||||
#[primary_span]
|
||||
|
|
@ -372,24 +373,39 @@ pub(crate) struct InclusiveRangeWithNoEnd {
|
|||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
#[multipart_suggestion(
|
||||
ast_lowering_bad_return_type_notation_output_suggestion,
|
||||
applicability = "machine-applicable",
|
||||
style = "verbose"
|
||||
)]
|
||||
/// Given `T: Tr<m() -> Ret>` or `T: Tr<m(Ty) -> Ret>`, suggest `T: Tr<m(..)>`.
|
||||
pub(crate) struct RTNSuggestion {
|
||||
#[suggestion_part(code = "")]
|
||||
pub output: Span,
|
||||
#[suggestion_part(code = "(..)")]
|
||||
pub input: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
pub(crate) enum BadReturnTypeNotation {
|
||||
#[diag(ast_lowering_bad_return_type_notation_inputs)]
|
||||
Inputs {
|
||||
#[primary_span]
|
||||
#[suggestion(code = "()", applicability = "maybe-incorrect")]
|
||||
#[suggestion(code = "(..)", applicability = "machine-applicable", style = "verbose")]
|
||||
span: Span,
|
||||
},
|
||||
#[diag(ast_lowering_bad_return_type_notation_output)]
|
||||
Output {
|
||||
#[primary_span]
|
||||
#[suggestion(code = "", applicability = "maybe-incorrect")]
|
||||
span: Span,
|
||||
#[subdiagnostic]
|
||||
suggestion: RTNSuggestion,
|
||||
},
|
||||
#[diag(ast_lowering_bad_return_type_notation_needs_dots)]
|
||||
NeedsDots {
|
||||
#[primary_span]
|
||||
#[suggestion(code = "(..)", applicability = "maybe-incorrect")]
|
||||
#[suggestion(code = "(..)", applicability = "machine-applicable", style = "verbose")]
|
||||
span: Span,
|
||||
},
|
||||
#[diag(ast_lowering_bad_return_type_notation_position)]
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ use rustc_middle::span_bug;
|
|||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_session::errors::report_lit_error;
|
||||
use rustc_span::source_map::{Spanned, respan};
|
||||
use rustc_span::{DUMMY_SP, DesugaringKind, Ident, Span, Symbol, kw, sym};
|
||||
use rustc_span::{DUMMY_SP, DesugaringKind, Ident, Span, Symbol, sym};
|
||||
use thin_vec::{ThinVec, thin_vec};
|
||||
use visit::{Visitor, walk_expr};
|
||||
|
||||
|
|
@ -207,6 +207,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
},
|
||||
),
|
||||
ExprKind::Await(expr, await_kw_span) => self.lower_expr_await(*await_kw_span, expr),
|
||||
ExprKind::Use(expr, use_kw_span) => self.lower_expr_use(*use_kw_span, expr),
|
||||
ExprKind::Closure(box Closure {
|
||||
binder,
|
||||
capture_clause,
|
||||
|
|
@ -483,7 +484,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
if legacy_args_idx.contains(&idx) {
|
||||
let parent_def_id = self.current_hir_id_owner.def_id;
|
||||
let node_id = self.next_node_id();
|
||||
self.create_def(parent_def_id, node_id, kw::Empty, DefKind::AnonConst, f.span);
|
||||
self.create_def(parent_def_id, node_id, None, DefKind::AnonConst, f.span);
|
||||
let mut visitor = WillCreateDefIdsVisitor {};
|
||||
let const_value = if let ControlFlow::Break(span) = visitor.visit_expr(&arg) {
|
||||
AstP(Expr {
|
||||
|
|
@ -671,10 +672,13 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
let span = self.lower_span(arm.span);
|
||||
self.lower_attrs(hir_id, &arm.attrs, arm.span);
|
||||
let is_never_pattern = pat.is_never_pattern();
|
||||
let body = if let Some(body) = &arm.body
|
||||
// We need to lower the body even if it's unneeded for never pattern in match,
|
||||
// ensure that we can get HirId for DefId if need (issue #137708).
|
||||
let body = arm.body.as_ref().map(|x| self.lower_expr(x));
|
||||
let body = if let Some(body) = body
|
||||
&& !is_never_pattern
|
||||
{
|
||||
self.lower_expr(body)
|
||||
body
|
||||
} else {
|
||||
// Either `body.is_none()` or `is_never_pattern` here.
|
||||
if !is_never_pattern {
|
||||
|
|
@ -1064,6 +1068,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
)
|
||||
}
|
||||
|
||||
fn lower_expr_use(&mut self, use_kw_span: Span, expr: &Expr) -> hir::ExprKind<'hir> {
|
||||
hir::ExprKind::Use(self.lower_expr(expr), use_kw_span)
|
||||
}
|
||||
|
||||
fn lower_expr_closure(
|
||||
&mut self,
|
||||
binder: &ClosureBinder,
|
||||
|
|
@ -1687,6 +1695,19 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
let yielded =
|
||||
opt_expr.as_ref().map(|x| self.lower_expr(x)).unwrap_or_else(|| self.expr_unit(span));
|
||||
|
||||
if !self.tcx.features().yield_expr()
|
||||
&& !self.tcx.features().coroutines()
|
||||
&& !self.tcx.features().gen_blocks()
|
||||
{
|
||||
rustc_session::parse::feature_err(
|
||||
&self.tcx.sess,
|
||||
sym::yield_expr,
|
||||
span,
|
||||
fluent_generated::ast_lowering_yield,
|
||||
)
|
||||
.emit();
|
||||
}
|
||||
|
||||
let is_async_gen = match self.coroutine_kind {
|
||||
Some(hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Gen, _)) => false,
|
||||
Some(hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::AsyncGen, _)) => true,
|
||||
|
|
@ -1711,28 +1732,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
None,
|
||||
);
|
||||
}
|
||||
Some(hir::CoroutineKind::Coroutine(_)) => {
|
||||
if !self.tcx.features().coroutines() {
|
||||
rustc_session::parse::feature_err(
|
||||
&self.tcx.sess,
|
||||
sym::coroutines,
|
||||
span,
|
||||
fluent_generated::ast_lowering_yield,
|
||||
)
|
||||
.emit();
|
||||
}
|
||||
false
|
||||
}
|
||||
Some(hir::CoroutineKind::Coroutine(_)) => false,
|
||||
None => {
|
||||
if !self.tcx.features().coroutines() {
|
||||
rustc_session::parse::feature_err(
|
||||
&self.tcx.sess,
|
||||
sym::coroutines,
|
||||
span,
|
||||
fluent_generated::ast_lowering_yield,
|
||||
)
|
||||
.emit();
|
||||
}
|
||||
let suggestion = self.current_item.map(|s| s.shrink_to_lo());
|
||||
self.dcx().emit_err(YieldInClosure { span, suggestion });
|
||||
self.coroutine_kind = Some(hir::CoroutineKind::Coroutine(Movability::Movable));
|
||||
|
|
@ -2129,26 +2130,24 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
self.arena.alloc(self.expr(sp, hir::ExprKind::Tup(&[])))
|
||||
}
|
||||
|
||||
pub(super) fn expr_usize(&mut self, sp: Span, value: usize) -> hir::Expr<'hir> {
|
||||
fn expr_uint(&mut self, sp: Span, ty: ast::UintTy, value: u128) -> hir::Expr<'hir> {
|
||||
let lit = self.arena.alloc(hir::Lit {
|
||||
span: sp,
|
||||
node: ast::LitKind::Int(
|
||||
(value as u128).into(),
|
||||
ast::LitIntType::Unsigned(ast::UintTy::Usize),
|
||||
),
|
||||
node: ast::LitKind::Int(value.into(), ast::LitIntType::Unsigned(ty)),
|
||||
});
|
||||
self.expr(sp, hir::ExprKind::Lit(lit))
|
||||
}
|
||||
|
||||
pub(super) fn expr_usize(&mut self, sp: Span, value: usize) -> hir::Expr<'hir> {
|
||||
self.expr_uint(sp, ast::UintTy::Usize, value as u128)
|
||||
}
|
||||
|
||||
pub(super) fn expr_u32(&mut self, sp: Span, value: u32) -> hir::Expr<'hir> {
|
||||
let lit = self.arena.alloc(hir::Lit {
|
||||
span: sp,
|
||||
node: ast::LitKind::Int(
|
||||
u128::from(value).into(),
|
||||
ast::LitIntType::Unsigned(ast::UintTy::U32),
|
||||
),
|
||||
});
|
||||
self.expr(sp, hir::ExprKind::Lit(lit))
|
||||
self.expr_uint(sp, ast::UintTy::U32, value as u128)
|
||||
}
|
||||
|
||||
pub(super) fn expr_u16(&mut self, sp: Span, value: u16) -> hir::Expr<'hir> {
|
||||
self.expr_uint(sp, ast::UintTy::U16, value as u128)
|
||||
}
|
||||
|
||||
pub(super) fn expr_char(&mut self, sp: Span, value: char) -> hir::Expr<'hir> {
|
||||
|
|
|
|||
|
|
@ -292,7 +292,7 @@ fn make_count<'hir>(
|
|||
hir::LangItem::FormatCount,
|
||||
sym::Is,
|
||||
));
|
||||
let value = ctx.arena.alloc_from_iter([ctx.expr_usize(sp, *n)]);
|
||||
let value = ctx.arena.alloc_from_iter([ctx.expr_u16(sp, *n)]);
|
||||
ctx.expr_call_mut(sp, count_is, value)
|
||||
}
|
||||
Some(FormatCount::Argument(arg)) => {
|
||||
|
|
|
|||
|
|
@ -3,10 +3,9 @@ use rustc_ast::ptr::P;
|
|||
use rustc_ast::visit::AssocCtxt;
|
||||
use rustc_ast::*;
|
||||
use rustc_errors::ErrorGuaranteed;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::PredicateOrigin;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::def_id::{CRATE_DEF_ID, LocalDefId};
|
||||
use rustc_hir::{self as hir, HirId, PredicateOrigin};
|
||||
use rustc_index::{IndexSlice, IndexVec};
|
||||
use rustc_middle::span_bug;
|
||||
use rustc_middle::ty::{ResolverAstLowering, TyCtxt};
|
||||
|
|
@ -209,6 +208,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
generics,
|
||||
body,
|
||||
contract,
|
||||
define_opaque,
|
||||
..
|
||||
}) => {
|
||||
self.with_new_scopes(*fn_sig_span, |this| {
|
||||
|
|
@ -237,6 +237,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
header: this.lower_fn_header(*header, hir::Safety::Safe, attrs),
|
||||
span: this.lower_span(*fn_sig_span),
|
||||
};
|
||||
this.lower_define_opaque(hir_id, &define_opaque);
|
||||
hir::ItemKind::Fn { sig, generics, body: body_id, has_body: body.is_some() }
|
||||
})
|
||||
}
|
||||
|
|
@ -779,7 +780,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
);
|
||||
(generics, kind, expr.is_some())
|
||||
}
|
||||
AssocItemKind::Fn(box Fn { sig, generics, body: None, .. }) => {
|
||||
AssocItemKind::Fn(box Fn { sig, generics, body: None, define_opaque, .. }) => {
|
||||
// FIXME(contracts): Deny contract here since it won't apply to
|
||||
// any impl method or callees.
|
||||
let names = self.lower_fn_params_to_names(&sig.decl);
|
||||
|
|
@ -791,9 +792,22 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
sig.header.coroutine_kind,
|
||||
attrs,
|
||||
);
|
||||
if define_opaque.is_some() {
|
||||
self.dcx().span_err(
|
||||
i.span,
|
||||
"only trait methods with default bodies can define opaque types",
|
||||
);
|
||||
}
|
||||
(generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Required(names)), false)
|
||||
}
|
||||
AssocItemKind::Fn(box Fn { sig, generics, body: Some(body), contract, .. }) => {
|
||||
AssocItemKind::Fn(box Fn {
|
||||
sig,
|
||||
generics,
|
||||
body: Some(body),
|
||||
contract,
|
||||
define_opaque,
|
||||
..
|
||||
}) => {
|
||||
let body_id = self.lower_maybe_coroutine_body(
|
||||
sig.span,
|
||||
i.span,
|
||||
|
|
@ -812,6 +826,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
sig.header.coroutine_kind,
|
||||
attrs,
|
||||
);
|
||||
self.lower_define_opaque(hir_id, &define_opaque);
|
||||
(generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Provided(body_id)), true)
|
||||
}
|
||||
AssocItemKind::Type(box TyAlias { generics, where_clauses, bounds, ty, .. }) => {
|
||||
|
|
@ -871,7 +886,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
hir::AssocItemKind::Fn { has_self: sig.decl.has_self() }
|
||||
}
|
||||
AssocItemKind::Delegation(box delegation) => hir::AssocItemKind::Fn {
|
||||
has_self: self.delegation_has_self(i.id, delegation.id, i.span),
|
||||
has_self: self.delegatee_is_method(i.id, delegation.id, i.span),
|
||||
},
|
||||
AssocItemKind::MacCall(..) | AssocItemKind::DelegationMac(..) => {
|
||||
panic!("macros should have been expanded by now")
|
||||
|
|
@ -911,7 +926,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
hir::ImplItemKind::Const(ty, body)
|
||||
},
|
||||
),
|
||||
AssocItemKind::Fn(box Fn { sig, generics, body, contract, .. }) => {
|
||||
AssocItemKind::Fn(box Fn { sig, generics, body, contract, define_opaque, .. }) => {
|
||||
let body_id = self.lower_maybe_coroutine_body(
|
||||
sig.span,
|
||||
i.span,
|
||||
|
|
@ -930,6 +945,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
sig.header.coroutine_kind,
|
||||
attrs,
|
||||
);
|
||||
self.lower_define_opaque(hir_id, &define_opaque);
|
||||
|
||||
(generics, hir::ImplItemKind::Fn(sig, body_id))
|
||||
}
|
||||
|
|
@ -1000,7 +1016,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
hir::AssocItemKind::Fn { has_self: sig.decl.has_self() }
|
||||
}
|
||||
AssocItemKind::Delegation(box delegation) => hir::AssocItemKind::Fn {
|
||||
has_self: self.delegation_has_self(i.id, delegation.id, i.span),
|
||||
has_self: self.delegatee_is_method(i.id, delegation.id, i.span),
|
||||
},
|
||||
AssocItemKind::MacCall(..) | AssocItemKind::DelegationMac(..) => {
|
||||
panic!("macros should have been expanded by now")
|
||||
|
|
@ -1510,7 +1526,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
span: abi.span,
|
||||
suggestion: suggested_name.map(|suggested_name| InvalidAbiSuggestion {
|
||||
span: abi.span,
|
||||
suggestion: format!("\"{suggested_name}\""),
|
||||
suggestion: suggested_name.to_string(),
|
||||
}),
|
||||
command: "rustc --print=calling-conventions".to_string(),
|
||||
});
|
||||
|
|
@ -1657,6 +1673,35 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
(lowered_generics, res)
|
||||
}
|
||||
|
||||
pub(super) fn lower_define_opaque(
|
||||
&mut self,
|
||||
hir_id: HirId,
|
||||
define_opaque: &Option<ThinVec<(NodeId, Path)>>,
|
||||
) {
|
||||
assert_eq!(self.define_opaque, None);
|
||||
assert!(hir_id.is_owner());
|
||||
let Some(define_opaque) = define_opaque.as_ref() else {
|
||||
return;
|
||||
};
|
||||
let define_opaque = define_opaque.iter().filter_map(|(id, path)| {
|
||||
let res = self.resolver.get_partial_res(*id).unwrap();
|
||||
let Some(did) = res.expect_full_res().opt_def_id() else {
|
||||
self.dcx().span_delayed_bug(path.span, "should have errored in resolve");
|
||||
return None;
|
||||
};
|
||||
let Some(did) = did.as_local() else {
|
||||
self.dcx().span_err(
|
||||
path.span,
|
||||
"only opaque types defined in the local crate can be defined",
|
||||
);
|
||||
return None;
|
||||
};
|
||||
Some((self.lower_span(path.span), did))
|
||||
});
|
||||
let define_opaque = self.arena.alloc_from_iter(define_opaque);
|
||||
self.define_opaque = Some(define_opaque);
|
||||
}
|
||||
|
||||
pub(super) fn lower_generic_bound_predicate(
|
||||
&mut self,
|
||||
ident: Ident,
|
||||
|
|
@ -1728,6 +1773,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
fn lower_where_predicate(&mut self, pred: &WherePredicate) -> hir::WherePredicate<'hir> {
|
||||
let hir_id = self.lower_node_id(pred.id);
|
||||
let span = self.lower_span(pred.span);
|
||||
self.lower_attrs(hir_id, &pred.attrs, span);
|
||||
let kind = self.arena.alloc(match &pred.kind {
|
||||
WherePredicateKind::BoundPredicate(WhereBoundPredicate {
|
||||
bound_generic_params,
|
||||
|
|
|
|||
|
|
@ -32,13 +32,14 @@
|
|||
|
||||
// tidy-alphabetical-start
|
||||
#![allow(internal_features)]
|
||||
#![cfg_attr(doc, recursion_limit = "256")] // FIXME(nnethercote): will be removed by #124141
|
||||
#![doc(rust_logo)]
|
||||
#![feature(assert_matches)]
|
||||
#![feature(box_patterns)]
|
||||
#![feature(exact_size_is_empty)]
|
||||
#![feature(if_let_guard)]
|
||||
#![feature(let_chains)]
|
||||
#![feature(rustdoc_internals)]
|
||||
#![warn(unreachable_pub)]
|
||||
// tidy-alphabetical-end
|
||||
|
||||
use std::sync::Arc;
|
||||
|
|
@ -98,6 +99,8 @@ struct LoweringContext<'a, 'hir> {
|
|||
|
||||
/// Bodies inside the owner being lowered.
|
||||
bodies: Vec<(hir::ItemLocalId, &'hir hir::Body<'hir>)>,
|
||||
/// `#[define_opaque]` attributes
|
||||
define_opaque: Option<&'hir [(Span, LocalDefId)]>,
|
||||
/// Attributes inside the owner being lowered.
|
||||
attrs: SortedMap<hir::ItemLocalId, &'hir [hir::Attribute]>,
|
||||
/// Collect items that were created by lowering the current owner.
|
||||
|
|
@ -136,6 +139,7 @@ struct LoweringContext<'a, 'hir> {
|
|||
|
||||
allow_try_trait: Arc<[Symbol]>,
|
||||
allow_gen_future: Arc<[Symbol]>,
|
||||
allow_pattern_type: Arc<[Symbol]>,
|
||||
allow_async_iterator: Arc<[Symbol]>,
|
||||
allow_for_await: Arc<[Symbol]>,
|
||||
allow_async_fn_traits: Arc<[Symbol]>,
|
||||
|
|
@ -154,6 +158,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
|
||||
// HirId handling.
|
||||
bodies: Vec::new(),
|
||||
define_opaque: None,
|
||||
attrs: SortedMap::default(),
|
||||
children: Vec::default(),
|
||||
contract_ensures: None,
|
||||
|
|
@ -176,6 +181,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
impl_trait_defs: Vec::new(),
|
||||
impl_trait_bounds: Vec::new(),
|
||||
allow_try_trait: [sym::try_trait_v2, sym::yeet_desugar_details].into(),
|
||||
allow_pattern_type: [sym::pattern_types, sym::pattern_type_range_trait].into(),
|
||||
allow_gen_future: if tcx.features().async_fn_track_caller() {
|
||||
[sym::gen_future, sym::closure_track_caller].into()
|
||||
} else {
|
||||
|
|
@ -492,7 +498,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
&mut self,
|
||||
parent: LocalDefId,
|
||||
node_id: ast::NodeId,
|
||||
name: Symbol,
|
||||
name: Option<Symbol>,
|
||||
def_kind: DefKind,
|
||||
span: Span,
|
||||
) -> LocalDefId {
|
||||
|
|
@ -545,6 +551,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
|
||||
let current_attrs = std::mem::take(&mut self.attrs);
|
||||
let current_bodies = std::mem::take(&mut self.bodies);
|
||||
let current_define_opaque = std::mem::take(&mut self.define_opaque);
|
||||
let current_ident_and_label_to_local_id =
|
||||
std::mem::take(&mut self.ident_and_label_to_local_id);
|
||||
|
||||
|
|
@ -578,6 +585,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
|
||||
self.attrs = current_attrs;
|
||||
self.bodies = current_bodies;
|
||||
self.define_opaque = current_define_opaque;
|
||||
self.ident_and_label_to_local_id = current_ident_and_label_to_local_id;
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
|
|
@ -597,6 +605,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
fn make_owner_info(&mut self, node: hir::OwnerNode<'hir>) -> &'hir hir::OwnerInfo<'hir> {
|
||||
let attrs = std::mem::take(&mut self.attrs);
|
||||
let mut bodies = std::mem::take(&mut self.bodies);
|
||||
let define_opaque = std::mem::take(&mut self.define_opaque);
|
||||
let trait_map = std::mem::take(&mut self.trait_map);
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
|
|
@ -616,7 +625,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
let num_nodes = self.item_local_id_counter.as_usize();
|
||||
let (nodes, parenting) = index::index_hir(self.tcx, node, &bodies, num_nodes);
|
||||
let nodes = hir::OwnerNodes { opt_hash_including_bodies, nodes, bodies };
|
||||
let attrs = hir::AttributeMap { map: attrs, opt_hash: attrs_hash };
|
||||
let attrs = hir::AttributeMap { map: attrs, opt_hash: attrs_hash, define_opaque };
|
||||
|
||||
self.arena.alloc(hir::OwnerInfo { nodes, parenting, attrs, trait_map })
|
||||
}
|
||||
|
|
@ -772,7 +781,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
let _def_id = self.create_def(
|
||||
self.current_hir_id_owner.def_id,
|
||||
param,
|
||||
kw::UnderscoreLifetime,
|
||||
Some(kw::UnderscoreLifetime),
|
||||
DefKind::LifetimeParam,
|
||||
ident.span,
|
||||
);
|
||||
|
|
@ -926,19 +935,26 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
if let Some(first_char) = constraint.ident.as_str().chars().next()
|
||||
&& first_char.is_ascii_lowercase()
|
||||
{
|
||||
let mut err = if !data.inputs.is_empty() {
|
||||
self.dcx().create_err(errors::BadReturnTypeNotation::Inputs {
|
||||
span: data.inputs_span,
|
||||
})
|
||||
} else if let FnRetTy::Ty(ty) = &data.output {
|
||||
self.dcx().create_err(errors::BadReturnTypeNotation::Output {
|
||||
span: data.inputs_span.shrink_to_hi().to(ty.span),
|
||||
})
|
||||
} else {
|
||||
self.dcx().create_err(errors::BadReturnTypeNotation::NeedsDots {
|
||||
span: data.inputs_span,
|
||||
})
|
||||
let err = match (&data.inputs[..], &data.output) {
|
||||
([_, ..], FnRetTy::Default(_)) => {
|
||||
errors::BadReturnTypeNotation::Inputs { span: data.inputs_span }
|
||||
}
|
||||
([], FnRetTy::Default(_)) => {
|
||||
errors::BadReturnTypeNotation::NeedsDots { span: data.inputs_span }
|
||||
}
|
||||
// The case `T: Trait<method(..) -> Ret>` is handled in the parser.
|
||||
(_, FnRetTy::Ty(ty)) => {
|
||||
let span = data.inputs_span.shrink_to_hi().to(ty.span);
|
||||
errors::BadReturnTypeNotation::Output {
|
||||
span,
|
||||
suggestion: errors::RTNSuggestion {
|
||||
output: span,
|
||||
input: data.inputs_span,
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
let mut err = self.dcx().create_err(err);
|
||||
if !self.tcx.features().return_type_notation()
|
||||
&& self.tcx.sess.is_nightly_build()
|
||||
{
|
||||
|
|
@ -1086,7 +1102,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
.and_then(|partial_res| partial_res.full_res())
|
||||
{
|
||||
if !res.matches_ns(Namespace::TypeNS)
|
||||
&& path.is_potential_trivial_const_arg()
|
||||
&& path.is_potential_trivial_const_arg(false)
|
||||
{
|
||||
debug!(
|
||||
"lower_generic_arg: Lowering type argument as const argument: {:?}",
|
||||
|
|
@ -1357,7 +1373,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
}
|
||||
}
|
||||
TyKind::Pat(ty, pat) => {
|
||||
hir::TyKind::Pat(self.lower_ty(ty, itctx), self.lower_ty_pat(pat))
|
||||
hir::TyKind::Pat(self.lower_ty(ty, itctx), self.lower_ty_pat(pat, ty.span))
|
||||
}
|
||||
TyKind::MacCall(_) => {
|
||||
span_bug!(t.span, "`TyKind::MacCall` should have been expanded by now")
|
||||
|
|
@ -2053,8 +2069,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
) -> &'hir hir::ConstArg<'hir> {
|
||||
let tcx = self.tcx;
|
||||
|
||||
// FIXME(min_generic_const_args): we only allow one-segment const paths for now
|
||||
let ct_kind = if path.is_potential_trivial_const_arg()
|
||||
let ct_kind = if path
|
||||
.is_potential_trivial_const_arg(tcx.features().min_generic_const_args())
|
||||
&& (tcx.features().min_generic_const_args()
|
||||
|| matches!(res, Res::Def(DefKind::ConstParam, _)))
|
||||
{
|
||||
|
|
@ -2064,7 +2080,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
path,
|
||||
ParamMode::Optional,
|
||||
AllowReturnTypeNotation::No,
|
||||
// FIXME(min_generic_const_args): update for `fn foo() -> Bar<FOO<impl Trait>>` support
|
||||
// FIXME(mgca): update for `fn foo() -> Bar<FOO<impl Trait>>` support
|
||||
ImplTraitContext::Disallowed(ImplTraitPosition::Path),
|
||||
None,
|
||||
);
|
||||
|
|
@ -2080,8 +2096,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
// We're lowering a const argument that was originally thought to be a type argument,
|
||||
// so the def collector didn't create the def ahead of time. That's why we have to do
|
||||
// it here.
|
||||
let def_id =
|
||||
self.create_def(parent_def_id, node_id, kw::Empty, DefKind::AnonConst, span);
|
||||
let def_id = self.create_def(parent_def_id, node_id, None, DefKind::AnonConst, span);
|
||||
let hir_id = self.lower_node_id(node_id);
|
||||
|
||||
let path_expr = Expr {
|
||||
|
|
@ -2128,19 +2143,18 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
};
|
||||
let maybe_res =
|
||||
self.resolver.get_partial_res(expr.id).and_then(|partial_res| partial_res.full_res());
|
||||
// FIXME(min_generic_const_args): we only allow one-segment const paths for now
|
||||
if let ExprKind::Path(None, path) = &expr.kind
|
||||
&& path.is_potential_trivial_const_arg()
|
||||
if let ExprKind::Path(qself, path) = &expr.kind
|
||||
&& path.is_potential_trivial_const_arg(tcx.features().min_generic_const_args())
|
||||
&& (tcx.features().min_generic_const_args()
|
||||
|| matches!(maybe_res, Some(Res::Def(DefKind::ConstParam, _))))
|
||||
{
|
||||
let qpath = self.lower_qpath(
|
||||
expr.id,
|
||||
&None,
|
||||
qself,
|
||||
path,
|
||||
ParamMode::Optional,
|
||||
AllowReturnTypeNotation::No,
|
||||
// FIXME(min_generic_const_args): update for `fn foo() -> Bar<FOO<impl Trait>>` support
|
||||
// FIXME(mgca): update for `fn foo() -> Bar<FOO<impl Trait>>` support
|
||||
ImplTraitContext::Disallowed(ImplTraitPosition::Path),
|
||||
None,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -3,11 +3,11 @@ use std::sync::Arc;
|
|||
use rustc_ast::ptr::P;
|
||||
use rustc_ast::*;
|
||||
use rustc_data_structures::stack::ensure_sufficient_stack;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::Res;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::{self as hir, LangItem};
|
||||
use rustc_middle::span_bug;
|
||||
use rustc_span::source_map::{Spanned, respan};
|
||||
use rustc_span::{Ident, Span};
|
||||
use rustc_span::{DesugaringKind, Ident, Span};
|
||||
|
||||
use super::errors::{
|
||||
ArbitraryExpressionInPattern, ExtraDoubleDot, MisplacedDoubleDot, SubTupleBinding,
|
||||
|
|
@ -430,22 +430,124 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
self.arena.alloc(hir::PatExpr { hir_id: self.lower_node_id(expr.id), span, kind })
|
||||
}
|
||||
|
||||
pub(crate) fn lower_ty_pat(&mut self, pattern: &TyPat) -> &'hir hir::TyPat<'hir> {
|
||||
self.arena.alloc(self.lower_ty_pat_mut(pattern))
|
||||
pub(crate) fn lower_ty_pat(
|
||||
&mut self,
|
||||
pattern: &TyPat,
|
||||
base_type: Span,
|
||||
) -> &'hir hir::TyPat<'hir> {
|
||||
self.arena.alloc(self.lower_ty_pat_mut(pattern, base_type))
|
||||
}
|
||||
|
||||
fn lower_ty_pat_mut(&mut self, pattern: &TyPat) -> hir::TyPat<'hir> {
|
||||
fn lower_ty_pat_mut(&mut self, pattern: &TyPat, base_type: Span) -> hir::TyPat<'hir> {
|
||||
// loop here to avoid recursion
|
||||
let pat_hir_id = self.lower_node_id(pattern.id);
|
||||
let node = match &pattern.kind {
|
||||
TyPatKind::Range(e1, e2, Spanned { node: end, .. }) => hir::TyPatKind::Range(
|
||||
e1.as_deref().map(|e| self.lower_anon_const_to_const_arg(e)),
|
||||
e2.as_deref().map(|e| self.lower_anon_const_to_const_arg(e)),
|
||||
self.lower_range_end(end, e2.is_some()),
|
||||
TyPatKind::Range(e1, e2, Spanned { node: end, span }) => hir::TyPatKind::Range(
|
||||
e1.as_deref().map(|e| self.lower_anon_const_to_const_arg(e)).unwrap_or_else(|| {
|
||||
self.lower_ty_pat_range_end(
|
||||
hir::LangItem::RangeMin,
|
||||
span.shrink_to_lo(),
|
||||
base_type,
|
||||
)
|
||||
}),
|
||||
e2.as_deref()
|
||||
.map(|e| match end {
|
||||
RangeEnd::Included(..) => self.lower_anon_const_to_const_arg(e),
|
||||
RangeEnd::Excluded => self.lower_excluded_range_end(e),
|
||||
})
|
||||
.unwrap_or_else(|| {
|
||||
self.lower_ty_pat_range_end(
|
||||
hir::LangItem::RangeMax,
|
||||
span.shrink_to_hi(),
|
||||
base_type,
|
||||
)
|
||||
}),
|
||||
),
|
||||
TyPatKind::Err(guar) => hir::TyPatKind::Err(*guar),
|
||||
};
|
||||
|
||||
hir::TyPat { hir_id: pat_hir_id, kind: node, span: self.lower_span(pattern.span) }
|
||||
}
|
||||
|
||||
/// Lowers the range end of an exclusive range (`2..5`) to an inclusive range 2..=(5 - 1).
|
||||
/// This way the type system doesn't have to handle the distinction between inclusive/exclusive ranges.
|
||||
fn lower_excluded_range_end(&mut self, e: &AnonConst) -> &'hir hir::ConstArg<'hir> {
|
||||
let span = self.lower_span(e.value.span);
|
||||
let unstable_span = self.mark_span_with_reason(
|
||||
DesugaringKind::PatTyRange,
|
||||
span,
|
||||
Some(Arc::clone(&self.allow_pattern_type)),
|
||||
);
|
||||
let anon_const = self.with_new_scopes(span, |this| {
|
||||
let def_id = this.local_def_id(e.id);
|
||||
let hir_id = this.lower_node_id(e.id);
|
||||
let body = this.lower_body(|this| {
|
||||
// Need to use a custom function as we can't just subtract `1` from a `char`.
|
||||
let kind = hir::ExprKind::Path(this.make_lang_item_qpath(
|
||||
hir::LangItem::RangeSub,
|
||||
unstable_span,
|
||||
None,
|
||||
));
|
||||
let fn_def = this.arena.alloc(hir::Expr { hir_id: this.next_id(), kind, span });
|
||||
let args = this.arena.alloc([this.lower_expr_mut(&e.value)]);
|
||||
(
|
||||
&[],
|
||||
hir::Expr {
|
||||
hir_id: this.next_id(),
|
||||
kind: hir::ExprKind::Call(fn_def, args),
|
||||
span,
|
||||
},
|
||||
)
|
||||
});
|
||||
hir::AnonConst { def_id, hir_id, body, span }
|
||||
});
|
||||
self.arena.alloc(hir::ConstArg {
|
||||
hir_id: self.next_id(),
|
||||
kind: hir::ConstArgKind::Anon(self.arena.alloc(anon_const)),
|
||||
})
|
||||
}
|
||||
|
||||
/// When a range has no end specified (`1..` or `1..=`) or no start specified (`..5` or `..=5`),
|
||||
/// we instead use a constant of the MAX/MIN of the type.
|
||||
/// This way the type system does not have to handle the lack of a start/end.
|
||||
fn lower_ty_pat_range_end(
|
||||
&mut self,
|
||||
lang_item: LangItem,
|
||||
span: Span,
|
||||
base_type: Span,
|
||||
) -> &'hir hir::ConstArg<'hir> {
|
||||
let parent_def_id = self.current_hir_id_owner.def_id;
|
||||
let node_id = self.next_node_id();
|
||||
|
||||
// Add a definition for the in-band const def.
|
||||
// We're generating a range end that didn't exist in the AST,
|
||||
// so the def collector didn't create the def ahead of time. That's why we have to do
|
||||
// it here.
|
||||
let def_id = self.create_def(parent_def_id, node_id, None, DefKind::AnonConst, span);
|
||||
let hir_id = self.lower_node_id(node_id);
|
||||
|
||||
let unstable_span = self.mark_span_with_reason(
|
||||
DesugaringKind::PatTyRange,
|
||||
self.lower_span(span),
|
||||
Some(Arc::clone(&self.allow_pattern_type)),
|
||||
);
|
||||
let span = self.lower_span(base_type);
|
||||
|
||||
let path_expr = hir::Expr {
|
||||
hir_id: self.next_id(),
|
||||
kind: hir::ExprKind::Path(self.make_lang_item_qpath(lang_item, unstable_span, None)),
|
||||
span,
|
||||
};
|
||||
|
||||
let ct = self.with_new_scopes(span, |this| {
|
||||
self.arena.alloc(hir::AnonConst {
|
||||
def_id,
|
||||
hir_id,
|
||||
body: this.lower_body(|_this| (&[], path_expr)),
|
||||
span,
|
||||
})
|
||||
});
|
||||
let hir_id = self.next_id();
|
||||
self.arena.alloc(hir::ConstArg { kind: hir::ConstArgKind::Anon(ct), hir_id })
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ use tracing::{debug, instrument};
|
|||
|
||||
use super::errors::{
|
||||
AsyncBoundNotOnTrait, AsyncBoundOnlyForFnTraits, BadReturnTypeNotation,
|
||||
GenericTypeWithParentheses, UseAngleBrackets,
|
||||
GenericTypeWithParentheses, RTNSuggestion, UseAngleBrackets,
|
||||
};
|
||||
use super::{
|
||||
AllowReturnTypeNotation, GenericArgsCtor, GenericArgsMode, ImplTraitContext, ImplTraitPosition,
|
||||
|
|
@ -268,19 +268,26 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
}
|
||||
GenericArgs::Parenthesized(data) => match generic_args_mode {
|
||||
GenericArgsMode::ReturnTypeNotation => {
|
||||
let mut err = if !data.inputs.is_empty() {
|
||||
self.dcx().create_err(BadReturnTypeNotation::Inputs {
|
||||
span: data.inputs_span,
|
||||
})
|
||||
} else if let FnRetTy::Ty(ty) = &data.output {
|
||||
self.dcx().create_err(BadReturnTypeNotation::Output {
|
||||
span: data.inputs_span.shrink_to_hi().to(ty.span),
|
||||
})
|
||||
} else {
|
||||
self.dcx().create_err(BadReturnTypeNotation::NeedsDots {
|
||||
span: data.inputs_span,
|
||||
})
|
||||
let err = match (&data.inputs[..], &data.output) {
|
||||
([_, ..], FnRetTy::Default(_)) => {
|
||||
BadReturnTypeNotation::Inputs { span: data.inputs_span }
|
||||
}
|
||||
([], FnRetTy::Default(_)) => {
|
||||
BadReturnTypeNotation::NeedsDots { span: data.inputs_span }
|
||||
}
|
||||
// The case `T: Trait<method(..) -> Ret>` is handled in the parser.
|
||||
(_, FnRetTy::Ty(ty)) => {
|
||||
let span = data.inputs_span.shrink_to_hi().to(ty.span);
|
||||
BadReturnTypeNotation::Output {
|
||||
span,
|
||||
suggestion: RTNSuggestion {
|
||||
output: span,
|
||||
input: data.inputs_span,
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
let mut err = self.dcx().create_err(err);
|
||||
if !self.tcx.features().return_type_notation()
|
||||
&& self.tcx.sess.is_nightly_build()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -917,7 +917,10 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
|||
walk_list!(self, visit_attribute, &item.attrs);
|
||||
return; // Avoid visiting again.
|
||||
}
|
||||
ItemKind::Fn(func @ box Fn { defaultness, generics: _, sig, contract: _, body }) => {
|
||||
ItemKind::Fn(
|
||||
func
|
||||
@ box Fn { defaultness, generics: _, sig, contract: _, body, define_opaque: _ },
|
||||
) => {
|
||||
self.check_defaultness(item.span, *defaultness);
|
||||
|
||||
let is_intrinsic =
|
||||
|
|
|
|||
|
|
@ -535,7 +535,6 @@ pub(crate) struct WhereClauseBeforeTypeAlias {
|
|||
}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
|
||||
pub(crate) enum WhereClauseBeforeTypeAliasSugg {
|
||||
#[suggestion(ast_passes_remove_suggestion, applicability = "machine-applicable", code = "")]
|
||||
Remove {
|
||||
|
|
|
|||
|
|
@ -489,6 +489,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
|
|||
gate_all!(dyn_star, "`dyn*` trait objects are experimental");
|
||||
gate_all!(const_closures, "const closures are experimental");
|
||||
gate_all!(builtin_syntax, "`builtin #` syntax is unstable");
|
||||
gate_all!(ergonomic_clones, "ergonomic clones are experimental");
|
||||
gate_all!(explicit_tail_calls, "`become` expression is experimental");
|
||||
gate_all!(generic_const_items, "generic const items are experimental");
|
||||
gate_all!(guard_patterns, "guard patterns are experimental", "consider using match arm guards");
|
||||
|
|
@ -503,6 +504,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
|
|||
gate_all!(unsafe_binders, "unsafe binder types are experimental");
|
||||
gate_all!(contracts, "contracts are incomplete");
|
||||
gate_all!(contracts_internals, "contract internal machinery is for internal use only");
|
||||
gate_all!(where_clause_attrs, "attributes in `where` clause are unstable");
|
||||
|
||||
if !visitor.features.never_patterns() {
|
||||
if let Some(spans) = spans.get(&sym::never_patterns) {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@
|
|||
#![feature(iter_is_partitioned)]
|
||||
#![feature(let_chains)]
|
||||
#![feature(rustdoc_internals)]
|
||||
#![warn(unreachable_pub)]
|
||||
// tidy-alphabetical-end
|
||||
|
||||
pub mod ast_validation;
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
#![doc(rust_logo)]
|
||||
#![feature(box_patterns)]
|
||||
#![feature(rustdoc_internals)]
|
||||
#![warn(unreachable_pub)]
|
||||
// tidy-alphabetical-end
|
||||
|
||||
mod helpers;
|
||||
|
|
|
|||
|
|
@ -11,9 +11,7 @@ use std::sync::Arc;
|
|||
|
||||
use rustc_ast::attr::AttrIdGenerator;
|
||||
use rustc_ast::ptr::P;
|
||||
use rustc_ast::token::{
|
||||
self, BinOpToken, CommentKind, Delimiter, IdentIsRaw, Nonterminal, Token, TokenKind,
|
||||
};
|
||||
use rustc_ast::token::{self, CommentKind, Delimiter, IdentIsRaw, Nonterminal, Token, TokenKind};
|
||||
use rustc_ast::tokenstream::{Spacing, TokenStream, TokenTree};
|
||||
use rustc_ast::util::classify;
|
||||
use rustc_ast::util::comments::{Comment, CommentStyle};
|
||||
|
|
@ -26,7 +24,6 @@ use rustc_span::edition::Edition;
|
|||
use rustc_span::source_map::{SourceMap, Spanned};
|
||||
use rustc_span::symbol::IdentPrinter;
|
||||
use rustc_span::{BytePos, CharPos, DUMMY_SP, FileName, Ident, Pos, Span, Symbol, kw, sym};
|
||||
use thin_vec::ThinVec;
|
||||
|
||||
use crate::pp::Breaks::{Consistent, Inconsistent};
|
||||
use crate::pp::{self, Breaks};
|
||||
|
|
@ -319,7 +316,7 @@ fn space_between(tt1: &TokenTree, tt2: &TokenTree) -> bool {
|
|||
(tt1, Tok(Token { kind: Comma | Semi | Dot, .. }, _)) if !is_punct(tt1) => false,
|
||||
|
||||
// IDENT + `!`: `println!()`, but `if !x { ... }` needs a space after the `if`
|
||||
(Tok(Token { kind: Ident(sym, is_raw), span }, _), Tok(Token { kind: Not, .. }, _))
|
||||
(Tok(Token { kind: Ident(sym, is_raw), span }, _), Tok(Token { kind: Bang, .. }, _))
|
||||
if !Ident::new(*sym, *span).is_reserved() || matches!(is_raw, IdentIsRaw::Yes) =>
|
||||
{
|
||||
false
|
||||
|
|
@ -344,21 +341,6 @@ fn space_between(tt1: &TokenTree, tt2: &TokenTree) -> bool {
|
|||
}
|
||||
}
|
||||
|
||||
fn binop_to_string(op: BinOpToken) -> &'static str {
|
||||
match op {
|
||||
token::Plus => "+",
|
||||
token::Minus => "-",
|
||||
token::Star => "*",
|
||||
token::Slash => "/",
|
||||
token::Percent => "%",
|
||||
token::Caret => "^",
|
||||
token::And => "&",
|
||||
token::Or => "|",
|
||||
token::Shl => "<<",
|
||||
token::Shr => ">>",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn doc_comment_to_string(
|
||||
comment_kind: CommentKind,
|
||||
attr_style: ast::AttrStyle,
|
||||
|
|
@ -913,12 +895,30 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
|
|||
token::Ne => "!=".into(),
|
||||
token::Ge => ">=".into(),
|
||||
token::Gt => ">".into(),
|
||||
token::Not => "!".into(),
|
||||
token::Bang => "!".into(),
|
||||
token::Tilde => "~".into(),
|
||||
token::OrOr => "||".into(),
|
||||
token::AndAnd => "&&".into(),
|
||||
token::BinOp(op) => binop_to_string(op).into(),
|
||||
token::BinOpEq(op) => format!("{}=", binop_to_string(op)).into(),
|
||||
token::Plus => "+".into(),
|
||||
token::Minus => "-".into(),
|
||||
token::Star => "*".into(),
|
||||
token::Slash => "/".into(),
|
||||
token::Percent => "%".into(),
|
||||
token::Caret => "^".into(),
|
||||
token::And => "&".into(),
|
||||
token::Or => "|".into(),
|
||||
token::Shl => "<<".into(),
|
||||
token::Shr => ">>".into(),
|
||||
token::PlusEq => "+=".into(),
|
||||
token::MinusEq => "-=".into(),
|
||||
token::StarEq => "*=".into(),
|
||||
token::SlashEq => "/=".into(),
|
||||
token::PercentEq => "%=".into(),
|
||||
token::CaretEq => "^=".into(),
|
||||
token::AndEq => "&=".into(),
|
||||
token::OrEq => "|=".into(),
|
||||
token::ShlEq => "<<=".into(),
|
||||
token::ShrEq => ">>=".into(),
|
||||
|
||||
/* Structural symbols */
|
||||
token::At => "@".into(),
|
||||
|
|
@ -1782,6 +1782,13 @@ impl<'a> State<'a> {
|
|||
self.print_mutability(*m, false);
|
||||
self.word("self")
|
||||
}
|
||||
SelfKind::Pinned(lt, m) => {
|
||||
self.word("&");
|
||||
self.print_opt_lifetime(lt);
|
||||
self.word("pin ");
|
||||
self.print_mutability(*m, true);
|
||||
self.word("self")
|
||||
}
|
||||
SelfKind::Explicit(typ, m) => {
|
||||
self.print_mutability(*m, false);
|
||||
self.word("self");
|
||||
|
|
@ -1970,15 +1977,7 @@ impl<'a> State<'a> {
|
|||
) {
|
||||
self.ibox(INDENT_UNIT);
|
||||
self.print_formal_generic_params(generic_params);
|
||||
let generics = ast::Generics {
|
||||
params: ThinVec::new(),
|
||||
where_clause: ast::WhereClause {
|
||||
has_where_token: false,
|
||||
predicates: ThinVec::new(),
|
||||
span: DUMMY_SP,
|
||||
},
|
||||
span: DUMMY_SP,
|
||||
};
|
||||
let generics = ast::Generics::default();
|
||||
let header = ast::FnHeader { safety, ext, ..ast::FnHeader::default() };
|
||||
self.print_fn(decl, header, name, &generics);
|
||||
self.end();
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use itertools::{Itertools, Position};
|
|||
use rustc_ast::ptr::P;
|
||||
use rustc_ast::util::classify;
|
||||
use rustc_ast::util::literal::escape_byte_str_symbol;
|
||||
use rustc_ast::util::parser::{self, AssocOp, ExprPrecedence, Fixity};
|
||||
use rustc_ast::util::parser::{self, ExprPrecedence, Fixity};
|
||||
use rustc_ast::{
|
||||
self as ast, BlockCheckMode, FormatAlignment, FormatArgPosition, FormatArgsPiece, FormatCount,
|
||||
FormatDebugHex, FormatSign, FormatTrait, token,
|
||||
|
|
@ -279,12 +279,11 @@ impl<'a> State<'a> {
|
|||
rhs: &ast::Expr,
|
||||
fixup: FixupContext,
|
||||
) {
|
||||
let assoc_op = AssocOp::from_ast_binop(op.node);
|
||||
let binop_prec = assoc_op.precedence();
|
||||
let binop_prec = op.node.precedence();
|
||||
let left_prec = lhs.precedence();
|
||||
let right_prec = rhs.precedence();
|
||||
|
||||
let (mut left_needs_paren, right_needs_paren) = match assoc_op.fixity() {
|
||||
let (mut left_needs_paren, right_needs_paren) = match op.node.fixity() {
|
||||
Fixity::Left => (left_prec < binop_prec, right_prec <= binop_prec),
|
||||
Fixity::Right => (left_prec <= binop_prec, right_prec < binop_prec),
|
||||
Fixity::None => (left_prec <= binop_prec, right_prec <= binop_prec),
|
||||
|
|
@ -575,6 +574,14 @@ impl<'a> State<'a> {
|
|||
);
|
||||
self.word(".await");
|
||||
}
|
||||
ast::ExprKind::Use(expr, _) => {
|
||||
self.print_expr_cond_paren(
|
||||
expr,
|
||||
expr.precedence() < ExprPrecedence::Unambiguous,
|
||||
fixup,
|
||||
);
|
||||
self.word(".use");
|
||||
}
|
||||
ast::ExprKind::Assign(lhs, rhs, _) => {
|
||||
self.print_expr_cond_paren(
|
||||
lhs,
|
||||
|
|
@ -886,6 +893,7 @@ impl<'a> State<'a> {
|
|||
fn print_capture_clause(&mut self, capture_clause: ast::CaptureBy) {
|
||||
match capture_clause {
|
||||
ast::CaptureBy::Value { .. } => self.word_space("move"),
|
||||
ast::CaptureBy::Use { .. } => self.word_space("use"),
|
||||
ast::CaptureBy::Ref => {}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -650,7 +650,16 @@ impl<'a> State<'a> {
|
|||
attrs: &[ast::Attribute],
|
||||
func: &ast::Fn,
|
||||
) {
|
||||
let ast::Fn { defaultness, generics, sig, contract, body } = func;
|
||||
let ast::Fn { defaultness, generics, sig, contract, body, define_opaque } = func;
|
||||
|
||||
if let Some(define_opaque) = define_opaque {
|
||||
for (_, path) in define_opaque {
|
||||
self.word("define opaques from ");
|
||||
self.print_path(path, false, 0);
|
||||
self.word(",");
|
||||
}
|
||||
}
|
||||
|
||||
if body.is_some() {
|
||||
self.head("");
|
||||
}
|
||||
|
|
@ -698,7 +707,7 @@ impl<'a> State<'a> {
|
|||
}
|
||||
self.print_generic_params(&generics.params);
|
||||
self.print_fn_params_and_ret(decl, false);
|
||||
self.print_where_clause(&generics.where_clause)
|
||||
self.print_where_clause(&generics.where_clause);
|
||||
}
|
||||
|
||||
pub(crate) fn print_fn_params_and_ret(&mut self, decl: &ast::FnDecl, is_closure: bool) {
|
||||
|
|
@ -735,7 +744,8 @@ impl<'a> State<'a> {
|
|||
}
|
||||
|
||||
pub fn print_where_predicate(&mut self, predicate: &ast::WherePredicate) {
|
||||
let ast::WherePredicate { kind, id: _, span: _ } = predicate;
|
||||
let ast::WherePredicate { attrs, kind, id: _, span: _, is_placeholder: _ } = predicate;
|
||||
self.print_outer_attributes(attrs);
|
||||
match kind {
|
||||
ast::WherePredicateKind::BoundPredicate(where_bound_predicate) => {
|
||||
self.print_where_bound_predicate(where_bound_predicate);
|
||||
|
|
|
|||
|
|
@ -138,13 +138,14 @@ impl Deprecation {
|
|||
}
|
||||
}
|
||||
|
||||
/// Attributes represent parsed, *built in*, inert attributes. That means,
|
||||
/// attributes that are not actually ever expanded.
|
||||
/// For more information on this, see the module docs on the rustc_attr_parsing crate.
|
||||
/// Represent parsed, *built in*, inert attributes.
|
||||
///
|
||||
/// That means attributes that are not actually ever expanded.
|
||||
/// For more information on this, see the module docs on the [`rustc_attr_parsing`] crate.
|
||||
/// They're instead used as markers, to guide the compilation process in various way in most every stage of the compiler.
|
||||
/// These are kept around after the AST, into the HIR and further on.
|
||||
///
|
||||
/// The word parsed could be a little misleading here, because the parser already parses
|
||||
/// The word "parsed" could be a little misleading here, because the parser already parses
|
||||
/// attributes early on. However, the result, an [`ast::Attribute`]
|
||||
/// is only parsed at a high level, still containing a token stream in many cases. That is
|
||||
/// because the structure of the contents varies from attribute to attribute.
|
||||
|
|
@ -153,7 +154,9 @@ impl Deprecation {
|
|||
/// the place where `must_use` is checked) little to no extra parsing or validating needs to
|
||||
/// happen.
|
||||
///
|
||||
/// For more docs, look in [`rustc_attr`](https://doc.rust-lang.org/stable/nightly-rustc/rustc_attr/index.html)
|
||||
/// For more docs, look in [`rustc_attr_parsing`].
|
||||
///
|
||||
/// [`rustc_attr_parsing`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_attr_parsing/index.html
|
||||
#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)]
|
||||
pub enum AttributeKind {
|
||||
// tidy-alphabetical-start
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
#![doc(rust_logo)]
|
||||
#![feature(let_chains)]
|
||||
#![feature(rustdoc_internals)]
|
||||
#![warn(unreachable_pub)]
|
||||
// tidy-alphabetical-end
|
||||
|
||||
mod attributes;
|
||||
|
|
@ -35,13 +34,17 @@ pub trait HashStableContext: rustc_ast::HashStableContext + rustc_abi::HashStabl
|
|||
/// like [`Span`]s and empty tuples, are gracefully skipped so they don't clutter the
|
||||
/// representation much.
|
||||
pub trait PrintAttribute {
|
||||
fn print_something(&self) -> bool;
|
||||
/// Whether or not this will render as something meaningful, or if it's skipped
|
||||
/// (which will force the containing struct to also skip printing a comma
|
||||
/// and the field name).
|
||||
fn should_render(&self) -> bool;
|
||||
|
||||
fn print_attribute(&self, p: &mut Printer);
|
||||
}
|
||||
|
||||
impl<T: PrintAttribute> PrintAttribute for &T {
|
||||
fn print_something(&self) -> bool {
|
||||
T::print_something(self)
|
||||
fn should_render(&self) -> bool {
|
||||
T::should_render(self)
|
||||
}
|
||||
|
||||
fn print_attribute(&self, p: &mut Printer) {
|
||||
|
|
@ -49,9 +52,10 @@ impl<T: PrintAttribute> PrintAttribute for &T {
|
|||
}
|
||||
}
|
||||
impl<T: PrintAttribute> PrintAttribute for Option<T> {
|
||||
fn print_something(&self) -> bool {
|
||||
self.as_ref().is_some_and(|x| x.print_something())
|
||||
fn should_render(&self) -> bool {
|
||||
self.as_ref().is_some_and(|x| x.should_render())
|
||||
}
|
||||
|
||||
fn print_attribute(&self, p: &mut Printer) {
|
||||
if let Some(i) = self {
|
||||
T::print_attribute(i, p)
|
||||
|
|
@ -59,9 +63,10 @@ impl<T: PrintAttribute> PrintAttribute for Option<T> {
|
|||
}
|
||||
}
|
||||
impl<T: PrintAttribute> PrintAttribute for ThinVec<T> {
|
||||
fn print_something(&self) -> bool {
|
||||
self.is_empty() || self[0].print_something()
|
||||
fn should_render(&self) -> bool {
|
||||
self.is_empty() || self[0].should_render()
|
||||
}
|
||||
|
||||
fn print_attribute(&self, p: &mut Printer) {
|
||||
let mut last_printed = false;
|
||||
p.word("[");
|
||||
|
|
@ -70,7 +75,7 @@ impl<T: PrintAttribute> PrintAttribute for ThinVec<T> {
|
|||
p.word_space(",");
|
||||
}
|
||||
i.print_attribute(p);
|
||||
last_printed = i.print_something();
|
||||
last_printed = i.should_render();
|
||||
}
|
||||
p.word("]");
|
||||
}
|
||||
|
|
@ -78,7 +83,7 @@ impl<T: PrintAttribute> PrintAttribute for ThinVec<T> {
|
|||
macro_rules! print_skip {
|
||||
($($t: ty),* $(,)?) => {$(
|
||||
impl PrintAttribute for $t {
|
||||
fn print_something(&self) -> bool { false }
|
||||
fn should_render(&self) -> bool { false }
|
||||
fn print_attribute(&self, _: &mut Printer) { }
|
||||
})*
|
||||
};
|
||||
|
|
@ -87,7 +92,7 @@ macro_rules! print_skip {
|
|||
macro_rules! print_disp {
|
||||
($($t: ty),* $(,)?) => {$(
|
||||
impl PrintAttribute for $t {
|
||||
fn print_something(&self) -> bool { true }
|
||||
fn should_render(&self) -> bool { true }
|
||||
fn print_attribute(&self, p: &mut Printer) {
|
||||
p.word(format!("{}", self));
|
||||
}
|
||||
|
|
@ -97,7 +102,7 @@ macro_rules! print_disp {
|
|||
macro_rules! print_debug {
|
||||
($($t: ty),* $(,)?) => {$(
|
||||
impl PrintAttribute for $t {
|
||||
fn print_something(&self) -> bool { true }
|
||||
fn should_render(&self) -> bool { true }
|
||||
fn print_attribute(&self, p: &mut Printer) {
|
||||
p.word(format!("{:?}", self));
|
||||
}
|
||||
|
|
@ -106,37 +111,39 @@ macro_rules! print_debug {
|
|||
}
|
||||
|
||||
macro_rules! print_tup {
|
||||
(num_print_something $($ts: ident)*) => { 0 $(+ $ts.print_something() as usize)* };
|
||||
(num_should_render $($ts: ident)*) => { 0 $(+ $ts.should_render() as usize)* };
|
||||
() => {};
|
||||
($t: ident $($ts: ident)*) => {
|
||||
#[allow(non_snake_case, unused)]
|
||||
impl<$t: PrintAttribute, $($ts: PrintAttribute),*> PrintAttribute for ($t, $($ts),*) {
|
||||
fn print_something(&self) -> bool {
|
||||
fn should_render(&self) -> bool {
|
||||
let ($t, $($ts),*) = self;
|
||||
print_tup!(num_print_something $t $($ts)*) != 0
|
||||
print_tup!(num_should_render $t $($ts)*) != 0
|
||||
}
|
||||
|
||||
fn print_attribute(&self, p: &mut Printer) {
|
||||
let ($t, $($ts),*) = self;
|
||||
let parens = print_tup!(num_print_something $t $($ts)*) > 1;
|
||||
let parens = print_tup!(num_should_render $t $($ts)*) > 1;
|
||||
if parens {
|
||||
p.word("(");
|
||||
p.popen();
|
||||
}
|
||||
|
||||
let mut printed_anything = $t.print_something();
|
||||
let mut printed_anything = $t.should_render();
|
||||
|
||||
$t.print_attribute(p);
|
||||
|
||||
$(
|
||||
if printed_anything && $ts.print_something() {
|
||||
p.word_space(",");
|
||||
if $ts.should_render() {
|
||||
if printed_anything {
|
||||
p.word_space(",");
|
||||
}
|
||||
printed_anything = true;
|
||||
}
|
||||
$ts.print_attribute(p);
|
||||
)*
|
||||
|
||||
if parens {
|
||||
p.word(")");
|
||||
p.pclose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -147,5 +154,49 @@ macro_rules! print_tup {
|
|||
|
||||
print_tup!(A B C D E F G H);
|
||||
print_skip!(Span, ());
|
||||
print_disp!(Symbol, u16, bool, NonZero<u32>);
|
||||
print_debug!(UintTy, IntTy, Align, AttrStyle, CommentKind, Transparency);
|
||||
print_disp!(u16, bool, NonZero<u32>);
|
||||
print_debug!(Symbol, UintTy, IntTy, Align, AttrStyle, CommentKind, Transparency);
|
||||
|
||||
/// Finds attributes in sequences of attributes by pattern matching.
|
||||
///
|
||||
/// A little like `matches` but for attributes.
|
||||
///
|
||||
/// ```rust,ignore (illustrative)
|
||||
/// // finds the repr attribute
|
||||
/// if let Some(r) = find_attr!(attrs, AttributeKind::Repr(r) => r) {
|
||||
///
|
||||
/// }
|
||||
///
|
||||
/// // checks if one has matched
|
||||
/// if find_attr!(attrs, AttributeKind::Repr(_)) {
|
||||
///
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Often this requires you to first end up with a list of attributes.
|
||||
/// A common way to get those is through `tcx.get_all_attrs(did)`
|
||||
#[macro_export]
|
||||
macro_rules! find_attr {
|
||||
($attributes_list: expr, $pattern: pat $(if $guard: expr)?) => {{
|
||||
$crate::find_attr!($attributes_list, $pattern $(if $guard)? => ()).is_some()
|
||||
}};
|
||||
|
||||
($attributes_list: expr, $pattern: pat $(if $guard: expr)? => $e: expr) => {{
|
||||
fn check_attribute_iterator<'a>(_: &'_ impl IntoIterator<Item = &'a rustc_hir::Attribute>) {}
|
||||
check_attribute_iterator(&$attributes_list);
|
||||
|
||||
let find_attribute = |iter| {
|
||||
for i in $attributes_list {
|
||||
match i {
|
||||
rustc_hir::Attribute::Parsed($pattern) $(if $guard)? => {
|
||||
return Some($e);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
};
|
||||
find_attribute($attributes_list)
|
||||
}};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ rustc_fluent_macro = { path = "../rustc_fluent_macro" }
|
|||
rustc_hir = { path = "../rustc_hir" }
|
||||
rustc_lexer = { path = "../rustc_lexer" }
|
||||
rustc_macros = { path = "../rustc_macros" }
|
||||
rustc_middle = { path = "../rustc_middle" }
|
||||
rustc_serialize = { path = "../rustc_serialize" }
|
||||
rustc_session = { path = "../rustc_session" }
|
||||
rustc_span = { path = "../rustc_span" }
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ use rustc_errors::{DiagCtxtHandle, Diagnostic};
|
|||
use rustc_feature::Features;
|
||||
use rustc_hir::{AttrArgs, AttrItem, AttrPath, Attribute, HashIgnoredAttrId};
|
||||
use rustc_session::Session;
|
||||
use rustc_span::symbol::kw;
|
||||
use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym};
|
||||
|
||||
use crate::attributes::allow_unstable::{AllowConstFnUnstableParser, AllowInternalUnstableParser};
|
||||
|
|
@ -333,9 +332,12 @@ impl<'sess> AttributeParser<'sess> {
|
|||
{
|
||||
lit
|
||||
} else {
|
||||
let guar = self.dcx().has_errors().unwrap();
|
||||
let guar = self.dcx().span_delayed_bug(
|
||||
args.span().unwrap_or(DUMMY_SP),
|
||||
"expr in place where literal is expected (builtin attr parsing)",
|
||||
);
|
||||
ast::MetaItemLit {
|
||||
symbol: kw::Empty,
|
||||
symbol: sym::dummy,
|
||||
suffix: None,
|
||||
kind: ast::LitKind::Err(guar),
|
||||
span: DUMMY_SP,
|
||||
|
|
|
|||
|
|
@ -77,10 +77,10 @@
|
|||
|
||||
// tidy-alphabetical-start
|
||||
#![allow(internal_features)]
|
||||
#![cfg_attr(doc, recursion_limit = "256")] // FIXME(nnethercote): will be removed by #124141
|
||||
#![doc(rust_logo)]
|
||||
#![feature(let_chains)]
|
||||
#![feature(rustdoc_internals)]
|
||||
#![warn(unreachable_pub)]
|
||||
// tidy-alphabetical-end
|
||||
|
||||
#[macro_use]
|
||||
|
|
@ -95,47 +95,3 @@ pub use context::{AttributeParser, OmitDoc};
|
|||
pub use rustc_attr_data_structures::*;
|
||||
|
||||
rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
|
||||
|
||||
/// Finds attributes in sequences of attributes by pattern matching.
|
||||
///
|
||||
/// A little like `matches` but for attributes.
|
||||
///
|
||||
/// ```rust,ignore (illustrative)
|
||||
/// // finds the repr attribute
|
||||
/// if let Some(r) = find_attr!(attrs, AttributeKind::Repr(r) => r) {
|
||||
///
|
||||
/// }
|
||||
///
|
||||
/// // checks if one has matched
|
||||
/// if find_attr!(attrs, AttributeKind::Repr(_)) {
|
||||
///
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Often this requires you to first end up with a list of attributes.
|
||||
/// A common way to get those is through `tcx.get_all_attrs(did)`
|
||||
#[macro_export]
|
||||
macro_rules! find_attr {
|
||||
($attributes_list: expr, $pattern: pat $(if $guard: expr)?) => {{
|
||||
$crate::find_attr!($attributes_list, $pattern $(if $guard)? => ()).is_some()
|
||||
}};
|
||||
|
||||
($attributes_list: expr, $pattern: pat $(if $guard: expr)? => $e: expr) => {{
|
||||
fn check_attribute_iterator<'a>(_: &'_ impl IntoIterator<Item = &'a rustc_hir::Attribute>) {}
|
||||
check_attribute_iterator(&$attributes_list);
|
||||
|
||||
let find_attribute = |iter| {
|
||||
for i in $attributes_list {
|
||||
match i {
|
||||
rustc_hir::Attribute::Parsed($pattern) $(if $guard)? => {
|
||||
return Some($e);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
};
|
||||
find_attribute($attributes_list)
|
||||
}};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,8 +12,8 @@ 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};
|
||||
use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol};
|
||||
use rustc_span::symbol::{Ident, kw, sym};
|
||||
use rustc_span::{ErrorGuaranteed, Span, Symbol};
|
||||
|
||||
pub struct SegmentIterator<'a> {
|
||||
offset: usize,
|
||||
|
|
@ -127,7 +127,7 @@ impl<'a> ArgParser<'a> {
|
|||
}
|
||||
AttrArgs::Eq { eq_span, expr } => Self::NameValue(NameValueParser {
|
||||
eq_span: *eq_span,
|
||||
value: expr_to_lit(dcx, &expr),
|
||||
value: expr_to_lit(dcx, &expr, *eq_span),
|
||||
value_span: expr.span,
|
||||
}),
|
||||
}
|
||||
|
|
@ -348,7 +348,7 @@ impl NameValueParser {
|
|||
}
|
||||
}
|
||||
|
||||
fn expr_to_lit(dcx: DiagCtxtHandle<'_>, expr: &Expr) -> MetaItemLit {
|
||||
fn expr_to_lit(dcx: DiagCtxtHandle<'_>, expr: &Expr, span: Span) -> MetaItemLit {
|
||||
// In valid code the value always ends up as a single literal. Otherwise, a dummy
|
||||
// literal suffices because the error is handled elsewhere.
|
||||
if let ExprKind::Lit(token_lit) = expr.kind
|
||||
|
|
@ -356,8 +356,11 @@ fn expr_to_lit(dcx: DiagCtxtHandle<'_>, expr: &Expr) -> MetaItemLit {
|
|||
{
|
||||
lit
|
||||
} else {
|
||||
let guar = dcx.has_errors().unwrap();
|
||||
MetaItemLit { symbol: kw::Empty, suffix: None, kind: LitKind::Err(guar), span: DUMMY_SP }
|
||||
let guar = dcx.span_delayed_bug(
|
||||
span,
|
||||
"expr in place where literal is expected (builtin attr parsing)",
|
||||
);
|
||||
MetaItemLit { symbol: sym::dummy, suffix: None, kind: LitKind::Err(guar), span }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -470,45 +473,34 @@ impl<'a> MetaItemListParserContext<'a> {
|
|||
{
|
||||
self.inside_delimiters.next();
|
||||
return Some(MetaItemOrLitParser::Lit(lit));
|
||||
} else if let Some(TokenTree::Delimited(.., Delimiter::Invisible(_), inner_tokens)) =
|
||||
self.inside_delimiters.peek()
|
||||
{
|
||||
self.inside_delimiters.next();
|
||||
return MetaItemListParserContext {
|
||||
inside_delimiters: inner_tokens.iter().peekable(),
|
||||
dcx: self.dcx,
|
||||
}
|
||||
.next();
|
||||
}
|
||||
|
||||
// or a path.
|
||||
let path =
|
||||
if let Some(TokenTree::Token(Token { kind: token::Interpolated(nt), span, .. }, _)) =
|
||||
if let Some(TokenTree::Token(Token { kind: token::Interpolated(_), span, .. }, _)) =
|
||||
self.inside_delimiters.peek()
|
||||
{
|
||||
match &**nt {
|
||||
// or maybe a full nt meta including the path but we return immediately
|
||||
token::Nonterminal::NtMeta(item) => {
|
||||
self.inside_delimiters.next();
|
||||
self.inside_delimiters.next();
|
||||
// We go into this path if an expr ended up in an attribute that
|
||||
// expansion did not turn into a literal. Say, `#[repr(align(macro!()))]`
|
||||
// where the macro didn't expand to a literal. An error is already given
|
||||
// for this at this point, and then we do continue. This makes this path
|
||||
// reachable...
|
||||
let e = self.dcx.span_delayed_bug(
|
||||
*span,
|
||||
"expr in place where literal is expected (builtin attr parsing)",
|
||||
);
|
||||
|
||||
return Some(MetaItemOrLitParser::MetaItemParser(MetaItemParser {
|
||||
path: PathParser::Ast(&item.path),
|
||||
args: ArgParser::from_attr_args(&item.args, self.dcx),
|
||||
}));
|
||||
}
|
||||
// an already interpolated path from a macro expansion is a path, no need to parse
|
||||
// one from tokens
|
||||
token::Nonterminal::NtPath(path) => {
|
||||
self.inside_delimiters.next();
|
||||
|
||||
AttrPath::from_ast(path)
|
||||
}
|
||||
_ => {
|
||||
self.inside_delimiters.next();
|
||||
// we go into this path if an expr ended up in an attribute that
|
||||
// expansion did not turn into a literal. Say, `#[repr(align(macro!()))]`
|
||||
// where the macro didn't expand to a literal. An error is already given
|
||||
// for this at this point, and then we do continue. This makes this path
|
||||
// reachable...
|
||||
let e = self.dcx.span_delayed_bug(
|
||||
*span,
|
||||
"expr in place where literal is expected (builtin attr parsing)",
|
||||
);
|
||||
|
||||
return Some(MetaItemOrLitParser::Err(*span, e));
|
||||
}
|
||||
}
|
||||
return Some(MetaItemOrLitParser::Err(*span, e));
|
||||
} else {
|
||||
self.next_path()?
|
||||
};
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
// tidy-alphabetical-start
|
||||
#![allow(elided_lifetimes_in_paths)]
|
||||
#![allow(internal_features)]
|
||||
#![allow(unreachable_pub)] // because this crate is mostly generated code
|
||||
#![doc(rust_logo)]
|
||||
#![feature(rustdoc_internals)]
|
||||
// #![warn(unreachable_pub)] // don't use because this crate is mostly generated code
|
||||
|
|
|
|||
|
|
@ -403,6 +403,7 @@ impl<'infcx, 'tcx> crate::MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
.expect_closure();
|
||||
let span = match capture_clause {
|
||||
rustc_hir::CaptureBy::Value { move_kw } => move_kw.shrink_to_lo(),
|
||||
rustc_hir::CaptureBy::Use { use_kw } => use_kw.shrink_to_lo(),
|
||||
rustc_hir::CaptureBy::Ref => fn_decl_span.shrink_to_lo(),
|
||||
};
|
||||
diag.span_suggestion_verbose(
|
||||
|
|
|
|||
|
|
@ -1,11 +1,8 @@
|
|||
use rustc_data_structures::graph;
|
||||
use rustc_index::IndexVec;
|
||||
use rustc_middle::mir::ConstraintCategory;
|
||||
use rustc_middle::ty::{RegionVid, VarianceDiagInfo};
|
||||
use rustc_span::DUMMY_SP;
|
||||
use rustc_middle::ty::RegionVid;
|
||||
|
||||
use crate::constraints::{OutlivesConstraint, OutlivesConstraintIndex, OutlivesConstraintSet};
|
||||
use crate::type_check::Locations;
|
||||
|
||||
/// The construct graph organizes the constraints by their end-points.
|
||||
/// It can be used to view a `R1: R2` constraint as either an edge `R1
|
||||
|
|
@ -23,8 +20,8 @@ pub(crate) type ReverseConstraintGraph = ConstraintGraph<Reverse>;
|
|||
/// Marker trait that controls whether a `R1: R2` constraint
|
||||
/// represents an edge `R1 -> R2` or `R2 -> R1`.
|
||||
pub(crate) trait ConstraintGraphDirection: Copy + 'static {
|
||||
fn start_region(c: &OutlivesConstraint<'_>) -> RegionVid;
|
||||
fn end_region(c: &OutlivesConstraint<'_>) -> RegionVid;
|
||||
fn start_region(sup: RegionVid, sub: RegionVid) -> RegionVid;
|
||||
fn end_region(sup: RegionVid, sub: RegionVid) -> RegionVid;
|
||||
fn is_normal() -> bool;
|
||||
}
|
||||
|
||||
|
|
@ -36,12 +33,12 @@ pub(crate) trait ConstraintGraphDirection: Copy + 'static {
|
|||
pub(crate) struct Normal;
|
||||
|
||||
impl ConstraintGraphDirection for Normal {
|
||||
fn start_region(c: &OutlivesConstraint<'_>) -> RegionVid {
|
||||
c.sup
|
||||
fn start_region(sup: RegionVid, _sub: RegionVid) -> RegionVid {
|
||||
sup
|
||||
}
|
||||
|
||||
fn end_region(c: &OutlivesConstraint<'_>) -> RegionVid {
|
||||
c.sub
|
||||
fn end_region(_sup: RegionVid, sub: RegionVid) -> RegionVid {
|
||||
sub
|
||||
}
|
||||
|
||||
fn is_normal() -> bool {
|
||||
|
|
@ -57,12 +54,12 @@ impl ConstraintGraphDirection for Normal {
|
|||
pub(crate) struct Reverse;
|
||||
|
||||
impl ConstraintGraphDirection for Reverse {
|
||||
fn start_region(c: &OutlivesConstraint<'_>) -> RegionVid {
|
||||
c.sub
|
||||
fn start_region(_sup: RegionVid, sub: RegionVid) -> RegionVid {
|
||||
sub
|
||||
}
|
||||
|
||||
fn end_region(c: &OutlivesConstraint<'_>) -> RegionVid {
|
||||
c.sup
|
||||
fn end_region(sup: RegionVid, _sub: RegionVid) -> RegionVid {
|
||||
sup
|
||||
}
|
||||
|
||||
fn is_normal() -> bool {
|
||||
|
|
@ -84,7 +81,7 @@ impl<D: ConstraintGraphDirection> ConstraintGraph<D> {
|
|||
let mut next_constraints = IndexVec::from_elem(None, &set.outlives);
|
||||
|
||||
for (idx, constraint) in set.outlives.iter_enumerated().rev() {
|
||||
let head = &mut first_constraints[D::start_region(constraint)];
|
||||
let head = &mut first_constraints[D::start_region(constraint.sup, constraint.sub)];
|
||||
let next = &mut next_constraints[idx];
|
||||
debug_assert!(next.is_none());
|
||||
*next = *head;
|
||||
|
|
@ -105,63 +102,57 @@ impl<D: ConstraintGraphDirection> ConstraintGraph<D> {
|
|||
RegionGraph::new(set, self, static_region)
|
||||
}
|
||||
|
||||
pub(crate) fn is_normal(&self) -> bool {
|
||||
D::is_normal()
|
||||
}
|
||||
|
||||
/// Given a region `R`, iterate over all constraints `R: R1`.
|
||||
pub(crate) fn outgoing_edges<'a, 'tcx>(
|
||||
pub(crate) fn outgoing_edges_from_graph<'a, 'tcx>(
|
||||
&'a self,
|
||||
region_sup: RegionVid,
|
||||
constraints: &'a OutlivesConstraintSet<'tcx>,
|
||||
static_region: RegionVid,
|
||||
) -> Edges<'a, 'tcx, D> {
|
||||
//if this is the `'static` region and the graph's direction is normal,
|
||||
//then setup the Edges iterator to return all regions #53178
|
||||
if region_sup == static_region && D::is_normal() {
|
||||
Edges {
|
||||
graph: self,
|
||||
constraints,
|
||||
pointer: None,
|
||||
next_static_idx: Some(0),
|
||||
static_region,
|
||||
}
|
||||
} else {
|
||||
//otherwise, just setup the iterator as normal
|
||||
let first = self.first_constraints[region_sup];
|
||||
Edges { graph: self, constraints, pointer: first, next_static_idx: None, static_region }
|
||||
}
|
||||
) -> EdgesFromGraph<'a, 'tcx, D> {
|
||||
EdgesFromGraph { graph: self, constraints, pointer: self.first_constraints[region_sup] }
|
||||
}
|
||||
|
||||
/// Returns all regions (#53178).
|
||||
pub(crate) fn outgoing_edges_from_static(&self) -> EdgesFromStatic {
|
||||
EdgesFromStatic { next_static_idx: 0, end_static_idx: self.first_constraints.len() }
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct Edges<'a, 'tcx, D: ConstraintGraphDirection> {
|
||||
pub(crate) struct EdgesFromGraph<'a, 'tcx, D: ConstraintGraphDirection> {
|
||||
graph: &'a ConstraintGraph<D>,
|
||||
constraints: &'a OutlivesConstraintSet<'tcx>,
|
||||
pointer: Option<OutlivesConstraintIndex>,
|
||||
next_static_idx: Option<usize>,
|
||||
static_region: RegionVid,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx, D: ConstraintGraphDirection> Iterator for Edges<'a, 'tcx, D> {
|
||||
type Item = OutlivesConstraint<'tcx>;
|
||||
impl<'a, 'tcx, D: ConstraintGraphDirection> Iterator for EdgesFromGraph<'a, 'tcx, D> {
|
||||
type Item = &'a OutlivesConstraint<'tcx>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if let Some(p) = self.pointer {
|
||||
self.pointer = self.graph.next_constraints[p];
|
||||
Some(&self.constraints[p])
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some(self.constraints[p])
|
||||
} else if let Some(next_static_idx) = self.next_static_idx {
|
||||
self.next_static_idx = if next_static_idx == (self.graph.first_constraints.len() - 1) {
|
||||
None
|
||||
} else {
|
||||
Some(next_static_idx + 1)
|
||||
};
|
||||
pub(crate) struct EdgesFromStatic {
|
||||
next_static_idx: usize,
|
||||
end_static_idx: usize,
|
||||
}
|
||||
|
||||
Some(OutlivesConstraint {
|
||||
sup: self.static_region,
|
||||
sub: next_static_idx.into(),
|
||||
locations: Locations::All(DUMMY_SP),
|
||||
span: DUMMY_SP,
|
||||
category: ConstraintCategory::Internal,
|
||||
variance_info: VarianceDiagInfo::default(),
|
||||
from_closure: false,
|
||||
})
|
||||
impl Iterator for EdgesFromStatic {
|
||||
type Item = RegionVid;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.next_static_idx < self.end_static_idx {
|
||||
let ret = RegionVid::from_usize(self.next_static_idx);
|
||||
self.next_static_idx += 1;
|
||||
Some(ret)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
|
@ -193,21 +184,38 @@ impl<'a, 'tcx, D: ConstraintGraphDirection> RegionGraph<'a, 'tcx, D> {
|
|||
/// Given a region `R`, iterate over all regions `R1` such that
|
||||
/// there exists a constraint `R: R1`.
|
||||
pub(crate) fn outgoing_regions(&self, region_sup: RegionVid) -> Successors<'a, 'tcx, D> {
|
||||
Successors {
|
||||
edges: self.constraint_graph.outgoing_edges(region_sup, self.set, self.static_region),
|
||||
// If this is the `'static` region and the graph's direction is normal,
|
||||
// then setup the Edges iterator to return all regions (#53178).
|
||||
if region_sup == self.static_region && D::is_normal() {
|
||||
Successors::FromStatic(self.constraint_graph.outgoing_edges_from_static())
|
||||
} else {
|
||||
// Otherwise, just setup the iterator as normal.
|
||||
Successors::FromGraph(
|
||||
self.constraint_graph.outgoing_edges_from_graph(region_sup, self.set),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct Successors<'a, 'tcx, D: ConstraintGraphDirection> {
|
||||
edges: Edges<'a, 'tcx, D>,
|
||||
pub(crate) enum Successors<'a, 'tcx, D: ConstraintGraphDirection> {
|
||||
FromStatic(EdgesFromStatic),
|
||||
FromGraph(EdgesFromGraph<'a, 'tcx, D>),
|
||||
}
|
||||
|
||||
impl<'a, 'tcx, D: ConstraintGraphDirection> Iterator for Successors<'a, 'tcx, D> {
|
||||
type Item = RegionVid;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.edges.next().map(|c| D::end_region(&c))
|
||||
match self {
|
||||
Successors::FromStatic(edges) => {
|
||||
// No `D::end_region` call needed here: static successors are only possible when
|
||||
// the direction is `Normal`, so we can directly use what would be the `sub` value.
|
||||
edges.next()
|
||||
}
|
||||
Successors::FromGraph(edges) => {
|
||||
edges.next().map(|constraint| D::end_region(constraint.sup, constraint.sub))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -287,7 +287,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
None => "value".to_owned(),
|
||||
};
|
||||
if needs_note {
|
||||
let ty = self.infcx.tcx.short_string(ty, err.long_ty_path());
|
||||
if let Some(local) = place.as_local() {
|
||||
let span = self.body.local_decls[local].source_info.span;
|
||||
err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Label {
|
||||
|
|
|
|||
|
|
@ -505,7 +505,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
let var_id =
|
||||
self.infcx.tcx.closure_captures(def_id)[field.index()].get_root_variable();
|
||||
|
||||
Some(self.infcx.tcx.hir().name(var_id).to_string())
|
||||
Some(self.infcx.tcx.hir_name(var_id).to_string())
|
||||
}
|
||||
_ => {
|
||||
// Might need a revision when the fields in trait RFC is implemented
|
||||
|
|
@ -1124,7 +1124,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
def_id, target_place, places
|
||||
);
|
||||
let hir_id = self.infcx.tcx.local_def_id_to_hir_id(def_id);
|
||||
let expr = &self.infcx.tcx.hir().expect_expr(hir_id).kind;
|
||||
let expr = &self.infcx.tcx.hir_expect_expr(hir_id).kind;
|
||||
debug!("closure_span: hir_id={:?} expr={:?}", hir_id, expr);
|
||||
if let &hir::ExprKind::Closure(&hir::Closure { kind, fn_decl_span, .. }) = expr {
|
||||
for (captured_place, place) in
|
||||
|
|
|
|||
|
|
@ -596,10 +596,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
self.suggest_cloning(err, place_ty, expr, None);
|
||||
}
|
||||
|
||||
let ty = self.infcx.tcx.short_string(place_ty, err.long_ty_path());
|
||||
err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Label {
|
||||
is_partial_move: false,
|
||||
ty,
|
||||
ty: place_ty,
|
||||
place: &place_desc,
|
||||
span,
|
||||
});
|
||||
|
|
@ -629,10 +628,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
self.suggest_cloning(err, place_ty, expr, Some(use_spans));
|
||||
}
|
||||
|
||||
let ty = self.infcx.tcx.short_string(place_ty, err.long_ty_path());
|
||||
err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Label {
|
||||
is_partial_move: false,
|
||||
ty,
|
||||
ty: place_ty,
|
||||
place: &place_desc,
|
||||
span: use_span,
|
||||
});
|
||||
|
|
@ -833,10 +831,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
self.suggest_cloning(err, bind_to.ty, expr, None);
|
||||
}
|
||||
|
||||
let ty = self.infcx.tcx.short_string(bind_to.ty, err.long_ty_path());
|
||||
err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Label {
|
||||
is_partial_move: false,
|
||||
ty,
|
||||
ty: bind_to.ty,
|
||||
place: place_desc,
|
||||
span: binding_span,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -698,7 +698,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
if !matches!(k, hir::AssocItemKind::Fn { .. }) {
|
||||
continue;
|
||||
}
|
||||
if self.infcx.tcx.hir().name(hi) != self.infcx.tcx.hir().name(my_hir) {
|
||||
if self.infcx.tcx.hir_name(hi) != self.infcx.tcx.hir_name(my_hir) {
|
||||
continue;
|
||||
}
|
||||
f_in_trait_opt = Some(hi);
|
||||
|
|
@ -823,7 +823,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
) => {
|
||||
capture_reason = format!("mutable borrow of `{upvar}`");
|
||||
}
|
||||
ty::UpvarCapture::ByValue => {
|
||||
ty::UpvarCapture::ByValue | ty::UpvarCapture::ByUse => {
|
||||
capture_reason = format!("possible mutation of `{upvar}`");
|
||||
}
|
||||
_ => bug!("upvar `{upvar}` borrowed, but not mutably"),
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
|
||||
if let Some(opaque_def_id) = opaque_def_id.as_local()
|
||||
&& let hir::OpaqueTyOrigin::FnReturn { parent, .. } =
|
||||
tcx.hir().expect_opaque_ty(opaque_def_id).origin
|
||||
tcx.hir_expect_opaque_ty(opaque_def_id).origin
|
||||
{
|
||||
if let Some(sugg) = impl_trait_overcapture_suggestion(
|
||||
tcx,
|
||||
|
|
|
|||
|
|
@ -194,8 +194,8 @@ impl Display for RegionName {
|
|||
}
|
||||
|
||||
impl rustc_errors::IntoDiagArg for RegionName {
|
||||
fn into_diag_arg(self) -> rustc_errors::DiagArgValue {
|
||||
self.to_string().into_diag_arg()
|
||||
fn into_diag_arg(self, path: &mut Option<std::path::PathBuf>) -> rustc_errors::DiagArgValue {
|
||||
self.to_string().into_diag_arg(path)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -343,7 +343,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
|
|||
}
|
||||
};
|
||||
let hir::ExprKind::Closure(&hir::Closure { fn_decl_span, .. }) =
|
||||
tcx.hir().expect_expr(self.mir_hir_id()).kind
|
||||
tcx.hir_expect_expr(self.mir_hir_id()).kind
|
||||
else {
|
||||
bug!("Closure is not defined by a closure expr");
|
||||
};
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
let upvar_hir_id = upvars[upvar_index].get_root_variable();
|
||||
debug!("get_upvar_name_and_span_for_region: upvar_hir_id={upvar_hir_id:?}");
|
||||
|
||||
let upvar_name = tcx.hir().name(upvar_hir_id);
|
||||
let upvar_name = tcx.hir_name(upvar_hir_id);
|
||||
let upvar_span = tcx.hir().span(upvar_hir_id);
|
||||
debug!(
|
||||
"get_upvar_name_and_span_for_region: upvar_name={upvar_name:?} upvar_span={upvar_span:?}",
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
// tidy-alphabetical-start
|
||||
#![allow(internal_features)]
|
||||
#![cfg_attr(doc, recursion_limit = "256")] // FIXME(nnethercote): will be removed by #124141
|
||||
#![doc(rust_logo)]
|
||||
#![feature(assert_matches)]
|
||||
#![feature(box_patterns)]
|
||||
|
|
@ -13,7 +14,6 @@
|
|||
#![feature(rustdoc_internals)]
|
||||
#![feature(stmt_expr_attributes)]
|
||||
#![feature(try_blocks)]
|
||||
#![warn(unreachable_pub)]
|
||||
// tidy-alphabetical-end
|
||||
|
||||
use std::borrow::Cow;
|
||||
|
|
@ -648,7 +648,7 @@ impl<'a, 'tcx> ResultsVisitor<'a, 'tcx, Borrowck<'a, 'tcx>> for MirBorrowckCtxt<
|
|||
| StatementKind::StorageLive(..) => {}
|
||||
// This does not affect borrowck
|
||||
StatementKind::BackwardIncompatibleDropHint { place, reason: BackwardIncompatibleDropReason::Edition2024 } => {
|
||||
self.check_backward_incompatible_drop(location, (**place, span), state);
|
||||
self.check_backward_incompatible_drop(location, **place, state);
|
||||
}
|
||||
StatementKind::StorageDead(local) => {
|
||||
self.access_place(
|
||||
|
|
@ -1174,7 +1174,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
|
|||
fn check_backward_incompatible_drop(
|
||||
&mut self,
|
||||
location: Location,
|
||||
(place, place_span): (Place<'tcx>, Span),
|
||||
place: Place<'tcx>,
|
||||
state: &BorrowckDomain,
|
||||
) {
|
||||
let tcx = self.infcx.tcx;
|
||||
|
|
@ -1490,14 +1490,20 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
|
|||
let stmt = &bbd.statements[loc.statement_index];
|
||||
debug!("temporary assigned in: stmt={:?}", stmt);
|
||||
|
||||
if let StatementKind::Assign(box (_, Rvalue::Ref(_, _, source))) = stmt.kind
|
||||
{
|
||||
propagate_closure_used_mut_place(self, source);
|
||||
} else {
|
||||
bug!(
|
||||
"closures should only capture user variables \
|
||||
match stmt.kind {
|
||||
StatementKind::Assign(box (
|
||||
_,
|
||||
Rvalue::Ref(_, _, source)
|
||||
| Rvalue::Use(Operand::Copy(source) | Operand::Move(source)),
|
||||
)) => {
|
||||
propagate_closure_used_mut_place(self, source);
|
||||
}
|
||||
_ => {
|
||||
bug!(
|
||||
"closures should only capture user variables \
|
||||
or references to user variables"
|
||||
);
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => propagate_closure_used_mut_place(self, place),
|
||||
|
|
|
|||
|
|
@ -21,8 +21,8 @@ use rustc_middle::traits::{ObligationCause, ObligationCauseCode};
|
|||
use rustc_middle::ty::fold::fold_regions;
|
||||
use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt, TypeFoldable, UniverseIndex};
|
||||
use rustc_mir_dataflow::points::DenseLocationMap;
|
||||
use rustc_span::Span;
|
||||
use rustc_span::hygiene::DesugaringKind;
|
||||
use rustc_span::{DUMMY_SP, Span};
|
||||
use tracing::{debug, instrument, trace};
|
||||
|
||||
use crate::BorrowckInferCtxt;
|
||||
|
|
@ -311,9 +311,11 @@ enum RegionRelationCheckResult {
|
|||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
enum Trace<'tcx> {
|
||||
enum Trace<'a, 'tcx> {
|
||||
StartRegion,
|
||||
FromOutlivesConstraint(OutlivesConstraint<'tcx>),
|
||||
FromGraph(&'a OutlivesConstraint<'tcx>),
|
||||
FromStatic(RegionVid),
|
||||
FromMember(RegionVid, RegionVid, Span),
|
||||
NotVisited,
|
||||
}
|
||||
|
||||
|
|
@ -1764,6 +1766,8 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
let mut context = IndexVec::from_elem(Trace::NotVisited, &self.definitions);
|
||||
context[from_region] = Trace::StartRegion;
|
||||
|
||||
let fr_static = self.universal_regions().fr_static;
|
||||
|
||||
// Use a deque so that we do a breadth-first search. We will
|
||||
// stop at the first match, which ought to be the shortest
|
||||
// path (fewest constraints).
|
||||
|
|
@ -1783,13 +1787,39 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
if target_test(r) {
|
||||
let mut result = vec![];
|
||||
let mut p = r;
|
||||
// This loop is cold and runs at the end, which is why we delay
|
||||
// `OutlivesConstraint` construction until now.
|
||||
loop {
|
||||
match context[p].clone() {
|
||||
Trace::NotVisited => {
|
||||
bug!("found unvisited region {:?} on path to {:?}", p, r)
|
||||
match context[p] {
|
||||
Trace::FromGraph(c) => {
|
||||
p = c.sup;
|
||||
result.push(*c);
|
||||
}
|
||||
|
||||
Trace::FromOutlivesConstraint(c) => {
|
||||
Trace::FromStatic(sub) => {
|
||||
let c = OutlivesConstraint {
|
||||
sup: fr_static,
|
||||
sub,
|
||||
locations: Locations::All(DUMMY_SP),
|
||||
span: DUMMY_SP,
|
||||
category: ConstraintCategory::Internal,
|
||||
variance_info: ty::VarianceDiagInfo::default(),
|
||||
from_closure: false,
|
||||
};
|
||||
p = c.sup;
|
||||
result.push(c);
|
||||
}
|
||||
|
||||
Trace::FromMember(sup, sub, span) => {
|
||||
let c = OutlivesConstraint {
|
||||
sup,
|
||||
sub,
|
||||
locations: Locations::All(span),
|
||||
span,
|
||||
category: ConstraintCategory::OpaqueType,
|
||||
variance_info: ty::VarianceDiagInfo::default(),
|
||||
from_closure: false,
|
||||
};
|
||||
p = c.sup;
|
||||
result.push(c);
|
||||
}
|
||||
|
|
@ -1798,6 +1828,10 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
result.reverse();
|
||||
return Some((result, r));
|
||||
}
|
||||
|
||||
Trace::NotVisited => {
|
||||
bug!("found unvisited region {:?} on path to {:?}", p, r)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1808,45 +1842,42 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
|
||||
// A constraint like `'r: 'x` can come from our constraint
|
||||
// graph.
|
||||
let fr_static = self.universal_regions().fr_static;
|
||||
let outgoing_edges_from_graph =
|
||||
self.constraint_graph.outgoing_edges(r, &self.constraints, fr_static);
|
||||
|
||||
// Always inline this closure because it can be hot.
|
||||
let mut handle_constraint = #[inline(always)]
|
||||
|constraint: OutlivesConstraint<'tcx>| {
|
||||
debug_assert_eq!(constraint.sup, r);
|
||||
let sub_region = constraint.sub;
|
||||
if let Trace::NotVisited = context[sub_region] {
|
||||
context[sub_region] = Trace::FromOutlivesConstraint(constraint);
|
||||
deque.push_back(sub_region);
|
||||
let mut handle_trace = #[inline(always)]
|
||||
|sub, trace| {
|
||||
if let Trace::NotVisited = context[sub] {
|
||||
context[sub] = trace;
|
||||
deque.push_back(sub);
|
||||
}
|
||||
};
|
||||
|
||||
// This loop can be hot.
|
||||
for constraint in outgoing_edges_from_graph {
|
||||
if matches!(constraint.category, ConstraintCategory::IllegalUniverse) {
|
||||
debug!("Ignoring illegal universe constraint: {constraint:?}");
|
||||
continue;
|
||||
// If this is the `'static` region and the graph's direction is normal, then set up the
|
||||
// Edges iterator to return all regions (#53178).
|
||||
if r == fr_static && self.constraint_graph.is_normal() {
|
||||
for sub in self.constraint_graph.outgoing_edges_from_static() {
|
||||
handle_trace(sub, Trace::FromStatic(sub));
|
||||
}
|
||||
} else {
|
||||
let edges = self.constraint_graph.outgoing_edges_from_graph(r, &self.constraints);
|
||||
// This loop can be hot.
|
||||
for constraint in edges {
|
||||
if matches!(constraint.category, ConstraintCategory::IllegalUniverse) {
|
||||
debug!("Ignoring illegal universe constraint: {constraint:?}");
|
||||
continue;
|
||||
}
|
||||
debug_assert_eq!(constraint.sup, r);
|
||||
handle_trace(constraint.sub, Trace::FromGraph(constraint));
|
||||
}
|
||||
handle_constraint(constraint);
|
||||
}
|
||||
|
||||
// Member constraints can also give rise to `'r: 'x` edges that
|
||||
// were not part of the graph initially, so watch out for those.
|
||||
// (But they are extremely rare; this loop is very cold.)
|
||||
for constraint in self.applied_member_constraints(self.constraint_sccs.scc(r)) {
|
||||
let sub = constraint.min_choice;
|
||||
let p_c = &self.member_constraints[constraint.member_constraint_index];
|
||||
let constraint = OutlivesConstraint {
|
||||
sup: r,
|
||||
sub: constraint.min_choice,
|
||||
locations: Locations::All(p_c.definition_span),
|
||||
span: p_c.definition_span,
|
||||
category: ConstraintCategory::OpaqueType,
|
||||
variance_info: ty::VarianceDiagInfo::default(),
|
||||
from_closure: false,
|
||||
};
|
||||
handle_constraint(constraint);
|
||||
handle_trace(sub, Trace::FromMember(r, sub, p_c.definition_span));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -459,17 +459,17 @@ pub(crate) enum OnClosureNote<'a> {
|
|||
}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
pub(crate) enum TypeNoCopy<'a> {
|
||||
pub(crate) enum TypeNoCopy<'a, 'tcx> {
|
||||
#[label(borrowck_ty_no_impl_copy)]
|
||||
Label {
|
||||
is_partial_move: bool,
|
||||
ty: String,
|
||||
ty: Ty<'tcx>,
|
||||
place: &'a str,
|
||||
#[primary_span]
|
||||
span: Span,
|
||||
},
|
||||
#[note(borrowck_ty_no_impl_copy)]
|
||||
Note { is_partial_move: bool, ty: String, place: &'a str },
|
||||
Note { is_partial_move: bool, ty: Ty<'tcx>, place: &'a str },
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::def_id::LocalDefId;
|
||||
use rustc_infer::infer::canonical::QueryRegionConstraints;
|
||||
use rustc_infer::infer::outlives::env::RegionBoundPairs;
|
||||
use rustc_infer::infer::outlives::obligations::{TypeOutlives, TypeOutlivesDelegate};
|
||||
|
|
@ -88,7 +88,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
|
|||
pub(crate) fn apply_closure_requirements(
|
||||
&mut self,
|
||||
closure_requirements: &ClosureRegionRequirements<'tcx>,
|
||||
closure_def_id: DefId,
|
||||
closure_def_id: LocalDefId,
|
||||
closure_args: ty::GenericArgsRef<'tcx>,
|
||||
) {
|
||||
// Extract the values of the free regions in `closure_args`
|
||||
|
|
@ -98,7 +98,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
|
|||
self.tcx,
|
||||
closure_args,
|
||||
closure_requirements.num_external_vids,
|
||||
closure_def_id.expect_local(),
|
||||
closure_def_id,
|
||||
);
|
||||
debug!(?closure_mapping);
|
||||
|
||||
|
|
|
|||
|
|
@ -24,9 +24,9 @@ use crate::universal_regions::DefiningTy;
|
|||
impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||
/// Check explicit closure signature annotation,
|
||||
/// e.g., `|x: FxIndexMap<_, &'static u32>| ...`.
|
||||
#[instrument(skip(self, body), level = "debug")]
|
||||
pub(super) fn check_signature_annotation(&mut self, body: &Body<'tcx>) {
|
||||
let mir_def_id = body.source.def_id().expect_local();
|
||||
#[instrument(skip(self), level = "debug")]
|
||||
pub(super) fn check_signature_annotation(&mut self) {
|
||||
let mir_def_id = self.body.source.def_id().expect_local();
|
||||
|
||||
if !self.tcx().is_closure_like(mir_def_id.to_def_id()) {
|
||||
return;
|
||||
|
|
@ -38,9 +38,9 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
|||
// (e.g., the `_` in the code above) with fresh variables.
|
||||
// Then replace the bound items in the fn sig with fresh variables,
|
||||
// so that they represent the view from "inside" the closure.
|
||||
let user_provided_sig = self.instantiate_canonical(body.span, &user_provided_poly_sig);
|
||||
let user_provided_sig = self.instantiate_canonical(self.body.span, &user_provided_poly_sig);
|
||||
let mut user_provided_sig = self.infcx.instantiate_binder_with_fresh_vars(
|
||||
body.span,
|
||||
self.body.span,
|
||||
BoundRegionConversionTime::FnCall,
|
||||
user_provided_sig,
|
||||
);
|
||||
|
|
@ -66,12 +66,13 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
|||
Ty::new_tup(self.tcx(), user_provided_sig.inputs()),
|
||||
args.tupled_upvars_ty(),
|
||||
args.coroutine_captures_by_ref_ty(),
|
||||
self.infcx.next_region_var(RegionVariableOrigin::MiscVariable(body.span), || {
|
||||
RegionCtxt::Unknown
|
||||
}),
|
||||
self.infcx
|
||||
.next_region_var(RegionVariableOrigin::MiscVariable(self.body.span), || {
|
||||
RegionCtxt::Unknown
|
||||
}),
|
||||
);
|
||||
|
||||
let next_ty_var = || self.infcx.next_ty_var(body.span);
|
||||
let next_ty_var = || self.infcx.next_ty_var(self.body.span);
|
||||
let output_ty = Ty::new_coroutine(
|
||||
self.tcx(),
|
||||
self.tcx().coroutine_for_closure(mir_def_id),
|
||||
|
|
@ -107,9 +108,10 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
|||
for (&user_ty, arg_decl) in user_provided_sig.inputs().iter().zip_eq(
|
||||
// In MIR, closure args begin with an implicit `self`.
|
||||
// Also, coroutines have a resume type which may be implicitly `()`.
|
||||
body.args_iter()
|
||||
self.body
|
||||
.args_iter()
|
||||
.skip(1 + if is_coroutine_with_implicit_resume_ty { 1 } else { 0 })
|
||||
.map(|local| &body.local_decls[local]),
|
||||
.map(|local| &self.body.local_decls[local]),
|
||||
) {
|
||||
self.ascribe_user_type_skip_wf(
|
||||
arg_decl.ty,
|
||||
|
|
@ -119,7 +121,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
|||
}
|
||||
|
||||
// If the user explicitly annotated the output type, enforce it.
|
||||
let output_decl = &body.local_decls[RETURN_PLACE];
|
||||
let output_decl = &self.body.local_decls[RETURN_PLACE];
|
||||
self.ascribe_user_type_skip_wf(
|
||||
output_decl.ty,
|
||||
ty::UserType::new(ty::UserTypeKind::Ty(user_provided_sig.output())),
|
||||
|
|
@ -127,12 +129,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
|||
);
|
||||
}
|
||||
|
||||
#[instrument(skip(self, body), level = "debug")]
|
||||
pub(super) fn equate_inputs_and_outputs(
|
||||
&mut self,
|
||||
body: &Body<'tcx>,
|
||||
normalized_inputs_and_output: &[Ty<'tcx>],
|
||||
) {
|
||||
#[instrument(skip(self), level = "debug")]
|
||||
pub(super) fn equate_inputs_and_outputs(&mut self, normalized_inputs_and_output: &[Ty<'tcx>]) {
|
||||
let (&normalized_output_ty, normalized_input_tys) =
|
||||
normalized_inputs_and_output.split_last().unwrap();
|
||||
|
||||
|
|
@ -141,18 +139,18 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
|||
|
||||
// Equate expected input tys with those in the MIR.
|
||||
for (argument_index, &normalized_input_ty) in normalized_input_tys.iter().enumerate() {
|
||||
if argument_index + 1 >= body.local_decls.len() {
|
||||
if argument_index + 1 >= self.body.local_decls.len() {
|
||||
self.tcx()
|
||||
.dcx()
|
||||
.span_bug(body.span, "found more normalized_input_ty than local_decls");
|
||||
.span_bug(self.body.span, "found more normalized_input_ty than local_decls");
|
||||
}
|
||||
|
||||
// In MIR, argument N is stored in local N+1.
|
||||
let local = Local::from_usize(argument_index + 1);
|
||||
|
||||
let mir_input_ty = body.local_decls[local].ty;
|
||||
let mir_input_ty = self.body.local_decls[local].ty;
|
||||
|
||||
let mir_input_span = body.local_decls[local].source_info.span;
|
||||
let mir_input_span = self.body.local_decls[local].source_info.span;
|
||||
self.equate_normalized_input_or_output(
|
||||
normalized_input_ty,
|
||||
mir_input_ty,
|
||||
|
|
@ -160,8 +158,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
|||
);
|
||||
}
|
||||
|
||||
if let Some(mir_yield_ty) = body.yield_ty() {
|
||||
let yield_span = body.local_decls[RETURN_PLACE].source_info.span;
|
||||
if let Some(mir_yield_ty) = self.body.yield_ty() {
|
||||
let yield_span = self.body.local_decls[RETURN_PLACE].source_info.span;
|
||||
self.equate_normalized_input_or_output(
|
||||
self.universal_regions.yield_ty.unwrap(),
|
||||
mir_yield_ty,
|
||||
|
|
@ -169,8 +167,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
|||
);
|
||||
}
|
||||
|
||||
if let Some(mir_resume_ty) = body.resume_ty() {
|
||||
let yield_span = body.local_decls[RETURN_PLACE].source_info.span;
|
||||
if let Some(mir_resume_ty) = self.body.resume_ty() {
|
||||
let yield_span = self.body.local_decls[RETURN_PLACE].source_info.span;
|
||||
self.equate_normalized_input_or_output(
|
||||
self.universal_regions.resume_ty.unwrap(),
|
||||
mir_resume_ty,
|
||||
|
|
@ -179,8 +177,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
|||
}
|
||||
|
||||
// Return types are a bit more complex. They may contain opaque `impl Trait` types.
|
||||
let mir_output_ty = body.local_decls[RETURN_PLACE].ty;
|
||||
let output_span = body.local_decls[RETURN_PLACE].source_info.span;
|
||||
let mir_output_ty = self.body.local_decls[RETURN_PLACE].ty;
|
||||
let output_span = self.body.local_decls[RETURN_PLACE].source_info.span;
|
||||
self.equate_normalized_input_or_output(normalized_output_ty, mir_output_ty, output_span);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -31,7 +31,6 @@ mod trace;
|
|||
/// performed before
|
||||
pub(super) fn generate<'a, 'tcx>(
|
||||
typeck: &mut TypeChecker<'_, 'tcx>,
|
||||
body: &Body<'tcx>,
|
||||
location_map: &DenseLocationMap,
|
||||
flow_inits: ResultsCursor<'a, 'tcx, MaybeInitializedPlaces<'a, 'tcx>>,
|
||||
move_data: &MoveData<'tcx>,
|
||||
|
|
@ -51,23 +50,16 @@ pub(super) fn generate<'a, 'tcx>(
|
|||
// We do record these regions in the polonius context, since they're used to differentiate
|
||||
// relevant and boring locals, which is a key distinction used later in diagnostics.
|
||||
if typeck.tcx().sess.opts.unstable_opts.polonius.is_next_enabled() {
|
||||
let (_, boring_locals) = compute_relevant_live_locals(typeck.tcx(), &free_regions, body);
|
||||
let (_, boring_locals) =
|
||||
compute_relevant_live_locals(typeck.tcx(), &free_regions, typeck.body);
|
||||
typeck.polonius_liveness.as_mut().unwrap().boring_nll_locals =
|
||||
boring_locals.into_iter().collect();
|
||||
free_regions = typeck.universal_regions.universal_regions_iter().collect();
|
||||
}
|
||||
let (relevant_live_locals, boring_locals) =
|
||||
compute_relevant_live_locals(typeck.tcx(), &free_regions, body);
|
||||
compute_relevant_live_locals(typeck.tcx(), &free_regions, typeck.body);
|
||||
|
||||
trace::trace(
|
||||
typeck,
|
||||
body,
|
||||
location_map,
|
||||
flow_inits,
|
||||
move_data,
|
||||
relevant_live_locals,
|
||||
boring_locals,
|
||||
);
|
||||
trace::trace(typeck, location_map, flow_inits, 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.
|
||||
|
|
@ -76,7 +68,7 @@ pub(super) fn generate<'a, 'tcx>(
|
|||
&mut typeck.constraints.liveness_constraints,
|
||||
&typeck.universal_regions,
|
||||
&mut typeck.polonius_liveness,
|
||||
body,
|
||||
typeck.body,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -39,17 +39,15 @@ use crate::type_check::{NormalizeLocation, TypeChecker};
|
|||
/// this respects `#[may_dangle]` annotations).
|
||||
pub(super) fn trace<'a, 'tcx>(
|
||||
typeck: &mut TypeChecker<'_, 'tcx>,
|
||||
body: &Body<'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>,
|
||||
) {
|
||||
let local_use_map = &LocalUseMap::build(&relevant_live_locals, location_map, body);
|
||||
let local_use_map = &LocalUseMap::build(&relevant_live_locals, location_map, typeck.body);
|
||||
let cx = LivenessContext {
|
||||
typeck,
|
||||
body,
|
||||
flow_inits,
|
||||
location_map,
|
||||
local_use_map,
|
||||
|
|
@ -69,14 +67,13 @@ pub(super) fn trace<'a, 'tcx>(
|
|||
/// Contextual state for the type-liveness coroutine.
|
||||
struct LivenessContext<'a, 'typeck, 'b, 'tcx> {
|
||||
/// Current type-checker, giving us our inference context etc.
|
||||
///
|
||||
/// This also stores the body we're currently analyzing.
|
||||
typeck: &'a mut TypeChecker<'typeck, 'tcx>,
|
||||
|
||||
/// Defines the `PointIndex` mapping
|
||||
location_map: &'a DenseLocationMap,
|
||||
|
||||
/// MIR we are analyzing.
|
||||
body: &'a Body<'tcx>,
|
||||
|
||||
/// Mapping to/from the various indices used for initialization tracking.
|
||||
move_data: &'a MoveData<'tcx>,
|
||||
|
||||
|
|
@ -139,7 +136,7 @@ impl<'a, 'typeck, 'b, 'tcx> LivenessResults<'a, 'typeck, 'b, 'tcx> {
|
|||
self.compute_use_live_points_for(local);
|
||||
self.compute_drop_live_points_for(local);
|
||||
|
||||
let local_ty = self.cx.body.local_decls[local].ty;
|
||||
let local_ty = self.cx.body().local_decls[local].ty;
|
||||
|
||||
if !self.use_live_at.is_empty() {
|
||||
self.cx.add_use_live_facts_for(local_ty, &self.use_live_at);
|
||||
|
|
@ -164,8 +161,8 @@ impl<'a, 'typeck, 'b, 'tcx> LivenessResults<'a, 'typeck, 'b, 'tcx> {
|
|||
/// and can therefore safely be dropped.
|
||||
fn dropck_boring_locals(&mut self, boring_locals: Vec<Local>) {
|
||||
for local in boring_locals {
|
||||
let local_ty = self.cx.body.local_decls[local].ty;
|
||||
let local_span = self.cx.body.local_decls[local].source_info.span;
|
||||
let local_ty = self.cx.body().local_decls[local].ty;
|
||||
let local_span = self.cx.body().local_decls[local].source_info.span;
|
||||
let drop_data = self.cx.drop_data.entry(local_ty).or_insert_with({
|
||||
let typeck = &self.cx.typeck;
|
||||
move || LivenessContext::compute_drop_data(typeck, local_ty, local_span)
|
||||
|
|
@ -173,7 +170,7 @@ impl<'a, 'typeck, 'b, 'tcx> LivenessResults<'a, 'typeck, 'b, 'tcx> {
|
|||
|
||||
drop_data.dropck_result.report_overflows(
|
||||
self.cx.typeck.infcx.tcx,
|
||||
self.cx.body.local_decls[local].source_info.span,
|
||||
self.cx.typeck.body.local_decls[local].source_info.span,
|
||||
local_ty,
|
||||
);
|
||||
}
|
||||
|
|
@ -202,7 +199,7 @@ impl<'a, 'typeck, 'b, 'tcx> LivenessResults<'a, 'typeck, 'b, 'tcx> {
|
|||
.var_dropped_at
|
||||
.iter()
|
||||
.filter_map(|&(local, location_index)| {
|
||||
let local_ty = self.cx.body.local_decls[local].ty;
|
||||
let local_ty = self.cx.body().local_decls[local].ty;
|
||||
if relevant_live_locals.contains(&local) || !local_ty.has_free_regions() {
|
||||
return None;
|
||||
}
|
||||
|
|
@ -278,9 +275,9 @@ impl<'a, 'typeck, 'b, 'tcx> LivenessResults<'a, 'typeck, 'b, 'tcx> {
|
|||
|
||||
let block = self.cx.location_map.to_location(block_start).block;
|
||||
self.stack.extend(
|
||||
self.cx.body.basic_blocks.predecessors()[block]
|
||||
self.cx.body().basic_blocks.predecessors()[block]
|
||||
.iter()
|
||||
.map(|&pred_bb| self.cx.body.terminator_loc(pred_bb))
|
||||
.map(|&pred_bb| self.cx.body().terminator_loc(pred_bb))
|
||||
.map(|pred_loc| self.cx.location_map.point_from_location(pred_loc)),
|
||||
);
|
||||
}
|
||||
|
|
@ -305,7 +302,7 @@ impl<'a, 'typeck, 'b, 'tcx> LivenessResults<'a, 'typeck, 'b, 'tcx> {
|
|||
// Find the drops where `local` is initialized.
|
||||
for drop_point in self.cx.local_use_map.drops(local) {
|
||||
let location = self.cx.location_map.to_location(drop_point);
|
||||
debug_assert_eq!(self.cx.body.terminator_loc(location.block), location,);
|
||||
debug_assert_eq!(self.cx.body().terminator_loc(location.block), location,);
|
||||
|
||||
if self.cx.initialized_at_terminator(location.block, mpi)
|
||||
&& self.drop_live_at.insert(drop_point)
|
||||
|
|
@ -351,7 +348,7 @@ impl<'a, 'typeck, 'b, 'tcx> LivenessResults<'a, 'typeck, 'b, 'tcx> {
|
|||
// block. One of them may be either a definition or use
|
||||
// live point.
|
||||
let term_location = self.cx.location_map.to_location(term_point);
|
||||
debug_assert_eq!(self.cx.body.terminator_loc(term_location.block), term_location,);
|
||||
debug_assert_eq!(self.cx.body().terminator_loc(term_location.block), term_location,);
|
||||
let block = term_location.block;
|
||||
let entry_point = self.cx.location_map.entry_point(term_location.block);
|
||||
for p in (entry_point..term_point).rev() {
|
||||
|
|
@ -376,7 +373,7 @@ impl<'a, 'typeck, 'b, 'tcx> LivenessResults<'a, 'typeck, 'b, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
let body = self.cx.body;
|
||||
let body = self.cx.typeck.body;
|
||||
for &pred_block in body.basic_blocks.predecessors()[block].iter() {
|
||||
debug!("compute_drop_live_points_for_block: pred_block = {:?}", pred_block,);
|
||||
|
||||
|
|
@ -403,7 +400,7 @@ impl<'a, 'typeck, 'b, 'tcx> LivenessResults<'a, 'typeck, 'b, 'tcx> {
|
|||
continue;
|
||||
}
|
||||
|
||||
let pred_term_loc = self.cx.body.terminator_loc(pred_block);
|
||||
let pred_term_loc = self.cx.body().terminator_loc(pred_block);
|
||||
let pred_term_point = self.cx.location_map.point_from_location(pred_term_loc);
|
||||
|
||||
// If the terminator of this predecessor either *assigns*
|
||||
|
|
@ -463,6 +460,9 @@ impl<'a, 'typeck, 'b, 'tcx> LivenessResults<'a, 'typeck, 'b, 'tcx> {
|
|||
}
|
||||
|
||||
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.
|
||||
|
|
@ -481,7 +481,7 @@ 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));
|
||||
self.flow_inits.seek_before_primary_effect(self.body().terminator_loc(block));
|
||||
self.initialized_at_curr_loc(mpi)
|
||||
}
|
||||
|
||||
|
|
@ -491,7 +491,7 @@ 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));
|
||||
self.flow_inits.seek_after_primary_effect(self.body().terminator_loc(block));
|
||||
self.initialized_at_curr_loc(mpi)
|
||||
}
|
||||
|
||||
|
|
@ -526,7 +526,7 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> {
|
|||
values::pretty_print_points(self.location_map, live_at.iter()),
|
||||
);
|
||||
|
||||
let local_span = self.body.local_decls()[dropped_local].source_info.span;
|
||||
let local_span = self.body().local_decls()[dropped_local].source_info.span;
|
||||
let drop_data = self.drop_data.entry(dropped_ty).or_insert_with({
|
||||
let typeck = &self.typeck;
|
||||
move || Self::compute_drop_data(typeck, dropped_ty, local_span)
|
||||
|
|
@ -544,7 +544,7 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> {
|
|||
|
||||
drop_data.dropck_result.report_overflows(
|
||||
self.typeck.infcx.tcx,
|
||||
self.body.source_info(*drop_locations.first().unwrap()).span,
|
||||
self.typeck.body.source_info(*drop_locations.first().unwrap()).span,
|
||||
dropped_ty,
|
||||
);
|
||||
|
||||
|
|
@ -610,7 +610,7 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> {
|
|||
Err(ErrorGuaranteed { .. }) => {
|
||||
// We don't run dropck on HIR, and dropck looks inside fields of
|
||||
// types, so there's no guarantee that it succeeds. We also
|
||||
// can't rely on the the `ErrorGuaranteed` from `fully_perform` here
|
||||
// can't rely on the `ErrorGuaranteed` from `fully_perform` here
|
||||
// because it comes from delay_span_bug.
|
||||
//
|
||||
// Do this inside of a probe because we don't particularly care (or want)
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -180,6 +180,7 @@ pub(super) fn take_opaques_and_register_member_constraints<'tcx>(
|
|||
/// // Equivalent to:
|
||||
/// # mod dummy { use super::*;
|
||||
/// type FooReturn<'a, T> = impl Foo<'a>;
|
||||
/// #[define_opaque(FooReturn)]
|
||||
/// fn foo<'a, T>(x: &'a u32, y: T) -> FooReturn<'a, T> {
|
||||
/// (x, y)
|
||||
/// }
|
||||
|
|
|
|||
|
|
@ -3,10 +3,6 @@ name = "rustc_builtin_macros"
|
|||
version = "0.0.0"
|
||||
edition = "2024"
|
||||
|
||||
|
||||
[lints.rust]
|
||||
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(llvm_enzyme)'] }
|
||||
|
||||
[lib]
|
||||
doctest = false
|
||||
|
||||
|
|
|
|||
|
|
@ -75,9 +75,10 @@ builtin_macros_autodiff_mode = unknown Mode: `{$mode}`. Use `Forward` or `Revers
|
|||
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}
|
||||
builtin_macros_autodiff_ret_activity = invalid return activity {$act} in {$mode} Mode
|
||||
builtin_macros_autodiff_ty_activity = {$act} can not be used for this type
|
||||
|
||||
builtin_macros_autodiff_unknown_activity = did not recognize Activity: `{$act}`
|
||||
|
||||
builtin_macros_bad_derive_target = `derive` may only be applied to `struct`s, `enum`s and `union`s
|
||||
.label = not applicable here
|
||||
.label2 = not a `struct`, `enum` or `union`
|
||||
|
|
|
|||
|
|
@ -88,6 +88,7 @@ fn generate_handler(cx: &ExtCtxt<'_>, handler: Ident, span: Span, sig_span: Span
|
|||
generics: Generics::default(),
|
||||
contract: None,
|
||||
body,
|
||||
define_opaque: None,
|
||||
}));
|
||||
|
||||
let attrs = thin_vec![cx.attr_word(sym::rustc_std_internal_symbol, span)];
|
||||
|
|
|
|||
|
|
@ -297,6 +297,7 @@ impl<'cx, 'a> Context<'cx, 'a> {
|
|||
| ExprKind::AssignOp(_, _, _)
|
||||
| ExprKind::Gen(_, _, _, _)
|
||||
| ExprKind::Await(_, _)
|
||||
| ExprKind::Use(_, _)
|
||||
| ExprKind::Block(_, _)
|
||||
| ExprKind::Break(_, _)
|
||||
| ExprKind::Closure(_)
|
||||
|
|
|
|||
|
|
@ -3,13 +3,13 @@
|
|||
//! configs (autodiff enabled or disabled), so we have to add cfg's to each import.
|
||||
//! FIXME(ZuseZ4): Remove this once we have a smarter linter.
|
||||
|
||||
#[cfg(llvm_enzyme)]
|
||||
mod llvm_enzyme {
|
||||
use std::str::FromStr;
|
||||
use std::string::String;
|
||||
|
||||
use rustc_ast::expand::autodiff_attrs::{
|
||||
AutoDiffAttrs, DiffActivity, DiffMode, valid_input_activity, valid_ty_for_activity,
|
||||
AutoDiffAttrs, DiffActivity, DiffMode, valid_input_activity, valid_ret_activity,
|
||||
valid_ty_for_activity,
|
||||
};
|
||||
use rustc_ast::ptr::P;
|
||||
use rustc_ast::token::{Token, TokenKind};
|
||||
|
|
@ -130,10 +130,14 @@ mod llvm_enzyme {
|
|||
meta_item: &ast::MetaItem,
|
||||
mut item: Annotatable,
|
||||
) -> Vec<Annotatable> {
|
||||
if cfg!(not(llvm_enzyme)) {
|
||||
ecx.sess.dcx().emit_err(errors::AutoDiffSupportNotBuild { span: meta_item.span });
|
||||
return vec![item];
|
||||
}
|
||||
let dcx = ecx.sess.dcx();
|
||||
// first get the annotable item:
|
||||
let (sig, is_impl): (FnSig, bool) = match &item {
|
||||
Annotatable::Item(ref iitem) => {
|
||||
Annotatable::Item(iitem) => {
|
||||
let sig = match &iitem.kind {
|
||||
ItemKind::Fn(box ast::Fn { sig, .. }) => sig,
|
||||
_ => {
|
||||
|
|
@ -143,7 +147,7 @@ mod llvm_enzyme {
|
|||
};
|
||||
(sig.clone(), false)
|
||||
}
|
||||
Annotatable::AssocItem(ref assoc_item, _) => {
|
||||
Annotatable::AssocItem(assoc_item, _) => {
|
||||
let sig = match &assoc_item.kind {
|
||||
ast::AssocItemKind::Fn(box ast::Fn { sig, .. }) => sig,
|
||||
_ => {
|
||||
|
|
@ -171,8 +175,8 @@ mod llvm_enzyme {
|
|||
let sig_span = ecx.with_call_site_ctxt(sig.span);
|
||||
|
||||
let (vis, primal) = match &item {
|
||||
Annotatable::Item(ref iitem) => (iitem.vis.clone(), iitem.ident.clone()),
|
||||
Annotatable::AssocItem(ref assoc_item, _) => {
|
||||
Annotatable::Item(iitem) => (iitem.vis.clone(), iitem.ident.clone()),
|
||||
Annotatable::AssocItem(assoc_item, _) => {
|
||||
(assoc_item.vis.clone(), assoc_item.ident.clone())
|
||||
}
|
||||
_ => {
|
||||
|
|
@ -244,6 +248,7 @@ mod llvm_enzyme {
|
|||
generics: Generics::default(),
|
||||
contract: None,
|
||||
body: Some(d_body),
|
||||
define_opaque: None,
|
||||
});
|
||||
let mut rustc_ad_attr =
|
||||
P(ast::NormalAttr::from_ident(Ident::with_dummy_span(sym::rustc_autodiff)));
|
||||
|
|
@ -283,7 +288,7 @@ mod llvm_enzyme {
|
|||
let orig_annotatable: Annotatable = match item {
|
||||
Annotatable::Item(ref mut iitem) => {
|
||||
if !iitem.attrs.iter().any(|a| a.id == attr.id) {
|
||||
iitem.attrs.push(attr.clone());
|
||||
iitem.attrs.push(attr);
|
||||
}
|
||||
if !iitem.attrs.iter().any(|a| a.id == inline_never.id) {
|
||||
iitem.attrs.push(inline_never.clone());
|
||||
|
|
@ -292,7 +297,7 @@ mod llvm_enzyme {
|
|||
}
|
||||
Annotatable::AssocItem(ref mut assoc_item, i @ Impl) => {
|
||||
if !assoc_item.attrs.iter().any(|a| a.id == attr.id) {
|
||||
assoc_item.attrs.push(attr.clone());
|
||||
assoc_item.attrs.push(attr);
|
||||
}
|
||||
if !assoc_item.attrs.iter().any(|a| a.id == inline_never.id) {
|
||||
assoc_item.attrs.push(inline_never.clone());
|
||||
|
|
@ -319,7 +324,7 @@ mod llvm_enzyme {
|
|||
let d_annotatable = if is_impl {
|
||||
let assoc_item: AssocItemKind = ast::AssocItemKind::Fn(asdf);
|
||||
let d_fn = P(ast::AssocItem {
|
||||
attrs: thin_vec![d_attr.clone(), inline_never],
|
||||
attrs: thin_vec![d_attr, inline_never],
|
||||
id: ast::DUMMY_NODE_ID,
|
||||
span,
|
||||
vis,
|
||||
|
|
@ -329,12 +334,8 @@ mod llvm_enzyme {
|
|||
});
|
||||
Annotatable::AssocItem(d_fn, Impl)
|
||||
} else {
|
||||
let mut d_fn = ecx.item(
|
||||
span,
|
||||
d_ident,
|
||||
thin_vec![d_attr.clone(), inline_never],
|
||||
ItemKind::Fn(asdf),
|
||||
);
|
||||
let mut d_fn =
|
||||
ecx.item(span, d_ident, thin_vec![d_attr, inline_never], ItemKind::Fn(asdf));
|
||||
d_fn.vis = vis;
|
||||
Annotatable::Item(d_fn)
|
||||
};
|
||||
|
|
@ -443,7 +444,7 @@ mod llvm_enzyme {
|
|||
|
||||
if primal_ret && n_active == 0 && x.mode.is_rev() {
|
||||
// We only have the primal ret.
|
||||
body.stmts.push(ecx.stmt_expr(black_box_primal_call.clone()));
|
||||
body.stmts.push(ecx.stmt_expr(black_box_primal_call));
|
||||
return body;
|
||||
}
|
||||
|
||||
|
|
@ -468,7 +469,7 @@ mod llvm_enzyme {
|
|||
if primal_ret {
|
||||
// We have both primal ret and active floats.
|
||||
// primal ret is first, by construction.
|
||||
exprs.push(primal_call.clone());
|
||||
exprs.push(primal_call);
|
||||
}
|
||||
|
||||
// Now construct default placeholder for each active float.
|
||||
|
|
@ -535,16 +536,11 @@ mod llvm_enzyme {
|
|||
return body;
|
||||
}
|
||||
[arg] => {
|
||||
ret = ecx.expr_call(
|
||||
new_decl_span,
|
||||
blackbox_call_expr.clone(),
|
||||
thin_vec![arg.clone()],
|
||||
);
|
||||
ret = ecx.expr_call(new_decl_span, blackbox_call_expr, thin_vec![arg.clone()]);
|
||||
}
|
||||
args => {
|
||||
let ret_tuple: P<ast::Expr> = ecx.expr_tuple(span, args.into());
|
||||
ret =
|
||||
ecx.expr_call(new_decl_span, blackbox_call_expr.clone(), thin_vec![ret_tuple]);
|
||||
ret = ecx.expr_call(new_decl_span, blackbox_call_expr, thin_vec![ret_tuple]);
|
||||
}
|
||||
}
|
||||
assert!(has_ret(&d_sig.decl.output));
|
||||
|
|
@ -564,7 +560,7 @@ mod llvm_enzyme {
|
|||
let args: ThinVec<_> =
|
||||
idents[1..].iter().map(|arg| ecx.expr_path(ecx.path_ident(span, *arg))).collect();
|
||||
let self_expr = ecx.expr_self(span);
|
||||
ecx.expr_method_call(span, self_expr, primal, args.clone())
|
||||
ecx.expr_method_call(span, self_expr, primal, args)
|
||||
} else {
|
||||
let args: ThinVec<_> =
|
||||
idents.iter().map(|arg| ecx.expr_path(ecx.path_ident(span, *arg))).collect();
|
||||
|
|
@ -582,6 +578,8 @@ mod llvm_enzyme {
|
|||
//
|
||||
// Error handling: If the user provides an invalid configuration (incorrect numbers, types, or
|
||||
// both), we emit an error and return the original signature. This allows us to continue parsing.
|
||||
// FIXME(Sa4dUs): make individual activities' span available so errors
|
||||
// can point to only the activity instead of the entire attribute
|
||||
fn gen_enzyme_decl(
|
||||
ecx: &ExtCtxt<'_>,
|
||||
sig: &ast::FnSig,
|
||||
|
|
@ -629,10 +627,22 @@ mod llvm_enzyme {
|
|||
errors = true;
|
||||
}
|
||||
}
|
||||
|
||||
if has_ret && !valid_ret_activity(x.mode, x.ret_activity) {
|
||||
dcx.emit_err(errors::AutoDiffInvalidRetAct {
|
||||
span,
|
||||
mode: x.mode.to_string(),
|
||||
act: x.ret_activity.to_string(),
|
||||
});
|
||||
// We don't set `errors = true` to avoid annoying type errors relative
|
||||
// to the expanded macro type signature
|
||||
}
|
||||
|
||||
if errors {
|
||||
// This is not the right signature, but we can continue parsing.
|
||||
return (sig.clone(), new_inputs, idents, true);
|
||||
}
|
||||
|
||||
let unsafe_activities = x
|
||||
.input_activity
|
||||
.iter()
|
||||
|
|
@ -801,25 +811,4 @@ mod llvm_enzyme {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(not(llvm_enzyme))]
|
||||
mod ad_fallback {
|
||||
use rustc_ast::ast;
|
||||
use rustc_expand::base::{Annotatable, ExtCtxt};
|
||||
use rustc_span::Span;
|
||||
|
||||
use crate::errors;
|
||||
pub(crate) fn expand(
|
||||
ecx: &mut ExtCtxt<'_>,
|
||||
_expand_span: Span,
|
||||
meta_item: &ast::MetaItem,
|
||||
item: Annotatable,
|
||||
) -> Vec<Annotatable> {
|
||||
ecx.sess.dcx().emit_err(errors::AutoDiffSupportNotBuild { span: meta_item.span });
|
||||
return vec![item];
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(llvm_enzyme))]
|
||||
pub(crate) use ad_fallback::expand;
|
||||
#[cfg(llvm_enzyme)]
|
||||
pub(crate) use llvm_enzyme::expand;
|
||||
|
|
|
|||
|
|
@ -140,8 +140,9 @@ impl CfgEval<'_> {
|
|||
Annotatable::ForeignItem(self.flat_map_foreign_item(item).pop().unwrap())
|
||||
}
|
||||
Annotatable::Stmt(_) => {
|
||||
let stmt =
|
||||
parser.parse_stmt_without_recovery(false, ForceCollect::Yes)?.unwrap();
|
||||
let stmt = parser
|
||||
.parse_stmt_without_recovery(false, ForceCollect::Yes, false)?
|
||||
.unwrap();
|
||||
Annotatable::Stmt(P(self.flat_map_stmt(stmt).pop().unwrap()))
|
||||
}
|
||||
Annotatable::Expr(_) => {
|
||||
|
|
|
|||
54
compiler/rustc_builtin_macros/src/define_opaque.rs
Normal file
54
compiler/rustc_builtin_macros/src/define_opaque.rs
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
use rustc_ast::{DUMMY_NODE_ID, ast};
|
||||
use rustc_expand::base::{Annotatable, ExtCtxt};
|
||||
use rustc_span::Span;
|
||||
|
||||
pub(crate) fn expand(
|
||||
ecx: &mut ExtCtxt<'_>,
|
||||
_expand_span: Span,
|
||||
meta_item: &ast::MetaItem,
|
||||
mut item: Annotatable,
|
||||
) -> Vec<Annotatable> {
|
||||
let define_opaque = match &mut item {
|
||||
Annotatable::Item(p) => match &mut p.kind {
|
||||
ast::ItemKind::Fn(f) => Some(&mut f.define_opaque),
|
||||
_ => None,
|
||||
},
|
||||
Annotatable::AssocItem(i, _assoc_ctxt) => match &mut i.kind {
|
||||
ast::AssocItemKind::Fn(func) => Some(&mut func.define_opaque),
|
||||
_ => None,
|
||||
},
|
||||
Annotatable::Stmt(s) => match &mut s.kind {
|
||||
ast::StmtKind::Item(p) => match &mut p.kind {
|
||||
ast::ItemKind::Fn(f) => Some(&mut f.define_opaque),
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let Some(list) = meta_item.meta_item_list() else {
|
||||
ecx.dcx().span_err(meta_item.span, "expected list of type aliases");
|
||||
return vec![item];
|
||||
};
|
||||
|
||||
if let Some(define_opaque) = define_opaque {
|
||||
*define_opaque = Some(
|
||||
list.iter()
|
||||
.filter_map(|entry| match entry {
|
||||
ast::MetaItemInner::MetaItem(meta_item) if meta_item.is_word() => {
|
||||
Some((DUMMY_NODE_ID, meta_item.path.clone()))
|
||||
}
|
||||
_ => {
|
||||
ecx.dcx().span_err(entry.span(), "expected path to type alias");
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect(),
|
||||
);
|
||||
} else {
|
||||
ecx.dcx().span_err(meta_item.span, "only functions and methods can define opaque types");
|
||||
}
|
||||
|
||||
vec![item]
|
||||
}
|
||||
|
|
@ -300,13 +300,16 @@ pub(crate) fn expand_deriving_coerce_pointee(
|
|||
to_ty: &s_ty,
|
||||
rewritten: false,
|
||||
};
|
||||
let mut predicate = ast::WherePredicate {
|
||||
kind: ast::WherePredicateKind::BoundPredicate(bound.clone()),
|
||||
span: predicate.span,
|
||||
id: ast::DUMMY_NODE_ID,
|
||||
};
|
||||
substitution.visit_where_predicate(&mut predicate);
|
||||
let mut kind = ast::WherePredicateKind::BoundPredicate(bound.clone());
|
||||
substitution.visit_where_predicate_kind(&mut kind);
|
||||
if substitution.rewritten {
|
||||
let predicate = ast::WherePredicate {
|
||||
attrs: predicate.attrs.clone(),
|
||||
kind,
|
||||
span: predicate.span,
|
||||
id: ast::DUMMY_NODE_ID,
|
||||
is_placeholder: false,
|
||||
};
|
||||
impl_generics.where_clause.predicates.push(predicate);
|
||||
}
|
||||
}
|
||||
|
|
@ -388,8 +391,8 @@ impl<'a> ast::mut_visit::MutVisitor for TypeSubstitution<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn visit_where_predicate(&mut self, where_predicate: &mut ast::WherePredicate) {
|
||||
match &mut where_predicate.kind {
|
||||
fn visit_where_predicate_kind(&mut self, kind: &mut ast::WherePredicateKind) {
|
||||
match kind {
|
||||
rustc_ast::WherePredicateKind::BoundPredicate(bound) => {
|
||||
bound
|
||||
.bound_generic_params
|
||||
|
|
|
|||
|
|
@ -687,9 +687,11 @@ impl<'a> TraitDef<'a> {
|
|||
// and similarly for where clauses
|
||||
where_clause.predicates.extend(generics.where_clause.predicates.iter().map(|clause| {
|
||||
ast::WherePredicate {
|
||||
attrs: clause.attrs.clone(),
|
||||
kind: clause.kind.clone(),
|
||||
id: ast::DUMMY_NODE_ID,
|
||||
span: clause.span.with_ctxt(ctxt),
|
||||
is_placeholder: false,
|
||||
}
|
||||
}));
|
||||
|
||||
|
|
@ -744,8 +746,13 @@ impl<'a> TraitDef<'a> {
|
|||
};
|
||||
|
||||
let kind = ast::WherePredicateKind::BoundPredicate(predicate);
|
||||
let predicate =
|
||||
ast::WherePredicate { kind, id: ast::DUMMY_NODE_ID, span: self.span };
|
||||
let predicate = ast::WherePredicate {
|
||||
attrs: ThinVec::new(),
|
||||
kind,
|
||||
id: ast::DUMMY_NODE_ID,
|
||||
span: self.span,
|
||||
is_placeholder: false,
|
||||
};
|
||||
where_clause.predicates.push(predicate);
|
||||
}
|
||||
}
|
||||
|
|
@ -1033,6 +1040,7 @@ impl<'a> MethodDef<'a> {
|
|||
generics: fn_generics,
|
||||
contract: None,
|
||||
body: Some(body_block),
|
||||
define_opaque: None,
|
||||
})),
|
||||
tokens: None,
|
||||
})
|
||||
|
|
|
|||
|
|
@ -144,10 +144,8 @@ pub(crate) struct AllocMustStatics {
|
|||
pub(crate) span: Span,
|
||||
}
|
||||
|
||||
#[cfg(llvm_enzyme)]
|
||||
pub(crate) use autodiff::*;
|
||||
|
||||
#[cfg(llvm_enzyme)]
|
||||
mod autodiff {
|
||||
use super::*;
|
||||
#[derive(Diagnostic)]
|
||||
|
|
@ -187,6 +185,15 @@ mod autodiff {
|
|||
pub(crate) act: String,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(builtin_macros_autodiff_ret_activity)]
|
||||
pub(crate) struct AutoDiffInvalidRetAct {
|
||||
#[primary_span]
|
||||
pub(crate) span: Span,
|
||||
pub(crate) mode: String,
|
||||
pub(crate) act: String,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(builtin_macros_autodiff_mode)]
|
||||
pub(crate) struct AutoDiffInvalidMode {
|
||||
|
|
@ -203,9 +210,7 @@ mod autodiff {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(not(llvm_enzyme))]
|
||||
pub(crate) use ad_fallback::*;
|
||||
#[cfg(not(llvm_enzyme))]
|
||||
mod ad_fallback {
|
||||
use super::*;
|
||||
#[derive(Diagnostic)]
|
||||
|
|
|
|||
|
|
@ -190,7 +190,8 @@ fn make_format_args(
|
|||
&& let [stmt] = block.stmts.as_slice()
|
||||
&& let StmtKind::Expr(expr) = &stmt.kind
|
||||
&& let ExprKind::Path(None, path) = &expr.kind
|
||||
&& path.is_potential_trivial_const_arg()
|
||||
&& path.segments.len() == 1
|
||||
&& path.segments[0].args.is_none()
|
||||
{
|
||||
err.multipart_suggestion(
|
||||
"quote your inlined format argument to use as string literal",
|
||||
|
|
@ -710,11 +711,9 @@ fn report_missing_placeholders(
|
|||
};
|
||||
|
||||
let pos = sub.position();
|
||||
let sub = String::from(sub.as_str());
|
||||
if explained.contains(&sub) {
|
||||
if !explained.insert(sub.to_string()) {
|
||||
continue;
|
||||
}
|
||||
explained.insert(sub);
|
||||
|
||||
if !found_foreign {
|
||||
found_foreign = true;
|
||||
|
|
|
|||
|
|
@ -12,14 +12,16 @@ pub(crate) mod printf {
|
|||
Escape((usize, usize)),
|
||||
}
|
||||
|
||||
impl<'a> Substitution<'a> {
|
||||
pub(crate) fn as_str(&self) -> &str {
|
||||
impl ToString for Substitution<'_> {
|
||||
fn to_string(&self) -> String {
|
||||
match self {
|
||||
Substitution::Format(fmt) => fmt.span,
|
||||
Substitution::Escape(_) => "%%",
|
||||
Substitution::Format(fmt) => fmt.span.into(),
|
||||
Substitution::Escape(_) => "%%".into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Substitution<'_> {
|
||||
pub(crate) fn position(&self) -> InnerSpan {
|
||||
match self {
|
||||
Substitution::Format(fmt) => fmt.position,
|
||||
|
|
@ -627,15 +629,17 @@ pub(crate) mod shell {
|
|||
Escape((usize, usize)),
|
||||
}
|
||||
|
||||
impl Substitution<'_> {
|
||||
pub(crate) fn as_str(&self) -> String {
|
||||
impl ToString for Substitution<'_> {
|
||||
fn to_string(&self) -> String {
|
||||
match self {
|
||||
Substitution::Ordinal(n, _) => format!("${n}"),
|
||||
Substitution::Name(n, _) => format!("${n}"),
|
||||
Substitution::Escape(_) => "$$".into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Substitution<'_> {
|
||||
pub(crate) fn position(&self) -> InnerSpan {
|
||||
let (Self::Ordinal(_, pos) | Self::Name(_, pos) | Self::Escape(pos)) = self;
|
||||
InnerSpan::new(pos.0, pos.1)
|
||||
|
|
|
|||
|
|
@ -83,6 +83,7 @@ impl AllocFnFactory<'_, '_> {
|
|||
generics: Generics::default(),
|
||||
contract: None,
|
||||
body,
|
||||
define_opaque: None,
|
||||
}));
|
||||
let item = self.cx.item(
|
||||
self.span,
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
#![allow(internal_features)]
|
||||
#![allow(rustc::diagnostic_outside_of_impl)]
|
||||
#![allow(rustc::untranslatable_diagnostic)]
|
||||
#![cfg_attr(doc, recursion_limit = "256")] // FIXME(nnethercote): will be removed by #124141
|
||||
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
|
||||
#![doc(rust_logo)]
|
||||
#![feature(assert_matches)]
|
||||
|
|
@ -18,7 +19,6 @@
|
|||
#![feature(rustdoc_internals)]
|
||||
#![feature(string_from_utf8_lossy_owned)]
|
||||
#![feature(try_blocks)]
|
||||
#![warn(unreachable_pub)]
|
||||
// tidy-alphabetical-end
|
||||
|
||||
extern crate proc_macro;
|
||||
|
|
@ -39,6 +39,7 @@ mod compile_error;
|
|||
mod concat;
|
||||
mod concat_bytes;
|
||||
mod concat_idents;
|
||||
mod define_opaque;
|
||||
mod derive;
|
||||
mod deriving;
|
||||
mod edition_panic;
|
||||
|
|
@ -114,6 +115,7 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) {
|
|||
bench: test::expand_bench,
|
||||
cfg_accessible: cfg_accessible::Expander,
|
||||
cfg_eval: cfg_eval::expand,
|
||||
define_opaque: define_opaque::expand,
|
||||
derive: derive::Expander { is_const: false },
|
||||
derive_const: derive::Expander { is_const: true },
|
||||
global_allocator: global_allocator::expand,
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ pub fn inject(
|
|||
Edition2018 => sym::rust_2018,
|
||||
Edition2021 => sym::rust_2021,
|
||||
Edition2024 => sym::rust_2024,
|
||||
EditionFuture => sym::rust_future,
|
||||
}])
|
||||
.map(|&symbol| Ident::new(symbol, span))
|
||||
.collect();
|
||||
|
|
|
|||
|
|
@ -346,6 +346,7 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> P<ast::Item> {
|
|||
generics: ast::Generics::default(),
|
||||
contract: None,
|
||||
body: Some(main_body),
|
||||
define_opaque: None,
|
||||
}));
|
||||
|
||||
// Honor the reexport_test_harness_main attribute
|
||||
|
|
|
|||
|
|
@ -2,23 +2,21 @@
|
|||
|
||||
#![feature(type_alias_impl_trait)]
|
||||
|
||||
mod helper {
|
||||
pub trait T {
|
||||
type Item;
|
||||
}
|
||||
|
||||
pub type Alias<'a> = impl T<Item = &'a ()>;
|
||||
|
||||
struct S;
|
||||
impl<'a> T for &'a S {
|
||||
type Item = &'a ();
|
||||
}
|
||||
|
||||
pub fn filter_positive<'a>() -> Alias<'a> {
|
||||
&S
|
||||
}
|
||||
pub trait T {
|
||||
type Item;
|
||||
}
|
||||
|
||||
pub type Alias<'a> = impl T<Item = &'a ()>;
|
||||
|
||||
struct S;
|
||||
impl<'a> T for &'a S {
|
||||
type Item = &'a ();
|
||||
}
|
||||
|
||||
#[define_opaque(Alias)]
|
||||
pub fn filter_positive<'a>() -> Alias<'a> {
|
||||
&S
|
||||
}
|
||||
use helper::*;
|
||||
|
||||
fn with_positive(fun: impl Fn(Alias<'_>)) {
|
||||
fun(filter_positive());
|
||||
|
|
|
|||
|
|
@ -624,25 +624,25 @@ pub mod intrinsics {
|
|||
#[rustc_intrinsic]
|
||||
pub fn size_of<T>() -> usize;
|
||||
#[rustc_intrinsic]
|
||||
pub unsafe fn size_of_val<T: ?::Sized>(_val: *const T) -> usize;
|
||||
pub unsafe fn size_of_val<T: ?::Sized>(val: *const T) -> usize;
|
||||
#[rustc_intrinsic]
|
||||
pub fn min_align_of<T>() -> usize;
|
||||
#[rustc_intrinsic]
|
||||
pub unsafe fn min_align_of_val<T: ?::Sized>(_val: *const T) -> usize;
|
||||
pub unsafe fn min_align_of_val<T: ?::Sized>(val: *const T) -> usize;
|
||||
#[rustc_intrinsic]
|
||||
pub unsafe fn copy<T>(_src: *const T, _dst: *mut T, _count: usize);
|
||||
pub unsafe fn copy<T>(src: *const T, dst: *mut T, count: usize);
|
||||
#[rustc_intrinsic]
|
||||
pub unsafe fn transmute<T, U>(_e: T) -> U;
|
||||
pub unsafe fn transmute<T, U>(e: T) -> U;
|
||||
#[rustc_intrinsic]
|
||||
pub unsafe fn ctlz_nonzero<T>(_x: T) -> u32;
|
||||
pub unsafe fn ctlz_nonzero<T>(x: T) -> u32;
|
||||
#[rustc_intrinsic]
|
||||
pub fn needs_drop<T: ?::Sized>() -> bool;
|
||||
#[rustc_intrinsic]
|
||||
pub fn bitreverse<T>(_x: T) -> T;
|
||||
pub fn bitreverse<T>(x: T) -> T;
|
||||
#[rustc_intrinsic]
|
||||
pub fn bswap<T>(_x: T) -> T;
|
||||
pub fn bswap<T>(x: T) -> T;
|
||||
#[rustc_intrinsic]
|
||||
pub unsafe fn write_bytes<T>(_dst: *mut T, _val: u8, _count: usize);
|
||||
pub unsafe fn write_bytes<T>(dst: *mut T, val: u8, count: usize);
|
||||
#[rustc_intrinsic]
|
||||
pub unsafe fn unreachable() -> !;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,15 +12,15 @@ index 7165c3e48af..968552ad435 100644
|
|||
--- a/library/alloc/Cargo.toml
|
||||
+++ b/library/alloc/Cargo.toml
|
||||
@@ -11,7 +11,7 @@ test = { path = "../test" }
|
||||
edition = "2021"
|
||||
bench = false
|
||||
|
||||
[dependencies]
|
||||
core = { path = "../core", public = true }
|
||||
-compiler_builtins = { version = "=0.1.148", features = ['rustc-dep-of-std'] }
|
||||
+compiler_builtins = { version = "=0.1.148", features = ['rustc-dep-of-std', 'no-f16-f128'] }
|
||||
-compiler_builtins = { version = "=0.1.151", features = ['rustc-dep-of-std'] }
|
||||
+compiler_builtins = { version = "=0.1.151", features = ['rustc-dep-of-std', 'no-f16-f128'] }
|
||||
|
||||
[dev-dependencies]
|
||||
rand = { version = "0.8.5", default-features = false, features = ["alloc"] }
|
||||
[features]
|
||||
compiler-builtins-mem = ['compiler_builtins/mem']
|
||||
--
|
||||
2.34.1
|
||||
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ impl<'tcx> ArgAbiExt<'tcx> for ArgAbi<'tcx, Ty<'tcx>> {
|
|||
AbiParam::new(scalar_to_clif_type(tcx, scalar)),
|
||||
attrs
|
||||
)],
|
||||
BackendRepr::Vector { .. } => {
|
||||
BackendRepr::SimdVector { .. } => {
|
||||
let vector_ty = crate::intrinsics::clif_vector_type(tcx, self.layout);
|
||||
smallvec![AbiParam::new(vector_ty)]
|
||||
}
|
||||
|
|
@ -135,7 +135,7 @@ impl<'tcx> ArgAbiExt<'tcx> for ArgAbi<'tcx, Ty<'tcx>> {
|
|||
BackendRepr::Scalar(scalar) => {
|
||||
(None, vec![AbiParam::new(scalar_to_clif_type(tcx, scalar))])
|
||||
}
|
||||
BackendRepr::Vector { .. } => {
|
||||
BackendRepr::SimdVector { .. } => {
|
||||
let vector_ty = crate::intrinsics::clif_vector_type(tcx, self.layout);
|
||||
(None, vec![AbiParam::new(vector_ty)])
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ fn report_atomic_type_validation_error<'tcx>(
|
|||
|
||||
pub(crate) fn clif_vector_type<'tcx>(tcx: TyCtxt<'tcx>, layout: TyAndLayout<'tcx>) -> Type {
|
||||
let (element, count) = match layout.backend_repr {
|
||||
BackendRepr::Vector { element, count } => (element, count),
|
||||
BackendRepr::SimdVector { element, count } => (element, count),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -460,64 +460,6 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
|
|||
});
|
||||
}
|
||||
|
||||
sym::simd_fpow => {
|
||||
intrinsic_args!(fx, args => (a, b); intrinsic);
|
||||
|
||||
if !a.layout().ty.is_simd() {
|
||||
report_simd_type_validation_error(fx, intrinsic, span, a.layout().ty);
|
||||
return;
|
||||
}
|
||||
|
||||
simd_pair_for_each_lane(fx, a, b, ret, &|fx, lane_ty, _ret_lane_ty, a_lane, b_lane| {
|
||||
match lane_ty.kind() {
|
||||
ty::Float(FloatTy::F32) => fx.lib_call(
|
||||
"powf",
|
||||
vec![AbiParam::new(types::F32), AbiParam::new(types::F32)],
|
||||
vec![AbiParam::new(types::F32)],
|
||||
&[a_lane, b_lane],
|
||||
)[0],
|
||||
ty::Float(FloatTy::F64) => fx.lib_call(
|
||||
"pow",
|
||||
vec![AbiParam::new(types::F64), AbiParam::new(types::F64)],
|
||||
vec![AbiParam::new(types::F64)],
|
||||
&[a_lane, b_lane],
|
||||
)[0],
|
||||
_ => unreachable!("{:?}", lane_ty),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
sym::simd_fpowi => {
|
||||
intrinsic_args!(fx, args => (a, exp); intrinsic);
|
||||
let exp = exp.load_scalar(fx);
|
||||
|
||||
if !a.layout().ty.is_simd() {
|
||||
report_simd_type_validation_error(fx, intrinsic, span, a.layout().ty);
|
||||
return;
|
||||
}
|
||||
|
||||
simd_for_each_lane(
|
||||
fx,
|
||||
a,
|
||||
ret,
|
||||
&|fx, lane_ty, _ret_lane_ty, lane| match lane_ty.kind() {
|
||||
ty::Float(FloatTy::F32) => fx.lib_call(
|
||||
"__powisf2", // compiler-builtins
|
||||
vec![AbiParam::new(types::F32), AbiParam::new(types::I32)],
|
||||
vec![AbiParam::new(types::F32)],
|
||||
&[lane, exp],
|
||||
)[0],
|
||||
ty::Float(FloatTy::F64) => fx.lib_call(
|
||||
"__powidf2", // compiler-builtins
|
||||
vec![AbiParam::new(types::F64), AbiParam::new(types::I32)],
|
||||
vec![AbiParam::new(types::F64)],
|
||||
&[lane, exp],
|
||||
)[0],
|
||||
_ => unreachable!("{:?}", lane_ty),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
sym::simd_fsin
|
||||
| sym::simd_fcos
|
||||
| sym::simd_fexp
|
||||
|
|
|
|||
|
|
@ -176,13 +176,9 @@ impl CodegenBackend for CraneliftCodegenBackend {
|
|||
}
|
||||
}
|
||||
|
||||
fn target_features_cfg(
|
||||
&self,
|
||||
sess: &Session,
|
||||
_allow_unstable: bool,
|
||||
) -> Vec<rustc_span::Symbol> {
|
||||
fn target_features_cfg(&self, sess: &Session) -> (Vec<Symbol>, Vec<Symbol>) {
|
||||
// FIXME return the actually used target features. this is necessary for #[cfg(target_feature)]
|
||||
if sess.target.arch == "x86_64" && sess.target.os != "none" {
|
||||
let target_features = if sess.target.arch == "x86_64" && sess.target.os != "none" {
|
||||
// x86_64 mandates SSE2 support and rustc requires the x87 feature to be enabled
|
||||
vec![sym::fsxr, sym::sse, sym::sse2, Symbol::intern("x87")]
|
||||
} else if sess.target.arch == "aarch64" {
|
||||
|
|
@ -196,7 +192,10 @@ impl CodegenBackend for CraneliftCodegenBackend {
|
|||
}
|
||||
} else {
|
||||
vec![]
|
||||
}
|
||||
};
|
||||
// FIXME do `unstable_target_features` properly
|
||||
let unstable_target_features = target_features.clone();
|
||||
(target_features, unstable_target_features)
|
||||
}
|
||||
|
||||
fn print_version(&self) {
|
||||
|
|
|
|||
|
|
@ -173,9 +173,11 @@ impl<'tcx> CValue<'tcx> {
|
|||
CValueInner::ByRef(ptr, None) => {
|
||||
let clif_ty = match layout.backend_repr {
|
||||
BackendRepr::Scalar(scalar) => scalar_to_clif_type(fx.tcx, scalar),
|
||||
BackendRepr::Vector { element, count } => scalar_to_clif_type(fx.tcx, element)
|
||||
.by(u32::try_from(count).unwrap())
|
||||
.unwrap(),
|
||||
BackendRepr::SimdVector { element, count } => {
|
||||
scalar_to_clif_type(fx.tcx, element)
|
||||
.by(u32::try_from(count).unwrap())
|
||||
.unwrap()
|
||||
}
|
||||
_ => unreachable!("{:?}", layout.ty),
|
||||
};
|
||||
let mut flags = MemFlags::new();
|
||||
|
|
|
|||
|
|
@ -595,25 +595,25 @@ pub mod intrinsics {
|
|||
#[rustc_intrinsic]
|
||||
pub fn size_of<T>() -> usize;
|
||||
#[rustc_intrinsic]
|
||||
pub unsafe fn size_of_val<T: ?::Sized>(_val: *const T) -> usize;
|
||||
pub unsafe fn size_of_val<T: ?::Sized>(val: *const T) -> usize;
|
||||
#[rustc_intrinsic]
|
||||
pub fn min_align_of<T>() -> usize;
|
||||
#[rustc_intrinsic]
|
||||
pub unsafe fn min_align_of_val<T: ?::Sized>(_val: *const T) -> usize;
|
||||
pub unsafe fn min_align_of_val<T: ?::Sized>(val: *const T) -> usize;
|
||||
#[rustc_intrinsic]
|
||||
pub unsafe fn copy<T>(_src: *const T, _dst: *mut T, _count: usize);
|
||||
pub unsafe fn copy<T>(src: *const T, dst: *mut T, count: usize);
|
||||
#[rustc_intrinsic]
|
||||
pub unsafe fn transmute<T, U>(_e: T) -> U;
|
||||
pub unsafe fn transmute<T, U>(e: T) -> U;
|
||||
#[rustc_intrinsic]
|
||||
pub unsafe fn ctlz_nonzero<T>(_x: T) -> u32;
|
||||
pub unsafe fn ctlz_nonzero<T>(x: T) -> u32;
|
||||
#[rustc_intrinsic]
|
||||
pub fn needs_drop<T: ?::Sized>() -> bool;
|
||||
#[rustc_intrinsic]
|
||||
pub fn bitreverse<T>(_x: T) -> T;
|
||||
pub fn bitreverse<T>(x: T) -> T;
|
||||
#[rustc_intrinsic]
|
||||
pub fn bswap<T>(_x: T) -> T;
|
||||
pub fn bswap<T>(x: T) -> T;
|
||||
#[rustc_intrinsic]
|
||||
pub unsafe fn write_bytes<T>(_dst: *mut T, _val: u8, _count: usize);
|
||||
pub unsafe fn write_bytes<T>(dst: *mut T, val: u8, count: usize);
|
||||
#[rustc_intrinsic]
|
||||
pub unsafe fn unreachable() -> !;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ use crate::context::CodegenCx;
|
|||
use crate::intrinsic::ArgAbiExt;
|
||||
use crate::type_of::LayoutGccExt;
|
||||
|
||||
impl<'a, 'gcc, 'tcx> AbiBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
|
||||
impl AbiBuilderMethods for Builder<'_, '_, '_> {
|
||||
fn get_param(&mut self, index: usize) -> Self::Value {
|
||||
let func = self.current_func();
|
||||
let param = func.get_param(index as i32);
|
||||
|
|
|
|||
|
|
@ -632,17 +632,16 @@ pub unsafe fn optimize_thin_module(
|
|||
Arc::new(SyncContext::new(context))
|
||||
}
|
||||
};
|
||||
let module = ModuleCodegen {
|
||||
module_llvm: GccContext {
|
||||
let module = ModuleCodegen::new_regular(
|
||||
thin_module.name().to_string(),
|
||||
GccContext {
|
||||
context,
|
||||
should_combine_object_files,
|
||||
// TODO(antoyo): use the correct relocation model here.
|
||||
relocation_model: RelocModel::Pic,
|
||||
temp_dir: None,
|
||||
},
|
||||
name: thin_module.name().to_string(),
|
||||
kind: ModuleKind::Regular,
|
||||
};
|
||||
);
|
||||
/*{
|
||||
let target = &*module.module_llvm.tm;
|
||||
let llmod = module.module_llvm.llmod();
|
||||
|
|
|
|||
|
|
@ -4,10 +4,10 @@ use std::sync::Arc;
|
|||
use std::time::Instant;
|
||||
|
||||
use gccjit::{CType, Context, FunctionType, GlobalKind};
|
||||
use rustc_codegen_ssa::ModuleCodegen;
|
||||
use rustc_codegen_ssa::base::maybe_create_entry_wrapper;
|
||||
use rustc_codegen_ssa::mono_item::MonoItemExt;
|
||||
use rustc_codegen_ssa::traits::DebugInfoCodegenMethods;
|
||||
use rustc_codegen_ssa::{ModuleCodegen, ModuleKind};
|
||||
use rustc_middle::dep_graph;
|
||||
use rustc_middle::mir::mono::Linkage;
|
||||
#[cfg(feature = "master")]
|
||||
|
|
@ -237,16 +237,15 @@ pub fn compile_codegen_unit(
|
|||
}
|
||||
}
|
||||
|
||||
ModuleCodegen {
|
||||
name: cgu_name.to_string(),
|
||||
module_llvm: GccContext {
|
||||
ModuleCodegen::new_regular(
|
||||
cgu_name.to_string(),
|
||||
GccContext {
|
||||
context: Arc::new(SyncContext::new(context)),
|
||||
relocation_model: tcx.sess.relocation_model(),
|
||||
should_combine_object_files: false,
|
||||
temp_dir: None,
|
||||
},
|
||||
kind: ModuleKind::Regular,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
(module, cost)
|
||||
|
|
|
|||
|
|
@ -2439,9 +2439,5 @@ fn get_maybe_pointer_size(value: RValue<'_>) -> u32 {
|
|||
#[cfg(not(feature = "master"))]
|
||||
fn get_maybe_pointer_size(value: RValue<'_>) -> u32 {
|
||||
let type_ = value.get_type();
|
||||
if type_.get_pointee().is_some() {
|
||||
std::mem::size_of::<*const ()>() as _
|
||||
} else {
|
||||
type_.get_size()
|
||||
}
|
||||
if type_.get_pointee().is_some() { size_of::<*const ()>() as _ } else { type_.get_size() }
|
||||
}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue