Merge pull request #19331 from lnicola/sync-from-rust

minor: Sync from downstream
This commit is contained in:
Laurențiu Nicola 2025-03-10 09:52:17 +00:00 committed by GitHub
commit 52f39ffe83
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1544 changed files with 21963 additions and 17016 deletions

View File

@ -58,8 +58,8 @@ jobs:
COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
run: |
cd src/ci/citool
cargo test
cargo run calculate-job-matrix >> $GITHUB_OUTPUT
CARGO_INCREMENTAL=0 cargo test
CARGO_INCREMENTAL=0 cargo run calculate-job-matrix >> $GITHUB_OUTPUT
id: jobs
job:
name: ${{ matrix.full_name }}
@ -182,19 +182,12 @@ jobs:
- name: show the current environment
run: src/ci/scripts/dump-environment.sh
# Temporary fix to unblock CI
# Remove the latest Windows SDK for 32-bit Windows MSVC builds.
# See issue https://github.com/rust-lang/rust/issues/137733 for more details.
- name: Remove Windows SDK 10.0.26100.0
shell: powershell
if: ${{ matrix.name == 'i686-msvc-1' || matrix.name == 'i686-msvc-2' || matrix.name == 'dist-i686-msvc' }}
# Pre-build citool before the following step uninstalls rustup
# Build it into the build directory, to avoid modifying sources
- name: build citool
run: |
$kits = (Get-ItemProperty -path 'HKLM:\SOFTWARE\Microsoft\Windows Kits\Installed Roots').KitsRoot10
$sdk_version = "10.0.26100.0"
foreach ($kind in 'Bin', 'Lib', 'Include') {
Remove-Item -Force -Recurse $kits\$kind\$sdk_version -ErrorAction Continue
}
cd src/ci/citool
CARGO_INCREMENTAL=0 CARGO_TARGET_DIR=../../../build/citool cargo build
- name: run the build
# Redirect stderr to stdout to avoid reordering the two streams in the GHA logs.
@ -232,16 +225,22 @@ jobs:
# erroring about invalid credentials instead.
if: github.event_name == 'push' || env.DEPLOY == '1' || env.DEPLOY_ALT == '1'
- name: postprocess metrics into the summary
run: |
if [ -f build/metrics.json ]; then
./build/citool/debug/citool postprocess-metrics build/metrics.json ${GITHUB_STEP_SUMMARY}
elif [ -f obj/build/metrics.json ]; then
./build/citool/debug/citool postprocess-metrics obj/build/metrics.json ${GITHUB_STEP_SUMMARY}
else
echo "No metrics.json found"
fi
- name: upload job metrics to DataDog
if: needs.calculate_matrix.outputs.run_type != 'pr'
env:
DATADOG_SITE: datadoghq.com
DATADOG_API_KEY: ${{ secrets.DATADOG_API_KEY }}
DD_GITHUB_JOB_NAME: ${{ matrix.full_name }}
run: |
cd src/ci
npm ci
python3 scripts/upload-build-metrics.py ../../build/cpu-usage.csv
run: ./build/citool/debug/citool upload-build-metrics build/cpu-usage.csv
# This job isused to tell bors the final status of the build, as there is no practical way to detect
# when a workflow is successful listening to webhooks only in our current bors implementation (homu).

36
.github/workflows/post-merge.yml vendored Normal file
View File

@ -0,0 +1,36 @@
# Workflow that runs after a merge to master, analyses changes in test executions
# and posts the result to the merged PR.
name: Post merge analysis
on:
push:
branches:
- master
jobs:
analysis:
runs-on: ubuntu-24.04
if: github.repository == 'rust-lang/rust'
permissions:
pull-requests: write
steps:
- uses: actions/checkout@v4
- name: Perform analysis and send PR
run: |
# Get closest bors merge commit
PARENT_COMMIT=`git rev-list --author='bors <bors@rust-lang.org>' -n1 --first-parent HEAD^1`
# Find PR for the current commit
HEAD_PR=`gh pr list --search "${{ github.sha }}" --state merged --json number --jq '.[0].number'`
echo "Parent: ${PARENT_COMMIT}"
echo "HEAD: ${{ github.sha }} (#${HEAD_PR})"
cd src/ci/citool
echo "Post-merge analysis result" > output.log
cargo run --release post-merge-analysis ${PARENT_COMMIT} ${{ github.sha }} >> output.log
cat output.log
gh pr comment ${HEAD_PR} -F output.log

1
.gitignore vendored
View File

@ -53,6 +53,7 @@ no_llvm_build
/target
/library/target
/src/bootstrap/target
/src/ci/citool/target
/src/tools/x/target
# Created by `x vendor`
/vendor

View File

@ -292,6 +292,9 @@ James Hinshelwood <jameshinshelwood1@gmail.com> <james.hinshelwood@bigpayme.com>
James Miller <bladeon@gmail.com> <james@aatch.net>
James Perry <james.austin.perry@gmail.com>
James Sanderson <zofrex@gmail.com>
Jana Dönszelmann <jana@donsz.nl>
Jana Dönszelmann <jana@donsz.nl> <jonathan@donsz.nl>
Jana Dönszelmann <jana@donsz.nl> <jonabent@gmail.com>
Jan-Erik Rediger <janerik@fnordig.de> <badboy@archlinux.us>
Jaro Fietz <jaro.fietz@gmx.de>
Jason Fager <jfager@gmail.com>

View File

@ -18,7 +18,7 @@ For submodules, changes need to be made against the repository corresponding the
submodule, and not the main `rust-lang/rust` repository.
For subtrees, prefer sending a PR against the subtree's repository if it does
not need to be made against the main `rust-lang/rust` repostory (e.g. a
not need to be made against the main `rust-lang/rust` repository (e.g. a
rustc-dev-guide change that does not accompany a compiler change).
## About the [rustc-dev-guide]

View File

@ -2526,6 +2526,16 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
[[package]]
name = "os_pipe"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ffd2b0a5634335b135d5728d84c5e0fd726954b87111f7506a61c502280d982"
dependencies = [
"libc",
"windows-sys 0.59.0",
]
[[package]]
name = "overload"
version = "0.1.1"
@ -3050,6 +3060,7 @@ dependencies = [
"gimli 0.31.1",
"libc",
"object 0.36.7",
"os_pipe",
"regex",
"serde_json",
"similar",
@ -3286,6 +3297,7 @@ dependencies = [
"rustc_hir",
"rustc_lexer",
"rustc_macros",
"rustc_middle",
"rustc_serialize",
"rustc_session",
"rustc_span",
@ -3741,7 +3753,7 @@ dependencies = [
"rustc_abi",
"rustc_ast",
"rustc_ast_pretty",
"rustc_attr_parsing",
"rustc_attr_data_structures",
"rustc_hir",
"rustc_span",
]
@ -4009,7 +4021,8 @@ dependencies = [
"rustc_apfloat",
"rustc_arena",
"rustc_ast",
"rustc_attr_parsing",
"rustc_ast_ir",
"rustc_attr_data_structures",
"rustc_data_structures",
"rustc_error_messages",
"rustc_errors",
@ -5260,6 +5273,7 @@ dependencies = [
"serde",
"similar",
"termcolor",
"toml 0.7.8",
"walkdir",
]

View File

@ -63,6 +63,20 @@ exclude = [
"src/tools/x",
]
# These lints are applied to many crates in the workspace. In practice, this is
# all crates under `compiler/`.
#
# NOTE: rustc-specific lints (e.g. `rustc::internal`) aren't supported by
# Cargo. (Support for them is possibly blocked by #44690 (attributes for
# tools).) Those lints are instead specified for `compiler/` crates in
# `src/bootstrap/src/core/builder/cargo.rs`.
[workspace.lints.rust]
# FIXME(edition_2024): Change this to `-Wrust_2024_idioms` when all of the
# individual lints are satisfied.
keyword_idents_2024 = "warn"
unreachable_pub = "warn"
unsafe_op_in_unsafe_fn = "warn"
[profile.release.package.rustc-rayon-core]
# The rustc fork of Rayon has deadlock detection code which intermittently
# causes overflows in the CI (see https://github.com/rust-lang/rust/issues/90227)

View File

@ -210,9 +210,13 @@ itself back on after some time).
### MSVC
MSVC builds of Rust additionally require an installation of Visual Studio 2017
(or later) so `rustc` can use its linker. The simplest way is to get
[Visual Studio], check the "C++ build tools" and "Windows 10 SDK" workload.
MSVC builds of Rust additionally requires an installation of:
- Visual Studio 2022 (or later) build tools so `rustc` can use its linker. Older
Visual Studio versions such as 2019 *may* work but aren't actively tested.
- A recent Windows 10 or 11 SDK.
The simplest way is to get [Visual Studio], check the "C++ build tools".
[Visual Studio]: https://visualstudio.microsoft.com/downloads/

View File

@ -32,3 +32,6 @@ llvm = ['rustc_driver_impl/llvm']
max_level_info = ['rustc_driver_impl/max_level_info']
rustc_randomized_layouts = ['rustc_driver_impl/rustc_randomized_layouts']
# tidy-alphabetical-end
[lints]
workspace = true

View File

@ -31,3 +31,6 @@ nightly = [
]
randomize = ["dep:rand", "dep:rand_xoshiro", "nightly"]
# tidy-alphabetical-end
[lints]
workspace = true

View File

@ -191,6 +191,17 @@ impl StableOrd for ExternAbi {
}
impl ExternAbi {
/// An ABI "like Rust"
///
/// These ABIs are fully controlled by the Rust compiler, which means they
/// - support unwinding with `-Cpanic=unwind`, unlike `extern "C"`
/// - often diverge from the C ABI
/// - are subject to change between compiler versions
pub fn is_rustic_abi(self) -> bool {
use ExternAbi::*;
matches!(self, Rust | RustCall | RustIntrinsic | RustCold)
}
pub fn supports_varargs(self) -> bool {
// * C and Cdecl obviously support varargs.
// * C can be based on Aapcs, SysV64 or Win64, so they must support varargs.

View File

@ -4,6 +4,7 @@ use std::{cmp, iter};
use rustc_hashes::Hash64;
use rustc_index::Idx;
use rustc_index::bit_set::BitMatrix;
use tracing::debug;
use crate::{
@ -12,6 +13,9 @@ use crate::{
Variants, WrappingRange,
};
mod coroutine;
mod simple;
#[cfg(feature = "nightly")]
mod ty;
@ -60,17 +64,28 @@ pub enum LayoutCalculatorError<F> {
/// The fields or variants have irreconcilable reprs
ReprConflict,
/// The length of an SIMD type is zero
ZeroLengthSimdType,
/// The length of an SIMD type exceeds the maximum number of lanes
OversizedSimdType { max_lanes: u64 },
/// An element type of an SIMD type isn't a primitive
NonPrimitiveSimdType(F),
}
impl<F> LayoutCalculatorError<F> {
pub fn without_payload(&self) -> LayoutCalculatorError<()> {
match self {
LayoutCalculatorError::UnexpectedUnsized(_) => {
LayoutCalculatorError::UnexpectedUnsized(())
}
LayoutCalculatorError::SizeOverflow => LayoutCalculatorError::SizeOverflow,
LayoutCalculatorError::EmptyUnion => LayoutCalculatorError::EmptyUnion,
LayoutCalculatorError::ReprConflict => LayoutCalculatorError::ReprConflict,
use LayoutCalculatorError::*;
match *self {
UnexpectedUnsized(_) => UnexpectedUnsized(()),
SizeOverflow => SizeOverflow,
EmptyUnion => EmptyUnion,
ReprConflict => ReprConflict,
ZeroLengthSimdType => ZeroLengthSimdType,
OversizedSimdType { max_lanes } => OversizedSimdType { max_lanes },
NonPrimitiveSimdType(_) => NonPrimitiveSimdType(()),
}
}
@ -78,13 +93,15 @@ impl<F> LayoutCalculatorError<F> {
///
/// Intended for use by rust-analyzer, as neither it nor `rustc_abi` depend on fluent infra.
pub fn fallback_fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use LayoutCalculatorError::*;
f.write_str(match self {
LayoutCalculatorError::UnexpectedUnsized(_) => {
"an unsized type was found where a sized type was expected"
UnexpectedUnsized(_) => "an unsized type was found where a sized type was expected",
SizeOverflow => "size overflow",
EmptyUnion => "type is a union with no fields",
ReprConflict => "type has an invalid repr",
ZeroLengthSimdType | OversizedSimdType { .. } | NonPrimitiveSimdType(_) => {
"invalid simd type definition"
}
LayoutCalculatorError::SizeOverflow => "size overflow",
LayoutCalculatorError::EmptyUnion => "type is a union with no fields",
LayoutCalculatorError::ReprConflict => "type has an invalid repr",
})
}
}
@ -102,41 +119,115 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
Self { cx }
}
pub fn scalar_pair<FieldIdx: Idx, VariantIdx: Idx>(
pub fn array_like<FieldIdx: Idx, VariantIdx: Idx, F>(
&self,
a: Scalar,
b: Scalar,
) -> LayoutData<FieldIdx, VariantIdx> {
let dl = self.cx.data_layout();
let b_align = b.align(dl);
let align = a.align(dl).max(b_align).max(dl.aggregate_align);
let b_offset = a.size(dl).align_to(b_align.abi);
let size = (b_offset + b.size(dl)).align_to(align.abi);
element: &LayoutData<FieldIdx, VariantIdx>,
count_if_sized: Option<u64>, // None for slices
) -> LayoutCalculatorResult<FieldIdx, VariantIdx, F> {
let count = count_if_sized.unwrap_or(0);
let size =
element.size.checked_mul(count, &self.cx).ok_or(LayoutCalculatorError::SizeOverflow)?;
// HACK(nox): We iter on `b` and then `a` because `max_by_key`
// returns the last maximum.
let largest_niche = Niche::from_scalar(dl, b_offset, b)
.into_iter()
.chain(Niche::from_scalar(dl, Size::ZERO, a))
.max_by_key(|niche| niche.available(dl));
let combined_seed = a.size(&self.cx).bytes().wrapping_add(b.size(&self.cx).bytes());
LayoutData {
Ok(LayoutData {
variants: Variants::Single { index: VariantIdx::new(0) },
fields: FieldsShape::Arbitrary {
offsets: [Size::ZERO, b_offset].into(),
memory_index: [0, 1].into(),
},
backend_repr: BackendRepr::ScalarPair(a, b),
largest_niche,
uninhabited: false,
align,
fields: FieldsShape::Array { stride: element.size, count },
backend_repr: BackendRepr::Memory { sized: count_if_sized.is_some() },
largest_niche: element.largest_niche.filter(|_| count != 0),
uninhabited: element.uninhabited && count != 0,
align: element.align,
size,
max_repr_align: None,
unadjusted_abi_align: align.abi,
randomization_seed: Hash64::new(combined_seed),
unadjusted_abi_align: element.align.abi,
randomization_seed: element.randomization_seed.wrapping_add(Hash64::new(count)),
})
}
pub fn simd_type<
FieldIdx: Idx,
VariantIdx: Idx,
F: AsRef<LayoutData<FieldIdx, VariantIdx>> + fmt::Debug,
>(
&self,
element: F,
count: u64,
repr_packed: bool,
) -> LayoutCalculatorResult<FieldIdx, VariantIdx, F> {
let elt = element.as_ref();
if count == 0 {
return Err(LayoutCalculatorError::ZeroLengthSimdType);
} else if count > crate::MAX_SIMD_LANES {
return Err(LayoutCalculatorError::OversizedSimdType {
max_lanes: crate::MAX_SIMD_LANES,
});
}
let BackendRepr::Scalar(e_repr) = elt.backend_repr else {
return Err(LayoutCalculatorError::NonPrimitiveSimdType(element));
};
// Compute the size and alignment of the vector
let dl = self.cx.data_layout();
let size =
elt.size.checked_mul(count, dl).ok_or_else(|| LayoutCalculatorError::SizeOverflow)?;
let (repr, align) = if repr_packed && !count.is_power_of_two() {
// Non-power-of-two vectors have padding up to the next power-of-two.
// If we're a packed repr, remove the padding while keeping the alignment as close
// to a vector as possible.
(
BackendRepr::Memory { sized: true },
AbiAndPrefAlign {
abi: Align::max_aligned_factor(size),
pref: dl.llvmlike_vector_align(size).pref,
},
)
} else {
(BackendRepr::SimdVector { element: e_repr, count }, dl.llvmlike_vector_align(size))
};
let size = size.align_to(align.abi);
Ok(LayoutData {
variants: Variants::Single { index: VariantIdx::new(0) },
fields: FieldsShape::Arbitrary {
offsets: [Size::ZERO].into(),
memory_index: [0].into(),
},
backend_repr: repr,
largest_niche: elt.largest_niche,
uninhabited: false,
size,
align,
max_repr_align: None,
unadjusted_abi_align: elt.align.abi,
randomization_seed: elt.randomization_seed.wrapping_add(Hash64::new(count)),
})
}
/// Compute the layout for a coroutine.
///
/// This uses dedicated code instead of [`Self::layout_of_struct_or_enum`], as coroutine
/// fields may be shared between multiple variants (see the [`coroutine`] module for details).
pub fn coroutine<
'a,
F: Deref<Target = &'a LayoutData<FieldIdx, VariantIdx>> + fmt::Debug + Copy,
VariantIdx: Idx,
FieldIdx: Idx,
LocalIdx: Idx,
>(
&self,
local_layouts: &IndexSlice<LocalIdx, F>,
prefix_layouts: IndexVec<FieldIdx, F>,
variant_fields: &IndexSlice<VariantIdx, IndexVec<FieldIdx, LocalIdx>>,
storage_conflicts: &BitMatrix<LocalIdx, LocalIdx>,
tag_to_layout: impl Fn(Scalar) -> F,
) -> LayoutCalculatorResult<FieldIdx, VariantIdx, F> {
coroutine::layout(
self,
local_layouts,
prefix_layouts,
variant_fields,
storage_conflicts,
tag_to_layout,
)
}
pub fn univariant<
@ -214,25 +305,6 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
layout
}
pub fn layout_of_never_type<FieldIdx: Idx, VariantIdx: Idx>(
&self,
) -> LayoutData<FieldIdx, VariantIdx> {
let dl = self.cx.data_layout();
// This is also used for uninhabited enums, so we use `Variants::Empty`.
LayoutData {
variants: Variants::Empty,
fields: FieldsShape::Primitive,
backend_repr: BackendRepr::Memory { sized: true },
largest_niche: None,
uninhabited: true,
align: dl.i8_align,
size: Size::ZERO,
max_repr_align: None,
unadjusted_abi_align: dl.i8_align.abi,
randomization_seed: Hash64::ZERO,
}
}
pub fn layout_of_struct_or_enum<
'a,
FieldIdx: Idx,
@ -260,7 +332,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
Some(present_first) => present_first,
// Uninhabited because it has no variants, or only absent ones.
None if is_enum => {
return Ok(self.layout_of_never_type());
return Ok(LayoutData::never_type(&self.cx));
}
// If it's a struct, still compute a layout so that we can still compute the
// field offsets.
@ -949,7 +1021,8 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
// Common prim might be uninit.
Scalar::Union { value: prim }
};
let pair = self.scalar_pair::<FieldIdx, VariantIdx>(tag, prim_scalar);
let pair =
LayoutData::<FieldIdx, VariantIdx>::scalar_pair(&self.cx, tag, prim_scalar);
let pair_offsets = match pair.fields {
FieldsShape::Arbitrary { ref offsets, ref memory_index } => {
assert_eq!(memory_index.raw, [0, 1]);
@ -1341,7 +1414,8 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
} else {
((j, b), (i, a))
};
let pair = self.scalar_pair::<FieldIdx, VariantIdx>(a, b);
let pair =
LayoutData::<FieldIdx, VariantIdx>::scalar_pair(&self.cx, a, b);
let pair_offsets = match pair.fields {
FieldsShape::Arbitrary { ref offsets, ref memory_index } => {
assert_eq!(memory_index.raw, [0, 1]);

View File

@ -0,0 +1,320 @@
//! Coroutine layout logic.
//!
//! When laying out coroutines, we divide our saved local fields into two
//! categories: overlap-eligible and overlap-ineligible.
//!
//! Those fields which are ineligible for overlap go in a "prefix" at the
//! beginning of the layout, and always have space reserved for them.
//!
//! Overlap-eligible fields are only assigned to one variant, so we lay
//! those fields out for each variant and put them right after the
//! prefix.
//!
//! Finally, in the layout details, we point to the fields from the
//! variants they are assigned to. It is possible for some fields to be
//! included in multiple variants. No field ever "moves around" in the
//! layout; its offset is always the same.
//!
//! Also included in the layout are the upvars and the discriminant.
//! These are included as fields on the "outer" layout; they are not part
//! of any variant.
use std::iter;
use rustc_index::bit_set::{BitMatrix, DenseBitSet};
use rustc_index::{Idx, IndexSlice, IndexVec};
use tracing::{debug, trace};
use crate::{
BackendRepr, FieldsShape, HasDataLayout, Integer, LayoutData, Primitive, ReprOptions, Scalar,
StructKind, TagEncoding, Variants, WrappingRange,
};
/// Overlap eligibility and variant assignment for each CoroutineSavedLocal.
#[derive(Clone, Debug, PartialEq)]
enum SavedLocalEligibility<VariantIdx, FieldIdx> {
Unassigned,
Assigned(VariantIdx),
Ineligible(Option<FieldIdx>),
}
/// Compute the eligibility and assignment of each local.
fn coroutine_saved_local_eligibility<VariantIdx: Idx, FieldIdx: Idx, LocalIdx: Idx>(
nb_locals: usize,
variant_fields: &IndexSlice<VariantIdx, IndexVec<FieldIdx, LocalIdx>>,
storage_conflicts: &BitMatrix<LocalIdx, LocalIdx>,
) -> (DenseBitSet<LocalIdx>, IndexVec<LocalIdx, SavedLocalEligibility<VariantIdx, FieldIdx>>) {
use SavedLocalEligibility::*;
let mut assignments: IndexVec<LocalIdx, _> = IndexVec::from_elem_n(Unassigned, nb_locals);
// The saved locals not eligible for overlap. These will get
// "promoted" to the prefix of our coroutine.
let mut ineligible_locals = DenseBitSet::new_empty(nb_locals);
// Figure out which of our saved locals are fields in only
// one variant. The rest are deemed ineligible for overlap.
for (variant_index, fields) in variant_fields.iter_enumerated() {
for local in fields {
match assignments[*local] {
Unassigned => {
assignments[*local] = Assigned(variant_index);
}
Assigned(idx) => {
// We've already seen this local at another suspension
// point, so it is no longer a candidate.
trace!(
"removing local {:?} in >1 variant ({:?}, {:?})",
local, variant_index, idx
);
ineligible_locals.insert(*local);
assignments[*local] = Ineligible(None);
}
Ineligible(_) => {}
}
}
}
// Next, check every pair of eligible locals to see if they
// conflict.
for local_a in storage_conflicts.rows() {
let conflicts_a = storage_conflicts.count(local_a);
if ineligible_locals.contains(local_a) {
continue;
}
for local_b in storage_conflicts.iter(local_a) {
// local_a and local_b are storage live at the same time, therefore they
// cannot overlap in the coroutine layout. The only way to guarantee
// this is if they are in the same variant, or one is ineligible
// (which means it is stored in every variant).
if ineligible_locals.contains(local_b) || assignments[local_a] == assignments[local_b] {
continue;
}
// If they conflict, we will choose one to make ineligible.
// This is not always optimal; it's just a greedy heuristic that
// seems to produce good results most of the time.
let conflicts_b = storage_conflicts.count(local_b);
let (remove, other) =
if conflicts_a > conflicts_b { (local_a, local_b) } else { (local_b, local_a) };
ineligible_locals.insert(remove);
assignments[remove] = Ineligible(None);
trace!("removing local {:?} due to conflict with {:?}", remove, other);
}
}
// Count the number of variants in use. If only one of them, then it is
// impossible to overlap any locals in our layout. In this case it's
// always better to make the remaining locals ineligible, so we can
// lay them out with the other locals in the prefix and eliminate
// unnecessary padding bytes.
{
let mut used_variants = DenseBitSet::new_empty(variant_fields.len());
for assignment in &assignments {
if let Assigned(idx) = assignment {
used_variants.insert(*idx);
}
}
if used_variants.count() < 2 {
for assignment in assignments.iter_mut() {
*assignment = Ineligible(None);
}
ineligible_locals.insert_all();
}
}
// Write down the order of our locals that will be promoted to the prefix.
{
for (idx, local) in ineligible_locals.iter().enumerate() {
assignments[local] = Ineligible(Some(FieldIdx::new(idx)));
}
}
debug!("coroutine saved local assignments: {:?}", assignments);
(ineligible_locals, assignments)
}
/// Compute the full coroutine layout.
pub(super) fn layout<
'a,
F: core::ops::Deref<Target = &'a LayoutData<FieldIdx, VariantIdx>> + core::fmt::Debug + Copy,
VariantIdx: Idx,
FieldIdx: Idx,
LocalIdx: Idx,
>(
calc: &super::LayoutCalculator<impl HasDataLayout>,
local_layouts: &IndexSlice<LocalIdx, F>,
mut prefix_layouts: IndexVec<FieldIdx, F>,
variant_fields: &IndexSlice<VariantIdx, IndexVec<FieldIdx, LocalIdx>>,
storage_conflicts: &BitMatrix<LocalIdx, LocalIdx>,
tag_to_layout: impl Fn(Scalar) -> F,
) -> super::LayoutCalculatorResult<FieldIdx, VariantIdx, F> {
use SavedLocalEligibility::*;
let (ineligible_locals, assignments) =
coroutine_saved_local_eligibility(local_layouts.len(), variant_fields, storage_conflicts);
// Build a prefix layout, including "promoting" all ineligible
// locals as part of the prefix. We compute the layout of all of
// these fields at once to get optimal packing.
let tag_index = prefix_layouts.len();
// `variant_fields` already accounts for the reserved variants, so no need to add them.
let max_discr = (variant_fields.len() - 1) as u128;
let discr_int = Integer::fit_unsigned(max_discr);
let tag = Scalar::Initialized {
value: Primitive::Int(discr_int, /* signed = */ false),
valid_range: WrappingRange { start: 0, end: max_discr },
};
let promoted_layouts = ineligible_locals.iter().map(|local| local_layouts[local]);
prefix_layouts.push(tag_to_layout(tag));
prefix_layouts.extend(promoted_layouts);
let prefix =
calc.univariant(&prefix_layouts, &ReprOptions::default(), StructKind::AlwaysSized)?;
let (prefix_size, prefix_align) = (prefix.size, prefix.align);
// Split the prefix layout into the "outer" fields (upvars and
// discriminant) and the "promoted" fields. Promoted fields will
// get included in each variant that requested them in
// CoroutineLayout.
debug!("prefix = {:#?}", prefix);
let (outer_fields, promoted_offsets, promoted_memory_index) = match prefix.fields {
FieldsShape::Arbitrary { mut offsets, memory_index } => {
let mut inverse_memory_index = memory_index.invert_bijective_mapping();
// "a" (`0..b_start`) and "b" (`b_start..`) correspond to
// "outer" and "promoted" fields respectively.
let b_start = FieldIdx::new(tag_index + 1);
let offsets_b = IndexVec::from_raw(offsets.raw.split_off(b_start.index()));
let offsets_a = offsets;
// Disentangle the "a" and "b" components of `inverse_memory_index`
// by preserving the order but keeping only one disjoint "half" each.
// FIXME(eddyb) build a better abstraction for permutations, if possible.
let inverse_memory_index_b: IndexVec<u32, FieldIdx> = inverse_memory_index
.iter()
.filter_map(|&i| i.index().checked_sub(b_start.index()).map(FieldIdx::new))
.collect();
inverse_memory_index.raw.retain(|&i| i.index() < b_start.index());
let inverse_memory_index_a = inverse_memory_index;
// Since `inverse_memory_index_{a,b}` each only refer to their
// respective fields, they can be safely inverted
let memory_index_a = inverse_memory_index_a.invert_bijective_mapping();
let memory_index_b = inverse_memory_index_b.invert_bijective_mapping();
let outer_fields =
FieldsShape::Arbitrary { offsets: offsets_a, memory_index: memory_index_a };
(outer_fields, offsets_b, memory_index_b)
}
_ => unreachable!(),
};
let mut size = prefix.size;
let mut align = prefix.align;
let variants = variant_fields
.iter_enumerated()
.map(|(index, variant_fields)| {
// Only include overlap-eligible fields when we compute our variant layout.
let variant_only_tys = variant_fields
.iter()
.filter(|local| match assignments[**local] {
Unassigned => unreachable!(),
Assigned(v) if v == index => true,
Assigned(_) => unreachable!("assignment does not match variant"),
Ineligible(_) => false,
})
.map(|local| local_layouts[*local]);
let mut variant = calc.univariant(
&variant_only_tys.collect::<IndexVec<_, _>>(),
&ReprOptions::default(),
StructKind::Prefixed(prefix_size, prefix_align.abi),
)?;
variant.variants = Variants::Single { index };
let FieldsShape::Arbitrary { offsets, memory_index } = variant.fields else {
unreachable!();
};
// Now, stitch the promoted and variant-only fields back together in
// the order they are mentioned by our CoroutineLayout.
// Because we only use some subset (that can differ between variants)
// of the promoted fields, we can't just pick those elements of the
// `promoted_memory_index` (as we'd end up with gaps).
// So instead, we build an "inverse memory_index", as if all of the
// promoted fields were being used, but leave the elements not in the
// subset as `invalid_field_idx`, which we can filter out later to
// obtain a valid (bijective) mapping.
let invalid_field_idx = promoted_memory_index.len() + memory_index.len();
let mut combined_inverse_memory_index =
IndexVec::from_elem_n(FieldIdx::new(invalid_field_idx), invalid_field_idx);
let mut offsets_and_memory_index = iter::zip(offsets, memory_index);
let combined_offsets = variant_fields
.iter_enumerated()
.map(|(i, local)| {
let (offset, memory_index) = match assignments[*local] {
Unassigned => unreachable!(),
Assigned(_) => {
let (offset, memory_index) = offsets_and_memory_index.next().unwrap();
(offset, promoted_memory_index.len() as u32 + memory_index)
}
Ineligible(field_idx) => {
let field_idx = field_idx.unwrap();
(promoted_offsets[field_idx], promoted_memory_index[field_idx])
}
};
combined_inverse_memory_index[memory_index] = i;
offset
})
.collect();
// Remove the unused slots and invert the mapping to obtain the
// combined `memory_index` (also see previous comment).
combined_inverse_memory_index.raw.retain(|&i| i.index() != invalid_field_idx);
let combined_memory_index = combined_inverse_memory_index.invert_bijective_mapping();
variant.fields = FieldsShape::Arbitrary {
offsets: combined_offsets,
memory_index: combined_memory_index,
};
size = size.max(variant.size);
align = align.max(variant.align);
Ok(variant)
})
.collect::<Result<IndexVec<VariantIdx, _>, _>>()?;
size = size.align_to(align.abi);
let uninhabited = prefix.uninhabited || variants.iter().all(|v| v.is_uninhabited());
let abi = BackendRepr::Memory { sized: true };
Ok(LayoutData {
variants: Variants::Multiple {
tag,
tag_encoding: TagEncoding::Direct,
tag_field: tag_index,
variants,
},
fields: outer_fields,
backend_repr: abi,
// Suppress niches inside coroutines. If the niche is inside a field that is aliased (due to
// self-referentiality), getting the discriminant can cause aliasing violations.
// `UnsafeCell` blocks niches for the same reason, but we don't yet have `UnsafePinned` that
// would do the same for us here.
// See <https://github.com/rust-lang/rust/issues/63818>, <https://github.com/rust-lang/miri/issues/3780>.
// FIXME: Remove when <https://github.com/rust-lang/rust/issues/125735> is implemented and aliased coroutine fields are wrapped in `UnsafePinned`.
largest_niche: None,
uninhabited,
size,
align,
max_repr_align: None,
unadjusted_abi_align: align.abi,
randomization_seed: Default::default(),
})
}

View File

@ -0,0 +1,148 @@
use std::num::NonZero;
use rustc_hashes::Hash64;
use rustc_index::{Idx, IndexVec};
use crate::{
BackendRepr, FieldsShape, HasDataLayout, LayoutData, Niche, Primitive, Scalar, Size, Variants,
};
/// "Simple" layout constructors that cannot fail.
impl<FieldIdx: Idx, VariantIdx: Idx> LayoutData<FieldIdx, VariantIdx> {
pub fn unit<C: HasDataLayout>(cx: &C, sized: bool) -> Self {
let dl = cx.data_layout();
LayoutData {
variants: Variants::Single { index: VariantIdx::new(0) },
fields: FieldsShape::Arbitrary {
offsets: IndexVec::new(),
memory_index: IndexVec::new(),
},
backend_repr: BackendRepr::Memory { sized },
largest_niche: None,
uninhabited: false,
align: dl.i8_align,
size: Size::ZERO,
max_repr_align: None,
unadjusted_abi_align: dl.i8_align.abi,
randomization_seed: Hash64::new(0),
}
}
pub fn never_type<C: HasDataLayout>(cx: &C) -> Self {
let dl = cx.data_layout();
// This is also used for uninhabited enums, so we use `Variants::Empty`.
LayoutData {
variants: Variants::Empty,
fields: FieldsShape::Primitive,
backend_repr: BackendRepr::Memory { sized: true },
largest_niche: None,
uninhabited: true,
align: dl.i8_align,
size: Size::ZERO,
max_repr_align: None,
unadjusted_abi_align: dl.i8_align.abi,
randomization_seed: Hash64::ZERO,
}
}
pub fn scalar<C: HasDataLayout>(cx: &C, scalar: Scalar) -> Self {
let largest_niche = Niche::from_scalar(cx, Size::ZERO, scalar);
let size = scalar.size(cx);
let align = scalar.align(cx);
let range = scalar.valid_range(cx);
// All primitive types for which we don't have subtype coercions should get a distinct seed,
// so that types wrapping them can use randomization to arrive at distinct layouts.
//
// Some type information is already lost at this point, so as an approximation we derive
// the seed from what remains. For example on 64-bit targets usize and u64 can no longer
// be distinguished.
let randomization_seed = size
.bytes()
.wrapping_add(
match scalar.primitive() {
Primitive::Int(_, true) => 1,
Primitive::Int(_, false) => 2,
Primitive::Float(_) => 3,
Primitive::Pointer(_) => 4,
} << 32,
)
// distinguishes references from pointers
.wrapping_add((range.start as u64).rotate_right(16))
// distinguishes char from u32 and bool from u8
.wrapping_add((range.end as u64).rotate_right(16));
LayoutData {
variants: Variants::Single { index: VariantIdx::new(0) },
fields: FieldsShape::Primitive,
backend_repr: BackendRepr::Scalar(scalar),
largest_niche,
uninhabited: false,
size,
align,
max_repr_align: None,
unadjusted_abi_align: align.abi,
randomization_seed: Hash64::new(randomization_seed),
}
}
pub fn scalar_pair<C: HasDataLayout>(cx: &C, a: Scalar, b: Scalar) -> Self {
let dl = cx.data_layout();
let b_align = b.align(dl);
let align = a.align(dl).max(b_align).max(dl.aggregate_align);
let b_offset = a.size(dl).align_to(b_align.abi);
let size = (b_offset + b.size(dl)).align_to(align.abi);
// HACK(nox): We iter on `b` and then `a` because `max_by_key`
// returns the last maximum.
let largest_niche = Niche::from_scalar(dl, b_offset, b)
.into_iter()
.chain(Niche::from_scalar(dl, Size::ZERO, a))
.max_by_key(|niche| niche.available(dl));
let combined_seed = a.size(dl).bytes().wrapping_add(b.size(dl).bytes());
LayoutData {
variants: Variants::Single { index: VariantIdx::new(0) },
fields: FieldsShape::Arbitrary {
offsets: [Size::ZERO, b_offset].into(),
memory_index: [0, 1].into(),
},
backend_repr: BackendRepr::ScalarPair(a, b),
largest_niche,
uninhabited: false,
align,
size,
max_repr_align: None,
unadjusted_abi_align: align.abi,
randomization_seed: Hash64::new(combined_seed),
}
}
/// Returns a dummy layout for an uninhabited variant.
///
/// Uninhabited variants get pruned as part of the layout calculation,
/// so this can be used after the fact to reconstitute a layout.
pub fn uninhabited_variant<C: HasDataLayout>(cx: &C, index: VariantIdx, fields: usize) -> Self {
let dl = cx.data_layout();
LayoutData {
variants: Variants::Single { index },
fields: match NonZero::new(fields) {
Some(fields) => FieldsShape::Union(fields),
None => FieldsShape::Arbitrary {
offsets: IndexVec::new(),
memory_index: IndexVec::new(),
},
},
backend_repr: BackendRepr::Memory { sized: true },
largest_niche: None,
uninhabited: true,
align: dl.i8_align,
size: Size::ZERO,
max_repr_align: None,
unadjusted_abi_align: dl.i8_align.abi,
randomization_seed: Hash64::ZERO,
}
}
}

View File

@ -150,6 +150,12 @@ impl<'a, Ty> Deref for TyAndLayout<'a, Ty> {
}
}
impl<'a, Ty> AsRef<LayoutData<FieldIdx, VariantIdx>> for TyAndLayout<'a, Ty> {
fn as_ref(&self) -> &LayoutData<FieldIdx, VariantIdx> {
&*self.layout.0.0
}
}
/// Trait that needs to be implemented by the higher-level type representation
/// (e.g. `rustc_middle::ty::Ty`), to provide `rustc_target::abi` functionality.
pub trait TyAbiInterface<'a, C>: Sized + std::fmt::Debug {

View File

@ -5,7 +5,6 @@
#![cfg_attr(feature = "nightly", feature(rustc_attrs))]
#![cfg_attr(feature = "nightly", feature(rustdoc_internals))]
#![cfg_attr(feature = "nightly", feature(step_trait))]
#![warn(unreachable_pub)]
// tidy-alphabetical-end
/*! ABI handling for rustc
@ -205,6 +204,13 @@ impl ReprOptions {
}
}
/// The maximum supported number of lanes in a SIMD vector.
///
/// This value is selected based on backend support:
/// * LLVM does not appear to have a vector width limit.
/// * Cranelift stores the base-2 log of the lane count in a 4 bit integer.
pub const MAX_SIMD_LANES: u64 = 1 << 0xF;
/// Parsed [Data layout](https://llvm.org/docs/LangRef.html#data-layout)
/// for a target, which contains everything needed to compute layouts.
#[derive(Debug, PartialEq, Eq)]
@ -1744,48 +1750,6 @@ impl<FieldIdx: Idx, VariantIdx: Idx> LayoutData<FieldIdx, VariantIdx> {
pub fn is_uninhabited(&self) -> bool {
self.uninhabited
}
pub fn scalar<C: HasDataLayout>(cx: &C, scalar: Scalar) -> Self {
let largest_niche = Niche::from_scalar(cx, Size::ZERO, scalar);
let size = scalar.size(cx);
let align = scalar.align(cx);
let range = scalar.valid_range(cx);
// All primitive types for which we don't have subtype coercions should get a distinct seed,
// so that types wrapping them can use randomization to arrive at distinct layouts.
//
// Some type information is already lost at this point, so as an approximation we derive
// the seed from what remains. For example on 64-bit targets usize and u64 can no longer
// be distinguished.
let randomization_seed = size
.bytes()
.wrapping_add(
match scalar.primitive() {
Primitive::Int(_, true) => 1,
Primitive::Int(_, false) => 2,
Primitive::Float(_) => 3,
Primitive::Pointer(_) => 4,
} << 32,
)
// distinguishes references from pointers
.wrapping_add((range.start as u64).rotate_right(16))
// distinguishes char from u32 and bool from u8
.wrapping_add((range.end as u64).rotate_right(16));
LayoutData {
variants: Variants::Single { index: VariantIdx::new(0) },
fields: FieldsShape::Primitive,
backend_repr: BackendRepr::Scalar(scalar),
largest_niche,
uninhabited: false,
size,
align,
max_repr_align: None,
unadjusted_abi_align: align.abi,
randomization_seed: Hash64::new(randomization_seed),
}
}
}
impl<FieldIdx: Idx, VariantIdx: Idx> fmt::Debug for LayoutData<FieldIdx, VariantIdx>
@ -1812,7 +1776,7 @@ where
f.debug_struct("Layout")
.field("size", size)
.field("align", align)
.field("abi", backend_repr)
.field("backend_repr", backend_repr)
.field("fields", fields)
.field("largest_niche", largest_niche)
.field("uninhabited", uninhabited)

View File

@ -7,3 +7,6 @@ edition = "2024"
# tidy-alphabetical-start
smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
# tidy-alphabetical-end
[lints]
workspace = true

View File

@ -23,7 +23,6 @@
#![feature(maybe_uninit_slice)]
#![feature(rustc_attrs)]
#![feature(rustdoc_internals)]
#![warn(unreachable_pub)]
// tidy-alphabetical-end
use std::alloc::Layout;
@ -93,7 +92,7 @@ impl<T> ArenaChunk<T> {
#[inline]
fn end(&mut self) -> *mut T {
unsafe {
if mem::size_of::<T>() == 0 {
if size_of::<T>() == 0 {
// A pointer as large as possible for zero-sized elements.
ptr::without_provenance_mut(!0)
} else {
@ -151,7 +150,7 @@ impl<T> TypedArena<T> {
}
unsafe {
if mem::size_of::<T>() == 0 {
if size_of::<T>() == 0 {
self.ptr.set(self.ptr.get().wrapping_byte_add(1));
let ptr = ptr::NonNull::<T>::dangling().as_ptr();
// Don't drop the object. This `write` is equivalent to `forget`.
@ -173,13 +172,13 @@ impl<T> TypedArena<T> {
// FIXME: this should *likely* use `offset_from`, but more
// investigation is needed (including running tests in miri).
let available_bytes = self.end.get().addr() - self.ptr.get().addr();
let additional_bytes = additional.checked_mul(mem::size_of::<T>()).unwrap();
let additional_bytes = additional.checked_mul(size_of::<T>()).unwrap();
available_bytes >= additional_bytes
}
#[inline]
fn alloc_raw_slice(&self, len: usize) -> *mut T {
assert!(mem::size_of::<T>() != 0);
assert!(size_of::<T>() != 0);
assert!(len != 0);
// Ensure the current chunk can fit `len` objects.
@ -213,7 +212,7 @@ impl<T> TypedArena<T> {
// So we collect all the elements beforehand, which takes care of reentrancy and panic
// safety. This function is much less hot than `DroplessArena::alloc_from_iter`, so it
// doesn't need to be hyper-optimized.
assert!(mem::size_of::<T>() != 0);
assert!(size_of::<T>() != 0);
let mut vec: SmallVec<[_; 8]> = iter.into_iter().collect();
if vec.is_empty() {
@ -236,7 +235,7 @@ impl<T> TypedArena<T> {
unsafe {
// We need the element size to convert chunk sizes (ranging from
// PAGE to HUGE_PAGE bytes) to element counts.
let elem_size = cmp::max(1, mem::size_of::<T>());
let elem_size = cmp::max(1, size_of::<T>());
let mut chunks = self.chunks.borrow_mut();
let mut new_cap;
if let Some(last_chunk) = chunks.last_mut() {
@ -246,7 +245,7 @@ impl<T> TypedArena<T> {
// FIXME: this should *likely* use `offset_from`, but more
// investigation is needed (including running tests in miri).
let used_bytes = self.ptr.get().addr() - last_chunk.start().addr();
last_chunk.entries = used_bytes / mem::size_of::<T>();
last_chunk.entries = used_bytes / size_of::<T>();
}
// If the previous chunk's len is less than HUGE_PAGE
@ -276,7 +275,7 @@ impl<T> TypedArena<T> {
let end = self.ptr.get().addr();
// We then calculate the number of elements to be dropped in the last chunk,
// which is the filled area's length.
let diff = if mem::size_of::<T>() == 0 {
let diff = if size_of::<T>() == 0 {
// `T` is ZST. It can't have a drop flag, so the value here doesn't matter. We get
// the number of zero-sized values in the last and only chunk, just out of caution.
// Recall that `end` was incremented for each allocated value.
@ -284,7 +283,7 @@ impl<T> TypedArena<T> {
} else {
// FIXME: this should *likely* use `offset_from`, but more
// investigation is needed (including running tests in miri).
(end - start) / mem::size_of::<T>()
(end - start) / size_of::<T>()
};
// Pass that to the `destroy` method.
unsafe {
@ -329,7 +328,7 @@ fn align_up(val: usize, align: usize) -> usize {
// Pointer alignment is common in compiler types, so keep `DroplessArena` aligned to them
// to optimize away alignment code.
const DROPLESS_ALIGNMENT: usize = mem::align_of::<usize>();
const DROPLESS_ALIGNMENT: usize = align_of::<usize>();
/// An arena that can hold objects of multiple different types that impl `Copy`
/// and/or satisfy `!mem::needs_drop`.
@ -447,7 +446,7 @@ impl DroplessArena {
#[inline]
pub fn alloc<T>(&self, object: T) -> &mut T {
assert!(!mem::needs_drop::<T>());
assert!(mem::size_of::<T>() != 0);
assert!(size_of::<T>() != 0);
let mem = self.alloc_raw(Layout::new::<T>()) as *mut T;
@ -471,7 +470,7 @@ impl DroplessArena {
T: Copy,
{
assert!(!mem::needs_drop::<T>());
assert!(mem::size_of::<T>() != 0);
assert!(size_of::<T>() != 0);
assert!(!slice.is_empty());
let mem = self.alloc_raw(Layout::for_value::<[T]>(slice)) as *mut T;
@ -546,7 +545,7 @@ impl DroplessArena {
// Warning: this function is reentrant: `iter` could hold a reference to `&self` and
// allocate additional elements while we're iterating.
let iter = iter.into_iter();
assert!(mem::size_of::<T>() != 0);
assert!(size_of::<T>() != 0);
assert!(!mem::needs_drop::<T>());
let size_hint = iter.size_hint();

View File

@ -18,3 +18,6 @@ smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
thin-vec = "0.2.12"
tracing = "0.1"
# tidy-alphabetical-end
[lints]
workspace = true

View File

@ -124,10 +124,19 @@ impl Path {
self.segments.first().is_some_and(|segment| segment.ident.name == kw::PathRoot)
}
/// If this path is a single identifier with no arguments, does not ensure
/// that the path resolves to a const param, the caller should check this.
pub fn is_potential_trivial_const_arg(&self) -> bool {
matches!(self.segments[..], [PathSegment { args: None, .. }])
/// Check if this path is potentially a trivial const arg, i.e., one that can _potentially_
/// be represented without an anon const in the HIR.
///
/// If `allow_mgca_arg` is true (as should be the case in most situations when
/// `#![feature(min_generic_const_args)]` is enabled), then this always returns true
/// because all paths are valid.
///
/// Otherwise, it returns true iff the path has exactly one segment, and it has no generic args
/// (i.e., it is _potentially_ a const parameter).
#[tracing::instrument(level = "debug", ret)]
pub fn is_potential_trivial_const_arg(&self, allow_mgca_arg: bool) -> bool {
allow_mgca_arg
|| self.segments.len() == 1 && self.segments.iter().all(|seg| seg.args.is_none())
}
}
@ -417,9 +426,11 @@ impl WhereClause {
/// A single predicate in a where-clause.
#[derive(Clone, Encodable, Decodable, Debug)]
pub struct WherePredicate {
pub attrs: AttrVec,
pub kind: WherePredicateKind,
pub id: NodeId,
pub span: Span,
pub is_placeholder: bool,
}
/// Predicate kind in where-clause.
@ -1206,22 +1217,31 @@ pub struct Expr {
}
impl Expr {
/// Could this expr be either `N`, or `{ N }`, where `N` is a const parameter.
/// Check if this expression is potentially a trivial const arg, i.e., one that can _potentially_
/// be represented without an anon const in the HIR.
///
/// If this is not the case, name resolution does not resolve `N` when using
/// `min_const_generics` as more complex expressions are not supported.
/// This will unwrap at most one block level (curly braces). After that, if the expression
/// is a path, it mostly dispatches to [`Path::is_potential_trivial_const_arg`].
/// See there for more info about `allow_mgca_arg`.
///
/// Does not ensure that the path resolves to a const param, the caller should check this.
/// The only additional thing to note is that when `allow_mgca_arg` is false, this function
/// will only allow paths with no qself, before dispatching to the `Path` function of
/// the same name.
///
/// Does not ensure that the path resolves to a const param/item, the caller should check this.
/// This also does not consider macros, so it's only correct after macro-expansion.
pub fn is_potential_trivial_const_arg(&self) -> bool {
pub fn is_potential_trivial_const_arg(&self, allow_mgca_arg: bool) -> bool {
let this = self.maybe_unwrap_block();
if let ExprKind::Path(None, path) = &this.kind
&& path.is_potential_trivial_const_arg()
{
true
if allow_mgca_arg {
matches!(this.kind, ExprKind::Path(..))
} else {
false
if let ExprKind::Path(None, path) = &this.kind
&& path.is_potential_trivial_const_arg(allow_mgca_arg)
{
true
} else {
false
}
}
}
@ -1379,6 +1399,7 @@ impl Expr {
// Never need parens
ExprKind::Array(_)
| ExprKind::Await(..)
| ExprKind::Use(..)
| ExprKind::Block(..)
| ExprKind::Call(..)
| ExprKind::ConstBlock(_)
@ -1568,6 +1589,8 @@ pub enum ExprKind {
Gen(CaptureBy, P<Block>, GenBlockKind, Span),
/// An await expression (`my_future.await`). Span is of await keyword.
Await(P<Expr>, Span),
/// A use expression (`x.use`). Span is of use keyword.
Use(P<Expr>, Span),
/// A try block (`try { ... }`).
TryBlock(P<Block>),
@ -1737,8 +1760,17 @@ pub enum CaptureBy {
/// The span of the `move` keyword.
move_kw: Span,
},
/// `move` keyword was not specified.
/// `move` or `use` keywords were not specified.
Ref,
/// `use |x| y + x`.
///
/// Note that if you have a regular closure like `|| x.use`, this will *not* result
/// in a `Use` capture. Instead, the `ExprUseVisitor` will look at the type
/// of `x` and treat `x.use` as either a copy/clone/move as appropriate.
Use {
/// The span of the `use` keyword.
use_kw: Span,
},
}
/// Closure lifetime binder, `for<'a, 'b>` in `for<'a, 'b> |_: &'a (), _: &'b ()|`.
@ -2621,6 +2653,8 @@ pub enum SelfKind {
Value(Mutability),
/// `&'lt self`, `&'lt mut self`
Region(Option<Lifetime>, Mutability),
/// `&'lt pin const self`, `&'lt pin mut self`
Pinned(Option<Lifetime>, Mutability),
/// `self: TYPE`, `mut self: TYPE`
Explicit(P<Ty>, Mutability),
}
@ -2630,6 +2664,8 @@ impl SelfKind {
match self {
SelfKind::Region(None, mutbl) => mutbl.ref_prefix_str().to_string(),
SelfKind::Region(Some(lt), mutbl) => format!("&{lt} {}", mutbl.prefix_str()),
SelfKind::Pinned(None, mutbl) => format!("&pin {}", mutbl.ptr_str()),
SelfKind::Pinned(Some(lt), mutbl) => format!("&{lt} pin {}", mutbl.ptr_str()),
SelfKind::Value(_) | SelfKind::Explicit(_, _) => {
unreachable!("if we had an explicit self, we wouldn't be here")
}
@ -2646,11 +2682,13 @@ impl Param {
if ident.name == kw::SelfLower {
return match self.ty.kind {
TyKind::ImplicitSelf => Some(respan(self.pat.span, SelfKind::Value(mutbl))),
TyKind::Ref(lt, MutTy { ref ty, mutbl })
| TyKind::PinnedRef(lt, MutTy { ref ty, mutbl })
TyKind::Ref(lt, MutTy { ref ty, mutbl }) if ty.kind.is_implicit_self() => {
Some(respan(self.pat.span, SelfKind::Region(lt, mutbl)))
}
TyKind::PinnedRef(lt, MutTy { ref ty, mutbl })
if ty.kind.is_implicit_self() =>
{
Some(respan(self.pat.span, SelfKind::Region(lt, mutbl)))
Some(respan(self.pat.span, SelfKind::Pinned(lt, mutbl)))
}
_ => Some(respan(
self.pat.span.to(self.ty.span),
@ -2692,6 +2730,15 @@ impl Param {
tokens: None,
}),
),
SelfKind::Pinned(lt, mutbl) => (
mutbl,
P(Ty {
id: DUMMY_NODE_ID,
kind: TyKind::PinnedRef(lt, MutTy { ty: infer_ty, mutbl }),
span,
tokens: None,
}),
),
};
Param {
attrs,

View File

@ -11,7 +11,7 @@ use crate::tokenstream::LazyAttrTokenStream;
use crate::{
Arm, AssocItem, AttrItem, AttrKind, AttrVec, Attribute, Block, Crate, Expr, ExprField,
FieldDef, ForeignItem, GenericParam, Item, NodeId, Param, Pat, PatField, Path, Stmt, StmtKind,
Ty, Variant, Visibility,
Ty, Variant, Visibility, WherePredicate,
};
/// A utility trait to reduce boilerplate.
@ -79,6 +79,7 @@ impl_has_node_id!(
Stmt,
Ty,
Variant,
WherePredicate,
);
impl<T: AstDeref<Target: HasNodeId>> HasNodeId for T {
@ -127,7 +128,16 @@ macro_rules! impl_has_tokens_none {
}
impl_has_tokens!(AssocItem, AttrItem, Block, Expr, ForeignItem, Item, Pat, Path, Ty, Visibility);
impl_has_tokens_none!(Arm, ExprField, FieldDef, GenericParam, Param, PatField, Variant);
impl_has_tokens_none!(
Arm,
ExprField,
FieldDef,
GenericParam,
Param,
PatField,
Variant,
WherePredicate
);
impl<T: AstDeref<Target: HasTokens>> HasTokens for T {
fn tokens(&self) -> Option<&LazyAttrTokenStream> {
@ -279,6 +289,7 @@ impl_has_attrs!(
Param,
PatField,
Variant,
WherePredicate,
);
impl_has_attrs_none!(Attribute, AttrItem, Block, Pat, Path, Ty, Visibility);

View File

@ -19,7 +19,6 @@
#![feature(never_type)]
#![feature(rustdoc_internals)]
#![feature(stmt_expr_attributes)]
#![warn(unreachable_pub)]
// tidy-alphabetical-end
pub mod util {

View File

@ -338,8 +338,11 @@ pub trait MutVisitor: Sized {
walk_where_clause(self, where_clause);
}
fn visit_where_predicate(&mut self, where_predicate: &mut WherePredicate) {
walk_where_predicate(self, where_predicate)
fn flat_map_where_predicate(
&mut self,
where_predicate: WherePredicate,
) -> SmallVec<[WherePredicate; 1]> {
walk_flat_map_where_predicate(self, where_predicate)
}
fn visit_where_predicate_kind(&mut self, kind: &mut WherePredicateKind) {
@ -1097,15 +1100,20 @@ fn walk_ty_alias_where_clauses<T: MutVisitor>(vis: &mut T, tawcs: &mut TyAliasWh
fn walk_where_clause<T: MutVisitor>(vis: &mut T, wc: &mut WhereClause) {
let WhereClause { has_where_token: _, predicates, span } = wc;
visit_thin_vec(predicates, |predicate| vis.visit_where_predicate(predicate));
predicates.flat_map_in_place(|predicate| vis.flat_map_where_predicate(predicate));
vis.visit_span(span);
}
pub fn walk_where_predicate<T: MutVisitor>(vis: &mut T, pred: &mut WherePredicate) {
let WherePredicate { kind, id, span } = pred;
pub fn walk_flat_map_where_predicate<T: MutVisitor>(
vis: &mut T,
mut pred: WherePredicate,
) -> SmallVec<[WherePredicate; 1]> {
let WherePredicate { attrs, kind, id, span, is_placeholder: _ } = &mut pred;
vis.visit_id(id);
visit_attrs(vis, attrs);
vis.visit_where_predicate_kind(kind);
vis.visit_span(span);
smallvec![pred]
}
pub fn walk_where_predicate_kind<T: MutVisitor>(vis: &mut T, kind: &mut WherePredicateKind) {
@ -1737,6 +1745,10 @@ pub fn walk_expr<T: MutVisitor>(vis: &mut T, Expr { kind, id, span, attrs, token
vis.visit_expr(expr);
vis.visit_span(await_kw_span);
}
ExprKind::Use(expr, use_kw_span) => {
vis.visit_expr(expr);
vis.visit_span(use_kw_span);
}
ExprKind::Assign(el, er, span) => {
vis.visit_expr(el);
vis.visit_expr(er);
@ -1887,6 +1899,9 @@ fn walk_capture_by<T: MutVisitor>(vis: &mut T, capture_by: &mut CaptureBy) {
CaptureBy::Value { move_kw } => {
vis.visit_span(move_kw);
}
CaptureBy::Use { use_kw } => {
vis.visit_span(use_kw);
}
}
}

View File

@ -2,7 +2,6 @@ use std::borrow::Cow;
use std::fmt;
use std::sync::Arc;
pub use BinOpToken::*;
pub use LitKind::*;
pub use Nonterminal::*;
pub use NtExprKind::*;
@ -26,21 +25,6 @@ pub enum CommentKind {
Block,
}
#[derive(Clone, PartialEq, Encodable, Decodable, Hash, Debug, Copy)]
#[derive(HashStable_Generic)]
pub enum BinOpToken {
Plus,
Minus,
Star,
Slash,
Percent,
Caret,
And,
Or,
Shl,
Shr,
}
// This type must not implement `Hash` due to the unusual `PartialEq` impl below.
#[derive(Copy, Clone, Debug, Encodable, Decodable, HashStable_Generic)]
pub enum InvisibleOrigin {
@ -376,11 +360,49 @@ pub enum TokenKind {
/// `||`
OrOr,
/// `!`
Not,
Bang,
/// `~`
Tilde,
BinOp(BinOpToken),
BinOpEq(BinOpToken),
// `+`
Plus,
// `-`
Minus,
// `*`
Star,
// `/`
Slash,
// `%`
Percent,
// `^`
Caret,
// `&`
And,
// `|`
Or,
// `<<`
Shl,
// `>>`
Shr,
// `+=`
PlusEq,
// `-=`
MinusEq,
// `*=`
StarEq,
// `/=`
SlashEq,
// `%=`
PercentEq,
// `^=`
CaretEq,
// `&=`
AndEq,
// `|=`
OrEq,
// `<<=`
ShlEq,
// `>>=`
ShrEq,
/* Structural symbols */
/// `@`
@ -500,31 +522,31 @@ impl TokenKind {
Some(match (self, n) {
(Le, 1) => (Lt, Eq),
(EqEq, 1) => (Eq, Eq),
(Ne, 1) => (Not, Eq),
(Ne, 1) => (Bang, Eq),
(Ge, 1) => (Gt, Eq),
(AndAnd, 1) => (BinOp(And), BinOp(And)),
(OrOr, 1) => (BinOp(Or), BinOp(Or)),
(BinOp(Shl), 1) => (Lt, Lt),
(BinOp(Shr), 1) => (Gt, Gt),
(BinOpEq(Plus), 1) => (BinOp(Plus), Eq),
(BinOpEq(Minus), 1) => (BinOp(Minus), Eq),
(BinOpEq(Star), 1) => (BinOp(Star), Eq),
(BinOpEq(Slash), 1) => (BinOp(Slash), Eq),
(BinOpEq(Percent), 1) => (BinOp(Percent), Eq),
(BinOpEq(Caret), 1) => (BinOp(Caret), Eq),
(BinOpEq(And), 1) => (BinOp(And), Eq),
(BinOpEq(Or), 1) => (BinOp(Or), Eq),
(BinOpEq(Shl), 1) => (Lt, Le), // `<` + `<=`
(BinOpEq(Shl), 2) => (BinOp(Shl), Eq), // `<<` + `=`
(BinOpEq(Shr), 1) => (Gt, Ge), // `>` + `>=`
(BinOpEq(Shr), 2) => (BinOp(Shr), Eq), // `>>` + `=`
(AndAnd, 1) => (And, And),
(OrOr, 1) => (Or, Or),
(Shl, 1) => (Lt, Lt),
(Shr, 1) => (Gt, Gt),
(PlusEq, 1) => (Plus, Eq),
(MinusEq, 1) => (Minus, Eq),
(StarEq, 1) => (Star, Eq),
(SlashEq, 1) => (Slash, Eq),
(PercentEq, 1) => (Percent, Eq),
(CaretEq, 1) => (Caret, Eq),
(AndEq, 1) => (And, Eq),
(OrEq, 1) => (Or, Eq),
(ShlEq, 1) => (Lt, Le), // `<` + `<=`
(ShlEq, 2) => (Shl, Eq), // `<<` + `=`
(ShrEq, 1) => (Gt, Ge), // `>` + `>=`
(ShrEq, 2) => (Shr, Eq), // `>>` + `=`
(DotDot, 1) => (Dot, Dot),
(DotDotDot, 1) => (Dot, DotDot), // `.` + `..`
(DotDotDot, 2) => (DotDot, Dot), // `..` + `.`
(DotDotEq, 2) => (DotDot, Eq),
(PathSep, 1) => (Colon, Colon),
(RArrow, 1) => (BinOp(Minus), Gt),
(LArrow, 1) => (Lt, BinOp(Minus)),
(RArrow, 1) => (Minus, Gt),
(LArrow, 1) => (Lt, Minus),
(FatArrow, 1) => (Eq, Gt),
_ => return None,
})
@ -543,7 +565,7 @@ impl TokenKind {
}
pub fn should_end_const_arg(&self) -> bool {
matches!(self, Gt | Ge | BinOp(Shr) | BinOpEq(Shr))
matches!(self, Gt | Ge | Shr | ShrEq)
}
}
@ -582,11 +604,11 @@ impl Token {
pub fn is_punct(&self) -> bool {
match self.kind {
Eq | Lt | Le | EqEq | Ne | Ge | Gt | AndAnd | OrOr | Not | Tilde | BinOp(_)
| BinOpEq(_) | At | Dot | DotDot | DotDotDot | DotDotEq | Comma | Semi | Colon
| PathSep | RArrow | LArrow | FatArrow | Pound | Dollar | Question | SingleQuote => {
true
}
Eq | Lt | Le | EqEq | Ne | Ge | Gt | AndAnd | OrOr | Bang | Tilde | Plus | Minus
| Star | Slash | Percent | Caret | And | Or | Shl | Shr | PlusEq | MinusEq | StarEq
| SlashEq | PercentEq | CaretEq | AndEq | OrEq | ShlEq | ShrEq | At | Dot | DotDot
| DotDotDot | DotDotEq | Comma | Semi | Colon | PathSep | RArrow | LArrow
| FatArrow | Pound | Dollar | Question | SingleQuote => true,
OpenDelim(..) | CloseDelim(..) | Literal(..) | DocComment(..) | Ident(..)
| NtIdent(..) | Lifetime(..) | NtLifetime(..) | Interpolated(..) | Eof => false,
@ -594,7 +616,7 @@ impl Token {
}
pub fn is_like_plus(&self) -> bool {
matches!(self.kind, BinOp(Plus) | BinOpEq(Plus))
matches!(self.kind, Plus | PlusEq)
}
/// Returns `true` if the token can appear at the start of an expression.
@ -608,15 +630,15 @@ impl Token {
ident_can_begin_expr(name, self.span, is_raw), // value name or keyword
OpenDelim(Parenthesis | Brace | Bracket) | // tuple, array or block
Literal(..) | // literal
Not | // operator not
BinOp(Minus) | // unary minus
BinOp(Star) | // dereference
BinOp(Or) | OrOr | // closure
BinOp(And) | // reference
Bang | // operator not
Minus | // unary minus
Star | // dereference
Or | OrOr | // closure
And | // reference
AndAnd | // double reference
// DotDotDot is no longer supported, but we need some way to display the error
DotDot | DotDotDot | DotDotEq | // range notation
Lt | BinOp(Shl) | // associated path
Lt | Shl | // associated path
PathSep | // global path
Lifetime(..) | // labeled loop
Pound => true, // expression attributes
@ -645,17 +667,16 @@ impl Token {
Ident(..) | NtIdent(..) |
OpenDelim(Delimiter::Parenthesis) | // tuple pattern
OpenDelim(Delimiter::Bracket) | // slice pattern
BinOp(And) | // reference
BinOp(Minus) | // negative literal
AndAnd | // double reference
Literal(_) | // literal
DotDot | // range pattern (future compat)
DotDotDot | // range pattern (future compat)
PathSep | // path
Lt | // path (UFCS constant)
BinOp(Shl) => true, // path (double UFCS)
// leading vert `|` or-pattern
BinOp(Or) => matches!(pat_kind, PatWithOr),
And | // reference
Minus | // negative literal
AndAnd | // double reference
Literal(_) | // literal
DotDot | // range pattern (future compat)
DotDotDot | // range pattern (future compat)
PathSep | // path
Lt | // path (UFCS constant)
Shl => true, // path (double UFCS)
Or => matches!(pat_kind, PatWithOr), // leading vert `|` or-pattern
Interpolated(nt) =>
matches!(&**nt,
| NtExpr(..)
@ -676,18 +697,18 @@ impl Token {
/// Returns `true` if the token can appear at the start of a type.
pub fn can_begin_type(&self) -> bool {
match self.uninterpolate().kind {
Ident(name, is_raw) =>
Ident(name, is_raw) =>
ident_can_begin_type(name, self.span, is_raw), // type name or keyword
OpenDelim(Delimiter::Parenthesis) | // tuple
OpenDelim(Delimiter::Bracket) | // array
Not | // never
BinOp(Star) | // raw pointer
BinOp(And) | // reference
AndAnd | // double reference
Question | // maybe bound in trait object
Lifetime(..) | // lifetime bound in trait object
Lt | BinOp(Shl) | // associated path
PathSep => true, // global path
Bang | // never
Star | // raw pointer
And | // reference
AndAnd | // double reference
Question | // maybe bound in trait object
Lifetime(..) | // lifetime bound in trait object
Lt | Shl | // associated path
PathSep => true, // global path
OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(
MetaVarKind::Ty { .. } |
MetaVarKind::Path
@ -701,7 +722,7 @@ impl Token {
/// Returns `true` if the token can appear at the start of a const param.
pub fn can_begin_const_arg(&self) -> bool {
match self.kind {
OpenDelim(Delimiter::Brace) | Literal(..) | BinOp(Minus) => true,
OpenDelim(Delimiter::Brace) | Literal(..) | Minus => true,
Ident(name, IdentIsRaw::No) if name.is_bool_lit() => true,
Interpolated(ref nt) => matches!(&**nt, NtExpr(..) | NtBlock(..) | NtLiteral(..)),
OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(
@ -750,7 +771,7 @@ impl Token {
/// Keep this in sync with and `Lit::from_token`, excluding unary negation.
pub fn can_begin_literal_maybe_minus(&self) -> bool {
match self.uninterpolate().kind {
Literal(..) | BinOp(Minus) => true,
Literal(..) | Minus => true,
Ident(name, IdentIsRaw::No) if name.is_bool_lit() => true,
Interpolated(ref nt) => match &**nt {
NtLiteral(_) => true,
@ -875,7 +896,7 @@ impl Token {
}
pub fn is_qpath_start(&self) -> bool {
self == &Lt || self == &BinOp(Shl)
self == &Lt || self == &Shl
}
pub fn is_path_start(&self) -> bool {
@ -967,59 +988,82 @@ impl Token {
}
pub fn glue(&self, joint: &Token) -> Option<Token> {
let kind = match self.kind {
Eq => match joint.kind {
Eq => EqEq,
Gt => FatArrow,
_ => return None,
},
Lt => match joint.kind {
Eq => Le,
Lt => BinOp(Shl),
Le => BinOpEq(Shl),
BinOp(Minus) => LArrow,
_ => return None,
},
Gt => match joint.kind {
Eq => Ge,
Gt => BinOp(Shr),
Ge => BinOpEq(Shr),
_ => return None,
},
Not => match joint.kind {
Eq => Ne,
_ => return None,
},
BinOp(op) => match joint.kind {
Eq => BinOpEq(op),
BinOp(And) if op == And => AndAnd,
BinOp(Or) if op == Or => OrOr,
Gt if op == Minus => RArrow,
_ => return None,
},
Dot => match joint.kind {
Dot => DotDot,
DotDot => DotDotDot,
_ => return None,
},
DotDot => match joint.kind {
Dot => DotDotDot,
Eq => DotDotEq,
_ => return None,
},
Colon => match joint.kind {
Colon => PathSep,
_ => return None,
},
SingleQuote => match joint.kind {
Ident(name, is_raw) => Lifetime(Symbol::intern(&format!("'{name}")), is_raw),
_ => return None,
},
let kind = match (&self.kind, &joint.kind) {
(Eq, Eq) => EqEq,
(Eq, Gt) => FatArrow,
(Eq, _) => return None,
Le | EqEq | Ne | Ge | AndAnd | OrOr | Tilde | BinOpEq(..) | At | DotDotDot
| DotDotEq | Comma | Semi | PathSep | RArrow | LArrow | FatArrow | Pound | Dollar
| Question | OpenDelim(..) | CloseDelim(..) | Literal(..) | Ident(..) | NtIdent(..)
| Lifetime(..) | NtLifetime(..) | Interpolated(..) | DocComment(..) | Eof => {
(Lt, Eq) => Le,
(Lt, Lt) => Shl,
(Lt, Le) => ShlEq,
(Lt, Minus) => LArrow,
(Lt, _) => return None,
(Gt, Eq) => Ge,
(Gt, Gt) => Shr,
(Gt, Ge) => ShrEq,
(Gt, _) => return None,
(Bang, Eq) => Ne,
(Bang, _) => return None,
(Plus, Eq) => PlusEq,
(Plus, _) => return None,
(Minus, Eq) => MinusEq,
(Minus, Gt) => RArrow,
(Minus, _) => return None,
(Star, Eq) => StarEq,
(Star, _) => return None,
(Slash, Eq) => SlashEq,
(Slash, _) => return None,
(Percent, Eq) => PercentEq,
(Percent, _) => return None,
(Caret, Eq) => CaretEq,
(Caret, _) => return None,
(And, Eq) => AndEq,
(And, And) => AndAnd,
(And, _) => return None,
(Or, Eq) => OrEq,
(Or, Or) => OrOr,
(Or, _) => return None,
(Shl, Eq) => ShlEq,
(Shl, _) => return None,
(Shr, Eq) => ShrEq,
(Shr, _) => return None,
(Dot, Dot) => DotDot,
(Dot, DotDot) => DotDotDot,
(Dot, _) => return None,
(DotDot, Dot) => DotDotDot,
(DotDot, Eq) => DotDotEq,
(DotDot, _) => return None,
(Colon, Colon) => PathSep,
(Colon, _) => return None,
(SingleQuote, Ident(name, is_raw)) => {
Lifetime(Symbol::intern(&format!("'{name}")), *is_raw)
}
(SingleQuote, _) => return None,
(
Le | EqEq | Ne | Ge | AndAnd | OrOr | Tilde | PlusEq | MinusEq | StarEq | SlashEq
| PercentEq | CaretEq | AndEq | OrEq | ShlEq | ShrEq | At | DotDotDot | DotDotEq
| Comma | Semi | PathSep | RArrow | LArrow | FatArrow | Pound | Dollar | Question
| OpenDelim(..) | CloseDelim(..) | Literal(..) | Ident(..) | NtIdent(..)
| Lifetime(..) | NtLifetime(..) | Interpolated(..) | DocComment(..) | Eof,
_,
) => {
return None;
}
};

View File

@ -651,7 +651,7 @@ impl TokenStream {
if attr_style == AttrStyle::Inner {
vec![
TokenTree::token_joint(token::Pound, span),
TokenTree::token_joint_hidden(token::Not, span),
TokenTree::token_joint_hidden(token::Bang, span),
body,
]
} else {

View File

@ -108,6 +108,7 @@ pub fn leading_labeled_expr(mut expr: &ast::Expr) -> bool {
Assign(e, _, _)
| AssignOp(_, e, _)
| Await(e, _)
| Use(e, _)
| Binary(_, e, _)
| Call(e, _)
| Cast(e, _)
@ -224,6 +225,7 @@ pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option<TrailingBrace<'_>> {
| Lit(_)
| Type(_, _)
| Await(_, _)
| Use(_, _)
| Field(_, _)
| Index(_, _, _)
| Underscore

View File

@ -1,7 +1,7 @@
use rustc_span::kw;
use crate::ast::{self, BinOpKind, RangeLimits};
use crate::token::{self, BinOpToken, Token};
use crate::token::{self, Token};
/// Associative operator.
#[derive(Copy, Clone, PartialEq, Debug)]
@ -34,26 +34,26 @@ impl AssocOp {
use AssocOp::*;
match t.kind {
token::Eq => Some(Assign),
token::BinOp(BinOpToken::Plus) => Some(Binary(BinOpKind::Add)),
token::BinOp(BinOpToken::Minus) => Some(Binary(BinOpKind::Sub)),
token::BinOp(BinOpToken::Star) => Some(Binary(BinOpKind::Mul)),
token::BinOp(BinOpToken::Slash) => Some(Binary(BinOpKind::Div)),
token::BinOp(BinOpToken::Percent) => Some(Binary(BinOpKind::Rem)),
token::BinOp(BinOpToken::Caret) => Some(Binary(BinOpKind::BitXor)),
token::BinOp(BinOpToken::And) => Some(Binary(BinOpKind::BitAnd)),
token::BinOp(BinOpToken::Or) => Some(Binary(BinOpKind::BitOr)),
token::BinOp(BinOpToken::Shl) => Some(Binary(BinOpKind::Shl)),
token::BinOp(BinOpToken::Shr) => Some(Binary(BinOpKind::Shr)),
token::BinOpEq(BinOpToken::Plus) => Some(AssignOp(BinOpKind::Add)),
token::BinOpEq(BinOpToken::Minus) => Some(AssignOp(BinOpKind::Sub)),
token::BinOpEq(BinOpToken::Star) => Some(AssignOp(BinOpKind::Mul)),
token::BinOpEq(BinOpToken::Slash) => Some(AssignOp(BinOpKind::Div)),
token::BinOpEq(BinOpToken::Percent) => Some(AssignOp(BinOpKind::Rem)),
token::BinOpEq(BinOpToken::Caret) => Some(AssignOp(BinOpKind::BitXor)),
token::BinOpEq(BinOpToken::And) => Some(AssignOp(BinOpKind::BitAnd)),
token::BinOpEq(BinOpToken::Or) => Some(AssignOp(BinOpKind::BitOr)),
token::BinOpEq(BinOpToken::Shl) => Some(AssignOp(BinOpKind::Shl)),
token::BinOpEq(BinOpToken::Shr) => Some(AssignOp(BinOpKind::Shr)),
token::Plus => Some(Binary(BinOpKind::Add)),
token::Minus => Some(Binary(BinOpKind::Sub)),
token::Star => Some(Binary(BinOpKind::Mul)),
token::Slash => Some(Binary(BinOpKind::Div)),
token::Percent => Some(Binary(BinOpKind::Rem)),
token::Caret => Some(Binary(BinOpKind::BitXor)),
token::And => Some(Binary(BinOpKind::BitAnd)),
token::Or => Some(Binary(BinOpKind::BitOr)),
token::Shl => Some(Binary(BinOpKind::Shl)),
token::Shr => Some(Binary(BinOpKind::Shr)),
token::PlusEq => Some(AssignOp(BinOpKind::Add)),
token::MinusEq => Some(AssignOp(BinOpKind::Sub)),
token::StarEq => Some(AssignOp(BinOpKind::Mul)),
token::SlashEq => Some(AssignOp(BinOpKind::Div)),
token::PercentEq => Some(AssignOp(BinOpKind::Rem)),
token::CaretEq => Some(AssignOp(BinOpKind::BitXor)),
token::AndEq => Some(AssignOp(BinOpKind::BitAnd)),
token::OrEq => Some(AssignOp(BinOpKind::BitOr)),
token::ShlEq => Some(AssignOp(BinOpKind::Shl)),
token::ShrEq => Some(AssignOp(BinOpKind::Shr)),
token::Lt => Some(Binary(BinOpKind::Lt)),
token::Le => Some(Binary(BinOpKind::Le)),
token::Ge => Some(Binary(BinOpKind::Ge)),

View File

@ -833,7 +833,8 @@ pub fn walk_where_predicate<'a, V: Visitor<'a>>(
visitor: &mut V,
predicate: &'a WherePredicate,
) -> V::Result {
let WherePredicate { kind, id: _, span: _ } = predicate;
let WherePredicate { attrs, kind, id: _, span: _, is_placeholder: _ } = predicate;
walk_list!(visitor, visit_attribute, attrs);
visitor.visit_where_predicate_kind(kind)
}
@ -1210,6 +1211,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) -> V
}
ExprKind::Gen(_capt, body, _kind, _decl_span) => try_visit!(visitor.visit_block(body)),
ExprKind::Await(expr, _span) => try_visit!(visitor.visit_expr(expr)),
ExprKind::Use(expr, _span) => try_visit!(visitor.visit_expr(expr)),
ExprKind::Assign(lhs, rhs, _span) => {
try_visit!(visitor.visit_expr(lhs));
try_visit!(visitor.visit_expr(rhs));

View File

@ -19,3 +19,6 @@ nightly = [
"dep:rustc_macros",
"dep:rustc_span",
]
[lints]
workspace = true

View File

@ -9,7 +9,6 @@
#![cfg_attr(feature = "nightly", allow(internal_features))]
#![cfg_attr(feature = "nightly", feature(never_type))]
#![cfg_attr(feature = "nightly", feature(rustc_attrs))]
#![warn(unreachable_pub)]
// tidy-alphabetical-end
#[cfg(feature = "nightly")]

View File

@ -28,3 +28,6 @@ smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
thin-vec = "0.2.12"
tracing = "0.1"
# tidy-alphabetical-end
[lints]
workspace = true

View File

@ -13,7 +13,7 @@ use rustc_middle::span_bug;
use rustc_middle::ty::TyCtxt;
use rustc_session::errors::report_lit_error;
use rustc_span::source_map::{Spanned, respan};
use rustc_span::{DUMMY_SP, DesugaringKind, Ident, Span, Symbol, kw, sym};
use rustc_span::{DUMMY_SP, DesugaringKind, Ident, Span, Symbol, sym};
use thin_vec::{ThinVec, thin_vec};
use visit::{Visitor, walk_expr};
@ -207,6 +207,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
},
),
ExprKind::Await(expr, await_kw_span) => self.lower_expr_await(*await_kw_span, expr),
ExprKind::Use(expr, use_kw_span) => self.lower_expr_use(*use_kw_span, expr),
ExprKind::Closure(box Closure {
binder,
capture_clause,
@ -483,7 +484,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
if legacy_args_idx.contains(&idx) {
let parent_def_id = self.current_hir_id_owner.def_id;
let node_id = self.next_node_id();
self.create_def(parent_def_id, node_id, kw::Empty, DefKind::AnonConst, f.span);
self.create_def(parent_def_id, node_id, None, DefKind::AnonConst, f.span);
let mut visitor = WillCreateDefIdsVisitor {};
let const_value = if let ControlFlow::Break(span) = visitor.visit_expr(&arg) {
AstP(Expr {
@ -1067,6 +1068,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
)
}
fn lower_expr_use(&mut self, use_kw_span: Span, expr: &Expr) -> hir::ExprKind<'hir> {
hir::ExprKind::Use(self.lower_expr(expr), use_kw_span)
}
fn lower_expr_closure(
&mut self,
binder: &ClosureBinder,
@ -1690,6 +1695,19 @@ impl<'hir> LoweringContext<'_, 'hir> {
let yielded =
opt_expr.as_ref().map(|x| self.lower_expr(x)).unwrap_or_else(|| self.expr_unit(span));
if !self.tcx.features().yield_expr()
&& !self.tcx.features().coroutines()
&& !self.tcx.features().gen_blocks()
{
rustc_session::parse::feature_err(
&self.tcx.sess,
sym::yield_expr,
span,
fluent_generated::ast_lowering_yield,
)
.emit();
}
let is_async_gen = match self.coroutine_kind {
Some(hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Gen, _)) => false,
Some(hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::AsyncGen, _)) => true,
@ -1714,28 +1732,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
None,
);
}
Some(hir::CoroutineKind::Coroutine(_)) => {
if !self.tcx.features().coroutines() {
rustc_session::parse::feature_err(
&self.tcx.sess,
sym::coroutines,
span,
fluent_generated::ast_lowering_yield,
)
.emit();
}
false
}
Some(hir::CoroutineKind::Coroutine(_)) => false,
None => {
if !self.tcx.features().coroutines() {
rustc_session::parse::feature_err(
&self.tcx.sess,
sym::coroutines,
span,
fluent_generated::ast_lowering_yield,
)
.emit();
}
let suggestion = self.current_item.map(|s| s.shrink_to_lo());
self.dcx().emit_err(YieldInClosure { span, suggestion });
self.coroutine_kind = Some(hir::CoroutineKind::Coroutine(Movability::Movable));

View File

@ -1728,6 +1728,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
fn lower_where_predicate(&mut self, pred: &WherePredicate) -> hir::WherePredicate<'hir> {
let hir_id = self.lower_node_id(pred.id);
let span = self.lower_span(pred.span);
self.lower_attrs(hir_id, &pred.attrs, span);
let kind = self.arena.alloc(match &pred.kind {
WherePredicateKind::BoundPredicate(WhereBoundPredicate {
bound_generic_params,

View File

@ -38,7 +38,6 @@
#![feature(if_let_guard)]
#![feature(let_chains)]
#![feature(rustdoc_internals)]
#![warn(unreachable_pub)]
// tidy-alphabetical-end
use std::sync::Arc;
@ -136,6 +135,7 @@ struct LoweringContext<'a, 'hir> {
allow_try_trait: Arc<[Symbol]>,
allow_gen_future: Arc<[Symbol]>,
allow_pattern_type: Arc<[Symbol]>,
allow_async_iterator: Arc<[Symbol]>,
allow_for_await: Arc<[Symbol]>,
allow_async_fn_traits: Arc<[Symbol]>,
@ -176,6 +176,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
impl_trait_defs: Vec::new(),
impl_trait_bounds: Vec::new(),
allow_try_trait: [sym::try_trait_v2, sym::yeet_desugar_details].into(),
allow_pattern_type: [sym::pattern_types, sym::pattern_type_range_trait].into(),
allow_gen_future: if tcx.features().async_fn_track_caller() {
[sym::gen_future, sym::closure_track_caller].into()
} else {
@ -492,7 +493,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
&mut self,
parent: LocalDefId,
node_id: ast::NodeId,
name: Symbol,
name: Option<Symbol>,
def_kind: DefKind,
span: Span,
) -> LocalDefId {
@ -772,7 +773,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
let _def_id = self.create_def(
self.current_hir_id_owner.def_id,
param,
kw::UnderscoreLifetime,
Some(kw::UnderscoreLifetime),
DefKind::LifetimeParam,
ident.span,
);
@ -926,7 +927,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
if let Some(first_char) = constraint.ident.as_str().chars().next()
&& first_char.is_ascii_lowercase()
{
tracing::info!(?data, ?data.inputs);
let err = match (&data.inputs[..], &data.output) {
([_, ..], FnRetTy::Default(_)) => {
errors::BadReturnTypeNotation::Inputs { span: data.inputs_span }
@ -1094,7 +1094,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
.and_then(|partial_res| partial_res.full_res())
{
if !res.matches_ns(Namespace::TypeNS)
&& path.is_potential_trivial_const_arg()
&& path.is_potential_trivial_const_arg(false)
{
debug!(
"lower_generic_arg: Lowering type argument as const argument: {:?}",
@ -1365,7 +1365,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
}
}
TyKind::Pat(ty, pat) => {
hir::TyKind::Pat(self.lower_ty(ty, itctx), self.lower_ty_pat(pat))
hir::TyKind::Pat(self.lower_ty(ty, itctx), self.lower_ty_pat(pat, ty.span))
}
TyKind::MacCall(_) => {
span_bug!(t.span, "`TyKind::MacCall` should have been expanded by now")
@ -2061,8 +2061,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
) -> &'hir hir::ConstArg<'hir> {
let tcx = self.tcx;
// FIXME(min_generic_const_args): we only allow one-segment const paths for now
let ct_kind = if path.is_potential_trivial_const_arg()
let ct_kind = if path
.is_potential_trivial_const_arg(tcx.features().min_generic_const_args())
&& (tcx.features().min_generic_const_args()
|| matches!(res, Res::Def(DefKind::ConstParam, _)))
{
@ -2072,7 +2072,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
path,
ParamMode::Optional,
AllowReturnTypeNotation::No,
// FIXME(min_generic_const_args): update for `fn foo() -> Bar<FOO<impl Trait>>` support
// FIXME(mgca): update for `fn foo() -> Bar<FOO<impl Trait>>` support
ImplTraitContext::Disallowed(ImplTraitPosition::Path),
None,
);
@ -2088,8 +2088,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
// We're lowering a const argument that was originally thought to be a type argument,
// so the def collector didn't create the def ahead of time. That's why we have to do
// it here.
let def_id =
self.create_def(parent_def_id, node_id, kw::Empty, DefKind::AnonConst, span);
let def_id = self.create_def(parent_def_id, node_id, None, DefKind::AnonConst, span);
let hir_id = self.lower_node_id(node_id);
let path_expr = Expr {
@ -2136,19 +2135,18 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
};
let maybe_res =
self.resolver.get_partial_res(expr.id).and_then(|partial_res| partial_res.full_res());
// FIXME(min_generic_const_args): we only allow one-segment const paths for now
if let ExprKind::Path(None, path) = &expr.kind
&& path.is_potential_trivial_const_arg()
if let ExprKind::Path(qself, path) = &expr.kind
&& path.is_potential_trivial_const_arg(tcx.features().min_generic_const_args())
&& (tcx.features().min_generic_const_args()
|| matches!(maybe_res, Some(Res::Def(DefKind::ConstParam, _))))
{
let qpath = self.lower_qpath(
expr.id,
&None,
qself,
path,
ParamMode::Optional,
AllowReturnTypeNotation::No,
// FIXME(min_generic_const_args): update for `fn foo() -> Bar<FOO<impl Trait>>` support
// FIXME(mgca): update for `fn foo() -> Bar<FOO<impl Trait>>` support
ImplTraitContext::Disallowed(ImplTraitPosition::Path),
None,
);

View File

@ -3,11 +3,11 @@ use std::sync::Arc;
use rustc_ast::ptr::P;
use rustc_ast::*;
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_hir as hir;
use rustc_hir::def::Res;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::{self as hir, LangItem};
use rustc_middle::span_bug;
use rustc_span::source_map::{Spanned, respan};
use rustc_span::{Ident, Span};
use rustc_span::{DesugaringKind, Ident, Span};
use super::errors::{
ArbitraryExpressionInPattern, ExtraDoubleDot, MisplacedDoubleDot, SubTupleBinding,
@ -430,22 +430,124 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
self.arena.alloc(hir::PatExpr { hir_id: self.lower_node_id(expr.id), span, kind })
}
pub(crate) fn lower_ty_pat(&mut self, pattern: &TyPat) -> &'hir hir::TyPat<'hir> {
self.arena.alloc(self.lower_ty_pat_mut(pattern))
pub(crate) fn lower_ty_pat(
&mut self,
pattern: &TyPat,
base_type: Span,
) -> &'hir hir::TyPat<'hir> {
self.arena.alloc(self.lower_ty_pat_mut(pattern, base_type))
}
fn lower_ty_pat_mut(&mut self, pattern: &TyPat) -> hir::TyPat<'hir> {
fn lower_ty_pat_mut(&mut self, pattern: &TyPat, base_type: Span) -> hir::TyPat<'hir> {
// loop here to avoid recursion
let pat_hir_id = self.lower_node_id(pattern.id);
let node = match &pattern.kind {
TyPatKind::Range(e1, e2, Spanned { node: end, .. }) => hir::TyPatKind::Range(
e1.as_deref().map(|e| self.lower_anon_const_to_const_arg(e)),
e2.as_deref().map(|e| self.lower_anon_const_to_const_arg(e)),
self.lower_range_end(end, e2.is_some()),
TyPatKind::Range(e1, e2, Spanned { node: end, span }) => hir::TyPatKind::Range(
e1.as_deref().map(|e| self.lower_anon_const_to_const_arg(e)).unwrap_or_else(|| {
self.lower_ty_pat_range_end(
hir::LangItem::RangeMin,
span.shrink_to_lo(),
base_type,
)
}),
e2.as_deref()
.map(|e| match end {
RangeEnd::Included(..) => self.lower_anon_const_to_const_arg(e),
RangeEnd::Excluded => self.lower_excluded_range_end(e),
})
.unwrap_or_else(|| {
self.lower_ty_pat_range_end(
hir::LangItem::RangeMax,
span.shrink_to_hi(),
base_type,
)
}),
),
TyPatKind::Err(guar) => hir::TyPatKind::Err(*guar),
};
hir::TyPat { hir_id: pat_hir_id, kind: node, span: self.lower_span(pattern.span) }
}
/// Lowers the range end of an exclusive range (`2..5`) to an inclusive range 2..=(5 - 1).
/// This way the type system doesn't have to handle the distinction between inclusive/exclusive ranges.
fn lower_excluded_range_end(&mut self, e: &AnonConst) -> &'hir hir::ConstArg<'hir> {
let span = self.lower_span(e.value.span);
let unstable_span = self.mark_span_with_reason(
DesugaringKind::PatTyRange,
span,
Some(Arc::clone(&self.allow_pattern_type)),
);
let anon_const = self.with_new_scopes(span, |this| {
let def_id = this.local_def_id(e.id);
let hir_id = this.lower_node_id(e.id);
let body = this.lower_body(|this| {
// Need to use a custom function as we can't just subtract `1` from a `char`.
let kind = hir::ExprKind::Path(this.make_lang_item_qpath(
hir::LangItem::RangeSub,
unstable_span,
None,
));
let fn_def = this.arena.alloc(hir::Expr { hir_id: this.next_id(), kind, span });
let args = this.arena.alloc([this.lower_expr_mut(&e.value)]);
(
&[],
hir::Expr {
hir_id: this.next_id(),
kind: hir::ExprKind::Call(fn_def, args),
span,
},
)
});
hir::AnonConst { def_id, hir_id, body, span }
});
self.arena.alloc(hir::ConstArg {
hir_id: self.next_id(),
kind: hir::ConstArgKind::Anon(self.arena.alloc(anon_const)),
})
}
/// When a range has no end specified (`1..` or `1..=`) or no start specified (`..5` or `..=5`),
/// we instead use a constant of the MAX/MIN of the type.
/// This way the type system does not have to handle the lack of a start/end.
fn lower_ty_pat_range_end(
&mut self,
lang_item: LangItem,
span: Span,
base_type: Span,
) -> &'hir hir::ConstArg<'hir> {
let parent_def_id = self.current_hir_id_owner.def_id;
let node_id = self.next_node_id();
// Add a definition for the in-band const def.
// We're generating a range end that didn't exist in the AST,
// so the def collector didn't create the def ahead of time. That's why we have to do
// it here.
let def_id = self.create_def(parent_def_id, node_id, None, DefKind::AnonConst, span);
let hir_id = self.lower_node_id(node_id);
let unstable_span = self.mark_span_with_reason(
DesugaringKind::PatTyRange,
self.lower_span(span),
Some(Arc::clone(&self.allow_pattern_type)),
);
let span = self.lower_span(base_type);
let path_expr = hir::Expr {
hir_id: self.next_id(),
kind: hir::ExprKind::Path(self.make_lang_item_qpath(lang_item, unstable_span, None)),
span,
};
let ct = self.with_new_scopes(span, |this| {
self.arena.alloc(hir::AnonConst {
def_id,
hir_id,
body: this.lower_body(|_this| (&[], path_expr)),
span,
})
});
let hir_id = self.next_id();
self.arena.alloc(hir::ConstArg { kind: hir::ConstArgKind::Anon(ct), hir_id })
}
}

View File

@ -268,7 +268,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
}
GenericArgs::Parenthesized(data) => match generic_args_mode {
GenericArgsMode::ReturnTypeNotation => {
tracing::info!(?data, ?data.inputs);
let err = match (&data.inputs[..], &data.output) {
([_, ..], FnRetTy::Default(_)) => {
BadReturnTypeNotation::Inputs { span: data.inputs_span }

View File

@ -20,3 +20,6 @@ rustc_session = { path = "../rustc_session" }
rustc_span = { path = "../rustc_span" }
thin-vec = "0.2.12"
# tidy-alphabetical-end
[lints]
workspace = true

View File

@ -489,6 +489,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
gate_all!(dyn_star, "`dyn*` trait objects are experimental");
gate_all!(const_closures, "const closures are experimental");
gate_all!(builtin_syntax, "`builtin #` syntax is unstable");
gate_all!(ergonomic_clones, "ergonomic clones are experimental");
gate_all!(explicit_tail_calls, "`become` expression is experimental");
gate_all!(generic_const_items, "generic const items are experimental");
gate_all!(guard_patterns, "guard patterns are experimental", "consider using match arm guards");
@ -503,6 +504,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
gate_all!(unsafe_binders, "unsafe binder types are experimental");
gate_all!(contracts, "contracts are incomplete");
gate_all!(contracts_internals, "contract internal machinery is for internal use only");
gate_all!(where_clause_attrs, "attributes in `where` clause are unstable");
if !visitor.features.never_patterns() {
if let Some(spans) = spans.get(&sym::never_patterns) {

View File

@ -10,7 +10,6 @@
#![feature(iter_is_partitioned)]
#![feature(let_chains)]
#![feature(rustdoc_internals)]
#![warn(unreachable_pub)]
// tidy-alphabetical-end
pub mod ast_validation;

View File

@ -12,3 +12,6 @@ rustc_lexer = { path = "../rustc_lexer" }
rustc_span = { path = "../rustc_span" }
thin-vec = "0.2.12"
# tidy-alphabetical-end
[lints]
workspace = true

View File

@ -3,7 +3,6 @@
#![doc(rust_logo)]
#![feature(box_patterns)]
#![feature(rustdoc_internals)]
#![warn(unreachable_pub)]
// tidy-alphabetical-end
mod helpers;

View File

@ -11,9 +11,7 @@ use std::sync::Arc;
use rustc_ast::attr::AttrIdGenerator;
use rustc_ast::ptr::P;
use rustc_ast::token::{
self, BinOpToken, CommentKind, Delimiter, IdentIsRaw, Nonterminal, Token, TokenKind,
};
use rustc_ast::token::{self, CommentKind, Delimiter, IdentIsRaw, Nonterminal, Token, TokenKind};
use rustc_ast::tokenstream::{Spacing, TokenStream, TokenTree};
use rustc_ast::util::classify;
use rustc_ast::util::comments::{Comment, CommentStyle};
@ -319,7 +317,7 @@ fn space_between(tt1: &TokenTree, tt2: &TokenTree) -> bool {
(tt1, Tok(Token { kind: Comma | Semi | Dot, .. }, _)) if !is_punct(tt1) => false,
// IDENT + `!`: `println!()`, but `if !x { ... }` needs a space after the `if`
(Tok(Token { kind: Ident(sym, is_raw), span }, _), Tok(Token { kind: Not, .. }, _))
(Tok(Token { kind: Ident(sym, is_raw), span }, _), Tok(Token { kind: Bang, .. }, _))
if !Ident::new(*sym, *span).is_reserved() || matches!(is_raw, IdentIsRaw::Yes) =>
{
false
@ -344,21 +342,6 @@ fn space_between(tt1: &TokenTree, tt2: &TokenTree) -> bool {
}
}
fn binop_to_string(op: BinOpToken) -> &'static str {
match op {
token::Plus => "+",
token::Minus => "-",
token::Star => "*",
token::Slash => "/",
token::Percent => "%",
token::Caret => "^",
token::And => "&",
token::Or => "|",
token::Shl => "<<",
token::Shr => ">>",
}
}
pub fn doc_comment_to_string(
comment_kind: CommentKind,
attr_style: ast::AttrStyle,
@ -913,12 +896,30 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
token::Ne => "!=".into(),
token::Ge => ">=".into(),
token::Gt => ">".into(),
token::Not => "!".into(),
token::Bang => "!".into(),
token::Tilde => "~".into(),
token::OrOr => "||".into(),
token::AndAnd => "&&".into(),
token::BinOp(op) => binop_to_string(op).into(),
token::BinOpEq(op) => format!("{}=", binop_to_string(op)).into(),
token::Plus => "+".into(),
token::Minus => "-".into(),
token::Star => "*".into(),
token::Slash => "/".into(),
token::Percent => "%".into(),
token::Caret => "^".into(),
token::And => "&".into(),
token::Or => "|".into(),
token::Shl => "<<".into(),
token::Shr => ">>".into(),
token::PlusEq => "+=".into(),
token::MinusEq => "-=".into(),
token::StarEq => "*=".into(),
token::SlashEq => "/=".into(),
token::PercentEq => "%=".into(),
token::CaretEq => "^=".into(),
token::AndEq => "&=".into(),
token::OrEq => "|=".into(),
token::ShlEq => "<<=".into(),
token::ShrEq => ">>=".into(),
/* Structural symbols */
token::At => "@".into(),
@ -1782,6 +1783,13 @@ impl<'a> State<'a> {
self.print_mutability(*m, false);
self.word("self")
}
SelfKind::Pinned(lt, m) => {
self.word("&");
self.print_opt_lifetime(lt);
self.word("pin ");
self.print_mutability(*m, true);
self.word("self")
}
SelfKind::Explicit(typ, m) => {
self.print_mutability(*m, false);
self.word("self");

View File

@ -574,6 +574,14 @@ impl<'a> State<'a> {
);
self.word(".await");
}
ast::ExprKind::Use(expr, _) => {
self.print_expr_cond_paren(
expr,
expr.precedence() < ExprPrecedence::Unambiguous,
fixup,
);
self.word(".use");
}
ast::ExprKind::Assign(lhs, rhs, _) => {
self.print_expr_cond_paren(
lhs,
@ -885,6 +893,7 @@ impl<'a> State<'a> {
fn print_capture_clause(&mut self, capture_clause: ast::CaptureBy) {
match capture_clause {
ast::CaptureBy::Value { .. } => self.word_space("move"),
ast::CaptureBy::Use { .. } => self.word_space("use"),
ast::CaptureBy::Ref => {}
}
}

View File

@ -735,7 +735,8 @@ impl<'a> State<'a> {
}
pub fn print_where_predicate(&mut self, predicate: &ast::WherePredicate) {
let ast::WherePredicate { kind, id: _, span: _ } = predicate;
let ast::WherePredicate { attrs, kind, id: _, span: _, is_placeholder: _ } = predicate;
self.print_outer_attributes(attrs);
match kind {
ast::WherePredicateKind::BoundPredicate(where_bound_predicate) => {
self.print_where_bound_predicate(where_bound_predicate);

View File

@ -14,3 +14,6 @@ rustc_serialize = {path = "../rustc_serialize"}
rustc_span = {path = "../rustc_span"}
thin-vec = "0.2.12"
# tidy-alphabetical-end
[lints]
workspace = true

View File

@ -3,7 +3,6 @@
#![doc(rust_logo)]
#![feature(let_chains)]
#![feature(rustdoc_internals)]
#![warn(unreachable_pub)]
// tidy-alphabetical-end
mod attributes;
@ -149,3 +148,47 @@ print_tup!(A B C D E F G H);
print_skip!(Span, ());
print_disp!(Symbol, u16, bool, NonZero<u32>);
print_debug!(UintTy, IntTy, Align, AttrStyle, CommentKind, Transparency);
/// Finds attributes in sequences of attributes by pattern matching.
///
/// A little like `matches` but for attributes.
///
/// ```rust,ignore (illustrative)
/// // finds the repr attribute
/// if let Some(r) = find_attr!(attrs, AttributeKind::Repr(r) => r) {
///
/// }
///
/// // checks if one has matched
/// if find_attr!(attrs, AttributeKind::Repr(_)) {
///
/// }
/// ```
///
/// Often this requires you to first end up with a list of attributes.
/// A common way to get those is through `tcx.get_all_attrs(did)`
#[macro_export]
macro_rules! find_attr {
($attributes_list: expr, $pattern: pat $(if $guard: expr)?) => {{
$crate::find_attr!($attributes_list, $pattern $(if $guard)? => ()).is_some()
}};
($attributes_list: expr, $pattern: pat $(if $guard: expr)? => $e: expr) => {{
fn check_attribute_iterator<'a>(_: &'_ impl IntoIterator<Item = &'a rustc_hir::Attribute>) {}
check_attribute_iterator(&$attributes_list);
let find_attribute = |iter| {
for i in $attributes_list {
match i {
rustc_hir::Attribute::Parsed($pattern) $(if $guard)? => {
return Some($e);
}
_ => {}
}
}
None
};
find_attribute($attributes_list)
}};
}

View File

@ -16,8 +16,12 @@ rustc_fluent_macro = { path = "../rustc_fluent_macro" }
rustc_hir = { path = "../rustc_hir" }
rustc_lexer = { path = "../rustc_lexer" }
rustc_macros = { path = "../rustc_macros" }
rustc_middle = { path = "../rustc_middle" }
rustc_serialize = { path = "../rustc_serialize" }
rustc_session = { path = "../rustc_session" }
rustc_span = { path = "../rustc_span" }
thin-vec = "0.2.12"
# tidy-alphabetical-end
[lints]
workspace = true

View File

@ -80,7 +80,6 @@
#![doc(rust_logo)]
#![feature(let_chains)]
#![feature(rustdoc_internals)]
#![warn(unreachable_pub)]
// tidy-alphabetical-end
#[macro_use]
@ -95,47 +94,3 @@ pub use context::{AttributeParser, OmitDoc};
pub use rustc_attr_data_structures::*;
rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
/// Finds attributes in sequences of attributes by pattern matching.
///
/// A little like `matches` but for attributes.
///
/// ```rust,ignore (illustrative)
/// // finds the repr attribute
/// if let Some(r) = find_attr!(attrs, AttributeKind::Repr(r) => r) {
///
/// }
///
/// // checks if one has matched
/// if find_attr!(attrs, AttributeKind::Repr(_)) {
///
/// }
/// ```
///
/// Often this requires you to first end up with a list of attributes.
/// A common way to get those is through `tcx.get_all_attrs(did)`
#[macro_export]
macro_rules! find_attr {
($attributes_list: expr, $pattern: pat $(if $guard: expr)?) => {{
$crate::find_attr!($attributes_list, $pattern $(if $guard)? => ()).is_some()
}};
($attributes_list: expr, $pattern: pat $(if $guard: expr)? => $e: expr) => {{
fn check_attribute_iterator<'a>(_: &'_ impl IntoIterator<Item = &'a rustc_hir::Attribute>) {}
check_attribute_iterator(&$attributes_list);
let find_attribute = |iter| {
for i in $attributes_list {
match i {
rustc_hir::Attribute::Parsed($pattern) $(if $guard)? => {
return Some($e);
}
_ => {}
}
}
None
};
find_attribute($attributes_list)
}};
}

View File

@ -473,6 +473,15 @@ impl<'a> MetaItemListParserContext<'a> {
{
self.inside_delimiters.next();
return Some(MetaItemOrLitParser::Lit(lit));
} else if let Some(TokenTree::Delimited(.., Delimiter::Invisible(_), inner_tokens)) =
self.inside_delimiters.peek()
{
self.inside_delimiters.next();
return MetaItemListParserContext {
inside_delimiters: inner_tokens.iter().peekable(),
dcx: self.dcx,
}
.next();
}
// or a path.

View File

@ -11,3 +11,6 @@ icu_locid_transform = "1.3.2"
icu_provider = { version = "1.2", features = ["sync"] }
zerovec = "0.10.0"
# tidy-alphabetical-end
[lints]
workspace = true

View File

@ -23,9 +23,9 @@
// tidy-alphabetical-start
#![allow(elided_lifetimes_in_paths)]
#![allow(internal_features)]
#![allow(unreachable_pub)] // because this crate is mostly generated code
#![doc(rust_logo)]
#![feature(rustdoc_internals)]
// #![warn(unreachable_pub)] // don't use because this crate is mostly generated code
// tidy-alphabetical-end
mod data {

View File

@ -27,3 +27,6 @@ rustc_traits = { path = "../rustc_traits" }
smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
tracing = "0.1"
# tidy-alphabetical-end
[lints]
workspace = true

View File

@ -403,6 +403,7 @@ impl<'infcx, 'tcx> crate::MirBorrowckCtxt<'_, 'infcx, 'tcx> {
.expect_closure();
let span = match capture_clause {
rustc_hir::CaptureBy::Value { move_kw } => move_kw.shrink_to_lo(),
rustc_hir::CaptureBy::Use { use_kw } => use_kw.shrink_to_lo(),
rustc_hir::CaptureBy::Ref => fn_decl_span.shrink_to_lo(),
};
diag.span_suggestion_verbose(

View File

@ -1,11 +1,8 @@
use rustc_data_structures::graph;
use rustc_index::IndexVec;
use rustc_middle::mir::ConstraintCategory;
use rustc_middle::ty::{RegionVid, VarianceDiagInfo};
use rustc_span::DUMMY_SP;
use rustc_middle::ty::RegionVid;
use crate::constraints::{OutlivesConstraint, OutlivesConstraintIndex, OutlivesConstraintSet};
use crate::type_check::Locations;
/// The construct graph organizes the constraints by their end-points.
/// It can be used to view a `R1: R2` constraint as either an edge `R1
@ -23,8 +20,8 @@ pub(crate) type ReverseConstraintGraph = ConstraintGraph<Reverse>;
/// Marker trait that controls whether a `R1: R2` constraint
/// represents an edge `R1 -> R2` or `R2 -> R1`.
pub(crate) trait ConstraintGraphDirection: Copy + 'static {
fn start_region(c: &OutlivesConstraint<'_>) -> RegionVid;
fn end_region(c: &OutlivesConstraint<'_>) -> RegionVid;
fn start_region(sup: RegionVid, sub: RegionVid) -> RegionVid;
fn end_region(sup: RegionVid, sub: RegionVid) -> RegionVid;
fn is_normal() -> bool;
}
@ -36,12 +33,12 @@ pub(crate) trait ConstraintGraphDirection: Copy + 'static {
pub(crate) struct Normal;
impl ConstraintGraphDirection for Normal {
fn start_region(c: &OutlivesConstraint<'_>) -> RegionVid {
c.sup
fn start_region(sup: RegionVid, _sub: RegionVid) -> RegionVid {
sup
}
fn end_region(c: &OutlivesConstraint<'_>) -> RegionVid {
c.sub
fn end_region(_sup: RegionVid, sub: RegionVid) -> RegionVid {
sub
}
fn is_normal() -> bool {
@ -57,12 +54,12 @@ impl ConstraintGraphDirection for Normal {
pub(crate) struct Reverse;
impl ConstraintGraphDirection for Reverse {
fn start_region(c: &OutlivesConstraint<'_>) -> RegionVid {
c.sub
fn start_region(_sup: RegionVid, sub: RegionVid) -> RegionVid {
sub
}
fn end_region(c: &OutlivesConstraint<'_>) -> RegionVid {
c.sup
fn end_region(sup: RegionVid, _sub: RegionVid) -> RegionVid {
sup
}
fn is_normal() -> bool {
@ -84,7 +81,7 @@ impl<D: ConstraintGraphDirection> ConstraintGraph<D> {
let mut next_constraints = IndexVec::from_elem(None, &set.outlives);
for (idx, constraint) in set.outlives.iter_enumerated().rev() {
let head = &mut first_constraints[D::start_region(constraint)];
let head = &mut first_constraints[D::start_region(constraint.sup, constraint.sub)];
let next = &mut next_constraints[idx];
debug_assert!(next.is_none());
*next = *head;
@ -105,63 +102,57 @@ impl<D: ConstraintGraphDirection> ConstraintGraph<D> {
RegionGraph::new(set, self, static_region)
}
pub(crate) fn is_normal(&self) -> bool {
D::is_normal()
}
/// Given a region `R`, iterate over all constraints `R: R1`.
pub(crate) fn outgoing_edges<'a, 'tcx>(
pub(crate) fn outgoing_edges_from_graph<'a, 'tcx>(
&'a self,
region_sup: RegionVid,
constraints: &'a OutlivesConstraintSet<'tcx>,
static_region: RegionVid,
) -> Edges<'a, 'tcx, D> {
//if this is the `'static` region and the graph's direction is normal,
//then setup the Edges iterator to return all regions #53178
if region_sup == static_region && D::is_normal() {
Edges {
graph: self,
constraints,
pointer: None,
next_static_idx: Some(0),
static_region,
}
} else {
//otherwise, just setup the iterator as normal
let first = self.first_constraints[region_sup];
Edges { graph: self, constraints, pointer: first, next_static_idx: None, static_region }
}
) -> EdgesFromGraph<'a, 'tcx, D> {
EdgesFromGraph { graph: self, constraints, pointer: self.first_constraints[region_sup] }
}
/// Returns all regions (#53178).
pub(crate) fn outgoing_edges_from_static(&self) -> EdgesFromStatic {
EdgesFromStatic { next_static_idx: 0, end_static_idx: self.first_constraints.len() }
}
}
pub(crate) struct Edges<'a, 'tcx, D: ConstraintGraphDirection> {
pub(crate) struct EdgesFromGraph<'a, 'tcx, D: ConstraintGraphDirection> {
graph: &'a ConstraintGraph<D>,
constraints: &'a OutlivesConstraintSet<'tcx>,
pointer: Option<OutlivesConstraintIndex>,
next_static_idx: Option<usize>,
static_region: RegionVid,
}
impl<'a, 'tcx, D: ConstraintGraphDirection> Iterator for Edges<'a, 'tcx, D> {
type Item = OutlivesConstraint<'tcx>;
impl<'a, 'tcx, D: ConstraintGraphDirection> Iterator for EdgesFromGraph<'a, 'tcx, D> {
type Item = &'a OutlivesConstraint<'tcx>;
fn next(&mut self) -> Option<Self::Item> {
if let Some(p) = self.pointer {
self.pointer = self.graph.next_constraints[p];
Some(&self.constraints[p])
} else {
None
}
}
}
Some(self.constraints[p])
} else if let Some(next_static_idx) = self.next_static_idx {
self.next_static_idx = if next_static_idx == (self.graph.first_constraints.len() - 1) {
None
} else {
Some(next_static_idx + 1)
};
pub(crate) struct EdgesFromStatic {
next_static_idx: usize,
end_static_idx: usize,
}
Some(OutlivesConstraint {
sup: self.static_region,
sub: next_static_idx.into(),
locations: Locations::All(DUMMY_SP),
span: DUMMY_SP,
category: ConstraintCategory::Internal,
variance_info: VarianceDiagInfo::default(),
from_closure: false,
})
impl Iterator for EdgesFromStatic {
type Item = RegionVid;
fn next(&mut self) -> Option<Self::Item> {
if self.next_static_idx < self.end_static_idx {
let ret = RegionVid::from_usize(self.next_static_idx);
self.next_static_idx += 1;
Some(ret)
} else {
None
}
@ -193,21 +184,38 @@ impl<'a, 'tcx, D: ConstraintGraphDirection> RegionGraph<'a, 'tcx, D> {
/// Given a region `R`, iterate over all regions `R1` such that
/// there exists a constraint `R: R1`.
pub(crate) fn outgoing_regions(&self, region_sup: RegionVid) -> Successors<'a, 'tcx, D> {
Successors {
edges: self.constraint_graph.outgoing_edges(region_sup, self.set, self.static_region),
// If this is the `'static` region and the graph's direction is normal,
// then setup the Edges iterator to return all regions (#53178).
if region_sup == self.static_region && D::is_normal() {
Successors::FromStatic(self.constraint_graph.outgoing_edges_from_static())
} else {
// Otherwise, just setup the iterator as normal.
Successors::FromGraph(
self.constraint_graph.outgoing_edges_from_graph(region_sup, self.set),
)
}
}
}
pub(crate) struct Successors<'a, 'tcx, D: ConstraintGraphDirection> {
edges: Edges<'a, 'tcx, D>,
pub(crate) enum Successors<'a, 'tcx, D: ConstraintGraphDirection> {
FromStatic(EdgesFromStatic),
FromGraph(EdgesFromGraph<'a, 'tcx, D>),
}
impl<'a, 'tcx, D: ConstraintGraphDirection> Iterator for Successors<'a, 'tcx, D> {
type Item = RegionVid;
fn next(&mut self) -> Option<Self::Item> {
self.edges.next().map(|c| D::end_region(&c))
match self {
Successors::FromStatic(edges) => {
// No `D::end_region` call needed here: static successors are only possible when
// the direction is `Normal`, so we can directly use what would be the `sub` value.
edges.next()
}
Successors::FromGraph(edges) => {
edges.next().map(|constraint| D::end_region(constraint.sup, constraint.sub))
}
}
}
}

View File

@ -823,7 +823,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
) => {
capture_reason = format!("mutable borrow of `{upvar}`");
}
ty::UpvarCapture::ByValue => {
ty::UpvarCapture::ByValue | ty::UpvarCapture::ByUse => {
capture_reason = format!("possible mutation of `{upvar}`");
}
_ => bug!("upvar `{upvar}` borrowed, but not mutably"),

View File

@ -13,7 +13,6 @@
#![feature(rustdoc_internals)]
#![feature(stmt_expr_attributes)]
#![feature(try_blocks)]
#![warn(unreachable_pub)]
// tidy-alphabetical-end
use std::borrow::Cow;
@ -1490,14 +1489,20 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
let stmt = &bbd.statements[loc.statement_index];
debug!("temporary assigned in: stmt={:?}", stmt);
if let StatementKind::Assign(box (_, Rvalue::Ref(_, _, source))) = stmt.kind
{
propagate_closure_used_mut_place(self, source);
} else {
bug!(
"closures should only capture user variables \
match stmt.kind {
StatementKind::Assign(box (
_,
Rvalue::Ref(_, _, source)
| Rvalue::Use(Operand::Copy(source) | Operand::Move(source)),
)) => {
propagate_closure_used_mut_place(self, source);
}
_ => {
bug!(
"closures should only capture user variables \
or references to user variables"
);
);
}
}
}
_ => propagate_closure_used_mut_place(self, place),

View File

@ -21,8 +21,8 @@ use rustc_middle::traits::{ObligationCause, ObligationCauseCode};
use rustc_middle::ty::fold::fold_regions;
use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt, TypeFoldable, UniverseIndex};
use rustc_mir_dataflow::points::DenseLocationMap;
use rustc_span::Span;
use rustc_span::hygiene::DesugaringKind;
use rustc_span::{DUMMY_SP, Span};
use tracing::{debug, instrument, trace};
use crate::BorrowckInferCtxt;
@ -311,9 +311,11 @@ enum RegionRelationCheckResult {
}
#[derive(Clone, PartialEq, Eq, Debug)]
enum Trace<'tcx> {
enum Trace<'a, 'tcx> {
StartRegion,
FromOutlivesConstraint(OutlivesConstraint<'tcx>),
FromGraph(&'a OutlivesConstraint<'tcx>),
FromStatic(RegionVid),
FromMember(RegionVid, RegionVid, Span),
NotVisited,
}
@ -1764,6 +1766,8 @@ impl<'tcx> RegionInferenceContext<'tcx> {
let mut context = IndexVec::from_elem(Trace::NotVisited, &self.definitions);
context[from_region] = Trace::StartRegion;
let fr_static = self.universal_regions().fr_static;
// Use a deque so that we do a breadth-first search. We will
// stop at the first match, which ought to be the shortest
// path (fewest constraints).
@ -1783,13 +1787,39 @@ impl<'tcx> RegionInferenceContext<'tcx> {
if target_test(r) {
let mut result = vec![];
let mut p = r;
// This loop is cold and runs at the end, which is why we delay
// `OutlivesConstraint` construction until now.
loop {
match context[p].clone() {
Trace::NotVisited => {
bug!("found unvisited region {:?} on path to {:?}", p, r)
match context[p] {
Trace::FromGraph(c) => {
p = c.sup;
result.push(*c);
}
Trace::FromOutlivesConstraint(c) => {
Trace::FromStatic(sub) => {
let c = OutlivesConstraint {
sup: fr_static,
sub,
locations: Locations::All(DUMMY_SP),
span: DUMMY_SP,
category: ConstraintCategory::Internal,
variance_info: ty::VarianceDiagInfo::default(),
from_closure: false,
};
p = c.sup;
result.push(c);
}
Trace::FromMember(sup, sub, span) => {
let c = OutlivesConstraint {
sup,
sub,
locations: Locations::All(span),
span,
category: ConstraintCategory::OpaqueType,
variance_info: ty::VarianceDiagInfo::default(),
from_closure: false,
};
p = c.sup;
result.push(c);
}
@ -1798,6 +1828,10 @@ impl<'tcx> RegionInferenceContext<'tcx> {
result.reverse();
return Some((result, r));
}
Trace::NotVisited => {
bug!("found unvisited region {:?} on path to {:?}", p, r)
}
}
}
}
@ -1808,45 +1842,42 @@ impl<'tcx> RegionInferenceContext<'tcx> {
// A constraint like `'r: 'x` can come from our constraint
// graph.
let fr_static = self.universal_regions().fr_static;
let outgoing_edges_from_graph =
self.constraint_graph.outgoing_edges(r, &self.constraints, fr_static);
// Always inline this closure because it can be hot.
let mut handle_constraint = #[inline(always)]
|constraint: OutlivesConstraint<'tcx>| {
debug_assert_eq!(constraint.sup, r);
let sub_region = constraint.sub;
if let Trace::NotVisited = context[sub_region] {
context[sub_region] = Trace::FromOutlivesConstraint(constraint);
deque.push_back(sub_region);
let mut handle_trace = #[inline(always)]
|sub, trace| {
if let Trace::NotVisited = context[sub] {
context[sub] = trace;
deque.push_back(sub);
}
};
// This loop can be hot.
for constraint in outgoing_edges_from_graph {
if matches!(constraint.category, ConstraintCategory::IllegalUniverse) {
debug!("Ignoring illegal universe constraint: {constraint:?}");
continue;
// If this is the `'static` region and the graph's direction is normal, then set up the
// Edges iterator to return all regions (#53178).
if r == fr_static && self.constraint_graph.is_normal() {
for sub in self.constraint_graph.outgoing_edges_from_static() {
handle_trace(sub, Trace::FromStatic(sub));
}
} else {
let edges = self.constraint_graph.outgoing_edges_from_graph(r, &self.constraints);
// This loop can be hot.
for constraint in edges {
if matches!(constraint.category, ConstraintCategory::IllegalUniverse) {
debug!("Ignoring illegal universe constraint: {constraint:?}");
continue;
}
debug_assert_eq!(constraint.sup, r);
handle_trace(constraint.sub, Trace::FromGraph(constraint));
}
handle_constraint(constraint);
}
// Member constraints can also give rise to `'r: 'x` edges that
// were not part of the graph initially, so watch out for those.
// (But they are extremely rare; this loop is very cold.)
for constraint in self.applied_member_constraints(self.constraint_sccs.scc(r)) {
let sub = constraint.min_choice;
let p_c = &self.member_constraints[constraint.member_constraint_index];
let constraint = OutlivesConstraint {
sup: r,
sub: constraint.min_choice,
locations: Locations::All(p_c.definition_span),
span: p_c.definition_span,
category: ConstraintCategory::OpaqueType,
variance_info: ty::VarianceDiagInfo::default(),
from_closure: false,
};
handle_constraint(constraint);
handle_trace(sub, Trace::FromMember(r, sub, p_c.definition_span));
}
}

View File

@ -1,4 +1,4 @@
use rustc_hir::def_id::DefId;
use rustc_hir::def_id::LocalDefId;
use rustc_infer::infer::canonical::QueryRegionConstraints;
use rustc_infer::infer::outlives::env::RegionBoundPairs;
use rustc_infer::infer::outlives::obligations::{TypeOutlives, TypeOutlivesDelegate};
@ -88,7 +88,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
pub(crate) fn apply_closure_requirements(
&mut self,
closure_requirements: &ClosureRegionRequirements<'tcx>,
closure_def_id: DefId,
closure_def_id: LocalDefId,
closure_args: ty::GenericArgsRef<'tcx>,
) {
// Extract the values of the free regions in `closure_args`
@ -98,7 +98,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
self.tcx,
closure_args,
closure_requirements.num_external_vids,
closure_def_id.expect_local(),
closure_def_id,
);
debug!(?closure_mapping);

View File

@ -328,9 +328,8 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> {
}
}
#[instrument(level = "debug", skip(self))]
fn visit_const_operand(&mut self, constant: &ConstOperand<'tcx>, location: Location) {
debug!(?constant, ?location, "visit_const_operand");
self.super_const_operand(constant, location);
let ty = constant.const_.ty();
@ -339,14 +338,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> {
self.typeck.constraints.liveness_constraints.add_location(live_region_vid, location);
});
// HACK(compiler-errors): Constants that are gathered into Body.required_consts
// have their locations erased...
let locations = if location != Location::START {
location.to_locations()
} else {
Locations::All(constant.span)
};
let locations = location.to_locations();
if let Some(annotation_index) = constant.user_ty {
if let Err(terr) = self.typeck.relate_type_and_user_type(
constant.const_.ty(),
@ -491,9 +483,28 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> {
}
}
#[instrument(level = "debug", skip(self))]
fn visit_body(&mut self, body: &Body<'tcx>) {
// The types of local_decls are checked above which is called in super_body.
self.super_body(body);
// We intentionally do not recurse into `body.required_consts` or
// `body.mentioned_items` here as the MIR at this phase should still
// refer to all items and we don't want to check them multiple times.
for (local, local_decl) in body.local_decls.iter_enumerated() {
self.visit_local_decl(local, local_decl);
}
for (block, block_data) in body.basic_blocks.iter_enumerated() {
let mut location = Location { block, statement_index: 0 };
for stmt in &block_data.statements {
if !stmt.source_info.span.is_dummy() {
self.last_span = stmt.source_info.span;
}
self.visit_statement(stmt, location);
location.statement_index += 1;
}
self.visit_terminator(block_data.terminator(), location);
}
}
}
@ -2120,8 +2131,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
//
// Note that other checks (such as denying `dyn Send` -> `dyn
// Debug`) are in `rustc_hir_typeck`.
if let ty::Dynamic(src_tty, _src_lt, _) = *src_tail.kind()
&& let ty::Dynamic(dst_tty, dst_lt, _) = *dst_tail.kind()
if let ty::Dynamic(src_tty, _src_lt, ty::Dyn) = *src_tail.kind()
&& let ty::Dynamic(dst_tty, dst_lt, ty::Dyn) = *dst_tail.kind()
&& src_tty.principal().is_some()
&& dst_tty.principal().is_some()
{
@ -2582,7 +2593,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
ConstraintCategory::Boring, // same as above.
self.constraints,
)
.apply_closure_requirements(closure_requirements, def_id.to_def_id(), args);
.apply_closure_requirements(closure_requirements, def_id, args);
}
// Now equate closure args to regions inherited from `typeck_root_def_id`. Fixes #98589.

View File

@ -3,10 +3,6 @@ name = "rustc_builtin_macros"
version = "0.0.0"
edition = "2024"
[lints.rust]
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(llvm_enzyme)'] }
[lib]
doctest = false
@ -34,3 +30,6 @@ smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
thin-vec = "0.2.12"
tracing = "0.1"
# tidy-alphabetical-end
[lints]
workspace = true

View File

@ -297,6 +297,7 @@ impl<'cx, 'a> Context<'cx, 'a> {
| ExprKind::AssignOp(_, _, _)
| ExprKind::Gen(_, _, _, _)
| ExprKind::Await(_, _)
| ExprKind::Use(_, _)
| ExprKind::Block(_, _)
| ExprKind::Break(_, _)
| ExprKind::Closure(_)

View File

@ -286,7 +286,7 @@ mod llvm_enzyme {
let orig_annotatable: Annotatable = match item {
Annotatable::Item(ref mut iitem) => {
if !iitem.attrs.iter().any(|a| a.id == attr.id) {
iitem.attrs.push(attr.clone());
iitem.attrs.push(attr);
}
if !iitem.attrs.iter().any(|a| a.id == inline_never.id) {
iitem.attrs.push(inline_never.clone());
@ -295,7 +295,7 @@ mod llvm_enzyme {
}
Annotatable::AssocItem(ref mut assoc_item, i @ Impl) => {
if !assoc_item.attrs.iter().any(|a| a.id == attr.id) {
assoc_item.attrs.push(attr.clone());
assoc_item.attrs.push(attr);
}
if !assoc_item.attrs.iter().any(|a| a.id == inline_never.id) {
assoc_item.attrs.push(inline_never.clone());
@ -322,7 +322,7 @@ mod llvm_enzyme {
let d_annotatable = if is_impl {
let assoc_item: AssocItemKind = ast::AssocItemKind::Fn(asdf);
let d_fn = P(ast::AssocItem {
attrs: thin_vec![d_attr.clone(), inline_never],
attrs: thin_vec![d_attr, inline_never],
id: ast::DUMMY_NODE_ID,
span,
vis,
@ -332,12 +332,8 @@ mod llvm_enzyme {
});
Annotatable::AssocItem(d_fn, Impl)
} else {
let mut d_fn = ecx.item(
span,
d_ident,
thin_vec![d_attr.clone(), inline_never],
ItemKind::Fn(asdf),
);
let mut d_fn =
ecx.item(span, d_ident, thin_vec![d_attr, inline_never], ItemKind::Fn(asdf));
d_fn.vis = vis;
Annotatable::Item(d_fn)
};
@ -446,7 +442,7 @@ mod llvm_enzyme {
if primal_ret && n_active == 0 && x.mode.is_rev() {
// We only have the primal ret.
body.stmts.push(ecx.stmt_expr(black_box_primal_call.clone()));
body.stmts.push(ecx.stmt_expr(black_box_primal_call));
return body;
}
@ -471,7 +467,7 @@ mod llvm_enzyme {
if primal_ret {
// We have both primal ret and active floats.
// primal ret is first, by construction.
exprs.push(primal_call.clone());
exprs.push(primal_call);
}
// Now construct default placeholder for each active float.
@ -538,16 +534,11 @@ mod llvm_enzyme {
return body;
}
[arg] => {
ret = ecx.expr_call(
new_decl_span,
blackbox_call_expr.clone(),
thin_vec![arg.clone()],
);
ret = ecx.expr_call(new_decl_span, blackbox_call_expr, thin_vec![arg.clone()]);
}
args => {
let ret_tuple: P<ast::Expr> = ecx.expr_tuple(span, args.into());
ret =
ecx.expr_call(new_decl_span, blackbox_call_expr.clone(), thin_vec![ret_tuple]);
ret = ecx.expr_call(new_decl_span, blackbox_call_expr, thin_vec![ret_tuple]);
}
}
assert!(has_ret(&d_sig.decl.output));
@ -567,7 +558,7 @@ mod llvm_enzyme {
let args: ThinVec<_> =
idents[1..].iter().map(|arg| ecx.expr_path(ecx.path_ident(span, *arg))).collect();
let self_expr = ecx.expr_self(span);
ecx.expr_method_call(span, self_expr, primal, args.clone())
ecx.expr_method_call(span, self_expr, primal, args)
} else {
let args: ThinVec<_> =
idents.iter().map(|arg| ecx.expr_path(ecx.path_ident(span, *arg))).collect();

View File

@ -300,13 +300,16 @@ pub(crate) fn expand_deriving_coerce_pointee(
to_ty: &s_ty,
rewritten: false,
};
let mut predicate = ast::WherePredicate {
kind: ast::WherePredicateKind::BoundPredicate(bound.clone()),
span: predicate.span,
id: ast::DUMMY_NODE_ID,
};
substitution.visit_where_predicate(&mut predicate);
let mut kind = ast::WherePredicateKind::BoundPredicate(bound.clone());
substitution.visit_where_predicate_kind(&mut kind);
if substitution.rewritten {
let predicate = ast::WherePredicate {
attrs: predicate.attrs.clone(),
kind,
span: predicate.span,
id: ast::DUMMY_NODE_ID,
is_placeholder: false,
};
impl_generics.where_clause.predicates.push(predicate);
}
}
@ -388,8 +391,8 @@ impl<'a> ast::mut_visit::MutVisitor for TypeSubstitution<'a> {
}
}
fn visit_where_predicate(&mut self, where_predicate: &mut ast::WherePredicate) {
match &mut where_predicate.kind {
fn visit_where_predicate_kind(&mut self, kind: &mut ast::WherePredicateKind) {
match kind {
rustc_ast::WherePredicateKind::BoundPredicate(bound) => {
bound
.bound_generic_params

View File

@ -687,9 +687,11 @@ impl<'a> TraitDef<'a> {
// and similarly for where clauses
where_clause.predicates.extend(generics.where_clause.predicates.iter().map(|clause| {
ast::WherePredicate {
attrs: clause.attrs.clone(),
kind: clause.kind.clone(),
id: ast::DUMMY_NODE_ID,
span: clause.span.with_ctxt(ctxt),
is_placeholder: false,
}
}));
@ -744,8 +746,13 @@ impl<'a> TraitDef<'a> {
};
let kind = ast::WherePredicateKind::BoundPredicate(predicate);
let predicate =
ast::WherePredicate { kind, id: ast::DUMMY_NODE_ID, span: self.span };
let predicate = ast::WherePredicate {
attrs: ThinVec::new(),
kind,
id: ast::DUMMY_NODE_ID,
span: self.span,
is_placeholder: false,
};
where_clause.predicates.push(predicate);
}
}

View File

@ -190,7 +190,8 @@ fn make_format_args(
&& let [stmt] = block.stmts.as_slice()
&& let StmtKind::Expr(expr) = &stmt.kind
&& let ExprKind::Path(None, path) = &expr.kind
&& path.is_potential_trivial_const_arg()
&& path.segments.len() == 1
&& path.segments[0].args.is_none()
{
err.multipart_suggestion(
"quote your inlined format argument to use as string literal",
@ -710,11 +711,9 @@ fn report_missing_placeholders(
};
let pos = sub.position();
let sub = String::from(sub.as_str());
if explained.contains(&sub) {
if !explained.insert(sub.to_string()) {
continue;
}
explained.insert(sub);
if !found_foreign {
found_foreign = true;

View File

@ -12,14 +12,16 @@ pub(crate) mod printf {
Escape((usize, usize)),
}
impl<'a> Substitution<'a> {
pub(crate) fn as_str(&self) -> &str {
impl ToString for Substitution<'_> {
fn to_string(&self) -> String {
match self {
Substitution::Format(fmt) => fmt.span,
Substitution::Escape(_) => "%%",
Substitution::Format(fmt) => fmt.span.into(),
Substitution::Escape(_) => "%%".into(),
}
}
}
impl Substitution<'_> {
pub(crate) fn position(&self) -> InnerSpan {
match self {
Substitution::Format(fmt) => fmt.position,
@ -627,15 +629,17 @@ pub(crate) mod shell {
Escape((usize, usize)),
}
impl Substitution<'_> {
pub(crate) fn as_str(&self) -> String {
impl ToString for Substitution<'_> {
fn to_string(&self) -> String {
match self {
Substitution::Ordinal(n, _) => format!("${n}"),
Substitution::Name(n, _) => format!("${n}"),
Substitution::Escape(_) => "$$".into(),
}
}
}
impl Substitution<'_> {
pub(crate) fn position(&self) -> InnerSpan {
let (Self::Ordinal(_, pos) | Self::Name(_, pos) | Self::Escape(pos)) = self;
InnerSpan::new(pos.0, pos.1)

View File

@ -18,7 +18,6 @@
#![feature(rustdoc_internals)]
#![feature(string_from_utf8_lossy_owned)]
#![feature(try_blocks)]
#![warn(unreachable_pub)]
// tidy-alphabetical-end
extern crate proc_macro;

View File

@ -60,6 +60,7 @@ pub fn inject(
Edition2018 => sym::rust_2018,
Edition2021 => sym::rust_2021,
Edition2024 => sym::rust_2024,
EditionFuture => sym::rust_future,
}])
.map(|&symbol| Ident::new(symbol, span))
.collect();

View File

@ -12,15 +12,15 @@ index 7165c3e48af..968552ad435 100644
--- a/library/alloc/Cargo.toml
+++ b/library/alloc/Cargo.toml
@@ -11,7 +11,7 @@ test = { path = "../test" }
edition = "2021"
bench = false
[dependencies]
core = { path = "../core", public = true }
-compiler_builtins = { version = "=0.1.148", features = ['rustc-dep-of-std'] }
+compiler_builtins = { version = "=0.1.148", features = ['rustc-dep-of-std', 'no-f16-f128'] }
-compiler_builtins = { version = "=0.1.151", features = ['rustc-dep-of-std'] }
+compiler_builtins = { version = "=0.1.151", features = ['rustc-dep-of-std', 'no-f16-f128'] }
[dev-dependencies]
rand = { version = "0.8.5", default-features = false, features = ["alloc"] }
[features]
compiler-builtins-mem = ['compiler_builtins/mem']
--
2.34.1

View File

@ -16,7 +16,7 @@ use crate::context::CodegenCx;
use crate::intrinsic::ArgAbiExt;
use crate::type_of::LayoutGccExt;
impl<'a, 'gcc, 'tcx> AbiBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
impl AbiBuilderMethods for Builder<'_, '_, '_> {
fn get_param(&mut self, index: usize) -> Self::Value {
let func = self.current_func();
let param = func.get_param(index as i32);

View File

@ -2439,9 +2439,5 @@ fn get_maybe_pointer_size(value: RValue<'_>) -> u32 {
#[cfg(not(feature = "master"))]
fn get_maybe_pointer_size(value: RValue<'_>) -> u32 {
let type_ = value.get_type();
if type_.get_pointee().is_some() {
std::mem::size_of::<*const ()>() as _
} else {
type_.get_size()
}
if type_.get_pointee().is_some() { size_of::<*const ()>() as _ } else { type_.get_size() }
}

View File

@ -59,16 +59,11 @@ pub fn type_is_pointer(typ: Type<'_>) -> bool {
typ.get_pointee().is_some()
}
impl<'gcc, 'tcx> ConstCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
impl<'gcc, 'tcx> ConstCodegenMethods for CodegenCx<'gcc, 'tcx> {
fn const_null(&self, typ: Type<'gcc>) -> RValue<'gcc> {
if type_is_pointer(typ) { self.context.new_null(typ) } else { self.const_int(typ, 0) }
}
fn is_undef(&self, _val: RValue<'gcc>) -> bool {
// FIXME: actually check for undef
false
}
fn const_undef(&self, typ: Type<'gcc>) -> RValue<'gcc> {
let local = self.current_func.borrow().expect("func").new_local(None, typ, "undefined");
if typ.is_struct().is_some() {
@ -146,13 +141,12 @@ impl<'gcc, 'tcx> ConstCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
}
fn const_str(&self, s: &str) -> (RValue<'gcc>, RValue<'gcc>) {
let str_global = *self
.const_str_cache
.borrow_mut()
.raw_entry_mut()
.from_key(s)
.or_insert_with(|| (s.to_owned(), self.global_string(s)))
.1;
let mut const_str_cache = self.const_str_cache.borrow_mut();
let str_global = const_str_cache.get(s).copied().unwrap_or_else(|| {
let g = self.global_string(s);
const_str_cache.insert(s.to_owned(), g);
g
});
let len = s.len();
let cs = self.const_ptrcast(
str_global.get_address(None),
@ -263,7 +257,7 @@ impl<'gcc, 'tcx> ConstCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
}
}
fn const_data_from_alloc(&self, alloc: ConstAllocation<'tcx>) -> Self::Value {
fn const_data_from_alloc(&self, alloc: ConstAllocation<'_>) -> Self::Value {
const_alloc_to_gcc(self, alloc)
}

View File

@ -302,9 +302,9 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
}
}
pub fn const_alloc_to_gcc<'gcc, 'tcx>(
cx: &CodegenCx<'gcc, 'tcx>,
alloc: ConstAllocation<'tcx>,
pub fn const_alloc_to_gcc<'gcc>(
cx: &CodegenCx<'gcc, '_>,
alloc: ConstAllocation<'_>,
) -> RValue<'gcc> {
let alloc = alloc.inner();
let mut llvals = Vec::with_capacity(alloc.provenance().ptrs().len() + 1);

View File

@ -16,7 +16,7 @@
#![allow(internal_features)]
#![doc(rust_logo)]
#![feature(rustdoc_internals)]
#![feature(rustc_private, decl_macro, never_type, trusted_len, hash_raw_entry, let_chains)]
#![feature(rustc_private, decl_macro, never_type, trusted_len, let_chains)]
#![allow(broken_intra_doc_links)]
#![recursion_limit = "256"]
#![warn(rust_2018_idioms)]

View File

@ -123,7 +123,7 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
}
}
impl<'gcc, 'tcx> BaseTypeCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
impl<'gcc, 'tcx> BaseTypeCodegenMethods for CodegenCx<'gcc, 'tcx> {
fn type_i8(&self) -> Type<'gcc> {
self.i8_type
}

View File

@ -43,3 +43,6 @@ serde_json = "1"
smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
tracing = "0.1"
# tidy-alphabetical-end
[lints]
workspace = true

View File

@ -654,7 +654,7 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
}
}
impl<'tcx> AbiBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
impl AbiBuilderMethods for Builder<'_, '_, '_> {
fn get_param(&mut self, index: usize) -> Self::Value {
llvm::get_param(self.llfn(), index as c_uint)
}

View File

@ -3,33 +3,31 @@ use rustc_ast::expand::allocator::{
ALLOCATOR_METHODS, AllocatorKind, AllocatorTy, NO_ALLOC_SHIM_IS_UNSTABLE,
alloc_error_handler_name, default_fn_name, global_fn_name,
};
use rustc_codegen_ssa::traits::BaseTypeCodegenMethods as _;
use rustc_middle::bug;
use rustc_middle::ty::TyCtxt;
use rustc_session::config::{DebugInfo, OomStrategy};
use crate::common::AsCCharPtr;
use crate::llvm::{self, Context, False, Module, True, Type};
use crate::{ModuleLlvm, attributes, debuginfo};
use crate::builder::SBuilder;
use crate::declare::declare_simple_fn;
use crate::llvm::{self, False, True, Type};
use crate::{SimpleCx, attributes, debuginfo};
pub(crate) unsafe fn codegen(
tcx: TyCtxt<'_>,
module_llvm: &mut ModuleLlvm,
cx: SimpleCx<'_>,
module_name: &str,
kind: AllocatorKind,
alloc_error_handler_kind: AllocatorKind,
) {
let llcx = &*module_llvm.llcx;
let llmod = module_llvm.llmod();
let usize = unsafe {
match tcx.sess.target.pointer_width {
16 => llvm::LLVMInt16TypeInContext(llcx),
32 => llvm::LLVMInt32TypeInContext(llcx),
64 => llvm::LLVMInt64TypeInContext(llcx),
tws => bug!("Unsupported target word size for int: {}", tws),
}
let usize = match tcx.sess.target.pointer_width {
16 => cx.type_i16(),
32 => cx.type_i32(),
64 => cx.type_i64(),
tws => bug!("Unsupported target word size for int: {}", tws),
};
let i8 = unsafe { llvm::LLVMInt8TypeInContext(llcx) };
let i8p = unsafe { llvm::LLVMPointerTypeInContext(llcx, 0) };
let i8 = cx.type_i8();
let i8p = cx.type_ptr();
if kind == AllocatorKind::Default {
for method in ALLOCATOR_METHODS {
@ -58,15 +56,14 @@ pub(crate) unsafe fn codegen(
let from_name = global_fn_name(method.name);
let to_name = default_fn_name(method.name);
create_wrapper_function(tcx, llcx, llmod, &from_name, &to_name, &args, output, false);
create_wrapper_function(tcx, &cx, &from_name, &to_name, &args, output, false);
}
}
// rust alloc error handler
create_wrapper_function(
tcx,
llcx,
llmod,
&cx,
"__rust_alloc_error_handler",
alloc_error_handler_name(alloc_error_handler_kind),
&[usize, usize], // size, align
@ -77,21 +74,21 @@ pub(crate) unsafe fn codegen(
unsafe {
// __rust_alloc_error_handler_should_panic
let name = OomStrategy::SYMBOL;
let ll_g = llvm::LLVMRustGetOrInsertGlobal(llmod, name.as_c_char_ptr(), name.len(), i8);
let ll_g = cx.declare_global(name, i8);
llvm::set_visibility(ll_g, llvm::Visibility::from_generic(tcx.sess.default_visibility()));
let val = tcx.sess.opts.unstable_opts.oom.should_panic();
let llval = llvm::LLVMConstInt(i8, val as u64, False);
llvm::set_initializer(ll_g, llval);
let name = NO_ALLOC_SHIM_IS_UNSTABLE;
let ll_g = llvm::LLVMRustGetOrInsertGlobal(llmod, name.as_c_char_ptr(), name.len(), i8);
let ll_g = cx.declare_global(name, i8);
llvm::set_visibility(ll_g, llvm::Visibility::from_generic(tcx.sess.default_visibility()));
let llval = llvm::LLVMConstInt(i8, 0, False);
llvm::set_initializer(ll_g, llval);
}
if tcx.sess.opts.debuginfo != DebugInfo::None {
let dbg_cx = debuginfo::CodegenUnitDebugContext::new(llmod);
let dbg_cx = debuginfo::CodegenUnitDebugContext::new(cx.llmod);
debuginfo::metadata::build_compile_unit_di_node(tcx, module_name, &dbg_cx);
dbg_cx.finalize(tcx.sess);
}
@ -99,77 +96,64 @@ pub(crate) unsafe fn codegen(
fn create_wrapper_function(
tcx: TyCtxt<'_>,
llcx: &Context,
llmod: &Module,
cx: &SimpleCx<'_>,
from_name: &str,
to_name: &str,
args: &[&Type],
output: Option<&Type>,
no_return: bool,
) {
unsafe {
let ty = llvm::LLVMFunctionType(
output.unwrap_or_else(|| llvm::LLVMVoidTypeInContext(llcx)),
args.as_ptr(),
args.len() as c_uint,
False,
);
let llfn = llvm::LLVMRustGetOrInsertFunction(
llmod,
from_name.as_c_char_ptr(),
from_name.len(),
ty,
);
let no_return = if no_return {
// -> ! DIFlagNoReturn
let no_return = llvm::AttributeKind::NoReturn.create_attr(llcx);
attributes::apply_to_llfn(llfn, llvm::AttributePlace::Function, &[no_return]);
Some(no_return)
} else {
None
};
let ty = cx.type_func(args, output.unwrap_or_else(|| cx.type_void()));
let llfn = declare_simple_fn(
&cx,
from_name,
llvm::CallConv::CCallConv,
llvm::UnnamedAddr::Global,
llvm::Visibility::from_generic(tcx.sess.default_visibility()),
ty,
);
let no_return = if no_return {
// -> ! DIFlagNoReturn
let no_return = llvm::AttributeKind::NoReturn.create_attr(cx.llcx);
attributes::apply_to_llfn(llfn, llvm::AttributePlace::Function, &[no_return]);
Some(no_return)
} else {
None
};
llvm::set_visibility(llfn, llvm::Visibility::from_generic(tcx.sess.default_visibility()));
if tcx.sess.must_emit_unwind_tables() {
let uwtable =
attributes::uwtable_attr(cx.llcx, tcx.sess.opts.unstable_opts.use_sync_unwind);
attributes::apply_to_llfn(llfn, llvm::AttributePlace::Function, &[uwtable]);
}
if tcx.sess.must_emit_unwind_tables() {
let uwtable =
attributes::uwtable_attr(llcx, tcx.sess.opts.unstable_opts.use_sync_unwind);
attributes::apply_to_llfn(llfn, llvm::AttributePlace::Function, &[uwtable]);
}
let callee = declare_simple_fn(
&cx,
to_name,
llvm::CallConv::CCallConv,
llvm::UnnamedAddr::Global,
llvm::Visibility::Hidden,
ty,
);
if let Some(no_return) = no_return {
// -> ! DIFlagNoReturn
attributes::apply_to_llfn(callee, llvm::AttributePlace::Function, &[no_return]);
}
llvm::set_visibility(callee, llvm::Visibility::Hidden);
let callee =
llvm::LLVMRustGetOrInsertFunction(llmod, to_name.as_c_char_ptr(), to_name.len(), ty);
if let Some(no_return) = no_return {
// -> ! DIFlagNoReturn
attributes::apply_to_llfn(callee, llvm::AttributePlace::Function, &[no_return]);
}
llvm::set_visibility(callee, llvm::Visibility::Hidden);
let llbb = unsafe { llvm::LLVMAppendBasicBlockInContext(cx.llcx, llfn, c"entry".as_ptr()) };
let llbb = llvm::LLVMAppendBasicBlockInContext(llcx, llfn, c"entry".as_ptr());
let llbuilder = llvm::LLVMCreateBuilderInContext(llcx);
llvm::LLVMPositionBuilderAtEnd(llbuilder, llbb);
let args = args
.iter()
.enumerate()
.map(|(i, _)| llvm::LLVMGetParam(llfn, i as c_uint))
.collect::<Vec<_>>();
let ret = llvm::LLVMBuildCallWithOperandBundles(
llbuilder,
ty,
callee,
args.as_ptr(),
args.len() as c_uint,
[].as_ptr(),
0 as c_uint,
c"".as_ptr(),
);
llvm::LLVMSetTailCall(ret, True);
if output.is_some() {
llvm::LLVMBuildRet(llbuilder, ret);
} else {
llvm::LLVMBuildRetVoid(llbuilder);
}
llvm::LLVMDisposeBuilder(llbuilder);
let mut bx = SBuilder::build(&cx, llbb);
let args = args
.iter()
.enumerate()
.map(|(i, _)| llvm::get_param(llfn, i as c_uint))
.collect::<Vec<_>>();
let ret = bx.call(ty, callee, &args, None);
llvm::LLVMSetTailCall(ret, True);
if output.is_some() {
bx.ret(ret);
} else {
bx.ret_void()
}
}

View File

@ -1,6 +1,5 @@
use std::assert_matches::assert_matches;
use libc::{c_char, c_uint};
use rustc_abi::{BackendRepr, Float, Integer, Primitive, Scalar};
use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece};
use rustc_codegen_ssa::mir::operand::OperandValue;
@ -483,12 +482,13 @@ pub(crate) fn inline_asm_call<'ll>(
debug!("Asm Output Type: {:?}", output);
let fty = bx.cx.type_func(&argtys, output);
unsafe {
// Ask LLVM to verify that the constraints are well-formed.
let constraints_ok = llvm::LLVMRustInlineAsmVerify(fty, cons.as_c_char_ptr(), cons.len());
debug!("constraint verification result: {:?}", constraints_ok);
if constraints_ok {
let v = llvm::LLVMRustInlineAsm(
// Ask LLVM to verify that the constraints are well-formed.
let constraints_ok =
unsafe { llvm::LLVMRustInlineAsmVerify(fty, cons.as_c_char_ptr(), cons.len()) };
debug!("constraint verification result: {:?}", constraints_ok);
if constraints_ok {
let v = unsafe {
llvm::LLVMRustInlineAsm(
fty,
asm.as_c_char_ptr(),
asm.len(),
@ -498,54 +498,50 @@ pub(crate) fn inline_asm_call<'ll>(
alignstack,
dia,
can_throw,
);
)
};
let call = if !labels.is_empty() {
assert!(catch_funclet.is_none());
bx.callbr(fty, None, None, v, inputs, dest.unwrap(), labels, None, None)
} else if let Some((catch, funclet)) = catch_funclet {
bx.invoke(fty, None, None, v, inputs, dest.unwrap(), catch, funclet, None)
} else {
bx.call(fty, None, None, v, inputs, None, None)
};
// Store mark in a metadata node so we can map LLVM errors
// back to source locations. See #17552.
let key = "srcloc";
let kind = llvm::LLVMGetMDKindIDInContext(
bx.llcx,
key.as_ptr().cast::<c_char>(),
key.len() as c_uint,
);
// `srcloc` contains one 64-bit integer for each line of assembly code,
// where the lower 32 bits hold the lo byte position and the upper 32 bits
// hold the hi byte position.
let mut srcloc = vec![];
if dia == llvm::AsmDialect::Intel && line_spans.len() > 1 {
// LLVM inserts an extra line to add the ".intel_syntax", so add
// a dummy srcloc entry for it.
//
// Don't do this if we only have 1 line span since that may be
// due to the asm template string coming from a macro. LLVM will
// default to the first srcloc for lines that don't have an
// associated srcloc.
srcloc.push(llvm::LLVMValueAsMetadata(bx.const_u64(0)));
}
srcloc.extend(line_spans.iter().map(|span| {
llvm::LLVMValueAsMetadata(bx.const_u64(
u64::from(span.lo().to_u32()) | (u64::from(span.hi().to_u32()) << 32),
))
}));
let md = llvm::LLVMMDNodeInContext2(bx.llcx, srcloc.as_ptr(), srcloc.len());
let md = llvm::LLVMMetadataAsValue(&bx.llcx, md);
llvm::LLVMSetMetadata(call, kind, md);
Some(call)
let call = if !labels.is_empty() {
assert!(catch_funclet.is_none());
bx.callbr(fty, None, None, v, inputs, dest.unwrap(), labels, None, None)
} else if let Some((catch, funclet)) = catch_funclet {
bx.invoke(fty, None, None, v, inputs, dest.unwrap(), catch, funclet, None)
} else {
// LLVM has detected an issue with our constraints, bail out
None
bx.call(fty, None, None, v, inputs, None, None)
};
// Store mark in a metadata node so we can map LLVM errors
// back to source locations. See #17552.
let key = "srcloc";
let kind = bx.get_md_kind_id(key);
// `srcloc` contains one 64-bit integer for each line of assembly code,
// where the lower 32 bits hold the lo byte position and the upper 32 bits
// hold the hi byte position.
let mut srcloc = vec![];
if dia == llvm::AsmDialect::Intel && line_spans.len() > 1 {
// LLVM inserts an extra line to add the ".intel_syntax", so add
// a dummy srcloc entry for it.
//
// Don't do this if we only have 1 line span since that may be
// due to the asm template string coming from a macro. LLVM will
// default to the first srcloc for lines that don't have an
// associated srcloc.
srcloc.push(llvm::LLVMValueAsMetadata(bx.const_u64(0)));
}
srcloc.extend(line_spans.iter().map(|span| {
llvm::LLVMValueAsMetadata(
bx.const_u64(u64::from(span.lo().to_u32()) | (u64::from(span.hi().to_u32()) << 32)),
)
}));
let md = unsafe { llvm::LLVMMDNodeInContext2(bx.llcx, srcloc.as_ptr(), srcloc.len()) };
let md = bx.get_metadata_value(md);
llvm::LLVMSetMetadata(call, kind, md);
Some(call)
} else {
// LLVM has detected an issue with our constraints, bail out
None
}
}

View File

@ -30,7 +30,7 @@ use tracing::{debug, instrument};
use crate::abi::FnAbiLlvmExt;
use crate::common::Funclet;
use crate::context::{CodegenCx, SimpleCx};
use crate::context::{CodegenCx, FullCx, GenericCx, SCx};
use crate::llvm::{
self, AtomicOrdering, AtomicRmwBinOp, BasicBlock, False, GEPNoWrapFlags, Metadata, True,
};
@ -40,15 +40,15 @@ use crate::value::Value;
use crate::{attributes, llvm_util};
#[must_use]
pub(crate) struct GenericBuilder<'a, 'll, CX: Borrow<SimpleCx<'ll>>> {
pub(crate) struct GenericBuilder<'a, 'll, CX: Borrow<SCx<'ll>>> {
pub llbuilder: &'ll mut llvm::Builder<'ll>,
pub cx: &'a CX,
pub cx: &'a GenericCx<'ll, CX>,
}
pub(crate) type SBuilder<'a, 'll> = GenericBuilder<'a, 'll, SimpleCx<'ll>>;
pub(crate) type Builder<'a, 'll, 'tcx> = GenericBuilder<'a, 'll, CodegenCx<'ll, 'tcx>>;
pub(crate) type SBuilder<'a, 'll> = GenericBuilder<'a, 'll, SCx<'ll>>;
pub(crate) type Builder<'a, 'll, 'tcx> = GenericBuilder<'a, 'll, FullCx<'ll, 'tcx>>;
impl<'a, 'll, CX: Borrow<SimpleCx<'ll>>> Drop for GenericBuilder<'a, 'll, CX> {
impl<'a, 'll, CX: Borrow<SCx<'ll>>> Drop for GenericBuilder<'a, 'll, CX> {
fn drop(&mut self) {
unsafe {
llvm::LLVMDisposeBuilder(&mut *(self.llbuilder as *mut _));
@ -57,7 +57,7 @@ impl<'a, 'll, CX: Borrow<SimpleCx<'ll>>> Drop for GenericBuilder<'a, 'll, CX> {
}
impl<'a, 'll> SBuilder<'a, 'll> {
fn call(
pub(crate) fn call(
&mut self,
llty: &'ll Type,
llfn: &'ll Value,
@ -87,79 +87,36 @@ impl<'a, 'll> SBuilder<'a, 'll> {
};
call
}
fn with_scx(scx: &'a SimpleCx<'ll>) -> Self {
// Create a fresh builder from the simple context.
let llbuilder = unsafe { llvm::LLVMCreateBuilderInContext(scx.llcx) };
SBuilder { llbuilder, cx: scx }
}
}
impl<'a, 'll, CX: Borrow<SimpleCx<'ll>>> GenericBuilder<'a, 'll, CX> {
impl<'a, 'll, CX: Borrow<SCx<'ll>>> GenericBuilder<'a, 'll, CX> {
fn with_cx(scx: &'a GenericCx<'ll, CX>) -> Self {
// Create a fresh builder from the simple context.
let llbuilder = unsafe { llvm::LLVMCreateBuilderInContext(scx.deref().borrow().llcx) };
GenericBuilder { llbuilder, cx: scx }
}
pub(crate) fn bitcast(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> &'ll Value {
unsafe { llvm::LLVMBuildBitCast(self.llbuilder, val, dest_ty, UNNAMED) }
}
fn ret_void(&mut self) {
unsafe {
llvm::LLVMBuildRetVoid(self.llbuilder);
}
pub(crate) fn ret_void(&mut self) {
llvm::LLVMBuildRetVoid(self.llbuilder);
}
fn ret(&mut self, v: &'ll Value) {
pub(crate) fn ret(&mut self, v: &'ll Value) {
unsafe {
llvm::LLVMBuildRet(self.llbuilder, v);
}
}
}
impl<'a, 'll> SBuilder<'a, 'll> {
fn build(cx: &'a SimpleCx<'ll>, llbb: &'ll BasicBlock) -> SBuilder<'a, 'll> {
let bx = SBuilder::with_scx(cx);
pub(crate) fn build(cx: &'a GenericCx<'ll, CX>, llbb: &'ll BasicBlock) -> Self {
let bx = Self::with_cx(cx);
unsafe {
llvm::LLVMPositionBuilderAtEnd(bx.llbuilder, llbb);
}
bx
}
fn check_call<'b>(
&mut self,
typ: &str,
fn_ty: &'ll Type,
llfn: &'ll Value,
args: &'b [&'ll Value],
) -> Cow<'b, [&'ll Value]> {
assert!(
self.cx.type_kind(fn_ty) == TypeKind::Function,
"builder::{typ} not passed a function, but {fn_ty:?}"
);
let param_tys = self.cx.func_params_types(fn_ty);
let all_args_match = iter::zip(&param_tys, args.iter().map(|&v| self.cx.val_ty(v)))
.all(|(expected_ty, actual_ty)| *expected_ty == actual_ty);
if all_args_match {
return Cow::Borrowed(args);
}
let casted_args: Vec<_> = iter::zip(param_tys, args)
.enumerate()
.map(|(i, (expected_ty, &actual_val))| {
let actual_ty = self.cx.val_ty(actual_val);
if expected_ty != actual_ty {
debug!(
"type mismatch in function call of {:?}. \
Expected {:?} for param {}, got {:?}; injecting bitcast",
llfn, expected_ty, i, actual_ty
);
self.bitcast(actual_val, expected_ty)
} else {
actual_val
}
})
.collect();
Cow::Owned(casted_args)
}
}
/// Empty string, to be used where LLVM expects an instruction name, indicating
@ -167,17 +124,17 @@ impl<'a, 'll> SBuilder<'a, 'll> {
// FIXME(eddyb) pass `&CStr` directly to FFI once it's a thin pointer.
const UNNAMED: *const c_char = c"".as_ptr();
impl<'ll, 'tcx> BackendTypes for Builder<'_, 'll, 'tcx> {
type Value = <CodegenCx<'ll, 'tcx> as BackendTypes>::Value;
type Metadata = <CodegenCx<'ll, 'tcx> as BackendTypes>::Metadata;
type Function = <CodegenCx<'ll, 'tcx> as BackendTypes>::Function;
type BasicBlock = <CodegenCx<'ll, 'tcx> as BackendTypes>::BasicBlock;
type Type = <CodegenCx<'ll, 'tcx> as BackendTypes>::Type;
type Funclet = <CodegenCx<'ll, 'tcx> as BackendTypes>::Funclet;
impl<'ll, CX: Borrow<SCx<'ll>>> BackendTypes for GenericBuilder<'_, 'll, CX> {
type Value = <GenericCx<'ll, CX> as BackendTypes>::Value;
type Metadata = <GenericCx<'ll, CX> as BackendTypes>::Metadata;
type Function = <GenericCx<'ll, CX> as BackendTypes>::Function;
type BasicBlock = <GenericCx<'ll, CX> as BackendTypes>::BasicBlock;
type Type = <GenericCx<'ll, CX> as BackendTypes>::Type;
type Funclet = <GenericCx<'ll, CX> as BackendTypes>::Funclet;
type DIScope = <CodegenCx<'ll, 'tcx> as BackendTypes>::DIScope;
type DILocation = <CodegenCx<'ll, 'tcx> as BackendTypes>::DILocation;
type DIVariable = <CodegenCx<'ll, 'tcx> as BackendTypes>::DIVariable;
type DIScope = <GenericCx<'ll, CX> as BackendTypes>::DIScope;
type DILocation = <GenericCx<'ll, CX> as BackendTypes>::DILocation;
type DIVariable = <GenericCx<'ll, CX> as BackendTypes>::DIVariable;
}
impl abi::HasDataLayout for Builder<'_, '_, '_> {
@ -293,9 +250,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
}
fn ret_void(&mut self) {
unsafe {
llvm::LLVMBuildRetVoid(self.llbuilder);
}
llvm::LLVMBuildRetVoid(self.llbuilder);
}
fn ret(&mut self, v: &'ll Value) {
@ -356,8 +311,8 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
// This function handles switch instructions with more than 2 targets and it needs to
// emit branch weights metadata instead of using the intrinsic.
// The values 1 and 2000 are the same as the values used by the `llvm.expect` intrinsic.
let cold_weight = unsafe { llvm::LLVMValueAsMetadata(self.cx.const_u32(1)) };
let hot_weight = unsafe { llvm::LLVMValueAsMetadata(self.cx.const_u32(2000)) };
let cold_weight = llvm::LLVMValueAsMetadata(self.cx.const_u32(1));
let hot_weight = llvm::LLVMValueAsMetadata(self.cx.const_u32(2000));
let weight =
|is_cold: bool| -> &Metadata { if is_cold { cold_weight } else { hot_weight } };
@ -1476,26 +1431,12 @@ impl<'ll> StaticBuilderMethods for Builder<'_, 'll, '_> {
}
impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
fn build(cx: &'a CodegenCx<'ll, 'tcx>, llbb: &'ll BasicBlock) -> Builder<'a, 'll, 'tcx> {
let bx = Builder::with_cx(cx);
unsafe {
llvm::LLVMPositionBuilderAtEnd(bx.llbuilder, llbb);
}
bx
}
fn with_cx(cx: &'a CodegenCx<'ll, 'tcx>) -> Self {
// Create a fresh builder from the crate context.
let llbuilder = unsafe { llvm::LLVMCreateBuilderInContext(cx.llcx) };
Builder { llbuilder, cx }
}
pub(crate) fn llfn(&self) -> &'ll Value {
unsafe { llvm::LLVMGetBasicBlockParent(self.llbb()) }
}
}
impl<'a, 'll, CX: Borrow<SimpleCx<'ll>>> GenericBuilder<'a, 'll, CX> {
impl<'a, 'll, CX: Borrow<SCx<'ll>>> GenericBuilder<'a, 'll, CX> {
fn position_at_start(&mut self, llbb: &'ll BasicBlock) {
unsafe {
llvm::LLVMRustPositionBuilderAtStart(self.llbuilder, llbb);
@ -1525,7 +1466,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
}
}
}
impl<'a, 'll, CX: Borrow<SimpleCx<'ll>>> GenericBuilder<'a, 'll, CX> {
impl<'a, 'll, CX: Borrow<SCx<'ll>>> GenericBuilder<'a, 'll, CX> {
pub(crate) fn minnum(&mut self, lhs: &'ll Value, rhs: &'ll Value) -> &'ll Value {
unsafe { llvm::LLVMRustBuildMinNum(self.llbuilder, lhs, rhs) }
}
@ -1626,9 +1567,7 @@ impl<'a, 'll, CX: Borrow<SimpleCx<'ll>>> GenericBuilder<'a, 'll, CX> {
let ret = unsafe { llvm::LLVMBuildCatchRet(self.llbuilder, funclet.cleanuppad(), unwind) };
ret.expect("LLVM does not have support for catchret")
}
}
impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
fn check_call<'b>(
&mut self,
typ: &str,
@ -1643,7 +1582,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
let param_tys = self.cx.func_params_types(fn_ty);
let all_args_match = iter::zip(&param_tys, args.iter().map(|&v| self.val_ty(v)))
let all_args_match = iter::zip(&param_tys, args.iter().map(|&v| self.cx.val_ty(v)))
.all(|(expected_ty, actual_ty)| *expected_ty == actual_ty);
if all_args_match {
@ -1653,7 +1592,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
let casted_args: Vec<_> = iter::zip(param_tys, args)
.enumerate()
.map(|(i, (expected_ty, &actual_val))| {
let actual_ty = self.val_ty(actual_val);
let actual_ty = self.cx.val_ty(actual_val);
if expected_ty != actual_ty {
debug!(
"type mismatch in function call of {:?}. \
@ -1669,12 +1608,12 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
Cow::Owned(casted_args)
}
}
impl<'a, 'll, CX: Borrow<SimpleCx<'ll>>> GenericBuilder<'a, 'll, CX> {
pub(crate) fn va_arg(&mut self, list: &'ll Value, ty: &'ll Type) -> &'ll Value {
unsafe { llvm::LLVMBuildVAArg(self.llbuilder, list, ty, UNNAMED) }
}
}
impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
pub(crate) fn call_intrinsic(&mut self, intrinsic: &str, args: &[&'ll Value]) -> &'ll Value {
let (ty, f) = self.cx.get_intrinsic(intrinsic);
@ -1694,7 +1633,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
self.call_intrinsic(intrinsic, &[self.cx.const_u64(size), ptr]);
}
}
impl<'a, 'll, CX: Borrow<SimpleCx<'ll>>> GenericBuilder<'a, 'll, CX> {
impl<'a, 'll, CX: Borrow<SCx<'ll>>> GenericBuilder<'a, 'll, CX> {
pub(crate) fn phi(
&mut self,
ty: &'ll Type,

View File

@ -3,6 +3,7 @@ use std::ptr;
use rustc_ast::expand::autodiff_attrs::{AutoDiffAttrs, AutoDiffItem, DiffActivity, DiffMode};
use rustc_codegen_ssa::ModuleCodegen;
use rustc_codegen_ssa::back::write::ModuleConfig;
use rustc_codegen_ssa::traits::BaseTypeCodegenMethods as _;
use rustc_errors::FatalError;
use tracing::{debug, trace};
@ -286,7 +287,8 @@ pub(crate) fn differentiate<'ll>(
}
let diag_handler = cgcx.create_dcx();
let cx = SimpleCx { llmod: module.module_llvm.llmod(), llcx: module.module_llvm.llcx };
let cx = SimpleCx::new(module.module_llvm.llmod(), module.module_llvm.llcx, cgcx.pointer_size);
// First of all, did the user try to use autodiff without using the -Zautodiff=Enable flag?
if !diff_items.is_empty()

View File

@ -1,5 +1,7 @@
//! Code that is useful in various codegen modules.
use std::borrow::Borrow;
use libc::{c_char, c_uint};
use rustc_abi as abi;
use rustc_abi::Primitive::Pointer;
@ -18,6 +20,7 @@ use tracing::debug;
use crate::consts::const_alloc_to_llvm;
pub(crate) use crate::context::CodegenCx;
use crate::context::{GenericCx, SCx};
use crate::llvm::{self, BasicBlock, Bool, ConstantInt, False, Metadata, True};
use crate::type_::Type;
use crate::value::Value;
@ -81,7 +84,7 @@ impl<'ll> Funclet<'ll> {
}
}
impl<'ll> BackendTypes for CodegenCx<'ll, '_> {
impl<'ll, CX: Borrow<SCx<'ll>>> BackendTypes for GenericCx<'ll, CX> {
type Value = &'ll Value;
type Metadata = &'ll Metadata;
// FIXME(eddyb) replace this with a `Function` "subclass" of `Value`.
@ -118,7 +121,7 @@ impl<'ll> CodegenCx<'ll, '_> {
}
}
impl<'ll, 'tcx> ConstCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> {
impl<'ll, 'tcx> ConstCodegenMethods for CodegenCx<'ll, 'tcx> {
fn const_null(&self, t: &'ll Type) -> &'ll Value {
unsafe { llvm::LLVMConstNull(t) }
}
@ -127,10 +130,6 @@ impl<'ll, 'tcx> ConstCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> {
unsafe { llvm::LLVMGetUndef(t) }
}
fn is_undef(&self, v: &'ll Value) -> bool {
unsafe { llvm::LLVMIsUndef(v) == True }
}
fn const_poison(&self, t: &'ll Type) -> &'ll Value {
unsafe { llvm::LLVMGetPoison(t) }
}
@ -209,28 +208,24 @@ impl<'ll, 'tcx> ConstCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> {
}
fn const_str(&self, s: &str) -> (&'ll Value, &'ll Value) {
let str_global = *self
.const_str_cache
.borrow_mut()
.raw_entry_mut()
.from_key(s)
.or_insert_with(|| {
let sc = self.const_bytes(s.as_bytes());
let sym = self.generate_local_symbol_name("str");
let g = self.define_global(&sym, self.val_ty(sc)).unwrap_or_else(|| {
bug!("symbol `{}` is already defined", sym);
});
llvm::set_initializer(g, sc);
unsafe {
llvm::LLVMSetGlobalConstant(g, True);
llvm::LLVMSetUnnamedAddress(g, llvm::UnnamedAddr::Global);
}
llvm::set_linkage(g, llvm::Linkage::InternalLinkage);
// Cast to default address space if globals are in a different addrspace
let g = self.const_pointercast(g, self.type_ptr());
(s.to_owned(), g)
})
.1;
let mut const_str_cache = self.const_str_cache.borrow_mut();
let str_global = const_str_cache.get(s).copied().unwrap_or_else(|| {
let sc = self.const_bytes(s.as_bytes());
let sym = self.generate_local_symbol_name("str");
let g = self.define_global(&sym, self.val_ty(sc)).unwrap_or_else(|| {
bug!("symbol `{}` is already defined", sym);
});
llvm::set_initializer(g, sc);
unsafe {
llvm::LLVMSetGlobalConstant(g, True);
llvm::LLVMSetUnnamedAddress(g, llvm::UnnamedAddr::Global);
}
llvm::set_linkage(g, llvm::Linkage::InternalLinkage);
// Cast to default address space if globals are in a different addrspace
let g = self.const_pointercast(g, self.type_ptr());
const_str_cache.insert(s.to_owned(), g);
g
});
let len = s.len();
(str_global, self.const_usize(len as u64))
}
@ -350,7 +345,7 @@ impl<'ll, 'tcx> ConstCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> {
}
}
fn const_data_from_alloc(&self, alloc: ConstAllocation<'tcx>) -> Self::Value {
fn const_data_from_alloc(&self, alloc: ConstAllocation<'_>) -> Self::Value {
const_alloc_to_llvm(self, alloc, /*static*/ false)
}

View File

@ -16,7 +16,6 @@ use rustc_middle::mir::mono::MonoItem;
use rustc_middle::ty::Instance;
use rustc_middle::ty::layout::{HasTypingEnv, LayoutOf};
use rustc_middle::{bug, span_bug};
use rustc_session::config::Lto;
use tracing::{debug, instrument, trace};
use crate::common::{AsCCharPtr, CodegenCx};
@ -344,11 +343,11 @@ impl<'ll> CodegenCx<'ll, '_> {
// Local definitions can never be imported, so we must not apply
// the DLLImport annotation.
&& !dso_local
// ThinLTO can't handle this workaround in all cases, so we don't
// emit the attrs. Instead we make them unnecessary by disallowing
// dynamic linking when linker plugin based LTO is enabled.
&& !self.tcx.sess.opts.cg.linker_plugin_lto.enabled()
&& self.tcx.sess.lto() != Lto::Thin;
// Linker plugin ThinLTO doesn't create the self-dllimport Rust uses for rlibs
// as the code generation happens out of process. Instead we assume static linkage
// and disallow dynamic linking when linker plugin based LTO is enabled.
// Regular in-process ThinLTO doesn't need this workaround.
&& !self.tcx.sess.opts.cg.linker_plugin_lto.enabled();
// If this assertion triggers, there's something wrong with commandline
// argument validation.
@ -490,7 +489,7 @@ impl<'ll> CodegenCx<'ll, '_> {
llvm::LLVMMDStringInContext2(self.llcx, bytes.as_c_char_ptr(), bytes.len());
let data = [section, alloc];
let meta = llvm::LLVMMDNodeInContext2(self.llcx, data.as_ptr(), data.len());
let val = llvm::LLVMMetadataAsValue(self.llcx, meta);
let val = self.get_metadata_value(meta);
llvm::LLVMAddNamedMetadataOperand(
self.llmod,
c"wasm.custom_sections".as_ptr(),

View File

@ -1,13 +1,13 @@
use std::borrow::Borrow;
use std::cell::{Cell, RefCell};
use std::ffi::{CStr, c_char, c_uint};
use std::marker::PhantomData;
use std::ops::Deref;
use std::str;
use rustc_abi::{HasDataLayout, TargetDataLayout, VariantIdx};
use rustc_abi::{HasDataLayout, Size, TargetDataLayout, VariantIdx};
use rustc_codegen_ssa::back::versioned_llvm_target;
use rustc_codegen_ssa::base::{wants_msvc_seh, wants_wasm_eh};
use rustc_codegen_ssa::common::TypeKind;
use rustc_codegen_ssa::errors as ssa_errors;
use rustc_codegen_ssa::traits::*;
use rustc_data_structures::base_n::{ALPHANUMERIC_ONLY, ToBaseN};
@ -32,9 +32,9 @@ use smallvec::SmallVec;
use crate::back::write::to_llvm_code_model;
use crate::callee::get_fn;
use crate::common::{self, AsCCharPtr};
use crate::common::AsCCharPtr;
use crate::debuginfo::metadata::apply_vcall_visibility_metadata;
use crate::llvm::{Metadata, MetadataType};
use crate::llvm::Metadata;
use crate::type_::Type;
use crate::value::Value;
use crate::{attributes, coverageinfo, debuginfo, llvm, llvm_util};
@ -43,18 +43,19 @@ use crate::{attributes, coverageinfo, debuginfo, llvm, llvm_util};
/// However, there are various cx related functions which we want to be available to the builder and
/// other compiler pieces. Here we define a small subset which has enough information and can be
/// moved around more freely.
pub(crate) struct SimpleCx<'ll> {
pub(crate) struct SCx<'ll> {
pub llmod: &'ll llvm::Module,
pub llcx: &'ll llvm::Context,
pub isize_ty: &'ll Type,
}
impl<'ll> Borrow<SimpleCx<'ll>> for CodegenCx<'ll, '_> {
fn borrow(&self) -> &SimpleCx<'ll> {
impl<'ll> Borrow<SCx<'ll>> for FullCx<'ll, '_> {
fn borrow(&self) -> &SCx<'ll> {
&self.scx
}
}
impl<'ll, 'tcx> Deref for CodegenCx<'ll, 'tcx> {
impl<'ll, 'tcx> Deref for FullCx<'ll, 'tcx> {
type Target = SimpleCx<'ll>;
#[inline]
@ -63,10 +64,25 @@ impl<'ll, 'tcx> Deref for CodegenCx<'ll, 'tcx> {
}
}
pub(crate) struct GenericCx<'ll, T: Borrow<SCx<'ll>>>(T, PhantomData<SCx<'ll>>);
impl<'ll, T: Borrow<SCx<'ll>>> Deref for GenericCx<'ll, T> {
type Target = T;
#[inline]
fn deref(&self) -> &Self::Target {
&self.0
}
}
pub(crate) type SimpleCx<'ll> = GenericCx<'ll, SCx<'ll>>;
/// There is one `CodegenCx` per codegen unit. Each one has its own LLVM
/// `llvm::Context` so that several codegen units may be processed in parallel.
/// All other LLVM data structures in the `CodegenCx` are tied to that `llvm::Context`.
pub(crate) struct CodegenCx<'ll, 'tcx> {
pub(crate) type CodegenCx<'ll, 'tcx> = GenericCx<'ll, FullCx<'ll, 'tcx>>;
pub(crate) struct FullCx<'ll, 'tcx> {
pub tcx: TyCtxt<'tcx>,
pub scx: SimpleCx<'ll>,
pub use_dll_storage_attrs: bool,
@ -104,8 +120,6 @@ pub(crate) struct CodegenCx<'ll, 'tcx> {
/// Mapping of scalar types to llvm types.
pub scalar_lltypes: RefCell<FxHashMap<Ty<'tcx>, &'ll Type>>,
pub isize_ty: &'ll Type,
/// Extra per-CGU codegen state needed when coverage instrumentation is enabled.
pub coverage_cx: Option<coverageinfo::CguCoverageContext<'ll, 'tcx>>,
pub dbg_cx: Option<debuginfo::CodegenUnitDebugContext<'ll, 'tcx>>,
@ -579,33 +593,33 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
None
};
let isize_ty = Type::ix_llcx(llcx, tcx.data_layout.pointer_size.bits());
CodegenCx {
tcx,
scx: SimpleCx { llcx, llmod },
use_dll_storage_attrs,
tls_model,
codegen_unit,
instances: Default::default(),
vtables: Default::default(),
const_str_cache: Default::default(),
const_globals: Default::default(),
statics_to_rauw: RefCell::new(Vec::new()),
used_statics: RefCell::new(Vec::new()),
compiler_used_statics: RefCell::new(Vec::new()),
type_lowering: Default::default(),
scalar_lltypes: Default::default(),
isize_ty,
coverage_cx,
dbg_cx,
eh_personality: Cell::new(None),
eh_catch_typeinfo: Cell::new(None),
rust_try_fn: Cell::new(None),
intrinsics: Default::default(),
local_gen_sym_counter: Cell::new(0),
renamed_statics: Default::default(),
}
GenericCx(
FullCx {
tcx,
scx: SimpleCx::new(llmod, llcx, tcx.data_layout.pointer_size),
use_dll_storage_attrs,
tls_model,
codegen_unit,
instances: Default::default(),
vtables: Default::default(),
const_str_cache: Default::default(),
const_globals: Default::default(),
statics_to_rauw: RefCell::new(Vec::new()),
used_statics: RefCell::new(Vec::new()),
compiler_used_statics: RefCell::new(Vec::new()),
type_lowering: Default::default(),
scalar_lltypes: Default::default(),
coverage_cx,
dbg_cx,
eh_personality: Cell::new(None),
eh_catch_typeinfo: Cell::new(None),
rust_try_fn: Cell::new(None),
intrinsics: Default::default(),
local_gen_sym_counter: Cell::new(0),
renamed_statics: Default::default(),
},
PhantomData,
)
}
pub(crate) fn statics_to_rauw(&self) -> &RefCell<Vec<(&'ll Value, &'ll Value)>> {
@ -628,24 +642,32 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
llvm::set_section(g, c"llvm.metadata");
}
}
impl<'ll> SimpleCx<'ll> {
pub(crate) fn val_ty(&self, v: &'ll Value) -> &'ll Type {
common::val_ty(v)
}
impl<'ll> SimpleCx<'ll> {
pub(crate) fn new(
llmod: &'ll llvm::Module,
llcx: &'ll llvm::Context,
pointer_size: Size,
) -> Self {
let isize_ty = llvm::Type::ix_llcx(llcx, pointer_size.bits());
Self(SCx { llmod, llcx, isize_ty }, PhantomData)
}
}
impl<'ll, CX: Borrow<SCx<'ll>>> GenericCx<'ll, CX> {
pub(crate) fn get_metadata_value(&self, metadata: &'ll Metadata) -> &'ll Value {
unsafe { llvm::LLVMMetadataAsValue(self.llcx, metadata) }
llvm::LLVMMetadataAsValue(self.llcx(), metadata)
}
pub(crate) fn get_function(&self, name: &str) -> Option<&'ll Value> {
let name = SmallCStr::new(name);
unsafe { llvm::LLVMGetNamedFunction(self.llmod, name.as_ptr()) }
unsafe { llvm::LLVMGetNamedFunction((**self).borrow().llmod, name.as_ptr()) }
}
pub(crate) fn get_md_kind_id(&self, name: &str) -> u32 {
pub(crate) fn get_md_kind_id(&self, name: &str) -> llvm::MetadataKindId {
unsafe {
llvm::LLVMGetMDKindIDInContext(
self.llcx,
self.llcx(),
name.as_ptr() as *const c_char,
name.len() as c_uint,
)
@ -654,13 +676,9 @@ impl<'ll> SimpleCx<'ll> {
pub(crate) fn create_metadata(&self, name: String) -> Option<&'ll Metadata> {
Some(unsafe {
llvm::LLVMMDStringInContext2(self.llcx, name.as_ptr() as *const c_char, name.len())
llvm::LLVMMDStringInContext2(self.llcx(), name.as_ptr() as *const c_char, name.len())
})
}
pub(crate) fn type_kind(&self, ty: &'ll Type) -> TypeKind {
unsafe { llvm::LLVMRustGetTypeKind(ty).to_generic() }
}
}
impl<'ll, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> {
@ -1203,27 +1221,18 @@ impl CodegenCx<'_, '_> {
name.push_str(&(idx as u64).to_base(ALPHANUMERIC_ONLY));
name
}
/// A wrapper for [`llvm::LLVMSetMetadata`], but it takes `Metadata` as a parameter instead of `Value`.
pub(crate) fn set_metadata<'a>(&self, val: &'a Value, kind_id: MetadataType, md: &'a Metadata) {
unsafe {
let node = llvm::LLVMMetadataAsValue(&self.llcx, md);
llvm::LLVMSetMetadata(val, kind_id as c_uint, node);
}
}
}
// This is a duplication of the set_metadata function above. However, so far it's the only one
// shared between both contexts, so it doesn't seem worth it to make the Cx generic like we did it
// for the Builder.
impl SimpleCx<'_> {
#[allow(unused)]
impl<'ll, CX: Borrow<SCx<'ll>>> GenericCx<'ll, CX> {
/// A wrapper for [`llvm::LLVMSetMetadata`], but it takes `Metadata` as a parameter instead of `Value`.
pub(crate) fn set_metadata<'a>(&self, val: &'a Value, kind_id: MetadataType, md: &'a Metadata) {
unsafe {
let node = llvm::LLVMMetadataAsValue(&self.llcx, md);
llvm::LLVMSetMetadata(val, kind_id as c_uint, node);
}
pub(crate) fn set_metadata<'a>(
&self,
val: &'a Value,
kind_id: impl Into<llvm::MetadataKindId>,
md: &'ll Metadata,
) {
let node = self.get_metadata_value(md);
llvm::LLVMSetMetadata(val, kind_id.into(), node);
}
}

View File

@ -8,7 +8,7 @@ use std::ffi::CString;
use rustc_abi::Align;
use rustc_codegen_ssa::traits::{
BaseTypeCodegenMethods, ConstCodegenMethods, StaticCodegenMethods,
BaseTypeCodegenMethods as _, ConstCodegenMethods, StaticCodegenMethods,
};
use rustc_middle::mir::coverage::{
BasicCoverageBlock, CovTerm, CoverageIdsInfo, Expression, FunctionCoverageInfo, Mapping,

View File

@ -11,6 +11,8 @@
//! * Use define_* family of methods when you might be defining the Value.
//! * When in doubt, define.
use std::borrow::Borrow;
use itertools::Itertools;
use rustc_codegen_ssa::traits::TypeMembershipCodegenMethods;
use rustc_data_structures::fx::FxIndexSet;
@ -22,7 +24,7 @@ use tracing::debug;
use crate::abi::FnAbiLlvmExt;
use crate::common::AsCCharPtr;
use crate::context::{CodegenCx, SimpleCx};
use crate::context::{CodegenCx, GenericCx, SCx, SimpleCx};
use crate::llvm::AttributePlace::Function;
use crate::llvm::Visibility;
use crate::type_::Type;
@ -81,16 +83,25 @@ pub(crate) fn declare_raw_fn<'ll, 'tcx>(
llfn
}
impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
impl<'ll, CX: Borrow<SCx<'ll>>> GenericCx<'ll, CX> {
/// Declare a global value.
///
/// If theres a value with the same name already declared, the function will
/// return its Value instead.
pub(crate) fn declare_global(&self, name: &str, ty: &'ll Type) -> &'ll Value {
debug!("declare_global(name={:?})", name);
unsafe { llvm::LLVMRustGetOrInsertGlobal(self.llmod, name.as_c_char_ptr(), name.len(), ty) }
unsafe {
llvm::LLVMRustGetOrInsertGlobal(
(**self).borrow().llmod,
name.as_c_char_ptr(),
name.len(),
ty,
)
}
}
}
impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
/// Declare a C ABI function.
///
/// Only use this for foreign function ABIs and glue. For Rust functions use

View File

@ -649,7 +649,7 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
fn type_test(&mut self, pointer: Self::Value, typeid: Self::Metadata) -> Self::Value {
// Test the called operand using llvm.type.test intrinsic. The LowerTypeTests link-time
// optimization pass replaces calls to this intrinsic with code to test type membership.
let typeid = unsafe { llvm::LLVMMetadataAsValue(&self.llcx, typeid) };
let typeid = self.get_metadata_value(typeid);
self.call_intrinsic("llvm.type.test", &[pointer, typeid])
}
@ -659,7 +659,7 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
vtable_byte_offset: u64,
typeid: &'ll Metadata,
) -> Self::Value {
let typeid = unsafe { llvm::LLVMMetadataAsValue(&self.llcx, typeid) };
let typeid = self.get_metadata_value(typeid);
let vtable_byte_offset = self.const_i32(vtable_byte_offset as i32);
let type_checked_load =
self.call_intrinsic("llvm.type.checked.load", &[llvtable, vtable_byte_offset, typeid]);

View File

@ -12,7 +12,6 @@
#![feature(exact_size_is_empty)]
#![feature(extern_types)]
#![feature(file_buffered)]
#![feature(hash_raw_entry)]
#![feature(if_let_guard)]
#![feature(impl_trait_in_assoc_type)]
#![feature(iter_intersperse)]
@ -20,7 +19,6 @@
#![feature(rustdoc_internals)]
#![feature(slice_as_array)]
#![feature(try_blocks)]
#![warn(unreachable_pub)]
// tidy-alphabetical-end
use std::any::Any;
@ -29,6 +27,7 @@ use std::mem::ManuallyDrop;
use back::owned_target_machine::OwnedTargetMachine;
use back::write::{create_informational_target_machine, create_target_machine};
use context::SimpleCx;
use errors::{AutoDiffWithoutLTO, ParseTargetMachineConfig};
pub(crate) use llvm_util::target_features_cfg;
use rustc_ast::expand::allocator::AllocatorKind;
@ -116,9 +115,11 @@ impl ExtraBackendMethods for LlvmCodegenBackend {
kind: AllocatorKind,
alloc_error_handler_kind: AllocatorKind,
) -> ModuleLlvm {
let mut module_llvm = ModuleLlvm::new_metadata(tcx, module_name);
let module_llvm = ModuleLlvm::new_metadata(tcx, module_name);
let cx =
SimpleCx::new(module_llvm.llmod(), &module_llvm.llcx, tcx.data_layout.pointer_size);
unsafe {
allocator::codegen(tcx, &mut module_llvm, module_name, kind, alloc_error_handler_kind);
allocator::codegen(tcx, cx, module_name, kind, alloc_error_handler_kind);
}
module_llvm
}

View File

@ -3,13 +3,14 @@
use libc::{c_char, c_uint};
use super::MetadataKindId;
use super::ffi::{BasicBlock, Metadata, Module, Type, Value};
use crate::llvm::Bool;
#[link(name = "llvm-wrapper", kind = "static")]
unsafe extern "C" {
// Enzyme
pub(crate) fn LLVMRustHasMetadata(I: &Value, KindID: c_uint) -> bool;
pub(crate) safe fn LLVMRustHasMetadata(I: &Value, KindID: MetadataKindId) -> bool;
pub(crate) fn LLVMRustEraseInstUntilInclusive(BB: &BasicBlock, I: &Value);
pub(crate) fn LLVMRustGetLastInstruction<'a>(BB: &BasicBlock) -> Option<&'a Value>;
pub(crate) fn LLVMRustDIGetInstMetadata(I: &Value) -> Option<&Metadata>;

View File

@ -976,6 +976,16 @@ pub type SelfProfileAfterPassCallback = unsafe extern "C" fn(*mut c_void);
pub type GetSymbolsCallback = unsafe extern "C" fn(*mut c_void, *const c_char) -> *mut c_void;
pub type GetSymbolsErrorCallback = unsafe extern "C" fn(*const c_char) -> *mut c_void;
#[derive(Copy, Clone)]
#[repr(transparent)]
pub struct MetadataKindId(c_uint);
impl From<MetadataType> for MetadataKindId {
fn from(value: MetadataType) -> Self {
Self(value as c_uint)
}
}
unsafe extern "C" {
// Create and destroy contexts.
pub(crate) fn LLVMContextDispose(C: &'static mut Context);
@ -983,7 +993,7 @@ unsafe extern "C" {
C: &Context,
Name: *const c_char,
SLen: c_uint,
) -> c_uint;
) -> MetadataKindId;
// Create modules.
pub(crate) fn LLVMModuleCreateWithNameInContext(
@ -1046,14 +1056,13 @@ unsafe extern "C" {
pub(crate) fn LLVMMetadataTypeInContext(C: &Context) -> &Type;
// Operations on all values
pub(crate) fn LLVMIsUndef(Val: &Value) -> Bool;
pub(crate) fn LLVMTypeOf(Val: &Value) -> &Type;
pub(crate) fn LLVMGetValueName2(Val: &Value, Length: *mut size_t) -> *const c_char;
pub(crate) fn LLVMSetValueName2(Val: &Value, Name: *const c_char, NameLen: size_t);
pub(crate) fn LLVMReplaceAllUsesWith<'a>(OldVal: &'a Value, NewVal: &'a Value);
pub(crate) fn LLVMSetMetadata<'a>(Val: &'a Value, KindID: c_uint, Node: &'a Value);
pub(crate) safe fn LLVMSetMetadata<'a>(Val: &'a Value, KindID: MetadataKindId, Node: &'a Value);
pub(crate) fn LLVMGlobalSetMetadata<'a>(Val: &'a Value, KindID: c_uint, Metadata: &'a Metadata);
pub(crate) fn LLVMValueAsMetadata(Node: &Value) -> &Metadata;
pub(crate) safe fn LLVMValueAsMetadata(Node: &Value) -> &Metadata;
// Operations on constants of any type
pub(crate) fn LLVMConstNull(Ty: &Type) -> &Value;
@ -1147,7 +1156,7 @@ unsafe extern "C" {
pub(crate) fn LLVMSetThreadLocalMode(GlobalVar: &Value, Mode: ThreadLocalMode);
pub(crate) fn LLVMIsGlobalConstant(GlobalVar: &Value) -> Bool;
pub(crate) fn LLVMSetGlobalConstant(GlobalVar: &Value, IsConstant: Bool);
pub(crate) fn LLVMSetTailCall(CallInst: &Value, IsTailCall: Bool);
pub(crate) safe fn LLVMSetTailCall(CallInst: &Value, IsTailCall: Bool);
// Operations on attributes
pub(crate) fn LLVMCreateStringAttribute(
@ -1204,7 +1213,7 @@ unsafe extern "C" {
pub(crate) fn LLVMGetCurrentDebugLocation2<'a>(Builder: &Builder<'a>) -> Option<&'a Metadata>;
// Terminators
pub(crate) fn LLVMBuildRetVoid<'a>(B: &Builder<'a>) -> &'a Value;
pub(crate) safe fn LLVMBuildRetVoid<'a>(B: &Builder<'a>) -> &'a Value;
pub(crate) fn LLVMBuildRet<'a>(B: &Builder<'a>, V: &'a Value) -> &'a Value;
pub(crate) fn LLVMBuildBr<'a>(B: &Builder<'a>, Dest: &'a BasicBlock) -> &'a Value;
pub(crate) fn LLVMBuildCondBr<'a>(
@ -1680,7 +1689,7 @@ unsafe extern "C" {
Packed: Bool,
);
pub(crate) fn LLVMMetadataAsValue<'a>(C: &'a Context, MD: &'a Metadata) -> &'a Value;
pub(crate) safe fn LLVMMetadataAsValue<'a>(C: &'a Context, MD: &'a Metadata) -> &'a Value;
pub(crate) fn LLVMSetUnnamedAddress(Global: &Value, UnnamedAddr: UnnamedAddr);

View File

@ -1,3 +1,4 @@
use std::borrow::Borrow;
use std::{fmt, ptr};
use libc::{c_char, c_uint};
@ -11,7 +12,7 @@ use rustc_middle::ty::{self, Ty};
use rustc_target::callconv::{CastTarget, FnAbi};
use crate::abi::{FnAbiLlvmExt, LlvmType};
use crate::context::{CodegenCx, SimpleCx};
use crate::context::{CodegenCx, GenericCx, SCx};
pub(crate) use crate::llvm::Type;
use crate::llvm::{Bool, False, Metadata, True};
use crate::type_of::LayoutLlvmExt;
@ -36,29 +37,29 @@ impl fmt::Debug for Type {
}
impl<'ll> CodegenCx<'ll, '_> {}
impl<'ll> SimpleCx<'ll> {
impl<'ll, CX: Borrow<SCx<'ll>>> GenericCx<'ll, CX> {
pub(crate) fn type_named_struct(&self, name: &str) -> &'ll Type {
let name = SmallCStr::new(name);
unsafe { llvm::LLVMStructCreateNamed(self.llcx, name.as_ptr()) }
unsafe { llvm::LLVMStructCreateNamed(self.llcx(), name.as_ptr()) }
}
pub(crate) fn set_struct_body(&self, ty: &'ll Type, els: &[&'ll Type], packed: bool) {
unsafe { llvm::LLVMStructSetBody(ty, els.as_ptr(), els.len() as c_uint, packed as Bool) }
}
pub(crate) fn type_void(&self) -> &'ll Type {
unsafe { llvm::LLVMVoidTypeInContext(self.llcx) }
unsafe { llvm::LLVMVoidTypeInContext(self.llcx()) }
}
pub(crate) fn type_token(&self) -> &'ll Type {
unsafe { llvm::LLVMTokenTypeInContext(self.llcx) }
unsafe { llvm::LLVMTokenTypeInContext(self.llcx()) }
}
pub(crate) fn type_metadata(&self) -> &'ll Type {
unsafe { llvm::LLVMMetadataTypeInContext(self.llcx) }
unsafe { llvm::LLVMMetadataTypeInContext(self.llcx()) }
}
///x Creates an integer type with the given number of bits, e.g., i24
pub(crate) fn type_ix(&self, num_bits: u64) -> &'ll Type {
unsafe { llvm::LLVMIntTypeInContext(self.llcx, num_bits as c_uint) }
unsafe { llvm::LLVMIntTypeInContext(self.llcx(), num_bits as c_uint) }
}
pub(crate) fn type_vector(&self, ty: &'ll Type, len: u64) -> &'ll Type {
@ -121,19 +122,28 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
self.type_array(self.type_from_integer(unit), size / unit_size)
}
}
impl<'ll> SimpleCx<'ll> {
impl<'ll, CX: Borrow<SCx<'ll>>> GenericCx<'ll, CX> {
pub(crate) fn llcx(&self) -> &'ll llvm::Context {
(**self).borrow().llcx
}
pub(crate) fn isize_ty(&self) -> &'ll Type {
(**self).borrow().isize_ty
}
pub(crate) fn type_variadic_func(&self, args: &[&'ll Type], ret: &'ll Type) -> &'ll Type {
unsafe { llvm::LLVMFunctionType(ret, args.as_ptr(), args.len() as c_uint, True) }
}
pub(crate) fn type_i1(&self) -> &'ll Type {
unsafe { llvm::LLVMInt1TypeInContext(self.llcx) }
unsafe { llvm::LLVMInt1TypeInContext(self.llcx()) }
}
pub(crate) fn type_struct(&self, els: &[&'ll Type], packed: bool) -> &'ll Type {
unsafe {
llvm::LLVMStructTypeInContext(
self.llcx,
self.llcx(),
els.as_ptr(),
els.len() as c_uint,
packed as Bool,
@ -142,45 +152,45 @@ impl<'ll> SimpleCx<'ll> {
}
}
impl<'ll, 'tcx> BaseTypeCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> {
impl<'ll, CX: Borrow<SCx<'ll>>> BaseTypeCodegenMethods for GenericCx<'ll, CX> {
fn type_i8(&self) -> &'ll Type {
unsafe { llvm::LLVMInt8TypeInContext(self.llcx) }
unsafe { llvm::LLVMInt8TypeInContext(self.llcx()) }
}
fn type_i16(&self) -> &'ll Type {
unsafe { llvm::LLVMInt16TypeInContext(self.llcx) }
unsafe { llvm::LLVMInt16TypeInContext(self.llcx()) }
}
fn type_i32(&self) -> &'ll Type {
unsafe { llvm::LLVMInt32TypeInContext(self.llcx) }
unsafe { llvm::LLVMInt32TypeInContext(self.llcx()) }
}
fn type_i64(&self) -> &'ll Type {
unsafe { llvm::LLVMInt64TypeInContext(self.llcx) }
unsafe { llvm::LLVMInt64TypeInContext(self.llcx()) }
}
fn type_i128(&self) -> &'ll Type {
unsafe { llvm::LLVMIntTypeInContext(self.llcx, 128) }
unsafe { llvm::LLVMIntTypeInContext(self.llcx(), 128) }
}
fn type_isize(&self) -> &'ll Type {
self.isize_ty
self.isize_ty()
}
fn type_f16(&self) -> &'ll Type {
unsafe { llvm::LLVMHalfTypeInContext(self.llcx) }
unsafe { llvm::LLVMHalfTypeInContext(self.llcx()) }
}
fn type_f32(&self) -> &'ll Type {
unsafe { llvm::LLVMFloatTypeInContext(self.llcx) }
unsafe { llvm::LLVMFloatTypeInContext(self.llcx()) }
}
fn type_f64(&self) -> &'ll Type {
unsafe { llvm::LLVMDoubleTypeInContext(self.llcx) }
unsafe { llvm::LLVMDoubleTypeInContext(self.llcx()) }
}
fn type_f128(&self) -> &'ll Type {
unsafe { llvm::LLVMFP128TypeInContext(self.llcx) }
unsafe { llvm::LLVMFP128TypeInContext(self.llcx()) }
}
fn type_func(&self, args: &[&'ll Type], ret: &'ll Type) -> &'ll Type {
@ -196,7 +206,7 @@ impl<'ll, 'tcx> BaseTypeCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> {
}
fn type_ptr_ext(&self, address_space: AddressSpace) -> &'ll Type {
unsafe { llvm::LLVMPointerTypeInContext(self.llcx, address_space.0) }
unsafe { llvm::LLVMPointerTypeInContext(self.llcx(), address_space.0) }
}
fn element_type(&self, ty: &'ll Type) -> &'ll Type {

View File

@ -63,3 +63,6 @@ features = ["read_core", "elf", "macho", "pe", "xcoff", "unaligned", "archive",
[target.'cfg(windows)'.dependencies.windows]
version = "0.59.0"
features = ["Win32_Globalization"]
[lints]
workspace = true

View File

@ -119,7 +119,8 @@ codegen_ssa_invalid_monomorphization_inserted_type = invalid monomorphization of
codegen_ssa_invalid_monomorphization_invalid_bitmask = invalid monomorphization of `{$name}` intrinsic: invalid bitmask `{$mask_ty}`, expected `u{$expected_int_bits}` or `[u8; {$expected_bytes}]`
codegen_ssa_invalid_monomorphization_mask_type = invalid monomorphization of `{$name}` intrinsic: mask element type is `{$ty}`, expected `i_`
codegen_ssa_invalid_monomorphization_mask_type = invalid monomorphization of `{$name}` intrinsic: found mask element type is `{$ty}`, expected a signed integer type
.note = the mask may be widened, which only has the correct behavior for signed integers
codegen_ssa_invalid_monomorphization_mismatched_lengths = invalid monomorphization of `{$name}` intrinsic: mismatched lengths: mask length `{$m_len}` != other vector length `{$v_len}`

View File

@ -1,3 +1,5 @@
mod raw_dylib;
use std::collections::BTreeSet;
use std::ffi::OsString;
use std::fs::{File, OpenOptions, read};
@ -12,7 +14,7 @@ use itertools::Itertools;
use regex::Regex;
use rustc_arena::TypedArena;
use rustc_ast::CRATE_NODE_ID;
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
use rustc_data_structures::fx::FxIndexSet;
use rustc_data_structures::memmap::Mmap;
use rustc_data_structures::temp_dir::MaybeTempDir;
use rustc_errors::{DiagCtxtHandle, LintDiagnostic};
@ -30,7 +32,6 @@ use rustc_session::config::{
self, CFGuard, CrateType, DebugInfo, LinkerFeaturesCli, OutFileName, OutputFilenames,
OutputType, PrintKind, SplitDwarfKind, Strip,
};
use rustc_session::cstore::DllImport;
use rustc_session::lint::builtin::LINKER_MESSAGES;
use rustc_session::output::{check_file_is_writeable, invalid_output_for_target, out_filename};
use rustc_session::search_paths::PathKind;
@ -41,22 +42,21 @@ use rustc_session::{Session, filesearch};
use rustc_span::Symbol;
use rustc_target::spec::crt_objects::CrtObjects;
use rustc_target::spec::{
Cc, LinkOutputKind, LinkSelfContainedComponents, LinkSelfContainedDefault, LinkerFeatures,
LinkerFlavor, LinkerFlavorCli, Lld, PanicStrategy, RelocModel, RelroLevel, SanitizerSet,
SplitDebuginfo,
BinaryFormat, Cc, LinkOutputKind, LinkSelfContainedComponents, LinkSelfContainedDefault,
LinkerFeatures, LinkerFlavor, LinkerFlavorCli, Lld, PanicStrategy, RelocModel, RelroLevel,
SanitizerSet, SplitDebuginfo,
};
use tempfile::Builder as TempFileBuilder;
use tracing::{debug, info, warn};
use super::archive::{ArchiveBuilder, ArchiveBuilderBuilder, ImportLibraryItem};
use super::archive::{ArchiveBuilder, ArchiveBuilderBuilder};
use super::command::Command;
use super::linker::{self, Linker};
use super::metadata::{MetadataPosition, create_wrapper_file};
use super::rpath::{self, RPathConfig};
use super::{apple, versioned_llvm_target};
use crate::{
CodegenResults, CompiledModule, CrateInfo, NativeLib, common, errors,
looks_like_rust_object_file,
CodegenResults, CompiledModule, CrateInfo, NativeLib, errors, looks_like_rust_object_file,
};
pub fn ensure_removed(dcx: DiagCtxtHandle<'_>, path: &Path) {
@ -376,16 +376,22 @@ fn link_rlib<'a>(
}
}
for output_path in create_dll_import_libs(
sess,
archive_builder_builder,
codegen_results.crate_info.used_libraries.iter(),
tmpdir.as_ref(),
true,
) {
ab.add_archive(&output_path, Box::new(|_| false)).unwrap_or_else(|error| {
sess.dcx().emit_fatal(errors::AddNativeLibrary { library_path: output_path, error });
});
// On Windows, we add the raw-dylib import libraries to the rlibs already.
// But on ELF, this is not possible, as a shared object cannot be a member of a static library.
// Instead, we add all raw-dylibs to the final link on ELF.
if sess.target.is_like_windows {
for output_path in raw_dylib::create_raw_dylib_dll_import_libs(
sess,
archive_builder_builder,
codegen_results.crate_info.used_libraries.iter(),
tmpdir.as_ref(),
true,
) {
ab.add_archive(&output_path, Box::new(|_| false)).unwrap_or_else(|error| {
sess.dcx()
.emit_fatal(errors::AddNativeLibrary { library_path: output_path, error });
});
}
}
if let Some(trailing_metadata) = trailing_metadata {
@ -426,108 +432,6 @@ fn link_rlib<'a>(
ab
}
/// Extract all symbols defined in raw-dylib libraries, collated by library name.
///
/// If we have multiple extern blocks that specify symbols defined in the same raw-dylib library,
/// then the CodegenResults value contains one NativeLib instance for each block. However, the
/// linker appears to expect only a single import library for each library used, so we need to
/// collate the symbols together by library name before generating the import libraries.
fn collate_raw_dylibs<'a>(
sess: &Session,
used_libraries: impl IntoIterator<Item = &'a NativeLib>,
) -> Vec<(String, Vec<DllImport>)> {
// Use index maps to preserve original order of imports and libraries.
let mut dylib_table = FxIndexMap::<String, FxIndexMap<Symbol, &DllImport>>::default();
for lib in used_libraries {
if lib.kind == NativeLibKind::RawDylib {
let ext = if lib.verbatim { "" } else { ".dll" };
let name = format!("{}{}", lib.name, ext);
let imports = dylib_table.entry(name.clone()).or_default();
for import in &lib.dll_imports {
if let Some(old_import) = imports.insert(import.name, import) {
// FIXME: when we add support for ordinals, figure out if we need to do anything
// if we have two DllImport values with the same name but different ordinals.
if import.calling_convention != old_import.calling_convention {
sess.dcx().emit_err(errors::MultipleExternalFuncDecl {
span: import.span,
function: import.name,
library_name: &name,
});
}
}
}
}
}
sess.dcx().abort_if_errors();
dylib_table
.into_iter()
.map(|(name, imports)| {
(name, imports.into_iter().map(|(_, import)| import.clone()).collect())
})
.collect()
}
fn create_dll_import_libs<'a>(
sess: &Session,
archive_builder_builder: &dyn ArchiveBuilderBuilder,
used_libraries: impl IntoIterator<Item = &'a NativeLib>,
tmpdir: &Path,
is_direct_dependency: bool,
) -> Vec<PathBuf> {
collate_raw_dylibs(sess, used_libraries)
.into_iter()
.map(|(raw_dylib_name, raw_dylib_imports)| {
let name_suffix = if is_direct_dependency { "_imports" } else { "_imports_indirect" };
let output_path = tmpdir.join(format!("{raw_dylib_name}{name_suffix}.lib"));
let mingw_gnu_toolchain = common::is_mingw_gnu_toolchain(&sess.target);
let items: Vec<ImportLibraryItem> = raw_dylib_imports
.iter()
.map(|import: &DllImport| {
if sess.target.arch == "x86" {
ImportLibraryItem {
name: common::i686_decorated_name(
import,
mingw_gnu_toolchain,
false,
false,
),
ordinal: import.ordinal(),
symbol_name: import.is_missing_decorations().then(|| {
common::i686_decorated_name(
import,
mingw_gnu_toolchain,
false,
true,
)
}),
is_data: !import.is_fn,
}
} else {
ImportLibraryItem {
name: import.name.to_string(),
ordinal: import.ordinal(),
symbol_name: None,
is_data: !import.is_fn,
}
}
})
.collect();
archive_builder_builder.create_dll_import_lib(
sess,
&raw_dylib_name,
items,
&output_path,
);
output_path
})
.collect()
}
/// Create a static archive.
///
/// This is essentially the same thing as an rlib, but it also involves adding all of the upstream
@ -1273,7 +1177,7 @@ mod win {
let mut cp: u32 = 0;
// We're using the `LOCALE_RETURN_NUMBER` flag to return a u32.
// But the API requires us to pass the data as though it's a [u16] string.
let len = std::mem::size_of::<u32>() / std::mem::size_of::<u16>();
let len = size_of::<u32>() / size_of::<u16>();
let data = std::slice::from_raw_parts_mut(&mut cp as *mut u32 as *mut u16, len);
let len_written = GetLocaleInfoEx(
LOCALE_NAME_SYSTEM_DEFAULT,
@ -2422,15 +2326,39 @@ fn linker_with_args(
link_output_kind,
);
// Raw-dylibs from all crates.
let raw_dylib_dir = tmpdir.join("raw-dylibs");
if sess.target.binary_format == BinaryFormat::Elf {
// On ELF we can't pass the raw-dylibs stubs to the linker as a path,
// instead we need to pass them via -l. To find the stub, we need to add
// the directory of the stub to the linker search path.
// We make an extra directory for this to avoid polluting the search path.
if let Err(error) = fs::create_dir(&raw_dylib_dir) {
sess.dcx().emit_fatal(errors::CreateTempDir { error })
}
cmd.include_path(&raw_dylib_dir);
}
// Link with the import library generated for any raw-dylib functions.
for output_path in create_dll_import_libs(
sess,
archive_builder_builder,
codegen_results.crate_info.used_libraries.iter(),
tmpdir,
true,
) {
cmd.add_object(&output_path);
if sess.target.is_like_windows {
for output_path in raw_dylib::create_raw_dylib_dll_import_libs(
sess,
archive_builder_builder,
codegen_results.crate_info.used_libraries.iter(),
tmpdir,
true,
) {
cmd.add_object(&output_path);
}
} else {
for link_path in raw_dylib::create_raw_dylib_elf_stub_shared_objects(
sess,
codegen_results.crate_info.used_libraries.iter(),
&raw_dylib_dir,
) {
// Always use verbatim linkage, see comments in create_raw_dylib_elf_stub_shared_objects.
cmd.link_dylib_by_name(&link_path, true, false);
}
}
// As with add_upstream_native_libraries, we need to add the upstream raw-dylib symbols in case
// they are used within inlined functions or instantiated generic functions. We do this *after*
@ -2449,19 +2377,35 @@ fn linker_with_args(
.native_libraries
.iter()
.filter_map(|(&cnum, libraries)| {
(dependency_linkage[cnum] != Linkage::Static).then_some(libraries)
if sess.target.is_like_windows {
(dependency_linkage[cnum] != Linkage::Static).then_some(libraries)
} else {
Some(libraries)
}
})
.flatten()
.collect::<Vec<_>>();
native_libraries_from_nonstatics.sort_unstable_by(|a, b| a.name.as_str().cmp(b.name.as_str()));
for output_path in create_dll_import_libs(
sess,
archive_builder_builder,
native_libraries_from_nonstatics,
tmpdir,
false,
) {
cmd.add_object(&output_path);
if sess.target.is_like_windows {
for output_path in raw_dylib::create_raw_dylib_dll_import_libs(
sess,
archive_builder_builder,
native_libraries_from_nonstatics,
tmpdir,
false,
) {
cmd.add_object(&output_path);
}
} else {
for link_path in raw_dylib::create_raw_dylib_elf_stub_shared_objects(
sess,
native_libraries_from_nonstatics,
&raw_dylib_dir,
) {
// Always use verbatim linkage, see comments in create_raw_dylib_elf_stub_shared_objects.
cmd.link_dylib_by_name(&link_path, true, false);
}
}
// Library linking above uses some global state for things like `-Bstatic`/`-Bdynamic` to make
@ -3438,6 +3382,35 @@ fn add_lld_args(
// this, `wasm-component-ld`, which is overridden if this option is passed.
if !sess.target.is_like_wasm {
cmd.cc_arg("-fuse-ld=lld");
// On ELF platforms like at least x64 linux, GNU ld and LLD have opposite defaults on some
// section garbage-collection features. For example, the somewhat popular `linkme` crate and
// its dependents rely in practice on this difference: when using lld, they need `-z
// nostart-stop-gc` to prevent encapsulation symbols and sections from being
// garbage-collected.
//
// More information about all this can be found in:
// - https://maskray.me/blog/2021-01-31-metadata-sections-comdat-and-shf-link-order
// - https://lld.llvm.org/ELF/start-stop-gc
//
// So when using lld, we restore, for now, the traditional behavior to help migration, but
// will remove it in the future.
// Since this only disables an optimization, it shouldn't create issues, but is in theory
// slightly suboptimal. However, it:
// - doesn't have any visible impact on our benchmarks
// - reduces the need to disable lld for the crates that depend on this
//
// Note that lld can detect some cases where this difference is relied on, and emits a
// dedicated error to add this link arg. We could make use of this error to emit an FCW. As
// of writing this, we don't do it, because lld is already enabled by default on nightly
// without this mitigation: no working project would see the FCW, so we do this to help
// stabilization.
//
// FIXME: emit an FCW if linking fails due its absence, and then remove this link-arg in the
// future.
if sess.target.llvm_target == "x86_64-unknown-linux-gnu" {
cmd.link_arg("-znostart-stop-gc");
}
}
if !flavor.is_gnu() {

View File

@ -0,0 +1,373 @@
use std::fs;
use std::io::{BufWriter, Write};
use std::path::{Path, PathBuf};
use rustc_abi::Endian;
use rustc_data_structures::base_n::{CASE_INSENSITIVE, ToBaseN};
use rustc_data_structures::fx::FxIndexMap;
use rustc_data_structures::stable_hasher::StableHasher;
use rustc_hashes::Hash128;
use rustc_session::Session;
use rustc_session::cstore::DllImport;
use rustc_session::utils::NativeLibKind;
use rustc_span::Symbol;
use crate::back::archive::ImportLibraryItem;
use crate::back::link::ArchiveBuilderBuilder;
use crate::errors::ErrorCreatingImportLibrary;
use crate::{NativeLib, common, errors};
/// Extract all symbols defined in raw-dylib libraries, collated by library name.
///
/// If we have multiple extern blocks that specify symbols defined in the same raw-dylib library,
/// then the CodegenResults value contains one NativeLib instance for each block. However, the
/// linker appears to expect only a single import library for each library used, so we need to
/// collate the symbols together by library name before generating the import libraries.
fn collate_raw_dylibs_windows<'a>(
sess: &Session,
used_libraries: impl IntoIterator<Item = &'a NativeLib>,
) -> Vec<(String, Vec<DllImport>)> {
// Use index maps to preserve original order of imports and libraries.
let mut dylib_table = FxIndexMap::<String, FxIndexMap<Symbol, &DllImport>>::default();
for lib in used_libraries {
if lib.kind == NativeLibKind::RawDylib {
let ext = if lib.verbatim { "" } else { ".dll" };
let name = format!("{}{}", lib.name, ext);
let imports = dylib_table.entry(name.clone()).or_default();
for import in &lib.dll_imports {
if let Some(old_import) = imports.insert(import.name, import) {
// FIXME: when we add support for ordinals, figure out if we need to do anything
// if we have two DllImport values with the same name but different ordinals.
if import.calling_convention != old_import.calling_convention {
sess.dcx().emit_err(errors::MultipleExternalFuncDecl {
span: import.span,
function: import.name,
library_name: &name,
});
}
}
}
}
}
sess.dcx().abort_if_errors();
dylib_table
.into_iter()
.map(|(name, imports)| {
(name, imports.into_iter().map(|(_, import)| import.clone()).collect())
})
.collect()
}
pub(super) fn create_raw_dylib_dll_import_libs<'a>(
sess: &Session,
archive_builder_builder: &dyn ArchiveBuilderBuilder,
used_libraries: impl IntoIterator<Item = &'a NativeLib>,
tmpdir: &Path,
is_direct_dependency: bool,
) -> Vec<PathBuf> {
collate_raw_dylibs_windows(sess, used_libraries)
.into_iter()
.map(|(raw_dylib_name, raw_dylib_imports)| {
let name_suffix = if is_direct_dependency { "_imports" } else { "_imports_indirect" };
let output_path = tmpdir.join(format!("{raw_dylib_name}{name_suffix}.lib"));
let mingw_gnu_toolchain = common::is_mingw_gnu_toolchain(&sess.target);
let items: Vec<ImportLibraryItem> = raw_dylib_imports
.iter()
.map(|import: &DllImport| {
if sess.target.arch == "x86" {
ImportLibraryItem {
name: common::i686_decorated_name(
import,
mingw_gnu_toolchain,
false,
false,
),
ordinal: import.ordinal(),
symbol_name: import.is_missing_decorations().then(|| {
common::i686_decorated_name(
import,
mingw_gnu_toolchain,
false,
true,
)
}),
is_data: !import.is_fn,
}
} else {
ImportLibraryItem {
name: import.name.to_string(),
ordinal: import.ordinal(),
symbol_name: None,
is_data: !import.is_fn,
}
}
})
.collect();
archive_builder_builder.create_dll_import_lib(
sess,
&raw_dylib_name,
items,
&output_path,
);
output_path
})
.collect()
}
/// Extract all symbols defined in raw-dylib libraries, collated by library name.
///
/// If we have multiple extern blocks that specify symbols defined in the same raw-dylib library,
/// then the CodegenResults value contains one NativeLib instance for each block. However, the
/// linker appears to expect only a single import library for each library used, so we need to
/// collate the symbols together by library name before generating the import libraries.
fn collate_raw_dylibs_elf<'a>(
sess: &Session,
used_libraries: impl IntoIterator<Item = &'a NativeLib>,
) -> Vec<(String, Vec<DllImport>)> {
// Use index maps to preserve original order of imports and libraries.
let mut dylib_table = FxIndexMap::<String, FxIndexMap<Symbol, &DllImport>>::default();
for lib in used_libraries {
if lib.kind == NativeLibKind::RawDylib {
let filename = if lib.verbatim {
lib.name.as_str().to_owned()
} else {
let ext = sess.target.dll_suffix.as_ref();
let prefix = sess.target.dll_prefix.as_ref();
format!("{prefix}{}{ext}", lib.name)
};
let imports = dylib_table.entry(filename.clone()).or_default();
for import in &lib.dll_imports {
imports.insert(import.name, import);
}
}
}
sess.dcx().abort_if_errors();
dylib_table
.into_iter()
.map(|(name, imports)| {
(name, imports.into_iter().map(|(_, import)| import.clone()).collect())
})
.collect()
}
pub(super) fn create_raw_dylib_elf_stub_shared_objects<'a>(
sess: &Session,
used_libraries: impl IntoIterator<Item = &'a NativeLib>,
raw_dylib_so_dir: &Path,
) -> Vec<String> {
collate_raw_dylibs_elf(sess, used_libraries)
.into_iter()
.map(|(load_filename, raw_dylib_imports)| {
use std::hash::Hash;
// `load_filename` is the *target/loader* filename that will end up in NEEDED.
// Usually this will be something like `libc.so` or `libc.so.6` but with
// verbatim it might also be an absolute path.
// To be able to support this properly, we always put this load filename
// into the SONAME of the library and link it via a temporary file with a random name.
// This also avoids naming conflicts with non-raw-dylib linkage of the same library.
let shared_object = create_elf_raw_dylib_stub(sess, &load_filename, &raw_dylib_imports);
let mut file_name_hasher = StableHasher::new();
load_filename.hash(&mut file_name_hasher);
for raw_dylib in raw_dylib_imports {
raw_dylib.name.as_str().hash(&mut file_name_hasher);
}
let library_filename: Hash128 = file_name_hasher.finish();
let temporary_lib_name = format!(
"{}{}{}",
sess.target.dll_prefix,
library_filename.as_u128().to_base_fixed_len(CASE_INSENSITIVE),
sess.target.dll_suffix
);
let link_path = raw_dylib_so_dir.join(&temporary_lib_name);
let file = match fs::File::create_new(&link_path) {
Ok(file) => file,
Err(error) => sess.dcx().emit_fatal(ErrorCreatingImportLibrary {
lib_name: &load_filename,
error: error.to_string(),
}),
};
if let Err(error) = BufWriter::new(file).write_all(&shared_object) {
sess.dcx().emit_fatal(ErrorCreatingImportLibrary {
lib_name: &load_filename,
error: error.to_string(),
});
};
temporary_lib_name
})
.collect()
}
/// Create an ELF .so stub file for raw-dylib.
/// It exports all the provided symbols, but is otherwise empty.
fn create_elf_raw_dylib_stub(sess: &Session, soname: &str, symbols: &[DllImport]) -> Vec<u8> {
use object::write::elf as write;
use object::{Architecture, elf};
let mut stub_buf = Vec::new();
// Build the stub ELF using the object crate.
// The high-level portable API does not allow for the fine-grained control we need,
// so this uses the low-level object::write::elf API.
// The low-level API consists of two stages: reservation and writing.
// We first reserve space for all the things in the binary and then write them.
// It is important that the order of reservation matches the order of writing.
// The object crate contains many debug asserts that fire if you get this wrong.
let endianness = match sess.target.options.endian {
Endian::Little => object::Endianness::Little,
Endian::Big => object::Endianness::Big,
};
let mut stub = write::Writer::new(endianness, true, &mut stub_buf);
// These initial reservations don't reserve any bytes in the binary yet,
// they just allocate in the internal data structures.
// First, we crate the dynamic symbol table. It starts with a null symbol
// and then all the symbols and their dynamic strings.
stub.reserve_null_dynamic_symbol_index();
let dynstrs = symbols
.iter()
.map(|sym| {
stub.reserve_dynamic_symbol_index();
(sym, stub.add_dynamic_string(sym.name.as_str().as_bytes()))
})
.collect::<Vec<_>>();
let soname = stub.add_dynamic_string(soname.as_bytes());
// Reserve the sections.
// We have the minimal sections for a dynamic SO and .text where we point our dummy symbols to.
stub.reserve_shstrtab_section_index();
let text_section_name = stub.add_section_name(".text".as_bytes());
let text_section = stub.reserve_section_index();
stub.reserve_dynstr_section_index();
stub.reserve_dynsym_section_index();
stub.reserve_dynamic_section_index();
// These reservations now determine the actual layout order of the object file.
stub.reserve_file_header();
stub.reserve_shstrtab();
stub.reserve_section_headers();
stub.reserve_dynstr();
stub.reserve_dynsym();
stub.reserve_dynamic(2); // DT_SONAME, DT_NULL
// First write the ELF header with the arch information.
let Some((arch, sub_arch)) = sess.target.object_architecture(&sess.unstable_target_features)
else {
sess.dcx().fatal(format!(
"raw-dylib is not supported for the architecture `{}`",
sess.target.arch
));
};
let e_machine = match (arch, sub_arch) {
(Architecture::Aarch64, None) => elf::EM_AARCH64,
(Architecture::Aarch64_Ilp32, None) => elf::EM_AARCH64,
(Architecture::Arm, None) => elf::EM_ARM,
(Architecture::Avr, None) => elf::EM_AVR,
(Architecture::Bpf, None) => elf::EM_BPF,
(Architecture::Csky, None) => elf::EM_CSKY,
(Architecture::E2K32, None) => elf::EM_MCST_ELBRUS,
(Architecture::E2K64, None) => elf::EM_MCST_ELBRUS,
(Architecture::I386, None) => elf::EM_386,
(Architecture::X86_64, None) => elf::EM_X86_64,
(Architecture::X86_64_X32, None) => elf::EM_X86_64,
(Architecture::Hexagon, None) => elf::EM_HEXAGON,
(Architecture::LoongArch64, None) => elf::EM_LOONGARCH,
(Architecture::M68k, None) => elf::EM_68K,
(Architecture::Mips, None) => elf::EM_MIPS,
(Architecture::Mips64, None) => elf::EM_MIPS,
(Architecture::Mips64_N32, None) => elf::EM_MIPS,
(Architecture::Msp430, None) => elf::EM_MSP430,
(Architecture::PowerPc, None) => elf::EM_PPC,
(Architecture::PowerPc64, None) => elf::EM_PPC64,
(Architecture::Riscv32, None) => elf::EM_RISCV,
(Architecture::Riscv64, None) => elf::EM_RISCV,
(Architecture::S390x, None) => elf::EM_S390,
(Architecture::Sbf, None) => elf::EM_SBF,
(Architecture::Sharc, None) => elf::EM_SHARC,
(Architecture::Sparc, None) => elf::EM_SPARC,
(Architecture::Sparc32Plus, None) => elf::EM_SPARC32PLUS,
(Architecture::Sparc64, None) => elf::EM_SPARCV9,
(Architecture::Xtensa, None) => elf::EM_XTENSA,
_ => {
sess.dcx().fatal(format!(
"raw-dylib is not supported for the architecture `{}`",
sess.target.arch
));
}
};
stub.write_file_header(&write::FileHeader {
os_abi: crate::back::metadata::elf_os_abi(sess),
abi_version: 0,
e_type: object::elf::ET_DYN,
e_machine,
e_entry: 0,
e_flags: crate::back::metadata::elf_e_flags(arch, sess),
})
.unwrap();
// .shstrtab
stub.write_shstrtab();
// Section headers
stub.write_null_section_header();
stub.write_shstrtab_section_header();
// Create a dummy .text section for our dummy symbols.
stub.write_section_header(&write::SectionHeader {
name: Some(text_section_name),
sh_type: elf::SHT_PROGBITS,
sh_flags: 0,
sh_addr: 0,
sh_offset: 0,
sh_size: 0,
sh_link: 0,
sh_info: 0,
sh_addralign: 1,
sh_entsize: 0,
});
stub.write_dynstr_section_header(0);
stub.write_dynsym_section_header(0, 1);
stub.write_dynamic_section_header(0);
// .dynstr
stub.write_dynstr();
// .dynsym
stub.write_null_dynamic_symbol();
for (_, name) in dynstrs {
stub.write_dynamic_symbol(&write::Sym {
name: Some(name),
st_info: (elf::STB_GLOBAL << 4) | elf::STT_NOTYPE,
st_other: elf::STV_DEFAULT,
section: Some(text_section),
st_shndx: 0, // ignored by object in favor of the `section` field
st_value: 0,
st_size: 0,
});
}
// .dynamic
// the DT_SONAME will be used by the linker to populate DT_NEEDED
// which the loader uses to find the library.
// DT_NULL terminates the .dynamic table.
stub.write_dynamic_string(elf::DT_SONAME, soname);
stub.write_dynamic(elf::DT_NULL, 0);
stub_buf
}

View File

@ -1655,9 +1655,9 @@ impl<'a> Linker for AixLinker<'a> {
}
}
fn link_dylib_by_name(&mut self, name: &str, _verbatim: bool, _as_needed: bool) {
fn link_dylib_by_name(&mut self, name: &str, verbatim: bool, _as_needed: bool) {
self.hint_dynamic();
self.link_or_cc_arg(format!("-l{name}"));
self.link_or_cc_arg(if verbatim { String::from(name) } else { format!("-l{name}") });
}
fn link_dylib_by_path(&mut self, path: &Path, _as_needed: bool) {
@ -1668,7 +1668,7 @@ impl<'a> Linker for AixLinker<'a> {
fn link_staticlib_by_name(&mut self, name: &str, verbatim: bool, whole_archive: bool) {
self.hint_static();
if !whole_archive {
self.link_or_cc_arg(format!("-l{name}"));
self.link_or_cc_arg(if verbatim { String::from(name) } else { format!("-l{name}") });
} else {
let mut arg = OsString::from("-bkeepfile:");
arg.push(find_native_static_library(name, verbatim, self.sess));

View File

@ -9,8 +9,7 @@ use itertools::Itertools;
use object::write::{self, StandardSegment, Symbol, SymbolSection};
use object::{
Architecture, BinaryFormat, Endianness, FileFlags, Object, ObjectSection, ObjectSymbol,
SectionFlags, SectionKind, SubArchitecture, SymbolFlags, SymbolKind, SymbolScope, elf, pe,
xcoff,
SectionFlags, SectionKind, SymbolFlags, SymbolKind, SymbolScope, elf, pe, xcoff,
};
use rustc_abi::Endian;
use rustc_data_structures::memmap::Mmap;
@ -206,51 +205,10 @@ pub(crate) fn create_object_file(sess: &Session) -> Option<write::Object<'static
Endian::Little => Endianness::Little,
Endian::Big => Endianness::Big,
};
let (architecture, sub_architecture) = match &sess.target.arch[..] {
"arm" => (Architecture::Arm, None),
"aarch64" => (
if sess.target.pointer_width == 32 {
Architecture::Aarch64_Ilp32
} else {
Architecture::Aarch64
},
None,
),
"x86" => (Architecture::I386, None),
"s390x" => (Architecture::S390x, None),
"mips" | "mips32r6" => (Architecture::Mips, None),
"mips64" | "mips64r6" => (Architecture::Mips64, None),
"x86_64" => (
if sess.target.pointer_width == 32 {
Architecture::X86_64_X32
} else {
Architecture::X86_64
},
None,
),
"powerpc" => (Architecture::PowerPc, None),
"powerpc64" => (Architecture::PowerPc64, None),
"riscv32" => (Architecture::Riscv32, None),
"riscv64" => (Architecture::Riscv64, None),
"sparc" => {
if sess.unstable_target_features.contains(&sym::v8plus) {
// Target uses V8+, aka EM_SPARC32PLUS, aka 64-bit V9 but in 32-bit mode
(Architecture::Sparc32Plus, None)
} else {
// Target uses V7 or V8, aka EM_SPARC
(Architecture::Sparc, None)
}
}
"sparc64" => (Architecture::Sparc64, None),
"avr" => (Architecture::Avr, None),
"msp430" => (Architecture::Msp430, None),
"hexagon" => (Architecture::Hexagon, None),
"bpf" => (Architecture::Bpf, None),
"loongarch64" => (Architecture::LoongArch64, None),
"csky" => (Architecture::Csky, None),
"arm64ec" => (Architecture::Aarch64, Some(SubArchitecture::Arm64EC)),
// Unsupported architecture.
_ => return None,
let Some((architecture, sub_architecture)) =
sess.target.object_architecture(&sess.unstable_target_features)
else {
return None;
};
let binary_format = sess.target.binary_format.to_object();
@ -292,7 +250,26 @@ pub(crate) fn create_object_file(sess: &Session) -> Option<write::Object<'static
file.set_mangling(original_mangling);
}
let e_flags = match architecture {
let e_flags = elf_e_flags(architecture, sess);
// adapted from LLVM's `MCELFObjectTargetWriter::getOSABI`
let os_abi = elf_os_abi(sess);
let abi_version = 0;
add_gnu_property_note(&mut file, architecture, binary_format, endianness);
file.flags = FileFlags::Elf { os_abi, abi_version, e_flags };
Some(file)
}
pub(super) fn elf_os_abi(sess: &Session) -> u8 {
match sess.target.options.os.as_ref() {
"hermit" => elf::ELFOSABI_STANDALONE,
"freebsd" => elf::ELFOSABI_FREEBSD,
"solaris" => elf::ELFOSABI_SOLARIS,
_ => elf::ELFOSABI_NONE,
}
}
pub(super) fn elf_e_flags(architecture: Architecture, sess: &Session) -> u32 {
match architecture {
Architecture::Mips => {
let arch = match sess.target.options.cpu.as_ref() {
"mips1" => elf::EF_MIPS_ARCH_1,
@ -387,18 +364,7 @@ pub(crate) fn create_object_file(sess: &Session) -> Option<write::Object<'static
e_flags
}
_ => 0,
};
// adapted from LLVM's `MCELFObjectTargetWriter::getOSABI`
let os_abi = match sess.target.options.os.as_ref() {
"hermit" => elf::ELFOSABI_STANDALONE,
"freebsd" => elf::ELFOSABI_FREEBSD,
"solaris" => elf::ELFOSABI_SOLARIS,
_ => elf::ELFOSABI_NONE,
};
let abi_version = 0;
add_gnu_property_note(&mut file, architecture, binary_format, endianness);
file.flags = FileFlags::Elf { os_abi, abi_version, e_flags };
Some(file)
}
}
/// Mach-O files contain information about:

View File

@ -6,6 +6,7 @@ use std::sync::Arc;
use std::sync::mpsc::{Receiver, Sender, channel};
use std::{fs, io, mem, str, thread};
use rustc_abi::Size;
use rustc_ast::attr;
use rustc_ast::expand::autodiff_attrs::AutoDiffItem;
use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
@ -355,6 +356,7 @@ pub struct CodegenContext<B: WriteBackendMethods> {
pub target_is_like_aix: bool,
pub split_debuginfo: rustc_target::spec::SplitDebuginfo,
pub split_dwarf_kind: rustc_session::config::SplitDwarfKind,
pub pointer_size: Size,
/// All commandline args used to invoke the compiler, with @file args fully expanded.
/// This will only be used within debug info, e.g. in the pdb file on windows
@ -1216,6 +1218,7 @@ fn start_executing_work<B: ExtraBackendMethods>(
split_debuginfo: tcx.sess.split_debuginfo(),
split_dwarf_kind: tcx.sess.opts.unstable_opts.split_dwarf_kind,
parallel: backend.supports_parallel() && !sess.opts.unstable_opts.no_parallel_backend,
pointer_size: tcx.data_layout.pointer_size,
};
// This is the "main loop" of parallel work happening for parallel codegen.

View File

@ -957,6 +957,7 @@ pub enum InvalidMonomorphization<'tcx> {
},
#[diag(codegen_ssa_invalid_monomorphization_mask_type, code = E0511)]
#[note]
MaskType {
#[primary_span]
span: Span,

View File

@ -14,7 +14,6 @@
#![feature(rustdoc_internals)]
#![feature(trait_alias)]
#![feature(try_blocks)]
#![warn(unreachable_pub)]
// tidy-alphabetical-end
//! This crate contains codegen code that is used by all codegen backends (LLVM and others).

Some files were not shown because too many files have changed in this diff Show More