Merge ref '32e7a4b92b' from rust-lang/rust
Pull recent changes from https://github.com/rust-lang/rust via Josh.
Upstream ref: 32e7a4b92b
Filtered ref: d39f3479bfafb04026ed3afec68aa671d13e9c3c
This merge was created using https://github.com/rust-lang/josh-sync.
This commit is contained in:
commit
7cab27d73a
789 changed files with 13870 additions and 8025 deletions
9
.github/workflows/ci.yml
vendored
9
.github/workflows/ci.yml
vendored
|
|
@ -117,7 +117,7 @@ jobs:
|
|||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
# Free up disk space on Linux by removing preinstalled components that
|
||||
# Free up disk space on Linux and Windows by removing preinstalled components that
|
||||
# we do not need. We do this to enable some of the less resource
|
||||
# intensive jobs to run on free runners, which however also have
|
||||
# less disk space.
|
||||
|
|
@ -125,6 +125,13 @@ jobs:
|
|||
run: src/ci/scripts/free-disk-space.sh
|
||||
if: matrix.free_disk
|
||||
|
||||
# If we don't need to free up disk space then just report how much space we have
|
||||
- name: print disk usage
|
||||
run: |
|
||||
echo "disk usage:"
|
||||
df -h
|
||||
if: matrix.free_disk == false
|
||||
|
||||
# Rust Log Analyzer can't currently detect the PR number of a GitHub
|
||||
# Actions build on its own, so a hint in the log message is needed to
|
||||
# point it in the right direction.
|
||||
|
|
|
|||
183
Cargo.lock
183
Cargo.lock
|
|
@ -441,20 +441,6 @@ dependencies = [
|
|||
"thiserror 1.0.69",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cargo_metadata"
|
||||
version = "0.19.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd5eb614ed4c27c5d706420e4320fbe3216ab31fa1c33cd8246ac36dae4479ba"
|
||||
dependencies = [
|
||||
"camino",
|
||||
"cargo-platform 0.1.9",
|
||||
"semver",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror 2.0.12",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cargo_metadata"
|
||||
version = "0.21.0"
|
||||
|
|
@ -480,6 +466,8 @@ version = "1.2.16"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c"
|
||||
dependencies = [
|
||||
"jobserver",
|
||||
"libc",
|
||||
"shlex",
|
||||
]
|
||||
|
||||
|
|
@ -669,6 +657,26 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cmake"
|
||||
version = "0.1.54"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "codespan-reporting"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fe6d2e5af09e8c8ad56c969f2157a3d4238cebc7c55f0a517728c38f7b200f81"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"termcolor",
|
||||
"unicode-width 0.2.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "collect-license-metadata"
|
||||
version = "0.1.0"
|
||||
|
|
@ -927,6 +935,68 @@ dependencies = [
|
|||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cxx"
|
||||
version = "1.0.161"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a3523cc02ad831111491dd64b27ad999f1ae189986728e477604e61b81f828df"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cxxbridge-cmd",
|
||||
"cxxbridge-flags",
|
||||
"cxxbridge-macro",
|
||||
"foldhash",
|
||||
"link-cplusplus",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cxx-build"
|
||||
version = "1.0.161"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "212b754247a6f07b10fa626628c157593f0abf640a3dd04cce2760eca970f909"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"codespan-reporting",
|
||||
"indexmap",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"scratch",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cxxbridge-cmd"
|
||||
version = "1.0.161"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f426a20413ec2e742520ba6837c9324b55ffac24ead47491a6e29f933c5b135a"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"codespan-reporting",
|
||||
"indexmap",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cxxbridge-flags"
|
||||
version = "1.0.161"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a258b6069020b4e5da6415df94a50ee4f586a6c38b037a180e940a43d06a070d"
|
||||
|
||||
[[package]]
|
||||
name = "cxxbridge-macro"
|
||||
version = "1.0.161"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8dec184b52be5008d6eaf7e62fc1802caf1ad1227d11b3b7df2c409c7ffc3f4"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustversion",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.20.11"
|
||||
|
|
@ -1364,7 +1434,7 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"anyhow",
|
||||
"askama",
|
||||
"cargo_metadata 0.18.1",
|
||||
"cargo_metadata 0.21.0",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror 1.0.69",
|
||||
|
|
@ -1387,6 +1457,17 @@ dependencies = [
|
|||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "genmc-sys"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cmake",
|
||||
"cxx",
|
||||
"cxx-build",
|
||||
"git2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getopts"
|
||||
version = "0.2.23"
|
||||
|
|
@ -1441,6 +1522,21 @@ dependencies = [
|
|||
"stable_deref_trait",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "git2"
|
||||
version = "0.20.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2deb07a133b1520dc1a5690e9bd08950108873d7ed5de38dcc74d3b5ebffa110"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"libc",
|
||||
"libgit2-sys",
|
||||
"log",
|
||||
"openssl-probe",
|
||||
"openssl-sys",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "glob"
|
||||
version = "0.3.2"
|
||||
|
|
@ -1890,16 +1986,16 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "ipc-channel"
|
||||
version = "0.20.0"
|
||||
version = "0.20.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b1c98b70019c830a1fc39cecfe1f60ff99c4122f0a189697c810c90ec545c14"
|
||||
checksum = "1700f6b8b9f00cdd675f32fbb3a5be882213140dfe045805273221ca266c43f8"
|
||||
dependencies = [
|
||||
"bincode",
|
||||
"crossbeam-channel",
|
||||
"fnv",
|
||||
"libc",
|
||||
"mio",
|
||||
"rand 0.9.1",
|
||||
"rand 0.9.2",
|
||||
"serde",
|
||||
"tempfile",
|
||||
"uuid",
|
||||
|
|
@ -2074,6 +2170,19 @@ dependencies = [
|
|||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libgit2-sys"
|
||||
version = "0.18.2+1.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c42fe03df2bd3c53a3a9c7317ad91d80c81cd1fb0caec8d7cc4cd2bfa10c222"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"libz-sys",
|
||||
"openssl-sys",
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
version = "0.8.8"
|
||||
|
|
@ -2113,6 +2222,15 @@ dependencies = [
|
|||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "link-cplusplus"
|
||||
version = "1.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4a6f6da007f968f9def0d65a05b187e2960183de70c160204ecfccf0ee330212"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "linkchecker"
|
||||
version = "0.1.0"
|
||||
|
|
@ -2322,6 +2440,7 @@ dependencies = [
|
|||
"chrono-tz",
|
||||
"colored 3.0.0",
|
||||
"directories",
|
||||
"genmc-sys",
|
||||
"getrandom 0.3.3",
|
||||
"ipc-channel",
|
||||
"libc",
|
||||
|
|
@ -2329,7 +2448,7 @@ dependencies = [
|
|||
"libloading",
|
||||
"measureme",
|
||||
"nix",
|
||||
"rand 0.9.1",
|
||||
"rand 0.9.2",
|
||||
"regex",
|
||||
"rustc_version",
|
||||
"serde",
|
||||
|
|
@ -2962,9 +3081,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.9.1"
|
||||
version = "0.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97"
|
||||
checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
|
||||
dependencies = [
|
||||
"rand_chacha 0.9.0",
|
||||
"rand_core 0.9.3",
|
||||
|
|
@ -3048,9 +3167,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.13"
|
||||
version = "0.5.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6"
|
||||
checksum = "7251471db004e509f4e75a62cca9435365b5ec7bcdff530d612ac7c87c44a792"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
|
@ -3245,7 +3364,7 @@ name = "rustc_abi"
|
|||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"rand 0.9.1",
|
||||
"rand 0.9.2",
|
||||
"rand_xoshiro",
|
||||
"rustc_data_structures",
|
||||
"rustc_hashes",
|
||||
|
|
@ -3880,7 +3999,7 @@ dependencies = [
|
|||
name = "rustc_incremental"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"rand 0.9.1",
|
||||
"rand 0.9.2",
|
||||
"rustc_ast",
|
||||
"rustc_data_structures",
|
||||
"rustc_errors",
|
||||
|
|
@ -4493,7 +4612,7 @@ dependencies = [
|
|||
"bitflags",
|
||||
"getopts",
|
||||
"libc",
|
||||
"rand 0.9.1",
|
||||
"rand 0.9.2",
|
||||
"rustc_abi",
|
||||
"rustc_ast",
|
||||
"rustc_data_structures",
|
||||
|
|
@ -4578,7 +4697,7 @@ dependencies = [
|
|||
"crossbeam-deque",
|
||||
"crossbeam-utils",
|
||||
"libc",
|
||||
"rand 0.9.1",
|
||||
"rand 0.9.2",
|
||||
"rand_xorshift",
|
||||
"scoped-tls",
|
||||
"smallvec",
|
||||
|
|
@ -4891,6 +5010,12 @@ version = "1.2.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "scratch"
|
||||
version = "1.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f6280af86e5f559536da57a45ebc84948833b3bee313a7dd25232e09c878a52"
|
||||
|
||||
[[package]]
|
||||
name = "self_cell"
|
||||
version = "1.2.0"
|
||||
|
|
@ -5284,7 +5409,7 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"indicatif",
|
||||
"num",
|
||||
"rand 0.9.1",
|
||||
"rand 0.9.2",
|
||||
"rand_chacha 0.9.0",
|
||||
"rayon",
|
||||
]
|
||||
|
|
@ -5370,7 +5495,7 @@ name = "tidy"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"build_helper",
|
||||
"cargo_metadata 0.19.2",
|
||||
"cargo_metadata 0.21.0",
|
||||
"fluent-syntax",
|
||||
"ignore",
|
||||
"miropt-test-tools",
|
||||
|
|
|
|||
|
|
@ -465,6 +465,11 @@
|
|||
# What custom diff tool to use for displaying compiletest tests.
|
||||
#build.compiletest-diff-tool = <none>
|
||||
|
||||
# Whether to allow `compiletest` self-tests and `compiletest`-managed test
|
||||
# suites to be run against the stage 0 rustc. This is only intended to be used
|
||||
# when the stage 0 compiler is actually built from in-tree sources.
|
||||
#build.compiletest-allow-stage0 = false
|
||||
|
||||
# Whether to use the precompiled stage0 libtest with compiletest.
|
||||
#build.compiletest-use-stage0-libtest = true
|
||||
|
||||
|
|
@ -475,6 +480,9 @@
|
|||
# Note that if any value is manually given to bootstrap such as
|
||||
# `./x test tidy --extra-checks=js`, this value is ignored.
|
||||
# Use `--extra-checks=''` to temporarily disable all extra checks.
|
||||
#
|
||||
# Automatically enabled in the "tools" profile.
|
||||
# Set to the empty string to force disable (recommeded for hdd systems).
|
||||
#build.tidy-extra-checks = ""
|
||||
|
||||
# Indicates whether ccache is used when building certain artifacts (e.g. LLVM).
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
use std::collections::BTreeSet;
|
||||
use std::fmt::{self, Write};
|
||||
use std::ops::{Bound, Deref};
|
||||
use std::{cmp, iter};
|
||||
|
|
@ -5,7 +6,7 @@ use std::{cmp, iter};
|
|||
use rustc_hashes::Hash64;
|
||||
use rustc_index::Idx;
|
||||
use rustc_index::bit_set::BitMatrix;
|
||||
use tracing::debug;
|
||||
use tracing::{debug, trace};
|
||||
|
||||
use crate::{
|
||||
AbiAlign, Align, BackendRepr, FieldsShape, HasDataLayout, IndexSlice, IndexVec, Integer,
|
||||
|
|
@ -766,30 +767,63 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
|
|||
|
||||
let niche_filling_layout = calculate_niche_filling_layout();
|
||||
|
||||
let (mut min, mut max) = (i128::MAX, i128::MIN);
|
||||
let discr_type = repr.discr_type();
|
||||
let bits = Integer::from_attr(dl, discr_type).size().bits();
|
||||
for (i, mut val) in discriminants {
|
||||
if !repr.c() && variants[i].iter().any(|f| f.is_uninhabited()) {
|
||||
continue;
|
||||
}
|
||||
if discr_type.is_signed() {
|
||||
// sign extend the raw representation to be an i128
|
||||
val = (val << (128 - bits)) >> (128 - bits);
|
||||
}
|
||||
if val < min {
|
||||
min = val;
|
||||
}
|
||||
if val > max {
|
||||
max = val;
|
||||
}
|
||||
}
|
||||
// We might have no inhabited variants, so pretend there's at least one.
|
||||
if (min, max) == (i128::MAX, i128::MIN) {
|
||||
min = 0;
|
||||
max = 0;
|
||||
}
|
||||
assert!(min <= max, "discriminant range is {min}...{max}");
|
||||
let discr_int = Integer::from_attr(dl, discr_type);
|
||||
// Because we can only represent one range of valid values, we'll look for the
|
||||
// largest range of invalid values and pick everything else as the range of valid
|
||||
// values.
|
||||
|
||||
// First we need to sort the possible discriminant values so that we can look for the largest gap:
|
||||
let valid_discriminants: BTreeSet<i128> = discriminants
|
||||
.filter(|&(i, _)| repr.c() || variants[i].iter().all(|f| !f.is_uninhabited()))
|
||||
.map(|(_, val)| {
|
||||
if discr_type.is_signed() {
|
||||
// sign extend the raw representation to be an i128
|
||||
// FIXME: do this at the discriminant iterator creation sites
|
||||
discr_int.size().sign_extend(val as u128)
|
||||
} else {
|
||||
val
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
trace!(?valid_discriminants);
|
||||
let discriminants = valid_discriminants.iter().copied();
|
||||
//let next_discriminants = discriminants.clone().cycle().skip(1);
|
||||
let next_discriminants =
|
||||
discriminants.clone().chain(valid_discriminants.first().copied()).skip(1);
|
||||
// Iterate over pairs of each discriminant together with the next one.
|
||||
// Since they were sorted, we can now compute the niche sizes and pick the largest.
|
||||
let discriminants = discriminants.zip(next_discriminants);
|
||||
let largest_niche = discriminants.max_by_key(|&(start, end)| {
|
||||
trace!(?start, ?end);
|
||||
// If this is a wraparound range, the niche size is `MAX - abs(diff)`, as the diff between
|
||||
// the two end points is actually the size of the valid discriminants.
|
||||
let dist = if start > end {
|
||||
// Overflow can happen for 128 bit discriminants if `end` is negative.
|
||||
// But in that case casting to `u128` still gets us the right value,
|
||||
// as the distance must be positive if the lhs of the subtraction is larger than the rhs.
|
||||
let dist = start.wrapping_sub(end);
|
||||
if discr_type.is_signed() {
|
||||
discr_int.signed_max().wrapping_sub(dist) as u128
|
||||
} else {
|
||||
discr_int.size().unsigned_int_max() - dist as u128
|
||||
}
|
||||
} else {
|
||||
// Overflow can happen for 128 bit discriminants if `start` is negative.
|
||||
// But in that case casting to `u128` still gets us the right value,
|
||||
// as the distance must be positive if the lhs of the subtraction is larger than the rhs.
|
||||
end.wrapping_sub(start) as u128
|
||||
};
|
||||
trace!(?dist);
|
||||
dist
|
||||
});
|
||||
trace!(?largest_niche);
|
||||
|
||||
// `max` is the last valid discriminant before the largest niche
|
||||
// `min` is the first valid discriminant after the largest niche
|
||||
let (max, min) = largest_niche
|
||||
// We might have no inhabited variants, so pretend there's at least one.
|
||||
.unwrap_or((0, 0));
|
||||
let (min_ity, signed) = discr_range_of_repr(min, max); //Integer::repr_discr(tcx, ty, &repr, min, max);
|
||||
|
||||
let mut align = dl.aggregate_align;
|
||||
|
|
|
|||
|
|
@ -1205,6 +1205,19 @@ impl Integer {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns the smallest signed value that can be represented by this Integer.
|
||||
#[inline]
|
||||
pub fn signed_min(self) -> i128 {
|
||||
use Integer::*;
|
||||
match self {
|
||||
I8 => i8::MIN as i128,
|
||||
I16 => i16::MIN as i128,
|
||||
I32 => i32::MIN as i128,
|
||||
I64 => i64::MIN as i128,
|
||||
I128 => i128::MIN,
|
||||
}
|
||||
}
|
||||
|
||||
/// Finds the smallest Integer type which can represent the signed value.
|
||||
#[inline]
|
||||
pub fn fit_signed(x: i128) -> Integer {
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
|
||||
fn lower_local(&mut self, l: &Local) -> &'hir hir::LetStmt<'hir> {
|
||||
// Let statements are allowed to have impl trait in bindings.
|
||||
let super_ = l.super_;
|
||||
let super_ = l.super_.map(|span| self.lower_span(span));
|
||||
let ty = l.ty.as_ref().map(|t| {
|
||||
self.lower_ty(t, self.impl_trait_in_bindings_ctxt(ImplTraitPosition::Variable))
|
||||
});
|
||||
|
|
|
|||
|
|
@ -282,9 +282,11 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
ExprKind::Field(el, ident) => {
|
||||
hir::ExprKind::Field(self.lower_expr(el), self.lower_ident(*ident))
|
||||
}
|
||||
ExprKind::Index(el, er, brackets_span) => {
|
||||
hir::ExprKind::Index(self.lower_expr(el), self.lower_expr(er), *brackets_span)
|
||||
}
|
||||
ExprKind::Index(el, er, brackets_span) => hir::ExprKind::Index(
|
||||
self.lower_expr(el),
|
||||
self.lower_expr(er),
|
||||
self.lower_span(*brackets_span),
|
||||
),
|
||||
ExprKind::Range(e1, e2, lims) => {
|
||||
self.lower_expr_range(e.span, e1.as_deref(), e2.as_deref(), *lims)
|
||||
}
|
||||
|
|
@ -334,7 +336,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
ExprKind::Struct(se) => {
|
||||
let rest = match &se.rest {
|
||||
StructRest::Base(e) => hir::StructTailExpr::Base(self.lower_expr(e)),
|
||||
StructRest::Rest(sp) => hir::StructTailExpr::DefaultFields(*sp),
|
||||
StructRest::Rest(sp) => {
|
||||
hir::StructTailExpr::DefaultFields(self.lower_span(*sp))
|
||||
}
|
||||
StructRest::None => hir::StructTailExpr::None,
|
||||
};
|
||||
hir::ExprKind::Struct(
|
||||
|
|
@ -678,6 +682,14 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
hir::Arm { hir_id, pat, guard, body, span }
|
||||
}
|
||||
|
||||
fn lower_capture_clause(&mut self, capture_clause: CaptureBy) -> CaptureBy {
|
||||
match capture_clause {
|
||||
CaptureBy::Ref => CaptureBy::Ref,
|
||||
CaptureBy::Use { use_kw } => CaptureBy::Use { use_kw: self.lower_span(use_kw) },
|
||||
CaptureBy::Value { move_kw } => CaptureBy::Value { move_kw: self.lower_span(move_kw) },
|
||||
}
|
||||
}
|
||||
|
||||
/// Lower/desugar a coroutine construct.
|
||||
///
|
||||
/// In particular, this creates the correct async resume argument and `_task_context`.
|
||||
|
|
@ -769,7 +781,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
hir::ExprKind::Closure(self.arena.alloc(hir::Closure {
|
||||
def_id: closure_def_id,
|
||||
binder: hir::ClosureBinder::Default,
|
||||
capture_clause,
|
||||
capture_clause: self.lower_capture_clause(capture_clause),
|
||||
bound_generic_params: &[],
|
||||
fn_decl,
|
||||
body,
|
||||
|
|
@ -1035,7 +1047,7 @@ 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)
|
||||
hir::ExprKind::Use(self.lower_expr(expr), self.lower_span(use_kw_span))
|
||||
}
|
||||
|
||||
fn lower_expr_closure(
|
||||
|
|
@ -1083,7 +1095,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
let c = self.arena.alloc(hir::Closure {
|
||||
def_id: closure_def_id,
|
||||
binder: binder_clause,
|
||||
capture_clause,
|
||||
capture_clause: self.lower_capture_clause(capture_clause),
|
||||
bound_generic_params,
|
||||
fn_decl,
|
||||
body: body_id,
|
||||
|
|
@ -1197,7 +1209,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
let c = self.arena.alloc(hir::Closure {
|
||||
def_id: closure_def_id,
|
||||
binder: binder_clause,
|
||||
capture_clause,
|
||||
capture_clause: self.lower_capture_clause(capture_clause),
|
||||
bound_generic_params,
|
||||
fn_decl,
|
||||
body,
|
||||
|
|
@ -2101,7 +2113,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
|
||||
fn expr_uint(&mut self, sp: Span, ty: ast::UintTy, value: u128) -> hir::Expr<'hir> {
|
||||
let lit = hir::Lit {
|
||||
span: sp,
|
||||
span: self.lower_span(sp),
|
||||
node: ast::LitKind::Int(value.into(), ast::LitIntType::Unsigned(ty)),
|
||||
};
|
||||
self.expr(sp, hir::ExprKind::Lit(lit))
|
||||
|
|
@ -2120,7 +2132,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
}
|
||||
|
||||
pub(super) fn expr_str(&mut self, sp: Span, value: Symbol) -> hir::Expr<'hir> {
|
||||
let lit = hir::Lit { span: sp, node: ast::LitKind::Str(value, ast::StrStyle::Cooked) };
|
||||
let lit = hir::Lit {
|
||||
span: self.lower_span(sp),
|
||||
node: ast::LitKind::Str(value, ast::StrStyle::Cooked),
|
||||
};
|
||||
self.expr(sp, hir::ExprKind::Lit(lit))
|
||||
}
|
||||
|
||||
|
|
@ -2206,7 +2221,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
self.arena.alloc(hir::Path {
|
||||
span: self.lower_span(span),
|
||||
res,
|
||||
segments: arena_vec![self; hir::PathSegment::new(ident, hir_id, res)],
|
||||
segments: arena_vec![self; hir::PathSegment::new(self.lower_ident(ident), hir_id, res)],
|
||||
}),
|
||||
));
|
||||
|
||||
|
|
|
|||
|
|
@ -402,6 +402,8 @@ fn expand_format_args<'hir>(
|
|||
fmt: &FormatArgs,
|
||||
allow_const: bool,
|
||||
) -> hir::ExprKind<'hir> {
|
||||
let macsp = ctx.lower_span(macsp);
|
||||
|
||||
let mut incomplete_lit = String::new();
|
||||
let lit_pieces =
|
||||
ctx.arena.alloc_from_iter(fmt.template.iter().enumerate().filter_map(|(i, piece)| {
|
||||
|
|
|
|||
|
|
@ -164,11 +164,11 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> {
|
|||
fn visit_item(&mut self, i: &'hir Item<'hir>) {
|
||||
debug_assert_eq!(i.owner_id, self.owner);
|
||||
self.with_parent(i.hir_id(), |this| {
|
||||
if let ItemKind::Struct(_, _, struct_def) = &i.kind {
|
||||
if let ItemKind::Struct(_, _, struct_def) = &i.kind
|
||||
// If this is a tuple or unit-like struct, register the constructor.
|
||||
if let Some(ctor_hir_id) = struct_def.ctor_hir_id() {
|
||||
this.insert(i.span, ctor_hir_id, Node::Ctor(struct_def));
|
||||
}
|
||||
&& let Some(ctor_hir_id) = struct_def.ctor_hir_id()
|
||||
{
|
||||
this.insert(i.span, ctor_hir_id, Node::Ctor(struct_def));
|
||||
}
|
||||
intravisit::walk_item(this, i);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -627,6 +627,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
} else {
|
||||
// For non-empty lists we can just drop all the data, the prefix is already
|
||||
// present in HIR as a part of nested imports.
|
||||
let span = self.lower_span(span);
|
||||
self.arena.alloc(hir::UsePath { res: PerNS::default(), segments: &[], span })
|
||||
};
|
||||
hir::ItemKind::Use(path, hir::UseKind::ListStem)
|
||||
|
|
@ -1567,7 +1568,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
attrs: &[hir::Attribute],
|
||||
) -> hir::FnHeader {
|
||||
let asyncness = if let Some(CoroutineKind::Async { span, .. }) = h.coroutine_kind {
|
||||
hir::IsAsync::Async(span)
|
||||
hir::IsAsync::Async(self.lower_span(span))
|
||||
} else {
|
||||
hir::IsAsync::NotAsync
|
||||
};
|
||||
|
|
@ -1804,7 +1805,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
let res = Res::Def(DefKind::TyParam, def_id);
|
||||
let ident = self.lower_ident(ident);
|
||||
let ty_path = self.arena.alloc(hir::Path {
|
||||
span: param_span,
|
||||
span: self.lower_span(param_span),
|
||||
res,
|
||||
segments: self
|
||||
.arena
|
||||
|
|
|
|||
|
|
@ -2368,7 +2368,17 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
&mut self,
|
||||
modifiers: TraitBoundModifiers,
|
||||
) -> hir::TraitBoundModifiers {
|
||||
hir::TraitBoundModifiers { constness: modifiers.constness, polarity: modifiers.polarity }
|
||||
let constness = match modifiers.constness {
|
||||
BoundConstness::Never => BoundConstness::Never,
|
||||
BoundConstness::Always(span) => BoundConstness::Always(self.lower_span(span)),
|
||||
BoundConstness::Maybe(span) => BoundConstness::Maybe(self.lower_span(span)),
|
||||
};
|
||||
let polarity = match modifiers.polarity {
|
||||
BoundPolarity::Positive => BoundPolarity::Positive,
|
||||
BoundPolarity::Negative(span) => BoundPolarity::Negative(self.lower_span(span)),
|
||||
BoundPolarity::Maybe(span) => BoundPolarity::Maybe(self.lower_span(span)),
|
||||
};
|
||||
hir::TraitBoundModifiers { constness, polarity }
|
||||
}
|
||||
|
||||
// Helper methods for building HIR.
|
||||
|
|
@ -2414,6 +2424,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
init: Option<&'hir hir::Expr<'hir>>,
|
||||
) -> hir::Stmt<'hir> {
|
||||
let hir_id = self.next_id();
|
||||
let span = self.lower_span(span);
|
||||
let local = hir::LetStmt {
|
||||
super_: Some(span),
|
||||
hir_id,
|
||||
|
|
@ -2421,7 +2432,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
pat,
|
||||
els: None,
|
||||
source: hir::LocalSource::Normal,
|
||||
span: self.lower_span(span),
|
||||
span,
|
||||
ty: None,
|
||||
};
|
||||
self.stmt(span, hir::StmtKind::Let(self.arena.alloc(local)))
|
||||
|
|
|
|||
|
|
@ -640,16 +640,16 @@ impl<'a> AstValidator<'a> {
|
|||
return;
|
||||
}
|
||||
|
||||
if let Some(header) = fk.header() {
|
||||
if let Const::Yes(const_span) = header.constness {
|
||||
let mut spans = variadic_spans.clone();
|
||||
spans.push(const_span);
|
||||
self.dcx().emit_err(errors::ConstAndCVariadic {
|
||||
spans,
|
||||
const_span,
|
||||
variadic_spans: variadic_spans.clone(),
|
||||
});
|
||||
}
|
||||
if let Some(header) = fk.header()
|
||||
&& let Const::Yes(const_span) = header.constness
|
||||
{
|
||||
let mut spans = variadic_spans.clone();
|
||||
spans.push(const_span);
|
||||
self.dcx().emit_err(errors::ConstAndCVariadic {
|
||||
spans,
|
||||
const_span,
|
||||
variadic_spans: variadic_spans.clone(),
|
||||
});
|
||||
}
|
||||
|
||||
match (fk.ctxt(), fk.header()) {
|
||||
|
|
|
|||
|
|
@ -630,16 +630,11 @@ fn check_incompatible_features(sess: &Session, features: &Features) {
|
|||
.iter()
|
||||
.filter(|(f1, f2)| features.enabled(*f1) && features.enabled(*f2))
|
||||
{
|
||||
if let Some((f1_name, f1_span)) = enabled_features.clone().find(|(name, _)| name == f1) {
|
||||
if let Some((f2_name, f2_span)) = enabled_features.clone().find(|(name, _)| name == f2)
|
||||
{
|
||||
let spans = vec![f1_span, f2_span];
|
||||
sess.dcx().emit_err(errors::IncompatibleFeatures {
|
||||
spans,
|
||||
f1: f1_name,
|
||||
f2: f2_name,
|
||||
});
|
||||
}
|
||||
if let Some((f1_name, f1_span)) = enabled_features.clone().find(|(name, _)| name == f1)
|
||||
&& let Some((f2_name, f2_span)) = enabled_features.clone().find(|(name, _)| name == f2)
|
||||
{
|
||||
let spans = vec![f1_span, f2_span];
|
||||
sess.dcx().emit_err(errors::IncompatibleFeatures { spans, f1: f1_name, f2: f2_name });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -572,10 +572,10 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
|
|||
}
|
||||
|
||||
fn maybe_print_trailing_comment(&mut self, span: rustc_span::Span, next_pos: Option<BytePos>) {
|
||||
if let Some(cmnts) = self.comments_mut() {
|
||||
if let Some(cmnt) = cmnts.trailing_comment(span, next_pos) {
|
||||
self.print_comment(cmnt);
|
||||
}
|
||||
if let Some(cmnts) = self.comments_mut()
|
||||
&& let Some(cmnt) = cmnts.trailing_comment(span, next_pos)
|
||||
{
|
||||
self.print_comment(cmnt);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -110,18 +110,10 @@ pub enum DeprecatedSince {
|
|||
Err,
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Copy,
|
||||
Debug,
|
||||
Eq,
|
||||
PartialEq,
|
||||
Encodable,
|
||||
Decodable,
|
||||
Clone,
|
||||
HashStable_Generic,
|
||||
PrintAttribute
|
||||
)]
|
||||
pub enum CoverageStatus {
|
||||
/// Successfully-parsed value of a `#[coverage(..)]` attribute.
|
||||
#[derive(Copy, Debug, Eq, PartialEq, Encodable, Decodable, Clone)]
|
||||
#[derive(HashStable_Generic, PrintAttribute)]
|
||||
pub enum CoverageAttrKind {
|
||||
On,
|
||||
Off,
|
||||
}
|
||||
|
|
@ -304,8 +296,8 @@ pub enum AttributeKind {
|
|||
/// Represents `#[const_trait]`.
|
||||
ConstTrait(Span),
|
||||
|
||||
/// Represents `#[coverage]`.
|
||||
Coverage(Span, CoverageStatus),
|
||||
/// Represents `#[coverage(..)]`.
|
||||
Coverage(Span, CoverageAttrKind),
|
||||
|
||||
///Represents `#[rustc_deny_explicit_impl]`.
|
||||
DenyExplicitImpl(Span),
|
||||
|
|
@ -416,12 +408,24 @@ pub enum AttributeKind {
|
|||
/// Represents `#[pointee]`
|
||||
Pointee(Span),
|
||||
|
||||
/// Represents `#[proc_macro]`
|
||||
ProcMacro(Span),
|
||||
|
||||
/// Represents `#[proc_macro_attribute]`
|
||||
ProcMacroAttribute(Span),
|
||||
|
||||
/// Represents `#[proc_macro_derive]`
|
||||
ProcMacroDerive { trait_name: Symbol, helper_attrs: ThinVec<Symbol>, span: Span },
|
||||
|
||||
/// Represents `#[rustc_pub_transparent]` (used by the `repr_transparent_external_private_fields` lint).
|
||||
PubTransparent(Span),
|
||||
|
||||
/// Represents [`#[repr]`](https://doc.rust-lang.org/stable/reference/type-layout.html#representations).
|
||||
Repr { reprs: ThinVec<(ReprAttr, Span)>, first_span: Span },
|
||||
|
||||
/// Represents `#[rustc_builtin_macro]`.
|
||||
RustcBuiltinMacro { builtin_name: Option<Symbol>, helper_attrs: ThinVec<Symbol>, span: Span },
|
||||
|
||||
/// Represents `#[rustc_layout_scalar_valid_range_end]`.
|
||||
RustcLayoutScalarValidRangeEnd(Box<u128>, Span),
|
||||
|
||||
|
|
|
|||
|
|
@ -61,8 +61,12 @@ impl AttributeKind {
|
|||
PassByValue(..) => Yes,
|
||||
Path(..) => No,
|
||||
Pointee(..) => No,
|
||||
ProcMacro(..) => No,
|
||||
ProcMacroAttribute(..) => No,
|
||||
ProcMacroDerive { .. } => No,
|
||||
PubTransparent(..) => Yes,
|
||||
Repr { .. } => No,
|
||||
RustcBuiltinMacro { .. } => Yes,
|
||||
RustcLayoutScalarValidRangeEnd(..) => Yes,
|
||||
RustcLayoutScalarValidRangeStart(..) => Yes,
|
||||
RustcObjectLifetimeDefault => No,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use rustc_attr_data_structures::{AttributeKind, CoverageStatus, OptimizeAttr, UsedBy};
|
||||
use rustc_attr_data_structures::{AttributeKind, CoverageAttrKind, OptimizeAttr, UsedBy};
|
||||
use rustc_feature::{AttributeTemplate, template};
|
||||
use rustc_session::parse::feature_err;
|
||||
use rustc_span::{Span, Symbol, sym};
|
||||
|
|
@ -78,16 +78,16 @@ impl<S: Stage> SingleAttributeParser<S> for CoverageParser {
|
|||
return None;
|
||||
};
|
||||
|
||||
let status = match arg.path().word_sym() {
|
||||
Some(sym::off) => CoverageStatus::Off,
|
||||
Some(sym::on) => CoverageStatus::On,
|
||||
let kind = match arg.path().word_sym() {
|
||||
Some(sym::off) => CoverageAttrKind::Off,
|
||||
Some(sym::on) => CoverageAttrKind::On,
|
||||
None | Some(_) => {
|
||||
fail_incorrect_argument(arg.span());
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
Some(AttributeKind::Coverage(cx.attr_span, status))
|
||||
Some(AttributeKind::Coverage(cx.attr_span, kind))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ pub(crate) mod must_use;
|
|||
pub(crate) mod no_implicit_prelude;
|
||||
pub(crate) mod non_exhaustive;
|
||||
pub(crate) mod path;
|
||||
pub(crate) mod proc_macro_attrs;
|
||||
pub(crate) mod repr;
|
||||
pub(crate) mod rustc_internal;
|
||||
pub(crate) mod semantics;
|
||||
|
|
|
|||
139
compiler/rustc_attr_parsing/src/attributes/proc_macro_attrs.rs
Normal file
139
compiler/rustc_attr_parsing/src/attributes/proc_macro_attrs.rs
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
use rustc_attr_data_structures::AttributeKind;
|
||||
use rustc_feature::{AttributeTemplate, template};
|
||||
use rustc_span::{Span, Symbol, sym};
|
||||
use thin_vec::ThinVec;
|
||||
|
||||
use crate::attributes::{
|
||||
AttributeOrder, NoArgsAttributeParser, OnDuplicate, SingleAttributeParser,
|
||||
};
|
||||
use crate::context::{AcceptContext, Stage};
|
||||
use crate::parser::ArgParser;
|
||||
|
||||
pub(crate) struct ProcMacroParser;
|
||||
impl<S: Stage> NoArgsAttributeParser<S> for ProcMacroParser {
|
||||
const PATH: &[Symbol] = &[sym::proc_macro];
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
|
||||
const CREATE: fn(Span) -> AttributeKind = AttributeKind::ProcMacro;
|
||||
}
|
||||
|
||||
pub(crate) struct ProcMacroAttributeParser;
|
||||
impl<S: Stage> NoArgsAttributeParser<S> for ProcMacroAttributeParser {
|
||||
const PATH: &[Symbol] = &[sym::proc_macro_attribute];
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
|
||||
const CREATE: fn(Span) -> AttributeKind = AttributeKind::ProcMacroAttribute;
|
||||
}
|
||||
|
||||
pub(crate) struct ProcMacroDeriveParser;
|
||||
impl<S: Stage> SingleAttributeParser<S> for ProcMacroDeriveParser {
|
||||
const PATH: &[Symbol] = &[sym::proc_macro_derive];
|
||||
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
|
||||
const TEMPLATE: AttributeTemplate =
|
||||
template!(List: "TraitName, /*opt*/ attributes(name1, name2, ...)");
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
let (trait_name, helper_attrs) = parse_derive_like(cx, args, true)?;
|
||||
Some(AttributeKind::ProcMacroDerive {
|
||||
trait_name: trait_name.expect("Trait name is mandatory, so it is present"),
|
||||
helper_attrs,
|
||||
span: cx.attr_span,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct RustcBuiltinMacroParser;
|
||||
impl<S: Stage> SingleAttributeParser<S> for RustcBuiltinMacroParser {
|
||||
const PATH: &[Symbol] = &[sym::rustc_builtin_macro];
|
||||
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
|
||||
const TEMPLATE: AttributeTemplate =
|
||||
template!(List: "TraitName, /*opt*/ attributes(name1, name2, ...)");
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
let (builtin_name, helper_attrs) = parse_derive_like(cx, args, false)?;
|
||||
Some(AttributeKind::RustcBuiltinMacro { builtin_name, helper_attrs, span: cx.attr_span })
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_derive_like<S: Stage>(
|
||||
cx: &mut AcceptContext<'_, '_, S>,
|
||||
args: &ArgParser<'_>,
|
||||
trait_name_mandatory: bool,
|
||||
) -> Option<(Option<Symbol>, ThinVec<Symbol>)> {
|
||||
let Some(list) = args.list() else {
|
||||
// For #[rustc_builtin_macro], it is permitted to leave out the trait name
|
||||
if args.no_args().is_ok() && !trait_name_mandatory {
|
||||
return Some((None, ThinVec::new()));
|
||||
}
|
||||
cx.expected_list(cx.attr_span);
|
||||
return None;
|
||||
};
|
||||
let mut items = list.mixed();
|
||||
|
||||
// Parse the name of the trait that is derived.
|
||||
let Some(trait_attr) = items.next() else {
|
||||
cx.expected_at_least_one_argument(list.span);
|
||||
return None;
|
||||
};
|
||||
let Some(trait_attr) = trait_attr.meta_item() else {
|
||||
cx.unexpected_literal(trait_attr.span());
|
||||
return None;
|
||||
};
|
||||
let Some(trait_ident) = trait_attr.path().word() else {
|
||||
cx.expected_identifier(trait_attr.path().span());
|
||||
return None;
|
||||
};
|
||||
if !trait_ident.name.can_be_raw() {
|
||||
cx.expected_identifier(trait_ident.span);
|
||||
return None;
|
||||
}
|
||||
if let Err(e) = trait_attr.args().no_args() {
|
||||
cx.expected_no_args(e);
|
||||
return None;
|
||||
};
|
||||
|
||||
// Parse optional attributes
|
||||
let mut attributes = ThinVec::new();
|
||||
if let Some(attrs) = items.next() {
|
||||
let Some(attr_list) = attrs.meta_item() else {
|
||||
cx.expected_list(attrs.span());
|
||||
return None;
|
||||
};
|
||||
if !attr_list.path().word_is(sym::attributes) {
|
||||
cx.expected_specific_argument(attrs.span(), vec!["attributes"]);
|
||||
return None;
|
||||
}
|
||||
let Some(attr_list) = attr_list.args().list() else {
|
||||
cx.expected_list(attrs.span());
|
||||
return None;
|
||||
};
|
||||
|
||||
// Parse item in `attributes(...)` argument
|
||||
for attr in attr_list.mixed() {
|
||||
let Some(attr) = attr.meta_item() else {
|
||||
cx.expected_identifier(attr.span());
|
||||
return None;
|
||||
};
|
||||
if let Err(e) = attr.args().no_args() {
|
||||
cx.expected_no_args(e);
|
||||
return None;
|
||||
};
|
||||
let Some(ident) = attr.path().word() else {
|
||||
cx.expected_identifier(attr.path().span());
|
||||
return None;
|
||||
};
|
||||
if !ident.name.can_be_raw() {
|
||||
cx.expected_identifier(ident.span);
|
||||
return None;
|
||||
}
|
||||
attributes.push(ident.name);
|
||||
}
|
||||
}
|
||||
|
||||
// If anything else is specified, we should reject it
|
||||
if let Some(next) = items.next() {
|
||||
cx.expected_no_args(next.span());
|
||||
}
|
||||
|
||||
Some((Some(trait_ident.name), attributes))
|
||||
}
|
||||
|
|
@ -38,6 +38,9 @@ use crate::attributes::must_use::MustUseParser;
|
|||
use crate::attributes::no_implicit_prelude::NoImplicitPreludeParser;
|
||||
use crate::attributes::non_exhaustive::NonExhaustiveParser;
|
||||
use crate::attributes::path::PathParser as PathAttributeParser;
|
||||
use crate::attributes::proc_macro_attrs::{
|
||||
ProcMacroAttributeParser, ProcMacroDeriveParser, ProcMacroParser, RustcBuiltinMacroParser,
|
||||
};
|
||||
use crate::attributes::repr::{AlignParser, ReprParser};
|
||||
use crate::attributes::rustc_internal::{
|
||||
RustcLayoutScalarValidRangeEnd, RustcLayoutScalarValidRangeStart,
|
||||
|
|
@ -154,6 +157,8 @@ attribute_parsers!(
|
|||
Single<MustUseParser>,
|
||||
Single<OptimizeParser>,
|
||||
Single<PathAttributeParser>,
|
||||
Single<ProcMacroDeriveParser>,
|
||||
Single<RustcBuiltinMacroParser>,
|
||||
Single<RustcForceInlineParser>,
|
||||
Single<RustcLayoutScalarValidRangeEnd>,
|
||||
Single<RustcLayoutScalarValidRangeStart>,
|
||||
|
|
@ -186,6 +191,8 @@ attribute_parsers!(
|
|||
Single<WithoutArgs<ParenSugarParser>>,
|
||||
Single<WithoutArgs<PassByValueParser>>,
|
||||
Single<WithoutArgs<PointeeParser>>,
|
||||
Single<WithoutArgs<ProcMacroAttributeParser>>,
|
||||
Single<WithoutArgs<ProcMacroParser>>,
|
||||
Single<WithoutArgs<PubTransparentParser>>,
|
||||
Single<WithoutArgs<SpecializationTraitParser>>,
|
||||
Single<WithoutArgs<StdInternalSymbolParser>>,
|
||||
|
|
|
|||
|
|
@ -2384,7 +2384,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
if let Some(body_expr) = finder.body_expr
|
||||
&& let Some(loop_span) = finder.loop_span
|
||||
&& let Some(def_id) = typeck_results.type_dependent_def_id(body_expr.hir_id)
|
||||
&& let Some(trait_did) = tcx.trait_of_item(def_id)
|
||||
&& let Some(trait_did) = tcx.trait_of_assoc(def_id)
|
||||
&& tcx.is_diagnostic_item(sym::Iterator, trait_did)
|
||||
{
|
||||
if let Some(loop_bind) = finder.loop_bind {
|
||||
|
|
@ -2533,13 +2533,13 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
|
||||
// Check that the parent of the closure is a method call,
|
||||
// with receiver matching with local's type (modulo refs)
|
||||
if let hir::Node::Expr(parent) = tcx.parent_hir_node(closure_expr.hir_id) {
|
||||
if let hir::ExprKind::MethodCall(_, recv, ..) = parent.kind {
|
||||
let recv_ty = typeck_results.expr_ty(recv);
|
||||
if let hir::Node::Expr(parent) = tcx.parent_hir_node(closure_expr.hir_id)
|
||||
&& let hir::ExprKind::MethodCall(_, recv, ..) = parent.kind
|
||||
{
|
||||
let recv_ty = typeck_results.expr_ty(recv);
|
||||
|
||||
if recv_ty.peel_refs() != local_ty {
|
||||
return;
|
||||
}
|
||||
if recv_ty.peel_refs() != local_ty {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2805,16 +2805,16 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
// With the place of a union and a field access into it, we traverse the second
|
||||
// borrowed place and look for an access to a different field of the same union.
|
||||
for (place_base, elem) in second_borrowed_place.iter_projections().rev() {
|
||||
if let ProjectionElem::Field(field, _) = elem {
|
||||
if let Some(union_ty) = union_ty(place_base) {
|
||||
if field != target_field && place_base == target_base {
|
||||
return Some((
|
||||
self.describe_any_place(place_base),
|
||||
self.describe_any_place(first_borrowed_place.as_ref()),
|
||||
self.describe_any_place(second_borrowed_place.as_ref()),
|
||||
union_ty.to_string(),
|
||||
));
|
||||
}
|
||||
if let ProjectionElem::Field(field, _) = elem
|
||||
&& let Some(union_ty) = union_ty(place_base)
|
||||
{
|
||||
if field != target_field && place_base == target_base {
|
||||
return Some((
|
||||
self.describe_any_place(place_base),
|
||||
self.describe_any_place(first_borrowed_place.as_ref()),
|
||||
self.describe_any_place(second_borrowed_place.as_ref()),
|
||||
union_ty.to_string(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3001,16 +3001,15 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
from_closure: false,
|
||||
..
|
||||
} = explanation
|
||||
{
|
||||
if let Err(diag) = self.try_report_cannot_return_reference_to_local(
|
||||
&& let Err(diag) = self.try_report_cannot_return_reference_to_local(
|
||||
borrow,
|
||||
borrow_span,
|
||||
span,
|
||||
category,
|
||||
opt_place_desc.as_ref(),
|
||||
) {
|
||||
return diag;
|
||||
}
|
||||
)
|
||||
{
|
||||
return diag;
|
||||
}
|
||||
|
||||
let name = format!("`{name}`");
|
||||
|
|
@ -3772,30 +3771,30 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
let loan_span = loan_spans.args_or_use();
|
||||
|
||||
let descr_place = self.describe_any_place(place.as_ref());
|
||||
if let BorrowKind::Fake(_) = loan.kind {
|
||||
if let Some(section) = self.classify_immutable_section(loan.assigned_place) {
|
||||
let mut err = self.cannot_mutate_in_immutable_section(
|
||||
span,
|
||||
loan_span,
|
||||
&descr_place,
|
||||
section,
|
||||
"assign",
|
||||
);
|
||||
if let BorrowKind::Fake(_) = loan.kind
|
||||
&& let Some(section) = self.classify_immutable_section(loan.assigned_place)
|
||||
{
|
||||
let mut err = self.cannot_mutate_in_immutable_section(
|
||||
span,
|
||||
loan_span,
|
||||
&descr_place,
|
||||
section,
|
||||
"assign",
|
||||
);
|
||||
|
||||
loan_spans.var_subdiag(&mut err, Some(loan.kind), |kind, var_span| {
|
||||
use crate::session_diagnostics::CaptureVarCause::*;
|
||||
match kind {
|
||||
hir::ClosureKind::Coroutine(_) => BorrowUseInCoroutine { var_span },
|
||||
hir::ClosureKind::Closure | hir::ClosureKind::CoroutineClosure(_) => {
|
||||
BorrowUseInClosure { var_span }
|
||||
}
|
||||
loan_spans.var_subdiag(&mut err, Some(loan.kind), |kind, var_span| {
|
||||
use crate::session_diagnostics::CaptureVarCause::*;
|
||||
match kind {
|
||||
hir::ClosureKind::Coroutine(_) => BorrowUseInCoroutine { var_span },
|
||||
hir::ClosureKind::Closure | hir::ClosureKind::CoroutineClosure(_) => {
|
||||
BorrowUseInClosure { var_span }
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
self.buffer_error(err);
|
||||
self.buffer_error(err);
|
||||
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let mut err = self.cannot_assign_to_borrowed(span, loan_span, &descr_place);
|
||||
|
|
@ -4048,119 +4047,116 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
"annotate_argument_and_return_for_borrow: target={:?} stmt={:?}",
|
||||
target, stmt
|
||||
);
|
||||
if let StatementKind::Assign(box (place, rvalue)) = &stmt.kind {
|
||||
if let Some(assigned_to) = place.as_local() {
|
||||
debug!(
|
||||
"annotate_argument_and_return_for_borrow: assigned_to={:?} \
|
||||
if let StatementKind::Assign(box (place, rvalue)) = &stmt.kind
|
||||
&& let Some(assigned_to) = place.as_local()
|
||||
{
|
||||
debug!(
|
||||
"annotate_argument_and_return_for_borrow: assigned_to={:?} \
|
||||
rvalue={:?}",
|
||||
assigned_to, rvalue
|
||||
);
|
||||
// Check if our `target` was captured by a closure.
|
||||
if let Rvalue::Aggregate(
|
||||
box AggregateKind::Closure(def_id, args),
|
||||
operands,
|
||||
) = rvalue
|
||||
{
|
||||
let def_id = def_id.expect_local();
|
||||
for operand in operands {
|
||||
let (Operand::Copy(assigned_from) | Operand::Move(assigned_from)) =
|
||||
operand
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
debug!(
|
||||
"annotate_argument_and_return_for_borrow: assigned_from={:?}",
|
||||
assigned_from
|
||||
);
|
||||
assigned_to, rvalue
|
||||
);
|
||||
// Check if our `target` was captured by a closure.
|
||||
if let Rvalue::Aggregate(box AggregateKind::Closure(def_id, args), operands) =
|
||||
rvalue
|
||||
{
|
||||
let def_id = def_id.expect_local();
|
||||
for operand in operands {
|
||||
let (Operand::Copy(assigned_from) | Operand::Move(assigned_from)) =
|
||||
operand
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
debug!(
|
||||
"annotate_argument_and_return_for_borrow: assigned_from={:?}",
|
||||
assigned_from
|
||||
);
|
||||
|
||||
// Find the local from the operand.
|
||||
let Some(assigned_from_local) =
|
||||
assigned_from.local_or_deref_local()
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
// Find the local from the operand.
|
||||
let Some(assigned_from_local) = assigned_from.local_or_deref_local()
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
|
||||
if assigned_from_local != target {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If a closure captured our `target` and then assigned
|
||||
// into a place then we should annotate the closure in
|
||||
// case it ends up being assigned into the return place.
|
||||
annotated_closure =
|
||||
self.annotate_fn_sig(def_id, args.as_closure().sig());
|
||||
debug!(
|
||||
"annotate_argument_and_return_for_borrow: \
|
||||
annotated_closure={:?} assigned_from_local={:?} \
|
||||
assigned_to={:?}",
|
||||
annotated_closure, assigned_from_local, assigned_to
|
||||
);
|
||||
|
||||
if assigned_to == mir::RETURN_PLACE {
|
||||
// If it was assigned directly into the return place, then
|
||||
// return now.
|
||||
return annotated_closure;
|
||||
} else {
|
||||
// Otherwise, update the target.
|
||||
target = assigned_to;
|
||||
}
|
||||
if assigned_from_local != target {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If none of our closure's operands matched, then skip to the next
|
||||
// statement.
|
||||
continue;
|
||||
// If a closure captured our `target` and then assigned
|
||||
// into a place then we should annotate the closure in
|
||||
// case it ends up being assigned into the return place.
|
||||
annotated_closure =
|
||||
self.annotate_fn_sig(def_id, args.as_closure().sig());
|
||||
debug!(
|
||||
"annotate_argument_and_return_for_borrow: \
|
||||
annotated_closure={:?} assigned_from_local={:?} \
|
||||
assigned_to={:?}",
|
||||
annotated_closure, assigned_from_local, assigned_to
|
||||
);
|
||||
|
||||
if assigned_to == mir::RETURN_PLACE {
|
||||
// If it was assigned directly into the return place, then
|
||||
// return now.
|
||||
return annotated_closure;
|
||||
} else {
|
||||
// Otherwise, update the target.
|
||||
target = assigned_to;
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, look at other types of assignment.
|
||||
let assigned_from = match rvalue {
|
||||
Rvalue::Ref(_, _, assigned_from) => assigned_from,
|
||||
Rvalue::Use(operand) => match operand {
|
||||
Operand::Copy(assigned_from) | Operand::Move(assigned_from) => {
|
||||
assigned_from
|
||||
}
|
||||
_ => continue,
|
||||
},
|
||||
_ => continue,
|
||||
};
|
||||
debug!(
|
||||
"annotate_argument_and_return_for_borrow: \
|
||||
assigned_from={:?}",
|
||||
assigned_from,
|
||||
);
|
||||
|
||||
// Find the local from the rvalue.
|
||||
let Some(assigned_from_local) = assigned_from.local_or_deref_local() else {
|
||||
continue;
|
||||
};
|
||||
debug!(
|
||||
"annotate_argument_and_return_for_borrow: \
|
||||
assigned_from_local={:?}",
|
||||
assigned_from_local,
|
||||
);
|
||||
|
||||
// Check if our local matches the target - if so, we've assigned our
|
||||
// borrow to a new place.
|
||||
if assigned_from_local != target {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If we assigned our `target` into a new place, then we should
|
||||
// check if it was the return place.
|
||||
debug!(
|
||||
"annotate_argument_and_return_for_borrow: \
|
||||
assigned_from_local={:?} assigned_to={:?}",
|
||||
assigned_from_local, assigned_to
|
||||
);
|
||||
if assigned_to == mir::RETURN_PLACE {
|
||||
// If it was then return the annotated closure if there was one,
|
||||
// else, annotate this function.
|
||||
return annotated_closure.or_else(fallback);
|
||||
}
|
||||
|
||||
// If we didn't assign into the return place, then we just update
|
||||
// the target.
|
||||
target = assigned_to;
|
||||
// If none of our closure's operands matched, then skip to the next
|
||||
// statement.
|
||||
continue;
|
||||
}
|
||||
|
||||
// Otherwise, look at other types of assignment.
|
||||
let assigned_from = match rvalue {
|
||||
Rvalue::Ref(_, _, assigned_from) => assigned_from,
|
||||
Rvalue::Use(operand) => match operand {
|
||||
Operand::Copy(assigned_from) | Operand::Move(assigned_from) => {
|
||||
assigned_from
|
||||
}
|
||||
_ => continue,
|
||||
},
|
||||
_ => continue,
|
||||
};
|
||||
debug!(
|
||||
"annotate_argument_and_return_for_borrow: \
|
||||
assigned_from={:?}",
|
||||
assigned_from,
|
||||
);
|
||||
|
||||
// Find the local from the rvalue.
|
||||
let Some(assigned_from_local) = assigned_from.local_or_deref_local() else {
|
||||
continue;
|
||||
};
|
||||
debug!(
|
||||
"annotate_argument_and_return_for_borrow: \
|
||||
assigned_from_local={:?}",
|
||||
assigned_from_local,
|
||||
);
|
||||
|
||||
// Check if our local matches the target - if so, we've assigned our
|
||||
// borrow to a new place.
|
||||
if assigned_from_local != target {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If we assigned our `target` into a new place, then we should
|
||||
// check if it was the return place.
|
||||
debug!(
|
||||
"annotate_argument_and_return_for_borrow: \
|
||||
assigned_from_local={:?} assigned_to={:?}",
|
||||
assigned_from_local, assigned_to
|
||||
);
|
||||
if assigned_to == mir::RETURN_PLACE {
|
||||
// If it was then return the annotated closure if there was one,
|
||||
// else, annotate this function.
|
||||
return annotated_closure.or_else(fallback);
|
||||
}
|
||||
|
||||
// If we didn't assign into the return place, then we just update
|
||||
// the target.
|
||||
target = assigned_to;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -4172,32 +4168,31 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
);
|
||||
if let TerminatorKind::Call { destination, target: Some(_), args, .. } =
|
||||
&terminator.kind
|
||||
&& let Some(assigned_to) = destination.as_local()
|
||||
{
|
||||
if let Some(assigned_to) = destination.as_local() {
|
||||
debug!(
|
||||
"annotate_argument_and_return_for_borrow: assigned_to={:?} args={:?}",
|
||||
assigned_to, args
|
||||
);
|
||||
for operand in args {
|
||||
let (Operand::Copy(assigned_from) | Operand::Move(assigned_from)) =
|
||||
&operand.node
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
debug!(
|
||||
"annotate_argument_and_return_for_borrow: assigned_to={:?} args={:?}",
|
||||
assigned_to, args
|
||||
"annotate_argument_and_return_for_borrow: assigned_from={:?}",
|
||||
assigned_from,
|
||||
);
|
||||
for operand in args {
|
||||
let (Operand::Copy(assigned_from) | Operand::Move(assigned_from)) =
|
||||
&operand.node
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
|
||||
if let Some(assigned_from_local) = assigned_from.local_or_deref_local() {
|
||||
debug!(
|
||||
"annotate_argument_and_return_for_borrow: assigned_from={:?}",
|
||||
assigned_from,
|
||||
"annotate_argument_and_return_for_borrow: assigned_from_local={:?}",
|
||||
assigned_from_local,
|
||||
);
|
||||
|
||||
if let Some(assigned_from_local) = assigned_from.local_or_deref_local() {
|
||||
debug!(
|
||||
"annotate_argument_and_return_for_borrow: assigned_from_local={:?}",
|
||||
assigned_from_local,
|
||||
);
|
||||
|
||||
if assigned_to == mir::RETURN_PLACE && assigned_from_local == target {
|
||||
return annotated_closure.or_else(fallback);
|
||||
}
|
||||
if assigned_to == mir::RETURN_PLACE && assigned_from_local == target {
|
||||
return annotated_closure.or_else(fallback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4296,10 +4291,10 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
// as the HIR doesn't have full types for closure arguments.
|
||||
let return_ty = sig.output().skip_binder();
|
||||
let mut return_span = fn_decl.output.span();
|
||||
if let hir::FnRetTy::Return(ty) = &fn_decl.output {
|
||||
if let hir::TyKind::Ref(lifetime, _) = ty.kind {
|
||||
return_span = lifetime.ident.span;
|
||||
}
|
||||
if let hir::FnRetTy::Return(ty) = &fn_decl.output
|
||||
&& let hir::TyKind::Ref(lifetime, _) = ty.kind
|
||||
{
|
||||
return_span = lifetime.ident.span;
|
||||
}
|
||||
|
||||
Some(AnnotatedBorrowFnSignature::NamedFunction {
|
||||
|
|
|
|||
|
|
@ -341,7 +341,7 @@ impl<'tcx> BorrowExplanation<'tcx> {
|
|||
}
|
||||
}
|
||||
} else if let LocalInfo::BlockTailTemp(info) = local_decl.local_info() {
|
||||
let sp = info.span.find_oldest_ancestor_in_same_ctxt();
|
||||
let sp = info.span.find_ancestor_not_from_macro().unwrap_or(info.span);
|
||||
if info.tail_result_is_ignored {
|
||||
// #85581: If the first mutable borrow's scope contains
|
||||
// the second borrow, this suggestion isn't helpful.
|
||||
|
|
@ -917,30 +917,29 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
|
|||
|
||||
if let TerminatorKind::Call { destination, target: Some(block), args, .. } =
|
||||
&terminator.kind
|
||||
&& let Some(dest) = destination.as_local()
|
||||
{
|
||||
if let Some(dest) = destination.as_local() {
|
||||
debug!(
|
||||
"was_captured_by_trait_object: target={:?} dest={:?} args={:?}",
|
||||
target, dest, args
|
||||
);
|
||||
// Check if one of the arguments to this function is the target place.
|
||||
let found_target = args.iter().any(|arg| {
|
||||
if let Operand::Move(place) = arg.node {
|
||||
if let Some(potential) = place.as_local() {
|
||||
potential == target
|
||||
} else {
|
||||
false
|
||||
}
|
||||
debug!(
|
||||
"was_captured_by_trait_object: target={:?} dest={:?} args={:?}",
|
||||
target, dest, args
|
||||
);
|
||||
// Check if one of the arguments to this function is the target place.
|
||||
let found_target = args.iter().any(|arg| {
|
||||
if let Operand::Move(place) = arg.node {
|
||||
if let Some(potential) = place.as_local() {
|
||||
potential == target
|
||||
} else {
|
||||
false
|
||||
}
|
||||
});
|
||||
|
||||
// If it is, follow this to the next block and update the target.
|
||||
if found_target {
|
||||
target = dest;
|
||||
queue.push(block.start_location());
|
||||
} else {
|
||||
false
|
||||
}
|
||||
});
|
||||
|
||||
// If it is, follow this to the next block and update the target.
|
||||
if found_target {
|
||||
target = dest;
|
||||
queue.push(block.start_location());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -266,48 +266,44 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
args,
|
||||
..
|
||||
} = &terminator.kind
|
||||
&& let ty::FnDef(id, _) = *const_.ty().kind()
|
||||
{
|
||||
if let ty::FnDef(id, _) = *const_.ty().kind() {
|
||||
debug!("add_moved_or_invoked_closure_note: id={:?}", id);
|
||||
if self.infcx.tcx.is_lang_item(self.infcx.tcx.parent(id), LangItem::FnOnce) {
|
||||
let closure = match args.first() {
|
||||
Some(Spanned {
|
||||
node: Operand::Copy(place) | Operand::Move(place), ..
|
||||
}) if target == place.local_or_deref_local() => {
|
||||
place.local_or_deref_local().unwrap()
|
||||
}
|
||||
_ => return false,
|
||||
};
|
||||
debug!("add_moved_or_invoked_closure_note: id={:?}", id);
|
||||
if self.infcx.tcx.is_lang_item(self.infcx.tcx.parent(id), LangItem::FnOnce) {
|
||||
let closure = match args.first() {
|
||||
Some(Spanned { node: Operand::Copy(place) | Operand::Move(place), .. })
|
||||
if target == place.local_or_deref_local() =>
|
||||
{
|
||||
place.local_or_deref_local().unwrap()
|
||||
}
|
||||
_ => return false,
|
||||
};
|
||||
|
||||
debug!("add_moved_or_invoked_closure_note: closure={:?}", closure);
|
||||
if let ty::Closure(did, _) = self.body.local_decls[closure].ty.kind() {
|
||||
let did = did.expect_local();
|
||||
if let Some((span, hir_place)) = self.infcx.tcx.closure_kind_origin(did) {
|
||||
diag.subdiagnostic(OnClosureNote::InvokedTwice {
|
||||
place_name: &ty::place_to_string_for_capture(
|
||||
self.infcx.tcx,
|
||||
hir_place,
|
||||
),
|
||||
span: *span,
|
||||
});
|
||||
return true;
|
||||
}
|
||||
debug!("add_moved_or_invoked_closure_note: closure={:?}", closure);
|
||||
if let ty::Closure(did, _) = self.body.local_decls[closure].ty.kind() {
|
||||
let did = did.expect_local();
|
||||
if let Some((span, hir_place)) = self.infcx.tcx.closure_kind_origin(did) {
|
||||
diag.subdiagnostic(OnClosureNote::InvokedTwice {
|
||||
place_name: &ty::place_to_string_for_capture(self.infcx.tcx, hir_place),
|
||||
span: *span,
|
||||
});
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if we are just moving a closure after it has been invoked.
|
||||
if let Some(target) = target {
|
||||
if let ty::Closure(did, _) = self.body.local_decls[target].ty.kind() {
|
||||
let did = did.expect_local();
|
||||
if let Some((span, hir_place)) = self.infcx.tcx.closure_kind_origin(did) {
|
||||
diag.subdiagnostic(OnClosureNote::MovedTwice {
|
||||
place_name: &ty::place_to_string_for_capture(self.infcx.tcx, hir_place),
|
||||
span: *span,
|
||||
});
|
||||
return true;
|
||||
}
|
||||
if let Some(target) = target
|
||||
&& let ty::Closure(did, _) = self.body.local_decls[target].ty.kind()
|
||||
{
|
||||
let did = did.expect_local();
|
||||
if let Some((span, hir_place)) = self.infcx.tcx.closure_kind_origin(did) {
|
||||
diag.subdiagnostic(OnClosureNote::MovedTwice {
|
||||
place_name: &ty::place_to_string_for_capture(self.infcx.tcx, hir_place),
|
||||
span: *span,
|
||||
});
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
|
|
@ -942,7 +938,7 @@ impl<'tcx> BorrowedContentSource<'tcx> {
|
|||
fn from_call(func: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> Option<Self> {
|
||||
match *func.kind() {
|
||||
ty::FnDef(def_id, args) => {
|
||||
let trait_id = tcx.trait_of_item(def_id)?;
|
||||
let trait_id = tcx.trait_of_assoc(def_id)?;
|
||||
|
||||
if tcx.is_lang_item(trait_id, LangItem::Deref)
|
||||
|| tcx.is_lang_item(trait_id, LangItem::DerefMut)
|
||||
|
|
|
|||
|
|
@ -126,36 +126,35 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
.statements
|
||||
.get(location.statement_index)
|
||||
.map(|stmt| &stmt.kind)
|
||||
&& let Some(local) = place.as_local()
|
||||
{
|
||||
if let Some(local) = place.as_local() {
|
||||
let local_decl = &self.body.local_decls[local];
|
||||
// opt_match_place is the
|
||||
// match_span is the span of the expression being matched on
|
||||
// match *x.y { ... } match_place is Some(*x.y)
|
||||
// ^^^^ match_span is the span of *x.y
|
||||
//
|
||||
// opt_match_place is None for let [mut] x = ... statements,
|
||||
// whether or not the right-hand side is a place expression
|
||||
if let LocalInfo::User(BindingForm::Var(VarBindingForm {
|
||||
opt_match_place: Some((opt_match_place, match_span)),
|
||||
binding_mode: _,
|
||||
opt_ty_info: _,
|
||||
pat_span: _,
|
||||
})) = *local_decl.local_info()
|
||||
{
|
||||
let stmt_source_info = self.body.source_info(location);
|
||||
self.append_binding_error(
|
||||
grouped_errors,
|
||||
kind,
|
||||
original_path,
|
||||
*move_from,
|
||||
local,
|
||||
opt_match_place,
|
||||
match_span,
|
||||
stmt_source_info.span,
|
||||
);
|
||||
return;
|
||||
}
|
||||
let local_decl = &self.body.local_decls[local];
|
||||
// opt_match_place is the
|
||||
// match_span is the span of the expression being matched on
|
||||
// match *x.y { ... } match_place is Some(*x.y)
|
||||
// ^^^^ match_span is the span of *x.y
|
||||
//
|
||||
// opt_match_place is None for let [mut] x = ... statements,
|
||||
// whether or not the right-hand side is a place expression
|
||||
if let LocalInfo::User(BindingForm::Var(VarBindingForm {
|
||||
opt_match_place: Some((opt_match_place, match_span)),
|
||||
binding_mode: _,
|
||||
opt_ty_info: _,
|
||||
pat_span: _,
|
||||
})) = *local_decl.local_info()
|
||||
{
|
||||
let stmt_source_info = self.body.source_info(location);
|
||||
self.append_binding_error(
|
||||
grouped_errors,
|
||||
kind,
|
||||
original_path,
|
||||
*move_from,
|
||||
local,
|
||||
opt_match_place,
|
||||
match_span,
|
||||
stmt_source_info.span,
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -682,7 +682,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
}
|
||||
let my_def = self.body.source.def_id();
|
||||
let Some(td) =
|
||||
self.infcx.tcx.impl_of_method(my_def).and_then(|x| self.infcx.tcx.trait_id_of_impl(x))
|
||||
self.infcx.tcx.impl_of_assoc(my_def).and_then(|x| self.infcx.tcx.trait_id_of_impl(x))
|
||||
else {
|
||||
return (false, false, None);
|
||||
};
|
||||
|
|
@ -880,7 +880,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
let opt_suggestions = tcx
|
||||
.typeck(path_segment.hir_id.owner.def_id)
|
||||
.type_dependent_def_id(expr.hir_id)
|
||||
.and_then(|def_id| tcx.impl_of_method(def_id))
|
||||
.and_then(|def_id| tcx.impl_of_assoc(def_id))
|
||||
.map(|def_id| tcx.associated_items(def_id))
|
||||
.map(|assoc_items| {
|
||||
assoc_items
|
||||
|
|
@ -1056,7 +1056,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
.tcx
|
||||
.typeck(path_segment.hir_id.owner.def_id)
|
||||
.type_dependent_def_id(cur_expr.hir_id)
|
||||
.and_then(|def_id| self.infcx.tcx.impl_of_method(def_id))
|
||||
.and_then(|def_id| self.infcx.tcx.impl_of_assoc(def_id))
|
||||
.map(|def_id| self.infcx.tcx.associated_items(def_id))
|
||||
.map(|assoc_items| {
|
||||
assoc_items.filter_by_name_unhygienic(sym::iter_mut).peekable()
|
||||
|
|
|
|||
|
|
@ -528,15 +528,15 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
|
|||
// match_adt_and_segment in this case.
|
||||
Res::Def(DefKind::TyAlias, _) => (),
|
||||
_ => {
|
||||
if let Some(last_segment) = path.segments.last() {
|
||||
if let Some(highlight) = self.match_adt_and_segment(
|
||||
if let Some(last_segment) = path.segments.last()
|
||||
&& let Some(highlight) = self.match_adt_and_segment(
|
||||
args,
|
||||
needle_fr,
|
||||
last_segment,
|
||||
search_stack,
|
||||
) {
|
||||
return Some(highlight);
|
||||
}
|
||||
)
|
||||
{
|
||||
return Some(highlight);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -822,10 +822,10 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
continue;
|
||||
}
|
||||
|
||||
if let Some(propagated_outlives_requirements) = &mut propagated_outlives_requirements {
|
||||
if self.try_promote_type_test(infcx, type_test, propagated_outlives_requirements) {
|
||||
continue;
|
||||
}
|
||||
if let Some(propagated_outlives_requirements) = &mut propagated_outlives_requirements
|
||||
&& self.try_promote_type_test(infcx, type_test, propagated_outlives_requirements)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Type-test failed. Report the error.
|
||||
|
|
@ -1479,40 +1479,36 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
shorter_fr: RegionVid,
|
||||
propagated_outlives_requirements: &mut Option<&mut Vec<ClosureOutlivesRequirement<'tcx>>>,
|
||||
) -> RegionRelationCheckResult {
|
||||
if let Some(propagated_outlives_requirements) = propagated_outlives_requirements {
|
||||
if let Some(propagated_outlives_requirements) = propagated_outlives_requirements
|
||||
// Shrink `longer_fr` until we find a non-local region (if we do).
|
||||
// We'll call it `fr-` -- it's ever so slightly smaller than
|
||||
// `longer_fr`.
|
||||
if let Some(fr_minus) = self.universal_region_relations.non_local_lower_bound(longer_fr)
|
||||
{
|
||||
debug!("try_propagate_universal_region_error: fr_minus={:?}", fr_minus);
|
||||
&& let Some(fr_minus) = self.universal_region_relations.non_local_lower_bound(longer_fr)
|
||||
{
|
||||
debug!("try_propagate_universal_region_error: fr_minus={:?}", fr_minus);
|
||||
|
||||
let blame_span_category = self.find_outlives_blame_span(
|
||||
longer_fr,
|
||||
NllRegionVariableOrigin::FreeRegion,
|
||||
shorter_fr,
|
||||
);
|
||||
let blame_span_category = self.find_outlives_blame_span(
|
||||
longer_fr,
|
||||
NllRegionVariableOrigin::FreeRegion,
|
||||
shorter_fr,
|
||||
);
|
||||
|
||||
// Grow `shorter_fr` until we find some non-local regions. (We
|
||||
// always will.) We'll call them `shorter_fr+` -- they're ever
|
||||
// so slightly larger than `shorter_fr`.
|
||||
let shorter_fr_plus =
|
||||
self.universal_region_relations.non_local_upper_bounds(shorter_fr);
|
||||
debug!(
|
||||
"try_propagate_universal_region_error: shorter_fr_plus={:?}",
|
||||
shorter_fr_plus
|
||||
);
|
||||
for fr in shorter_fr_plus {
|
||||
// Push the constraint `fr-: shorter_fr+`
|
||||
propagated_outlives_requirements.push(ClosureOutlivesRequirement {
|
||||
subject: ClosureOutlivesSubject::Region(fr_minus),
|
||||
outlived_free_region: fr,
|
||||
blame_span: blame_span_category.1.span,
|
||||
category: blame_span_category.0,
|
||||
});
|
||||
}
|
||||
return RegionRelationCheckResult::Propagated;
|
||||
// Grow `shorter_fr` until we find some non-local regions. (We
|
||||
// always will.) We'll call them `shorter_fr+` -- they're ever
|
||||
// so slightly larger than `shorter_fr`.
|
||||
let shorter_fr_plus =
|
||||
self.universal_region_relations.non_local_upper_bounds(shorter_fr);
|
||||
debug!("try_propagate_universal_region_error: shorter_fr_plus={:?}", shorter_fr_plus);
|
||||
for fr in shorter_fr_plus {
|
||||
// Push the constraint `fr-: shorter_fr+`
|
||||
propagated_outlives_requirements.push(ClosureOutlivesRequirement {
|
||||
subject: ClosureOutlivesSubject::Region(fr_minus),
|
||||
outlived_free_region: fr,
|
||||
blame_span: blame_span_category.1.span,
|
||||
category: blame_span_category.0,
|
||||
});
|
||||
}
|
||||
return RegionRelationCheckResult::Propagated;
|
||||
}
|
||||
|
||||
RegionRelationCheckResult::Error
|
||||
|
|
@ -2085,11 +2081,11 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
let locations = self.scc_values.locations_outlived_by(scc);
|
||||
for location in locations {
|
||||
let bb = &body[location.block];
|
||||
if let Some(terminator) = &bb.terminator {
|
||||
if let Some(terminator) = &bb.terminator
|
||||
// terminator of a loop should be TerminatorKind::FalseUnwind
|
||||
if let TerminatorKind::FalseUnwind { .. } = terminator.kind {
|
||||
return Some(location);
|
||||
}
|
||||
&& let TerminatorKind::FalseUnwind { .. } = terminator.kind
|
||||
{
|
||||
return Some(location);
|
||||
}
|
||||
}
|
||||
None
|
||||
|
|
|
|||
|
|
@ -669,24 +669,24 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
|||
);
|
||||
}
|
||||
|
||||
if let Some(annotation_index) = self.rvalue_user_ty(rv) {
|
||||
if let Err(terr) = self.relate_type_and_user_type(
|
||||
if let Some(annotation_index) = self.rvalue_user_ty(rv)
|
||||
&& let Err(terr) = self.relate_type_and_user_type(
|
||||
rv_ty,
|
||||
ty::Invariant,
|
||||
&UserTypeProjection { base: annotation_index, projs: vec![] },
|
||||
location.to_locations(),
|
||||
ConstraintCategory::TypeAnnotation(AnnotationSource::GenericArg),
|
||||
) {
|
||||
let annotation = &self.user_type_annotations[annotation_index];
|
||||
span_mirbug!(
|
||||
self,
|
||||
stmt,
|
||||
"bad user type on rvalue ({:?} = {:?}): {:?}",
|
||||
annotation,
|
||||
rv_ty,
|
||||
terr
|
||||
);
|
||||
}
|
||||
)
|
||||
{
|
||||
let annotation = &self.user_type_annotations[annotation_index];
|
||||
span_mirbug!(
|
||||
self,
|
||||
stmt,
|
||||
"bad user type on rvalue ({:?} = {:?}): {:?}",
|
||||
annotation,
|
||||
rv_ty,
|
||||
terr
|
||||
);
|
||||
}
|
||||
|
||||
if !self.unsized_feature_enabled() {
|
||||
|
|
@ -1761,7 +1761,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
|||
);
|
||||
|
||||
assert!(!matches!(
|
||||
tcx.impl_of_method(def_id).map(|imp| tcx.def_kind(imp)),
|
||||
tcx.impl_of_assoc(def_id).map(|imp| tcx.def_kind(imp)),
|
||||
Some(DefKind::Impl { of_trait: true })
|
||||
));
|
||||
self.prove_predicates(
|
||||
|
|
|
|||
|
|
@ -969,13 +969,28 @@ fn for_each_late_bound_region_in_item<'tcx>(
|
|||
mir_def_id: LocalDefId,
|
||||
mut f: impl FnMut(ty::Region<'tcx>),
|
||||
) {
|
||||
if !tcx.def_kind(mir_def_id).is_fn_like() {
|
||||
return;
|
||||
}
|
||||
let bound_vars = match tcx.def_kind(mir_def_id) {
|
||||
DefKind::Fn | DefKind::AssocFn => {
|
||||
tcx.late_bound_vars(tcx.local_def_id_to_hir_id(mir_def_id))
|
||||
}
|
||||
// We extract the bound vars from the deduced closure signature, since we may have
|
||||
// only deduced that a param in the closure signature is late-bound from a constraint
|
||||
// that we discover during typeck.
|
||||
DefKind::Closure => {
|
||||
let ty = tcx.type_of(mir_def_id).instantiate_identity();
|
||||
match *ty.kind() {
|
||||
ty::Closure(_, args) => args.as_closure().sig().bound_vars(),
|
||||
ty::CoroutineClosure(_, args) => {
|
||||
args.as_coroutine_closure().coroutine_closure_sig().bound_vars()
|
||||
}
|
||||
ty::Coroutine(_, _) | ty::Error(_) => return,
|
||||
_ => unreachable!("unexpected type for closure: {ty}"),
|
||||
}
|
||||
}
|
||||
_ => return,
|
||||
};
|
||||
|
||||
for (idx, bound_var) in
|
||||
tcx.late_bound_vars(tcx.local_def_id_to_hir_id(mir_def_id)).iter().enumerate()
|
||||
{
|
||||
for (idx, bound_var) in bound_vars.iter().enumerate() {
|
||||
if let ty::BoundVariableKind::Region(kind) = bound_var {
|
||||
let kind = ty::LateParamRegionKind::from_bound(ty::BoundVar::from_usize(idx), kind);
|
||||
let liberated_region = ty::Region::new_late_param(tcx, mir_def_id.to_def_id(), kind);
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
use std::mem;
|
||||
use std::{mem, slice};
|
||||
|
||||
use rustc_ast::ptr::P;
|
||||
use rustc_ast::visit::{self, Visitor};
|
||||
use rustc_ast::{self as ast, NodeId, attr};
|
||||
use rustc_ast::{self as ast, HasNodeId, NodeId, attr};
|
||||
use rustc_ast_pretty::pprust;
|
||||
use rustc_attr_data_structures::AttributeKind;
|
||||
use rustc_attr_parsing::AttributeParser;
|
||||
use rustc_errors::DiagCtxtHandle;
|
||||
use rustc_expand::base::{ExtCtxt, ResolverExpand, parse_macro_name_and_helper_attrs};
|
||||
use rustc_expand::base::{ExtCtxt, ResolverExpand};
|
||||
use rustc_expand::expand::{AstFragment, ExpansionConfig};
|
||||
use rustc_feature::Features;
|
||||
use rustc_session::Session;
|
||||
|
|
@ -22,7 +24,7 @@ struct ProcMacroDerive {
|
|||
trait_name: Symbol,
|
||||
function_ident: Ident,
|
||||
span: Span,
|
||||
attrs: Vec<Symbol>,
|
||||
attrs: ThinVec<Symbol>,
|
||||
}
|
||||
|
||||
struct ProcMacroDef {
|
||||
|
|
@ -41,6 +43,7 @@ struct CollectProcMacros<'a> {
|
|||
macros: Vec<ProcMacro>,
|
||||
in_root: bool,
|
||||
dcx: DiagCtxtHandle<'a>,
|
||||
session: &'a Session,
|
||||
source_map: &'a SourceMap,
|
||||
is_proc_macro_crate: bool,
|
||||
is_test_crate: bool,
|
||||
|
|
@ -63,6 +66,7 @@ pub fn inject(
|
|||
macros: Vec::new(),
|
||||
in_root: true,
|
||||
dcx,
|
||||
session: sess,
|
||||
source_map: sess.source_map(),
|
||||
is_proc_macro_crate,
|
||||
is_test_crate,
|
||||
|
|
@ -98,8 +102,18 @@ impl<'a> CollectProcMacros<'a> {
|
|||
function_ident: Ident,
|
||||
attr: &'a ast::Attribute,
|
||||
) {
|
||||
let Some((trait_name, proc_attrs)) =
|
||||
parse_macro_name_and_helper_attrs(self.dcx, attr, "derive")
|
||||
let Some(rustc_hir::Attribute::Parsed(AttributeKind::ProcMacroDerive {
|
||||
trait_name,
|
||||
helper_attrs,
|
||||
..
|
||||
})) = AttributeParser::parse_limited(
|
||||
self.session,
|
||||
slice::from_ref(attr),
|
||||
sym::proc_macro_derive,
|
||||
item.span,
|
||||
item.node_id(),
|
||||
None,
|
||||
)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
|
@ -110,7 +124,7 @@ impl<'a> CollectProcMacros<'a> {
|
|||
span: item.span,
|
||||
trait_name,
|
||||
function_ident,
|
||||
attrs: proc_attrs,
|
||||
attrs: helper_attrs,
|
||||
}));
|
||||
} else {
|
||||
let msg = if !self.in_root {
|
||||
|
|
|
|||
|
|
@ -47,8 +47,6 @@ pub fn inject(
|
|||
ast::ItemKind::ExternCrate(None, Ident::new(name, ident_span)),
|
||||
);
|
||||
|
||||
krate.items.insert(0, item);
|
||||
|
||||
let root = (edition == Edition2015).then_some(kw::PathRoot);
|
||||
|
||||
let import_path = root
|
||||
|
|
@ -75,6 +73,6 @@ pub fn inject(
|
|||
}),
|
||||
);
|
||||
|
||||
krate.items.insert(0, use_item);
|
||||
krate.items.splice(0..0, [item, use_item]);
|
||||
krate.items.len() - orig_num_items
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ index 1e336bf..35e6f54 100644
|
|||
-#![cfg_attr(target_has_atomic = "128", feature(integer_atomics))]
|
||||
#![cfg_attr(test, feature(cfg_select))]
|
||||
#![feature(alloc_layout_extra)]
|
||||
#![feature(array_chunks)]
|
||||
#![feature(array_ptr_get)]
|
||||
diff --git a/coretests/tests/atomic.rs b/coretests/tests/atomic.rs
|
||||
index b735957..ea728b6 100644
|
||||
--- a/coretests/tests/atomic.rs
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ rustc = "$(pwd)/../dist/bin/rustc-clif"
|
|||
cargo = "$(rustup which cargo)"
|
||||
full-bootstrap = true
|
||||
local-rebuild = true
|
||||
compiletest-allow-stage0 = true
|
||||
|
||||
[rust]
|
||||
download-rustc = false
|
||||
|
|
|
|||
|
|
@ -166,5 +166,5 @@ index 073116933bd..c3e4578204d 100644
|
|||
EOF
|
||||
|
||||
echo "[TEST] rustc test suite"
|
||||
COMPILETEST_FORCE_STAGE0=1 ./x.py test --stage 0 --test-args=--no-capture tests/{codegen-units,run-make,ui,incremental}
|
||||
./x.py test --stage 0 --test-args=--no-capture tests/{codegen-units,run-make,ui,incremental}
|
||||
popd
|
||||
|
|
|
|||
|
|
@ -561,8 +561,6 @@ fn asm_tests(env: &Env, args: &TestArg) -> Result<(), String> {
|
|||
// FIXME: create a function "display_if_not_quiet" or something along the line.
|
||||
println!("[TEST] rustc asm test suite");
|
||||
|
||||
env.insert("COMPILETEST_FORCE_STAGE0".to_string(), "1".to_string());
|
||||
|
||||
let codegen_backend_path = format!(
|
||||
"{pwd}/target/{channel}/librustc_codegen_gcc.{dylib_ext}",
|
||||
pwd = std::env::current_dir()
|
||||
|
|
@ -588,6 +586,8 @@ fn asm_tests(env: &Env, args: &TestArg) -> Result<(), String> {
|
|||
&"always",
|
||||
&"--stage",
|
||||
&"0",
|
||||
&"--set",
|
||||
&"build.compiletest-allow-stage0=true",
|
||||
&"tests/assembly-llvm/asm",
|
||||
&"--compiletest-rustc-args",
|
||||
&rustc_args,
|
||||
|
|
@ -1047,7 +1047,6 @@ where
|
|||
|
||||
// FIXME: create a function "display_if_not_quiet" or something along the line.
|
||||
println!("[TEST] rustc {test_type} test suite");
|
||||
env.insert("COMPILETEST_FORCE_STAGE0".to_string(), "1".to_string());
|
||||
|
||||
let extra =
|
||||
if args.is_using_gcc_master_branch() { "" } else { " -Csymbol-mangling-version=v0" };
|
||||
|
|
@ -1070,6 +1069,8 @@ where
|
|||
&"always",
|
||||
&"--stage",
|
||||
&"0",
|
||||
&"--set",
|
||||
&"build.compiletest-allow-stage0=true",
|
||||
&format!("tests/{test_type}"),
|
||||
&"--compiletest-rustc-args",
|
||||
&rustc_args,
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ use gccjit::{Context, OutputKind};
|
|||
use rustc_codegen_ssa::back::link::ensure_removed;
|
||||
use rustc_codegen_ssa::back::write::{BitcodeSection, CodegenContext, EmitObj, ModuleConfig};
|
||||
use rustc_codegen_ssa::{CompiledModule, ModuleCodegen};
|
||||
use rustc_errors::DiagCtxtHandle;
|
||||
use rustc_fs_util::link_or_copy;
|
||||
use rustc_session::config::OutputType;
|
||||
use rustc_span::fatal_error::FatalError;
|
||||
|
|
@ -258,14 +257,6 @@ pub(crate) fn codegen(
|
|||
))
|
||||
}
|
||||
|
||||
pub(crate) fn link(
|
||||
_cgcx: &CodegenContext<GccCodegenBackend>,
|
||||
_dcx: DiagCtxtHandle<'_>,
|
||||
mut _modules: Vec<ModuleCodegen<GccContext>>,
|
||||
) -> Result<ModuleCodegen<GccContext>, FatalError> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
pub(crate) fn save_temp_bitcode(
|
||||
cgcx: &CodegenContext<GccCodegenBackend>,
|
||||
_module: &ModuleCodegen<GccContext>,
|
||||
|
|
|
|||
|
|
@ -426,14 +426,6 @@ impl WriteBackendMethods for GccCodegenBackend {
|
|||
fn serialize_module(_module: ModuleCodegen<Self::Module>) -> (String, Self::ModuleBuffer) {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn run_link(
|
||||
cgcx: &CodegenContext<Self>,
|
||||
dcx: DiagCtxtHandle<'_>,
|
||||
modules: Vec<ModuleCodegen<Self::Module>>,
|
||||
) -> Result<ModuleCodegen<Self::Module>, FatalError> {
|
||||
back::write::link(cgcx, dcx, modules)
|
||||
}
|
||||
}
|
||||
|
||||
/// This is the entrypoint for a hot plugged rustc_codegen_gccjit
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ codegen_llvm_from_llvm_optimization_diag = {$filename}:{$line}:{$column} {$pass_
|
|||
codegen_llvm_load_bitcode = failed to load bitcode of module "{$name}"
|
||||
codegen_llvm_load_bitcode_with_llvm_err = failed to load bitcode of module "{$name}": {$llvm_err}
|
||||
|
||||
codegen_llvm_lto_bitcode_from_rlib = failed to get bitcode from object file for LTO ({$llvm_err})
|
||||
codegen_llvm_lto_bitcode_from_rlib = failed to get bitcode from object file for LTO ({$err})
|
||||
|
||||
codegen_llvm_mismatch_data_layout =
|
||||
data-layout for target `{$rustc_target}`, `{$rustc_layout}`, differs from LLVM target's `{$llvm_target}` default layout, `{$llvm_layout}`
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ use std::sync::Arc;
|
|||
use std::{io, iter, slice};
|
||||
|
||||
use object::read::archive::ArchiveFile;
|
||||
use object::{Object, ObjectSection};
|
||||
use rustc_codegen_ssa::back::lto::{SerializedModule, ThinModule, ThinShared};
|
||||
use rustc_codegen_ssa::back::write::{CodegenContext, FatLtoInput};
|
||||
use rustc_codegen_ssa::traits::*;
|
||||
|
|
@ -105,31 +106,15 @@ fn get_bitcode_slice_from_object_data<'a>(
|
|||
// name" which in the public API for sections gets treated as part of the section name, but
|
||||
// internally in MachOObjectFile.cpp gets treated separately.
|
||||
let section_name = bitcode_section_name(cgcx).to_str().unwrap().trim_start_matches("__LLVM,");
|
||||
let mut len = 0;
|
||||
let data = unsafe {
|
||||
llvm::LLVMRustGetSliceFromObjectDataByName(
|
||||
obj.as_ptr(),
|
||||
obj.len(),
|
||||
section_name.as_ptr(),
|
||||
section_name.len(),
|
||||
&mut len,
|
||||
)
|
||||
};
|
||||
if !data.is_null() {
|
||||
assert!(len != 0);
|
||||
let bc = unsafe { slice::from_raw_parts(data, len) };
|
||||
|
||||
// `bc` must be a sub-slice of `obj`.
|
||||
assert!(obj.as_ptr() <= bc.as_ptr());
|
||||
assert!(bc[bc.len()..bc.len()].as_ptr() <= obj[obj.len()..obj.len()].as_ptr());
|
||||
let obj =
|
||||
object::File::parse(obj).map_err(|err| LtoBitcodeFromRlib { err: err.to_string() })?;
|
||||
|
||||
Ok(bc)
|
||||
} else {
|
||||
assert!(len == 0);
|
||||
Err(LtoBitcodeFromRlib {
|
||||
llvm_err: llvm::last_error().unwrap_or_else(|| "unknown LLVM error".to_string()),
|
||||
})
|
||||
}
|
||||
let section = obj
|
||||
.section_by_name(section_name)
|
||||
.ok_or_else(|| LtoBitcodeFromRlib { err: format!("Can't find section {section_name}") })?;
|
||||
|
||||
section.data().map_err(|err| LtoBitcodeFromRlib { err: err.to_string() })
|
||||
}
|
||||
|
||||
/// Performs fat LTO by merging all modules into a single one and returning it
|
||||
|
|
@ -505,10 +490,10 @@ fn thin_lto(
|
|||
|
||||
// Save the current ThinLTO import information for the next compilation
|
||||
// session, overwriting the previous serialized data (if any).
|
||||
if let Some(path) = key_map_path {
|
||||
if let Err(err) = curr_key_map.save_to_file(&path) {
|
||||
return Err(write::llvm_err(dcx, LlvmError::WriteThinLtoKey { err }));
|
||||
}
|
||||
if let Some(path) = key_map_path
|
||||
&& let Err(err) = curr_key_map.save_to_file(&path)
|
||||
{
|
||||
return Err(write::llvm_err(dcx, LlvmError::WriteThinLtoKey { err }));
|
||||
}
|
||||
|
||||
Ok((opt_jobs, copy_jobs))
|
||||
|
|
|
|||
|
|
@ -796,29 +796,6 @@ pub(crate) fn optimize(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn link(
|
||||
cgcx: &CodegenContext<LlvmCodegenBackend>,
|
||||
dcx: DiagCtxtHandle<'_>,
|
||||
mut modules: Vec<ModuleCodegen<ModuleLlvm>>,
|
||||
) -> Result<ModuleCodegen<ModuleLlvm>, FatalError> {
|
||||
use super::lto::{Linker, ModuleBuffer};
|
||||
// Sort the modules by name to ensure deterministic behavior.
|
||||
modules.sort_by(|a, b| a.name.cmp(&b.name));
|
||||
let (first, elements) =
|
||||
modules.split_first().expect("Bug! modules must contain at least one module.");
|
||||
|
||||
let mut linker = Linker::new(first.module_llvm.llmod());
|
||||
for module in elements {
|
||||
let _timer = cgcx.prof.generic_activity_with_arg("LLVM_link_module", &*module.name);
|
||||
let buffer = ModuleBuffer::new(module.module_llvm.llmod());
|
||||
linker
|
||||
.add(buffer.data())
|
||||
.map_err(|()| llvm_err(dcx, LlvmError::SerializeModule { name: &module.name }))?;
|
||||
}
|
||||
drop(linker);
|
||||
Ok(modules.remove(0))
|
||||
}
|
||||
|
||||
pub(crate) fn codegen(
|
||||
cgcx: &CodegenContext<LlvmCodegenBackend>,
|
||||
module: ModuleCodegen<ModuleLlvm>,
|
||||
|
|
|
|||
|
|
@ -687,10 +687,10 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
|
|||
bx.nonnull_metadata(load);
|
||||
}
|
||||
|
||||
if let Some(pointee) = layout.pointee_info_at(bx, offset) {
|
||||
if let Some(_) = pointee.safe {
|
||||
bx.align_metadata(load, pointee.align);
|
||||
}
|
||||
if let Some(pointee) = layout.pointee_info_at(bx, offset)
|
||||
&& let Some(_) = pointee.safe
|
||||
{
|
||||
bx.align_metadata(load, pointee.align);
|
||||
}
|
||||
}
|
||||
abi::Primitive::Float(_) => {}
|
||||
|
|
|
|||
|
|
@ -46,21 +46,17 @@ pub(crate) fn finalize(cx: &mut CodegenCx<'_, '_>) {
|
|||
debug!("Generating coverage map for CodegenUnit: `{}`", cx.codegen_unit.name());
|
||||
|
||||
// FIXME(#132395): Can this be none even when coverage is enabled?
|
||||
let instances_used = match cx.coverage_cx {
|
||||
Some(ref cx) => cx.instances_used.borrow(),
|
||||
None => return,
|
||||
};
|
||||
let Some(ref coverage_cx) = cx.coverage_cx else { return };
|
||||
|
||||
let mut covfun_records = instances_used
|
||||
.iter()
|
||||
.copied()
|
||||
let mut covfun_records = coverage_cx
|
||||
.instances_used()
|
||||
.into_iter()
|
||||
// Sort by symbol name, so that the global file table is built in an
|
||||
// order that doesn't depend on the stable-hash-based order in which
|
||||
// instances were visited during codegen.
|
||||
.sorted_by_cached_key(|&instance| tcx.symbol_name(instance).name)
|
||||
.filter_map(|instance| prepare_covfun_record(tcx, instance, true))
|
||||
.collect::<Vec<_>>();
|
||||
drop(instances_used);
|
||||
|
||||
// In a single designated CGU, also prepare covfun records for functions
|
||||
// in this crate that were instrumented for coverage, but are unused.
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use rustc_abi::Size;
|
|||
use rustc_codegen_ssa::traits::{
|
||||
BuilderMethods, ConstCodegenMethods, CoverageInfoBuilderMethods, MiscCodegenMethods,
|
||||
};
|
||||
use rustc_data_structures::fx::{FxHashMap, FxIndexSet};
|
||||
use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
|
||||
use rustc_middle::mir::coverage::CoverageKind;
|
||||
use rustc_middle::ty::Instance;
|
||||
use tracing::{debug, instrument};
|
||||
|
|
@ -20,9 +20,14 @@ mod mapgen;
|
|||
|
||||
/// Extra per-CGU context/state needed for coverage instrumentation.
|
||||
pub(crate) struct CguCoverageContext<'ll, 'tcx> {
|
||||
/// Coverage data for each instrumented function identified by DefId.
|
||||
pub(crate) instances_used: RefCell<FxIndexSet<Instance<'tcx>>>,
|
||||
pub(crate) pgo_func_name_var_map: RefCell<FxHashMap<Instance<'tcx>, &'ll llvm::Value>>,
|
||||
/// Associates function instances with an LLVM global that holds the
|
||||
/// function's symbol name, as needed by LLVM coverage intrinsics.
|
||||
///
|
||||
/// Instances in this map are also considered "used" for the purposes of
|
||||
/// emitting covfun records. Every covfun record holds a hash of its
|
||||
/// symbol name, and `llvm-cov` will exit fatally if it can't resolve that
|
||||
/// hash back to an entry in the binary's `__llvm_prf_names` linker section.
|
||||
pub(crate) pgo_func_name_var_map: RefCell<FxIndexMap<Instance<'tcx>, &'ll llvm::Value>>,
|
||||
pub(crate) mcdc_condition_bitmap_map: RefCell<FxHashMap<Instance<'tcx>, Vec<&'ll llvm::Value>>>,
|
||||
|
||||
covfun_section_name: OnceCell<CString>,
|
||||
|
|
@ -31,7 +36,6 @@ pub(crate) struct CguCoverageContext<'ll, 'tcx> {
|
|||
impl<'ll, 'tcx> CguCoverageContext<'ll, 'tcx> {
|
||||
pub(crate) fn new() -> Self {
|
||||
Self {
|
||||
instances_used: RefCell::<FxIndexSet<_>>::default(),
|
||||
pgo_func_name_var_map: Default::default(),
|
||||
mcdc_condition_bitmap_map: Default::default(),
|
||||
covfun_section_name: Default::default(),
|
||||
|
|
@ -53,6 +57,14 @@ impl<'ll, 'tcx> CguCoverageContext<'ll, 'tcx> {
|
|||
.and_then(|bitmap_map| bitmap_map.get(decision_depth as usize))
|
||||
.copied() // Dereference Option<&&Value> to Option<&Value>
|
||||
}
|
||||
|
||||
/// Returns the list of instances considered "used" in this CGU, as
|
||||
/// inferred from the keys of `pgo_func_name_var_map`.
|
||||
pub(crate) fn instances_used(&self) -> Vec<Instance<'tcx>> {
|
||||
// Collecting into a Vec is way easier than trying to juggle RefCell
|
||||
// projections, and this should only run once per CGU anyway.
|
||||
self.pgo_func_name_var_map.borrow().keys().copied().collect::<Vec<_>>()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
|
||||
|
|
@ -78,7 +90,10 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
|
|||
/// string, to hold the function name passed to LLVM intrinsic
|
||||
/// `instrprof.increment()`. The `Value` is only created once per instance.
|
||||
/// Multiple invocations with the same instance return the same `Value`.
|
||||
fn get_pgo_func_name_var(&self, instance: Instance<'tcx>) -> &'ll llvm::Value {
|
||||
///
|
||||
/// This has the side-effect of causing coverage codegen to consider this
|
||||
/// function "used", making it eligible to emit an associated covfun record.
|
||||
fn ensure_pgo_func_name_var(&self, instance: Instance<'tcx>) -> &'ll llvm::Value {
|
||||
debug!("getting pgo_func_name_var for instance={:?}", instance);
|
||||
let mut pgo_func_name_var_map = self.coverage_cx().pgo_func_name_var_map.borrow_mut();
|
||||
pgo_func_name_var_map.entry(instance).or_insert_with(|| {
|
||||
|
|
@ -102,7 +117,7 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
|
|||
return;
|
||||
}
|
||||
|
||||
let fn_name = self.get_pgo_func_name_var(instance);
|
||||
let fn_name = self.ensure_pgo_func_name_var(instance);
|
||||
let hash = self.const_u64(function_coverage_info.function_source_hash);
|
||||
let bitmap_bits = self.const_u32(function_coverage_info.mcdc_bitmap_bits as u32);
|
||||
self.mcdc_parameters(fn_name, hash, bitmap_bits);
|
||||
|
|
@ -151,11 +166,6 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
|
|||
return;
|
||||
};
|
||||
|
||||
// Mark the instance as used in this CGU, for coverage purposes.
|
||||
// This includes functions that were not partitioned into this CGU,
|
||||
// but were MIR-inlined into one of this CGU's functions.
|
||||
coverage_cx.instances_used.borrow_mut().insert(instance);
|
||||
|
||||
match *kind {
|
||||
CoverageKind::SpanMarker | CoverageKind::BlockMarker { .. } => unreachable!(
|
||||
"marker statement {kind:?} should have been removed by CleanupPostBorrowck"
|
||||
|
|
@ -163,7 +173,7 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
|
|||
CoverageKind::VirtualCounter { bcb }
|
||||
if let Some(&id) = ids_info.phys_counter_for_node.get(&bcb) =>
|
||||
{
|
||||
let fn_name = bx.get_pgo_func_name_var(instance);
|
||||
let fn_name = bx.ensure_pgo_func_name_var(instance);
|
||||
let hash = bx.const_u64(function_coverage_info.function_source_hash);
|
||||
let num_counters = bx.const_u32(ids_info.num_counters);
|
||||
let index = bx.const_u32(id.as_u32());
|
||||
|
|
@ -193,7 +203,7 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
|
|||
"bitmap index of the decision out of range"
|
||||
);
|
||||
|
||||
let fn_name = bx.get_pgo_func_name_var(instance);
|
||||
let fn_name = bx.ensure_pgo_func_name_var(instance);
|
||||
let hash = bx.const_u64(function_coverage_info.function_source_hash);
|
||||
let bitmap_index = bx.const_u32(bitmap_idx);
|
||||
bx.mcdc_tvbitmap_update(fn_name, hash, bitmap_index, cond_bitmap);
|
||||
|
|
|
|||
|
|
@ -285,8 +285,8 @@ pub(super) fn build_type_with_children<'ll, 'tcx>(
|
|||
// Item(T),
|
||||
// }
|
||||
// ```
|
||||
let is_expanding_recursive =
|
||||
debug_context(cx).adt_stack.borrow().iter().any(|(parent_def_id, parent_args)| {
|
||||
let is_expanding_recursive = adt_def.is_enum()
|
||||
&& debug_context(cx).adt_stack.borrow().iter().any(|(parent_def_id, parent_args)| {
|
||||
if def_id == *parent_def_id {
|
||||
args.iter().zip(parent_args.iter()).any(|(arg, parent_arg)| {
|
||||
if let (Some(arg), Some(parent_arg)) = (arg.as_type(), parent_arg.as_type())
|
||||
|
|
|
|||
|
|
@ -533,7 +533,7 @@ impl<'ll, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> {
|
|||
// First, let's see if this is a method within an inherent impl. Because
|
||||
// if yes, we want to make the result subroutine DIE a child of the
|
||||
// subroutine's self-type.
|
||||
if let Some(impl_def_id) = cx.tcx.impl_of_method(instance.def_id()) {
|
||||
if let Some(impl_def_id) = cx.tcx.impl_of_assoc(instance.def_id()) {
|
||||
// If the method does *not* belong to a trait, proceed
|
||||
if cx.tcx.trait_id_of_impl(impl_def_id).is_none() {
|
||||
let impl_self_ty = cx.tcx.instantiate_and_normalize_erasing_regions(
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ pub(crate) struct AutoDiffWithoutEnable;
|
|||
#[derive(Diagnostic)]
|
||||
#[diag(codegen_llvm_lto_bitcode_from_rlib)]
|
||||
pub(crate) struct LtoBitcodeFromRlib {
|
||||
pub llvm_err: String,
|
||||
pub err: String,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
|
|
|
|||
|
|
@ -168,13 +168,6 @@ impl WriteBackendMethods for LlvmCodegenBackend {
|
|||
let stats = llvm::build_string(|s| unsafe { llvm::LLVMRustPrintStatistics(s) }).unwrap();
|
||||
print!("{stats}");
|
||||
}
|
||||
fn run_link(
|
||||
cgcx: &CodegenContext<Self>,
|
||||
dcx: DiagCtxtHandle<'_>,
|
||||
modules: Vec<ModuleCodegen<Self::Module>>,
|
||||
) -> Result<ModuleCodegen<Self::Module>, FatalError> {
|
||||
back::write::link(cgcx, dcx, modules)
|
||||
}
|
||||
fn run_and_optimize_fat_lto(
|
||||
cgcx: &CodegenContext<Self>,
|
||||
exported_symbols_for_lto: &[String],
|
||||
|
|
|
|||
|
|
@ -2612,13 +2612,6 @@ unsafe extern "C" {
|
|||
len: usize,
|
||||
Identifier: *const c_char,
|
||||
) -> Option<&Module>;
|
||||
pub(crate) fn LLVMRustGetSliceFromObjectDataByName(
|
||||
data: *const u8,
|
||||
len: usize,
|
||||
name: *const u8,
|
||||
name_len: usize,
|
||||
out_len: &mut usize,
|
||||
) -> *const u8;
|
||||
|
||||
pub(crate) fn LLVMRustLinkerNew(M: &Module) -> &mut Linker<'_>;
|
||||
pub(crate) fn LLVMRustLinkerAdd(
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ edition = "2024"
|
|||
ar_archive_writer = "0.4.2"
|
||||
bitflags = "2.4.1"
|
||||
bstr = "1.11.3"
|
||||
# Pinned so `cargo update` bumps don't cause breakage. Please also update the
|
||||
# `cc` in `rustc_llvm` if you update the `cc` here.
|
||||
# `cc` updates often break things, so we pin it here. Cargo enforces "max 1 semver-compat version
|
||||
# per crate", so if you change this, you need to also change it in `rustc_llvm`.
|
||||
cc = "=1.2.16"
|
||||
itertools = "0.12"
|
||||
pathdiff = "0.2.0"
|
||||
|
|
|
|||
|
|
@ -3369,12 +3369,12 @@ fn warn_if_linked_with_gold(sess: &Session, path: &Path) -> Result<(), Box<dyn s
|
|||
|
||||
let section =
|
||||
elf.sections(endian, data)?.section_by_name(endian, b".note.gnu.gold-version");
|
||||
if let Some((_, section)) = section {
|
||||
if let Some(mut notes) = section.notes(endian, data)? {
|
||||
return Ok(notes.any(|note| {
|
||||
note.is_ok_and(|note| note.n_type(endian) == elf::NT_GNU_GOLD_VERSION)
|
||||
}));
|
||||
}
|
||||
if let Some((_, section)) = section
|
||||
&& let Some(mut notes) = section.notes(endian, data)?
|
||||
{
|
||||
return Ok(notes.any(|note| {
|
||||
note.is_ok_and(|note| note.n_type(endian) == elf::NT_GNU_GOLD_VERSION)
|
||||
}));
|
||||
}
|
||||
|
||||
Ok(false)
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ fn reachable_non_generics_provider(tcx: TyCtxt<'_>, _: LocalCrate) -> DefIdMap<S
|
|||
// Only consider nodes that actually have exported symbols.
|
||||
match tcx.def_kind(def_id) {
|
||||
DefKind::Fn | DefKind::Static { .. } => {}
|
||||
DefKind::AssocFn if tcx.impl_of_method(def_id.to_def_id()).is_some() => {}
|
||||
DefKind::AssocFn if tcx.impl_of_assoc(def_id.to_def_id()).is_some() => {}
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
use std::any::Any;
|
||||
use std::assert_matches::assert_matches;
|
||||
use std::marker::PhantomData;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
|
@ -372,8 +371,6 @@ pub struct CodegenContext<B: WriteBackendMethods> {
|
|||
/// The incremental compilation session directory, or None if we are not
|
||||
/// compiling incrementally
|
||||
pub incr_comp_session_dir: Option<PathBuf>,
|
||||
/// Channel back to the main control thread to send messages to
|
||||
pub coordinator_send: Sender<Box<dyn Any + Send>>,
|
||||
/// `true` if the codegen should be run in parallel.
|
||||
///
|
||||
/// Depends on [`ExtraBackendMethods::supports_parallel()`] and `-Zno_parallel_backend`.
|
||||
|
|
@ -800,10 +797,6 @@ pub(crate) enum WorkItemResult<B: WriteBackendMethods> {
|
|||
/// The backend has finished compiling a CGU, nothing more required.
|
||||
Finished(CompiledModule),
|
||||
|
||||
/// The backend has finished compiling a CGU, which now needs linking
|
||||
/// because `-Zcombine-cgu` was specified.
|
||||
NeedsLink(ModuleCodegen<B::Module>),
|
||||
|
||||
/// The backend has finished compiling a CGU, which now needs to go through
|
||||
/// fat LTO.
|
||||
NeedsFatLto(FatLtoInput<B>),
|
||||
|
|
@ -887,7 +880,10 @@ fn execute_optimize_work_item<B: ExtraBackendMethods>(
|
|||
};
|
||||
|
||||
match lto_type {
|
||||
ComputedLtoType::No => finish_intra_module_work(cgcx, module, module_config),
|
||||
ComputedLtoType::No => {
|
||||
let module = B::codegen(cgcx, module, module_config)?;
|
||||
Ok(WorkItemResult::Finished(module))
|
||||
}
|
||||
ComputedLtoType::Thin => {
|
||||
let (name, thin_buffer) = B::prepare_thin(module, false);
|
||||
if let Some(path) = bitcode {
|
||||
|
|
@ -1027,20 +1023,8 @@ fn execute_thin_lto_work_item<B: ExtraBackendMethods>(
|
|||
module_config: &ModuleConfig,
|
||||
) -> Result<WorkItemResult<B>, FatalError> {
|
||||
let module = B::optimize_thin(cgcx, module)?;
|
||||
finish_intra_module_work(cgcx, module, module_config)
|
||||
}
|
||||
|
||||
fn finish_intra_module_work<B: ExtraBackendMethods>(
|
||||
cgcx: &CodegenContext<B>,
|
||||
module: ModuleCodegen<B::Module>,
|
||||
module_config: &ModuleConfig,
|
||||
) -> Result<WorkItemResult<B>, FatalError> {
|
||||
if !cgcx.opts.unstable_opts.combine_cgu || module.kind == ModuleKind::Allocator {
|
||||
let module = B::codegen(cgcx, module, module_config)?;
|
||||
Ok(WorkItemResult::Finished(module))
|
||||
} else {
|
||||
Ok(WorkItemResult::NeedsLink(module))
|
||||
}
|
||||
let module = B::codegen(cgcx, module, module_config)?;
|
||||
Ok(WorkItemResult::Finished(module))
|
||||
}
|
||||
|
||||
/// Messages sent to the coordinator.
|
||||
|
|
@ -1122,10 +1106,10 @@ fn start_executing_work<B: ExtraBackendMethods>(
|
|||
autodiff_items: &[AutoDiffItem],
|
||||
shared_emitter: SharedEmitter,
|
||||
codegen_worker_send: Sender<CguMessage>,
|
||||
coordinator_receive: Receiver<Box<dyn Any + Send>>,
|
||||
coordinator_receive: Receiver<Message<B>>,
|
||||
regular_config: Arc<ModuleConfig>,
|
||||
allocator_config: Arc<ModuleConfig>,
|
||||
tx_to_llvm_workers: Sender<Box<dyn Any + Send>>,
|
||||
tx_to_llvm_workers: Sender<Message<B>>,
|
||||
) -> thread::JoinHandle<Result<CompiledModules, ()>> {
|
||||
let coordinator_send = tx_to_llvm_workers;
|
||||
let sess = tcx.sess;
|
||||
|
|
@ -1153,7 +1137,7 @@ fn start_executing_work<B: ExtraBackendMethods>(
|
|||
let coordinator_send2 = coordinator_send.clone();
|
||||
let helper = jobserver::client()
|
||||
.into_helper_thread(move |token| {
|
||||
drop(coordinator_send2.send(Box::new(Message::Token::<B>(token))));
|
||||
drop(coordinator_send2.send(Message::Token::<B>(token)));
|
||||
})
|
||||
.expect("failed to spawn helper thread");
|
||||
|
||||
|
|
@ -1187,7 +1171,6 @@ fn start_executing_work<B: ExtraBackendMethods>(
|
|||
remark: sess.opts.cg.remark.clone(),
|
||||
remark_dir,
|
||||
incr_comp_session_dir: sess.incr_comp_session_dir_opt().map(|r| r.clone()),
|
||||
coordinator_send,
|
||||
expanded_args: tcx.sess.expanded_args.clone(),
|
||||
diag_emitter: shared_emitter.clone(),
|
||||
output_filenames: Arc::clone(tcx.output_filenames(())),
|
||||
|
|
@ -1347,7 +1330,6 @@ fn start_executing_work<B: ExtraBackendMethods>(
|
|||
// through codegen and LLVM.
|
||||
let mut compiled_modules = vec![];
|
||||
let mut compiled_allocator_module = None;
|
||||
let mut needs_link = Vec::new();
|
||||
let mut needs_fat_lto = Vec::new();
|
||||
let mut needs_thin_lto = Vec::new();
|
||||
let mut lto_import_only_modules = Vec::new();
|
||||
|
|
@ -1423,7 +1405,7 @@ fn start_executing_work<B: ExtraBackendMethods>(
|
|||
let (item, _) =
|
||||
work_items.pop().expect("queue empty - queue_full_enough() broken?");
|
||||
main_thread_state = MainThreadState::Lending;
|
||||
spawn_work(&cgcx, &mut llvm_start_time, item);
|
||||
spawn_work(&cgcx, coordinator_send.clone(), &mut llvm_start_time, item);
|
||||
}
|
||||
}
|
||||
} else if codegen_state == Completed {
|
||||
|
|
@ -1502,7 +1484,7 @@ fn start_executing_work<B: ExtraBackendMethods>(
|
|||
MainThreadState::Idle => {
|
||||
if let Some((item, _)) = work_items.pop() {
|
||||
main_thread_state = MainThreadState::Lending;
|
||||
spawn_work(&cgcx, &mut llvm_start_time, item);
|
||||
spawn_work(&cgcx, coordinator_send.clone(), &mut llvm_start_time, item);
|
||||
} else {
|
||||
// There is no unstarted work, so let the main thread
|
||||
// take over for a running worker. Otherwise the
|
||||
|
|
@ -1538,7 +1520,7 @@ fn start_executing_work<B: ExtraBackendMethods>(
|
|||
while running_with_own_token < tokens.len()
|
||||
&& let Some((item, _)) = work_items.pop()
|
||||
{
|
||||
spawn_work(&cgcx, &mut llvm_start_time, item);
|
||||
spawn_work(&cgcx, coordinator_send.clone(), &mut llvm_start_time, item);
|
||||
running_with_own_token += 1;
|
||||
}
|
||||
}
|
||||
|
|
@ -1546,8 +1528,7 @@ fn start_executing_work<B: ExtraBackendMethods>(
|
|||
// Relinquish accidentally acquired extra tokens.
|
||||
tokens.truncate(running_with_own_token);
|
||||
|
||||
let msg = coordinator_receive.recv().unwrap();
|
||||
match *msg.downcast::<Message<B>>().ok().unwrap() {
|
||||
match coordinator_receive.recv().unwrap() {
|
||||
// Save the token locally and the next turn of the loop will use
|
||||
// this to spawn a new unit of work, or it may get dropped
|
||||
// immediately if we have no more work to spawn.
|
||||
|
|
@ -1630,7 +1611,6 @@ fn start_executing_work<B: ExtraBackendMethods>(
|
|||
Ok(WorkItemResult::Finished(compiled_module)) => {
|
||||
match compiled_module.kind {
|
||||
ModuleKind::Regular => {
|
||||
assert!(needs_link.is_empty());
|
||||
compiled_modules.push(compiled_module);
|
||||
}
|
||||
ModuleKind::Allocator => {
|
||||
|
|
@ -1639,10 +1619,6 @@ fn start_executing_work<B: ExtraBackendMethods>(
|
|||
}
|
||||
}
|
||||
}
|
||||
Ok(WorkItemResult::NeedsLink(module)) => {
|
||||
assert!(compiled_modules.is_empty());
|
||||
needs_link.push(module);
|
||||
}
|
||||
Ok(WorkItemResult::NeedsFatLto(fat_lto_input)) => {
|
||||
assert!(!started_lto);
|
||||
assert!(needs_thin_lto.is_empty());
|
||||
|
|
@ -1679,17 +1655,6 @@ fn start_executing_work<B: ExtraBackendMethods>(
|
|||
return Err(());
|
||||
}
|
||||
|
||||
let needs_link = mem::take(&mut needs_link);
|
||||
if !needs_link.is_empty() {
|
||||
assert!(compiled_modules.is_empty());
|
||||
let dcx = cgcx.create_dcx();
|
||||
let dcx = dcx.handle();
|
||||
let module = B::run_link(&cgcx, dcx, needs_link).map_err(|_| ())?;
|
||||
let module =
|
||||
B::codegen(&cgcx, module, cgcx.config(ModuleKind::Regular)).map_err(|_| ())?;
|
||||
compiled_modules.push(module);
|
||||
}
|
||||
|
||||
// Drop to print timings
|
||||
drop(llvm_start_time);
|
||||
|
||||
|
|
@ -1769,6 +1734,7 @@ pub(crate) struct WorkerFatalError;
|
|||
|
||||
fn spawn_work<'a, B: ExtraBackendMethods>(
|
||||
cgcx: &'a CodegenContext<B>,
|
||||
coordinator_send: Sender<Message<B>>,
|
||||
llvm_start_time: &mut Option<VerboseTimingGuard<'a>>,
|
||||
work: WorkItem<B>,
|
||||
) {
|
||||
|
|
@ -1782,7 +1748,7 @@ fn spawn_work<'a, B: ExtraBackendMethods>(
|
|||
// Set up a destructor which will fire off a message that we're done as
|
||||
// we exit.
|
||||
struct Bomb<B: ExtraBackendMethods> {
|
||||
coordinator_send: Sender<Box<dyn Any + Send>>,
|
||||
coordinator_send: Sender<Message<B>>,
|
||||
result: Option<Result<WorkItemResult<B>, FatalError>>,
|
||||
}
|
||||
impl<B: ExtraBackendMethods> Drop for Bomb<B> {
|
||||
|
|
@ -1794,11 +1760,11 @@ fn spawn_work<'a, B: ExtraBackendMethods>(
|
|||
}
|
||||
None => Message::WorkItem::<B> { result: Err(None) },
|
||||
};
|
||||
drop(self.coordinator_send.send(Box::new(msg)));
|
||||
drop(self.coordinator_send.send(msg));
|
||||
}
|
||||
}
|
||||
|
||||
let mut bomb = Bomb::<B> { coordinator_send: cgcx.coordinator_send.clone(), result: None };
|
||||
let mut bomb = Bomb::<B> { coordinator_send, result: None };
|
||||
|
||||
// Execute the work itself, and if it finishes successfully then flag
|
||||
// ourselves as a success as well.
|
||||
|
|
@ -2003,7 +1969,7 @@ impl SharedEmitterMain {
|
|||
}
|
||||
|
||||
pub struct Coordinator<B: ExtraBackendMethods> {
|
||||
pub sender: Sender<Box<dyn Any + Send>>,
|
||||
sender: Sender<Message<B>>,
|
||||
future: Option<thread::JoinHandle<Result<CompiledModules, ()>>>,
|
||||
// Only used for the Message type.
|
||||
phantom: PhantomData<B>,
|
||||
|
|
@ -2020,7 +1986,7 @@ impl<B: ExtraBackendMethods> Drop for Coordinator<B> {
|
|||
if let Some(future) = self.future.take() {
|
||||
// If we haven't joined yet, signal to the coordinator that it should spawn no more
|
||||
// work, and wait for worker threads to finish.
|
||||
drop(self.sender.send(Box::new(Message::CodegenAborted::<B>)));
|
||||
drop(self.sender.send(Message::CodegenAborted::<B>));
|
||||
drop(future.join());
|
||||
}
|
||||
}
|
||||
|
|
@ -2079,7 +2045,7 @@ impl<B: ExtraBackendMethods> OngoingCodegen<B> {
|
|||
pub(crate) fn codegen_finished(&self, tcx: TyCtxt<'_>) {
|
||||
self.wait_for_signal_to_codegen_item();
|
||||
self.check_for_errors(tcx.sess);
|
||||
drop(self.coordinator.sender.send(Box::new(Message::CodegenComplete::<B>)));
|
||||
drop(self.coordinator.sender.send(Message::CodegenComplete::<B>));
|
||||
}
|
||||
|
||||
pub(crate) fn check_for_errors(&self, sess: &Session) {
|
||||
|
|
@ -2100,28 +2066,25 @@ impl<B: ExtraBackendMethods> OngoingCodegen<B> {
|
|||
}
|
||||
|
||||
pub(crate) fn submit_codegened_module_to_llvm<B: ExtraBackendMethods>(
|
||||
_backend: &B,
|
||||
tx_to_llvm_workers: &Sender<Box<dyn Any + Send>>,
|
||||
coordinator: &Coordinator<B>,
|
||||
module: ModuleCodegen<B::Module>,
|
||||
cost: u64,
|
||||
) {
|
||||
let llvm_work_item = WorkItem::Optimize(module);
|
||||
drop(tx_to_llvm_workers.send(Box::new(Message::CodegenDone::<B> { llvm_work_item, cost })));
|
||||
drop(coordinator.sender.send(Message::CodegenDone::<B> { llvm_work_item, cost }));
|
||||
}
|
||||
|
||||
pub(crate) fn submit_post_lto_module_to_llvm<B: ExtraBackendMethods>(
|
||||
_backend: &B,
|
||||
tx_to_llvm_workers: &Sender<Box<dyn Any + Send>>,
|
||||
coordinator: &Coordinator<B>,
|
||||
module: CachedModuleCodegen,
|
||||
) {
|
||||
let llvm_work_item = WorkItem::CopyPostLtoArtifacts(module);
|
||||
drop(tx_to_llvm_workers.send(Box::new(Message::CodegenDone::<B> { llvm_work_item, cost: 0 })));
|
||||
drop(coordinator.sender.send(Message::CodegenDone::<B> { llvm_work_item, cost: 0 }));
|
||||
}
|
||||
|
||||
pub(crate) fn submit_pre_lto_module_to_llvm<B: ExtraBackendMethods>(
|
||||
_backend: &B,
|
||||
tcx: TyCtxt<'_>,
|
||||
tx_to_llvm_workers: &Sender<Box<dyn Any + Send>>,
|
||||
coordinator: &Coordinator<B>,
|
||||
module: CachedModuleCodegen,
|
||||
) {
|
||||
let filename = pre_lto_bitcode_filename(&module.name);
|
||||
|
|
@ -2135,10 +2098,10 @@ pub(crate) fn submit_pre_lto_module_to_llvm<B: ExtraBackendMethods>(
|
|||
})
|
||||
};
|
||||
// Schedule the module to be loaded
|
||||
drop(tx_to_llvm_workers.send(Box::new(Message::AddImportOnlyModule::<B> {
|
||||
drop(coordinator.sender.send(Message::AddImportOnlyModule::<B> {
|
||||
module_data: SerializedModule::FromUncompressedFile(mmap),
|
||||
work_product: module.source,
|
||||
})));
|
||||
}));
|
||||
}
|
||||
|
||||
fn pre_lto_bitcode_filename(module_name: &str) -> String {
|
||||
|
|
|
|||
|
|
@ -702,8 +702,7 @@ pub fn codegen_crate<B: ExtraBackendMethods>(
|
|||
// These modules are generally cheap and won't throw off scheduling.
|
||||
let cost = 0;
|
||||
submit_codegened_module_to_llvm(
|
||||
&backend,
|
||||
&ongoing_codegen.coordinator.sender,
|
||||
&ongoing_codegen.coordinator,
|
||||
ModuleCodegen::new_allocator(llmod_id, module_llvm),
|
||||
cost,
|
||||
);
|
||||
|
|
@ -800,18 +799,12 @@ pub fn codegen_crate<B: ExtraBackendMethods>(
|
|||
// compilation hang on post-monomorphization errors.
|
||||
tcx.dcx().abort_if_errors();
|
||||
|
||||
submit_codegened_module_to_llvm(
|
||||
&backend,
|
||||
&ongoing_codegen.coordinator.sender,
|
||||
module,
|
||||
cost,
|
||||
);
|
||||
submit_codegened_module_to_llvm(&ongoing_codegen.coordinator, module, cost);
|
||||
}
|
||||
CguReuse::PreLto => {
|
||||
submit_pre_lto_module_to_llvm(
|
||||
&backend,
|
||||
tcx,
|
||||
&ongoing_codegen.coordinator.sender,
|
||||
&ongoing_codegen.coordinator,
|
||||
CachedModuleCodegen {
|
||||
name: cgu.name().to_string(),
|
||||
source: cgu.previous_work_product(tcx),
|
||||
|
|
@ -820,8 +813,7 @@ pub fn codegen_crate<B: ExtraBackendMethods>(
|
|||
}
|
||||
CguReuse::PostLto => {
|
||||
submit_post_lto_module_to_llvm(
|
||||
&backend,
|
||||
&ongoing_codegen.coordinator.sender,
|
||||
&ongoing_codegen.coordinator,
|
||||
CachedModuleCodegen {
|
||||
name: cgu.name().to_string(),
|
||||
source: cgu.previous_work_product(tcx),
|
||||
|
|
|
|||
|
|
@ -4,12 +4,12 @@ use rustc_abi::{Align, ExternAbi};
|
|||
use rustc_ast::expand::autodiff_attrs::{AutoDiffAttrs, DiffActivity, DiffMode};
|
||||
use rustc_ast::{LitKind, MetaItem, MetaItemInner, attr};
|
||||
use rustc_attr_data_structures::{
|
||||
AttributeKind, InlineAttr, InstructionSetAttr, OptimizeAttr, UsedBy, find_attr,
|
||||
AttributeKind, InlineAttr, InstructionSetAttr, UsedBy, find_attr,
|
||||
};
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId};
|
||||
use rustc_hir::weak_lang_items::WEAK_LANG_ITEMS;
|
||||
use rustc_hir::{self as hir, LangItem, lang_items};
|
||||
use rustc_hir::{self as hir, Attribute, LangItem, lang_items};
|
||||
use rustc_middle::middle::codegen_fn_attrs::{
|
||||
CodegenFnAttrFlags, CodegenFnAttrs, PatchableFunctionEntry,
|
||||
};
|
||||
|
|
@ -53,77 +53,196 @@ fn linkage_by_name(tcx: TyCtxt<'_>, def_id: LocalDefId, name: &str) -> Linkage {
|
|||
}
|
||||
}
|
||||
|
||||
fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
||||
if cfg!(debug_assertions) {
|
||||
let def_kind = tcx.def_kind(did);
|
||||
assert!(
|
||||
def_kind.has_codegen_attrs(),
|
||||
"unexpected `def_kind` in `codegen_fn_attrs`: {def_kind:?}",
|
||||
);
|
||||
/// In some cases, attributes are only valid on functions, but it's the `check_attr`
|
||||
/// pass that checks that they aren't used anywhere else, rather than this module.
|
||||
/// In these cases, we bail from performing further checks that are only meaningful for
|
||||
/// functions (such as calling `fn_sig`, which ICEs if given a non-function). We also
|
||||
/// report a delayed bug, just in case `check_attr` isn't doing its job.
|
||||
fn try_fn_sig<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
did: LocalDefId,
|
||||
attr_span: Span,
|
||||
) -> Option<ty::EarlyBinder<'tcx, ty::PolyFnSig<'tcx>>> {
|
||||
use DefKind::*;
|
||||
|
||||
let def_kind = tcx.def_kind(did);
|
||||
if let Fn | AssocFn | Variant | Ctor(..) = def_kind {
|
||||
Some(tcx.fn_sig(did))
|
||||
} else {
|
||||
tcx.dcx().span_delayed_bug(attr_span, "this attribute can only be applied to functions");
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME(jdonszelmann): remove when instruction_set becomes a parsed attr
|
||||
fn parse_instruction_set_attr(tcx: TyCtxt<'_>, attr: &Attribute) -> Option<InstructionSetAttr> {
|
||||
let list = attr.meta_item_list()?;
|
||||
|
||||
match &list[..] {
|
||||
[MetaItemInner::MetaItem(set)] => {
|
||||
let segments = set.path.segments.iter().map(|x| x.ident.name).collect::<Vec<_>>();
|
||||
match segments.as_slice() {
|
||||
[sym::arm, sym::a32 | sym::t32] if !tcx.sess.target.has_thumb_interworking => {
|
||||
tcx.dcx().emit_err(errors::UnsupportedInstructionSet { span: attr.span() });
|
||||
None
|
||||
}
|
||||
[sym::arm, sym::a32] => Some(InstructionSetAttr::ArmA32),
|
||||
[sym::arm, sym::t32] => Some(InstructionSetAttr::ArmT32),
|
||||
_ => {
|
||||
tcx.dcx().emit_err(errors::InvalidInstructionSet { span: attr.span() });
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
[] => {
|
||||
tcx.dcx().emit_err(errors::BareInstructionSet { span: attr.span() });
|
||||
None
|
||||
}
|
||||
_ => {
|
||||
tcx.dcx().emit_err(errors::MultipleInstructionSet { span: attr.span() });
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME(jdonszelmann): remove when linkage becomes a parsed attr
|
||||
fn parse_linkage_attr(tcx: TyCtxt<'_>, did: LocalDefId, attr: &Attribute) -> Option<Linkage> {
|
||||
let val = attr.value_str()?;
|
||||
let linkage = linkage_by_name(tcx, did, val.as_str());
|
||||
Some(linkage)
|
||||
}
|
||||
|
||||
// FIXME(jdonszelmann): remove when no_sanitize becomes a parsed attr
|
||||
fn parse_no_sanitize_attr(tcx: TyCtxt<'_>, attr: &Attribute) -> Option<SanitizerSet> {
|
||||
let list = attr.meta_item_list()?;
|
||||
let mut sanitizer_set = SanitizerSet::empty();
|
||||
|
||||
for item in list.iter() {
|
||||
match item.name() {
|
||||
Some(sym::address) => {
|
||||
sanitizer_set |= SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS
|
||||
}
|
||||
Some(sym::cfi) => sanitizer_set |= SanitizerSet::CFI,
|
||||
Some(sym::kcfi) => sanitizer_set |= SanitizerSet::KCFI,
|
||||
Some(sym::memory) => sanitizer_set |= SanitizerSet::MEMORY,
|
||||
Some(sym::memtag) => sanitizer_set |= SanitizerSet::MEMTAG,
|
||||
Some(sym::shadow_call_stack) => sanitizer_set |= SanitizerSet::SHADOWCALLSTACK,
|
||||
Some(sym::thread) => sanitizer_set |= SanitizerSet::THREAD,
|
||||
Some(sym::hwaddress) => sanitizer_set |= SanitizerSet::HWADDRESS,
|
||||
_ => {
|
||||
tcx.dcx().emit_err(errors::InvalidNoSanitize { span: item.span() });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let attrs = tcx.hir_attrs(tcx.local_def_id_to_hir_id(did));
|
||||
let mut codegen_fn_attrs = CodegenFnAttrs::new();
|
||||
if tcx.should_inherit_track_caller(did) {
|
||||
codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER;
|
||||
}
|
||||
Some(sanitizer_set)
|
||||
}
|
||||
|
||||
// FIXME(jdonszelmann): remove when patchable_function_entry becomes a parsed attr
|
||||
fn parse_patchable_function_entry(
|
||||
tcx: TyCtxt<'_>,
|
||||
attr: &Attribute,
|
||||
) -> Option<PatchableFunctionEntry> {
|
||||
attr.meta_item_list().and_then(|l| {
|
||||
let mut prefix = None;
|
||||
let mut entry = None;
|
||||
for item in l {
|
||||
let Some(meta_item) = item.meta_item() else {
|
||||
tcx.dcx().emit_err(errors::ExpectedNameValuePair { span: item.span() });
|
||||
continue;
|
||||
};
|
||||
|
||||
let Some(name_value_lit) = meta_item.name_value_literal() else {
|
||||
tcx.dcx().emit_err(errors::ExpectedNameValuePair { span: item.span() });
|
||||
continue;
|
||||
};
|
||||
|
||||
let attrib_to_write = match meta_item.name() {
|
||||
Some(sym::prefix_nops) => &mut prefix,
|
||||
Some(sym::entry_nops) => &mut entry,
|
||||
_ => {
|
||||
tcx.dcx().emit_err(errors::UnexpectedParameterName {
|
||||
span: item.span(),
|
||||
prefix_nops: sym::prefix_nops,
|
||||
entry_nops: sym::entry_nops,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
let rustc_ast::LitKind::Int(val, _) = name_value_lit.kind else {
|
||||
tcx.dcx().emit_err(errors::InvalidLiteralValue { span: name_value_lit.span });
|
||||
continue;
|
||||
};
|
||||
|
||||
let Ok(val) = val.get().try_into() else {
|
||||
tcx.dcx().emit_err(errors::OutOfRangeInteger { span: name_value_lit.span });
|
||||
continue;
|
||||
};
|
||||
|
||||
*attrib_to_write = Some(val);
|
||||
}
|
||||
|
||||
if let (None, None) = (prefix, entry) {
|
||||
tcx.dcx().span_err(attr.span(), "must specify at least one parameter");
|
||||
}
|
||||
|
||||
Some(PatchableFunctionEntry::from_prefix_and_entry(prefix.unwrap_or(0), entry.unwrap_or(0)))
|
||||
})
|
||||
}
|
||||
|
||||
/// Spans that are collected when processing built-in attributes,
|
||||
/// that are useful for emitting diagnostics later.
|
||||
#[derive(Default)]
|
||||
struct InterestingAttributeDiagnosticSpans {
|
||||
link_ordinal: Option<Span>,
|
||||
no_sanitize: Option<Span>,
|
||||
inline: Option<Span>,
|
||||
no_mangle: Option<Span>,
|
||||
}
|
||||
|
||||
/// Process the builtin attrs ([`hir::Attribute`]) on the item.
|
||||
/// Many of them directly translate to codegen attrs.
|
||||
fn process_builtin_attrs(
|
||||
tcx: TyCtxt<'_>,
|
||||
did: LocalDefId,
|
||||
attrs: &[Attribute],
|
||||
codegen_fn_attrs: &mut CodegenFnAttrs,
|
||||
) -> InterestingAttributeDiagnosticSpans {
|
||||
let mut interesting_spans = InterestingAttributeDiagnosticSpans::default();
|
||||
let rust_target_features = tcx.rust_target_features(LOCAL_CRATE);
|
||||
|
||||
// If our rustc version supports autodiff/enzyme, then we call our handler
|
||||
// to check for any `#[rustc_autodiff(...)]` attributes.
|
||||
// FIXME(jdonszelmann): merge with loop below
|
||||
if cfg!(llvm_enzyme) {
|
||||
let ad = autodiff_attrs(tcx, did.into());
|
||||
codegen_fn_attrs.autodiff_item = ad;
|
||||
}
|
||||
|
||||
// When `no_builtins` is applied at the crate level, we should add the
|
||||
// `no-builtins` attribute to each function to ensure it takes effect in LTO.
|
||||
let crate_attrs = tcx.hir_attrs(rustc_hir::CRATE_HIR_ID);
|
||||
let no_builtins = attr::contains_name(crate_attrs, sym::no_builtins);
|
||||
if no_builtins {
|
||||
codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_BUILTINS;
|
||||
}
|
||||
|
||||
let rust_target_features = tcx.rust_target_features(LOCAL_CRATE);
|
||||
|
||||
let mut link_ordinal_span = None;
|
||||
let mut no_sanitize_span = None;
|
||||
|
||||
for attr in attrs.iter() {
|
||||
// In some cases, attribute are only valid on functions, but it's the `check_attr`
|
||||
// pass that check that they aren't used anywhere else, rather this module.
|
||||
// In these cases, we bail from performing further checks that are only meaningful for
|
||||
// functions (such as calling `fn_sig`, which ICEs if given a non-function). We also
|
||||
// report a delayed bug, just in case `check_attr` isn't doing its job.
|
||||
let fn_sig = |attr_span| {
|
||||
use DefKind::*;
|
||||
|
||||
let def_kind = tcx.def_kind(did);
|
||||
if let Fn | AssocFn | Variant | Ctor(..) = def_kind {
|
||||
Some(tcx.fn_sig(did))
|
||||
} else {
|
||||
tcx.dcx()
|
||||
.span_delayed_bug(attr_span, "this attribute can only be applied to functions");
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
if let hir::Attribute::Parsed(p) = attr {
|
||||
match p {
|
||||
AttributeKind::Cold(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::COLD,
|
||||
AttributeKind::ExportName { name, .. } => {
|
||||
codegen_fn_attrs.export_name = Some(*name);
|
||||
codegen_fn_attrs.export_name = Some(*name)
|
||||
}
|
||||
AttributeKind::Inline(inline, span) => {
|
||||
codegen_fn_attrs.inline = *inline;
|
||||
interesting_spans.inline = Some(*span);
|
||||
}
|
||||
AttributeKind::Naked(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED,
|
||||
AttributeKind::Align { align, .. } => codegen_fn_attrs.alignment = Some(*align),
|
||||
AttributeKind::LinkName { name, .. } => codegen_fn_attrs.link_name = Some(*name),
|
||||
AttributeKind::LinkOrdinal { ordinal, span } => {
|
||||
codegen_fn_attrs.link_ordinal = Some(*ordinal);
|
||||
link_ordinal_span = Some(*span);
|
||||
interesting_spans.link_ordinal = Some(*span);
|
||||
}
|
||||
AttributeKind::LinkSection { name, .. } => {
|
||||
codegen_fn_attrs.link_section = Some(*name)
|
||||
}
|
||||
AttributeKind::NoMangle(attr_span) => {
|
||||
interesting_spans.no_mangle = Some(*attr_span);
|
||||
if tcx.opt_item_name(did.to_def_id()).is_some() {
|
||||
codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE;
|
||||
} else {
|
||||
|
|
@ -137,6 +256,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
});
|
||||
}
|
||||
}
|
||||
AttributeKind::Optimize(optimize, _) => codegen_fn_attrs.optimize = *optimize,
|
||||
AttributeKind::TargetFeature(features, attr_span) => {
|
||||
let Some(sig) = tcx.hir_node_by_def_id(did).fn_sig() else {
|
||||
tcx.dcx().span_delayed_bug(*attr_span, "target_feature applied to non-fn");
|
||||
|
|
@ -184,7 +304,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
let is_closure = tcx.is_closure_like(did.to_def_id());
|
||||
|
||||
if !is_closure
|
||||
&& let Some(fn_sig) = fn_sig(*attr_span)
|
||||
&& let Some(fn_sig) = try_fn_sig(tcx, did, *attr_span)
|
||||
&& fn_sig.skip_binder().abi() != ExternAbi::Rust
|
||||
{
|
||||
tcx.dcx().emit_err(errors::RequiresRustAbi { span: *attr_span });
|
||||
|
|
@ -232,155 +352,49 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
}
|
||||
sym::thread_local => codegen_fn_attrs.flags |= CodegenFnAttrFlags::THREAD_LOCAL,
|
||||
sym::linkage => {
|
||||
if let Some(val) = attr.value_str() {
|
||||
let linkage = Some(linkage_by_name(tcx, did, val.as_str()));
|
||||
if tcx.is_foreign_item(did) {
|
||||
codegen_fn_attrs.import_linkage = linkage;
|
||||
let linkage = parse_linkage_attr(tcx, did, attr);
|
||||
|
||||
if tcx.is_mutable_static(did.into()) {
|
||||
let mut diag = tcx.dcx().struct_span_err(
|
||||
attr.span(),
|
||||
"extern mutable statics are not allowed with `#[linkage]`",
|
||||
);
|
||||
diag.note(
|
||||
"marking the extern static mutable would allow changing which \
|
||||
symbol the static references rather than make the target of the \
|
||||
symbol mutable",
|
||||
);
|
||||
diag.emit();
|
||||
}
|
||||
} else {
|
||||
codegen_fn_attrs.linkage = linkage;
|
||||
if tcx.is_foreign_item(did) {
|
||||
codegen_fn_attrs.import_linkage = linkage;
|
||||
|
||||
if tcx.is_mutable_static(did.into()) {
|
||||
let mut diag = tcx.dcx().struct_span_err(
|
||||
attr.span(),
|
||||
"extern mutable statics are not allowed with `#[linkage]`",
|
||||
);
|
||||
diag.note(
|
||||
"marking the extern static mutable would allow changing which \
|
||||
symbol the static references rather than make the target of the \
|
||||
symbol mutable",
|
||||
);
|
||||
diag.emit();
|
||||
}
|
||||
} else {
|
||||
codegen_fn_attrs.linkage = linkage;
|
||||
}
|
||||
}
|
||||
sym::no_sanitize => {
|
||||
no_sanitize_span = Some(attr.span());
|
||||
if let Some(list) = attr.meta_item_list() {
|
||||
for item in list.iter() {
|
||||
match item.name() {
|
||||
Some(sym::address) => {
|
||||
codegen_fn_attrs.no_sanitize |=
|
||||
SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS
|
||||
}
|
||||
Some(sym::cfi) => codegen_fn_attrs.no_sanitize |= SanitizerSet::CFI,
|
||||
Some(sym::kcfi) => codegen_fn_attrs.no_sanitize |= SanitizerSet::KCFI,
|
||||
Some(sym::memory) => {
|
||||
codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMORY
|
||||
}
|
||||
Some(sym::memtag) => {
|
||||
codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMTAG
|
||||
}
|
||||
Some(sym::shadow_call_stack) => {
|
||||
codegen_fn_attrs.no_sanitize |= SanitizerSet::SHADOWCALLSTACK
|
||||
}
|
||||
Some(sym::thread) => {
|
||||
codegen_fn_attrs.no_sanitize |= SanitizerSet::THREAD
|
||||
}
|
||||
Some(sym::hwaddress) => {
|
||||
codegen_fn_attrs.no_sanitize |= SanitizerSet::HWADDRESS
|
||||
}
|
||||
_ => {
|
||||
tcx.dcx().emit_err(errors::InvalidNoSanitize { span: item.span() });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
interesting_spans.no_sanitize = Some(attr.span());
|
||||
codegen_fn_attrs.no_sanitize |=
|
||||
parse_no_sanitize_attr(tcx, attr).unwrap_or_default();
|
||||
}
|
||||
sym::instruction_set => {
|
||||
codegen_fn_attrs.instruction_set =
|
||||
attr.meta_item_list().and_then(|l| match &l[..] {
|
||||
[MetaItemInner::MetaItem(set)] => {
|
||||
let segments =
|
||||
set.path.segments.iter().map(|x| x.ident.name).collect::<Vec<_>>();
|
||||
match segments.as_slice() {
|
||||
[sym::arm, sym::a32 | sym::t32]
|
||||
if !tcx.sess.target.has_thumb_interworking =>
|
||||
{
|
||||
tcx.dcx().emit_err(errors::UnsupportedInstructionSet {
|
||||
span: attr.span(),
|
||||
});
|
||||
None
|
||||
}
|
||||
[sym::arm, sym::a32] => Some(InstructionSetAttr::ArmA32),
|
||||
[sym::arm, sym::t32] => Some(InstructionSetAttr::ArmT32),
|
||||
_ => {
|
||||
tcx.dcx().emit_err(errors::InvalidInstructionSet {
|
||||
span: attr.span(),
|
||||
});
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
[] => {
|
||||
tcx.dcx().emit_err(errors::BareInstructionSet { span: attr.span() });
|
||||
None
|
||||
}
|
||||
_ => {
|
||||
tcx.dcx()
|
||||
.emit_err(errors::MultipleInstructionSet { span: attr.span() });
|
||||
None
|
||||
}
|
||||
})
|
||||
codegen_fn_attrs.instruction_set = parse_instruction_set_attr(tcx, attr)
|
||||
}
|
||||
sym::patchable_function_entry => {
|
||||
codegen_fn_attrs.patchable_function_entry = attr.meta_item_list().and_then(|l| {
|
||||
let mut prefix = None;
|
||||
let mut entry = None;
|
||||
for item in l {
|
||||
let Some(meta_item) = item.meta_item() else {
|
||||
tcx.dcx().emit_err(errors::ExpectedNameValuePair { span: item.span() });
|
||||
continue;
|
||||
};
|
||||
|
||||
let Some(name_value_lit) = meta_item.name_value_literal() else {
|
||||
tcx.dcx().emit_err(errors::ExpectedNameValuePair { span: item.span() });
|
||||
continue;
|
||||
};
|
||||
|
||||
let attrib_to_write = match meta_item.name() {
|
||||
Some(sym::prefix_nops) => &mut prefix,
|
||||
Some(sym::entry_nops) => &mut entry,
|
||||
_ => {
|
||||
tcx.dcx().emit_err(errors::UnexpectedParameterName {
|
||||
span: item.span(),
|
||||
prefix_nops: sym::prefix_nops,
|
||||
entry_nops: sym::entry_nops,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
let rustc_ast::LitKind::Int(val, _) = name_value_lit.kind else {
|
||||
tcx.dcx().emit_err(errors::InvalidLiteralValue {
|
||||
span: name_value_lit.span,
|
||||
});
|
||||
continue;
|
||||
};
|
||||
|
||||
let Ok(val) = val.get().try_into() else {
|
||||
tcx.dcx()
|
||||
.emit_err(errors::OutOfRangeInteger { span: name_value_lit.span });
|
||||
continue;
|
||||
};
|
||||
|
||||
*attrib_to_write = Some(val);
|
||||
}
|
||||
|
||||
if let (None, None) = (prefix, entry) {
|
||||
tcx.dcx().span_err(attr.span(), "must specify at least one parameter");
|
||||
}
|
||||
|
||||
Some(PatchableFunctionEntry::from_prefix_and_entry(
|
||||
prefix.unwrap_or(0),
|
||||
entry.unwrap_or(0),
|
||||
))
|
||||
})
|
||||
codegen_fn_attrs.patchable_function_entry =
|
||||
parse_patchable_function_entry(tcx, attr);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
interesting_spans
|
||||
}
|
||||
|
||||
/// Applies overrides for codegen fn attrs. These often have a specific reason why they're necessary.
|
||||
/// Please comment why when adding a new one!
|
||||
fn apply_overrides(tcx: TyCtxt<'_>, did: LocalDefId, codegen_fn_attrs: &mut CodegenFnAttrs) {
|
||||
// Apply the minimum function alignment here. This ensures that a function's alignment is
|
||||
// determined by the `-C` flags of the crate it is defined in, not the `-C` flags of the crate
|
||||
// it happens to be codegen'd (or const-eval'd) in.
|
||||
|
|
@ -390,15 +404,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
// On trait methods, inherit the `#[align]` of the trait's method prototype.
|
||||
codegen_fn_attrs.alignment = Ord::max(codegen_fn_attrs.alignment, tcx.inherited_align(did));
|
||||
|
||||
let inline_span;
|
||||
(codegen_fn_attrs.inline, inline_span) = if let Some((inline_attr, span)) =
|
||||
find_attr!(attrs, AttributeKind::Inline(i, span) => (*i, *span))
|
||||
{
|
||||
(inline_attr, Some(span))
|
||||
} else {
|
||||
(InlineAttr::None, None)
|
||||
};
|
||||
|
||||
// naked function MUST NOT be inlined! This attribute is required for the rust compiler itself,
|
||||
// but not for the code generation backend because at that point the naked function will just be
|
||||
// a declaration, with a definition provided in global assembly.
|
||||
|
|
@ -406,9 +411,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
codegen_fn_attrs.inline = InlineAttr::Never;
|
||||
}
|
||||
|
||||
codegen_fn_attrs.optimize =
|
||||
find_attr!(attrs, AttributeKind::Optimize(i, _) => *i).unwrap_or(OptimizeAttr::Default);
|
||||
|
||||
// #73631: closures inherit `#[target_feature]` annotations
|
||||
//
|
||||
// If this closure is marked `#[inline(always)]`, simply skip adding `#[target_feature]`.
|
||||
|
|
@ -431,6 +433,26 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
}
|
||||
}
|
||||
|
||||
// When `no_builtins` is applied at the crate level, we should add the
|
||||
// `no-builtins` attribute to each function to ensure it takes effect in LTO.
|
||||
let crate_attrs = tcx.hir_attrs(rustc_hir::CRATE_HIR_ID);
|
||||
let no_builtins = attr::contains_name(crate_attrs, sym::no_builtins);
|
||||
if no_builtins {
|
||||
codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_BUILTINS;
|
||||
}
|
||||
|
||||
// inherit track-caller properly
|
||||
if tcx.should_inherit_track_caller(did) {
|
||||
codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER;
|
||||
}
|
||||
}
|
||||
|
||||
fn check_result(
|
||||
tcx: TyCtxt<'_>,
|
||||
did: LocalDefId,
|
||||
interesting_spans: InterestingAttributeDiagnosticSpans,
|
||||
codegen_fn_attrs: &CodegenFnAttrs,
|
||||
) {
|
||||
// If a function uses `#[target_feature]` it can't be inlined into general
|
||||
// purpose functions as they wouldn't have the right target features
|
||||
// enabled. For that reason we also forbid `#[inline(always)]` as it can't be
|
||||
|
|
@ -446,14 +468,16 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
// llvm/llvm-project#70563).
|
||||
if !codegen_fn_attrs.target_features.is_empty()
|
||||
&& matches!(codegen_fn_attrs.inline, InlineAttr::Always)
|
||||
&& let Some(span) = inline_span
|
||||
&& let Some(span) = interesting_spans.inline
|
||||
{
|
||||
tcx.dcx().span_err(span, "cannot use `#[inline(always)]` with `#[target_feature]`");
|
||||
}
|
||||
|
||||
// warn that inline has no effect when no_sanitize is present
|
||||
if !codegen_fn_attrs.no_sanitize.is_empty()
|
||||
&& codegen_fn_attrs.inline.always()
|
||||
&& let (Some(no_sanitize_span), Some(inline_span)) = (no_sanitize_span, inline_span)
|
||||
&& let (Some(no_sanitize_span), Some(inline_span)) =
|
||||
(interesting_spans.no_sanitize, interesting_spans.inline)
|
||||
{
|
||||
let hir_id = tcx.local_def_id_to_hir_id(did);
|
||||
tcx.node_span_lint(lint::builtin::INLINE_NO_SANITIZE, hir_id, no_sanitize_span, |lint| {
|
||||
|
|
@ -462,53 +486,16 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
})
|
||||
}
|
||||
|
||||
// Weak lang items have the same semantics as "std internal" symbols in the
|
||||
// sense that they're preserved through all our LTO passes and only
|
||||
// strippable by the linker.
|
||||
//
|
||||
// Additionally weak lang items have predetermined symbol names.
|
||||
if let Some((name, _)) = lang_items::extract(attrs)
|
||||
&& let Some(lang_item) = LangItem::from_name(name)
|
||||
// error when specifying link_name together with link_ordinal
|
||||
if let Some(_) = codegen_fn_attrs.link_name
|
||||
&& let Some(_) = codegen_fn_attrs.link_ordinal
|
||||
{
|
||||
if WEAK_LANG_ITEMS.contains(&lang_item) {
|
||||
codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL;
|
||||
let msg = "cannot use `#[link_name]` with `#[link_ordinal]`";
|
||||
if let Some(span) = interesting_spans.link_ordinal {
|
||||
tcx.dcx().span_err(span, msg);
|
||||
} else {
|
||||
tcx.dcx().err(msg);
|
||||
}
|
||||
if let Some(link_name) = lang_item.link_name() {
|
||||
codegen_fn_attrs.export_name = Some(link_name);
|
||||
codegen_fn_attrs.link_name = Some(link_name);
|
||||
}
|
||||
}
|
||||
check_link_name_xor_ordinal(tcx, &codegen_fn_attrs, link_ordinal_span);
|
||||
|
||||
if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL)
|
||||
&& codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NO_MANGLE)
|
||||
{
|
||||
let no_mangle_span =
|
||||
find_attr!(attrs, AttributeKind::NoMangle(no_mangle_span) => *no_mangle_span)
|
||||
.unwrap_or_default();
|
||||
let lang_item =
|
||||
lang_items::extract(attrs).map_or(None, |(name, _span)| LangItem::from_name(name));
|
||||
let mut err = tcx
|
||||
.dcx()
|
||||
.struct_span_err(
|
||||
no_mangle_span,
|
||||
"`#[no_mangle]` cannot be used on internal language items",
|
||||
)
|
||||
.with_note("Rustc requires this item to have a specific mangled name.")
|
||||
.with_span_label(tcx.def_span(did), "should be the internal language item");
|
||||
if let Some(lang_item) = lang_item {
|
||||
if let Some(link_name) = lang_item.link_name() {
|
||||
err = err
|
||||
.with_note("If you are trying to prevent mangling to ease debugging, many")
|
||||
.with_note(format!(
|
||||
"debuggers support a command such as `rbreak {link_name}` to"
|
||||
))
|
||||
.with_note(format!(
|
||||
"match `.*{link_name}.*` instead of `break {link_name}` on a specific name"
|
||||
))
|
||||
}
|
||||
}
|
||||
err.emit();
|
||||
}
|
||||
|
||||
if let Some(features) = check_tied_features(
|
||||
|
|
@ -531,6 +518,84 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
})
|
||||
.emit();
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_lang_items(
|
||||
tcx: TyCtxt<'_>,
|
||||
did: LocalDefId,
|
||||
interesting_spans: &InterestingAttributeDiagnosticSpans,
|
||||
attrs: &[Attribute],
|
||||
codegen_fn_attrs: &mut CodegenFnAttrs,
|
||||
) {
|
||||
// Weak lang items have the same semantics as "std internal" symbols in the
|
||||
// sense that they're preserved through all our LTO passes and only
|
||||
// strippable by the linker.
|
||||
//
|
||||
// Additionally weak lang items have predetermined symbol names.
|
||||
if let Some((name, _)) = lang_items::extract(attrs)
|
||||
&& let Some(lang_item) = LangItem::from_name(name)
|
||||
{
|
||||
if WEAK_LANG_ITEMS.contains(&lang_item) {
|
||||
codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL;
|
||||
}
|
||||
if let Some(link_name) = lang_item.link_name() {
|
||||
codegen_fn_attrs.export_name = Some(link_name);
|
||||
codegen_fn_attrs.link_name = Some(link_name);
|
||||
}
|
||||
}
|
||||
|
||||
// error when using no_mangle on a lang item item
|
||||
if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL)
|
||||
&& codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NO_MANGLE)
|
||||
{
|
||||
let lang_item =
|
||||
lang_items::extract(attrs).map_or(None, |(name, _span)| LangItem::from_name(name));
|
||||
let mut err = tcx
|
||||
.dcx()
|
||||
.struct_span_err(
|
||||
interesting_spans.no_mangle.unwrap_or_default(),
|
||||
"`#[no_mangle]` cannot be used on internal language items",
|
||||
)
|
||||
.with_note("Rustc requires this item to have a specific mangled name.")
|
||||
.with_span_label(tcx.def_span(did), "should be the internal language item");
|
||||
if let Some(lang_item) = lang_item
|
||||
&& let Some(link_name) = lang_item.link_name()
|
||||
{
|
||||
err = err
|
||||
.with_note("If you are trying to prevent mangling to ease debugging, many")
|
||||
.with_note(format!("debuggers support a command such as `rbreak {link_name}` to"))
|
||||
.with_note(format!(
|
||||
"match `.*{link_name}.*` instead of `break {link_name}` on a specific name"
|
||||
))
|
||||
}
|
||||
err.emit();
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate the [`CodegenFnAttrs`] for an item (identified by the [`LocalDefId`]).
|
||||
///
|
||||
/// This happens in 4 stages:
|
||||
/// - apply built-in attributes that directly translate to codegen attributes.
|
||||
/// - handle lang items. These have special codegen attrs applied to them.
|
||||
/// - apply overrides, like minimum requirements for alignment and other settings that don't rely directly the built-in attrs on the item.
|
||||
/// overrides come after applying built-in attributes since they may only apply when certain attributes were already set in the stage before.
|
||||
/// - check that the result is valid. There's various ways in which this may not be the case, such as certain combinations of attrs.
|
||||
fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
||||
if cfg!(debug_assertions) {
|
||||
let def_kind = tcx.def_kind(did);
|
||||
assert!(
|
||||
def_kind.has_codegen_attrs(),
|
||||
"unexpected `def_kind` in `codegen_fn_attrs`: {def_kind:?}",
|
||||
);
|
||||
}
|
||||
|
||||
let mut codegen_fn_attrs = CodegenFnAttrs::new();
|
||||
let attrs = tcx.hir_attrs(tcx.local_def_id_to_hir_id(did));
|
||||
|
||||
let interesting_spans = process_builtin_attrs(tcx, did, attrs, &mut codegen_fn_attrs);
|
||||
handle_lang_items(tcx, did, &interesting_spans, attrs, &mut codegen_fn_attrs);
|
||||
apply_overrides(tcx, did, &mut codegen_fn_attrs);
|
||||
check_result(tcx, did, interesting_spans, &codegen_fn_attrs);
|
||||
|
||||
codegen_fn_attrs
|
||||
}
|
||||
|
|
@ -557,27 +622,12 @@ fn inherited_align<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> Option<Align> {
|
|||
tcx.codegen_fn_attrs(opt_trait_item(tcx, def_id)?).alignment
|
||||
}
|
||||
|
||||
fn check_link_name_xor_ordinal(
|
||||
tcx: TyCtxt<'_>,
|
||||
codegen_fn_attrs: &CodegenFnAttrs,
|
||||
inline_span: Option<Span>,
|
||||
) {
|
||||
if codegen_fn_attrs.link_name.is_none() || codegen_fn_attrs.link_ordinal.is_none() {
|
||||
return;
|
||||
}
|
||||
let msg = "cannot use `#[link_name]` with `#[link_ordinal]`";
|
||||
if let Some(span) = inline_span {
|
||||
tcx.dcx().span_err(span, msg);
|
||||
} else {
|
||||
tcx.dcx().err(msg);
|
||||
}
|
||||
}
|
||||
|
||||
/// We now check the #\[rustc_autodiff\] attributes which we generated from the #[autodiff(...)]
|
||||
/// macros. There are two forms. The pure one without args to mark primal functions (the functions
|
||||
/// being differentiated). The other form is #[rustc_autodiff(Mode, ActivityList)] on top of the
|
||||
/// placeholder functions. We wrote the rustc_autodiff attributes ourself, so this should never
|
||||
/// panic, unless we introduced a bug when parsing the autodiff macro.
|
||||
//FIXME(jdonszelmann): put in the main loop. No need to have two..... :/ Let's do that when we make autodiff parsed.
|
||||
fn autodiff_attrs(tcx: TyCtxt<'_>, id: DefId) -> Option<AutoDiffAttrs> {
|
||||
let attrs = tcx.get_attrs(id, sym::rustc_autodiff);
|
||||
|
||||
|
|
|
|||
|
|
@ -16,12 +16,6 @@ pub trait WriteBackendMethods: Clone + 'static {
|
|||
type ThinData: Send + Sync;
|
||||
type ThinBuffer: ThinBufferMethods;
|
||||
|
||||
/// Merge all modules into main_module and returning it
|
||||
fn run_link(
|
||||
cgcx: &CodegenContext<Self>,
|
||||
dcx: DiagCtxtHandle<'_>,
|
||||
modules: Vec<ModuleCodegen<Self::Module>>,
|
||||
) -> Result<ModuleCodegen<Self::Module>, FatalError>;
|
||||
/// Performs fat LTO by merging all modules into a single one, running autodiff
|
||||
/// if necessary and running any further optimizations
|
||||
fn run_and_optimize_fat_lto(
|
||||
|
|
|
|||
|
|
@ -128,15 +128,15 @@ const_eval_frame_note_inner = inside {$where_ ->
|
|||
|
||||
const_eval_frame_note_last = the failure occurred here
|
||||
|
||||
const_eval_incompatible_arg_types =
|
||||
calling a function whose parameter #{$arg_idx} has type {$callee_ty} passing argument of type {$caller_ty}
|
||||
|
||||
const_eval_incompatible_calling_conventions =
|
||||
calling a function with calling convention "{$callee_conv}" using calling convention "{$caller_conv}"
|
||||
|
||||
const_eval_incompatible_return_types =
|
||||
calling a function with return type {$callee_ty} passing return place of type {$caller_ty}
|
||||
|
||||
const_eval_incompatible_types =
|
||||
calling a function with argument of type {$callee_ty} passing data of type {$caller_ty}
|
||||
|
||||
const_eval_interior_mutable_borrow_escaping =
|
||||
interior mutable shared borrows of temporaries that have their lifetime extended until the end of the program are not allowed
|
||||
.label = this borrow of an interior mutable value refers to such a temporary
|
||||
|
|
|
|||
|
|
@ -714,10 +714,10 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
|
|||
|
||||
fn visit_operand(&mut self, op: &Operand<'tcx>, location: Location) {
|
||||
self.super_operand(op, location);
|
||||
if let Operand::Constant(c) = op {
|
||||
if let Some(def_id) = c.check_static_ptr(self.tcx) {
|
||||
self.check_static(def_id, self.span);
|
||||
}
|
||||
if let Operand::Constant(c) = op
|
||||
&& let Some(def_id) = c.check_static_ptr(self.tcx)
|
||||
{
|
||||
self.check_static(def_id, self.span);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -784,7 +784,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
|
|||
self.revalidate_conditional_constness(callee, fn_args, *fn_span);
|
||||
|
||||
// Attempting to call a trait method?
|
||||
if let Some(trait_did) = tcx.trait_of_item(callee) {
|
||||
if let Some(trait_did) = tcx.trait_of_assoc(callee) {
|
||||
// We can't determine the actual callee here, so we have to do different checks
|
||||
// than usual.
|
||||
|
||||
|
|
|
|||
|
|
@ -295,21 +295,18 @@ fn build_error_for_const_call<'tcx>(
|
|||
}
|
||||
let deref = "*".repeat(num_refs);
|
||||
|
||||
if let Ok(call_str) = ccx.tcx.sess.source_map().span_to_snippet(span) {
|
||||
if let Some(eq_idx) = call_str.find("==") {
|
||||
if let Some(rhs_idx) =
|
||||
call_str[(eq_idx + 2)..].find(|c: char| !c.is_whitespace())
|
||||
{
|
||||
let rhs_pos =
|
||||
span.lo() + BytePos::from_usize(eq_idx + 2 + rhs_idx);
|
||||
let rhs_span = span.with_lo(rhs_pos).with_hi(rhs_pos);
|
||||
sugg = Some(errors::ConsiderDereferencing {
|
||||
deref,
|
||||
span: span.shrink_to_lo(),
|
||||
rhs_span,
|
||||
});
|
||||
}
|
||||
}
|
||||
if let Ok(call_str) = ccx.tcx.sess.source_map().span_to_snippet(span)
|
||||
&& let Some(eq_idx) = call_str.find("==")
|
||||
&& let Some(rhs_idx) =
|
||||
call_str[(eq_idx + 2)..].find(|c: char| !c.is_whitespace())
|
||||
{
|
||||
let rhs_pos = span.lo() + BytePos::from_usize(eq_idx + 2 + rhs_idx);
|
||||
let rhs_span = span.with_lo(rhs_pos).with_hi(rhs_pos);
|
||||
sugg = Some(errors::ConsiderDereferencing {
|
||||
deref,
|
||||
span: span.shrink_to_lo(),
|
||||
rhs_span,
|
||||
});
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
|
|
|
|||
|
|
@ -369,7 +369,7 @@ where
|
|||
assert!(promoted.is_none() || Q::ALLOW_PROMOTED);
|
||||
|
||||
// Don't peek inside trait associated constants.
|
||||
if promoted.is_none() && cx.tcx.trait_of_item(def).is_none() {
|
||||
if promoted.is_none() && cx.tcx.trait_of_assoc(def).is_none() {
|
||||
let qualifs = cx.tcx.at(constant.span).mir_const_qualif(def);
|
||||
|
||||
if !Q::in_qualifs(&qualifs) {
|
||||
|
|
|
|||
|
|
@ -137,14 +137,14 @@ where
|
|||
|
||||
// If a local with no projections is moved from (e.g. `x` in `y = x`), record that
|
||||
// it no longer needs to be dropped.
|
||||
if let mir::Operand::Move(place) = operand {
|
||||
if let Some(local) = place.as_local() {
|
||||
// For backward compatibility with the MaybeMutBorrowedLocals used in an earlier
|
||||
// implementation we retain qualif if a local had been borrowed before. This might
|
||||
// not be strictly necessary since the local is no longer initialized.
|
||||
if !self.state.borrow.contains(local) {
|
||||
self.state.qualif.remove(local);
|
||||
}
|
||||
if let mir::Operand::Move(place) = operand
|
||||
&& let Some(local) = place.as_local()
|
||||
{
|
||||
// For backward compatibility with the MaybeMutBorrowedLocals used in an earlier
|
||||
// implementation we retain qualif if a local had been borrowed before. This might
|
||||
// not be strictly necessary since the local is no longer initialized.
|
||||
if !self.state.borrow.contains(local) {
|
||||
self.state.qualif.remove(local);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -500,7 +500,7 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
|
|||
InvalidNichedEnumVariantWritten { .. } => {
|
||||
const_eval_invalid_niched_enum_variant_written
|
||||
}
|
||||
AbiMismatchArgument { .. } => const_eval_incompatible_types,
|
||||
AbiMismatchArgument { .. } => const_eval_incompatible_arg_types,
|
||||
AbiMismatchReturn { .. } => const_eval_incompatible_return_types,
|
||||
}
|
||||
}
|
||||
|
|
@ -625,12 +625,16 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
|
|||
diag.arg("data_size", info.data_size);
|
||||
}
|
||||
InvalidNichedEnumVariantWritten { enum_ty } => {
|
||||
diag.arg("ty", enum_ty.to_string());
|
||||
diag.arg("ty", enum_ty);
|
||||
}
|
||||
AbiMismatchArgument { caller_ty, callee_ty }
|
||||
| AbiMismatchReturn { caller_ty, callee_ty } => {
|
||||
diag.arg("caller_ty", caller_ty.to_string());
|
||||
diag.arg("callee_ty", callee_ty.to_string());
|
||||
AbiMismatchArgument { arg_idx, caller_ty, callee_ty } => {
|
||||
diag.arg("arg_idx", arg_idx + 1); // adjust for 1-indexed lists in output
|
||||
diag.arg("caller_ty", caller_ty);
|
||||
diag.arg("callee_ty", callee_ty);
|
||||
}
|
||||
AbiMismatchReturn { caller_ty, callee_ty } => {
|
||||
diag.arg("caller_ty", caller_ty);
|
||||
diag.arg("callee_ty", callee_ty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -270,6 +270,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
Item = (&'x FnArg<'tcx, M::Provenance>, &'y ArgAbi<'tcx, Ty<'tcx>>),
|
||||
>,
|
||||
callee_abi: &ArgAbi<'tcx, Ty<'tcx>>,
|
||||
callee_arg_idx: usize,
|
||||
callee_arg: &mir::Place<'tcx>,
|
||||
callee_ty: Ty<'tcx>,
|
||||
already_live: bool,
|
||||
|
|
@ -298,6 +299,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
// Check compatibility
|
||||
if !self.check_argument_compat(caller_abi, callee_abi)? {
|
||||
throw_ub!(AbiMismatchArgument {
|
||||
arg_idx: callee_arg_idx,
|
||||
caller_ty: caller_abi.layout.ty,
|
||||
callee_ty: callee_abi.layout.ty
|
||||
});
|
||||
|
|
@ -424,7 +426,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
// this is a single iterator (that handles `spread_arg`), then
|
||||
// `pass_argument` would be the loop body. It takes care to
|
||||
// not advance `caller_iter` for ignored arguments.
|
||||
let mut callee_args_abis = callee_fn_abi.args.iter();
|
||||
let mut callee_args_abis = callee_fn_abi.args.iter().enumerate();
|
||||
for local in body.args_iter() {
|
||||
// Construct the destination place for this argument. At this point all
|
||||
// locals are still dead, so we cannot construct a `PlaceTy`.
|
||||
|
|
@ -445,10 +447,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
&[mir::ProjectionElem::Field(FieldIdx::from_usize(i), field_ty)],
|
||||
*self.tcx,
|
||||
);
|
||||
let callee_abi = callee_args_abis.next().unwrap();
|
||||
let (idx, callee_abi) = callee_args_abis.next().unwrap();
|
||||
self.pass_argument(
|
||||
&mut caller_args,
|
||||
callee_abi,
|
||||
idx,
|
||||
&dest,
|
||||
field_ty,
|
||||
/* already_live */ true,
|
||||
|
|
@ -456,10 +459,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
}
|
||||
} else {
|
||||
// Normal argument. Cannot mark it as live yet, it might be unsized!
|
||||
let callee_abi = callee_args_abis.next().unwrap();
|
||||
let (idx, callee_abi) = callee_args_abis.next().unwrap();
|
||||
self.pass_argument(
|
||||
&mut caller_args,
|
||||
callee_abi,
|
||||
idx,
|
||||
&dest,
|
||||
ty,
|
||||
/* already_live */ false,
|
||||
|
|
@ -721,7 +725,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
) {
|
||||
let tcx = *self.tcx;
|
||||
|
||||
let trait_def_id = tcx.trait_of_item(def_id).unwrap();
|
||||
let trait_def_id = tcx.trait_of_assoc(def_id).unwrap();
|
||||
let virtual_trait_ref = ty::TraitRef::from_method(tcx, trait_def_id, virtual_instance.args);
|
||||
let existential_trait_ref = ty::ExistentialTraitRef::erase_self_ty(tcx, virtual_trait_ref);
|
||||
let concrete_trait_ref = existential_trait_ref.with_self_ty(tcx, dyn_ty);
|
||||
|
|
|
|||
|
|
@ -442,10 +442,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
// # First compute the dynamic alignment
|
||||
|
||||
// Packed type alignment needs to be capped.
|
||||
if let ty::Adt(def, _) = layout.ty.kind() {
|
||||
if let Some(packed) = def.repr().pack {
|
||||
unsized_align = unsized_align.min(packed);
|
||||
}
|
||||
if let ty::Adt(def, _) = layout.ty.kind()
|
||||
&& let Some(packed) = def.repr().pack
|
||||
{
|
||||
unsized_align = unsized_align.min(packed);
|
||||
}
|
||||
|
||||
// Choose max of two known alignments (combined value must
|
||||
|
|
|
|||
|
|
@ -320,10 +320,10 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
|
|||
// for a coroutine).
|
||||
let var_hir_id = captured_place.get_root_variable();
|
||||
let node = self.ecx.tcx.hir_node(var_hir_id);
|
||||
if let hir::Node::Pat(pat) = node {
|
||||
if let hir::PatKind::Binding(_, _, ident, _) = pat.kind {
|
||||
name = Some(ident.name);
|
||||
}
|
||||
if let hir::Node::Pat(pat) = node
|
||||
&& let hir::PatKind::Binding(_, _, ident, _) = pat.kind
|
||||
{
|
||||
name = Some(ident.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -713,8 +713,7 @@ impl HumanEmitter {
|
|||
Style::LineNumber,
|
||||
);
|
||||
}
|
||||
buffer.puts(line_offset, 0, &self.maybe_anonymized(line_index), Style::LineNumber);
|
||||
|
||||
self.draw_line_num(buffer, line_index, line_offset, width_offset - 3);
|
||||
self.draw_col_separator_no_space(buffer, line_offset, width_offset - 2);
|
||||
left
|
||||
}
|
||||
|
|
@ -2128,11 +2127,11 @@ impl HumanEmitter {
|
|||
// Account for a suggestion to completely remove a line(s) with whitespace (#94192).
|
||||
let line_end = sm.lookup_char_pos(parts[0].span.hi()).line;
|
||||
for line in line_start..=line_end {
|
||||
buffer.puts(
|
||||
self.draw_line_num(
|
||||
&mut buffer,
|
||||
line,
|
||||
row_num - 1 + line - line_start,
|
||||
0,
|
||||
&self.maybe_anonymized(line),
|
||||
Style::LineNumber,
|
||||
max_line_num_len,
|
||||
);
|
||||
buffer.puts(
|
||||
row_num - 1 + line - line_start,
|
||||
|
|
@ -2612,12 +2611,7 @@ impl HumanEmitter {
|
|||
// For more info: https://github.com/rust-lang/rust/issues/92741
|
||||
let lines_to_remove = file_lines.lines.iter().take(file_lines.lines.len() - 1);
|
||||
for (index, line_to_remove) in lines_to_remove.enumerate() {
|
||||
buffer.puts(
|
||||
*row_num - 1,
|
||||
0,
|
||||
&self.maybe_anonymized(line_num + index),
|
||||
Style::LineNumber,
|
||||
);
|
||||
self.draw_line_num(buffer, line_num + index, *row_num - 1, max_line_num_len);
|
||||
buffer.puts(*row_num - 1, max_line_num_len + 1, "- ", Style::Removal);
|
||||
let line = normalize_whitespace(
|
||||
&file_lines.file.get_line(line_to_remove.line_index).unwrap(),
|
||||
|
|
@ -2634,11 +2628,11 @@ impl HumanEmitter {
|
|||
let last_line_index = file_lines.lines[file_lines.lines.len() - 1].line_index;
|
||||
let last_line = &file_lines.file.get_line(last_line_index).unwrap();
|
||||
if last_line != line_to_add {
|
||||
buffer.puts(
|
||||
self.draw_line_num(
|
||||
buffer,
|
||||
line_num + file_lines.lines.len() - 1,
|
||||
*row_num - 1,
|
||||
0,
|
||||
&self.maybe_anonymized(line_num + file_lines.lines.len() - 1),
|
||||
Style::LineNumber,
|
||||
max_line_num_len,
|
||||
);
|
||||
buffer.puts(*row_num - 1, max_line_num_len + 1, "- ", Style::Removal);
|
||||
buffer.puts(
|
||||
|
|
@ -2661,7 +2655,7 @@ impl HumanEmitter {
|
|||
// 2 - .await
|
||||
// |
|
||||
// *row_num -= 1;
|
||||
buffer.puts(*row_num, 0, &self.maybe_anonymized(line_num), Style::LineNumber);
|
||||
self.draw_line_num(buffer, line_num, *row_num, max_line_num_len);
|
||||
buffer.puts(*row_num, max_line_num_len + 1, "+ ", Style::Addition);
|
||||
buffer.append(*row_num, &normalize_whitespace(line_to_add), Style::NoStyle);
|
||||
} else {
|
||||
|
|
@ -2671,7 +2665,7 @@ impl HumanEmitter {
|
|||
*row_num -= 2;
|
||||
}
|
||||
} else if is_multiline {
|
||||
buffer.puts(*row_num, 0, &self.maybe_anonymized(line_num), Style::LineNumber);
|
||||
self.draw_line_num(buffer, line_num, *row_num, max_line_num_len);
|
||||
match &highlight_parts {
|
||||
[SubstitutionHighlight { start: 0, end }] if *end == line_to_add.len() => {
|
||||
buffer.puts(*row_num, max_line_num_len + 1, "+ ", Style::Addition);
|
||||
|
|
@ -2702,11 +2696,11 @@ impl HumanEmitter {
|
|||
Style::NoStyle,
|
||||
);
|
||||
} else if let DisplaySuggestion::Add = show_code_change {
|
||||
buffer.puts(*row_num, 0, &self.maybe_anonymized(line_num), Style::LineNumber);
|
||||
self.draw_line_num(buffer, line_num, *row_num, max_line_num_len);
|
||||
buffer.puts(*row_num, max_line_num_len + 1, "+ ", Style::Addition);
|
||||
buffer.append(*row_num, &normalize_whitespace(line_to_add), Style::NoStyle);
|
||||
} else {
|
||||
buffer.puts(*row_num, 0, &self.maybe_anonymized(line_num), Style::LineNumber);
|
||||
self.draw_line_num(buffer, line_num, *row_num, max_line_num_len);
|
||||
self.draw_col_separator(buffer, *row_num, max_line_num_len + 1);
|
||||
buffer.append(*row_num, &normalize_whitespace(line_to_add), Style::NoStyle);
|
||||
}
|
||||
|
|
@ -3016,6 +3010,22 @@ impl HumanEmitter {
|
|||
OutputTheme::Unicode => "…",
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_line_num(
|
||||
&self,
|
||||
buffer: &mut StyledBuffer,
|
||||
line_num: usize,
|
||||
line_offset: usize,
|
||||
max_line_num_len: usize,
|
||||
) {
|
||||
let line_num = self.maybe_anonymized(line_num);
|
||||
buffer.puts(
|
||||
line_offset,
|
||||
max_line_num_len.saturating_sub(str_width(&line_num)),
|
||||
&line_num,
|
||||
Style::LineNumber,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
|
|
|
|||
|
|
@ -153,12 +153,11 @@ impl StyledBuffer {
|
|||
/// 1. That line and column exist in `StyledBuffer`
|
||||
/// 2. `overwrite` is `true` or existing style is `Style::NoStyle` or `Style::Quotation`
|
||||
fn set_style(&mut self, line: usize, col: usize, style: Style, overwrite: bool) {
|
||||
if let Some(ref mut line) = self.lines.get_mut(line) {
|
||||
if let Some(StyledChar { style: s, .. }) = line.get_mut(col) {
|
||||
if overwrite || matches!(s, Style::NoStyle | Style::Quotation) {
|
||||
*s = style;
|
||||
}
|
||||
}
|
||||
if let Some(ref mut line) = self.lines.get_mut(line)
|
||||
&& let Some(StyledChar { style: s, .. }) = line.get_mut(col)
|
||||
&& (overwrite || matches!(s, Style::NoStyle | Style::Quotation))
|
||||
{
|
||||
*s = style;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,26 +1,8 @@
|
|||
expand_arg_not_attributes =
|
||||
second argument must be `attributes`
|
||||
|
||||
expand_attr_no_arguments =
|
||||
attribute must have either one or two arguments
|
||||
|
||||
expand_attribute_meta_item =
|
||||
attribute must be a meta item, not a literal
|
||||
|
||||
expand_attribute_single_word =
|
||||
attribute must only be a single word
|
||||
|
||||
expand_attributes_on_expressions_experimental =
|
||||
attributes on expressions are experimental
|
||||
.help_outer_doc = `///` is used for outer documentation comments; for a plain comment, use `//`
|
||||
.help_inner_doc = `//!` is used for inner documentation comments; for a plain comment, use `//` by removing the `!` or inserting a space in between them: `// !`
|
||||
|
||||
expand_attributes_wrong_form =
|
||||
attribute must be of form: `attributes(foo, bar)`
|
||||
|
||||
expand_cannot_be_name_of_macro =
|
||||
`{$trait_ident}` cannot be a name of {$macro_type} macro
|
||||
|
||||
expand_collapse_debuginfo_illegal =
|
||||
illegal value for attribute #[collapse_debuginfo(no|external|yes)]
|
||||
|
||||
|
|
@ -71,9 +53,6 @@ expand_glob_delegation_outside_impls =
|
|||
expand_glob_delegation_traitless_qpath =
|
||||
qualified path without a trait in glob delegation
|
||||
|
||||
expand_helper_attribute_name_invalid =
|
||||
`{$name}` cannot be a name of derive helper attribute
|
||||
|
||||
expand_incomplete_parse =
|
||||
macro expansion ignores {$descr} and any tokens following
|
||||
.label = caused by the macro expansion here
|
||||
|
|
@ -165,12 +144,6 @@ expand_mve_unrecognized_var =
|
|||
expand_non_inline_modules_in_proc_macro_input_are_unstable =
|
||||
non-inline modules in proc macro input are unstable
|
||||
|
||||
expand_not_a_meta_item =
|
||||
not a meta item
|
||||
|
||||
expand_only_one_word =
|
||||
must only be one word
|
||||
|
||||
expand_proc_macro_back_compat = using an old version of `{$crate_name}`
|
||||
.note = older versions of the `{$crate_name}` crate no longer compile; please update to `{$crate_name}` v{$fixed_version}, or switch to one of the `{$crate_name}` alternatives
|
||||
|
||||
|
|
|
|||
|
|
@ -861,7 +861,7 @@ impl SyntaxExtension {
|
|||
/// | (unspecified) | no | if-ext | if-ext | yes |
|
||||
/// | external | no | if-ext | if-ext | yes |
|
||||
/// | yes | yes | yes | yes | yes |
|
||||
fn get_collapse_debuginfo(sess: &Session, attrs: &[impl AttributeExt], ext: bool) -> bool {
|
||||
fn get_collapse_debuginfo(sess: &Session, attrs: &[hir::Attribute], ext: bool) -> bool {
|
||||
let flag = sess.opts.cg.collapse_macro_debuginfo;
|
||||
let attr = ast::attr::find_by_name(attrs, sym::collapse_debuginfo)
|
||||
.and_then(|attr| {
|
||||
|
|
@ -872,7 +872,7 @@ impl SyntaxExtension {
|
|||
.ok()
|
||||
})
|
||||
.unwrap_or_else(|| {
|
||||
if ast::attr::contains_name(attrs, sym::rustc_builtin_macro) {
|
||||
if find_attr!(attrs, AttributeKind::RustcBuiltinMacro { .. }) {
|
||||
CollapseMacroDebuginfo::Yes
|
||||
} else {
|
||||
CollapseMacroDebuginfo::Unspecified
|
||||
|
|
@ -915,16 +915,18 @@ impl SyntaxExtension {
|
|||
let collapse_debuginfo = Self::get_collapse_debuginfo(sess, attrs, !is_local);
|
||||
tracing::debug!(?name, ?local_inner_macros, ?collapse_debuginfo, ?allow_internal_unsafe);
|
||||
|
||||
let (builtin_name, helper_attrs) = ast::attr::find_by_name(attrs, sym::rustc_builtin_macro)
|
||||
.map(|attr| {
|
||||
// Override `helper_attrs` passed above if it's a built-in macro,
|
||||
// marking `proc_macro_derive` macros as built-in is not a realistic use case.
|
||||
parse_macro_name_and_helper_attrs(sess.dcx(), attr, "built-in").map_or_else(
|
||||
|| (Some(name), Vec::new()),
|
||||
|(name, helper_attrs)| (Some(name), helper_attrs),
|
||||
)
|
||||
})
|
||||
.unwrap_or_else(|| (None, helper_attrs));
|
||||
let (builtin_name, helper_attrs) = match find_attr!(attrs, AttributeKind::RustcBuiltinMacro { builtin_name, helper_attrs, .. } => (builtin_name, helper_attrs))
|
||||
{
|
||||
// Override `helper_attrs` passed above if it's a built-in macro,
|
||||
// marking `proc_macro_derive` macros as built-in is not a realistic use case.
|
||||
Some((Some(name), helper_attrs)) => {
|
||||
(Some(*name), helper_attrs.iter().copied().collect())
|
||||
}
|
||||
Some((None, _)) => (Some(name), Vec::new()),
|
||||
|
||||
// Not a built-in macro
|
||||
None => (None, helper_attrs),
|
||||
};
|
||||
|
||||
let stability = find_attr!(attrs, AttributeKind::Stability { stability, .. } => *stability);
|
||||
|
||||
|
|
@ -1390,80 +1392,6 @@ pub fn resolve_path(sess: &Session, path: impl Into<PathBuf>, span: Span) -> PRe
|
|||
}
|
||||
}
|
||||
|
||||
pub fn parse_macro_name_and_helper_attrs(
|
||||
dcx: DiagCtxtHandle<'_>,
|
||||
attr: &impl AttributeExt,
|
||||
macro_type: &str,
|
||||
) -> Option<(Symbol, Vec<Symbol>)> {
|
||||
// Once we've located the `#[proc_macro_derive]` attribute, verify
|
||||
// that it's of the form `#[proc_macro_derive(Foo)]` or
|
||||
// `#[proc_macro_derive(Foo, attributes(A, ..))]`
|
||||
let list = attr.meta_item_list()?;
|
||||
let ([trait_attr] | [trait_attr, _]) = list.as_slice() else {
|
||||
dcx.emit_err(errors::AttrNoArguments { span: attr.span() });
|
||||
return None;
|
||||
};
|
||||
let Some(trait_attr) = trait_attr.meta_item() else {
|
||||
dcx.emit_err(errors::NotAMetaItem { span: trait_attr.span() });
|
||||
return None;
|
||||
};
|
||||
let trait_ident = match trait_attr.ident() {
|
||||
Some(trait_ident) if trait_attr.is_word() => trait_ident,
|
||||
_ => {
|
||||
dcx.emit_err(errors::OnlyOneWord { span: trait_attr.span });
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
if !trait_ident.name.can_be_raw() {
|
||||
dcx.emit_err(errors::CannotBeNameOfMacro {
|
||||
span: trait_attr.span,
|
||||
trait_ident,
|
||||
macro_type,
|
||||
});
|
||||
}
|
||||
|
||||
let attributes_attr = list.get(1);
|
||||
let proc_attrs: Vec<_> = if let Some(attr) = attributes_attr {
|
||||
if !attr.has_name(sym::attributes) {
|
||||
dcx.emit_err(errors::ArgumentNotAttributes { span: attr.span() });
|
||||
}
|
||||
attr.meta_item_list()
|
||||
.unwrap_or_else(|| {
|
||||
dcx.emit_err(errors::AttributesWrongForm { span: attr.span() });
|
||||
&[]
|
||||
})
|
||||
.iter()
|
||||
.filter_map(|attr| {
|
||||
let Some(attr) = attr.meta_item() else {
|
||||
dcx.emit_err(errors::AttributeMetaItem { span: attr.span() });
|
||||
return None;
|
||||
};
|
||||
|
||||
let ident = match attr.ident() {
|
||||
Some(ident) if attr.is_word() => ident,
|
||||
_ => {
|
||||
dcx.emit_err(errors::AttributeSingleWord { span: attr.span });
|
||||
return None;
|
||||
}
|
||||
};
|
||||
if !ident.name.can_be_raw() {
|
||||
dcx.emit_err(errors::HelperAttributeNameInvalid {
|
||||
span: attr.span,
|
||||
name: ident,
|
||||
});
|
||||
}
|
||||
|
||||
Some(ident.name)
|
||||
})
|
||||
.collect()
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
|
||||
Some((trait_ident.name, proc_attrs))
|
||||
}
|
||||
|
||||
/// If this item looks like a specific enums from `rental`, emit a fatal error.
|
||||
/// See #73345 and #83125 for more details.
|
||||
/// FIXME(#73933): Remove this eventually.
|
||||
|
|
|
|||
|
|
@ -78,72 +78,6 @@ pub(crate) struct MacroBodyStability {
|
|||
pub head_span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(expand_attr_no_arguments)]
|
||||
pub(crate) struct AttrNoArguments {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(expand_not_a_meta_item)]
|
||||
pub(crate) struct NotAMetaItem {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(expand_only_one_word)]
|
||||
pub(crate) struct OnlyOneWord {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(expand_cannot_be_name_of_macro)]
|
||||
pub(crate) struct CannotBeNameOfMacro<'a> {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub trait_ident: Ident,
|
||||
pub macro_type: &'a str,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(expand_arg_not_attributes)]
|
||||
pub(crate) struct ArgumentNotAttributes {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(expand_attributes_wrong_form)]
|
||||
pub(crate) struct AttributesWrongForm {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(expand_attribute_meta_item)]
|
||||
pub(crate) struct AttributeMetaItem {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(expand_attribute_single_word)]
|
||||
pub(crate) struct AttributeSingleWord {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(expand_helper_attribute_name_invalid)]
|
||||
pub(crate) struct HelperAttributeNameInvalid {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub name: Ident,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(expand_feature_removed, code = E0557)]
|
||||
#[note]
|
||||
|
|
|
|||
|
|
@ -295,6 +295,10 @@ impl DefKind {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn is_assoc(self) -> bool {
|
||||
matches!(self, DefKind::AssocConst | DefKind::AssocFn | DefKind::AssocTy)
|
||||
}
|
||||
|
||||
/// This is a "module" in name resolution sense.
|
||||
#[inline]
|
||||
pub fn is_module_like(self) -> bool {
|
||||
|
|
|
|||
|
|
@ -148,6 +148,11 @@ impl From<Ident> for LifetimeSyntax {
|
|||
/// `LifetimeSource::OutlivesBound` or `LifetimeSource::PreciseCapturing`
|
||||
/// — there's no way to "elide" these lifetimes.
|
||||
#[derive(Debug, Copy, Clone, HashStable_Generic)]
|
||||
// Raise the aligement to at least 4 bytes - this is relied on in other parts of the compiler(for pointer tagging):
|
||||
// https://github.com/rust-lang/rust/blob/ce5fdd7d42aba9a2925692e11af2bd39cf37798a/compiler/rustc_data_structures/src/tagged_ptr.rs#L163
|
||||
// Removing this `repr(4)` will cause the compiler to not build on platforms like `m68k` Linux, where the aligement of u32 and usize is only 2.
|
||||
// Since `repr(align)` may only raise aligement, this has no effect on platforms where the aligement is already sufficient.
|
||||
#[repr(align(4))]
|
||||
pub struct Lifetime {
|
||||
#[stable_hasher(ignore)]
|
||||
pub hir_id: HirId,
|
||||
|
|
@ -1363,6 +1368,17 @@ impl AttributeExt for Attribute {
|
|||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_proc_macro_attr(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
Attribute::Parsed(
|
||||
AttributeKind::ProcMacro(..)
|
||||
| AttributeKind::ProcMacroAttribute(..)
|
||||
| AttributeKind::ProcMacroDerive { .. }
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME(fn_delegation): use function delegation instead of manually forwarding
|
||||
|
|
|
|||
|
|
@ -768,7 +768,9 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(),
|
|||
check_static_inhabited(tcx, def_id);
|
||||
check_static_linkage(tcx, def_id);
|
||||
let ty = tcx.type_of(def_id).instantiate_identity();
|
||||
res = res.and(wfcheck::check_static_item(tcx, def_id, ty, true));
|
||||
res = res.and(wfcheck::check_static_item(
|
||||
tcx, def_id, ty, /* should_check_for_sync */ true,
|
||||
));
|
||||
}
|
||||
DefKind::Const => res = res.and(wfcheck::check_const_item(tcx, def_id)),
|
||||
_ => unreachable!(),
|
||||
|
|
|
|||
|
|
@ -530,13 +530,12 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> {
|
|||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, a)| {
|
||||
if let hir::TyKind::Infer(()) = a.kind {
|
||||
if let Some(suggested_ty) =
|
||||
if let hir::TyKind::Infer(()) = a.kind
|
||||
&& let Some(suggested_ty) =
|
||||
self.lowerer().suggest_trait_fn_ty_for_impl_fn_infer(hir_id, Some(i))
|
||||
{
|
||||
infer_replacements.push((a.span, suggested_ty.to_string()));
|
||||
return Ty::new_error_with_message(tcx, a.span, suggested_ty.to_string());
|
||||
}
|
||||
{
|
||||
infer_replacements.push((a.span, suggested_ty.to_string()));
|
||||
return Ty::new_error_with_message(tcx, a.span, suggested_ty.to_string());
|
||||
}
|
||||
|
||||
self.lowerer().lower_ty(a)
|
||||
|
|
|
|||
|
|
@ -221,7 +221,9 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_
|
|||
let ty = icx.lower_ty(ty);
|
||||
// MIR relies on references to statics being scalars.
|
||||
// Verify that here to avoid ill-formed MIR.
|
||||
match check_static_item(tcx, def_id, ty, false) {
|
||||
// We skip the `Sync` check to avoid cycles for type-alias-impl-trait,
|
||||
// relying on the fact that non-Sync statics don't ICE the rest of the compiler.
|
||||
match check_static_item(tcx, def_id, ty, /* should_check_for_sync */ false) {
|
||||
Ok(()) => ty,
|
||||
Err(guar) => Ty::new_error(tcx, guar),
|
||||
}
|
||||
|
|
@ -286,7 +288,9 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_
|
|||
let ty = icx.lower_ty(ty);
|
||||
// MIR relies on references to statics being scalars.
|
||||
// Verify that here to avoid ill-formed MIR.
|
||||
match check_static_item(tcx, def_id, ty, false) {
|
||||
// We skip the `Sync` check to avoid cycles for type-alias-impl-trait,
|
||||
// relying on the fact that non-Sync statics don't ICE the rest of the compiler.
|
||||
match check_static_item(tcx, def_id, ty, /* should_check_for_sync */ false) {
|
||||
Ok(()) => ty,
|
||||
Err(guar) => Ty::new_error(tcx, guar),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -759,7 +759,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
|
|||
&self,
|
||||
err: &mut Diag<'_, impl EmissionGuarantee>,
|
||||
) {
|
||||
let trait_ = match self.tcx.trait_of_item(self.def_id) {
|
||||
let trait_ = match self.tcx.trait_of_assoc(self.def_id) {
|
||||
Some(def_id) => def_id,
|
||||
None => return,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -617,18 +617,14 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
});
|
||||
|
||||
// Provide the resolved type of the associated constant to `type_of(AnonConst)`.
|
||||
if let Some(const_arg) = constraint.ct() {
|
||||
if let hir::ConstArgKind::Anon(anon_const) = const_arg.kind {
|
||||
let ty = alias_term
|
||||
.map_bound(|alias| tcx.type_of(alias.def_id).instantiate(tcx, alias.args));
|
||||
let ty = check_assoc_const_binding_type(
|
||||
self,
|
||||
constraint.ident,
|
||||
ty,
|
||||
constraint.hir_id,
|
||||
);
|
||||
tcx.feed_anon_const_type(anon_const.def_id, ty::EarlyBinder::bind(ty));
|
||||
}
|
||||
if let Some(const_arg) = constraint.ct()
|
||||
&& let hir::ConstArgKind::Anon(anon_const) = const_arg.kind
|
||||
{
|
||||
let ty = alias_term
|
||||
.map_bound(|alias| tcx.type_of(alias.def_id).instantiate(tcx, alias.args));
|
||||
let ty =
|
||||
check_assoc_const_binding_type(self, constraint.ident, ty, constraint.hir_id);
|
||||
tcx.feed_anon_const_type(anon_const.def_id, ty::EarlyBinder::bind(ty));
|
||||
}
|
||||
|
||||
alias_term
|
||||
|
|
|
|||
|
|
@ -755,7 +755,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
let limit = if candidates.len() == 5 { 5 } else { 4 };
|
||||
|
||||
for (index, &item) in candidates.iter().take(limit).enumerate() {
|
||||
let impl_ = tcx.impl_of_method(item).unwrap();
|
||||
let impl_ = tcx.impl_of_assoc(item).unwrap();
|
||||
|
||||
let note_span = if item.is_local() {
|
||||
Some(tcx.def_span(item))
|
||||
|
|
|
|||
|
|
@ -103,16 +103,15 @@ pub(super) fn diagnostic_hir_wf_check<'tcx>(
|
|||
// over less-specific types (e.g. `Option<MyStruct<u8>>`)
|
||||
if self.depth >= self.cause_depth {
|
||||
self.cause = Some(error.obligation.cause);
|
||||
if let hir::TyKind::TraitObject(..) = ty.kind {
|
||||
if let DefKind::AssocTy | DefKind::AssocConst | DefKind::AssocFn =
|
||||
if let hir::TyKind::TraitObject(..) = ty.kind
|
||||
&& let DefKind::AssocTy | DefKind::AssocConst | DefKind::AssocFn =
|
||||
self.tcx.def_kind(self.def_id)
|
||||
{
|
||||
self.cause = Some(ObligationCause::new(
|
||||
ty.span,
|
||||
self.def_id,
|
||||
ObligationCauseCode::DynCompatible(ty.span),
|
||||
));
|
||||
}
|
||||
{
|
||||
self.cause = Some(ObligationCause::new(
|
||||
ty.span,
|
||||
self.def_id,
|
||||
ObligationCauseCode::DynCompatible(ty.span),
|
||||
));
|
||||
}
|
||||
self.cause_depth = self.depth
|
||||
}
|
||||
|
|
|
|||
|
|
@ -694,7 +694,7 @@ impl<'tcx> Visitor<'tcx> for AnnotateUnitFallbackVisitor<'_, 'tcx> {
|
|||
// i.e. changing `Default::default()` to `<() as Default>::default()`.
|
||||
if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind
|
||||
&& let Res::Def(DefKind::AssocFn, def_id) = path.res
|
||||
&& self.fcx.tcx.trait_of_item(def_id).is_some()
|
||||
&& self.fcx.tcx.trait_of_assoc(def_id).is_some()
|
||||
&& let Some(args) = self.fcx.typeck_results.borrow().node_args_opt(expr.hir_id)
|
||||
&& let self_ty = args.type_at(0)
|
||||
&& let Some(vid) = self.fcx.root_vid(self_ty)
|
||||
|
|
|
|||
|
|
@ -2136,10 +2136,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
)
|
||||
};
|
||||
|
||||
if let hir::ExprKind::If(_, _, Some(el)) = expr.kind {
|
||||
if let Some(rslt) = check_in_progress(el) {
|
||||
return rslt;
|
||||
}
|
||||
if let hir::ExprKind::If(_, _, Some(el)) = expr.kind
|
||||
&& let Some(rslt) = check_in_progress(el)
|
||||
{
|
||||
return rslt;
|
||||
}
|
||||
|
||||
if let hir::ExprKind::Match(_, arms, _) = expr.kind {
|
||||
|
|
|
|||
|
|
@ -615,31 +615,31 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
expected: Ty<'tcx>,
|
||||
found: Ty<'tcx>,
|
||||
) -> bool {
|
||||
if let (ty::FnPtr(..), ty::Closure(def_id, _)) = (expected.kind(), found.kind()) {
|
||||
if let Some(upvars) = self.tcx.upvars_mentioned(*def_id) {
|
||||
// Report upto four upvars being captured to reduce the amount error messages
|
||||
// reported back to the user.
|
||||
let spans_and_labels = upvars
|
||||
.iter()
|
||||
.take(4)
|
||||
.map(|(var_hir_id, upvar)| {
|
||||
let var_name = self.tcx.hir_name(*var_hir_id).to_string();
|
||||
let msg = format!("`{var_name}` captured here");
|
||||
(upvar.span, msg)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
if let (ty::FnPtr(..), ty::Closure(def_id, _)) = (expected.kind(), found.kind())
|
||||
&& let Some(upvars) = self.tcx.upvars_mentioned(*def_id)
|
||||
{
|
||||
// Report upto four upvars being captured to reduce the amount error messages
|
||||
// reported back to the user.
|
||||
let spans_and_labels = upvars
|
||||
.iter()
|
||||
.take(4)
|
||||
.map(|(var_hir_id, upvar)| {
|
||||
let var_name = self.tcx.hir_name(*var_hir_id).to_string();
|
||||
let msg = format!("`{var_name}` captured here");
|
||||
(upvar.span, msg)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let mut multi_span: MultiSpan =
|
||||
spans_and_labels.iter().map(|(sp, _)| *sp).collect::<Vec<_>>().into();
|
||||
for (sp, label) in spans_and_labels {
|
||||
multi_span.push_span_label(sp, label);
|
||||
}
|
||||
err.span_note(
|
||||
multi_span,
|
||||
"closures can only be coerced to `fn` types if they do not capture any variables"
|
||||
);
|
||||
return true;
|
||||
let mut multi_span: MultiSpan =
|
||||
spans_and_labels.iter().map(|(sp, _)| *sp).collect::<Vec<_>>().into();
|
||||
for (sp, label) in spans_and_labels {
|
||||
multi_span.push_span_label(sp, label);
|
||||
}
|
||||
err.span_note(
|
||||
multi_span,
|
||||
"closures can only be coerced to `fn` types if they do not capture any variables",
|
||||
);
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
|
@ -1302,7 +1302,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
None => ".clone()".to_string(),
|
||||
};
|
||||
|
||||
let span = expr.span.find_oldest_ancestor_in_same_ctxt().shrink_to_hi();
|
||||
let span = expr.span.find_ancestor_not_from_macro().unwrap_or(expr.span).shrink_to_hi();
|
||||
|
||||
diag.span_suggestion_verbose(
|
||||
span,
|
||||
|
|
@ -1395,7 +1395,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
.macro_backtrace()
|
||||
.any(|x| matches!(x.kind, ExpnKind::Macro(MacroKind::Attr | MacroKind::Derive, ..)))
|
||||
{
|
||||
let span = expr.span.find_oldest_ancestor_in_same_ctxt();
|
||||
let span = expr
|
||||
.span
|
||||
.find_ancestor_not_from_extern_macro(&self.tcx.sess.source_map())
|
||||
.unwrap_or(expr.span);
|
||||
|
||||
let mut sugg = if self.precedence(expr) >= ExprPrecedence::Unambiguous {
|
||||
vec![(span.shrink_to_hi(), ".into()".to_owned())]
|
||||
|
|
@ -2062,7 +2065,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
None => sugg.to_string(),
|
||||
};
|
||||
|
||||
let span = expr.span.find_oldest_ancestor_in_same_ctxt();
|
||||
let span = expr
|
||||
.span
|
||||
.find_ancestor_not_from_extern_macro(&self.tcx.sess.source_map())
|
||||
.unwrap_or(expr.span);
|
||||
err.span_suggestion_verbose(span.shrink_to_hi(), msg, sugg, Applicability::HasPlaceholders);
|
||||
true
|
||||
}
|
||||
|
|
@ -3008,13 +3014,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
|
||||
/// Returns whether the given expression is an `else if`.
|
||||
fn is_else_if_block(&self, expr: &hir::Expr<'_>) -> bool {
|
||||
if let hir::ExprKind::If(..) = expr.kind {
|
||||
if let Node::Expr(hir::Expr {
|
||||
kind: hir::ExprKind::If(_, _, Some(else_expr)), ..
|
||||
}) = self.tcx.parent_hir_node(expr.hir_id)
|
||||
{
|
||||
return else_expr.hir_id == expr.hir_id;
|
||||
}
|
||||
if let hir::ExprKind::If(..) = expr.kind
|
||||
&& let Node::Expr(hir::Expr { kind: hir::ExprKind::If(_, _, Some(else_expr)), .. }) =
|
||||
self.tcx.parent_hir_node(expr.hir_id)
|
||||
{
|
||||
return else_expr.hir_id == expr.hir_id;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ use rustc_hir_analysis::check::{check_abi, check_custom_abi};
|
|||
use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer;
|
||||
use rustc_infer::traits::{ObligationCauseCode, ObligationInspector, WellFormedLoc};
|
||||
use rustc_middle::query::Providers;
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt};
|
||||
use rustc_middle::{bug, span_bug};
|
||||
use rustc_session::config;
|
||||
use rustc_span::Span;
|
||||
|
|
@ -259,6 +259,21 @@ fn typeck_with_inspect<'tcx>(
|
|||
|
||||
let typeck_results = fcx.resolve_type_vars_in_body(body);
|
||||
|
||||
// Handle potentially region dependent goals, see `InferCtxt::in_hir_typeck`.
|
||||
if let None = fcx.infcx.tainted_by_errors() {
|
||||
for obligation in fcx.take_hir_typeck_potentially_region_dependent_goals() {
|
||||
let obligation = fcx.resolve_vars_if_possible(obligation);
|
||||
if obligation.has_non_region_infer() {
|
||||
bug!("unexpected inference variable after writeback: {obligation:?}");
|
||||
}
|
||||
fcx.register_predicate(obligation);
|
||||
}
|
||||
fcx.select_obligations_where_possible(|_| {});
|
||||
if let None = fcx.infcx.tainted_by_errors() {
|
||||
fcx.report_ambiguity_errors();
|
||||
}
|
||||
}
|
||||
|
||||
fcx.detect_opaque_types_added_during_writeback();
|
||||
|
||||
// Consistency check our TypeckResults instance can hold all ItemLocalIds
|
||||
|
|
|
|||
|
|
@ -669,17 +669,17 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
|
|||
|
||||
fn check_for_illegal_method_calls(&self, pick: &probe::Pick<'_>) {
|
||||
// Disallow calls to the method `drop` defined in the `Drop` trait.
|
||||
if let Some(trait_def_id) = pick.item.trait_container(self.tcx) {
|
||||
if let Err(e) = callee::check_legal_trait_for_method_call(
|
||||
if let Some(trait_def_id) = pick.item.trait_container(self.tcx)
|
||||
&& let Err(e) = callee::check_legal_trait_for_method_call(
|
||||
self.tcx,
|
||||
self.span,
|
||||
Some(self.self_expr.span),
|
||||
self.call_expr.span,
|
||||
trait_def_id,
|
||||
self.body_id.to_def_id(),
|
||||
) {
|
||||
self.set_tainted_by_errors(e);
|
||||
}
|
||||
)
|
||||
{
|
||||
self.set_tainted_by_errors(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -85,8 +85,11 @@ impl<'tcx> TypeckRootCtxt<'tcx> {
|
|||
pub(crate) fn new(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Self {
|
||||
let hir_owner = tcx.local_def_id_to_hir_id(def_id).owner;
|
||||
|
||||
let infcx =
|
||||
tcx.infer_ctxt().ignoring_regions().build(TypingMode::typeck_for_body(tcx, def_id));
|
||||
let infcx = tcx
|
||||
.infer_ctxt()
|
||||
.ignoring_regions()
|
||||
.in_hir_typeck()
|
||||
.build(TypingMode::typeck_for_body(tcx, def_id));
|
||||
let typeck_results = RefCell::new(ty::TypeckResults::new(hir_owner));
|
||||
let fulfillment_cx = RefCell::new(<dyn TraitEngine<'_, _>>::new(&infcx));
|
||||
|
||||
|
|
@ -165,13 +168,12 @@ impl<'tcx> TypeckRootCtxt<'tcx> {
|
|||
|
||||
if let ty::PredicateKind::Clause(ty::ClauseKind::Projection(predicate)) =
|
||||
obligation.predicate.kind().skip_binder()
|
||||
{
|
||||
// If the projection predicate (Foo::Bar == X) has X as a non-TyVid,
|
||||
// we need to make it into one.
|
||||
if let Some(vid) = predicate.term.as_type().and_then(|ty| ty.ty_vid()) {
|
||||
debug!("infer_var_info: {:?}.output = true", vid);
|
||||
infer_var_info.entry(vid).or_default().output = true;
|
||||
}
|
||||
&& let Some(vid) = predicate.term.as_type().and_then(|ty| ty.ty_vid())
|
||||
{
|
||||
debug!("infer_var_info: {:?}.output = true", vid);
|
||||
infer_var_info.entry(vid).or_default().output = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -227,21 +227,19 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
|
|||
self.typeck_results.type_dependent_defs_mut().remove(e.hir_id);
|
||||
self.typeck_results.node_args_mut().remove(e.hir_id);
|
||||
|
||||
if let Some(a) = self.typeck_results.adjustments_mut().get_mut(base.hir_id) {
|
||||
if let Some(a) = self.typeck_results.adjustments_mut().get_mut(base.hir_id)
|
||||
// Discard the need for a mutable borrow
|
||||
|
||||
// Extra adjustment made when indexing causes a drop
|
||||
// of size information - we need to get rid of it
|
||||
// Since this is "after" the other adjustment to be
|
||||
// discarded, we do an extra `pop()`
|
||||
if let Some(Adjustment {
|
||||
&& let Some(Adjustment {
|
||||
kind: Adjust::Pointer(PointerCoercion::Unsize),
|
||||
..
|
||||
}) = a.pop()
|
||||
{
|
||||
// So the borrow discard actually happens here
|
||||
a.pop();
|
||||
}
|
||||
{
|
||||
// So the borrow discard actually happens here
|
||||
a.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -71,6 +71,7 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||
tcx: self.tcx,
|
||||
typing_mode: self.typing_mode,
|
||||
considering_regions: self.considering_regions,
|
||||
in_hir_typeck: self.in_hir_typeck,
|
||||
skip_leak_check: self.skip_leak_check,
|
||||
inner: self.inner.clone(),
|
||||
lexical_region_resolutions: self.lexical_region_resolutions.clone(),
|
||||
|
|
@ -95,6 +96,7 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||
tcx: self.tcx,
|
||||
typing_mode,
|
||||
considering_regions: self.considering_regions,
|
||||
in_hir_typeck: self.in_hir_typeck,
|
||||
skip_leak_check: self.skip_leak_check,
|
||||
inner: self.inner.clone(),
|
||||
lexical_region_resolutions: self.lexical_region_resolutions.clone(),
|
||||
|
|
|
|||
|
|
@ -22,6 +22,10 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> {
|
|||
self.next_trait_solver
|
||||
}
|
||||
|
||||
fn in_hir_typeck(&self) -> bool {
|
||||
self.in_hir_typeck
|
||||
}
|
||||
|
||||
fn typing_mode(&self) -> ty::TypingMode<'tcx> {
|
||||
self.typing_mode()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,10 +37,11 @@ use snapshot::undo_log::InferCtxtUndoLogs;
|
|||
use tracing::{debug, instrument};
|
||||
use type_variable::TypeVariableOrigin;
|
||||
|
||||
use crate::infer::region_constraints::UndoLog;
|
||||
use crate::infer::snapshot::undo_log::UndoLog;
|
||||
use crate::infer::unify_key::{ConstVariableOrigin, ConstVariableValue, ConstVidKey};
|
||||
use crate::traits::{
|
||||
self, ObligationCause, ObligationInspector, PredicateObligations, TraitEngine,
|
||||
self, ObligationCause, ObligationInspector, PredicateObligation, PredicateObligations,
|
||||
TraitEngine,
|
||||
};
|
||||
|
||||
pub mod at;
|
||||
|
|
@ -156,6 +157,12 @@ pub struct InferCtxtInner<'tcx> {
|
|||
/// which may cause types to no longer be considered well-formed.
|
||||
region_assumptions: Vec<ty::ArgOutlivesPredicate<'tcx>>,
|
||||
|
||||
/// `-Znext-solver`: Successfully proven goals during HIR typeck which
|
||||
/// reference inference variables and get reproven after writeback.
|
||||
///
|
||||
/// See the documentation of `InferCtxt::in_hir_typeck` for more details.
|
||||
hir_typeck_potentially_region_dependent_goals: Vec<PredicateObligation<'tcx>>,
|
||||
|
||||
/// Caches for opaque type inference.
|
||||
opaque_type_storage: OpaqueTypeStorage<'tcx>,
|
||||
}
|
||||
|
|
@ -173,6 +180,7 @@ impl<'tcx> InferCtxtInner<'tcx> {
|
|||
region_constraint_storage: Some(Default::default()),
|
||||
region_obligations: Default::default(),
|
||||
region_assumptions: Default::default(),
|
||||
hir_typeck_potentially_region_dependent_goals: Default::default(),
|
||||
opaque_type_storage: Default::default(),
|
||||
}
|
||||
}
|
||||
|
|
@ -244,9 +252,29 @@ pub struct InferCtxt<'tcx> {
|
|||
typing_mode: TypingMode<'tcx>,
|
||||
|
||||
/// Whether this inference context should care about region obligations in
|
||||
/// the root universe. Most notably, this is used during hir typeck as region
|
||||
/// the root universe. Most notably, this is used during HIR typeck as region
|
||||
/// solving is left to borrowck instead.
|
||||
pub considering_regions: bool,
|
||||
/// `-Znext-solver`: Whether this inference context is used by HIR typeck. If so, we
|
||||
/// need to make sure we don't rely on region identity in the trait solver or when
|
||||
/// relating types. This is necessary as borrowck starts by replacing each occurrence of a
|
||||
/// free region with a unique inference variable. If HIR typeck ends up depending on two
|
||||
/// regions being equal we'd get unexpected mismatches between HIR typeck and MIR typeck,
|
||||
/// resulting in an ICE.
|
||||
///
|
||||
/// The trait solver sometimes depends on regions being identical. As a concrete example
|
||||
/// the trait solver ignores other candidates if one candidate exists without any constraints.
|
||||
/// The goal `&'a u32: Equals<&'a u32>` has no constraints right now. If we replace each
|
||||
/// occurrence of `'a` with a unique region the goal now equates these regions. See
|
||||
/// the tests in trait-system-refactor-initiative#27 for concrete examples.
|
||||
///
|
||||
/// We handle this by *uniquifying* region when canonicalizing root goals during HIR typeck.
|
||||
/// This is still insufficient as inference variables may *hide* region variables, so e.g.
|
||||
/// `dyn TwoSuper<?x, ?x>: Super<?x>` may hold but MIR typeck could end up having to prove
|
||||
/// `dyn TwoSuper<&'0 (), &'1 ()>: Super<&'2 ()>` which is now ambiguous. Because of this we
|
||||
/// stash all successfully proven goals which reference inference variables and then reprove
|
||||
/// them after writeback.
|
||||
pub in_hir_typeck: bool,
|
||||
|
||||
/// If set, this flag causes us to skip the 'leak check' during
|
||||
/// higher-ranked subtyping operations. This flag is a temporary one used
|
||||
|
|
@ -506,6 +534,7 @@ pub struct TypeOutlivesConstraint<'tcx> {
|
|||
pub struct InferCtxtBuilder<'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
considering_regions: bool,
|
||||
in_hir_typeck: bool,
|
||||
skip_leak_check: bool,
|
||||
/// Whether we should use the new trait solver in the local inference context,
|
||||
/// which affects things like which solver is used in `predicate_may_hold`.
|
||||
|
|
@ -518,6 +547,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
InferCtxtBuilder {
|
||||
tcx: self,
|
||||
considering_regions: true,
|
||||
in_hir_typeck: false,
|
||||
skip_leak_check: false,
|
||||
next_trait_solver: self.next_trait_solver_globally(),
|
||||
}
|
||||
|
|
@ -535,6 +565,11 @@ impl<'tcx> InferCtxtBuilder<'tcx> {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn in_hir_typeck(mut self) -> Self {
|
||||
self.in_hir_typeck = true;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn skip_leak_check(mut self, skip_leak_check: bool) -> Self {
|
||||
self.skip_leak_check = skip_leak_check;
|
||||
self
|
||||
|
|
@ -568,12 +603,18 @@ impl<'tcx> InferCtxtBuilder<'tcx> {
|
|||
}
|
||||
|
||||
pub fn build(&mut self, typing_mode: TypingMode<'tcx>) -> InferCtxt<'tcx> {
|
||||
let InferCtxtBuilder { tcx, considering_regions, skip_leak_check, next_trait_solver } =
|
||||
*self;
|
||||
let InferCtxtBuilder {
|
||||
tcx,
|
||||
considering_regions,
|
||||
in_hir_typeck,
|
||||
skip_leak_check,
|
||||
next_trait_solver,
|
||||
} = *self;
|
||||
InferCtxt {
|
||||
tcx,
|
||||
typing_mode,
|
||||
considering_regions,
|
||||
in_hir_typeck,
|
||||
skip_leak_check,
|
||||
inner: RefCell::new(InferCtxtInner::new()),
|
||||
lexical_region_resolutions: RefCell::new(None),
|
||||
|
|
@ -978,6 +1019,22 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn push_hir_typeck_potentially_region_dependent_goal(
|
||||
&self,
|
||||
goal: PredicateObligation<'tcx>,
|
||||
) {
|
||||
let mut inner = self.inner.borrow_mut();
|
||||
inner.undo_log.push(UndoLog::PushHirTypeckPotentiallyRegionDependentGoal);
|
||||
inner.hir_typeck_potentially_region_dependent_goals.push(goal);
|
||||
}
|
||||
|
||||
pub fn take_hir_typeck_potentially_region_dependent_goals(
|
||||
&self,
|
||||
) -> Vec<PredicateObligation<'tcx>> {
|
||||
assert!(!self.in_snapshot(), "cannot take goals in a snapshot");
|
||||
std::mem::take(&mut self.inner.borrow_mut().hir_typeck_potentially_region_dependent_goals)
|
||||
}
|
||||
|
||||
pub fn ty_to_string(&self, t: Ty<'tcx>) -> String {
|
||||
self.resolve_vars_if_possible(t).to_string()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -177,6 +177,7 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||
}
|
||||
|
||||
pub fn take_registered_region_assumptions(&self) -> Vec<ty::ArgOutlivesPredicate<'tcx>> {
|
||||
assert!(!self.in_snapshot(), "cannot take registered region assumptions in a snapshot");
|
||||
std::mem::take(&mut self.inner.borrow_mut().region_assumptions)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ pub(crate) enum UndoLog<'tcx> {
|
|||
ProjectionCache(traits::UndoLog<'tcx>),
|
||||
PushTypeOutlivesConstraint,
|
||||
PushRegionAssumption,
|
||||
PushHirTypeckPotentiallyRegionDependentGoal,
|
||||
}
|
||||
|
||||
macro_rules! impl_from {
|
||||
|
|
@ -79,7 +80,12 @@ impl<'tcx> Rollback<UndoLog<'tcx>> for InferCtxtInner<'tcx> {
|
|||
assert_matches!(popped, Some(_), "pushed region constraint but could not pop it");
|
||||
}
|
||||
UndoLog::PushRegionAssumption => {
|
||||
self.region_assumptions.pop();
|
||||
let popped = self.region_assumptions.pop();
|
||||
assert_matches!(popped, Some(_), "pushed region assumption but could not pop it");
|
||||
}
|
||||
UndoLog::PushHirTypeckPotentiallyRegionDependentGoal => {
|
||||
let popped = self.hir_typeck_potentially_region_dependent_goals.pop();
|
||||
assert_matches!(popped, Some(_), "pushed goal but could not pop it");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2446,16 +2446,16 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
|
|||
|
||||
/// Determine if this expression is a "dangerous initialization".
|
||||
fn is_dangerous_init(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<InitKind> {
|
||||
if let hir::ExprKind::Call(path_expr, args) = expr.kind {
|
||||
if let hir::ExprKind::Call(path_expr, args) = expr.kind
|
||||
// Find calls to `mem::{uninitialized,zeroed}` methods.
|
||||
if let hir::ExprKind::Path(ref qpath) = path_expr.kind {
|
||||
let def_id = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id()?;
|
||||
match cx.tcx.get_diagnostic_name(def_id) {
|
||||
Some(sym::mem_zeroed) => return Some(InitKind::Zeroed),
|
||||
Some(sym::mem_uninitialized) => return Some(InitKind::Uninit),
|
||||
Some(sym::transmute) if is_zero(&args[0]) => return Some(InitKind::Zeroed),
|
||||
_ => {}
|
||||
}
|
||||
&& let hir::ExprKind::Path(ref qpath) = path_expr.kind
|
||||
{
|
||||
let def_id = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id()?;
|
||||
match cx.tcx.get_diagnostic_name(def_id) {
|
||||
Some(sym::mem_zeroed) => return Some(InitKind::Zeroed),
|
||||
Some(sym::mem_uninitialized) => return Some(InitKind::Uninit),
|
||||
Some(sym::transmute) if is_zero(&args[0]) => return Some(InitKind::Zeroed),
|
||||
_ => {}
|
||||
}
|
||||
} else if let hir::ExprKind::MethodCall(_, receiver, ..) = expr.kind {
|
||||
// Find problematic calls to `MaybeUninit::assume_init`.
|
||||
|
|
@ -2463,14 +2463,14 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
|
|||
if cx.tcx.is_diagnostic_item(sym::assume_init, def_id) {
|
||||
// This is a call to *some* method named `assume_init`.
|
||||
// See if the `self` parameter is one of the dangerous constructors.
|
||||
if let hir::ExprKind::Call(path_expr, _) = receiver.kind {
|
||||
if let hir::ExprKind::Path(ref qpath) = path_expr.kind {
|
||||
let def_id = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id()?;
|
||||
match cx.tcx.get_diagnostic_name(def_id) {
|
||||
Some(sym::maybe_uninit_zeroed) => return Some(InitKind::Zeroed),
|
||||
Some(sym::maybe_uninit_uninit) => return Some(InitKind::Uninit),
|
||||
_ => {}
|
||||
}
|
||||
if let hir::ExprKind::Call(path_expr, _) = receiver.kind
|
||||
&& let hir::ExprKind::Path(ref qpath) = path_expr.kind
|
||||
{
|
||||
let def_id = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id()?;
|
||||
match cx.tcx.get_diagnostic_name(def_id) {
|
||||
Some(sym::maybe_uninit_zeroed) => return Some(InitKind::Zeroed),
|
||||
Some(sym::maybe_uninit_uninit) => return Some(InitKind::Uninit),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2724,13 +2724,13 @@ impl<'tcx> LateLintPass<'tcx> for DerefNullPtr {
|
|||
}
|
||||
// check for call to `core::ptr::null` or `core::ptr::null_mut`
|
||||
hir::ExprKind::Call(path, _) => {
|
||||
if let hir::ExprKind::Path(ref qpath) = path.kind {
|
||||
if let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id() {
|
||||
return matches!(
|
||||
cx.tcx.get_diagnostic_name(def_id),
|
||||
Some(sym::ptr_null | sym::ptr_null_mut)
|
||||
);
|
||||
}
|
||||
if let hir::ExprKind::Path(ref qpath) = path.kind
|
||||
&& let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id()
|
||||
{
|
||||
return matches!(
|
||||
cx.tcx.get_diagnostic_name(def_id),
|
||||
Some(sym::ptr_null | sym::ptr_null_mut)
|
||||
);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
|
|
|
|||
|
|
@ -411,22 +411,21 @@ declare_lint_pass!(LintPassImpl => [LINT_PASS_IMPL_WITHOUT_MACRO]);
|
|||
|
||||
impl EarlyLintPass for LintPassImpl {
|
||||
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
|
||||
if let ast::ItemKind::Impl(box ast::Impl { of_trait: Some(lint_pass), .. }) = &item.kind {
|
||||
if let Some(last) = lint_pass.path.segments.last() {
|
||||
if last.ident.name == sym::LintPass {
|
||||
let expn_data = lint_pass.path.span.ctxt().outer_expn_data();
|
||||
let call_site = expn_data.call_site;
|
||||
if expn_data.kind != ExpnKind::Macro(MacroKind::Bang, sym::impl_lint_pass)
|
||||
&& call_site.ctxt().outer_expn_data().kind
|
||||
!= ExpnKind::Macro(MacroKind::Bang, sym::declare_lint_pass)
|
||||
{
|
||||
cx.emit_span_lint(
|
||||
LINT_PASS_IMPL_WITHOUT_MACRO,
|
||||
lint_pass.path.span,
|
||||
LintPassByHand,
|
||||
);
|
||||
}
|
||||
}
|
||||
if let ast::ItemKind::Impl(box ast::Impl { of_trait: Some(lint_pass), .. }) = &item.kind
|
||||
&& let Some(last) = lint_pass.path.segments.last()
|
||||
&& last.ident.name == sym::LintPass
|
||||
{
|
||||
let expn_data = lint_pass.path.span.ctxt().outer_expn_data();
|
||||
let call_site = expn_data.call_site;
|
||||
if expn_data.kind != ExpnKind::Macro(MacroKind::Bang, sym::impl_lint_pass)
|
||||
&& call_site.ctxt().outer_expn_data().kind
|
||||
!= ExpnKind::Macro(MacroKind::Bang, sym::declare_lint_pass)
|
||||
{
|
||||
cx.emit_span_lint(
|
||||
LINT_PASS_IMPL_WITHOUT_MACRO,
|
||||
lint_pass.path.span,
|
||||
LintPassByHand,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,56 +43,50 @@ impl<'tcx> LateLintPass<'tcx> for MapUnitFn {
|
|||
return;
|
||||
}
|
||||
|
||||
if let StmtKind::Semi(expr) = stmt.kind {
|
||||
if let ExprKind::MethodCall(path, receiver, args, span) = expr.kind {
|
||||
if path.ident.name.as_str() == "map" {
|
||||
if receiver.span.from_expansion()
|
||||
|| args.iter().any(|e| e.span.from_expansion())
|
||||
|| !is_impl_slice(cx, receiver)
|
||||
|| !is_diagnostic_name(cx, expr.hir_id, "IteratorMap")
|
||||
{
|
||||
return;
|
||||
if let StmtKind::Semi(expr) = stmt.kind
|
||||
&& let ExprKind::MethodCall(path, receiver, args, span) = expr.kind
|
||||
{
|
||||
if path.ident.name.as_str() == "map" {
|
||||
if receiver.span.from_expansion()
|
||||
|| args.iter().any(|e| e.span.from_expansion())
|
||||
|| !is_impl_slice(cx, receiver)
|
||||
|| !is_diagnostic_name(cx, expr.hir_id, "IteratorMap")
|
||||
{
|
||||
return;
|
||||
}
|
||||
let arg_ty = cx.typeck_results().expr_ty(&args[0]);
|
||||
let default_span = args[0].span;
|
||||
if let ty::FnDef(id, _) = arg_ty.kind() {
|
||||
let fn_ty = cx.tcx.fn_sig(id).skip_binder();
|
||||
let ret_ty = fn_ty.output().skip_binder();
|
||||
if is_unit_type(ret_ty) {
|
||||
cx.emit_span_lint(
|
||||
MAP_UNIT_FN,
|
||||
span,
|
||||
MappingToUnit {
|
||||
function_label: cx.tcx.span_of_impl(*id).unwrap_or(default_span),
|
||||
argument_label: args[0].span,
|
||||
map_label: span,
|
||||
suggestion: path.ident.span,
|
||||
replace: "for_each".to_string(),
|
||||
},
|
||||
)
|
||||
}
|
||||
let arg_ty = cx.typeck_results().expr_ty(&args[0]);
|
||||
let default_span = args[0].span;
|
||||
if let ty::FnDef(id, _) = arg_ty.kind() {
|
||||
let fn_ty = cx.tcx.fn_sig(id).skip_binder();
|
||||
let ret_ty = fn_ty.output().skip_binder();
|
||||
if is_unit_type(ret_ty) {
|
||||
cx.emit_span_lint(
|
||||
MAP_UNIT_FN,
|
||||
span,
|
||||
MappingToUnit {
|
||||
function_label: cx
|
||||
.tcx
|
||||
.span_of_impl(*id)
|
||||
.unwrap_or(default_span),
|
||||
argument_label: args[0].span,
|
||||
map_label: span,
|
||||
suggestion: path.ident.span,
|
||||
replace: "for_each".to_string(),
|
||||
},
|
||||
)
|
||||
}
|
||||
} else if let ty::Closure(id, subs) = arg_ty.kind() {
|
||||
let cl_ty = subs.as_closure().sig();
|
||||
let ret_ty = cl_ty.output().skip_binder();
|
||||
if is_unit_type(ret_ty) {
|
||||
cx.emit_span_lint(
|
||||
MAP_UNIT_FN,
|
||||
span,
|
||||
MappingToUnit {
|
||||
function_label: cx
|
||||
.tcx
|
||||
.span_of_impl(*id)
|
||||
.unwrap_or(default_span),
|
||||
argument_label: args[0].span,
|
||||
map_label: span,
|
||||
suggestion: path.ident.span,
|
||||
replace: "for_each".to_string(),
|
||||
},
|
||||
)
|
||||
}
|
||||
} else if let ty::Closure(id, subs) = arg_ty.kind() {
|
||||
let cl_ty = subs.as_closure().sig();
|
||||
let ret_ty = cl_ty.output().skip_binder();
|
||||
if is_unit_type(ret_ty) {
|
||||
cx.emit_span_lint(
|
||||
MAP_UNIT_FN,
|
||||
span,
|
||||
MappingToUnit {
|
||||
function_label: cx.tcx.span_of_impl(*id).unwrap_or(default_span),
|
||||
argument_label: args[0].span,
|
||||
map_label: span,
|
||||
suggestion: path.ident.span,
|
||||
replace: "for_each".to_string(),
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -101,10 +95,10 @@ impl<'tcx> LateLintPass<'tcx> for MapUnitFn {
|
|||
}
|
||||
|
||||
fn is_impl_slice(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) {
|
||||
if let Some(impl_id) = cx.tcx.impl_of_method(method_id) {
|
||||
return cx.tcx.type_of(impl_id).skip_binder().is_slice();
|
||||
}
|
||||
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id)
|
||||
&& let Some(impl_id) = cx.tcx.impl_of_assoc(method_id)
|
||||
{
|
||||
return cx.tcx.type_of(impl_id).skip_binder().is_slice();
|
||||
}
|
||||
false
|
||||
}
|
||||
|
|
@ -114,11 +108,11 @@ fn is_unit_type(ty: Ty<'_>) -> bool {
|
|||
}
|
||||
|
||||
fn is_diagnostic_name(cx: &LateContext<'_>, id: HirId, name: &str) -> bool {
|
||||
if let Some(def_id) = cx.typeck_results().type_dependent_def_id(id) {
|
||||
if let Some(item) = cx.tcx.get_diagnostic_name(def_id) {
|
||||
if item.as_str() == name {
|
||||
return true;
|
||||
}
|
||||
if let Some(def_id) = cx.typeck_results().type_dependent_def_id(id)
|
||||
&& let Some(item) = cx.tcx.get_diagnostic_name(def_id)
|
||||
{
|
||||
if item.as_str() == name {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
|
|
|
|||
|
|
@ -48,38 +48,38 @@ declare_lint_pass!(NonPanicFmt => [NON_FMT_PANICS]);
|
|||
|
||||
impl<'tcx> LateLintPass<'tcx> for NonPanicFmt {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
|
||||
if let hir::ExprKind::Call(f, [arg]) = &expr.kind {
|
||||
if let &ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(f).kind() {
|
||||
let f_diagnostic_name = cx.tcx.get_diagnostic_name(def_id);
|
||||
if let hir::ExprKind::Call(f, [arg]) = &expr.kind
|
||||
&& let &ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(f).kind()
|
||||
{
|
||||
let f_diagnostic_name = cx.tcx.get_diagnostic_name(def_id);
|
||||
|
||||
if cx.tcx.is_lang_item(def_id, LangItem::BeginPanic)
|
||||
|| cx.tcx.is_lang_item(def_id, LangItem::Panic)
|
||||
|| f_diagnostic_name == Some(sym::panic_str_2015)
|
||||
if cx.tcx.is_lang_item(def_id, LangItem::BeginPanic)
|
||||
|| cx.tcx.is_lang_item(def_id, LangItem::Panic)
|
||||
|| f_diagnostic_name == Some(sym::panic_str_2015)
|
||||
{
|
||||
if let Some(id) = f.span.ctxt().outer_expn_data().macro_def_id {
|
||||
if matches!(
|
||||
cx.tcx.get_diagnostic_name(id),
|
||||
Some(sym::core_panic_2015_macro | sym::std_panic_2015_macro)
|
||||
) {
|
||||
check_panic(cx, f, arg);
|
||||
}
|
||||
}
|
||||
} else if f_diagnostic_name == Some(sym::unreachable_display) {
|
||||
if let Some(id) = f.span.ctxt().outer_expn_data().macro_def_id
|
||||
&& cx.tcx.is_diagnostic_item(sym::unreachable_2015_macro, id)
|
||||
{
|
||||
if let Some(id) = f.span.ctxt().outer_expn_data().macro_def_id {
|
||||
if matches!(
|
||||
cx.tcx.get_diagnostic_name(id),
|
||||
Some(sym::core_panic_2015_macro | sym::std_panic_2015_macro)
|
||||
) {
|
||||
check_panic(cx, f, arg);
|
||||
}
|
||||
}
|
||||
} else if f_diagnostic_name == Some(sym::unreachable_display) {
|
||||
if let Some(id) = f.span.ctxt().outer_expn_data().macro_def_id {
|
||||
if cx.tcx.is_diagnostic_item(sym::unreachable_2015_macro, id) {
|
||||
check_panic(
|
||||
cx,
|
||||
f,
|
||||
// This is safe because we checked above that the callee is indeed
|
||||
// unreachable_display
|
||||
match &arg.kind {
|
||||
// Get the borrowed arg not the borrow
|
||||
hir::ExprKind::AddrOf(ast::BorrowKind::Ref, _, arg) => arg,
|
||||
_ => bug!("call to unreachable_display without borrow"),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
check_panic(
|
||||
cx,
|
||||
f,
|
||||
// This is safe because we checked above that the callee is indeed
|
||||
// unreachable_display
|
||||
match &arg.kind {
|
||||
// Get the borrowed arg not the borrow
|
||||
hir::ExprKind::AddrOf(ast::BorrowKind::Ref, _, arg) => arg,
|
||||
_ => bug!("call to unreachable_display without borrow"),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -623,15 +623,15 @@ impl<'tcx> LateLintPass<'tcx> for NonUpperCaseGlobals {
|
|||
..
|
||||
}) = p.kind
|
||||
{
|
||||
if let Res::Def(DefKind::Const, _) = path.res {
|
||||
if let [segment] = path.segments {
|
||||
NonUpperCaseGlobals::check_upper_case(
|
||||
cx,
|
||||
"constant in pattern",
|
||||
None,
|
||||
&segment.ident,
|
||||
);
|
||||
}
|
||||
if let Res::Def(DefKind::Const, _) = path.res
|
||||
&& let [segment] = path.segments
|
||||
{
|
||||
NonUpperCaseGlobals::check_upper_case(
|
||||
cx,
|
||||
"constant in pattern",
|
||||
None,
|
||||
&segment.ident,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ impl<'tcx> LateLintPass<'tcx> for NoopMethodCall {
|
|||
return;
|
||||
};
|
||||
|
||||
let Some(trait_id) = cx.tcx.trait_of_item(did) else { return };
|
||||
let Some(trait_id) = cx.tcx.trait_of_assoc(did) else { return };
|
||||
|
||||
let Some(trait_) = cx.tcx.get_diagnostic_name(trait_id) else { return };
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ impl<'tcx> LateLintPass<'tcx> for PassByValue {
|
|||
fn check_ty(&mut self, cx: &LateContext<'_>, ty: &'tcx hir::Ty<'tcx, AmbigArg>) {
|
||||
match &ty.kind {
|
||||
TyKind::Ref(_, hir::MutTy { ty: inner_ty, mutbl: hir::Mutability::Not }) => {
|
||||
if let Some(impl_did) = cx.tcx.impl_of_method(ty.hir_id.owner.to_def_id()) {
|
||||
if let Some(impl_did) = cx.tcx.impl_of_assoc(ty.hir_id.owner.to_def_id()) {
|
||||
if cx.tcx.impl_trait_ref(impl_did).is_some() {
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1904,7 +1904,7 @@ impl InvalidAtomicOrdering {
|
|||
if let ExprKind::MethodCall(method_path, _, args, _) = &expr.kind
|
||||
&& recognized_names.contains(&method_path.ident.name)
|
||||
&& let Some(m_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id)
|
||||
&& let Some(impl_did) = cx.tcx.impl_of_method(m_def_id)
|
||||
&& let Some(impl_did) = cx.tcx.impl_of_assoc(m_def_id)
|
||||
&& let Some(adt) = cx.tcx.type_of(impl_did).instantiate_identity().ty_adt_def()
|
||||
// skip extension traits, only lint functions from the standard library
|
||||
&& cx.tcx.trait_id_of_impl(impl_did).is_none()
|
||||
|
|
|
|||
|
|
@ -185,7 +185,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
|
|||
let mut op_warned = false;
|
||||
|
||||
if let Some(must_use_op) = must_use_op {
|
||||
let span = expr.span.find_oldest_ancestor_in_same_ctxt();
|
||||
let span = expr.span.find_ancestor_not_from_macro().unwrap_or(expr.span);
|
||||
cx.emit_span_lint(
|
||||
UNUSED_MUST_USE,
|
||||
expr.span,
|
||||
|
|
@ -511,7 +511,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
|
|||
);
|
||||
}
|
||||
MustUsePath::Def(span, def_id, reason) => {
|
||||
let span = span.find_oldest_ancestor_in_same_ctxt();
|
||||
let span = span.find_ancestor_not_from_macro().unwrap_or(*span);
|
||||
cx.emit_span_lint(
|
||||
UNUSED_MUST_USE,
|
||||
span,
|
||||
|
|
@ -562,20 +562,19 @@ declare_lint_pass!(PathStatements => [PATH_STATEMENTS]);
|
|||
|
||||
impl<'tcx> LateLintPass<'tcx> for PathStatements {
|
||||
fn check_stmt(&mut self, cx: &LateContext<'_>, s: &hir::Stmt<'_>) {
|
||||
if let hir::StmtKind::Semi(expr) = s.kind {
|
||||
if let hir::ExprKind::Path(_) = expr.kind {
|
||||
let ty = cx.typeck_results().expr_ty(expr);
|
||||
if ty.needs_drop(cx.tcx, cx.typing_env()) {
|
||||
let sub = if let Ok(snippet) = cx.sess().source_map().span_to_snippet(expr.span)
|
||||
{
|
||||
PathStatementDropSub::Suggestion { span: s.span, snippet }
|
||||
} else {
|
||||
PathStatementDropSub::Help { span: s.span }
|
||||
};
|
||||
cx.emit_span_lint(PATH_STATEMENTS, s.span, PathStatementDrop { sub })
|
||||
if let hir::StmtKind::Semi(expr) = s.kind
|
||||
&& let hir::ExprKind::Path(_) = expr.kind
|
||||
{
|
||||
let ty = cx.typeck_results().expr_ty(expr);
|
||||
if ty.needs_drop(cx.tcx, cx.typing_env()) {
|
||||
let sub = if let Ok(snippet) = cx.sess().source_map().span_to_snippet(expr.span) {
|
||||
PathStatementDropSub::Suggestion { span: s.span, snippet }
|
||||
} else {
|
||||
cx.emit_span_lint(PATH_STATEMENTS, s.span, PathStatementNoEffect);
|
||||
}
|
||||
PathStatementDropSub::Help { span: s.span }
|
||||
};
|
||||
cx.emit_span_lint(PATH_STATEMENTS, s.span, PathStatementDrop { sub })
|
||||
} else {
|
||||
cx.emit_span_lint(PATH_STATEMENTS, s.span, PathStatementNoEffect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1517,21 +1516,19 @@ impl UnusedDelimLint for UnusedBraces {
|
|||
// let _: A<{produces_literal!()}>;
|
||||
// ```
|
||||
// FIXME(const_generics): handle paths when #67075 is fixed.
|
||||
if let [stmt] = inner.stmts.as_slice() {
|
||||
if let ast::StmtKind::Expr(ref expr) = stmt.kind {
|
||||
if !Self::is_expr_delims_necessary(expr, ctx, followed_by_block)
|
||||
&& (ctx != UnusedDelimsCtx::AnonConst
|
||||
|| (matches!(expr.kind, ast::ExprKind::Lit(_))
|
||||
&& !expr.span.from_expansion()))
|
||||
&& ctx != UnusedDelimsCtx::ClosureBody
|
||||
&& !cx.sess().source_map().is_multiline(value.span)
|
||||
&& value.attrs.is_empty()
|
||||
&& !value.span.from_expansion()
|
||||
&& !inner.span.from_expansion()
|
||||
{
|
||||
self.emit_unused_delims_expr(cx, value, ctx, left_pos, right_pos, is_kw)
|
||||
}
|
||||
}
|
||||
if let [stmt] = inner.stmts.as_slice()
|
||||
&& let ast::StmtKind::Expr(ref expr) = stmt.kind
|
||||
&& !Self::is_expr_delims_necessary(expr, ctx, followed_by_block)
|
||||
&& (ctx != UnusedDelimsCtx::AnonConst
|
||||
|| (matches!(expr.kind, ast::ExprKind::Lit(_))
|
||||
&& !expr.span.from_expansion()))
|
||||
&& ctx != UnusedDelimsCtx::ClosureBody
|
||||
&& !cx.sess().source_map().is_multiline(value.span)
|
||||
&& value.attrs.is_empty()
|
||||
&& !value.span.from_expansion()
|
||||
&& !inner.span.from_expansion()
|
||||
{
|
||||
self.emit_unused_delims_expr(cx, value, ctx, left_pos, right_pos, is_kw)
|
||||
}
|
||||
}
|
||||
ast::ExprKind::Let(_, ref expr, _, _) => {
|
||||
|
|
|
|||
|
|
@ -10,8 +10,8 @@ libc = "0.2.73"
|
|||
|
||||
[build-dependencies]
|
||||
# tidy-alphabetical-start
|
||||
# Pinned so `cargo update` bumps don't cause breakage. Please also update the
|
||||
# pinned `cc` in `rustc_codegen_ssa` if you update `cc` here.
|
||||
# `cc` updates often break things, so we pin it here. Cargo enforces "max 1 semver-compat version
|
||||
# per crate", so if you change this, you need to also change it in `rustc_codegen_ssa`.
|
||||
cc = "=1.2.16"
|
||||
# tidy-alphabetical-end
|
||||
|
||||
|
|
|
|||
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