commit
4967fd24de
1755 changed files with 72487 additions and 16305 deletions
|
|
@ -4673,6 +4673,7 @@ dependencies = [
|
|||
"bincode",
|
||||
"rustc-hash 2.1.1",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ members = [
|
|||
# tidy-alphabetical-start
|
||||
"compiler/rustc",
|
||||
"src/build_helper",
|
||||
"src/etc/test-float-parse",
|
||||
"src/rustc-std-workspace/rustc-std-workspace-alloc",
|
||||
"src/rustc-std-workspace/rustc-std-workspace-core",
|
||||
"src/rustc-std-workspace/rustc-std-workspace-std",
|
||||
|
|
@ -41,6 +40,7 @@ members = [
|
|||
"src/tools/rustdoc-themes",
|
||||
"src/tools/rustfmt",
|
||||
"src/tools/suggest-tests",
|
||||
"src/tools/test-float-parse",
|
||||
"src/tools/tidy",
|
||||
"src/tools/tier-check",
|
||||
"src/tools/unicode-table-generator",
|
||||
|
|
|
|||
129
compiler/rustc_abi/src/canon_abi.rs
Normal file
129
compiler/rustc_abi/src/canon_abi.rs
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
use std::fmt;
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
use rustc_macros::HashStable_Generic;
|
||||
|
||||
use crate::ExternAbi;
|
||||
|
||||
/// Calling convention to determine codegen
|
||||
///
|
||||
/// CanonAbi erases certain distinctions ExternAbi preserves, but remains target-dependent.
|
||||
/// There are still both target-specific variants and aliasing variants, though much fewer.
|
||||
/// The reason for this step is the frontend may wish to show an ExternAbi but implement that ABI
|
||||
/// using a different ABI than the string per se, or describe irrelevant differences, e.g.
|
||||
/// - extern "system"
|
||||
/// - extern "cdecl"
|
||||
/// - extern "C-unwind"
|
||||
/// In that sense, this erases mere syntactic distinctions to create a canonical *directive*,
|
||||
/// rather than picking the "actual" ABI.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[derive(PartialOrd, Ord, PartialEq, Eq, Hash)]
|
||||
#[cfg_attr(feature = "nightly", derive(HashStable_Generic))]
|
||||
pub enum CanonAbi {
|
||||
// NOTE: the use of nested variants for some ABIs is for many targets they don't matter,
|
||||
// and this pushes the complexity of their reasoning to target-specific code,
|
||||
// allowing a `match` to easily exhaustively ignore these subcategories of variants.
|
||||
// Otherwise it is very tempting to avoid matching exhaustively!
|
||||
C,
|
||||
Rust,
|
||||
RustCold,
|
||||
|
||||
/// ABIs relevant to 32-bit Arm targets
|
||||
Arm(ArmCall),
|
||||
/// ABI relevant to GPUs: the entry point for a GPU kernel
|
||||
GpuKernel,
|
||||
|
||||
/// ABIs relevant to bare-metal interrupt targets
|
||||
// FIXME(workingjubilee): a particular reason for this nesting is we might not need these?
|
||||
// interrupt ABIs should have the same properties:
|
||||
// - uncallable by Rust calls, as LLVM rejects it in most cases
|
||||
// - uses a preserve-all-registers *callee* convention
|
||||
// - should always return `-> !` (effectively... it can't use normal `ret`)
|
||||
// what differs between targets is
|
||||
// - allowed arguments: x86 differs slightly, having 2-3 arguments which are handled magically
|
||||
// - may need special prologues/epilogues for some interrupts, without affecting "call ABI"
|
||||
Interrupt(InterruptKind),
|
||||
|
||||
/// ABIs relevant to Windows or x86 targets
|
||||
X86(X86Call),
|
||||
}
|
||||
|
||||
impl fmt::Display for CanonAbi {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
// convert to the ExternAbi that *shares a string* with this CanonAbi.
|
||||
// FIXME: ideally we'd avoid printing `CanonAbi`, and preserve `ExternAbi` everywhere
|
||||
// that we need to generate error messages.
|
||||
let erased_abi = match self {
|
||||
CanonAbi::C => ExternAbi::C { unwind: false },
|
||||
CanonAbi::Rust => ExternAbi::Rust,
|
||||
CanonAbi::RustCold => ExternAbi::RustCold,
|
||||
CanonAbi::Arm(arm_call) => match arm_call {
|
||||
ArmCall::Aapcs => ExternAbi::Aapcs { unwind: false },
|
||||
ArmCall::CCmseNonSecureCall => ExternAbi::CCmseNonSecureCall,
|
||||
ArmCall::CCmseNonSecureEntry => ExternAbi::CCmseNonSecureEntry,
|
||||
},
|
||||
CanonAbi::GpuKernel => ExternAbi::GpuKernel,
|
||||
CanonAbi::Interrupt(interrupt_kind) => match interrupt_kind {
|
||||
InterruptKind::Avr => ExternAbi::AvrInterrupt,
|
||||
InterruptKind::AvrNonBlocking => ExternAbi::AvrNonBlockingInterrupt,
|
||||
InterruptKind::Msp430 => ExternAbi::Msp430Interrupt,
|
||||
InterruptKind::RiscvMachine => ExternAbi::RiscvInterruptM,
|
||||
InterruptKind::RiscvSupervisor => ExternAbi::RiscvInterruptS,
|
||||
InterruptKind::X86 => ExternAbi::X86Interrupt,
|
||||
},
|
||||
CanonAbi::X86(x86_call) => match x86_call {
|
||||
X86Call::Fastcall => ExternAbi::Fastcall { unwind: false },
|
||||
X86Call::Stdcall => ExternAbi::Stdcall { unwind: false },
|
||||
X86Call::SysV64 => ExternAbi::SysV64 { unwind: false },
|
||||
X86Call::Thiscall => ExternAbi::Thiscall { unwind: false },
|
||||
X86Call::Vectorcall => ExternAbi::Vectorcall { unwind: false },
|
||||
X86Call::Win64 => ExternAbi::Win64 { unwind: false },
|
||||
},
|
||||
};
|
||||
erased_abi.as_str().fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
/// Callee codegen for interrupts
|
||||
///
|
||||
/// This is named differently from the "Call" enums because it is different:
|
||||
/// these "ABI" differences are not relevant to callers, since there is "no caller".
|
||||
/// These only affect callee codegen. making their categorization as distinct ABIs a bit peculiar.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[derive(PartialOrd, Ord, PartialEq, Eq, Hash)]
|
||||
#[cfg_attr(feature = "nightly", derive(HashStable_Generic))]
|
||||
pub enum InterruptKind {
|
||||
Avr,
|
||||
AvrNonBlocking,
|
||||
Msp430,
|
||||
RiscvMachine,
|
||||
RiscvSupervisor,
|
||||
X86,
|
||||
}
|
||||
|
||||
/// ABIs defined for x86-{32,64}
|
||||
///
|
||||
/// One of SysV64 or Win64 may alias the C ABI, and arguably Win64 is cross-platform now?
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[derive(PartialOrd, Ord, PartialEq, Eq, Hash)]
|
||||
#[cfg_attr(feature = "nightly", derive(HashStable_Generic))]
|
||||
pub enum X86Call {
|
||||
/// "fastcall" has both GNU and Windows variants
|
||||
Fastcall,
|
||||
/// "stdcall" has both GNU and Windows variants
|
||||
Stdcall,
|
||||
SysV64,
|
||||
Thiscall,
|
||||
Vectorcall,
|
||||
Win64,
|
||||
}
|
||||
|
||||
/// ABIs defined for 32-bit Arm
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[derive(PartialOrd, Ord, PartialEq, Eq, Hash)]
|
||||
#[cfg_attr(feature = "nightly", derive(HashStable_Generic))]
|
||||
pub enum ArmCall {
|
||||
Aapcs,
|
||||
CCmseNonSecureCall,
|
||||
CCmseNonSecureEntry,
|
||||
}
|
||||
|
|
@ -7,69 +7,98 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher, StableOrd};
|
|||
#[cfg(feature = "nightly")]
|
||||
use rustc_macros::{Decodable, Encodable};
|
||||
|
||||
use crate::AbiFromStrErr;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
use ExternAbi as Abi;
|
||||
|
||||
/// ABI we expect to see within `extern "{abi}"`
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[cfg_attr(feature = "nightly", derive(Encodable, Decodable))]
|
||||
pub enum ExternAbi {
|
||||
// Some of the ABIs come first because every time we add a new ABI, we have to re-bless all the
|
||||
// hashing tests. These are used in many places, so giving them stable values reduces test
|
||||
// churn. The specific values are meaningless.
|
||||
Rust,
|
||||
/* universal */
|
||||
/// presumed C ABI for the platform
|
||||
C {
|
||||
unwind: bool,
|
||||
},
|
||||
/// ABI of the "system" interface, e.g. the Win32 API, always "aliasing"
|
||||
System {
|
||||
unwind: bool,
|
||||
},
|
||||
|
||||
/// that's us!
|
||||
Rust,
|
||||
/// the mostly-unused `unboxed_closures` ABI, effectively now an impl detail unless someone
|
||||
/// puts in the work to make it viable again... but would we need a special ABI?
|
||||
RustCall,
|
||||
/// For things unlikely to be called, where reducing register pressure in
|
||||
/// `extern "Rust"` callers is worth paying extra cost in the callee.
|
||||
/// Stronger than just `#[cold]` because `fn` pointers might be incompatible.
|
||||
RustCold,
|
||||
|
||||
/// Unstable impl detail that directly uses Rust types to describe the ABI to LLVM.
|
||||
/// Even normally-compatible Rust types can become ABI-incompatible with this ABI!
|
||||
Unadjusted,
|
||||
|
||||
/// UEFI ABI, usually an alias of C, but sometimes an arch-specific alias
|
||||
/// and only valid on platforms that have a UEFI standard
|
||||
EfiApi,
|
||||
|
||||
/* arm */
|
||||
/// Arm Architecture Procedure Call Standard, sometimes `ExternAbi::C` is an alias for this
|
||||
Aapcs {
|
||||
unwind: bool,
|
||||
},
|
||||
/// extremely constrained barely-C ABI for TrustZone
|
||||
CCmseNonSecureCall,
|
||||
/// extremely constrained barely-C ABI for TrustZone
|
||||
CCmseNonSecureEntry,
|
||||
|
||||
/* gpu */
|
||||
/// An entry-point function called by the GPU's host
|
||||
// FIXME: should not be callable from Rust on GPU targets, is for host's use only
|
||||
GpuKernel,
|
||||
/// An entry-point function called by the GPU's host
|
||||
// FIXME: why do we have two of these?
|
||||
PtxKernel,
|
||||
|
||||
/* interrupt */
|
||||
AvrInterrupt,
|
||||
AvrNonBlockingInterrupt,
|
||||
Msp430Interrupt,
|
||||
RiscvInterruptM,
|
||||
RiscvInterruptS,
|
||||
X86Interrupt,
|
||||
|
||||
/* x86 */
|
||||
/// `ExternAbi::C` but spelled funny because x86
|
||||
Cdecl {
|
||||
unwind: bool,
|
||||
},
|
||||
/// gnu-stdcall on "unix" and win-stdcall on "windows"
|
||||
Stdcall {
|
||||
unwind: bool,
|
||||
},
|
||||
/// gnu-fastcall on "unix" and win-fastcall on "windows"
|
||||
Fastcall {
|
||||
unwind: bool,
|
||||
},
|
||||
Vectorcall {
|
||||
unwind: bool,
|
||||
},
|
||||
/// windows C++ ABI
|
||||
Thiscall {
|
||||
unwind: bool,
|
||||
},
|
||||
Aapcs {
|
||||
/// uses AVX and stuff
|
||||
Vectorcall {
|
||||
unwind: bool,
|
||||
},
|
||||
|
||||
/* x86_64 */
|
||||
SysV64 {
|
||||
unwind: bool,
|
||||
},
|
||||
Win64 {
|
||||
unwind: bool,
|
||||
},
|
||||
SysV64 {
|
||||
unwind: bool,
|
||||
},
|
||||
PtxKernel,
|
||||
Msp430Interrupt,
|
||||
X86Interrupt,
|
||||
/// An entry-point function called by the GPU's host
|
||||
// FIXME: should not be callable from Rust on GPU targets, is for host's use only
|
||||
GpuKernel,
|
||||
EfiApi,
|
||||
AvrInterrupt,
|
||||
AvrNonBlockingInterrupt,
|
||||
CCmseNonSecureCall,
|
||||
CCmseNonSecureEntry,
|
||||
System {
|
||||
unwind: bool,
|
||||
},
|
||||
RustCall,
|
||||
/// *Not* a stable ABI, just directly use the Rust types to describe the ABI for LLVM. Even
|
||||
/// normally ABI-compatible Rust types can become ABI-incompatible with this ABI!
|
||||
Unadjusted,
|
||||
/// For things unlikely to be called, where reducing register pressure in
|
||||
/// `extern "Rust"` callers is worth paying extra cost in the callee.
|
||||
/// Stronger than just `#[cold]` because `fn` pointers might be incompatible.
|
||||
RustCold,
|
||||
RiscvInterruptM,
|
||||
RiscvInterruptS,
|
||||
}
|
||||
|
||||
macro_rules! abi_impls {
|
||||
|
|
@ -99,11 +128,6 @@ macro_rules! abi_impls {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum AbiFromStrErr {
|
||||
Unknown,
|
||||
}
|
||||
|
||||
abi_impls! {
|
||||
ExternAbi = {
|
||||
C { unwind: false } =><= "C",
|
||||
|
|
@ -227,7 +251,7 @@ pub fn all_names() -> Vec<&'static str> {
|
|||
|
||||
impl ExternAbi {
|
||||
/// Default ABI chosen for `extern fn` declarations without an explicit ABI.
|
||||
pub const FALLBACK: Abi = Abi::C { unwind: false };
|
||||
pub const FALLBACK: ExternAbi = ExternAbi::C { unwind: false };
|
||||
|
||||
pub fn name(self) -> &'static str {
|
||||
self.as_str()
|
||||
|
|
|
|||
|
|
@ -758,7 +758,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
|
|||
niche_variants,
|
||||
niche_start,
|
||||
},
|
||||
tag_field: 0,
|
||||
tag_field: FieldIdx::new(0),
|
||||
variants: IndexVec::new(),
|
||||
},
|
||||
fields: FieldsShape::Arbitrary {
|
||||
|
|
@ -1072,7 +1072,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
|
|||
variants: Variants::Multiple {
|
||||
tag,
|
||||
tag_encoding: TagEncoding::Direct,
|
||||
tag_field: 0,
|
||||
tag_field: FieldIdx::new(0),
|
||||
variants: IndexVec::new(),
|
||||
},
|
||||
fields: FieldsShape::Arbitrary {
|
||||
|
|
|
|||
|
|
@ -158,7 +158,7 @@ pub(super) fn layout<
|
|||
// Build a prefix layout, including "promoting" all ineligible
|
||||
// locals as part of the prefix. We compute the layout of all of
|
||||
// these fields at once to get optimal packing.
|
||||
let tag_index = prefix_layouts.len();
|
||||
let tag_index = prefix_layouts.next_index();
|
||||
|
||||
// `variant_fields` already accounts for the reserved variants, so no need to add them.
|
||||
let max_discr = (variant_fields.len() - 1) as u128;
|
||||
|
|
@ -187,7 +187,7 @@ pub(super) fn layout<
|
|||
|
||||
// "a" (`0..b_start`) and "b" (`b_start..`) correspond to
|
||||
// "outer" and "promoted" fields respectively.
|
||||
let b_start = FieldIdx::new(tag_index + 1);
|
||||
let b_start = tag_index.plus(1);
|
||||
let offsets_b = IndexVec::from_raw(offsets.raw.split_off(b_start.index()));
|
||||
let offsets_a = offsets;
|
||||
|
||||
|
|
|
|||
|
|
@ -39,6 +39,13 @@ rustc_index::newtype_index! {
|
|||
pub struct FieldIdx {}
|
||||
}
|
||||
|
||||
impl FieldIdx {
|
||||
/// The second field, at index 1.
|
||||
///
|
||||
/// For use alongside [`FieldIdx::ZERO`], particularly with scalar pairs.
|
||||
pub const ONE: FieldIdx = FieldIdx::from_u32(1);
|
||||
}
|
||||
|
||||
rustc_index::newtype_index! {
|
||||
/// The *source-order* index of a variant in a type.
|
||||
///
|
||||
|
|
@ -274,7 +281,7 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
|
|||
|
||||
/// Finds the one field that is not a 1-ZST.
|
||||
/// Returns `None` if there are multiple non-1-ZST fields or only 1-ZST-fields.
|
||||
pub fn non_1zst_field<C>(&self, cx: &C) -> Option<(usize, Self)>
|
||||
pub fn non_1zst_field<C>(&self, cx: &C) -> Option<(FieldIdx, Self)>
|
||||
where
|
||||
Ty: TyAbiInterface<'a, C> + Copy,
|
||||
{
|
||||
|
|
@ -288,7 +295,7 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
|
|||
// More than one non-1-ZST field.
|
||||
return None;
|
||||
}
|
||||
found = Some((field_idx, field));
|
||||
found = Some((FieldIdx::from_usize(field_idx), field));
|
||||
}
|
||||
found
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,13 +55,14 @@ use rustc_index::{Idx, IndexSlice, IndexVec};
|
|||
use rustc_macros::{Decodable_NoContext, Encodable_NoContext, HashStable_Generic};
|
||||
|
||||
mod callconv;
|
||||
mod canon_abi;
|
||||
mod extern_abi;
|
||||
mod layout;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
mod extern_abi;
|
||||
|
||||
pub use callconv::{Heterogeneous, HomogeneousAggregate, Reg, RegKind};
|
||||
pub use canon_abi::{ArmCall, CanonAbi, InterruptKind, X86Call};
|
||||
pub use extern_abi::{ExternAbi, all_names};
|
||||
#[cfg(feature = "nightly")]
|
||||
pub use layout::{FIRST_VARIANT, FieldIdx, Layout, TyAbiInterface, TyAndLayout, VariantIdx};
|
||||
|
|
@ -1572,7 +1573,7 @@ pub enum Variants<FieldIdx: Idx, VariantIdx: Idx> {
|
|||
Multiple {
|
||||
tag: Scalar,
|
||||
tag_encoding: TagEncoding<VariantIdx>,
|
||||
tag_field: usize,
|
||||
tag_field: FieldIdx,
|
||||
variants: IndexVec<VariantIdx, LayoutData<FieldIdx, VariantIdx>>,
|
||||
},
|
||||
}
|
||||
|
|
@ -1895,3 +1896,11 @@ pub enum StructKind {
|
|||
/// A univariant, but with a prefix of an arbitrary size & alignment (e.g., enum tag).
|
||||
Prefixed(Size, Align),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum AbiFromStrErr {
|
||||
/// not a known ABI
|
||||
Unknown,
|
||||
/// no "-unwind" variant can be used here
|
||||
NoExplicitUnwind,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -99,8 +99,15 @@ pub struct Path {
|
|||
|
||||
impl PartialEq<Symbol> for Path {
|
||||
#[inline]
|
||||
fn eq(&self, symbol: &Symbol) -> bool {
|
||||
matches!(&self.segments[..], [segment] if segment.ident.name == *symbol)
|
||||
fn eq(&self, name: &Symbol) -> bool {
|
||||
if let [segment] = self.segments.as_ref()
|
||||
&& segment.args.is_none()
|
||||
&& segment.ident.name == *name
|
||||
{
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -120,17 +127,6 @@ impl Path {
|
|||
Path { segments: thin_vec![PathSegment::from_ident(ident)], span: ident.span, tokens: None }
|
||||
}
|
||||
|
||||
pub fn is_ident(&self, name: Symbol) -> bool {
|
||||
if let [segment] = self.segments.as_ref()
|
||||
&& segment.args.is_none()
|
||||
&& segment.ident.name == name
|
||||
{
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_global(&self) -> bool {
|
||||
self.segments.first().is_some_and(|segment| segment.ident.name == kw::PathRoot)
|
||||
}
|
||||
|
|
@ -1123,10 +1119,9 @@ impl Stmt {
|
|||
pub fn add_trailing_semicolon(mut self) -> Self {
|
||||
self.kind = match self.kind {
|
||||
StmtKind::Expr(expr) => StmtKind::Semi(expr),
|
||||
StmtKind::MacCall(mac) => {
|
||||
StmtKind::MacCall(mac.map(|MacCallStmt { mac, style: _, attrs, tokens }| {
|
||||
MacCallStmt { mac, style: MacStmtStyle::Semicolon, attrs, tokens }
|
||||
}))
|
||||
StmtKind::MacCall(mut mac) => {
|
||||
mac.style = MacStmtStyle::Semicolon;
|
||||
StmtKind::MacCall(mac)
|
||||
}
|
||||
kind => kind,
|
||||
};
|
||||
|
|
@ -1728,7 +1723,7 @@ pub enum ExprKind {
|
|||
///
|
||||
/// Usually not written directly in user code but
|
||||
/// indirectly via the macro `core::mem::offset_of!(...)`.
|
||||
OffsetOf(P<Ty>, P<[Ident]>),
|
||||
OffsetOf(P<Ty>, Vec<Ident>),
|
||||
|
||||
/// A macro invocation; pre-expansion.
|
||||
MacCall(P<MacCall>),
|
||||
|
|
@ -2465,6 +2460,39 @@ impl TyKind {
|
|||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if this type is considered a scalar primitive (e.g.,
|
||||
/// `i32`, `u8`, `bool`, etc).
|
||||
///
|
||||
/// This check is based on **symbol equality** and does **not** remove any
|
||||
/// path prefixes or references. If a type alias or shadowing is present
|
||||
/// (e.g., `type i32 = CustomType;`), this method will still return `true`
|
||||
/// for `i32`, even though it may not refer to the primitive type.
|
||||
pub fn maybe_scalar(&self) -> bool {
|
||||
let Some(ty_sym) = self.is_simple_path() else {
|
||||
// unit type
|
||||
return self.is_unit();
|
||||
};
|
||||
matches!(
|
||||
ty_sym,
|
||||
sym::i8
|
||||
| sym::i16
|
||||
| sym::i32
|
||||
| sym::i64
|
||||
| sym::i128
|
||||
| sym::u8
|
||||
| sym::u16
|
||||
| sym::u32
|
||||
| sym::u64
|
||||
| sym::u128
|
||||
| sym::f16
|
||||
| sym::f32
|
||||
| sym::f64
|
||||
| sym::f128
|
||||
| sym::char
|
||||
| sym::bool
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// A pattern type pattern.
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ impl Attribute {
|
|||
|
||||
pub fn unwrap_normal_item(self) -> AttrItem {
|
||||
match self.kind {
|
||||
AttrKind::Normal(normal) => normal.into_inner().item,
|
||||
AttrKind::Normal(normal) => normal.item,
|
||||
AttrKind::DocComment(..) => panic!("unexpected doc comment"),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ use std::panic;
|
|||
|
||||
use rustc_data_structures::flat_map_in_place::FlatMapInPlace;
|
||||
use rustc_data_structures::stack::ensure_sufficient_stack;
|
||||
use rustc_span::source_map::Spanned;
|
||||
use rustc_span::{Ident, Span};
|
||||
use smallvec::{Array, SmallVec, smallvec};
|
||||
use thin_vec::ThinVec;
|
||||
|
|
@ -209,11 +208,7 @@ pub trait MutVisitor: Sized {
|
|||
}
|
||||
|
||||
fn visit_ident(&mut self, i: &mut Ident) {
|
||||
walk_ident(self, i);
|
||||
}
|
||||
|
||||
fn visit_modifiers(&mut self, m: &mut TraitBoundModifiers) {
|
||||
walk_modifiers(self, m);
|
||||
self.visit_span(&mut i.span);
|
||||
}
|
||||
|
||||
fn visit_path(&mut self, p: &mut Path) {
|
||||
|
|
@ -368,6 +363,33 @@ pub trait MutVisitor: Sized {
|
|||
|
||||
super::common_visitor_and_walkers!((mut) MutVisitor);
|
||||
|
||||
macro_rules! generate_flat_map_visitor_fns {
|
||||
($($name:ident, $Ty:ty, $flat_map_fn:ident$(, $param:ident: $ParamTy:ty)*;)+) => {
|
||||
$(
|
||||
fn $name<V: MutVisitor>(
|
||||
vis: &mut V,
|
||||
values: &mut ThinVec<$Ty>,
|
||||
$(
|
||||
$param: $ParamTy,
|
||||
)*
|
||||
) {
|
||||
values.flat_map_in_place(|value| vis.$flat_map_fn(value$(,$param)*));
|
||||
}
|
||||
)+
|
||||
}
|
||||
}
|
||||
|
||||
generate_flat_map_visitor_fns! {
|
||||
visit_items, P<Item>, flat_map_item;
|
||||
visit_foreign_items, P<ForeignItem>, flat_map_foreign_item;
|
||||
visit_generic_params, GenericParam, flat_map_generic_param;
|
||||
visit_stmts, Stmt, flat_map_stmt;
|
||||
visit_exprs, P<Expr>, filter_map_expr;
|
||||
visit_pat_fields, PatField, flat_map_pat_field;
|
||||
visit_variants, Variant, flat_map_variant;
|
||||
visit_assoc_items, P<AssocItem>, flat_map_assoc_item, ctxt: AssocCtxt;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn visit_vec<T, F>(elems: &mut Vec<T>, mut visit_elem: F)
|
||||
where
|
||||
|
|
@ -404,15 +426,6 @@ fn visit_attrs<T: MutVisitor>(vis: &mut T, attrs: &mut AttrVec) {
|
|||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
fn visit_exprs<T: MutVisitor>(vis: &mut T, exprs: &mut Vec<P<Expr>>) {
|
||||
exprs.flat_map_in_place(|expr| vis.filter_map_expr(expr))
|
||||
}
|
||||
|
||||
fn visit_thin_exprs<T: MutVisitor>(vis: &mut T, exprs: &mut ThinVec<P<Expr>>) {
|
||||
exprs.flat_map_in_place(|expr| vis.filter_map_expr(expr))
|
||||
}
|
||||
|
||||
fn visit_attr_args<T: MutVisitor>(vis: &mut T, args: &mut AttrArgs) {
|
||||
match args {
|
||||
AttrArgs::Empty => {}
|
||||
|
|
@ -431,15 +444,6 @@ fn visit_delim_args<T: MutVisitor>(vis: &mut T, args: &mut DelimArgs) {
|
|||
vis.visit_span(close);
|
||||
}
|
||||
|
||||
pub fn walk_pat_field<T: MutVisitor>(vis: &mut T, fp: &mut PatField) {
|
||||
let PatField { attrs, id, ident, is_placeholder: _, is_shorthand: _, pat, span } = fp;
|
||||
vis.visit_id(id);
|
||||
visit_attrs(vis, attrs);
|
||||
vis.visit_ident(ident);
|
||||
vis.visit_pat(pat);
|
||||
vis.visit_span(span);
|
||||
}
|
||||
|
||||
pub fn walk_flat_map_pat_field<T: MutVisitor>(
|
||||
vis: &mut T,
|
||||
mut fp: PatField,
|
||||
|
|
@ -448,21 +452,13 @@ pub fn walk_flat_map_pat_field<T: MutVisitor>(
|
|||
smallvec![fp]
|
||||
}
|
||||
|
||||
fn walk_use_tree<T: MutVisitor>(vis: &mut T, use_tree: &mut UseTree) {
|
||||
let UseTree { prefix, kind, span } = use_tree;
|
||||
vis.visit_path(prefix);
|
||||
match kind {
|
||||
UseTreeKind::Simple(rename) => visit_opt(rename, |rename| vis.visit_ident(rename)),
|
||||
UseTreeKind::Nested { items, span } => {
|
||||
for (tree, id) in items {
|
||||
vis.visit_id(id);
|
||||
vis.visit_use_tree(tree);
|
||||
}
|
||||
vis.visit_span(span);
|
||||
}
|
||||
UseTreeKind::Glob => {}
|
||||
}
|
||||
vis.visit_span(span);
|
||||
fn visit_nested_use_tree<V: MutVisitor>(
|
||||
vis: &mut V,
|
||||
nested_tree: &mut UseTree,
|
||||
nested_id: &mut NodeId,
|
||||
) {
|
||||
vis.visit_id(nested_id);
|
||||
vis.visit_use_tree(nested_tree);
|
||||
}
|
||||
|
||||
pub fn walk_arm<T: MutVisitor>(vis: &mut T, arm: &mut Arm) {
|
||||
|
|
@ -499,83 +495,6 @@ fn walk_assoc_item_constraint<T: MutVisitor>(
|
|||
vis.visit_span(span);
|
||||
}
|
||||
|
||||
pub fn walk_ty<T: MutVisitor>(vis: &mut T, ty: &mut Ty) {
|
||||
let Ty { id, kind, span, tokens: _ } = ty;
|
||||
vis.visit_id(id);
|
||||
match kind {
|
||||
TyKind::Err(_guar) => {}
|
||||
TyKind::Infer | TyKind::ImplicitSelf | TyKind::Dummy | TyKind::Never | TyKind::CVarArgs => {
|
||||
}
|
||||
TyKind::Slice(ty) => vis.visit_ty(ty),
|
||||
TyKind::Ptr(MutTy { ty, mutbl: _ }) => vis.visit_ty(ty),
|
||||
TyKind::Ref(lt, MutTy { ty, mutbl: _ }) | TyKind::PinnedRef(lt, MutTy { ty, mutbl: _ }) => {
|
||||
visit_opt(lt, |lt| vis.visit_lifetime(lt));
|
||||
vis.visit_ty(ty);
|
||||
}
|
||||
TyKind::BareFn(bft) => {
|
||||
let BareFnTy { safety, ext: _, generic_params, decl, decl_span } = bft.deref_mut();
|
||||
visit_safety(vis, safety);
|
||||
generic_params.flat_map_in_place(|param| vis.flat_map_generic_param(param));
|
||||
vis.visit_fn_decl(decl);
|
||||
vis.visit_span(decl_span);
|
||||
}
|
||||
TyKind::UnsafeBinder(binder) => {
|
||||
let UnsafeBinderTy { generic_params, inner_ty } = binder.deref_mut();
|
||||
generic_params.flat_map_in_place(|param| vis.flat_map_generic_param(param));
|
||||
vis.visit_ty(inner_ty);
|
||||
}
|
||||
TyKind::Tup(tys) => visit_thin_vec(tys, |ty| vis.visit_ty(ty)),
|
||||
TyKind::Paren(ty) => vis.visit_ty(ty),
|
||||
TyKind::Pat(ty, pat) => {
|
||||
vis.visit_ty(ty);
|
||||
vis.visit_ty_pat(pat);
|
||||
}
|
||||
TyKind::Path(qself, path) => {
|
||||
vis.visit_qself(qself);
|
||||
vis.visit_path(path);
|
||||
}
|
||||
TyKind::Array(ty, length) => {
|
||||
vis.visit_ty(ty);
|
||||
vis.visit_anon_const(length);
|
||||
}
|
||||
TyKind::Typeof(expr) => vis.visit_anon_const(expr),
|
||||
TyKind::TraitObject(bounds, _syntax) => {
|
||||
visit_vec(bounds, |bound| vis.visit_param_bound(bound, BoundKind::TraitObject))
|
||||
}
|
||||
TyKind::ImplTrait(id, bounds) => {
|
||||
vis.visit_id(id);
|
||||
visit_vec(bounds, |bound| vis.visit_param_bound(bound, BoundKind::Impl));
|
||||
}
|
||||
TyKind::MacCall(mac) => vis.visit_mac_call(mac),
|
||||
}
|
||||
vis.visit_span(span);
|
||||
}
|
||||
|
||||
pub fn walk_ty_pat<T: MutVisitor>(vis: &mut T, ty: &mut TyPat) {
|
||||
let TyPat { id, kind, span, tokens: _ } = ty;
|
||||
vis.visit_id(id);
|
||||
match kind {
|
||||
TyPatKind::Range(start, end, _include_end) => {
|
||||
visit_opt(start, |c| vis.visit_anon_const(c));
|
||||
visit_opt(end, |c| vis.visit_anon_const(c));
|
||||
}
|
||||
TyPatKind::Or(variants) => visit_thin_vec(variants, |p| vis.visit_ty_pat(p)),
|
||||
TyPatKind::Err(_) => {}
|
||||
}
|
||||
vis.visit_span(span);
|
||||
}
|
||||
|
||||
pub fn walk_variant<T: MutVisitor>(visitor: &mut T, variant: &mut Variant) {
|
||||
let Variant { ident, vis, attrs, id, data, disr_expr, span, is_placeholder: _ } = variant;
|
||||
visitor.visit_id(id);
|
||||
visit_attrs(visitor, attrs);
|
||||
visitor.visit_vis(vis);
|
||||
visitor.visit_ident(ident);
|
||||
visitor.visit_variant_data(data);
|
||||
visit_opt(disr_expr, |disr_expr| visitor.visit_anon_const(disr_expr));
|
||||
visitor.visit_span(span);
|
||||
}
|
||||
|
||||
pub fn walk_flat_map_variant<T: MutVisitor>(
|
||||
vis: &mut T,
|
||||
mut variant: Variant,
|
||||
|
|
@ -584,32 +503,6 @@ pub fn walk_flat_map_variant<T: MutVisitor>(
|
|||
smallvec![variant]
|
||||
}
|
||||
|
||||
fn walk_ident<T: MutVisitor>(vis: &mut T, Ident { name: _, span }: &mut Ident) {
|
||||
vis.visit_span(span);
|
||||
}
|
||||
|
||||
fn walk_path_segment<T: MutVisitor>(vis: &mut T, segment: &mut PathSegment) {
|
||||
let PathSegment { ident, id, args } = segment;
|
||||
vis.visit_id(id);
|
||||
vis.visit_ident(ident);
|
||||
visit_opt(args, |args| vis.visit_generic_args(args));
|
||||
}
|
||||
|
||||
fn walk_path<T: MutVisitor>(vis: &mut T, Path { segments, span, tokens: _ }: &mut Path) {
|
||||
for segment in segments {
|
||||
vis.visit_path_segment(segment);
|
||||
}
|
||||
vis.visit_span(span);
|
||||
}
|
||||
|
||||
fn walk_qself<T: MutVisitor>(vis: &mut T, qself: &mut Option<P<QSelf>>) {
|
||||
visit_opt(qself, |qself| {
|
||||
let QSelf { ty, path_span, position: _ } = &mut **qself;
|
||||
vis.visit_ty(ty);
|
||||
vis.visit_span(path_span);
|
||||
})
|
||||
}
|
||||
|
||||
fn walk_generic_args<T: MutVisitor>(vis: &mut T, generic_args: &mut GenericArgs) {
|
||||
match generic_args {
|
||||
GenericArgs::AngleBracketed(data) => vis.visit_angle_bracketed_parameter_data(data),
|
||||
|
|
@ -643,27 +536,6 @@ fn walk_parenthesized_parameter_data<T: MutVisitor>(vis: &mut T, args: &mut Pare
|
|||
vis.visit_span(inputs_span);
|
||||
}
|
||||
|
||||
fn walk_local<T: MutVisitor>(vis: &mut T, local: &mut Local) {
|
||||
let Local { id, super_, pat, ty, kind, span, colon_sp, attrs, tokens: _ } = local;
|
||||
visit_opt(super_, |sp| vis.visit_span(sp));
|
||||
vis.visit_id(id);
|
||||
visit_attrs(vis, attrs);
|
||||
vis.visit_pat(pat);
|
||||
visit_opt(ty, |ty| vis.visit_ty(ty));
|
||||
match kind {
|
||||
LocalKind::Decl => {}
|
||||
LocalKind::Init(init) => {
|
||||
vis.visit_expr(init);
|
||||
}
|
||||
LocalKind::InitElse(init, els) => {
|
||||
vis.visit_expr(init);
|
||||
vis.visit_block(els);
|
||||
}
|
||||
}
|
||||
visit_opt(colon_sp, |sp| vis.visit_span(sp));
|
||||
vis.visit_span(span);
|
||||
}
|
||||
|
||||
fn walk_attribute<T: MutVisitor>(vis: &mut T, attr: &mut Attribute) {
|
||||
let Attribute { kind, id: _, style: _, span } = attr;
|
||||
match kind {
|
||||
|
|
@ -729,18 +601,6 @@ fn walk_closure_binder<T: MutVisitor>(vis: &mut T, binder: &mut ClosureBinder) {
|
|||
}
|
||||
}
|
||||
|
||||
fn walk_coroutine_kind<T: MutVisitor>(vis: &mut T, coroutine_kind: &mut CoroutineKind) {
|
||||
match coroutine_kind {
|
||||
CoroutineKind::Async { span, closure_id, return_impl_trait_id }
|
||||
| CoroutineKind::Gen { span, closure_id, return_impl_trait_id }
|
||||
| CoroutineKind::AsyncGen { span, closure_id, return_impl_trait_id } => {
|
||||
vis.visit_id(closure_id);
|
||||
vis.visit_id(return_impl_trait_id);
|
||||
vis.visit_span(span);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn walk_fn<T: MutVisitor>(vis: &mut T, kind: FnKind<'_>) {
|
||||
match kind {
|
||||
FnKind::Fn(
|
||||
|
|
@ -925,35 +785,6 @@ fn walk_variant_data<T: MutVisitor>(vis: &mut T, vdata: &mut VariantData) {
|
|||
}
|
||||
}
|
||||
|
||||
fn walk_trait_ref<T: MutVisitor>(vis: &mut T, TraitRef { path, ref_id }: &mut TraitRef) {
|
||||
vis.visit_id(ref_id);
|
||||
vis.visit_path(path);
|
||||
}
|
||||
|
||||
fn walk_poly_trait_ref<T: MutVisitor>(vis: &mut T, p: &mut PolyTraitRef) {
|
||||
let PolyTraitRef { bound_generic_params, modifiers, trait_ref, span } = p;
|
||||
vis.visit_modifiers(modifiers);
|
||||
bound_generic_params.flat_map_in_place(|param| vis.flat_map_generic_param(param));
|
||||
vis.visit_trait_ref(trait_ref);
|
||||
vis.visit_span(span);
|
||||
}
|
||||
|
||||
fn walk_modifiers<V: MutVisitor>(vis: &mut V, m: &mut TraitBoundModifiers) {
|
||||
let TraitBoundModifiers { constness, asyncness, polarity } = m;
|
||||
match constness {
|
||||
BoundConstness::Never => {}
|
||||
BoundConstness::Always(span) | BoundConstness::Maybe(span) => vis.visit_span(span),
|
||||
}
|
||||
match asyncness {
|
||||
BoundAsyncness::Normal => {}
|
||||
BoundAsyncness::Async(span) => vis.visit_span(span),
|
||||
}
|
||||
match polarity {
|
||||
BoundPolarity::Positive => {}
|
||||
BoundPolarity::Negative(span) | BoundPolarity::Maybe(span) => vis.visit_span(span),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn walk_field_def<T: MutVisitor>(visitor: &mut T, fd: &mut FieldDef) {
|
||||
let FieldDef { span, ident, vis, id, ty, attrs, is_placeholder: _, safety, default } = fd;
|
||||
visitor.visit_id(id);
|
||||
|
|
@ -974,15 +805,6 @@ pub fn walk_flat_map_field_def<T: MutVisitor>(
|
|||
smallvec![fd]
|
||||
}
|
||||
|
||||
pub fn walk_expr_field<T: MutVisitor>(vis: &mut T, f: &mut ExprField) {
|
||||
let ExprField { ident, expr, span, is_shorthand: _, attrs, id, is_placeholder: _ } = f;
|
||||
vis.visit_id(id);
|
||||
visit_attrs(vis, attrs);
|
||||
vis.visit_ident(ident);
|
||||
vis.visit_expr(expr);
|
||||
vis.visit_span(span);
|
||||
}
|
||||
|
||||
pub fn walk_flat_map_expr_field<T: MutVisitor>(
|
||||
vis: &mut T,
|
||||
mut f: ExprField,
|
||||
|
|
@ -991,13 +813,6 @@ pub fn walk_flat_map_expr_field<T: MutVisitor>(
|
|||
smallvec![f]
|
||||
}
|
||||
|
||||
pub fn walk_block<T: MutVisitor>(vis: &mut T, block: &mut Block) {
|
||||
let Block { id, stmts, rules: _, span, tokens: _ } = block;
|
||||
vis.visit_id(id);
|
||||
stmts.flat_map_in_place(|stmt| vis.flat_map_stmt(stmt));
|
||||
vis.visit_span(span);
|
||||
}
|
||||
|
||||
pub fn walk_item_kind<K: WalkItemKind>(
|
||||
kind: &mut K,
|
||||
span: Span,
|
||||
|
|
@ -1009,16 +824,6 @@ pub fn walk_item_kind<K: WalkItemKind>(
|
|||
kind.walk(span, id, visibility, ctxt, vis)
|
||||
}
|
||||
|
||||
pub fn walk_crate<T: MutVisitor>(vis: &mut T, krate: &mut Crate) {
|
||||
let Crate { attrs, items, spans, id, is_placeholder: _ } = krate;
|
||||
vis.visit_id(id);
|
||||
visit_attrs(vis, attrs);
|
||||
items.flat_map_in_place(|item| vis.flat_map_item(item));
|
||||
let ModSpans { inner_span, inject_use_span } = spans;
|
||||
vis.visit_span(inner_span);
|
||||
vis.visit_span(inject_use_span);
|
||||
}
|
||||
|
||||
pub fn walk_flat_map_item(vis: &mut impl MutVisitor, mut item: P<Item>) -> SmallVec<[P<Item>; 1]> {
|
||||
vis.visit_item(&mut item);
|
||||
smallvec![item]
|
||||
|
|
@ -1041,57 +846,6 @@ pub fn walk_flat_map_assoc_item(
|
|||
smallvec![item]
|
||||
}
|
||||
|
||||
pub fn walk_pat<T: MutVisitor>(vis: &mut T, pat: &mut Pat) {
|
||||
let Pat { id, kind, span, tokens: _ } = pat;
|
||||
vis.visit_id(id);
|
||||
match kind {
|
||||
PatKind::Err(_guar) => {}
|
||||
PatKind::Missing | PatKind::Wild | PatKind::Rest | PatKind::Never => {}
|
||||
PatKind::Ident(_binding_mode, ident, sub) => {
|
||||
vis.visit_ident(ident);
|
||||
visit_opt(sub, |sub| vis.visit_pat(sub));
|
||||
}
|
||||
PatKind::Expr(e) => vis.visit_expr(e),
|
||||
PatKind::TupleStruct(qself, path, elems) => {
|
||||
vis.visit_qself(qself);
|
||||
vis.visit_path(path);
|
||||
visit_thin_vec(elems, |elem| vis.visit_pat(elem));
|
||||
}
|
||||
PatKind::Path(qself, path) => {
|
||||
vis.visit_qself(qself);
|
||||
vis.visit_path(path);
|
||||
}
|
||||
PatKind::Struct(qself, path, fields, _etc) => {
|
||||
vis.visit_qself(qself);
|
||||
vis.visit_path(path);
|
||||
fields.flat_map_in_place(|field| vis.flat_map_pat_field(field));
|
||||
}
|
||||
PatKind::Box(inner) => vis.visit_pat(inner),
|
||||
PatKind::Deref(inner) => vis.visit_pat(inner),
|
||||
PatKind::Ref(inner, _mutbl) => vis.visit_pat(inner),
|
||||
PatKind::Range(e1, e2, Spanned { span: _, node: _ }) => {
|
||||
visit_opt(e1, |e| vis.visit_expr(e));
|
||||
visit_opt(e2, |e| vis.visit_expr(e));
|
||||
vis.visit_span(span);
|
||||
}
|
||||
PatKind::Guard(p, e) => {
|
||||
vis.visit_pat(p);
|
||||
vis.visit_expr(e);
|
||||
}
|
||||
PatKind::Tuple(elems) | PatKind::Slice(elems) | PatKind::Or(elems) => {
|
||||
visit_thin_vec(elems, |elem| vis.visit_pat(elem))
|
||||
}
|
||||
PatKind::Paren(inner) => vis.visit_pat(inner),
|
||||
PatKind::MacCall(mac) => vis.visit_mac_call(mac),
|
||||
}
|
||||
vis.visit_span(span);
|
||||
}
|
||||
|
||||
fn walk_anon_const<T: MutVisitor>(vis: &mut T, AnonConst { id, value }: &mut AnonConst) {
|
||||
vis.visit_id(id);
|
||||
vis.visit_expr(value);
|
||||
}
|
||||
|
||||
fn walk_inline_asm<T: MutVisitor>(vis: &mut T, asm: &mut InlineAsm) {
|
||||
// FIXME: Visit spans inside all this currently ignored stuff.
|
||||
let InlineAsm {
|
||||
|
|
@ -1151,7 +905,7 @@ pub fn walk_expr<T: MutVisitor>(vis: &mut T, Expr { kind, id, span, attrs, token
|
|||
vis.visit_id(id);
|
||||
visit_attrs(vis, attrs);
|
||||
match kind {
|
||||
ExprKind::Array(exprs) => visit_thin_exprs(vis, exprs),
|
||||
ExprKind::Array(exprs) => visit_exprs(vis, exprs),
|
||||
ExprKind::ConstBlock(anon_const) => {
|
||||
vis.visit_anon_const(anon_const);
|
||||
}
|
||||
|
|
@ -1159,10 +913,10 @@ pub fn walk_expr<T: MutVisitor>(vis: &mut T, Expr { kind, id, span, attrs, token
|
|||
vis.visit_expr(expr);
|
||||
vis.visit_anon_const(count);
|
||||
}
|
||||
ExprKind::Tup(exprs) => visit_thin_exprs(vis, exprs),
|
||||
ExprKind::Tup(exprs) => visit_exprs(vis, exprs),
|
||||
ExprKind::Call(f, args) => {
|
||||
vis.visit_expr(f);
|
||||
visit_thin_exprs(vis, args);
|
||||
visit_exprs(vis, args);
|
||||
}
|
||||
ExprKind::MethodCall(box MethodCall {
|
||||
seg: PathSegment { ident, id, args: seg_args },
|
||||
|
|
@ -1174,7 +928,7 @@ pub fn walk_expr<T: MutVisitor>(vis: &mut T, Expr { kind, id, span, attrs, token
|
|||
vis.visit_id(id);
|
||||
vis.visit_ident(ident);
|
||||
visit_opt(seg_args, |args| vis.visit_generic_args(args));
|
||||
visit_thin_exprs(vis, call_args);
|
||||
visit_exprs(vis, call_args);
|
||||
vis.visit_span(span);
|
||||
}
|
||||
ExprKind::Binary(binop, lhs, rhs) => {
|
||||
|
|
|
|||
|
|
@ -1,209 +1,11 @@
|
|||
//! The AST pointer.
|
||||
//!
|
||||
//! Provides [`P<T>`][struct@P], an owned smart pointer.
|
||||
//!
|
||||
//! # Motivations and benefits
|
||||
//!
|
||||
//! * **Identity**: sharing AST nodes is problematic for the various analysis
|
||||
//! passes (e.g., one may be able to bypass the borrow checker with a shared
|
||||
//! `ExprKind::AddrOf` node taking a mutable borrow).
|
||||
//!
|
||||
//! * **Efficiency**: folding can reuse allocation space for `P<T>` and `Vec<T>`,
|
||||
//! the latter even when the input and output types differ (as it would be the
|
||||
//! case with arenas or a GADT AST using type parameters to toggle features).
|
||||
//!
|
||||
//! * **Maintainability**: `P<T>` provides an interface, which can remain fully
|
||||
//! functional even if the implementation changes (using a special thread-local
|
||||
//! heap, for example). Moreover, a switch to, e.g., `P<'a, T>` would be easy
|
||||
//! and mostly automated.
|
||||
|
||||
use std::fmt::{self, Debug, Display};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::{slice, vec};
|
||||
|
||||
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
|
||||
use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
|
||||
/// An owned smart pointer.
|
||||
/// A pointer type that uniquely owns a heap allocation of type T.
|
||||
///
|
||||
/// See the [module level documentation][crate::ptr] for details.
|
||||
pub struct P<T: ?Sized> {
|
||||
ptr: Box<T>,
|
||||
}
|
||||
/// This used to be its own type, but now it's just a typedef for `Box` and we are planning to
|
||||
/// remove it soon.
|
||||
pub type P<T> = Box<T>;
|
||||
|
||||
/// Construct a `P<T>` from a `T` value.
|
||||
#[allow(non_snake_case)]
|
||||
pub fn P<T: 'static>(value: T) -> P<T> {
|
||||
P { ptr: Box::new(value) }
|
||||
}
|
||||
|
||||
impl<T: 'static> P<T> {
|
||||
/// Move out of the pointer.
|
||||
/// Intended for chaining transformations not covered by `map`.
|
||||
pub fn and_then<U, F>(self, f: F) -> U
|
||||
where
|
||||
F: FnOnce(T) -> U,
|
||||
{
|
||||
f(*self.ptr)
|
||||
}
|
||||
|
||||
/// Equivalent to `and_then(|x| x)`.
|
||||
pub fn into_inner(self) -> T {
|
||||
*self.ptr
|
||||
}
|
||||
|
||||
/// Produce a new `P<T>` from `self` without reallocating.
|
||||
pub fn map<F>(mut self, f: F) -> P<T>
|
||||
where
|
||||
F: FnOnce(T) -> T,
|
||||
{
|
||||
let x = f(*self.ptr);
|
||||
*self.ptr = x;
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Optionally produce a new `P<T>` from `self` without reallocating.
|
||||
pub fn filter_map<F>(mut self, f: F) -> Option<P<T>>
|
||||
where
|
||||
F: FnOnce(T) -> Option<T>,
|
||||
{
|
||||
*self.ptr = f(*self.ptr)?;
|
||||
Some(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Deref for P<T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &T {
|
||||
&self.ptr
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> DerefMut for P<T> {
|
||||
fn deref_mut(&mut self) -> &mut T {
|
||||
&mut self.ptr
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static + Clone> Clone for P<T> {
|
||||
fn clone(&self) -> P<T> {
|
||||
P((**self).clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + Debug> Debug for P<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
Debug::fmt(&self.ptr, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Display> Display for P<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
Display::fmt(&**self, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> fmt::Pointer for P<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Pointer::fmt(&self.ptr, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: Decoder, T: 'static + Decodable<D>> Decodable<D> for P<T> {
|
||||
fn decode(d: &mut D) -> P<T> {
|
||||
P(Decodable::decode(d))
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: Encoder, T: Encodable<S>> Encodable<S> for P<T> {
|
||||
fn encode(&self, s: &mut S) {
|
||||
(**self).encode(s);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> P<[T]> {
|
||||
// FIXME(const-hack) make this const again
|
||||
pub fn new() -> P<[T]> {
|
||||
P { ptr: Box::default() }
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
pub fn from_vec(v: Vec<T>) -> P<[T]> {
|
||||
P { ptr: v.into_boxed_slice() }
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
pub fn into_vec(self) -> Vec<T> {
|
||||
self.ptr.into_vec()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Default for P<[T]> {
|
||||
/// Creates an empty `P<[T]>`.
|
||||
fn default() -> P<[T]> {
|
||||
P::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone> Clone for P<[T]> {
|
||||
fn clone(&self) -> P<[T]> {
|
||||
P::from_vec(self.to_vec())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<Vec<T>> for P<[T]> {
|
||||
fn from(v: Vec<T>) -> Self {
|
||||
P::from_vec(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<P<[T]>> for Vec<T> {
|
||||
fn from(val: P<[T]>) -> Self {
|
||||
val.into_vec()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> FromIterator<T> for P<[T]> {
|
||||
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> P<[T]> {
|
||||
P::from_vec(iter.into_iter().collect())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> IntoIterator for P<[T]> {
|
||||
type Item = T;
|
||||
type IntoIter = vec::IntoIter<T>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.into_vec().into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> IntoIterator for &'a P<[T]> {
|
||||
type Item = &'a T;
|
||||
type IntoIter = slice::Iter<'a, T>;
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.ptr.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: Encoder, T: Encodable<S>> Encodable<S> for P<[T]> {
|
||||
fn encode(&self, s: &mut S) {
|
||||
Encodable::encode(&**self, s);
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: Decoder, T: Decodable<D>> Decodable<D> for P<[T]> {
|
||||
fn decode(d: &mut D) -> P<[T]> {
|
||||
P::from_vec(Decodable::decode(d))
|
||||
}
|
||||
}
|
||||
|
||||
impl<CTX, T> HashStable<CTX> for P<T>
|
||||
where
|
||||
T: ?Sized + HashStable<CTX>,
|
||||
{
|
||||
fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) {
|
||||
(**self).hash_stable(hcx, hasher);
|
||||
}
|
||||
pub fn P<T>(value: T) -> P<T> {
|
||||
Box::new(value)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,7 +57,9 @@ impl TokenTree {
|
|||
match (self, other) {
|
||||
(TokenTree::Token(token, _), TokenTree::Token(token2, _)) => token.kind == token2.kind,
|
||||
(TokenTree::Delimited(.., delim, tts), TokenTree::Delimited(.., delim2, tts2)) => {
|
||||
delim == delim2 && tts.eq_unspanned(tts2)
|
||||
delim == delim2
|
||||
&& tts.len() == tts2.len()
|
||||
&& tts.iter().zip(tts2.iter()).all(|(a, b)| a.eq_unspanned(b))
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
|
|
@ -694,18 +696,6 @@ impl TokenStream {
|
|||
TokenStreamIter::new(self)
|
||||
}
|
||||
|
||||
/// Compares two `TokenStream`s, checking equality without regarding span information.
|
||||
pub fn eq_unspanned(&self, other: &TokenStream) -> bool {
|
||||
let mut iter1 = self.iter();
|
||||
let mut iter2 = other.iter();
|
||||
for (tt1, tt2) in iter::zip(&mut iter1, &mut iter2) {
|
||||
if !tt1.eq_unspanned(tt2) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
iter1.next().is_none() && iter2.next().is_none()
|
||||
}
|
||||
|
||||
/// Create a token stream containing a single token with alone spacing. The
|
||||
/// spacing used for the final token in a constructed stream doesn't matter
|
||||
/// because it's never used. In practice we arbitrarily use
|
||||
|
|
|
|||
|
|
@ -121,6 +121,10 @@ pub enum LifetimeCtxt {
|
|||
/// explicitly, you need to override each method. (And you also need
|
||||
/// to monitor future changes to `Visitor` in case a new method with a
|
||||
/// new default implementation gets introduced.)
|
||||
///
|
||||
/// Every `walk_*` method uses deconstruction to access fields of structs and
|
||||
/// enums. This will result in a compile error if a field is added, which makes
|
||||
/// it more likely the appropriate visit call will be added for it.
|
||||
pub trait Visitor<'ast>: Sized {
|
||||
/// The result type of the `visit_*` methods. Can be either `()`,
|
||||
/// or `ControlFlow<T>`.
|
||||
|
|
@ -210,14 +214,11 @@ pub trait Visitor<'ast>: Sized {
|
|||
walk_poly_trait_ref(self, t)
|
||||
}
|
||||
fn visit_variant_data(&mut self, s: &'ast VariantData) -> Self::Result {
|
||||
walk_struct_def(self, s)
|
||||
walk_variant_data(self, s)
|
||||
}
|
||||
fn visit_field_def(&mut self, s: &'ast FieldDef) -> Self::Result {
|
||||
walk_field_def(self, s)
|
||||
}
|
||||
fn visit_enum_def(&mut self, enum_definition: &'ast EnumDef) -> Self::Result {
|
||||
walk_enum_def(self, enum_definition)
|
||||
}
|
||||
fn visit_variant(&mut self, v: &'ast Variant) -> Self::Result {
|
||||
walk_variant(self, v)
|
||||
}
|
||||
|
|
@ -233,19 +234,21 @@ pub trait Visitor<'ast>: Sized {
|
|||
fn visit_mac_call(&mut self, mac: &'ast MacCall) -> Self::Result {
|
||||
walk_mac(self, mac)
|
||||
}
|
||||
fn visit_macro_def(&mut self, _mac: &'ast MacroDef, _id: NodeId) -> Self::Result {
|
||||
fn visit_id(&mut self, _id: NodeId) -> Self::Result {
|
||||
Self::Result::output()
|
||||
}
|
||||
fn visit_path(&mut self, path: &'ast Path, _id: NodeId) -> Self::Result {
|
||||
fn visit_macro_def(&mut self, _mac: &'ast MacroDef) -> Self::Result {
|
||||
Self::Result::output()
|
||||
}
|
||||
fn visit_path(&mut self, path: &'ast Path) -> Self::Result {
|
||||
walk_path(self, path)
|
||||
}
|
||||
fn visit_use_tree(
|
||||
&mut self,
|
||||
use_tree: &'ast UseTree,
|
||||
id: NodeId,
|
||||
_nested: bool,
|
||||
) -> Self::Result {
|
||||
walk_use_tree(self, use_tree, id)
|
||||
fn visit_use_tree(&mut self, use_tree: &'ast UseTree) -> Self::Result {
|
||||
walk_use_tree(self, use_tree)
|
||||
}
|
||||
fn visit_nested_use_tree(&mut self, use_tree: &'ast UseTree, id: NodeId) -> Self::Result {
|
||||
try_visit!(self.visit_id(id));
|
||||
self.visit_use_tree(use_tree)
|
||||
}
|
||||
fn visit_path_segment(&mut self, path_segment: &'ast PathSegment) -> Self::Result {
|
||||
walk_path_segment(self, path_segment)
|
||||
|
|
@ -295,8 +298,8 @@ pub trait Visitor<'ast>: Sized {
|
|||
fn visit_capture_by(&mut self, _capture_by: &'ast CaptureBy) -> Self::Result {
|
||||
Self::Result::output()
|
||||
}
|
||||
fn visit_coroutine_kind(&mut self, _coroutine_kind: &'ast CoroutineKind) -> Self::Result {
|
||||
Self::Result::output()
|
||||
fn visit_coroutine_kind(&mut self, coroutine_kind: &'ast CoroutineKind) -> Self::Result {
|
||||
walk_coroutine_kind(self, coroutine_kind)
|
||||
}
|
||||
fn visit_fn_decl(&mut self, fn_decl: &'ast FnDecl) -> Self::Result {
|
||||
walk_fn_decl(self, fn_decl)
|
||||
|
|
@ -334,17 +337,14 @@ macro_rules! common_visitor_and_walkers {
|
|||
$(${ignore($lt)}V::Result::output())?
|
||||
}
|
||||
|
||||
// this is only used by the MutVisitor. We include this symmetry here to make writing other functions easier
|
||||
/// helper since `Visitor` wants `NodeId` but `MutVisitor` wants `&mut NodeId`
|
||||
$(${ignore($lt)}
|
||||
#[expect(unused, rustc::pass_by_value)]
|
||||
#[inline]
|
||||
#[expect(rustc::pass_by_value)]
|
||||
)?
|
||||
#[inline]
|
||||
fn visit_id<$($lt,)? V: $Visitor$(<$lt>)?>(visitor: &mut V, id: &$($lt)? $($mut)? NodeId) $(-> <V as Visitor<$lt>>::Result)? {
|
||||
$(
|
||||
${ignore($mut)}
|
||||
visitor.visit_id(id);
|
||||
)?
|
||||
$(${ignore($lt)}V::Result::output())?
|
||||
// deref `&NodeId` into `NodeId` only for `Visitor`
|
||||
visitor.visit_id( $(${ignore($lt)} * )? id)
|
||||
}
|
||||
|
||||
// this is only used by the MutVisitor. We include this symmetry here to make writing other functions easier
|
||||
|
|
@ -374,13 +374,39 @@ macro_rules! common_visitor_and_walkers {
|
|||
}
|
||||
}
|
||||
|
||||
fn visit_polarity<$($lt,)? V: $Visitor$(<$lt>)?>(vis: &mut V, polarity: &$($lt)? $($mut)? ImplPolarity) $(-> <V as Visitor<$lt>>::Result)? {
|
||||
fn visit_polarity<$($lt,)? V: $Visitor$(<$lt>)?>(
|
||||
vis: &mut V,
|
||||
polarity: &$($lt)? $($mut)? ImplPolarity,
|
||||
) $(-> <V as Visitor<$lt>>::Result)? {
|
||||
match polarity {
|
||||
ImplPolarity::Positive => { $(<V as Visitor<$lt>>::Result::output())? }
|
||||
ImplPolarity::Negative(span) => visit_span(vis, span),
|
||||
}
|
||||
}
|
||||
|
||||
$(${ignore($lt)}
|
||||
#[inline]
|
||||
)?
|
||||
fn visit_modifiers<$($lt,)? V: $Visitor$(<$lt>)?>(
|
||||
vis: &mut V,
|
||||
m: &$($lt)? $($mut)? TraitBoundModifiers
|
||||
) $(-> <V as Visitor<$lt>>::Result)? {
|
||||
let TraitBoundModifiers { constness, asyncness, polarity } = m;
|
||||
match constness {
|
||||
BoundConstness::Never => {}
|
||||
BoundConstness::Always(span) | BoundConstness::Maybe(span) => try_visit!(visit_span(vis, span)),
|
||||
}
|
||||
match asyncness {
|
||||
BoundAsyncness::Normal => {}
|
||||
BoundAsyncness::Async(span) => try_visit!(visit_span(vis, span)),
|
||||
}
|
||||
match polarity {
|
||||
BoundPolarity::Positive => {}
|
||||
BoundPolarity::Negative(span) | BoundPolarity::Maybe(span) => try_visit!(visit_span(vis, span)),
|
||||
}
|
||||
$(<V as Visitor<$lt>>::Result::output())?
|
||||
}
|
||||
|
||||
fn visit_bounds<$($lt,)? V: $Visitor$(<$lt>)?>(visitor: &mut V, bounds: &$($lt)? $($mut)? GenericBounds, ctxt: BoundKind) $(-> <V as Visitor<$lt>>::Result)? {
|
||||
walk_list!(visitor, visit_param_bound, bounds, ctxt);
|
||||
$(<V as Visitor<$lt>>::Result::output())?
|
||||
|
|
@ -442,8 +468,7 @@ macro_rules! common_visitor_and_walkers {
|
|||
) $(-> <V as Visitor<$lt>>::Result)? {
|
||||
match self {
|
||||
ItemKind::ExternCrate(_orig_name, ident) => vis.visit_ident(ident),
|
||||
// FIXME(fee1-dead): look into this weird assymetry
|
||||
ItemKind::Use(use_tree) => vis.visit_use_tree(use_tree$(${ignore($lt)}, id, false)?),
|
||||
ItemKind::Use(use_tree) => vis.visit_use_tree(use_tree),
|
||||
ItemKind::Static(box StaticItem {
|
||||
ident,
|
||||
ty,
|
||||
|
|
@ -474,12 +499,7 @@ macro_rules! common_visitor_and_walkers {
|
|||
ModSpans { inner_span, inject_use_span },
|
||||
_,
|
||||
) => {
|
||||
$(${ignore($mut)}
|
||||
items.flat_map_in_place(|item| vis.flat_map_item(item));
|
||||
)?
|
||||
$(${ignore($lt)}
|
||||
walk_list!(vis, visit_item, items);
|
||||
)?
|
||||
try_visit!(visit_items(vis, items));
|
||||
try_visit!(visit_span(vis, inner_span));
|
||||
try_visit!(visit_span(vis, inject_use_span));
|
||||
}
|
||||
|
|
@ -511,10 +531,7 @@ macro_rules! common_visitor_and_walkers {
|
|||
ItemKind::Enum(ident, generics, enum_definition) => {
|
||||
try_visit!(vis.visit_ident(ident));
|
||||
try_visit!(vis.visit_generics(generics));
|
||||
$(${ignore($mut)}
|
||||
enum_definition.variants.flat_map_in_place(|variant| vis.flat_map_variant(variant));
|
||||
)?
|
||||
$(${ignore($lt)}vis.visit_enum_def(enum_definition))?
|
||||
visit_variants(vis, &$($mut)? enum_definition.variants)
|
||||
}
|
||||
ItemKind::Struct(ident, generics, variant_data)
|
||||
| ItemKind::Union(ident, generics, variant_data) => {
|
||||
|
|
@ -539,35 +556,14 @@ macro_rules! common_visitor_and_walkers {
|
|||
try_visit!(visit_polarity(vis, polarity));
|
||||
visit_opt!(vis, visit_trait_ref, of_trait);
|
||||
try_visit!(vis.visit_ty(self_ty));
|
||||
$(${ignore($mut)}
|
||||
items.flat_map_in_place(|item| {
|
||||
vis.flat_map_assoc_item(item, AssocCtxt::Impl { of_trait: of_trait.is_some() })
|
||||
});
|
||||
)?
|
||||
$(${ignore($lt)}
|
||||
walk_list!(
|
||||
vis,
|
||||
visit_assoc_item,
|
||||
items,
|
||||
AssocCtxt::Impl { of_trait: of_trait.is_some() }
|
||||
);
|
||||
<V as Visitor<$lt>>::Result::output()
|
||||
)?
|
||||
visit_assoc_items(vis, items, AssocCtxt::Impl { of_trait: of_trait.is_some() })
|
||||
}
|
||||
ItemKind::Trait(box Trait { safety, is_auto: _, ident, generics, bounds, items }) => {
|
||||
try_visit!(visit_safety(vis, safety));
|
||||
try_visit!(vis.visit_ident(ident));
|
||||
try_visit!(vis.visit_generics(generics));
|
||||
try_visit!(visit_bounds(vis, bounds, BoundKind::Bound));
|
||||
$(${ignore($mut)}
|
||||
items.flat_map_in_place(|item| {
|
||||
vis.flat_map_assoc_item(item, AssocCtxt::Trait)
|
||||
});
|
||||
)?
|
||||
$(${ignore($lt)}
|
||||
walk_list!(vis, visit_assoc_item, items, AssocCtxt::Trait);
|
||||
<V as Visitor<$lt>>::Result::output()
|
||||
)?
|
||||
visit_assoc_items(vis, items, AssocCtxt::Trait)
|
||||
}
|
||||
ItemKind::TraitAlias(ident, generics, bounds) => {
|
||||
try_visit!(vis.visit_ident(ident));
|
||||
|
|
@ -577,8 +573,7 @@ macro_rules! common_visitor_and_walkers {
|
|||
ItemKind::MacCall(m) => vis.visit_mac_call(m),
|
||||
ItemKind::MacroDef(ident, def) => {
|
||||
try_visit!(vis.visit_ident(ident));
|
||||
// FIXME(fee1-dead) assymetry
|
||||
vis.visit_macro_def(def$(${ignore($lt)}, id)?)
|
||||
vis.visit_macro_def(def)
|
||||
}
|
||||
ItemKind::Delegation(box Delegation {
|
||||
id,
|
||||
|
|
@ -591,7 +586,7 @@ macro_rules! common_visitor_and_walkers {
|
|||
}) => {
|
||||
try_visit!(visit_id(vis, id));
|
||||
try_visit!(vis.visit_qself(qself));
|
||||
try_visit!(vis.visit_path(path$(${ignore($lt)}, *id)?));
|
||||
try_visit!(vis.visit_path(path));
|
||||
try_visit!(vis.visit_ident(ident));
|
||||
visit_opt!(vis, visit_ident, rename);
|
||||
visit_opt!(vis, visit_block, body);
|
||||
|
|
@ -599,7 +594,7 @@ macro_rules! common_visitor_and_walkers {
|
|||
}
|
||||
ItemKind::DelegationMac(box DelegationMac { qself, prefix, suffixes, body }) => {
|
||||
try_visit!(vis.visit_qself(qself));
|
||||
try_visit!(vis.visit_path(prefix$(${ignore($lt)}, id)?));
|
||||
try_visit!(vis.visit_path(prefix));
|
||||
if let Some(suffixes) = suffixes {
|
||||
for (ident, rename) in suffixes {
|
||||
try_visit!(vis.visit_ident(ident));
|
||||
|
|
@ -613,7 +608,10 @@ macro_rules! common_visitor_and_walkers {
|
|||
}
|
||||
}
|
||||
|
||||
fn walk_const_item<$($lt,)? V: $Visitor$(<$lt>)?>(vis: &mut V, item: &$($lt)? $($mut)? ConstItem) $(-> <V as Visitor<$lt>>::Result)? {
|
||||
fn walk_const_item<$($lt,)? V: $Visitor$(<$lt>)?>(
|
||||
vis: &mut V,
|
||||
item: &$($lt)? $($mut)? ConstItem,
|
||||
) $(-> <V as Visitor<$lt>>::Result)? {
|
||||
let ConstItem { defaultness, ident, generics, ty, expr, define_opaque } = item;
|
||||
try_visit!(visit_defaultness(vis, defaultness));
|
||||
try_visit!(vis.visit_ident(ident));
|
||||
|
|
@ -626,13 +624,7 @@ macro_rules! common_visitor_and_walkers {
|
|||
fn walk_foreign_mod<$($lt,)? V: $Visitor$(<$lt>)?>(vis: &mut V, foreign_mod: &$($lt)? $($mut)? ForeignMod) $(-> <V as Visitor<$lt>>::Result)? {
|
||||
let ForeignMod { extern_span: _, safety, abi: _, items } = foreign_mod;
|
||||
try_visit!(visit_safety(vis, safety));
|
||||
$(${ignore($mut)}
|
||||
items.flat_map_in_place(|item| vis.flat_map_foreign_item(item));
|
||||
)?
|
||||
$(
|
||||
walk_list!(vis, visit_foreign_item, items);
|
||||
<V as Visitor<$lt>>::Result::output()
|
||||
)?
|
||||
visit_foreign_items(vis, items)
|
||||
}
|
||||
|
||||
fn walk_define_opaques<$($lt,)? V: $Visitor$(<$lt>)?>(
|
||||
|
|
@ -642,8 +634,7 @@ macro_rules! common_visitor_and_walkers {
|
|||
if let Some(define_opaque) = define_opaque {
|
||||
for (id, path) in define_opaque {
|
||||
try_visit!(visit_id(visitor, id));
|
||||
// FIXME(fee1-dead): look into this weird assymetry
|
||||
try_visit!(visitor.visit_path(path$(${ignore($lt)}, *id)?));
|
||||
try_visit!(visitor.visit_path(path));
|
||||
}
|
||||
}
|
||||
$(<V as Visitor<$lt>>::Result::output())?
|
||||
|
|
@ -699,7 +690,7 @@ macro_rules! common_visitor_and_walkers {
|
|||
}) => {
|
||||
try_visit!(visit_id(vis, id));
|
||||
try_visit!(vis.visit_qself(qself));
|
||||
try_visit!(vis.visit_path(path $(${ignore($lt)}, *id)?));
|
||||
try_visit!(vis.visit_path(path));
|
||||
try_visit!(vis.visit_ident(ident));
|
||||
visit_opt!(vis, visit_ident, rename);
|
||||
visit_opt!(vis, visit_block, body);
|
||||
|
|
@ -707,7 +698,7 @@ macro_rules! common_visitor_and_walkers {
|
|||
}
|
||||
AssocItemKind::DelegationMac(box DelegationMac { qself, prefix, suffixes, body }) => {
|
||||
try_visit!(vis.visit_qself(qself));
|
||||
try_visit!(vis.visit_path(prefix$(${ignore($lt)}, id)?));
|
||||
try_visit!(vis.visit_path(prefix));
|
||||
if let Some(suffixes) = suffixes {
|
||||
for (ident, rename) in suffixes {
|
||||
try_visit!(vis.visit_ident(ident));
|
||||
|
|
@ -773,188 +764,356 @@ macro_rules! common_visitor_and_walkers {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn walk_coroutine_kind<$($lt,)? V: $Visitor$(<$lt>)?>(
|
||||
vis: &mut V,
|
||||
coroutine_kind: &$($lt)? $($mut)? CoroutineKind,
|
||||
) $(-> <V as Visitor<$lt>>::Result)? {
|
||||
let (CoroutineKind::Async { span, closure_id, return_impl_trait_id }
|
||||
| CoroutineKind::Gen { span, closure_id, return_impl_trait_id }
|
||||
| CoroutineKind::AsyncGen { span, closure_id, return_impl_trait_id })
|
||||
= coroutine_kind;
|
||||
try_visit!(visit_id(vis, closure_id));
|
||||
try_visit!(visit_id(vis, return_impl_trait_id));
|
||||
visit_span(vis, span)
|
||||
}
|
||||
|
||||
pub fn walk_pat<$($lt,)? V: $Visitor$(<$lt>)?>(
|
||||
vis: &mut V,
|
||||
pattern: &$($lt)? $($mut)? Pat
|
||||
) $(-> <V as Visitor<$lt>>::Result)? {
|
||||
let Pat { id, kind, span, tokens: _ } = pattern;
|
||||
try_visit!(visit_id(vis, id));
|
||||
match kind {
|
||||
PatKind::Err(_guar) => {}
|
||||
PatKind::Missing | PatKind::Wild | PatKind::Rest | PatKind::Never => {}
|
||||
PatKind::Ident(_bmode, ident, optional_subpattern) => {
|
||||
try_visit!(vis.visit_ident(ident));
|
||||
visit_opt!(vis, visit_pat, optional_subpattern);
|
||||
}
|
||||
PatKind::Expr(expression) => try_visit!(vis.visit_expr(expression)),
|
||||
PatKind::TupleStruct(opt_qself, path, elems) => {
|
||||
try_visit!(vis.visit_qself(opt_qself));
|
||||
try_visit!(vis.visit_path(path));
|
||||
walk_list!(vis, visit_pat, elems);
|
||||
}
|
||||
PatKind::Path(opt_qself, path) => {
|
||||
try_visit!(vis.visit_qself(opt_qself));
|
||||
try_visit!(vis.visit_path(path))
|
||||
}
|
||||
PatKind::Struct(opt_qself, path, fields, _rest) => {
|
||||
try_visit!(vis.visit_qself(opt_qself));
|
||||
try_visit!(vis.visit_path(path));
|
||||
try_visit!(visit_pat_fields(vis, fields));
|
||||
}
|
||||
PatKind::Box(subpattern) | PatKind::Deref(subpattern) | PatKind::Paren(subpattern) => {
|
||||
try_visit!(vis.visit_pat(subpattern));
|
||||
}
|
||||
PatKind::Ref(subpattern, _ /*mutbl*/) => {
|
||||
try_visit!(vis.visit_pat(subpattern));
|
||||
}
|
||||
PatKind::Range(lower_bound, upper_bound, _end) => {
|
||||
visit_opt!(vis, visit_expr, lower_bound);
|
||||
visit_opt!(vis, visit_expr, upper_bound);
|
||||
try_visit!(visit_span(vis, span));
|
||||
}
|
||||
PatKind::Guard(subpattern, guard_condition) => {
|
||||
try_visit!(vis.visit_pat(subpattern));
|
||||
try_visit!(vis.visit_expr(guard_condition));
|
||||
}
|
||||
PatKind::Tuple(elems) | PatKind::Slice(elems) | PatKind::Or(elems) => {
|
||||
walk_list!(vis, visit_pat, elems);
|
||||
}
|
||||
PatKind::MacCall(mac) => try_visit!(vis.visit_mac_call(mac)),
|
||||
}
|
||||
visit_span(vis, span)
|
||||
}
|
||||
|
||||
pub fn walk_anon_const<$($lt,)? V: $Visitor$(<$lt>)?>(
|
||||
vis: &mut V,
|
||||
constant: &$($lt)? $($mut)? AnonConst,
|
||||
) $(-> <V as Visitor<$lt>>::Result)? {
|
||||
let AnonConst { id, value } = constant;
|
||||
try_visit!(visit_id(vis, id));
|
||||
vis.visit_expr(value)
|
||||
}
|
||||
|
||||
pub fn walk_path_segment<$($lt,)? V: $Visitor$(<$lt>)?>(
|
||||
vis: &mut V,
|
||||
segment: &$($lt)? $($mut)? PathSegment,
|
||||
) $(-> <V as Visitor<$lt>>::Result)? {
|
||||
let PathSegment { ident, id, args } = segment;
|
||||
try_visit!(visit_id(vis, id));
|
||||
try_visit!(vis.visit_ident(ident));
|
||||
visit_opt!(vis, visit_generic_args, args);
|
||||
$(<V as Visitor<$lt>>::Result::output())?
|
||||
}
|
||||
|
||||
pub fn walk_block<$($lt,)? V: $Visitor$(<$lt>)?>(
|
||||
vis: &mut V,
|
||||
block: &$($lt)? $($mut)? Block
|
||||
) $(-> <V as Visitor<$lt>>::Result)? {
|
||||
let Block { stmts, id, rules: _, span, tokens: _ } = block;
|
||||
try_visit!(visit_id(vis, id));
|
||||
try_visit!(visit_stmts(vis, stmts));
|
||||
visit_span(vis, span)
|
||||
}
|
||||
|
||||
|
||||
pub fn walk_ty<$($lt,)? V: $Visitor$(<$lt>)?>(
|
||||
vis: &mut V, ty: &$($lt)? $($mut)? Ty
|
||||
) $(-> <V as Visitor<$lt>>::Result)? {
|
||||
let Ty { id, kind, span, tokens: _ } = ty;
|
||||
try_visit!(visit_id(vis, id));
|
||||
match kind {
|
||||
TyKind::Err(_guar) => {}
|
||||
TyKind::Infer | TyKind::ImplicitSelf | TyKind::Dummy | TyKind::Never | TyKind::CVarArgs => {}
|
||||
TyKind::Slice(ty) | TyKind::Paren(ty) => try_visit!(vis.visit_ty(ty)),
|
||||
TyKind::Ptr(MutTy { ty, mutbl: _ }) => try_visit!(vis.visit_ty(ty)),
|
||||
TyKind::Ref(opt_lifetime, MutTy { ty, mutbl: _ })
|
||||
| TyKind::PinnedRef(opt_lifetime, MutTy { ty, mutbl: _ }) => {
|
||||
// FIXME(fee1-dead) asymmetry
|
||||
visit_opt!(vis, visit_lifetime, opt_lifetime$(${ignore($lt)}, LifetimeCtxt::Ref)?);
|
||||
try_visit!(vis.visit_ty(ty));
|
||||
}
|
||||
TyKind::Tup(tuple_element_types) => {
|
||||
walk_list!(vis, visit_ty, tuple_element_types);
|
||||
}
|
||||
TyKind::BareFn(function_declaration) => {
|
||||
let BareFnTy { safety, ext: _, generic_params, decl, decl_span } =
|
||||
&$($mut)? **function_declaration;
|
||||
visit_safety(vis, safety);
|
||||
try_visit!(visit_generic_params(vis, generic_params));
|
||||
try_visit!(vis.visit_fn_decl(decl));
|
||||
try_visit!(visit_span(vis, decl_span));
|
||||
}
|
||||
TyKind::UnsafeBinder(binder) => {
|
||||
try_visit!(visit_generic_params(vis, &$($mut)? binder.generic_params));
|
||||
try_visit!(vis.visit_ty(&$($mut)? binder.inner_ty));
|
||||
}
|
||||
TyKind::Path(maybe_qself, path) => {
|
||||
try_visit!(vis.visit_qself(maybe_qself));
|
||||
try_visit!(vis.visit_path(path));
|
||||
}
|
||||
TyKind::Pat(ty, pat) => {
|
||||
try_visit!(vis.visit_ty(ty));
|
||||
try_visit!(vis.visit_ty_pat(pat));
|
||||
}
|
||||
TyKind::Array(ty, length) => {
|
||||
try_visit!(vis.visit_ty(ty));
|
||||
try_visit!(vis.visit_anon_const(length));
|
||||
}
|
||||
TyKind::TraitObject(bounds, _syntax) => {
|
||||
walk_list!(vis, visit_param_bound, bounds, BoundKind::TraitObject);
|
||||
}
|
||||
TyKind::ImplTrait(id, bounds) => {
|
||||
try_visit!(visit_id(vis, id));
|
||||
walk_list!(vis, visit_param_bound, bounds, BoundKind::Impl);
|
||||
}
|
||||
TyKind::Typeof(expression) => try_visit!(vis.visit_anon_const(expression)),
|
||||
|
||||
TyKind::MacCall(mac) => try_visit!(vis.visit_mac_call(mac)),
|
||||
}
|
||||
visit_span(vis, span)
|
||||
}
|
||||
|
||||
pub fn walk_crate<$($lt,)? V: $Visitor$(<$lt>)?>(
|
||||
vis: &mut V,
|
||||
krate: &$($lt)? $($mut)? Crate,
|
||||
) $(-> <V as Visitor<$lt>>::Result)? {
|
||||
let Crate { attrs, items, spans, id, is_placeholder: _ } = krate;
|
||||
try_visit!(visit_id(vis, id));
|
||||
walk_list!(vis, visit_attribute, attrs);
|
||||
try_visit!(visit_items(vis, items));
|
||||
let ModSpans { inner_span, inject_use_span } = spans;
|
||||
try_visit!(visit_span(vis, inner_span));
|
||||
visit_span(vis, inject_use_span)
|
||||
}
|
||||
|
||||
pub fn walk_local<$($lt,)? V: $Visitor$(<$lt>)?>(
|
||||
vis: &mut V,
|
||||
local: &$($lt)? $($mut)? Local,
|
||||
) $(-> <V as Visitor<$lt>>::Result)? {
|
||||
let Local { id, super_, pat, ty, kind, span, colon_sp, attrs, tokens: _ } = local;
|
||||
if let Some(sp) = super_ {
|
||||
try_visit!(visit_span(vis, sp));
|
||||
}
|
||||
try_visit!(visit_id(vis, id));
|
||||
walk_list!(vis, visit_attribute, attrs);
|
||||
try_visit!(vis.visit_pat(pat));
|
||||
visit_opt!(vis, visit_ty, ty);
|
||||
match kind {
|
||||
LocalKind::Decl => {}
|
||||
LocalKind::Init(init) => {
|
||||
try_visit!(vis.visit_expr(init))
|
||||
}
|
||||
LocalKind::InitElse(init, els) => {
|
||||
try_visit!(vis.visit_expr(init));
|
||||
try_visit!(vis.visit_block(els));
|
||||
}
|
||||
}
|
||||
if let Some(sp) = colon_sp {
|
||||
try_visit!(visit_span(vis, sp));
|
||||
}
|
||||
visit_span(vis, span)
|
||||
}
|
||||
|
||||
pub fn walk_poly_trait_ref<$($lt,)? V: $Visitor$(<$lt>)?>(
|
||||
vis: &mut V,
|
||||
p: &$($lt)? $($mut)? PolyTraitRef,
|
||||
) $(-> <V as Visitor<$lt>>::Result)? {
|
||||
let PolyTraitRef { bound_generic_params, modifiers, trait_ref, span } = p;
|
||||
try_visit!(visit_modifiers(vis, modifiers));
|
||||
try_visit!(visit_generic_params(vis, bound_generic_params));
|
||||
try_visit!(vis.visit_trait_ref(trait_ref));
|
||||
visit_span(vis, span)
|
||||
}
|
||||
|
||||
pub fn walk_trait_ref<$($lt,)? V: $Visitor$(<$lt>)?>(
|
||||
vis: &mut V,
|
||||
TraitRef { path, ref_id }: &$($lt)? $($mut)? TraitRef,
|
||||
) $(-> <V as Visitor<$lt>>::Result)? {
|
||||
try_visit!(vis.visit_path(path));
|
||||
visit_id(vis, ref_id)
|
||||
}
|
||||
|
||||
pub fn walk_variant<$($lt,)? V: $Visitor$(<$lt>)?>(
|
||||
vis: &mut V,
|
||||
variant: &$($lt)? $($mut)? Variant,
|
||||
) $(-> <V as Visitor<$lt>>::Result)? {
|
||||
let Variant { attrs, id, span, vis: visibility, ident, data, disr_expr, is_placeholder: _ } = variant;
|
||||
try_visit!(visit_id(vis, id));
|
||||
walk_list!(vis, visit_attribute, attrs);
|
||||
try_visit!(vis.visit_vis(visibility));
|
||||
try_visit!(vis.visit_ident(ident));
|
||||
try_visit!(vis.visit_variant_data(data));
|
||||
$(${ignore($lt)} visit_opt!(vis, visit_variant_discr, disr_expr); )?
|
||||
$(${ignore($mut)} visit_opt!(vis, visit_anon_const, disr_expr); )?
|
||||
visit_span(vis, span)
|
||||
}
|
||||
|
||||
pub fn walk_expr_field<$($lt,)? V: $Visitor$(<$lt>)?>(
|
||||
vis: &mut V,
|
||||
f: &$($lt)? $($mut)? ExprField,
|
||||
) $(-> <V as Visitor<$lt>>::Result)? {
|
||||
let ExprField { attrs, id, span, ident, expr, is_shorthand: _, is_placeholder: _ } = f;
|
||||
try_visit!(visit_id(vis, id));
|
||||
walk_list!(vis, visit_attribute, attrs);
|
||||
try_visit!(vis.visit_ident(ident));
|
||||
try_visit!(vis.visit_expr(expr));
|
||||
visit_span(vis, span)
|
||||
}
|
||||
|
||||
pub fn walk_pat_field<$($lt,)? V: $Visitor$(<$lt>)?>(
|
||||
vis: &mut V,
|
||||
fp: &$($lt)? $($mut)? PatField,
|
||||
) $(-> <V as Visitor<$lt>>::Result)? {
|
||||
let PatField { ident, pat, is_shorthand: _, attrs, id, span, is_placeholder: _ } = fp;
|
||||
try_visit!(visit_id(vis, id));
|
||||
walk_list!(vis, visit_attribute, attrs);
|
||||
try_visit!(vis.visit_ident(ident));
|
||||
try_visit!(vis.visit_pat(pat));
|
||||
visit_span(vis, span)
|
||||
}
|
||||
|
||||
pub fn walk_ty_pat<$($lt,)? V: $Visitor$(<$lt>)?>(
|
||||
vis: &mut V,
|
||||
tp: &$($lt)? $($mut)? TyPat,
|
||||
) $(-> <V as Visitor<$lt>>::Result)? {
|
||||
let TyPat { id, kind, span, tokens: _ } = tp;
|
||||
try_visit!(visit_id(vis, id));
|
||||
match kind {
|
||||
TyPatKind::Range(start, end, _include_end) => {
|
||||
visit_opt!(vis, visit_anon_const, start);
|
||||
visit_opt!(vis, visit_anon_const, end);
|
||||
}
|
||||
TyPatKind::Or(variants) => walk_list!(vis, visit_ty_pat, variants),
|
||||
TyPatKind::Err(_) => {}
|
||||
}
|
||||
visit_span(vis, span)
|
||||
}
|
||||
|
||||
fn walk_qself<$($lt,)? V: $Visitor$(<$lt>)?>(
|
||||
vis: &mut V,
|
||||
qself: &$($lt)? $($mut)? Option<P<QSelf>>,
|
||||
) $(-> <V as Visitor<$lt>>::Result)? {
|
||||
if let Some(qself) = qself {
|
||||
let QSelf { ty, path_span, position: _ } = &$($mut)? **qself;
|
||||
try_visit!(vis.visit_ty(ty));
|
||||
try_visit!(visit_span(vis, path_span));
|
||||
}
|
||||
$(<V as Visitor<$lt>>::Result::output())?
|
||||
}
|
||||
|
||||
pub fn walk_path<$($lt,)? V: $Visitor$(<$lt>)?>(
|
||||
vis: &mut V,
|
||||
path: &$($lt)? $($mut)? Path,
|
||||
) $(-> <V as Visitor<$lt>>::Result)? {
|
||||
let Path { span, segments, tokens: _ } = path;
|
||||
walk_list!(vis, visit_path_segment, segments);
|
||||
visit_span(vis, span)
|
||||
}
|
||||
|
||||
pub fn walk_use_tree<$($lt,)? V: $Visitor$(<$lt>)?>(
|
||||
vis: &mut V,
|
||||
use_tree: &$($lt)? $($mut)? UseTree,
|
||||
) $(-> <V as Visitor<$lt>>::Result)? {
|
||||
let UseTree { prefix, kind, span } = use_tree;
|
||||
try_visit!(vis.visit_path(prefix));
|
||||
match kind {
|
||||
UseTreeKind::Simple(rename) => {
|
||||
// The extra IDs are handled during AST lowering.
|
||||
visit_opt!(vis, visit_ident, rename);
|
||||
}
|
||||
UseTreeKind::Glob => {}
|
||||
UseTreeKind::Nested { items, span } => {
|
||||
for (nested_tree, nested_id) in items {
|
||||
try_visit!(visit_nested_use_tree(vis, nested_tree, nested_id));
|
||||
}
|
||||
try_visit!(visit_span(vis, span));
|
||||
}
|
||||
}
|
||||
visit_span(vis, span)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
common_visitor_and_walkers!(Visitor<'a>);
|
||||
|
||||
pub fn walk_crate<'a, V: Visitor<'a>>(visitor: &mut V, krate: &'a Crate) -> V::Result {
|
||||
let Crate { attrs, items, spans: _, id: _, is_placeholder: _ } = krate;
|
||||
walk_list!(visitor, visit_attribute, attrs);
|
||||
walk_list!(visitor, visit_item, items);
|
||||
V::Result::output()
|
||||
}
|
||||
|
||||
pub fn walk_local<'a, V: Visitor<'a>>(visitor: &mut V, local: &'a Local) -> V::Result {
|
||||
let Local { id: _, super_: _, pat, ty, kind, span: _, colon_sp: _, attrs, tokens: _ } = local;
|
||||
walk_list!(visitor, visit_attribute, attrs);
|
||||
try_visit!(visitor.visit_pat(pat));
|
||||
visit_opt!(visitor, visit_ty, ty);
|
||||
if let Some((init, els)) = kind.init_else_opt() {
|
||||
try_visit!(visitor.visit_expr(init));
|
||||
visit_opt!(visitor, visit_block, els);
|
||||
}
|
||||
V::Result::output()
|
||||
}
|
||||
|
||||
pub fn walk_poly_trait_ref<'a, V>(visitor: &mut V, trait_ref: &'a PolyTraitRef) -> V::Result
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
let PolyTraitRef { bound_generic_params, modifiers: _, trait_ref, span: _ } = trait_ref;
|
||||
walk_list!(visitor, visit_generic_param, bound_generic_params);
|
||||
visitor.visit_trait_ref(trait_ref)
|
||||
}
|
||||
|
||||
pub fn walk_trait_ref<'a, V: Visitor<'a>>(visitor: &mut V, trait_ref: &'a TraitRef) -> V::Result {
|
||||
let TraitRef { path, ref_id } = trait_ref;
|
||||
visitor.visit_path(path, *ref_id)
|
||||
}
|
||||
|
||||
pub fn walk_enum_def<'a, V: Visitor<'a>>(
|
||||
visitor: &mut V,
|
||||
EnumDef { variants }: &'a EnumDef,
|
||||
) -> V::Result {
|
||||
walk_list!(visitor, visit_variant, variants);
|
||||
V::Result::output()
|
||||
}
|
||||
|
||||
pub fn walk_variant<'a, V: Visitor<'a>>(visitor: &mut V, variant: &'a Variant) -> V::Result
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
let Variant { attrs, id: _, span: _, vis, ident, data, disr_expr, is_placeholder: _ } = variant;
|
||||
walk_list!(visitor, visit_attribute, attrs);
|
||||
try_visit!(visitor.visit_vis(vis));
|
||||
try_visit!(visitor.visit_ident(ident));
|
||||
try_visit!(visitor.visit_variant_data(data));
|
||||
visit_opt!(visitor, visit_variant_discr, disr_expr);
|
||||
V::Result::output()
|
||||
}
|
||||
|
||||
pub fn walk_expr_field<'a, V: Visitor<'a>>(visitor: &mut V, f: &'a ExprField) -> V::Result {
|
||||
let ExprField { attrs, id: _, span: _, ident, expr, is_shorthand: _, is_placeholder: _ } = f;
|
||||
walk_list!(visitor, visit_attribute, attrs);
|
||||
try_visit!(visitor.visit_ident(ident));
|
||||
try_visit!(visitor.visit_expr(expr));
|
||||
V::Result::output()
|
||||
}
|
||||
|
||||
pub fn walk_pat_field<'a, V: Visitor<'a>>(visitor: &mut V, fp: &'a PatField) -> V::Result {
|
||||
let PatField { ident, pat, is_shorthand: _, attrs, id: _, span: _, is_placeholder: _ } = fp;
|
||||
walk_list!(visitor, visit_attribute, attrs);
|
||||
try_visit!(visitor.visit_ident(ident));
|
||||
try_visit!(visitor.visit_pat(pat));
|
||||
V::Result::output()
|
||||
}
|
||||
|
||||
pub fn walk_ty<'a, V: Visitor<'a>>(visitor: &mut V, typ: &'a Ty) -> V::Result {
|
||||
let Ty { id, kind, span: _, tokens: _ } = typ;
|
||||
match kind {
|
||||
TyKind::Slice(ty) | TyKind::Paren(ty) => try_visit!(visitor.visit_ty(ty)),
|
||||
TyKind::Ptr(MutTy { ty, mutbl: _ }) => try_visit!(visitor.visit_ty(ty)),
|
||||
TyKind::Ref(opt_lifetime, MutTy { ty, mutbl: _ })
|
||||
| TyKind::PinnedRef(opt_lifetime, MutTy { ty, mutbl: _ }) => {
|
||||
visit_opt!(visitor, visit_lifetime, opt_lifetime, LifetimeCtxt::Ref);
|
||||
try_visit!(visitor.visit_ty(ty));
|
||||
}
|
||||
TyKind::Tup(tuple_element_types) => {
|
||||
walk_list!(visitor, visit_ty, tuple_element_types);
|
||||
}
|
||||
TyKind::BareFn(function_declaration) => {
|
||||
let BareFnTy { safety: _, ext: _, generic_params, decl, decl_span: _ } =
|
||||
&**function_declaration;
|
||||
walk_list!(visitor, visit_generic_param, generic_params);
|
||||
try_visit!(visitor.visit_fn_decl(decl));
|
||||
}
|
||||
TyKind::UnsafeBinder(binder) => {
|
||||
walk_list!(visitor, visit_generic_param, &binder.generic_params);
|
||||
try_visit!(visitor.visit_ty(&binder.inner_ty));
|
||||
}
|
||||
TyKind::Path(maybe_qself, path) => {
|
||||
try_visit!(visitor.visit_qself(maybe_qself));
|
||||
try_visit!(visitor.visit_path(path, *id));
|
||||
}
|
||||
TyKind::Pat(ty, pat) => {
|
||||
try_visit!(visitor.visit_ty(ty));
|
||||
try_visit!(visitor.visit_ty_pat(pat));
|
||||
}
|
||||
TyKind::Array(ty, length) => {
|
||||
try_visit!(visitor.visit_ty(ty));
|
||||
try_visit!(visitor.visit_anon_const(length));
|
||||
}
|
||||
TyKind::TraitObject(bounds, _syntax) => {
|
||||
walk_list!(visitor, visit_param_bound, bounds, BoundKind::TraitObject);
|
||||
}
|
||||
TyKind::ImplTrait(_id, bounds) => {
|
||||
walk_list!(visitor, visit_param_bound, bounds, BoundKind::Impl);
|
||||
}
|
||||
TyKind::Typeof(expression) => try_visit!(visitor.visit_anon_const(expression)),
|
||||
TyKind::Infer | TyKind::ImplicitSelf | TyKind::Dummy => {}
|
||||
TyKind::Err(_guar) => {}
|
||||
TyKind::MacCall(mac) => try_visit!(visitor.visit_mac_call(mac)),
|
||||
TyKind::Never | TyKind::CVarArgs => {}
|
||||
}
|
||||
V::Result::output()
|
||||
}
|
||||
|
||||
pub fn walk_ty_pat<'a, V: Visitor<'a>>(visitor: &mut V, tp: &'a TyPat) -> V::Result {
|
||||
let TyPat { id: _, kind, span: _, tokens: _ } = tp;
|
||||
match kind {
|
||||
TyPatKind::Range(start, end, _include_end) => {
|
||||
visit_opt!(visitor, visit_anon_const, start);
|
||||
visit_opt!(visitor, visit_anon_const, end);
|
||||
}
|
||||
TyPatKind::Or(variants) => walk_list!(visitor, visit_ty_pat, variants),
|
||||
TyPatKind::Err(_) => {}
|
||||
}
|
||||
V::Result::output()
|
||||
}
|
||||
|
||||
fn walk_qself<'a, V: Visitor<'a>>(visitor: &mut V, qself: &'a Option<P<QSelf>>) -> V::Result {
|
||||
if let Some(qself) = qself {
|
||||
let QSelf { ty, path_span: _, position: _ } = &**qself;
|
||||
try_visit!(visitor.visit_ty(ty));
|
||||
}
|
||||
V::Result::output()
|
||||
}
|
||||
|
||||
pub fn walk_path<'a, V: Visitor<'a>>(visitor: &mut V, path: &'a Path) -> V::Result {
|
||||
let Path { span: _, segments, tokens: _ } = path;
|
||||
walk_list!(visitor, visit_path_segment, segments);
|
||||
V::Result::output()
|
||||
}
|
||||
|
||||
pub fn walk_use_tree<'a, V: Visitor<'a>>(
|
||||
visitor: &mut V,
|
||||
use_tree: &'a UseTree,
|
||||
id: NodeId,
|
||||
) -> V::Result {
|
||||
let UseTree { prefix, kind, span: _ } = use_tree;
|
||||
try_visit!(visitor.visit_path(prefix, id));
|
||||
match kind {
|
||||
UseTreeKind::Simple(rename) => {
|
||||
// The extra IDs are handled during AST lowering.
|
||||
visit_opt!(visitor, visit_ident, rename);
|
||||
}
|
||||
UseTreeKind::Glob => {}
|
||||
UseTreeKind::Nested { items, span: _ } => {
|
||||
for &(ref nested_tree, nested_id) in items {
|
||||
try_visit!(visitor.visit_use_tree(nested_tree, nested_id, true));
|
||||
macro_rules! generate_list_visit_fns {
|
||||
($($name:ident, $Ty:ty, $visit_fn:ident$(, $param:ident: $ParamTy:ty)*;)+) => {
|
||||
$(
|
||||
fn $name<'a, V: Visitor<'a>>(
|
||||
vis: &mut V,
|
||||
values: &'a ThinVec<$Ty>,
|
||||
$(
|
||||
$param: $ParamTy,
|
||||
)*
|
||||
) -> V::Result {
|
||||
walk_list!(vis, $visit_fn, values$(,$param)*);
|
||||
V::Result::output()
|
||||
}
|
||||
}
|
||||
)+
|
||||
}
|
||||
V::Result::output()
|
||||
}
|
||||
|
||||
pub fn walk_path_segment<'a, V: Visitor<'a>>(
|
||||
visitor: &mut V,
|
||||
segment: &'a PathSegment,
|
||||
generate_list_visit_fns! {
|
||||
visit_items, P<Item>, visit_item;
|
||||
visit_foreign_items, P<ForeignItem>, visit_foreign_item;
|
||||
visit_generic_params, GenericParam, visit_generic_param;
|
||||
visit_stmts, Stmt, visit_stmt;
|
||||
visit_pat_fields, PatField, visit_pat_field;
|
||||
visit_variants, Variant, visit_variant;
|
||||
visit_assoc_items, P<AssocItem>, visit_assoc_item, ctxt: AssocCtxt;
|
||||
}
|
||||
|
||||
#[expect(rustc::pass_by_value)] // needed for symmetry with mut_visit
|
||||
fn visit_nested_use_tree<'a, V: Visitor<'a>>(
|
||||
vis: &mut V,
|
||||
nested_tree: &'a UseTree,
|
||||
&nested_id: &NodeId,
|
||||
) -> V::Result {
|
||||
let PathSegment { ident, id: _, args } = segment;
|
||||
try_visit!(visitor.visit_ident(ident));
|
||||
visit_opt!(visitor, visit_generic_args, args);
|
||||
V::Result::output()
|
||||
vis.visit_nested_use_tree(nested_tree, nested_id)
|
||||
}
|
||||
|
||||
pub fn walk_generic_args<'a, V>(visitor: &mut V, generic_args: &'a GenericArgs) -> V::Result
|
||||
|
|
@ -1012,52 +1171,6 @@ pub fn walk_assoc_item_constraint<'a, V: Visitor<'a>>(
|
|||
V::Result::output()
|
||||
}
|
||||
|
||||
pub fn walk_pat<'a, V: Visitor<'a>>(visitor: &mut V, pattern: &'a Pat) -> V::Result {
|
||||
let Pat { id, kind, span: _, tokens: _ } = pattern;
|
||||
match kind {
|
||||
PatKind::TupleStruct(opt_qself, path, elems) => {
|
||||
try_visit!(visitor.visit_qself(opt_qself));
|
||||
try_visit!(visitor.visit_path(path, *id));
|
||||
walk_list!(visitor, visit_pat, elems);
|
||||
}
|
||||
PatKind::Path(opt_qself, path) => {
|
||||
try_visit!(visitor.visit_qself(opt_qself));
|
||||
try_visit!(visitor.visit_path(path, *id))
|
||||
}
|
||||
PatKind::Struct(opt_qself, path, fields, _rest) => {
|
||||
try_visit!(visitor.visit_qself(opt_qself));
|
||||
try_visit!(visitor.visit_path(path, *id));
|
||||
walk_list!(visitor, visit_pat_field, fields);
|
||||
}
|
||||
PatKind::Box(subpattern) | PatKind::Deref(subpattern) | PatKind::Paren(subpattern) => {
|
||||
try_visit!(visitor.visit_pat(subpattern));
|
||||
}
|
||||
PatKind::Ref(subpattern, _ /*mutbl*/) => {
|
||||
try_visit!(visitor.visit_pat(subpattern));
|
||||
}
|
||||
PatKind::Ident(_bmode, ident, optional_subpattern) => {
|
||||
try_visit!(visitor.visit_ident(ident));
|
||||
visit_opt!(visitor, visit_pat, optional_subpattern);
|
||||
}
|
||||
PatKind::Expr(expression) => try_visit!(visitor.visit_expr(expression)),
|
||||
PatKind::Range(lower_bound, upper_bound, _end) => {
|
||||
visit_opt!(visitor, visit_expr, lower_bound);
|
||||
visit_opt!(visitor, visit_expr, upper_bound);
|
||||
}
|
||||
PatKind::Guard(subpattern, guard_condition) => {
|
||||
try_visit!(visitor.visit_pat(subpattern));
|
||||
try_visit!(visitor.visit_expr(guard_condition));
|
||||
}
|
||||
PatKind::Missing | PatKind::Wild | PatKind::Rest | PatKind::Never => {}
|
||||
PatKind::Err(_guar) => {}
|
||||
PatKind::Tuple(elems) | PatKind::Slice(elems) | PatKind::Or(elems) => {
|
||||
walk_list!(visitor, visit_pat, elems);
|
||||
}
|
||||
PatKind::MacCall(mac) => try_visit!(visitor.visit_mac_call(mac)),
|
||||
}
|
||||
V::Result::output()
|
||||
}
|
||||
|
||||
pub fn walk_param_bound<'a, V: Visitor<'a>>(visitor: &mut V, bound: &'a GenericBound) -> V::Result {
|
||||
match bound {
|
||||
GenericBound::Trait(trait_ref) => visitor.visit_poly_trait_ref(trait_ref),
|
||||
|
|
@ -1075,7 +1188,10 @@ pub fn walk_precise_capturing_arg<'a, V: Visitor<'a>>(
|
|||
) -> V::Result {
|
||||
match arg {
|
||||
PreciseCapturingArg::Lifetime(lt) => visitor.visit_lifetime(lt, LifetimeCtxt::GenericArg),
|
||||
PreciseCapturingArg::Arg(path, id) => visitor.visit_path(path, *id),
|
||||
PreciseCapturingArg::Arg(path, id) => {
|
||||
try_visit!(visitor.visit_id(*id));
|
||||
visitor.visit_path(path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1216,11 +1332,9 @@ pub fn walk_fn<'a, V: Visitor<'a>>(visitor: &mut V, kind: FnKind<'a>) -> V::Resu
|
|||
V::Result::output()
|
||||
}
|
||||
|
||||
pub fn walk_struct_def<'a, V: Visitor<'a>>(
|
||||
visitor: &mut V,
|
||||
struct_definition: &'a VariantData,
|
||||
) -> V::Result {
|
||||
walk_list!(visitor, visit_field_def, struct_definition.fields());
|
||||
pub fn walk_variant_data<'a, V: Visitor<'a>>(visitor: &mut V, data: &'a VariantData) -> V::Result {
|
||||
visit_opt!(visitor, visit_id, data.ctor_node_id());
|
||||
walk_list!(visitor, visit_field_def, data.fields());
|
||||
V::Result::output()
|
||||
}
|
||||
|
||||
|
|
@ -1235,12 +1349,6 @@ pub fn walk_field_def<'a, V: Visitor<'a>>(visitor: &mut V, field: &'a FieldDef)
|
|||
V::Result::output()
|
||||
}
|
||||
|
||||
pub fn walk_block<'a, V: Visitor<'a>>(visitor: &mut V, block: &'a Block) -> V::Result {
|
||||
let Block { stmts, id: _, rules: _, span: _, tokens: _ } = block;
|
||||
walk_list!(visitor, visit_stmt, stmts);
|
||||
V::Result::output()
|
||||
}
|
||||
|
||||
pub fn walk_stmt<'a, V: Visitor<'a>>(visitor: &mut V, statement: &'a Stmt) -> V::Result {
|
||||
let Stmt { id: _, kind, span: _ } = statement;
|
||||
match kind {
|
||||
|
|
@ -1259,12 +1367,7 @@ pub fn walk_stmt<'a, V: Visitor<'a>>(visitor: &mut V, statement: &'a Stmt) -> V:
|
|||
|
||||
pub fn walk_mac<'a, V: Visitor<'a>>(visitor: &mut V, mac: &'a MacCall) -> V::Result {
|
||||
let MacCall { path, args: _ } = mac;
|
||||
visitor.visit_path(path, DUMMY_NODE_ID)
|
||||
}
|
||||
|
||||
pub fn walk_anon_const<'a, V: Visitor<'a>>(visitor: &mut V, constant: &'a AnonConst) -> V::Result {
|
||||
let AnonConst { id: _, value } = constant;
|
||||
visitor.visit_expr(value)
|
||||
visitor.visit_path(path)
|
||||
}
|
||||
|
||||
pub fn walk_inline_asm<'a, V: Visitor<'a>>(visitor: &mut V, asm: &'a InlineAsm) -> V::Result {
|
||||
|
|
@ -1304,7 +1407,8 @@ pub fn walk_inline_asm_sym<'a, V: Visitor<'a>>(
|
|||
InlineAsmSym { id, qself, path }: &'a InlineAsmSym,
|
||||
) -> V::Result {
|
||||
try_visit!(visitor.visit_qself(qself));
|
||||
visitor.visit_path(path, *id)
|
||||
try_visit!(visitor.visit_id(*id));
|
||||
visitor.visit_path(path)
|
||||
}
|
||||
|
||||
pub fn walk_format_args<'a, V: Visitor<'a>>(visitor: &mut V, fmt: &'a FormatArgs) -> V::Result {
|
||||
|
|
@ -1336,7 +1440,8 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) -> V
|
|||
ExprKind::Struct(se) => {
|
||||
let StructExpr { qself, path, fields, rest } = &**se;
|
||||
try_visit!(visitor.visit_qself(qself));
|
||||
try_visit!(visitor.visit_path(path, *id));
|
||||
try_visit!(visitor.visit_id(*id));
|
||||
try_visit!(visitor.visit_path(path));
|
||||
walk_list!(visitor, visit_expr_field, fields);
|
||||
match rest {
|
||||
StructRest::Base(expr) => try_visit!(visitor.visit_expr(expr)),
|
||||
|
|
@ -1446,7 +1551,8 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) -> V
|
|||
ExprKind::Underscore => {}
|
||||
ExprKind::Path(maybe_qself, path) => {
|
||||
try_visit!(visitor.visit_qself(maybe_qself));
|
||||
try_visit!(visitor.visit_path(path, *id));
|
||||
try_visit!(visitor.visit_id(*id));
|
||||
try_visit!(visitor.visit_path(path));
|
||||
}
|
||||
ExprKind::Break(opt_label, opt_expr) => {
|
||||
visit_opt!(visitor, visit_label, opt_label);
|
||||
|
|
@ -1509,7 +1615,8 @@ pub fn walk_vis<'a, V: Visitor<'a>>(visitor: &mut V, vis: &'a Visibility) -> V::
|
|||
let Visibility { kind, span: _, tokens: _ } = vis;
|
||||
match kind {
|
||||
VisibilityKind::Restricted { path, id, shorthand: _ } => {
|
||||
try_visit!(visitor.visit_path(path, *id));
|
||||
try_visit!(visitor.visit_id(*id));
|
||||
try_visit!(visitor.visit_path(path));
|
||||
}
|
||||
VisibilityKind::Public | VisibilityKind::Inherited => {}
|
||||
}
|
||||
|
|
@ -1522,7 +1629,7 @@ pub fn walk_attribute<'a, V: Visitor<'a>>(visitor: &mut V, attr: &'a Attribute)
|
|||
AttrKind::Normal(normal) => {
|
||||
let NormalAttr { item, tokens: _ } = &**normal;
|
||||
let AttrItem { unsafety: _, path, args, tokens: _ } = item;
|
||||
try_visit!(visitor.visit_path(path, DUMMY_NODE_ID));
|
||||
try_visit!(visitor.visit_path(path));
|
||||
try_visit!(walk_attr_args(visitor, args));
|
||||
}
|
||||
AttrKind::DocComment(_kind, _sym) => {}
|
||||
|
|
|
|||
|
|
@ -446,13 +446,7 @@ impl<'a> SelfResolver<'a> {
|
|||
}
|
||||
|
||||
impl<'ast, 'a> Visitor<'ast> for SelfResolver<'a> {
|
||||
fn visit_path(&mut self, path: &'ast Path, id: NodeId) {
|
||||
fn visit_id(&mut self, id: NodeId) {
|
||||
self.try_replace_id(id);
|
||||
visit::walk_path(self, path);
|
||||
}
|
||||
|
||||
fn visit_path_segment(&mut self, seg: &'ast PathSegment) {
|
||||
self.try_replace_id(seg.id);
|
||||
visit::walk_path_segment(self, seg);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
use std::assert_matches::assert_matches;
|
||||
use std::ops::ControlFlow;
|
||||
use std::sync::Arc;
|
||||
|
||||
|
|
@ -1199,11 +1198,13 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
let closure_def_id = self.local_def_id(closure_id);
|
||||
let (binder_clause, generic_params) = self.lower_closure_binder(binder);
|
||||
|
||||
assert_matches!(
|
||||
coroutine_kind,
|
||||
CoroutineKind::Async { .. },
|
||||
"only async closures are supported currently"
|
||||
);
|
||||
let coroutine_desugaring = match coroutine_kind {
|
||||
CoroutineKind::Async { .. } => hir::CoroutineDesugaring::Async,
|
||||
CoroutineKind::Gen { .. } => hir::CoroutineDesugaring::Gen,
|
||||
CoroutineKind::AsyncGen { span, .. } => {
|
||||
span_bug!(span, "only async closures and `iter!` closures are supported currently")
|
||||
}
|
||||
};
|
||||
|
||||
let body = self.with_new_scopes(fn_decl_span, |this| {
|
||||
let inner_decl =
|
||||
|
|
@ -1247,7 +1248,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
// Lower this as a `CoroutineClosure`. That will ensure that HIR typeck
|
||||
// knows that a `FnDecl` output type like `-> &str` actually means
|
||||
// "coroutine that returns &str", rather than directly returning a `&str`.
|
||||
kind: hir::ClosureKind::CoroutineClosure(hir::CoroutineDesugaring::Async),
|
||||
kind: hir::ClosureKind::CoroutineClosure(coroutine_desugaring),
|
||||
constness: hir::Constness::NotConst,
|
||||
});
|
||||
hir::ExprKind::Closure(c)
|
||||
|
|
|
|||
|
|
@ -732,7 +732,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
span: Span,
|
||||
args: Option<&'hir hir::GenericArgs<'hir>>,
|
||||
) -> &'hir hir::Path<'hir> {
|
||||
let def_id = self.tcx.require_lang_item(lang_item, Some(span));
|
||||
let def_id = self.tcx.require_lang_item(lang_item, span);
|
||||
let def_kind = self.tcx.def_kind(def_id);
|
||||
let res = Res::Def(def_kind, def_id);
|
||||
self.arena.alloc(hir::Path {
|
||||
|
|
@ -1406,7 +1406,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
};
|
||||
let span = self.tcx.sess.source_map().start_point(t.span).shrink_to_hi();
|
||||
let region = Lifetime { ident: Ident::new(kw::UnderscoreLifetime, span), id };
|
||||
(region, LifetimeSyntax::Hidden)
|
||||
(region, LifetimeSyntax::Implicit)
|
||||
}
|
||||
};
|
||||
self.lower_lifetime(®ion, LifetimeSource::Reference, syntax)
|
||||
|
|
@ -1790,7 +1790,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
id,
|
||||
Ident::new(kw::UnderscoreLifetime, span),
|
||||
LifetimeSource::Path { angle_brackets },
|
||||
LifetimeSyntax::Hidden,
|
||||
LifetimeSyntax::Implicit,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -2422,7 +2422,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
Ident::new(kw::UnderscoreLifetime, self.lower_span(span)),
|
||||
hir::LifetimeKind::ImplicitObjectLifetimeDefault,
|
||||
LifetimeSource::Other,
|
||||
LifetimeSyntax::Hidden,
|
||||
LifetimeSyntax::Implicit,
|
||||
);
|
||||
debug!("elided_dyn_bound: r={:?}", r);
|
||||
self.arena.alloc(r)
|
||||
|
|
|
|||
|
|
@ -477,11 +477,12 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
|
|||
for span in spans {
|
||||
if (!visitor.features.coroutines() && !span.allows_unstable(sym::coroutines))
|
||||
&& (!visitor.features.gen_blocks() && !span.allows_unstable(sym::gen_blocks))
|
||||
&& (!visitor.features.yield_expr() && !span.allows_unstable(sym::yield_expr))
|
||||
{
|
||||
#[allow(rustc::untranslatable_diagnostic)]
|
||||
// Don't know which of the two features to include in the
|
||||
// error message, so I am arbitrarily picking one.
|
||||
feature_err(&visitor.sess, sym::coroutines, *span, "yield syntax is experimental")
|
||||
// Emit yield_expr as the error, since that will be sufficient. You can think of it
|
||||
// as coroutines and gen_blocks imply yield_expr.
|
||||
feature_err(&visitor.sess, sym::yield_expr, *span, "yield syntax is experimental")
|
||||
.emit();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ fn parse_unstable<'a>(
|
|||
|
||||
for param in list.mixed() {
|
||||
let param_span = param.span();
|
||||
if let Some(ident) = param.meta_item().and_then(|i| i.path_without_args().word()) {
|
||||
if let Some(ident) = param.meta_item().and_then(|i| i.path().word()) {
|
||||
res.push(ident.name);
|
||||
} else {
|
||||
cx.emit_err(session_diagnostics::ExpectsFeatures {
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ impl SingleAttributeParser for DeprecationParser {
|
|||
return None;
|
||||
};
|
||||
|
||||
let ident_name = param.path_without_args().word_sym();
|
||||
let ident_name = param.path().word_sym();
|
||||
|
||||
match ident_name {
|
||||
Some(name @ sym::since) => {
|
||||
|
|
@ -102,7 +102,7 @@ impl SingleAttributeParser for DeprecationParser {
|
|||
_ => {
|
||||
cx.emit_err(session_diagnostics::UnknownMetaItem {
|
||||
span: param_span,
|
||||
item: param.path_without_args().to_string(),
|
||||
item: param.path().to_string(),
|
||||
expected: if features.deprecated_suggestion() {
|
||||
&["since", "note", "suggestion"]
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ pub(crate) trait AttributeParser: Default + 'static {
|
|||
pub(crate) trait SingleAttributeParser: 'static {
|
||||
const PATH: &'static [Symbol];
|
||||
|
||||
/// Caled when a duplicate attribute is found.
|
||||
/// Called when a duplicate attribute is found.
|
||||
///
|
||||
/// `first_span` is the span of the first occurrence of this attribute.
|
||||
// FIXME(jdonszelmann): default error
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@ fn parse_repr(cx: &AcceptContext<'_>, param: &MetaItemParser<'_>) -> Option<Repr
|
|||
|
||||
// FIXME(jdonszelmann): invert the parsing here to match on the word first and then the
|
||||
// structure.
|
||||
let (name, ident_span) = if let Some(ident) = param.path_without_args().word() {
|
||||
let (name, ident_span) = if let Some(ident) = param.path().word() {
|
||||
(Some(ident.name), ident.span)
|
||||
} else {
|
||||
(None, DUMMY_SP)
|
||||
|
|
|
|||
|
|
@ -204,7 +204,7 @@ fn insert_value_into_option_or_error(
|
|||
if item.is_some() {
|
||||
cx.emit_err(session_diagnostics::MultipleItem {
|
||||
span: param.span(),
|
||||
item: param.path_without_args().to_string(),
|
||||
item: param.path().to_string(),
|
||||
});
|
||||
None
|
||||
} else if let Some(v) = param.args().name_value()
|
||||
|
|
@ -242,13 +242,13 @@ pub(crate) fn parse_stability(
|
|||
return None;
|
||||
};
|
||||
|
||||
match param.path_without_args().word_sym() {
|
||||
match param.path().word_sym() {
|
||||
Some(sym::feature) => insert_value_into_option_or_error(cx, ¶m, &mut feature)?,
|
||||
Some(sym::since) => insert_value_into_option_or_error(cx, ¶m, &mut since)?,
|
||||
_ => {
|
||||
cx.emit_err(session_diagnostics::UnknownMetaItem {
|
||||
span: param_span,
|
||||
item: param.path_without_args().to_string(),
|
||||
item: param.path().to_string(),
|
||||
expected: &["feature", "since"],
|
||||
});
|
||||
return None;
|
||||
|
|
@ -310,7 +310,7 @@ pub(crate) fn parse_unstability(
|
|||
return None;
|
||||
};
|
||||
|
||||
match param.path_without_args().word_sym() {
|
||||
match param.path().word_sym() {
|
||||
Some(sym::feature) => insert_value_into_option_or_error(cx, ¶m, &mut feature)?,
|
||||
Some(sym::reason) => insert_value_into_option_or_error(cx, ¶m, &mut reason)?,
|
||||
Some(sym::issue) => {
|
||||
|
|
@ -349,7 +349,7 @@ pub(crate) fn parse_unstability(
|
|||
_ => {
|
||||
cx.emit_err(session_diagnostics::UnknownMetaItem {
|
||||
span: param.span(),
|
||||
item: param.path_without_args().to_string(),
|
||||
item: param.path().to_string(),
|
||||
expected: &["feature", "reason", "issue", "soft", "implied_by"],
|
||||
});
|
||||
return None;
|
||||
|
|
|
|||
|
|
@ -264,7 +264,8 @@ impl<'sess> AttributeParser<'sess> {
|
|||
// }
|
||||
ast::AttrKind::Normal(n) => {
|
||||
let parser = MetaItemParser::from_attr(n, self.dcx());
|
||||
let (path, args) = parser.deconstruct();
|
||||
let path = parser.path();
|
||||
let args = parser.args();
|
||||
let parts = path.segments().map(|i| i.name).collect::<Vec<_>>();
|
||||
|
||||
if let Some(accept) = ATTRIBUTE_MAPPING.0.get(parts.as_slice()) {
|
||||
|
|
|
|||
|
|
@ -1,31 +1,38 @@
|
|||
//! Centralized logic for parsing and attributes.
|
||||
//!
|
||||
//! Part of a series of crates:
|
||||
//! - rustc_attr_data_structures: contains types that the parsers parse into
|
||||
//! - rustc_attr_parsing: this crate
|
||||
//! - (in the future): rustc_attr_validation
|
||||
//! ## Architecture
|
||||
//! This crate is part of a series of crates that handle attribute processing.
|
||||
//! - [rustc_attr_data_structures](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_attr_data_structures/index.html): Defines the data structures that store parsed attributes
|
||||
//! - [rustc_attr_parsing](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_attr_parsing/index.html): This crate, handles the parsing of attributes
|
||||
//! - (planned) rustc_attr_validation: Will handle attribute validation
|
||||
//!
|
||||
//! History: Check out [#131229](https://github.com/rust-lang/rust/issues/131229).
|
||||
//! There used to be only one definition of attributes in the compiler: `ast::Attribute`.
|
||||
//! These were then parsed or validated or both in places distributed all over the compiler.
|
||||
//! This was a mess...
|
||||
//! The separation between data structures and parsing follows the principle of separation of concerns.
|
||||
//! Data structures (`rustc_attr_data_structures`) define what attributes look like after parsing.
|
||||
//! This crate (`rustc_attr_parsing`) handles how to convert raw tokens into those structures.
|
||||
//! This split allows other parts of the compiler to use the data structures without needing
|
||||
//! the parsing logic, making the codebase more modular and maintainable.
|
||||
//!
|
||||
//! Attributes are markers on items.
|
||||
//! Many of them are actually attribute-like proc-macros, and are expanded to some other rust syntax.
|
||||
//! This could either be a user provided proc macro, or something compiler provided.
|
||||
//! `derive` is an example of one that the compiler provides.
|
||||
//! These are built-in, but they have a valid expansion to Rust tokens and are thus called "active".
|
||||
//! I personally like calling these *active* compiler-provided attributes, built-in *macros*,
|
||||
//! because they still expand, and this helps to differentiate them from built-in *attributes*.
|
||||
//! However, I'll be the first to admit that the naming here can be confusing.
|
||||
//! ## Background
|
||||
//! Previously, the compiler had a single attribute definition (`ast::Attribute`) with parsing and
|
||||
//! validation scattered throughout the codebase. This was reorganized for better maintainability
|
||||
//! (see [#131229](https://github.com/rust-lang/rust/issues/131229)).
|
||||
//!
|
||||
//! The alternative to active attributes, are inert attributes.
|
||||
//! These can occur in user code (proc-macro helper attributes).
|
||||
//! But what's important is, many built-in attributes are inert like this.
|
||||
//! There is nothing they expand to during the macro expansion process,
|
||||
//! sometimes because they literally cannot expand to something that is valid Rust.
|
||||
//! They are really just markers to guide the compilation process.
|
||||
//! An example is `#[inline(...)]` which changes how code for functions is generated.
|
||||
//! ## Types of Attributes
|
||||
//! In Rust, attributes are markers that can be attached to items. They come in two main categories.
|
||||
//!
|
||||
//! ### 1. Active Attributes
|
||||
//! These are attribute-like proc-macros that expand into other Rust code.
|
||||
//! They can be either user-defined or compiler-provided. Examples of compiler-provided active attributes:
|
||||
//! - `#[derive(...)]`: Expands into trait implementations
|
||||
//! - `#[cfg()]`: Expands based on configuration
|
||||
//! - `#[cfg_attr()]`: Conditional attribute application
|
||||
//!
|
||||
//! ### 2. Inert Attributes
|
||||
//! These are pure markers that don't expand into other code. They guide the compilation process.
|
||||
//! They can be user-defined (in proc-macro helpers) or built-in. Examples of built-in inert attributes:
|
||||
//! - `#[stable()]`: Marks stable API items
|
||||
//! - `#[inline()]`: Suggests function inlining
|
||||
//! - `#[repr()]`: Controls type representation
|
||||
//!
|
||||
//! ```text
|
||||
//! Active Inert
|
||||
|
|
@ -33,27 +40,21 @@
|
|||
//! │ (mostly in) │ these are parsed │
|
||||
//! │ rustc_builtin_macros │ here! │
|
||||
//! │ │ │
|
||||
//! │ │ │
|
||||
//! │ #[derive(...)] │ #[stable()] │
|
||||
//! Built-in │ #[cfg()] │ #[inline()] │
|
||||
//! │ #[cfg_attr()] │ #[repr()] │
|
||||
//! │ │ │
|
||||
//! │ │ │
|
||||
//! │ │ │
|
||||
//! ├──────────────────────┼──────────────────────┤
|
||||
//! │ │ │
|
||||
//! │ │ │
|
||||
//! │ │ `b` in │
|
||||
//! │ │ #[proc_macro_derive( │
|
||||
//! User created │ #[proc_macro_attr()] │ a, │
|
||||
//! │ │ attributes(b) │
|
||||
//! │ │ ] │
|
||||
//! │ │ │
|
||||
//! │ │ │
|
||||
//! │ │ │
|
||||
//! └──────────────────────┴──────────────────────┘
|
||||
//! ```
|
||||
//!
|
||||
//! ## How This Crate Works
|
||||
//! In this crate, syntactical attributes (sequences of tokens that look like
|
||||
//! `#[something(something else)]`) are parsed into more semantic attributes, markers on items.
|
||||
//! Multiple syntactic attributes might influence a single semantic attribute. For example,
|
||||
|
|
@ -63,18 +64,17 @@
|
|||
//! and `#[unstable()]` syntactic attributes, and at the end produce a single
|
||||
//! [`AttributeKind::Stability`](rustc_attr_data_structures::AttributeKind::Stability).
|
||||
//!
|
||||
//! As a rule of thumb, when a syntactical attribute can be applied more than once, they should be
|
||||
//! combined into a single semantic attribute. For example:
|
||||
//! When multiple instances of the same attribute are allowed, they're combined into a single
|
||||
//! semantic attribute. For example:
|
||||
//!
|
||||
//! ```
|
||||
//! ```rust
|
||||
//! #[repr(C)]
|
||||
//! #[repr(packed)]
|
||||
//! struct Meow {}
|
||||
//! ```
|
||||
//!
|
||||
//! should result in a single `AttributeKind::Repr` containing a list of repr annotations, in this
|
||||
//! case `C` and `packed`. This is equivalent to writing `#[repr(C, packed)]` in a single
|
||||
//! syntactical annotation.
|
||||
//! This is equivalent to `#[repr(C, packed)]` and results in a single `AttributeKind::Repr`
|
||||
//! containing both `C` and `packed` annotations.
|
||||
|
||||
// tidy-alphabetical-start
|
||||
#![allow(internal_features)]
|
||||
|
|
|
|||
|
|
@ -252,9 +252,13 @@ impl<'a> MetaItemParser<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Gets just the path, without the args.
|
||||
pub fn path_without_args(&self) -> PathParser<'a> {
|
||||
self.path.clone()
|
||||
/// Gets just the path, without the args. Some examples:
|
||||
///
|
||||
/// - `#[rustfmt::skip]`: `rustfmt::skip` is a path
|
||||
/// - `#[allow(clippy::complexity)]`: `clippy::complexity` is a path
|
||||
/// - `#[inline]`: `inline` is a single segment path
|
||||
pub fn path(&self) -> &PathParser<'a> {
|
||||
&self.path
|
||||
}
|
||||
|
||||
/// Gets just the args parser, without caring about the path.
|
||||
|
|
@ -262,50 +266,14 @@ impl<'a> MetaItemParser<'a> {
|
|||
&self.args
|
||||
}
|
||||
|
||||
pub fn deconstruct(&self) -> (PathParser<'a>, &ArgParser<'a>) {
|
||||
(self.path_without_args(), self.args())
|
||||
}
|
||||
|
||||
/// Asserts that this MetaItem starts with a path. Some examples:
|
||||
///
|
||||
/// - `#[rustfmt::skip]`: `rustfmt::skip` is a path
|
||||
/// - `#[allow(clippy::complexity)]`: `clippy::complexity` is a path
|
||||
/// - `#[inline]`: `inline` is a single segment path
|
||||
pub fn path(&self) -> (PathParser<'a>, &ArgParser<'a>) {
|
||||
self.deconstruct()
|
||||
}
|
||||
|
||||
/// Asserts that this MetaItem starts with a word, or single segment path.
|
||||
/// Doesn't return the args parser.
|
||||
///
|
||||
/// For examples. see [`Self::word`]
|
||||
pub fn word_without_args(&self) -> Option<Ident> {
|
||||
Some(self.word()?.0)
|
||||
}
|
||||
|
||||
/// Asserts that this MetaItem starts with a word, or single segment path.
|
||||
///
|
||||
/// Some examples:
|
||||
/// - `#[inline]`: `inline` is a word
|
||||
/// - `#[rustfmt::skip]`: `rustfmt::skip` is a path,
|
||||
/// and not a word and should instead be parsed using [`path`](Self::path)
|
||||
pub fn word(&self) -> Option<(Ident, &ArgParser<'a>)> {
|
||||
let (path, args) = self.deconstruct();
|
||||
Some((path.word()?, args))
|
||||
}
|
||||
|
||||
/// Asserts that this MetaItem starts with some specific word.
|
||||
///
|
||||
/// See [`word`](Self::word) for examples of what a word is.
|
||||
pub fn word_is(&self, sym: Symbol) -> Option<&ArgParser<'a>> {
|
||||
self.path_without_args().word_is(sym).then(|| self.args())
|
||||
}
|
||||
|
||||
/// Asserts that this MetaItem starts with some specific path.
|
||||
///
|
||||
/// See [`word`](Self::path) for examples of what a word is.
|
||||
pub fn path_is(&self, segments: &[Symbol]) -> Option<&ArgParser<'a>> {
|
||||
self.path_without_args().segments_is(segments).then(|| self.args())
|
||||
self.path().word_is(sym).then(|| self.args())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -548,7 +516,7 @@ impl<'a> MetaItemListParser<'a> {
|
|||
}
|
||||
|
||||
/// Lets you pick and choose as what you want to parse each element in the list
|
||||
pub fn mixed<'s>(&'s self) -> impl Iterator<Item = &'s MetaItemOrLitParser<'a>> + 's {
|
||||
pub fn mixed(&self) -> impl Iterator<Item = &MetaItemOrLitParser<'a>> {
|
||||
self.sub_parsers.iter()
|
||||
}
|
||||
|
||||
|
|
@ -560,20 +528,6 @@ impl<'a> MetaItemListParser<'a> {
|
|||
self.len() == 0
|
||||
}
|
||||
|
||||
/// Asserts that every item in the list is another list starting with a word.
|
||||
///
|
||||
/// See [`MetaItemParser::word`] for examples of words.
|
||||
pub fn all_word_list<'s>(&'s self) -> Option<Vec<(Ident, &'s ArgParser<'a>)>> {
|
||||
self.mixed().map(|i| i.meta_item()?.word()).collect()
|
||||
}
|
||||
|
||||
/// Asserts that every item in the list is another list starting with a full path.
|
||||
///
|
||||
/// See [`MetaItemParser::path`] for examples of paths.
|
||||
pub fn all_path_list<'s>(&'s self) -> Option<Vec<(PathParser<'a>, &'s ArgParser<'a>)>> {
|
||||
self.mixed().map(|i| Some(i.meta_item()?.path())).collect()
|
||||
}
|
||||
|
||||
/// Returns Some if the list contains only a single element.
|
||||
///
|
||||
/// Inside the Some is the parser to parse this single element.
|
||||
|
|
|
|||
|
|
@ -5,11 +5,9 @@ use rustc_index::{IndexSlice, IndexVec};
|
|||
use rustc_middle::mir::ConstraintCategory;
|
||||
use rustc_middle::ty::{RegionVid, TyCtxt, VarianceDiagInfo};
|
||||
use rustc_span::Span;
|
||||
use tracing::{debug, instrument};
|
||||
use tracing::debug;
|
||||
|
||||
use crate::region_infer::{AnnotatedSccs, ConstraintSccs, RegionDefinition, SccAnnotations};
|
||||
use crate::type_check::Locations;
|
||||
use crate::universal_regions::UniversalRegions;
|
||||
|
||||
pub(crate) mod graph;
|
||||
|
||||
|
|
@ -53,112 +51,6 @@ impl<'tcx> OutlivesConstraintSet<'tcx> {
|
|||
) -> &IndexSlice<OutlivesConstraintIndex, OutlivesConstraint<'tcx>> {
|
||||
&self.outlives
|
||||
}
|
||||
|
||||
/// Computes cycles (SCCs) in the graph of regions. In particular,
|
||||
/// find all regions R1, R2 such that R1: R2 and R2: R1 and group
|
||||
/// them into an SCC, and find the relationships between SCCs.
|
||||
pub(crate) fn compute_sccs(
|
||||
&self,
|
||||
static_region: RegionVid,
|
||||
definitions: &IndexVec<RegionVid, RegionDefinition<'tcx>>,
|
||||
) -> AnnotatedSccs {
|
||||
let constraint_graph = self.graph(definitions.len());
|
||||
let region_graph = &constraint_graph.region_graph(self, static_region);
|
||||
let mut annotation_visitor = SccAnnotations::new(definitions);
|
||||
(
|
||||
ConstraintSccs::new_with_annotation(®ion_graph, &mut annotation_visitor),
|
||||
annotation_visitor.scc_to_annotation,
|
||||
)
|
||||
}
|
||||
|
||||
/// This method handles Universe errors by rewriting the constraint
|
||||
/// graph. For each strongly connected component in the constraint
|
||||
/// graph such that there is a series of constraints
|
||||
/// A: B: C: ... : X where
|
||||
/// A's universe is smaller than X's and A is a placeholder,
|
||||
/// add a constraint that A: 'static. This is a safe upper bound
|
||||
/// in the face of borrow checker/trait solver limitations that will
|
||||
/// eventually go away.
|
||||
///
|
||||
/// For a more precise definition, see the documentation for
|
||||
/// [`crate::region_infer::RegionTracker`].
|
||||
///
|
||||
/// This edge case used to be handled during constraint propagation
|
||||
/// by iterating over the strongly connected components in the constraint
|
||||
/// graph while maintaining a set of bookkeeping mappings similar
|
||||
/// to what is stored in `RegionTracker` and manually adding 'static as
|
||||
/// needed.
|
||||
///
|
||||
/// It was rewritten as part of the Polonius project with the goal of moving
|
||||
/// higher-kindedness concerns out of the path of the borrow checker,
|
||||
/// for two reasons:
|
||||
///
|
||||
/// 1. Implementing Polonius is difficult enough without also
|
||||
/// handling them.
|
||||
/// 2. The long-term goal is to handle higher-kinded concerns
|
||||
/// in the trait solver, where they belong. This avoids
|
||||
/// logic duplication and allows future trait solvers
|
||||
/// to compute better bounds than for example our
|
||||
/// "must outlive 'static" here.
|
||||
///
|
||||
/// This code is a stop-gap measure in preparation for the future trait solver.
|
||||
///
|
||||
/// Every constraint added by this method is an
|
||||
/// internal `IllegalUniverse` constraint.
|
||||
#[instrument(skip(self, universal_regions, definitions))]
|
||||
pub(crate) fn add_outlives_static(
|
||||
&mut self,
|
||||
universal_regions: &UniversalRegions<'tcx>,
|
||||
definitions: &IndexVec<RegionVid, RegionDefinition<'tcx>>,
|
||||
) -> AnnotatedSccs {
|
||||
let fr_static = universal_regions.fr_static;
|
||||
let (sccs, annotations) = self.compute_sccs(fr_static, definitions);
|
||||
|
||||
// Changed to `true` if we added any constraints to `self` and need to
|
||||
// recompute SCCs.
|
||||
let mut added_constraints = false;
|
||||
|
||||
for scc in sccs.all_sccs() {
|
||||
// No point in adding 'static: 'static!
|
||||
// This micro-optimisation makes somewhat sense
|
||||
// because static outlives *everything*.
|
||||
if scc == sccs.scc(fr_static) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let annotation = annotations[scc];
|
||||
|
||||
// If this SCC participates in a universe violation,
|
||||
// e.g. if it reaches a region with a universe smaller than
|
||||
// the largest region reached, add a requirement that it must
|
||||
// outlive `'static`.
|
||||
if annotation.has_incompatible_universes() {
|
||||
// Optimisation opportunity: this will add more constraints than
|
||||
// needed for correctness, since an SCC upstream of another with
|
||||
// a universe violation will "infect" its downstream SCCs to also
|
||||
// outlive static.
|
||||
added_constraints = true;
|
||||
let scc_representative_outlives_static = OutlivesConstraint {
|
||||
sup: annotation.representative,
|
||||
sub: fr_static,
|
||||
category: ConstraintCategory::IllegalUniverse,
|
||||
locations: Locations::All(rustc_span::DUMMY_SP),
|
||||
span: rustc_span::DUMMY_SP,
|
||||
variance_info: VarianceDiagInfo::None,
|
||||
from_closure: false,
|
||||
};
|
||||
self.push(scc_representative_outlives_static);
|
||||
}
|
||||
}
|
||||
|
||||
if added_constraints {
|
||||
// We changed the constraint set and so must recompute SCCs.
|
||||
self.compute_sccs(fr_static, definitions)
|
||||
} else {
|
||||
// If we didn't add any back-edges; no more work needs doing
|
||||
(sccs, annotations)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Index<OutlivesConstraintIndex> for OutlivesConstraintSet<'tcx> {
|
||||
|
|
|
|||
|
|
@ -263,7 +263,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
// something that already has `Fn`-like bounds (or is a closure), so we can't
|
||||
// restrict anyways.
|
||||
} else {
|
||||
let copy_did = self.infcx.tcx.require_lang_item(LangItem::Copy, Some(span));
|
||||
let copy_did = self.infcx.tcx.require_lang_item(LangItem::Copy, span);
|
||||
self.suggest_adding_bounds(&mut err, ty, copy_did, span);
|
||||
}
|
||||
|
||||
|
|
@ -1915,7 +1915,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
|
||||
let local_ty = self.body.local_decls[place.local].ty;
|
||||
let typeck_results = tcx.typeck(self.mir_def_id());
|
||||
let clone = tcx.require_lang_item(LangItem::Clone, Some(body.span));
|
||||
let clone = tcx.require_lang_item(LangItem::Clone, body.span);
|
||||
for expr in expr_finder.clones {
|
||||
if let hir::ExprKind::MethodCall(_, rcvr, _, span) = expr.kind
|
||||
&& let Some(rcvr_ty) = typeck_results.node_type_opt(rcvr.hir_id)
|
||||
|
|
@ -3314,7 +3314,13 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
"function parameter".to_string(),
|
||||
"function parameter borrowed here".to_string(),
|
||||
),
|
||||
LocalKind::Temp if self.body.local_decls[local].is_user_variable() => {
|
||||
LocalKind::Temp
|
||||
if self.body.local_decls[local].is_user_variable()
|
||||
&& !self.body.local_decls[local]
|
||||
.source_info
|
||||
.span
|
||||
.in_external_macro(self.infcx.tcx.sess.source_map()) =>
|
||||
{
|
||||
("local binding".to_string(), "local binding introduced here".to_string())
|
||||
}
|
||||
LocalKind::ReturnPointer | LocalKind::Temp => {
|
||||
|
|
|
|||
348
compiler/rustc_borrowck/src/handle_placeholders.rs
Normal file
348
compiler/rustc_borrowck/src/handle_placeholders.rs
Normal file
|
|
@ -0,0 +1,348 @@
|
|||
//! Logic for lowering higher-kinded outlives constraints
|
||||
//! (with placeholders and universes) and turn them into regular
|
||||
//! outlives constraints.
|
||||
|
||||
use rustc_data_structures::frozen::Frozen;
|
||||
use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_data_structures::graph::scc;
|
||||
use rustc_data_structures::graph::scc::Sccs;
|
||||
use rustc_index::IndexVec;
|
||||
use rustc_infer::infer::RegionVariableOrigin;
|
||||
use rustc_middle::mir::ConstraintCategory;
|
||||
use rustc_middle::ty::{RegionVid, UniverseIndex};
|
||||
use tracing::debug;
|
||||
|
||||
use crate::constraints::{ConstraintSccIndex, OutlivesConstraintSet};
|
||||
use crate::consumers::OutlivesConstraint;
|
||||
use crate::diagnostics::UniverseInfo;
|
||||
use crate::member_constraints::MemberConstraintSet;
|
||||
use crate::region_infer::values::{LivenessValues, PlaceholderIndices};
|
||||
use crate::region_infer::{ConstraintSccs, RegionDefinition, Representative, TypeTest};
|
||||
use crate::ty::VarianceDiagInfo;
|
||||
use crate::type_check::free_region_relations::UniversalRegionRelations;
|
||||
use crate::type_check::{Locations, MirTypeckRegionConstraints};
|
||||
use crate::universal_regions::UniversalRegions;
|
||||
use crate::{BorrowckInferCtxt, NllRegionVariableOrigin};
|
||||
|
||||
/// A set of outlives constraints after rewriting to remove
|
||||
/// higher-kinded constraints.
|
||||
pub(crate) struct LoweredConstraints<'tcx> {
|
||||
pub(crate) constraint_sccs: Sccs<RegionVid, ConstraintSccIndex>,
|
||||
pub(crate) definitions: Frozen<IndexVec<RegionVid, RegionDefinition<'tcx>>>,
|
||||
pub(crate) scc_annotations: IndexVec<ConstraintSccIndex, RegionTracker>,
|
||||
pub(crate) member_constraints: MemberConstraintSet<'tcx, RegionVid>,
|
||||
pub(crate) outlives_constraints: Frozen<OutlivesConstraintSet<'tcx>>,
|
||||
pub(crate) type_tests: Vec<TypeTest<'tcx>>,
|
||||
pub(crate) liveness_constraints: LivenessValues,
|
||||
pub(crate) universe_causes: FxIndexMap<UniverseIndex, UniverseInfo<'tcx>>,
|
||||
pub(crate) placeholder_indices: PlaceholderIndices,
|
||||
}
|
||||
|
||||
impl<'d, 'tcx, A: scc::Annotation> SccAnnotations<'d, 'tcx, A> {
|
||||
pub(crate) fn init(definitions: &'d IndexVec<RegionVid, RegionDefinition<'tcx>>) -> Self {
|
||||
Self { scc_to_annotation: IndexVec::new(), definitions }
|
||||
}
|
||||
}
|
||||
|
||||
/// A Visitor for SCC annotation construction.
|
||||
pub(crate) struct SccAnnotations<'d, 'tcx, A: scc::Annotation> {
|
||||
pub(crate) scc_to_annotation: IndexVec<ConstraintSccIndex, A>,
|
||||
definitions: &'d IndexVec<RegionVid, RegionDefinition<'tcx>>,
|
||||
}
|
||||
|
||||
impl scc::Annotations<RegionVid> for SccAnnotations<'_, '_, RegionTracker> {
|
||||
fn new(&self, element: RegionVid) -> RegionTracker {
|
||||
RegionTracker::new(element, &self.definitions[element])
|
||||
}
|
||||
|
||||
fn annotate_scc(&mut self, scc: ConstraintSccIndex, annotation: RegionTracker) {
|
||||
let idx = self.scc_to_annotation.push(annotation);
|
||||
assert!(idx == scc);
|
||||
}
|
||||
|
||||
type Ann = RegionTracker;
|
||||
type SccIdx = ConstraintSccIndex;
|
||||
}
|
||||
|
||||
/// An annotation for region graph SCCs that tracks
|
||||
/// the values of its elements. This annotates a single SCC.
|
||||
#[derive(Copy, Debug, Clone)]
|
||||
pub(crate) struct RegionTracker {
|
||||
/// The largest universe of a placeholder reached from this SCC.
|
||||
/// This includes placeholders within this SCC.
|
||||
max_placeholder_universe_reached: UniverseIndex,
|
||||
|
||||
/// The largest universe nameable from this SCC.
|
||||
/// It is the smallest nameable universes of all
|
||||
/// existential regions reachable from it.
|
||||
max_nameable_universe: UniverseIndex,
|
||||
|
||||
/// The representative Region Variable Id for this SCC.
|
||||
pub(crate) representative: Representative,
|
||||
}
|
||||
|
||||
impl RegionTracker {
|
||||
pub(crate) fn new(rvid: RegionVid, definition: &RegionDefinition<'_>) -> Self {
|
||||
let placeholder_universe =
|
||||
if matches!(definition.origin, NllRegionVariableOrigin::Placeholder(_)) {
|
||||
definition.universe
|
||||
} else {
|
||||
UniverseIndex::ROOT
|
||||
};
|
||||
|
||||
Self {
|
||||
max_placeholder_universe_reached: placeholder_universe,
|
||||
max_nameable_universe: definition.universe,
|
||||
representative: Representative::new(rvid, definition),
|
||||
}
|
||||
}
|
||||
|
||||
/// The largest universe this SCC can name. It's the smallest
|
||||
/// largest nameable uninverse of any reachable region.
|
||||
pub(crate) fn max_nameable_universe(self) -> UniverseIndex {
|
||||
self.max_nameable_universe
|
||||
}
|
||||
|
||||
fn merge_min_max_seen(&mut self, other: &Self) {
|
||||
self.max_placeholder_universe_reached = std::cmp::max(
|
||||
self.max_placeholder_universe_reached,
|
||||
other.max_placeholder_universe_reached,
|
||||
);
|
||||
|
||||
self.max_nameable_universe =
|
||||
std::cmp::min(self.max_nameable_universe, other.max_nameable_universe);
|
||||
}
|
||||
|
||||
/// Returns `true` if during the annotated SCC reaches a placeholder
|
||||
/// with a universe larger than the smallest nameable universe of any
|
||||
/// reachable existential region.
|
||||
pub(crate) fn has_incompatible_universes(&self) -> bool {
|
||||
self.max_nameable_universe().cannot_name(self.max_placeholder_universe_reached)
|
||||
}
|
||||
|
||||
/// Determine if the tracked universes of the two SCCs are compatible.
|
||||
pub(crate) fn universe_compatible_with(&self, other: Self) -> bool {
|
||||
self.max_nameable_universe().can_name(other.max_nameable_universe())
|
||||
|| self.max_nameable_universe().can_name(other.max_placeholder_universe_reached)
|
||||
}
|
||||
}
|
||||
|
||||
impl scc::Annotation for RegionTracker {
|
||||
fn merge_scc(mut self, other: Self) -> Self {
|
||||
self.representative = self.representative.merge_scc(other.representative);
|
||||
self.merge_min_max_seen(&other);
|
||||
self
|
||||
}
|
||||
|
||||
fn merge_reached(mut self, other: Self) -> Self {
|
||||
// No update to in-component values, only add seen values.
|
||||
self.merge_min_max_seen(&other);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Determines if the region variable definitions contain
|
||||
/// placeholders, and compute them for later use.
|
||||
fn region_definitions<'tcx>(
|
||||
universal_regions: &UniversalRegions<'tcx>,
|
||||
infcx: &BorrowckInferCtxt<'tcx>,
|
||||
) -> (Frozen<IndexVec<RegionVid, RegionDefinition<'tcx>>>, bool) {
|
||||
let var_infos = infcx.get_region_var_infos();
|
||||
// Create a RegionDefinition for each inference variable. This happens here because
|
||||
// it allows us to sneak in a cheap check for placeholders. Otherwise, its proper home
|
||||
// is in `RegionInferenceContext::new()`, probably.
|
||||
let mut definitions = IndexVec::with_capacity(var_infos.len());
|
||||
let mut has_placeholders = false;
|
||||
|
||||
for info in var_infos.iter() {
|
||||
let origin = match info.origin {
|
||||
RegionVariableOrigin::Nll(origin) => origin,
|
||||
_ => NllRegionVariableOrigin::Existential { from_forall: false },
|
||||
};
|
||||
|
||||
let definition = RegionDefinition { origin, universe: info.universe, external_name: None };
|
||||
|
||||
has_placeholders |= matches!(origin, NllRegionVariableOrigin::Placeholder(_));
|
||||
definitions.push(definition);
|
||||
}
|
||||
|
||||
// Add external names from universal regions in fun function definitions.
|
||||
// FIXME: this two-step method is annoying, but I don't know how to avoid it.
|
||||
for (external_name, variable) in universal_regions.named_universal_regions_iter() {
|
||||
debug!("region {:?} has external name {:?}", variable, external_name);
|
||||
definitions[variable].external_name = Some(external_name);
|
||||
}
|
||||
(Frozen::freeze(definitions), has_placeholders)
|
||||
}
|
||||
|
||||
/// This method handles placeholders by rewriting the constraint
|
||||
/// graph. For each strongly connected component in the constraint
|
||||
/// graph such that there is a series of constraints
|
||||
/// A: B: C: ... : X where
|
||||
/// A contains a placeholder whose universe cannot be named by X,
|
||||
/// add a constraint that A: 'static. This is a safe upper bound
|
||||
/// in the face of borrow checker/trait solver limitations that will
|
||||
/// eventually go away.
|
||||
///
|
||||
/// For a more precise definition, see the documentation for
|
||||
/// [`RegionTracker`] and its methods!
|
||||
///
|
||||
/// This edge case used to be handled during constraint propagation.
|
||||
/// It was rewritten as part of the Polonius project with the goal of moving
|
||||
/// higher-kindedness concerns out of the path of the borrow checker,
|
||||
/// for two reasons:
|
||||
///
|
||||
/// 1. Implementing Polonius is difficult enough without also
|
||||
/// handling them.
|
||||
/// 2. The long-term goal is to handle higher-kinded concerns
|
||||
/// in the trait solver, where they belong. This avoids
|
||||
/// logic duplication and allows future trait solvers
|
||||
/// to compute better bounds than for example our
|
||||
/// "must outlive 'static" here.
|
||||
///
|
||||
/// This code is a stop-gap measure in preparation for the future trait solver.
|
||||
///
|
||||
/// Every constraint added by this method is an internal `IllegalUniverse` constraint.
|
||||
pub(crate) fn compute_sccs_applying_placeholder_outlives_constraints<'tcx>(
|
||||
constraints: MirTypeckRegionConstraints<'tcx>,
|
||||
universal_region_relations: &Frozen<UniversalRegionRelations<'tcx>>,
|
||||
infcx: &BorrowckInferCtxt<'tcx>,
|
||||
) -> LoweredConstraints<'tcx> {
|
||||
let universal_regions = &universal_region_relations.universal_regions;
|
||||
let (definitions, has_placeholders) = region_definitions(universal_regions, infcx);
|
||||
|
||||
let MirTypeckRegionConstraints {
|
||||
placeholder_indices,
|
||||
placeholder_index_to_region: _,
|
||||
liveness_constraints,
|
||||
mut outlives_constraints,
|
||||
mut member_constraints,
|
||||
universe_causes,
|
||||
type_tests,
|
||||
} = constraints;
|
||||
|
||||
if let Some(guar) = universal_regions.tainted_by_errors() {
|
||||
debug!("Universal regions tainted by errors; removing constraints!");
|
||||
// Suppress unhelpful extra errors in `infer_opaque_types` by clearing out all
|
||||
// outlives bounds that we may end up checking.
|
||||
outlives_constraints = Default::default();
|
||||
member_constraints = Default::default();
|
||||
|
||||
// Also taint the entire scope.
|
||||
infcx.set_tainted_by_errors(guar);
|
||||
}
|
||||
|
||||
let fr_static = universal_regions.fr_static;
|
||||
let compute_sccs =
|
||||
|constraints: &OutlivesConstraintSet<'tcx>,
|
||||
annotations: &mut SccAnnotations<'_, 'tcx, RegionTracker>| {
|
||||
ConstraintSccs::new_with_annotation(
|
||||
&constraints.graph(definitions.len()).region_graph(constraints, fr_static),
|
||||
annotations,
|
||||
)
|
||||
};
|
||||
|
||||
let mut scc_annotations = SccAnnotations::init(&definitions);
|
||||
let constraint_sccs = compute_sccs(&outlives_constraints, &mut scc_annotations);
|
||||
|
||||
// This code structure is a bit convoluted because it allows for a planned
|
||||
// future change where the early return here has a different type of annotation
|
||||
// that does much less work.
|
||||
if !has_placeholders {
|
||||
debug!("No placeholder regions found; skipping rewriting logic!");
|
||||
|
||||
return LoweredConstraints {
|
||||
type_tests,
|
||||
member_constraints,
|
||||
constraint_sccs,
|
||||
scc_annotations: scc_annotations.scc_to_annotation,
|
||||
definitions,
|
||||
outlives_constraints: Frozen::freeze(outlives_constraints),
|
||||
liveness_constraints,
|
||||
universe_causes,
|
||||
placeholder_indices,
|
||||
};
|
||||
}
|
||||
debug!("Placeholders present; activating placeholder handling logic!");
|
||||
|
||||
let added_constraints = rewrite_placeholder_outlives(
|
||||
&constraint_sccs,
|
||||
&scc_annotations,
|
||||
fr_static,
|
||||
&mut outlives_constraints,
|
||||
);
|
||||
|
||||
let (constraint_sccs, scc_annotations) = if added_constraints {
|
||||
let mut annotations = SccAnnotations::init(&definitions);
|
||||
|
||||
// We changed the constraint set and so must recompute SCCs.
|
||||
// Optimisation opportunity: if we can add them incrementally (and that's
|
||||
// possible because edges to 'static always only merge SCCs into 'static),
|
||||
// we would potentially save a lot of work here.
|
||||
(compute_sccs(&outlives_constraints, &mut annotations), annotations.scc_to_annotation)
|
||||
} else {
|
||||
// If we didn't add any back-edges; no more work needs doing
|
||||
debug!("No constraints rewritten!");
|
||||
(constraint_sccs, scc_annotations.scc_to_annotation)
|
||||
};
|
||||
|
||||
LoweredConstraints {
|
||||
constraint_sccs,
|
||||
definitions,
|
||||
scc_annotations,
|
||||
member_constraints,
|
||||
outlives_constraints: Frozen::freeze(outlives_constraints),
|
||||
type_tests,
|
||||
liveness_constraints,
|
||||
universe_causes,
|
||||
placeholder_indices,
|
||||
}
|
||||
}
|
||||
|
||||
fn rewrite_placeholder_outlives<'tcx>(
|
||||
sccs: &Sccs<RegionVid, ConstraintSccIndex>,
|
||||
annotations: &SccAnnotations<'_, '_, RegionTracker>,
|
||||
fr_static: RegionVid,
|
||||
outlives_constraints: &mut OutlivesConstraintSet<'tcx>,
|
||||
) -> bool {
|
||||
// Changed to `true` if we added any constraints and need to
|
||||
// recompute SCCs.
|
||||
let mut added_constraints = false;
|
||||
|
||||
let annotations = &annotations.scc_to_annotation;
|
||||
|
||||
for scc in sccs.all_sccs() {
|
||||
// No point in adding 'static: 'static!
|
||||
// This micro-optimisation makes somewhat sense
|
||||
// because static outlives *everything*.
|
||||
if scc == sccs.scc(fr_static) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let annotation = annotations[scc];
|
||||
|
||||
// If this SCC participates in a universe violation,
|
||||
// e.g. if it reaches a region with a universe smaller than
|
||||
// the largest region reached, add a requirement that it must
|
||||
// outlive `'static`.
|
||||
if annotation.has_incompatible_universes() {
|
||||
// Optimisation opportunity: this will add more constraints than
|
||||
// needed for correctness, since an SCC upstream of another with
|
||||
// a universe violation will "infect" its downstream SCCs to also
|
||||
// outlive static.
|
||||
let scc_representative_outlives_static = OutlivesConstraint {
|
||||
sup: annotation.representative.rvid(),
|
||||
sub: fr_static,
|
||||
category: ConstraintCategory::IllegalUniverse,
|
||||
locations: Locations::All(rustc_span::DUMMY_SP),
|
||||
span: rustc_span::DUMMY_SP,
|
||||
variance_info: VarianceDiagInfo::None,
|
||||
from_closure: false,
|
||||
};
|
||||
outlives_constraints.push(scc_representative_outlives_static);
|
||||
added_constraints = true;
|
||||
debug!("Added {:?}: 'static!", annotation.representative.rvid());
|
||||
}
|
||||
}
|
||||
added_constraints
|
||||
}
|
||||
|
|
@ -72,6 +72,7 @@ mod constraints;
|
|||
mod dataflow;
|
||||
mod def_use;
|
||||
mod diagnostics;
|
||||
mod handle_placeholders;
|
||||
mod member_constraints;
|
||||
mod nll;
|
||||
mod path_utils;
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ use tracing::{debug, instrument};
|
|||
use crate::borrow_set::BorrowSet;
|
||||
use crate::consumers::ConsumerOptions;
|
||||
use crate::diagnostics::RegionErrors;
|
||||
use crate::handle_placeholders::compute_sccs_applying_placeholder_outlives_constraints;
|
||||
use crate::polonius::PoloniusDiagnosticsContext;
|
||||
use crate::polonius::legacy::{
|
||||
PoloniusFacts, PoloniusFactsExt, PoloniusLocationTable, PoloniusOutput,
|
||||
|
|
@ -113,6 +114,12 @@ pub(crate) fn compute_regions<'tcx>(
|
|||
Rc::clone(&location_map),
|
||||
);
|
||||
|
||||
let lowered_constraints = compute_sccs_applying_placeholder_outlives_constraints(
|
||||
constraints,
|
||||
&universal_region_relations,
|
||||
infcx,
|
||||
);
|
||||
|
||||
// If requested, emit legacy polonius facts.
|
||||
polonius::legacy::emit_facts(
|
||||
&mut polonius_facts,
|
||||
|
|
@ -122,11 +129,15 @@ pub(crate) fn compute_regions<'tcx>(
|
|||
borrow_set,
|
||||
move_data,
|
||||
&universal_region_relations,
|
||||
&constraints,
|
||||
&lowered_constraints,
|
||||
);
|
||||
|
||||
let mut regioncx =
|
||||
RegionInferenceContext::new(infcx, constraints, universal_region_relations, location_map);
|
||||
let mut regioncx = RegionInferenceContext::new(
|
||||
infcx,
|
||||
lowered_constraints,
|
||||
universal_region_relations,
|
||||
location_map,
|
||||
);
|
||||
|
||||
// If requested for `-Zpolonius=next`, convert NLL constraints to localized outlives constraints
|
||||
// and use them to compute loan liveness.
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ use tracing::debug;
|
|||
|
||||
use crate::borrow_set::BorrowSet;
|
||||
use crate::constraints::OutlivesConstraint;
|
||||
use crate::type_check::MirTypeckRegionConstraints;
|
||||
use crate::handle_placeholders::LoweredConstraints;
|
||||
use crate::type_check::free_region_relations::UniversalRegionRelations;
|
||||
use crate::universal_regions::UniversalRegions;
|
||||
|
||||
|
|
@ -43,7 +43,7 @@ pub(crate) fn emit_facts<'tcx>(
|
|||
borrow_set: &BorrowSet<'tcx>,
|
||||
move_data: &MoveData<'tcx>,
|
||||
universal_region_relations: &UniversalRegionRelations<'tcx>,
|
||||
constraints: &MirTypeckRegionConstraints<'tcx>,
|
||||
constraints: &LoweredConstraints<'tcx>,
|
||||
) {
|
||||
let Some(facts) = facts else {
|
||||
// We don't do anything if there are no facts to fill.
|
||||
|
|
@ -203,7 +203,7 @@ pub(crate) fn emit_drop_facts<'tcx>(
|
|||
fn emit_outlives_facts<'tcx>(
|
||||
facts: &mut PoloniusFacts,
|
||||
location_table: &PoloniusLocationTable,
|
||||
constraints: &MirTypeckRegionConstraints<'tcx>,
|
||||
constraints: &LoweredConstraints<'tcx>,
|
||||
) {
|
||||
facts.subset_base.extend(constraints.outlives_constraints.outlives().iter().flat_map(
|
||||
|constraint: &OutlivesConstraint<'_>| {
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
"| {r:rw$?} | {ui:4?} | {v}",
|
||||
r = region,
|
||||
rw = REGION_WIDTH,
|
||||
ui = self.region_universe(region),
|
||||
ui = self.max_nameable_universe(self.constraint_sccs.scc(region)),
|
||||
v = self.region_value_str(region),
|
||||
)?;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ use rustc_hir::def_id::CRATE_DEF_ID;
|
|||
use rustc_index::IndexVec;
|
||||
use rustc_infer::infer::outlives::test_type_match;
|
||||
use rustc_infer::infer::region_constraints::{GenericKind, VerifyBound, VerifyIfEq};
|
||||
use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin, RegionVariableOrigin};
|
||||
use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin};
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::mir::{
|
||||
AnnotationSource, BasicBlock, Body, ConstraintCategory, Local, Location, ReturnConstraint,
|
||||
|
|
@ -28,13 +28,14 @@ use crate::constraints::graph::{self, NormalConstraintGraph, RegionGraph};
|
|||
use crate::constraints::{ConstraintSccIndex, OutlivesConstraint, OutlivesConstraintSet};
|
||||
use crate::dataflow::BorrowIndex;
|
||||
use crate::diagnostics::{RegionErrorKind, RegionErrors, UniverseInfo};
|
||||
use crate::handle_placeholders::{LoweredConstraints, RegionTracker};
|
||||
use crate::member_constraints::{MemberConstraintSet, NllMemberConstraintIndex};
|
||||
use crate::polonius::LiveLoans;
|
||||
use crate::polonius::legacy::PoloniusOutput;
|
||||
use crate::region_infer::reverse_sccs::ReverseSccGraph;
|
||||
use crate::region_infer::values::{LivenessValues, RegionElement, RegionValues, ToElementIndex};
|
||||
use crate::type_check::Locations;
|
||||
use crate::type_check::free_region_relations::UniversalRegionRelations;
|
||||
use crate::type_check::{Locations, MirTypeckRegionConstraints};
|
||||
use crate::universal_regions::UniversalRegions;
|
||||
use crate::{
|
||||
BorrowckInferCtxt, ClosureOutlivesRequirement, ClosureOutlivesSubject,
|
||||
|
|
@ -48,125 +49,48 @@ mod reverse_sccs;
|
|||
|
||||
pub(crate) mod values;
|
||||
|
||||
pub(crate) type ConstraintSccs = Sccs<RegionVid, ConstraintSccIndex>;
|
||||
pub(crate) type AnnotatedSccs = (ConstraintSccs, IndexVec<ConstraintSccIndex, RegionTracker>);
|
||||
|
||||
/// An annotation for region graph SCCs that tracks
|
||||
/// the values of its elements. This annotates a single SCC.
|
||||
#[derive(Copy, Debug, Clone)]
|
||||
pub(crate) struct RegionTracker {
|
||||
/// The largest universe of a placeholder reached from this SCC.
|
||||
/// This includes placeholders within this SCC.
|
||||
max_placeholder_universe_reached: UniverseIndex,
|
||||
|
||||
/// The smallest universe index reachable form the nodes of this SCC.
|
||||
min_reachable_universe: UniverseIndex,
|
||||
|
||||
/// The representative Region Variable Id for this SCC. We prefer
|
||||
/// placeholders over existentially quantified variables, otherwise
|
||||
/// it's the one with the smallest Region Variable ID.
|
||||
pub(crate) representative: RegionVid,
|
||||
|
||||
/// Is the current representative a placeholder?
|
||||
representative_is_placeholder: bool,
|
||||
|
||||
/// Is the current representative existentially quantified?
|
||||
representative_is_existential: bool,
|
||||
/// The representative region variable for an SCC, tagged by its origin.
|
||||
/// We prefer placeholders over existentially quantified variables, otherwise
|
||||
/// it's the one with the smallest Region Variable ID. In other words,
|
||||
/// the order of this enumeration really matters!
|
||||
#[derive(Copy, Debug, Clone, PartialEq, PartialOrd, Eq, Ord)]
|
||||
pub(crate) enum Representative {
|
||||
FreeRegion(RegionVid),
|
||||
Placeholder(RegionVid),
|
||||
Existential(RegionVid),
|
||||
}
|
||||
|
||||
impl scc::Annotation for RegionTracker {
|
||||
fn merge_scc(mut self, mut other: Self) -> Self {
|
||||
// Prefer any placeholder over any existential
|
||||
if other.representative_is_placeholder && self.representative_is_existential {
|
||||
other.merge_min_max_seen(&self);
|
||||
return other;
|
||||
impl Representative {
|
||||
pub(crate) fn rvid(self) -> RegionVid {
|
||||
match self {
|
||||
Representative::FreeRegion(region_vid)
|
||||
| Representative::Placeholder(region_vid)
|
||||
| Representative::Existential(region_vid) => region_vid,
|
||||
}
|
||||
|
||||
if self.representative_is_placeholder && other.representative_is_existential
|
||||
|| (self.representative <= other.representative)
|
||||
{
|
||||
self.merge_min_max_seen(&other);
|
||||
return self;
|
||||
}
|
||||
other.merge_min_max_seen(&self);
|
||||
other
|
||||
}
|
||||
|
||||
fn merge_reached(mut self, other: Self) -> Self {
|
||||
// No update to in-component values, only add seen values.
|
||||
self.merge_min_max_seen(&other);
|
||||
pub(crate) fn new(r: RegionVid, definition: &RegionDefinition<'_>) -> Self {
|
||||
match definition.origin {
|
||||
NllRegionVariableOrigin::FreeRegion => Representative::FreeRegion(r),
|
||||
NllRegionVariableOrigin::Placeholder(_) => Representative::Placeholder(r),
|
||||
NllRegionVariableOrigin::Existential { .. } => Representative::Existential(r),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl scc::Annotation for Representative {
|
||||
fn merge_scc(self, other: Self) -> Self {
|
||||
// Just pick the smallest one. Note that we order by tag first!
|
||||
std::cmp::min(self, other)
|
||||
}
|
||||
|
||||
// For reachability, we do nothing since the representative doesn't change.
|
||||
fn merge_reached(self, _other: Self) -> Self {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// A Visitor for SCC annotation construction.
|
||||
pub(crate) struct SccAnnotations<'d, 'tcx, A: scc::Annotation> {
|
||||
pub(crate) scc_to_annotation: IndexVec<ConstraintSccIndex, A>,
|
||||
definitions: &'d IndexVec<RegionVid, RegionDefinition<'tcx>>,
|
||||
}
|
||||
|
||||
impl<'d, 'tcx, A: scc::Annotation> SccAnnotations<'d, 'tcx, A> {
|
||||
pub(crate) fn new(definitions: &'d IndexVec<RegionVid, RegionDefinition<'tcx>>) -> Self {
|
||||
Self { scc_to_annotation: IndexVec::new(), definitions }
|
||||
}
|
||||
}
|
||||
|
||||
impl scc::Annotations<RegionVid> for SccAnnotations<'_, '_, RegionTracker> {
|
||||
fn new(&self, element: RegionVid) -> RegionTracker {
|
||||
RegionTracker::new(element, &self.definitions[element])
|
||||
}
|
||||
|
||||
fn annotate_scc(&mut self, scc: ConstraintSccIndex, annotation: RegionTracker) {
|
||||
let idx = self.scc_to_annotation.push(annotation);
|
||||
assert!(idx == scc);
|
||||
}
|
||||
|
||||
type Ann = RegionTracker;
|
||||
type SccIdx = ConstraintSccIndex;
|
||||
}
|
||||
|
||||
impl RegionTracker {
|
||||
pub(crate) fn new(rvid: RegionVid, definition: &RegionDefinition<'_>) -> Self {
|
||||
let (representative_is_placeholder, representative_is_existential) = match definition.origin
|
||||
{
|
||||
NllRegionVariableOrigin::FreeRegion => (false, false),
|
||||
NllRegionVariableOrigin::Placeholder(_) => (true, false),
|
||||
NllRegionVariableOrigin::Existential { .. } => (false, true),
|
||||
};
|
||||
|
||||
let placeholder_universe =
|
||||
if representative_is_placeholder { definition.universe } else { UniverseIndex::ROOT };
|
||||
|
||||
Self {
|
||||
max_placeholder_universe_reached: placeholder_universe,
|
||||
min_reachable_universe: definition.universe,
|
||||
representative: rvid,
|
||||
representative_is_placeholder,
|
||||
representative_is_existential,
|
||||
}
|
||||
}
|
||||
|
||||
/// The smallest-indexed universe reachable from and/or in this SCC.
|
||||
fn min_universe(self) -> UniverseIndex {
|
||||
self.min_reachable_universe
|
||||
}
|
||||
|
||||
fn merge_min_max_seen(&mut self, other: &Self) {
|
||||
self.max_placeholder_universe_reached = std::cmp::max(
|
||||
self.max_placeholder_universe_reached,
|
||||
other.max_placeholder_universe_reached,
|
||||
);
|
||||
|
||||
self.min_reachable_universe =
|
||||
std::cmp::min(self.min_reachable_universe, other.min_reachable_universe);
|
||||
}
|
||||
|
||||
/// Returns `true` if during the annotated SCC reaches a placeholder
|
||||
/// with a universe larger than the smallest reachable one, `false` otherwise.
|
||||
pub(crate) fn has_incompatible_universes(&self) -> bool {
|
||||
self.min_universe().cannot_name(self.max_placeholder_universe_reached)
|
||||
}
|
||||
}
|
||||
pub(crate) type ConstraintSccs = Sccs<RegionVid, ConstraintSccIndex>;
|
||||
|
||||
pub struct RegionInferenceContext<'tcx> {
|
||||
/// Contains the definition for every region variable. Region
|
||||
|
|
@ -414,26 +338,6 @@ fn sccs_info<'tcx>(infcx: &BorrowckInferCtxt<'tcx>, sccs: &ConstraintSccs) {
|
|||
debug!("SCC edges {:#?}", scc_node_to_edges);
|
||||
}
|
||||
|
||||
fn create_definitions<'tcx>(
|
||||
infcx: &BorrowckInferCtxt<'tcx>,
|
||||
universal_regions: &UniversalRegions<'tcx>,
|
||||
) -> Frozen<IndexVec<RegionVid, RegionDefinition<'tcx>>> {
|
||||
// Create a RegionDefinition for each inference variable.
|
||||
let mut definitions: IndexVec<_, _> = infcx
|
||||
.get_region_var_infos()
|
||||
.iter()
|
||||
.map(|info| RegionDefinition::new(info.universe, info.origin))
|
||||
.collect();
|
||||
|
||||
// Add the external name for all universal regions.
|
||||
for (external_name, variable) in universal_regions.named_universal_regions_iter() {
|
||||
debug!("region {variable:?} has external name {external_name:?}");
|
||||
definitions[variable].external_name = Some(external_name);
|
||||
}
|
||||
|
||||
Frozen::freeze(definitions)
|
||||
}
|
||||
|
||||
impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
/// Creates a new region inference context with a total of
|
||||
/// `num_region_variables` valid inference variables; the first N
|
||||
|
|
@ -444,42 +348,30 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
/// of constraints produced by the MIR type check.
|
||||
pub(crate) fn new(
|
||||
infcx: &BorrowckInferCtxt<'tcx>,
|
||||
constraints: MirTypeckRegionConstraints<'tcx>,
|
||||
lowered_constraints: LoweredConstraints<'tcx>,
|
||||
universal_region_relations: Frozen<UniversalRegionRelations<'tcx>>,
|
||||
location_map: Rc<DenseLocationMap>,
|
||||
) -> Self {
|
||||
let universal_regions = &universal_region_relations.universal_regions;
|
||||
let MirTypeckRegionConstraints {
|
||||
placeholder_indices,
|
||||
placeholder_index_to_region: _,
|
||||
liveness_constraints,
|
||||
mut outlives_constraints,
|
||||
mut member_constraints,
|
||||
universe_causes,
|
||||
|
||||
let LoweredConstraints {
|
||||
constraint_sccs,
|
||||
definitions,
|
||||
outlives_constraints,
|
||||
scc_annotations,
|
||||
type_tests,
|
||||
} = constraints;
|
||||
liveness_constraints,
|
||||
universe_causes,
|
||||
placeholder_indices,
|
||||
member_constraints,
|
||||
} = lowered_constraints;
|
||||
|
||||
debug!("universal_regions: {:#?}", universal_region_relations.universal_regions);
|
||||
debug!("outlives constraints: {:#?}", outlives_constraints);
|
||||
debug!("placeholder_indices: {:#?}", placeholder_indices);
|
||||
debug!("type tests: {:#?}", type_tests);
|
||||
|
||||
if let Some(guar) = universal_region_relations.universal_regions.tainted_by_errors() {
|
||||
// Suppress unhelpful extra errors in `infer_opaque_types` by clearing out all
|
||||
// outlives bounds that we may end up checking.
|
||||
outlives_constraints = Default::default();
|
||||
member_constraints = Default::default();
|
||||
|
||||
// Also taint the entire scope.
|
||||
infcx.set_tainted_by_errors(guar);
|
||||
}
|
||||
|
||||
let definitions = create_definitions(infcx, &universal_regions);
|
||||
|
||||
let (constraint_sccs, scc_annotations) =
|
||||
outlives_constraints.add_outlives_static(&universal_regions, &definitions);
|
||||
let constraints = Frozen::freeze(outlives_constraints);
|
||||
let constraint_graph = Frozen::freeze(constraints.graph(definitions.len()));
|
||||
let constraint_graph = Frozen::freeze(outlives_constraints.graph(definitions.len()));
|
||||
|
||||
if cfg!(debug_assertions) {
|
||||
sccs_info(infcx, &constraint_sccs);
|
||||
|
|
@ -499,7 +391,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
let mut result = Self {
|
||||
definitions,
|
||||
liveness_constraints,
|
||||
constraints,
|
||||
constraints: outlives_constraints,
|
||||
constraint_graph,
|
||||
constraint_sccs,
|
||||
scc_annotations,
|
||||
|
|
@ -658,11 +550,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
self.scc_values.placeholders_contained_in(scc)
|
||||
}
|
||||
|
||||
/// Returns access to the value of `r` for debugging purposes.
|
||||
pub(crate) fn region_universe(&self, r: RegionVid) -> ty::UniverseIndex {
|
||||
self.scc_universe(self.constraint_sccs.scc(r))
|
||||
}
|
||||
|
||||
/// Once region solving has completed, this function will return the member constraints that
|
||||
/// were applied to the value of a given SCC `scc`. See `AppliedMemberConstraint`.
|
||||
pub(crate) fn applied_member_constraints(
|
||||
|
|
@ -826,7 +713,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
|
||||
// If the member region lives in a higher universe, we currently choose
|
||||
// the most conservative option by leaving it unchanged.
|
||||
if !self.scc_universe(scc).is_root() {
|
||||
if !self.max_nameable_universe(scc).is_root() {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -902,20 +789,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
/// in `scc_a`. Used during constraint propagation, and only once
|
||||
/// the value of `scc_b` has been computed.
|
||||
fn universe_compatible(&self, scc_b: ConstraintSccIndex, scc_a: ConstraintSccIndex) -> bool {
|
||||
let a_annotation = self.scc_annotations[scc_a];
|
||||
let b_annotation = self.scc_annotations[scc_b];
|
||||
let a_universe = a_annotation.min_universe();
|
||||
|
||||
// If scc_b's declared universe is a subset of
|
||||
// scc_a's declared universe (typically, both are ROOT), then
|
||||
// it cannot contain any problematic universe elements.
|
||||
if a_universe.can_name(b_annotation.min_universe()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Otherwise, there can be no placeholder in `b` with a too high
|
||||
// universe index to name from `a`.
|
||||
a_universe.can_name(b_annotation.max_placeholder_universe_reached)
|
||||
self.scc_annotations[scc_a].universe_compatible_with(self.scc_annotations[scc_b])
|
||||
}
|
||||
|
||||
/// Once regions have been propagated, this method is used to see
|
||||
|
|
@ -1019,7 +893,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
"lower_bound = {:?} r_scc={:?} universe={:?}",
|
||||
lower_bound,
|
||||
r_scc,
|
||||
self.scc_universe(r_scc)
|
||||
self.max_nameable_universe(r_scc)
|
||||
);
|
||||
// If the type test requires that `T: 'a` where `'a` is a
|
||||
// placeholder from another universe, that effectively requires
|
||||
|
|
@ -1497,10 +1371,9 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
/// The minimum universe of any variable reachable from this
|
||||
/// SCC, inside or outside of it.
|
||||
fn scc_universe(&self, scc: ConstraintSccIndex) -> UniverseIndex {
|
||||
self.scc_annotations[scc].min_universe()
|
||||
/// The largest universe of any region nameable from this SCC.
|
||||
fn max_nameable_universe(&self, scc: ConstraintSccIndex) -> UniverseIndex {
|
||||
self.scc_annotations[scc].max_nameable_universe()
|
||||
}
|
||||
|
||||
/// Checks the final value for the free region `fr` to see if it
|
||||
|
|
@ -1522,7 +1395,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
|
||||
// Because this free region must be in the ROOT universe, we
|
||||
// know it cannot contain any bound universes.
|
||||
assert!(self.scc_universe(longer_fr_scc).is_root());
|
||||
assert!(self.max_nameable_universe(longer_fr_scc).is_root());
|
||||
|
||||
// Only check all of the relations for the main representative of each
|
||||
// SCC, otherwise just check that we outlive said representative. This
|
||||
|
|
@ -1913,7 +1786,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
#[instrument(skip(self), level = "trace", ret)]
|
||||
pub(crate) fn find_sub_region_live_at(&self, fr1: RegionVid, location: Location) -> RegionVid {
|
||||
trace!(scc = ?self.constraint_sccs.scc(fr1));
|
||||
trace!(universe = ?self.region_universe(fr1));
|
||||
trace!(universe = ?self.max_nameable_universe(self.constraint_sccs.scc(fr1)));
|
||||
self.find_constraint_paths_between_regions(fr1, |r| {
|
||||
// First look for some `r` such that `fr1: r` and `r` is live at `location`
|
||||
trace!(?r, liveness_constraints=?self.liveness_constraints.pretty_print_live_points(r));
|
||||
|
|
@ -2244,7 +2117,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
/// they *must* be equal (though not having the same repr does not
|
||||
/// mean they are unequal).
|
||||
fn scc_representative(&self, scc: ConstraintSccIndex) -> RegionVid {
|
||||
self.scc_annotations[scc].representative
|
||||
self.scc_annotations[scc].representative.rvid()
|
||||
}
|
||||
|
||||
pub(crate) fn liveness_constraints(&self) -> &LivenessValues {
|
||||
|
|
@ -2266,21 +2139,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'tcx> RegionDefinition<'tcx> {
|
||||
fn new(universe: ty::UniverseIndex, rv_origin: RegionVariableOrigin) -> Self {
|
||||
// Create a new region definition. Note that, for free
|
||||
// regions, the `external_name` field gets updated later in
|
||||
// `init_free_and_bound_regions`.
|
||||
|
||||
let origin = match rv_origin {
|
||||
RegionVariableOrigin::Nll(origin) => origin,
|
||||
_ => NllRegionVariableOrigin::Existential { from_forall: false },
|
||||
};
|
||||
|
||||
Self { origin, universe, external_name: None }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct BlameConstraint<'tcx> {
|
||||
pub category: ConstraintCategory<'tcx>,
|
||||
|
|
|
|||
|
|
@ -191,7 +191,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
let scc = self.constraint_sccs.scc(vid);
|
||||
|
||||
// Special handling of higher-ranked regions.
|
||||
if !self.scc_universe(scc).is_root() {
|
||||
if !self.max_nameable_universe(scc).is_root() {
|
||||
match self.scc_values.placeholders_contained_in(scc).enumerate().last() {
|
||||
// If the region contains a single placeholder then they're equal.
|
||||
Some((0, placeholder)) => {
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
|||
assert_matches!(
|
||||
self.tcx().coroutine_kind(self.tcx().coroutine_for_closure(mir_def_id)),
|
||||
Some(hir::CoroutineKind::Desugared(
|
||||
hir::CoroutineDesugaring::Async,
|
||||
hir::CoroutineDesugaring::Async | hir::CoroutineDesugaring::Gen,
|
||||
hir::CoroutineSource::Closure
|
||||
)),
|
||||
"this needs to be modified if we're lowering non-async closures"
|
||||
|
|
|
|||
|
|
@ -688,7 +688,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
|||
if !self.unsized_feature_enabled() {
|
||||
let trait_ref = ty::TraitRef::new(
|
||||
tcx,
|
||||
tcx.require_lang_item(LangItem::Sized, Some(self.last_span)),
|
||||
tcx.require_lang_item(LangItem::Sized, self.last_span),
|
||||
[place_ty],
|
||||
);
|
||||
self.prove_trait_ref(
|
||||
|
|
@ -1010,7 +1010,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
|||
let ty = place.ty(self.body, tcx).ty;
|
||||
let trait_ref = ty::TraitRef::new(
|
||||
tcx,
|
||||
tcx.require_lang_item(LangItem::Copy, Some(span)),
|
||||
tcx.require_lang_item(LangItem::Copy, span),
|
||||
[ty],
|
||||
);
|
||||
|
||||
|
|
@ -1025,11 +1025,8 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
|||
}
|
||||
|
||||
&Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf, ty) => {
|
||||
let trait_ref = ty::TraitRef::new(
|
||||
tcx,
|
||||
tcx.require_lang_item(LangItem::Sized, Some(span)),
|
||||
[ty],
|
||||
);
|
||||
let trait_ref =
|
||||
ty::TraitRef::new(tcx, tcx.require_lang_item(LangItem::Sized, span), [ty]);
|
||||
|
||||
self.prove_trait_ref(
|
||||
trait_ref,
|
||||
|
|
@ -1041,11 +1038,8 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
|||
&Rvalue::NullaryOp(NullOp::UbChecks, _) => {}
|
||||
|
||||
Rvalue::ShallowInitBox(_operand, ty) => {
|
||||
let trait_ref = ty::TraitRef::new(
|
||||
tcx,
|
||||
tcx.require_lang_item(LangItem::Sized, Some(span)),
|
||||
[*ty],
|
||||
);
|
||||
let trait_ref =
|
||||
ty::TraitRef::new(tcx, tcx.require_lang_item(LangItem::Sized, span), [*ty]);
|
||||
|
||||
self.prove_trait_ref(
|
||||
trait_ref,
|
||||
|
|
@ -1222,7 +1216,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
|||
let &ty = ty;
|
||||
let trait_ref = ty::TraitRef::new(
|
||||
tcx,
|
||||
tcx.require_lang_item(LangItem::CoerceUnsized, Some(span)),
|
||||
tcx.require_lang_item(LangItem::CoerceUnsized, span),
|
||||
[op.ty(self.body, tcx), ty],
|
||||
);
|
||||
|
||||
|
|
@ -1811,7 +1805,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
|||
if let PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy) = context {
|
||||
let trait_ref = ty::TraitRef::new(
|
||||
tcx,
|
||||
tcx.require_lang_item(LangItem::Copy, Some(self.last_span)),
|
||||
tcx.require_lang_item(LangItem::Copy, self.last_span),
|
||||
[place_ty.ty],
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -544,10 +544,10 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> {
|
|||
// (as it's created inside the body itself, not passed in from outside).
|
||||
if let DefiningTy::FnDef(def_id, _) = defining_ty {
|
||||
if self.infcx.tcx.fn_sig(def_id).skip_binder().c_variadic() {
|
||||
let va_list_did = self.infcx.tcx.require_lang_item(
|
||||
LangItem::VaList,
|
||||
Some(self.infcx.tcx.def_span(self.mir_def)),
|
||||
);
|
||||
let va_list_did = self
|
||||
.infcx
|
||||
.tcx
|
||||
.require_lang_item(LangItem::VaList, self.infcx.tcx.def_span(self.mir_def));
|
||||
|
||||
let reg_vid = self
|
||||
.infcx
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ use crate::deriving::generic::ty::*;
|
|||
use crate::deriving::generic::*;
|
||||
use crate::deriving::{path_local, path_std};
|
||||
|
||||
/// Expands a `#[derive(PartialEq)]` attribute into an implementation for the
|
||||
/// target item.
|
||||
pub(crate) fn expand_deriving_partial_eq(
|
||||
cx: &ExtCtxt<'_>,
|
||||
span: Span,
|
||||
|
|
@ -16,62 +18,6 @@ pub(crate) fn expand_deriving_partial_eq(
|
|||
push: &mut dyn FnMut(Annotatable),
|
||||
is_const: bool,
|
||||
) {
|
||||
fn cs_eq(cx: &ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> BlockOrExpr {
|
||||
let base = true;
|
||||
let expr = cs_fold(
|
||||
true, // use foldl
|
||||
cx,
|
||||
span,
|
||||
substr,
|
||||
|cx, fold| match fold {
|
||||
CsFold::Single(field) => {
|
||||
let [other_expr] = &field.other_selflike_exprs[..] else {
|
||||
cx.dcx()
|
||||
.span_bug(field.span, "not exactly 2 arguments in `derive(PartialEq)`");
|
||||
};
|
||||
|
||||
// We received arguments of type `&T`. Convert them to type `T` by stripping
|
||||
// any leading `&`. This isn't necessary for type checking, but
|
||||
// it results in better error messages if something goes wrong.
|
||||
//
|
||||
// Note: for arguments that look like `&{ x }`, which occur with packed
|
||||
// structs, this would cause expressions like `{ self.x } == { other.x }`,
|
||||
// which isn't valid Rust syntax. This wouldn't break compilation because these
|
||||
// AST nodes are constructed within the compiler. But it would mean that code
|
||||
// printed by `-Zunpretty=expanded` (or `cargo expand`) would have invalid
|
||||
// syntax, which would be suboptimal. So we wrap these in parens, giving
|
||||
// `({ self.x }) == ({ other.x })`, which is valid syntax.
|
||||
let convert = |expr: &P<Expr>| {
|
||||
if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) =
|
||||
&expr.kind
|
||||
{
|
||||
if let ExprKind::Block(..) = &inner.kind {
|
||||
// `&{ x }` form: remove the `&`, add parens.
|
||||
cx.expr_paren(field.span, inner.clone())
|
||||
} else {
|
||||
// `&x` form: remove the `&`.
|
||||
inner.clone()
|
||||
}
|
||||
} else {
|
||||
expr.clone()
|
||||
}
|
||||
};
|
||||
cx.expr_binary(
|
||||
field.span,
|
||||
BinOpKind::Eq,
|
||||
convert(&field.self_expr),
|
||||
convert(other_expr),
|
||||
)
|
||||
}
|
||||
CsFold::Combine(span, expr1, expr2) => {
|
||||
cx.expr_binary(span, BinOpKind::And, expr1, expr2)
|
||||
}
|
||||
CsFold::Fieldless => cx.expr_bool(span, base),
|
||||
},
|
||||
);
|
||||
BlockOrExpr::new_expr(expr)
|
||||
}
|
||||
|
||||
let structural_trait_def = TraitDef {
|
||||
span,
|
||||
path: path_std!(marker::StructuralPartialEq),
|
||||
|
|
@ -97,7 +43,9 @@ pub(crate) fn expand_deriving_partial_eq(
|
|||
ret_ty: Path(path_local!(bool)),
|
||||
attributes: thin_vec![cx.attr_word(sym::inline, span)],
|
||||
fieldless_variants_strategy: FieldlessVariantsStrategy::Unify,
|
||||
combine_substructure: combine_substructure(Box::new(|a, b, c| cs_eq(a, b, c))),
|
||||
combine_substructure: combine_substructure(Box::new(|a, b, c| {
|
||||
BlockOrExpr::new_expr(get_substructure_equality_expr(a, b, c))
|
||||
})),
|
||||
}];
|
||||
|
||||
let trait_def = TraitDef {
|
||||
|
|
@ -113,3 +61,156 @@ pub(crate) fn expand_deriving_partial_eq(
|
|||
};
|
||||
trait_def.expand(cx, mitem, item, push)
|
||||
}
|
||||
|
||||
/// Generates the equality expression for a struct or enum variant when deriving
|
||||
/// `PartialEq`.
|
||||
///
|
||||
/// This function generates an expression that checks if all fields of a struct
|
||||
/// or enum variant are equal.
|
||||
/// - Scalar fields are compared first for efficiency, followed by compound
|
||||
/// fields.
|
||||
/// - If there are no fields, returns `true` (fieldless types are always equal).
|
||||
///
|
||||
/// Whether a field is considered "scalar" is determined by comparing the symbol
|
||||
/// of its type to a set of known scalar type symbols (e.g., `i32`, `u8`, etc).
|
||||
/// This check is based on the type's symbol.
|
||||
///
|
||||
/// ### Example 1
|
||||
/// ```
|
||||
/// #[derive(PartialEq)]
|
||||
/// struct i32;
|
||||
///
|
||||
/// // Here, `field_2` is of type `i32`, but since it's a user-defined type (not
|
||||
/// // the primitive), it will not be treated as scalar. The function will still
|
||||
/// // check equality of `field_2` first because the symbol matches `i32`.
|
||||
/// #[derive(PartialEq)]
|
||||
/// struct Struct {
|
||||
/// field_1: &'static str,
|
||||
/// field_2: i32,
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ### Example 2
|
||||
/// ```
|
||||
/// mod ty {
|
||||
/// pub type i32 = i32;
|
||||
/// }
|
||||
///
|
||||
/// // Here, `field_2` is of type `ty::i32`, which is a type alias for `i32`.
|
||||
/// // However, the function will not reorder the fields because the symbol for
|
||||
/// // `ty::i32` does not match the symbol for the primitive `i32`
|
||||
/// // ("ty::i32" != "i32").
|
||||
/// #[derive(PartialEq)]
|
||||
/// struct Struct {
|
||||
/// field_1: &'static str,
|
||||
/// field_2: ty::i32,
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// For enums, the discriminant is compared first, then the rest of the fields.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// If called on static or all-fieldless enums/structs, which should not occur
|
||||
/// during derive expansion.
|
||||
fn get_substructure_equality_expr(
|
||||
cx: &ExtCtxt<'_>,
|
||||
span: Span,
|
||||
substructure: &Substructure<'_>,
|
||||
) -> P<Expr> {
|
||||
use SubstructureFields::*;
|
||||
|
||||
match substructure.fields {
|
||||
EnumMatching(.., fields) | Struct(.., fields) => {
|
||||
let combine = move |acc, field| {
|
||||
let rhs = get_field_equality_expr(cx, field);
|
||||
if let Some(lhs) = acc {
|
||||
// Combine the previous comparison with the current field
|
||||
// using logical AND.
|
||||
return Some(cx.expr_binary(field.span, BinOpKind::And, lhs, rhs));
|
||||
}
|
||||
// Start the chain with the first field's comparison.
|
||||
Some(rhs)
|
||||
};
|
||||
|
||||
// First compare scalar fields, then compound fields, combining all
|
||||
// with logical AND.
|
||||
return fields
|
||||
.iter()
|
||||
.filter(|field| !field.maybe_scalar)
|
||||
.fold(fields.iter().filter(|field| field.maybe_scalar).fold(None, combine), combine)
|
||||
// If there are no fields, treat as always equal.
|
||||
.unwrap_or_else(|| cx.expr_bool(span, true));
|
||||
}
|
||||
EnumDiscr(disc, match_expr) => {
|
||||
let lhs = get_field_equality_expr(cx, disc);
|
||||
let Some(match_expr) = match_expr else {
|
||||
return lhs;
|
||||
};
|
||||
// Compare the discriminant first (cheaper), then the rest of the
|
||||
// fields.
|
||||
return cx.expr_binary(disc.span, BinOpKind::And, lhs, match_expr.clone());
|
||||
}
|
||||
StaticEnum(..) => cx.dcx().span_bug(
|
||||
span,
|
||||
"unexpected static enum encountered during `derive(PartialEq)` expansion",
|
||||
),
|
||||
StaticStruct(..) => cx.dcx().span_bug(
|
||||
span,
|
||||
"unexpected static struct encountered during `derive(PartialEq)` expansion",
|
||||
),
|
||||
AllFieldlessEnum(..) => cx.dcx().span_bug(
|
||||
span,
|
||||
"unexpected all-fieldless enum encountered during `derive(PartialEq)` expansion",
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
/// Generates an equality comparison expression for a single struct or enum
|
||||
/// field.
|
||||
///
|
||||
/// This function produces an AST expression that compares the `self` and
|
||||
/// `other` values for a field using `==`. It removes any leading references
|
||||
/// from both sides for readability. If the field is a block expression, it is
|
||||
/// wrapped in parentheses to ensure valid syntax.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if there are not exactly two arguments to compare (should be `self`
|
||||
/// and `other`).
|
||||
fn get_field_equality_expr(cx: &ExtCtxt<'_>, field: &FieldInfo) -> P<Expr> {
|
||||
let [rhs] = &field.other_selflike_exprs[..] else {
|
||||
cx.dcx().span_bug(field.span, "not exactly 2 arguments in `derive(PartialEq)`");
|
||||
};
|
||||
|
||||
cx.expr_binary(
|
||||
field.span,
|
||||
BinOpKind::Eq,
|
||||
wrap_block_expr(cx, peel_refs(&field.self_expr)),
|
||||
wrap_block_expr(cx, peel_refs(rhs)),
|
||||
)
|
||||
}
|
||||
|
||||
/// Removes all leading immutable references from an expression.
|
||||
///
|
||||
/// This is used to strip away any number of leading `&` from an expression
|
||||
/// (e.g., `&&&T` becomes `T`). Only removes immutable references; mutable
|
||||
/// references are preserved.
|
||||
fn peel_refs(mut expr: &P<Expr>) -> P<Expr> {
|
||||
while let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = &expr.kind {
|
||||
expr = &inner;
|
||||
}
|
||||
expr.clone()
|
||||
}
|
||||
|
||||
/// Wraps a block expression in parentheses to ensure valid AST in macro
|
||||
/// expansion output.
|
||||
///
|
||||
/// If the given expression is a block, it is wrapped in parentheses; otherwise,
|
||||
/// it is returned unchanged.
|
||||
fn wrap_block_expr(cx: &ExtCtxt<'_>, expr: P<Expr>) -> P<Expr> {
|
||||
if matches!(&expr.kind, ExprKind::Block(..)) {
|
||||
return cx.expr_paren(expr.span, expr);
|
||||
}
|
||||
expr
|
||||
}
|
||||
|
|
|
|||
|
|
@ -284,6 +284,7 @@ pub(crate) struct FieldInfo {
|
|||
/// The expressions corresponding to references to this field in
|
||||
/// the other selflike arguments.
|
||||
pub other_selflike_exprs: Vec<P<Expr>>,
|
||||
pub maybe_scalar: bool,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
|
|
@ -1220,7 +1221,8 @@ impl<'a> MethodDef<'a> {
|
|||
|
||||
let self_expr = discr_exprs.remove(0);
|
||||
let other_selflike_exprs = discr_exprs;
|
||||
let discr_field = FieldInfo { span, name: None, self_expr, other_selflike_exprs };
|
||||
let discr_field =
|
||||
FieldInfo { span, name: None, self_expr, other_selflike_exprs, maybe_scalar: true };
|
||||
|
||||
let discr_let_stmts: ThinVec<_> = iter::zip(&discr_idents, &selflike_args)
|
||||
.map(|(&ident, selflike_arg)| {
|
||||
|
|
@ -1533,6 +1535,7 @@ impl<'a> TraitDef<'a> {
|
|||
name: struct_field.ident,
|
||||
self_expr,
|
||||
other_selflike_exprs,
|
||||
maybe_scalar: struct_field.ty.peel_refs().kind.maybe_scalar(),
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ impl MultiItemModifier for BuiltinDerive {
|
|||
let mut items = Vec::new();
|
||||
match item {
|
||||
Annotatable::Stmt(stmt) => {
|
||||
if let ast::StmtKind::Item(item) = stmt.into_inner().kind {
|
||||
if let ast::StmtKind::Item(item) = stmt.kind {
|
||||
(self.0)(
|
||||
ecx,
|
||||
span,
|
||||
|
|
|
|||
53
compiler/rustc_builtin_macros/src/iter.rs
Normal file
53
compiler/rustc_builtin_macros/src/iter.rs
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
use rustc_ast::ptr::P;
|
||||
use rustc_ast::tokenstream::TokenStream;
|
||||
use rustc_ast::{CoroutineKind, DUMMY_NODE_ID, Expr, ast, token};
|
||||
use rustc_errors::PResult;
|
||||
use rustc_expand::base::{self, DummyResult, ExpandResult, ExtCtxt, MacroExpanderResult};
|
||||
use rustc_span::Span;
|
||||
|
||||
pub(crate) fn expand<'cx>(
|
||||
cx: &'cx mut ExtCtxt<'_>,
|
||||
sp: Span,
|
||||
tts: TokenStream,
|
||||
) -> MacroExpanderResult<'cx> {
|
||||
let closure = match parse_closure(cx, sp, tts) {
|
||||
Ok(parsed) => parsed,
|
||||
Err(err) => {
|
||||
return ExpandResult::Ready(DummyResult::any(sp, err.emit()));
|
||||
}
|
||||
};
|
||||
|
||||
ExpandResult::Ready(base::MacEager::expr(closure))
|
||||
}
|
||||
|
||||
fn parse_closure<'a>(
|
||||
cx: &mut ExtCtxt<'a>,
|
||||
span: Span,
|
||||
stream: TokenStream,
|
||||
) -> PResult<'a, P<Expr>> {
|
||||
let mut closure_parser = cx.new_parser_from_tts(stream);
|
||||
|
||||
let coroutine_kind = Some(CoroutineKind::Gen {
|
||||
span,
|
||||
closure_id: DUMMY_NODE_ID,
|
||||
return_impl_trait_id: DUMMY_NODE_ID,
|
||||
});
|
||||
|
||||
let mut closure = closure_parser.parse_expr()?;
|
||||
match &mut closure.kind {
|
||||
ast::ExprKind::Closure(c) => {
|
||||
if let Some(kind) = c.coroutine_kind {
|
||||
cx.dcx().span_err(kind.span(), "only plain closures allowed in `iter!`");
|
||||
}
|
||||
c.coroutine_kind = coroutine_kind;
|
||||
if closure_parser.token != token::Eof {
|
||||
closure_parser.unexpected()?;
|
||||
}
|
||||
Ok(closure)
|
||||
}
|
||||
_ => {
|
||||
cx.dcx().span_err(closure.span, "`iter!` body must be a closure");
|
||||
Err(closure_parser.unexpected().unwrap_err())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -47,6 +47,7 @@ mod errors;
|
|||
mod format;
|
||||
mod format_foreign;
|
||||
mod global_allocator;
|
||||
mod iter;
|
||||
mod log_syntax;
|
||||
mod pattern_type;
|
||||
mod source_util;
|
||||
|
|
@ -95,6 +96,7 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) {
|
|||
include: source_util::expand_include,
|
||||
include_bytes: source_util::expand_include_bytes,
|
||||
include_str: source_util::expand_include_str,
|
||||
iter: iter::expand,
|
||||
line: source_util::expand_line,
|
||||
log_syntax: log_syntax::expand_log_syntax,
|
||||
module_path: source_util::expand_mod,
|
||||
|
|
|
|||
|
|
@ -30,14 +30,12 @@ fn parse_pat_ty<'a>(cx: &mut ExtCtxt<'a>, stream: TokenStream) -> PResult<'a, (P
|
|||
|
||||
let pat = pat_to_ty_pat(
|
||||
cx,
|
||||
parser
|
||||
.parse_pat_no_top_guard(
|
||||
None,
|
||||
RecoverComma::No,
|
||||
RecoverColon::No,
|
||||
CommaRecoveryMode::EitherTupleOrPipe,
|
||||
)?
|
||||
.into_inner(),
|
||||
*parser.parse_pat_no_top_guard(
|
||||
None,
|
||||
RecoverComma::No,
|
||||
RecoverColon::No,
|
||||
CommaRecoveryMode::EitherTupleOrPipe,
|
||||
)?,
|
||||
);
|
||||
|
||||
if parser.token != token::Eof {
|
||||
|
|
@ -58,9 +56,9 @@ fn pat_to_ty_pat(cx: &mut ExtCtxt<'_>, pat: ast::Pat) -> P<TyPat> {
|
|||
end.map(|value| P(AnonConst { id: DUMMY_NODE_ID, value })),
|
||||
include_end,
|
||||
),
|
||||
ast::PatKind::Or(variants) => TyPatKind::Or(
|
||||
variants.into_iter().map(|pat| pat_to_ty_pat(cx, pat.into_inner())).collect(),
|
||||
),
|
||||
ast::PatKind::Or(variants) => {
|
||||
TyPatKind::Or(variants.into_iter().map(|pat| pat_to_ty_pat(cx, *pat)).collect())
|
||||
}
|
||||
ast::PatKind::Err(guar) => TyPatKind::Err(guar),
|
||||
_ => TyPatKind::Err(cx.dcx().span_err(pat.span, "pattern not supported in pattern types")),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -354,30 +354,28 @@ fn mk_decls(cx: &mut ExtCtxt<'_>, macros: &[ProcMacro]) -> P<ast::Item> {
|
|||
})
|
||||
.collect();
|
||||
|
||||
let decls_static = cx
|
||||
.item_static(
|
||||
let mut decls_static = cx.item_static(
|
||||
span,
|
||||
Ident::new(sym::_DECLS, span),
|
||||
cx.ty_ref(
|
||||
span,
|
||||
Ident::new(sym::_DECLS, span),
|
||||
cx.ty_ref(
|
||||
cx.ty(
|
||||
span,
|
||||
cx.ty(
|
||||
span,
|
||||
ast::TyKind::Slice(
|
||||
cx.ty_path(cx.path(span, vec![proc_macro, bridge, client, proc_macro_ty])),
|
||||
),
|
||||
ast::TyKind::Slice(
|
||||
cx.ty_path(cx.path(span, vec![proc_macro, bridge, client, proc_macro_ty])),
|
||||
),
|
||||
None,
|
||||
ast::Mutability::Not,
|
||||
),
|
||||
None,
|
||||
ast::Mutability::Not,
|
||||
cx.expr_array_ref(span, decls),
|
||||
)
|
||||
.map(|mut i| {
|
||||
i.attrs.push(cx.attr_word(sym::rustc_proc_macro_decls, span));
|
||||
i.attrs.push(cx.attr_word(sym::used, span));
|
||||
i.attrs.push(cx.attr_nested_word(sym::allow, sym::deprecated, span));
|
||||
i
|
||||
});
|
||||
),
|
||||
ast::Mutability::Not,
|
||||
cx.expr_array_ref(span, decls),
|
||||
);
|
||||
decls_static.attrs.extend([
|
||||
cx.attr_word(sym::rustc_proc_macro_decls, span),
|
||||
cx.attr_word(sym::used, span),
|
||||
cx.attr_nested_word(sym::allow, sym::deprecated, span),
|
||||
]);
|
||||
|
||||
let block = cx.expr_block(
|
||||
cx.block(span, thin_vec![cx.stmt_item(span, krate), cx.stmt_item(span, decls_static)]),
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ pub(crate) fn expand_test_case(
|
|||
let (mut item, is_stmt) = match anno_item {
|
||||
Annotatable::Item(item) => (item, false),
|
||||
Annotatable::Stmt(stmt) if let ast::StmtKind::Item(_) = stmt.kind => {
|
||||
if let ast::StmtKind::Item(i) = stmt.into_inner().kind {
|
||||
if let ast::StmtKind::Item(i) = stmt.kind {
|
||||
(i, true)
|
||||
} else {
|
||||
unreachable!()
|
||||
|
|
@ -120,11 +120,7 @@ pub(crate) fn expand_test_or_bench(
|
|||
Annotatable::Item(i) => (i, false),
|
||||
Annotatable::Stmt(stmt) if matches!(stmt.kind, ast::StmtKind::Item(_)) => {
|
||||
// FIXME: Use an 'if let' guard once they are implemented
|
||||
if let ast::StmtKind::Item(i) = stmt.into_inner().kind {
|
||||
(i, true)
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
if let ast::StmtKind::Item(i) = stmt.kind { (i, true) } else { unreachable!() }
|
||||
}
|
||||
other => {
|
||||
not_testable_error(cx, attr_sp, None);
|
||||
|
|
@ -381,10 +377,7 @@ pub(crate) fn expand_test_or_bench(
|
|||
.into(),
|
||||
),
|
||||
);
|
||||
test_const = test_const.map(|mut tc| {
|
||||
tc.vis.kind = ast::VisibilityKind::Public;
|
||||
tc
|
||||
});
|
||||
test_const.vis.kind = ast::VisibilityKind::Public;
|
||||
|
||||
// extern crate test
|
||||
let test_extern =
|
||||
|
|
|
|||
|
|
@ -213,11 +213,13 @@ pub(crate) fn copy_dir_recursively(from: &Path, to: &Path) {
|
|||
if filename == "." || filename == ".." {
|
||||
continue;
|
||||
}
|
||||
let src = from.join(&filename);
|
||||
let dst = to.join(&filename);
|
||||
if entry.metadata().unwrap().is_dir() {
|
||||
fs::create_dir(to.join(&filename)).unwrap();
|
||||
copy_dir_recursively(&from.join(&filename), &to.join(&filename));
|
||||
fs::create_dir(&dst).unwrap_or_else(|e| panic!("failed to create {dst:?}: {e}"));
|
||||
copy_dir_recursively(&src, &dst);
|
||||
} else {
|
||||
fs::copy(from.join(&filename), to.join(&filename)).unwrap();
|
||||
fs::copy(&src, &dst).unwrap_or_else(|e| panic!("failed to copy {src:?}->{dst:?}: {e}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -233,7 +233,7 @@ unsafe fn test_vaddvq_f32() {
|
|||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
unsafe fn test_vrndnq_f32() {
|
||||
// AArch64 llvm intrinsic: llvm.aarch64.neon.frintn.v4f32
|
||||
// llvm intrinsic: llvm.roundeven.v4f32
|
||||
let a = f32x4::from([0.1, -1.9, 4.5, 5.5]);
|
||||
let e = f32x4::from([0., -2., 4., 6.]);
|
||||
let r: f32x4 = transmute(vrndnq_f32(transmute(a)));
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ use std::mem;
|
|||
use cranelift_codegen::ir::{ArgumentPurpose, SigRef};
|
||||
use cranelift_codegen::isa::CallConv;
|
||||
use cranelift_module::ModuleError;
|
||||
use rustc_abi::ExternAbi;
|
||||
use rustc_abi::{CanonAbi, ExternAbi, X86Call};
|
||||
use rustc_codegen_ssa::base::is_call_from_compiler_builtins_to_upstream_monomorphization;
|
||||
use rustc_codegen_ssa::errors::CompilerBuiltinsCannotCall;
|
||||
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
|
||||
|
|
@ -19,7 +19,7 @@ use rustc_middle::ty::layout::FnAbiOf;
|
|||
use rustc_middle::ty::print::with_no_trimmed_paths;
|
||||
use rustc_session::Session;
|
||||
use rustc_span::source_map::Spanned;
|
||||
use rustc_target::callconv::{Conv, FnAbi, PassMode};
|
||||
use rustc_target::callconv::{FnAbi, PassMode};
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use self::pass_mode::*;
|
||||
|
|
@ -42,32 +42,27 @@ fn clif_sig_from_fn_abi<'tcx>(
|
|||
Signature { params, returns, call_conv }
|
||||
}
|
||||
|
||||
pub(crate) fn conv_to_call_conv(sess: &Session, c: Conv, default_call_conv: CallConv) -> CallConv {
|
||||
pub(crate) fn conv_to_call_conv(
|
||||
sess: &Session,
|
||||
c: CanonAbi,
|
||||
default_call_conv: CallConv,
|
||||
) -> CallConv {
|
||||
match c {
|
||||
Conv::Rust | Conv::C => default_call_conv,
|
||||
Conv::Cold | Conv::PreserveMost | Conv::PreserveAll => CallConv::Cold,
|
||||
Conv::X86_64SysV => CallConv::SystemV,
|
||||
Conv::X86_64Win64 => CallConv::WindowsFastcall,
|
||||
CanonAbi::Rust | CanonAbi::C => default_call_conv,
|
||||
CanonAbi::RustCold => CallConv::Cold,
|
||||
|
||||
// Should already get a back compat warning
|
||||
Conv::X86Fastcall | Conv::X86Stdcall | Conv::X86ThisCall | Conv::X86VectorCall => {
|
||||
default_call_conv
|
||||
}
|
||||
CanonAbi::X86(x86_call) => match x86_call {
|
||||
X86Call::SysV64 => CallConv::SystemV,
|
||||
X86Call::Win64 => CallConv::WindowsFastcall,
|
||||
// Should already get a back compat warning
|
||||
_ => default_call_conv,
|
||||
},
|
||||
|
||||
Conv::X86Intr | Conv::RiscvInterrupt { .. } => {
|
||||
sess.dcx().fatal(format!("interrupt call conv {c:?} not yet implemented"))
|
||||
CanonAbi::Interrupt(_) | CanonAbi::Arm(_) => {
|
||||
sess.dcx().fatal("call conv {c:?} is not yet implemented")
|
||||
}
|
||||
|
||||
Conv::ArmAapcs => sess.dcx().fatal("aapcs call conv not yet implemented"),
|
||||
Conv::CCmseNonSecureCall => {
|
||||
sess.dcx().fatal("C-cmse-nonsecure-call call conv is not yet implemented");
|
||||
}
|
||||
Conv::CCmseNonSecureEntry => {
|
||||
sess.dcx().fatal("C-cmse-nonsecure-entry call conv is not yet implemented");
|
||||
}
|
||||
|
||||
Conv::Msp430Intr | Conv::GpuKernel | Conv::AvrInterrupt | Conv::AvrNonBlockingInterrupt => {
|
||||
unreachable!("tried to use {c:?} call conv which only exists on an unsupported target");
|
||||
CanonAbi::GpuKernel => {
|
||||
unreachable!("tried to use {c:?} call conv which only exists on an unsupported target")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -610,7 +605,7 @@ pub(crate) fn codegen_terminator_call<'tcx>(
|
|||
target: CallTarget,
|
||||
call_args: &mut Vec<Value>,
|
||||
) {
|
||||
if fn_abi.conv != Conv::C {
|
||||
if fn_abi.conv != CanonAbi::C {
|
||||
fx.tcx.dcx().span_fatal(
|
||||
source_info.span,
|
||||
format!("Variadic call for non-C abi {:?}", fn_abi.conv),
|
||||
|
|
|
|||
|
|
@ -380,7 +380,7 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) {
|
|||
rustc_hir::LangItem::PanicBoundsCheck,
|
||||
&[index, len, location],
|
||||
*unwind,
|
||||
Some(source_info.span),
|
||||
source_info.span,
|
||||
);
|
||||
}
|
||||
AssertKind::MisalignedPointerDereference { ref required, ref found } => {
|
||||
|
|
@ -393,7 +393,7 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) {
|
|||
rustc_hir::LangItem::PanicMisalignedPointerDereference,
|
||||
&[required, found, location],
|
||||
*unwind,
|
||||
Some(source_info.span),
|
||||
source_info.span,
|
||||
);
|
||||
}
|
||||
AssertKind::NullPointerDereference => {
|
||||
|
|
@ -404,7 +404,7 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) {
|
|||
rustc_hir::LangItem::PanicNullPointerDereference,
|
||||
&[location],
|
||||
*unwind,
|
||||
Some(source_info.span),
|
||||
source_info.span,
|
||||
)
|
||||
}
|
||||
_ => {
|
||||
|
|
@ -415,7 +415,7 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) {
|
|||
msg.panic_function(),
|
||||
&[location],
|
||||
*unwind,
|
||||
Some(source_info.span),
|
||||
source_info.span,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -531,7 +531,7 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) {
|
|||
);
|
||||
}
|
||||
TerminatorKind::UnwindTerminate(reason) => {
|
||||
codegen_unwind_terminate(fx, Some(source_info.span), *reason);
|
||||
codegen_unwind_terminate(fx, source_info.span, *reason);
|
||||
}
|
||||
TerminatorKind::UnwindResume => {
|
||||
// FIXME implement unwinding
|
||||
|
|
@ -1074,7 +1074,7 @@ pub(crate) fn codegen_operand<'tcx>(
|
|||
pub(crate) fn codegen_panic_nounwind<'tcx>(
|
||||
fx: &mut FunctionCx<'_, '_, 'tcx>,
|
||||
msg_str: &str,
|
||||
span: Option<Span>,
|
||||
span: Span,
|
||||
) {
|
||||
let msg_ptr = fx.anonymous_str(msg_str);
|
||||
let msg_len = fx.bcx.ins().iconst(fx.pointer_type, i64::try_from(msg_str.len()).unwrap());
|
||||
|
|
@ -1091,7 +1091,7 @@ pub(crate) fn codegen_panic_nounwind<'tcx>(
|
|||
|
||||
pub(crate) fn codegen_unwind_terminate<'tcx>(
|
||||
fx: &mut FunctionCx<'_, '_, 'tcx>,
|
||||
span: Option<Span>,
|
||||
span: Span,
|
||||
reason: UnwindTerminateReason,
|
||||
) {
|
||||
codegen_panic_inner(fx, reason.lang_item(), &[], UnwindAction::Unreachable, span);
|
||||
|
|
@ -1102,7 +1102,7 @@ fn codegen_panic_inner<'tcx>(
|
|||
lang_item: rustc_hir::LangItem,
|
||||
args: &[Value],
|
||||
_unwind: UnwindAction,
|
||||
span: Option<Span>,
|
||||
span: Span,
|
||||
) {
|
||||
fx.bcx.set_cold_block(fx.bcx.current_block().unwrap());
|
||||
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ pub(crate) fn codegen_set_discriminant<'tcx>(
|
|||
tag_encoding: TagEncoding::Direct,
|
||||
variants: _,
|
||||
} => {
|
||||
let ptr = place.place_field(fx, FieldIdx::new(tag_field));
|
||||
let ptr = place.place_field(fx, tag_field);
|
||||
let to = layout.ty.discriminant_for_variant(fx.tcx, variant_index).unwrap().val;
|
||||
let to = match ptr.layout().ty.kind() {
|
||||
ty::Uint(UintTy::U128) | ty::Int(IntTy::I128) => {
|
||||
|
|
@ -53,7 +53,7 @@ pub(crate) fn codegen_set_discriminant<'tcx>(
|
|||
variants: _,
|
||||
} => {
|
||||
if variant_index != untagged_variant {
|
||||
let niche = place.place_field(fx, FieldIdx::new(tag_field));
|
||||
let niche = place.place_field(fx, tag_field);
|
||||
let niche_type = fx.clif_type(niche.layout().ty).unwrap();
|
||||
let niche_value = variant_index.as_u32() - niche_variants.start().as_u32();
|
||||
let niche_value = (niche_value as u128).wrapping_add(niche_start);
|
||||
|
|
@ -118,7 +118,7 @@ pub(crate) fn codegen_get_discriminant<'tcx>(
|
|||
let cast_to = fx.clif_type(dest_layout.ty).unwrap();
|
||||
|
||||
// Read the tag/niche-encoded discriminant from memory.
|
||||
let tag = value.value_field(fx, FieldIdx::new(tag_field));
|
||||
let tag = value.value_field(fx, tag_field);
|
||||
let tag = tag.load_scalar(fx);
|
||||
|
||||
// Decode the discriminant (specifically if it's niche-encoded).
|
||||
|
|
|
|||
|
|
@ -850,7 +850,7 @@ fn asm_clif_type<'tcx>(fx: &FunctionCx<'_, '_, 'tcx>, ty: Ty<'tcx>) -> Option<ty
|
|||
// Adapted from https://github.com/rust-lang/rust/blob/f3c66088610c1b80110297c2d9a8b5f9265b013f/compiler/rustc_hir_analysis/src/check/intrinsicck.rs#L136-L151
|
||||
ty::Adt(adt, args) if fx.tcx.is_lang_item(adt.did(), LangItem::MaybeUninit) => {
|
||||
let fields = &adt.non_enum_variant().fields;
|
||||
let ty = fields[FieldIdx::from_u32(1)].ty(fx.tcx, args);
|
||||
let ty = fields[FieldIdx::ONE].ty(fx.tcx, args);
|
||||
let ty::Adt(ty, args) = ty.kind() else {
|
||||
unreachable!("expected first field of `MaybeUninit` to be an ADT")
|
||||
};
|
||||
|
|
|
|||
|
|
@ -62,6 +62,14 @@ pub(crate) fn codegen_llvm_intrinsic_call<'tcx>(
|
|||
});
|
||||
}
|
||||
|
||||
_ if intrinsic.starts_with("llvm.roundeven.v") => {
|
||||
intrinsic_args!(fx, args => (v); intrinsic);
|
||||
|
||||
simd_for_each_lane(fx, v, ret, &|fx, _lane_ty, _res_lane_ty, lane| {
|
||||
fx.bcx.ins().nearest(lane)
|
||||
});
|
||||
}
|
||||
|
||||
_ => {
|
||||
fx.tcx
|
||||
.dcx()
|
||||
|
|
@ -71,7 +79,7 @@ pub(crate) fn codegen_llvm_intrinsic_call<'tcx>(
|
|||
See https://github.com/rust-lang/rustc_codegen_cranelift/issues/171\n\
|
||||
Please open an issue at https://github.com/rust-lang/rustc_codegen_cranelift/issues"
|
||||
);
|
||||
crate::base::codegen_panic_nounwind(fx, &msg, None);
|
||||
crate::base::codegen_panic_nounwind(fx, &msg, span);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -264,14 +264,6 @@ pub(super) fn codegen_aarch64_llvm_intrinsic_call<'tcx>(
|
|||
simd_reduce(fx, v, None, ret, &|fx, _ty, a, b| fx.bcx.ins().fadd(a, b));
|
||||
}
|
||||
|
||||
_ if intrinsic.starts_with("llvm.aarch64.neon.frintn.v") => {
|
||||
intrinsic_args!(fx, args => (v); intrinsic);
|
||||
|
||||
simd_for_each_lane(fx, v, ret, &|fx, _lane_ty, _res_lane_ty, lane| {
|
||||
fx.bcx.ins().nearest(lane)
|
||||
});
|
||||
}
|
||||
|
||||
_ if intrinsic.starts_with("llvm.aarch64.neon.smaxv.i") => {
|
||||
intrinsic_args!(fx, args => (v); intrinsic);
|
||||
|
||||
|
|
@ -512,7 +504,7 @@ pub(super) fn codegen_aarch64_llvm_intrinsic_call<'tcx>(
|
|||
See https://github.com/rust-lang/rustc_codegen_cranelift/issues/171\n\
|
||||
Please open an issue at https://github.com/rust-lang/rustc_codegen_cranelift/issues"
|
||||
);
|
||||
crate::base::codegen_panic_nounwind(fx, &msg, None);
|
||||
crate::base::codegen_panic_nounwind(fx, &msg, fx.mir.span);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1321,7 +1321,7 @@ pub(super) fn codegen_x86_llvm_intrinsic_call<'tcx>(
|
|||
See https://github.com/rust-lang/rustc_codegen_cranelift/issues/171\n\
|
||||
Please open an issue at https://github.com/rust-lang/rustc_codegen_cranelift/issues"
|
||||
);
|
||||
crate::base::codegen_panic_nounwind(fx, &msg, None);
|
||||
crate::base::codegen_panic_nounwind(fx, &msg, span);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -785,7 +785,7 @@ fn codegen_regular_intrinsic_call<'tcx>(
|
|||
}
|
||||
})
|
||||
});
|
||||
crate::base::codegen_panic_nounwind(fx, &msg_str, Some(source_info.span));
|
||||
crate::base::codegen_panic_nounwind(fx, &msg_str, source_info.span);
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
|
@ -875,7 +875,6 @@ fn codegen_regular_intrinsic_call<'tcx>(
|
|||
let ptr = ptr.load_scalar(fx);
|
||||
|
||||
let ty = generic_args.type_at(0);
|
||||
let _ord = generic_args.const_at(1).to_value(); // FIXME: forward this to cranelift once they support that
|
||||
match ty.kind() {
|
||||
ty::Uint(UintTy::U128) | ty::Int(IntTy::I128) => {
|
||||
// FIXME implement 128bit atomics
|
||||
|
|
@ -884,7 +883,7 @@ fn codegen_regular_intrinsic_call<'tcx>(
|
|||
crate::base::codegen_panic_nounwind(
|
||||
fx,
|
||||
"128bit atomics not yet supported",
|
||||
None,
|
||||
source_info.span,
|
||||
);
|
||||
return Ok(());
|
||||
} else {
|
||||
|
|
@ -906,7 +905,7 @@ fn codegen_regular_intrinsic_call<'tcx>(
|
|||
let val = CValue::by_val(val, fx.layout_of(ty));
|
||||
ret.write_cvalue(fx, val);
|
||||
}
|
||||
_ if intrinsic.as_str().starts_with("atomic_store") => {
|
||||
sym::atomic_store => {
|
||||
intrinsic_args!(fx, args => (ptr, val); intrinsic);
|
||||
let ptr = ptr.load_scalar(fx);
|
||||
|
||||
|
|
@ -919,7 +918,7 @@ fn codegen_regular_intrinsic_call<'tcx>(
|
|||
crate::base::codegen_panic_nounwind(
|
||||
fx,
|
||||
"128bit atomics not yet supported",
|
||||
None,
|
||||
source_info.span,
|
||||
);
|
||||
return Ok(());
|
||||
} else {
|
||||
|
|
@ -939,7 +938,7 @@ fn codegen_regular_intrinsic_call<'tcx>(
|
|||
|
||||
fx.bcx.ins().atomic_store(MemFlags::trusted(), val, ptr);
|
||||
}
|
||||
_ if intrinsic.as_str().starts_with("atomic_xchg") => {
|
||||
sym::atomic_xchg => {
|
||||
intrinsic_args!(fx, args => (ptr, new); intrinsic);
|
||||
let ptr = ptr.load_scalar(fx);
|
||||
|
||||
|
|
@ -960,8 +959,7 @@ fn codegen_regular_intrinsic_call<'tcx>(
|
|||
let old = CValue::by_val(old, layout);
|
||||
ret.write_cvalue(fx, old);
|
||||
}
|
||||
_ if intrinsic.as_str().starts_with("atomic_cxchg") => {
|
||||
// both atomic_cxchg_* and atomic_cxchgweak_*
|
||||
sym::atomic_cxchg | sym::atomic_cxchgweak => {
|
||||
intrinsic_args!(fx, args => (ptr, test_old, new); intrinsic);
|
||||
let ptr = ptr.load_scalar(fx);
|
||||
|
||||
|
|
@ -984,7 +982,7 @@ fn codegen_regular_intrinsic_call<'tcx>(
|
|||
ret.write_cvalue(fx, ret_val)
|
||||
}
|
||||
|
||||
_ if intrinsic.as_str().starts_with("atomic_xadd") => {
|
||||
sym::atomic_xadd => {
|
||||
intrinsic_args!(fx, args => (ptr, amount); intrinsic);
|
||||
let ptr = ptr.load_scalar(fx);
|
||||
|
||||
|
|
@ -1006,7 +1004,7 @@ fn codegen_regular_intrinsic_call<'tcx>(
|
|||
let old = CValue::by_val(old, layout);
|
||||
ret.write_cvalue(fx, old);
|
||||
}
|
||||
_ if intrinsic.as_str().starts_with("atomic_xsub") => {
|
||||
sym::atomic_xsub => {
|
||||
intrinsic_args!(fx, args => (ptr, amount); intrinsic);
|
||||
let ptr = ptr.load_scalar(fx);
|
||||
|
||||
|
|
@ -1028,7 +1026,7 @@ fn codegen_regular_intrinsic_call<'tcx>(
|
|||
let old = CValue::by_val(old, layout);
|
||||
ret.write_cvalue(fx, old);
|
||||
}
|
||||
_ if intrinsic.as_str().starts_with("atomic_and") => {
|
||||
sym::atomic_and => {
|
||||
intrinsic_args!(fx, args => (ptr, src); intrinsic);
|
||||
let ptr = ptr.load_scalar(fx);
|
||||
|
||||
|
|
@ -1049,7 +1047,7 @@ fn codegen_regular_intrinsic_call<'tcx>(
|
|||
let old = CValue::by_val(old, layout);
|
||||
ret.write_cvalue(fx, old);
|
||||
}
|
||||
_ if intrinsic.as_str().starts_with("atomic_or") => {
|
||||
sym::atomic_or => {
|
||||
intrinsic_args!(fx, args => (ptr, src); intrinsic);
|
||||
let ptr = ptr.load_scalar(fx);
|
||||
|
||||
|
|
@ -1070,7 +1068,7 @@ fn codegen_regular_intrinsic_call<'tcx>(
|
|||
let old = CValue::by_val(old, layout);
|
||||
ret.write_cvalue(fx, old);
|
||||
}
|
||||
_ if intrinsic.as_str().starts_with("atomic_xor") => {
|
||||
sym::atomic_xor => {
|
||||
intrinsic_args!(fx, args => (ptr, src); intrinsic);
|
||||
let ptr = ptr.load_scalar(fx);
|
||||
|
||||
|
|
@ -1091,7 +1089,7 @@ fn codegen_regular_intrinsic_call<'tcx>(
|
|||
let old = CValue::by_val(old, layout);
|
||||
ret.write_cvalue(fx, old);
|
||||
}
|
||||
_ if intrinsic.as_str().starts_with("atomic_nand") => {
|
||||
sym::atomic_nand => {
|
||||
intrinsic_args!(fx, args => (ptr, src); intrinsic);
|
||||
let ptr = ptr.load_scalar(fx);
|
||||
|
||||
|
|
@ -1112,7 +1110,7 @@ fn codegen_regular_intrinsic_call<'tcx>(
|
|||
let old = CValue::by_val(old, layout);
|
||||
ret.write_cvalue(fx, old);
|
||||
}
|
||||
_ if intrinsic.as_str().starts_with("atomic_max") => {
|
||||
sym::atomic_max => {
|
||||
intrinsic_args!(fx, args => (ptr, src); intrinsic);
|
||||
let ptr = ptr.load_scalar(fx);
|
||||
|
||||
|
|
@ -1133,7 +1131,7 @@ fn codegen_regular_intrinsic_call<'tcx>(
|
|||
let old = CValue::by_val(old, layout);
|
||||
ret.write_cvalue(fx, old);
|
||||
}
|
||||
_ if intrinsic.as_str().starts_with("atomic_umax") => {
|
||||
sym::atomic_umax => {
|
||||
intrinsic_args!(fx, args => (ptr, src); intrinsic);
|
||||
let ptr = ptr.load_scalar(fx);
|
||||
|
||||
|
|
@ -1154,7 +1152,7 @@ fn codegen_regular_intrinsic_call<'tcx>(
|
|||
let old = CValue::by_val(old, layout);
|
||||
ret.write_cvalue(fx, old);
|
||||
}
|
||||
_ if intrinsic.as_str().starts_with("atomic_min") => {
|
||||
sym::atomic_min => {
|
||||
intrinsic_args!(fx, args => (ptr, src); intrinsic);
|
||||
let ptr = ptr.load_scalar(fx);
|
||||
|
||||
|
|
@ -1175,7 +1173,7 @@ fn codegen_regular_intrinsic_call<'tcx>(
|
|||
let old = CValue::by_val(old, layout);
|
||||
ret.write_cvalue(fx, old);
|
||||
}
|
||||
_ if intrinsic.as_str().starts_with("atomic_umin") => {
|
||||
sym::atomic_umin => {
|
||||
intrinsic_args!(fx, args => (ptr, src); intrinsic);
|
||||
let ptr = ptr.load_scalar(fx);
|
||||
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ pub(crate) fn maybe_create_entry_wrapper(
|
|||
let call_inst = bcx.ins().call(main_func_ref, &[]);
|
||||
let call_results = bcx.func.dfg.inst_results(call_inst).to_owned();
|
||||
|
||||
let termination_trait = tcx.require_lang_item(LangItem::Termination, None);
|
||||
let termination_trait = tcx.require_lang_item(LangItem::Termination, DUMMY_SP);
|
||||
let report = tcx
|
||||
.associated_items(termination_trait)
|
||||
.find_by_ident_and_kind(
|
||||
|
|
@ -136,7 +136,7 @@ pub(crate) fn maybe_create_entry_wrapper(
|
|||
}
|
||||
} else {
|
||||
// Regular main fn invoked via start lang item.
|
||||
let start_def_id = tcx.require_lang_item(LangItem::Start, None);
|
||||
let start_def_id = tcx.require_lang_item(LangItem::Start, DUMMY_SP);
|
||||
let start_instance = Instance::expect_resolve(
|
||||
tcx,
|
||||
ty::TypingEnv::fully_monomorphized(),
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ fn codegen_three_way_compare<'tcx>(
|
|||
let gt = fx.bcx.ins().icmp(gt_cc, lhs, rhs);
|
||||
let lt = fx.bcx.ins().icmp(lt_cc, lhs, rhs);
|
||||
let val = fx.bcx.ins().isub(gt, lt);
|
||||
CValue::by_val(val, fx.layout_of(fx.tcx.ty_ordering_enum(Some(fx.mir.span))))
|
||||
CValue::by_val(val, fx.layout_of(fx.tcx.ty_ordering_enum(fx.mir.span)))
|
||||
}
|
||||
|
||||
fn codegen_compare_bin_op<'tcx>(
|
||||
|
|
|
|||
|
|
@ -240,7 +240,7 @@ pub(crate) fn size_and_align_of<'tcx>(
|
|||
})
|
||||
});
|
||||
|
||||
codegen_panic_nounwind(fx, &msg_str, None);
|
||||
codegen_panic_nounwind(fx, &msg_str, fx.mir.span);
|
||||
|
||||
fx.bcx.switch_to_block(next_block);
|
||||
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ pub(crate) fn get_ptr_and_method_ref<'tcx>(
|
|||
.layout()
|
||||
.non_1zst_field(fx)
|
||||
.expect("not exactly one non-1-ZST field in a `DispatchFromDyn` type");
|
||||
arg = arg.value_field(fx, FieldIdx::new(idx));
|
||||
arg = arg.value_field(fx, idx);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -62,8 +62,7 @@ pub(crate) fn get_ptr_and_method_ref<'tcx>(
|
|||
let inner_layout = fx.layout_of(arg.layout().ty.builtin_deref(true).unwrap());
|
||||
let dyn_star = CPlace::for_ptr(Pointer::new(arg.load_scalar(fx)), inner_layout);
|
||||
let ptr = dyn_star.place_field(fx, FieldIdx::ZERO).to_ptr();
|
||||
let vtable =
|
||||
dyn_star.place_field(fx, FieldIdx::new(1)).to_cvalue(fx).load_scalar(fx);
|
||||
let vtable = dyn_star.place_field(fx, FieldIdx::ONE).to_cvalue(fx).load_scalar(fx);
|
||||
break 'block (ptr, vtable);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#[cfg(feature = "master")]
|
||||
use gccjit::FnAttribute;
|
||||
use gccjit::{ToLValue, ToRValue, Type};
|
||||
use rustc_abi::{Reg, RegKind};
|
||||
use rustc_abi::{ArmCall, CanonAbi, InterruptKind, Reg, RegKind, X86Call};
|
||||
use rustc_codegen_ssa::traits::{AbiBuilderMethods, BaseTypeCodegenMethods};
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_middle::bug;
|
||||
|
|
@ -10,8 +10,6 @@ use rustc_middle::ty::layout::LayoutOf;
|
|||
#[cfg(feature = "master")]
|
||||
use rustc_session::config;
|
||||
use rustc_target::callconv::{ArgAttributes, CastTarget, FnAbi, PassMode};
|
||||
#[cfg(feature = "master")]
|
||||
use rustc_target::callconv::{Conv, RiscvInterruptKind};
|
||||
|
||||
use crate::builder::Builder;
|
||||
use crate::context::CodegenCx;
|
||||
|
|
@ -238,29 +236,16 @@ impl<'gcc, 'tcx> FnAbiGccExt<'gcc, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
|
|||
}
|
||||
|
||||
#[cfg(feature = "master")]
|
||||
pub fn conv_to_fn_attribute<'gcc>(conv: Conv, arch: &str) -> Option<FnAttribute<'gcc>> {
|
||||
pub fn conv_to_fn_attribute<'gcc>(conv: CanonAbi, arch: &str) -> Option<FnAttribute<'gcc>> {
|
||||
let attribute = match conv {
|
||||
Conv::C | Conv::Rust => return None,
|
||||
Conv::CCmseNonSecureCall => {
|
||||
if arch == "arm" {
|
||||
FnAttribute::ArmCmseNonsecureCall
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
Conv::CCmseNonSecureEntry => {
|
||||
if arch == "arm" {
|
||||
FnAttribute::ArmCmseNonsecureEntry
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
Conv::Cold => FnAttribute::Cold,
|
||||
// NOTE: the preserve attributes are not yet implemented in GCC:
|
||||
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=110899
|
||||
Conv::PreserveMost => return None,
|
||||
Conv::PreserveAll => return None,
|
||||
Conv::GpuKernel => {
|
||||
CanonAbi::C | CanonAbi::Rust => return None,
|
||||
CanonAbi::Arm(arm_call) => match arm_call {
|
||||
ArmCall::CCmseNonSecureCall => FnAttribute::ArmCmseNonsecureCall,
|
||||
ArmCall::CCmseNonSecureEntry => FnAttribute::ArmCmseNonsecureEntry,
|
||||
ArmCall::Aapcs => FnAttribute::ArmPcs("aapcs"),
|
||||
},
|
||||
CanonAbi::RustCold => FnAttribute::Cold,
|
||||
CanonAbi::GpuKernel => {
|
||||
if arch == "amdgpu" {
|
||||
FnAttribute::GcnAmdGpuHsaKernel
|
||||
} else if arch == "nvptx64" {
|
||||
|
|
@ -270,26 +255,24 @@ pub fn conv_to_fn_attribute<'gcc>(conv: Conv, arch: &str) -> Option<FnAttribute<
|
|||
}
|
||||
}
|
||||
// TODO(antoyo): check if those AVR attributes are mapped correctly.
|
||||
Conv::AvrInterrupt => FnAttribute::AvrSignal,
|
||||
Conv::AvrNonBlockingInterrupt => FnAttribute::AvrInterrupt,
|
||||
Conv::ArmAapcs => FnAttribute::ArmPcs("aapcs"),
|
||||
Conv::Msp430Intr => FnAttribute::Msp430Interrupt,
|
||||
Conv::RiscvInterrupt { kind } => {
|
||||
let kind = match kind {
|
||||
RiscvInterruptKind::Machine => "machine",
|
||||
RiscvInterruptKind::Supervisor => "supervisor",
|
||||
};
|
||||
FnAttribute::RiscvInterrupt(kind)
|
||||
}
|
||||
Conv::X86Fastcall => FnAttribute::X86FastCall,
|
||||
Conv::X86Intr => FnAttribute::X86Interrupt,
|
||||
Conv::X86Stdcall => FnAttribute::X86Stdcall,
|
||||
Conv::X86ThisCall => FnAttribute::X86ThisCall,
|
||||
// NOTE: the vectorcall calling convention is not yet implemented in GCC:
|
||||
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=89485
|
||||
Conv::X86VectorCall => return None,
|
||||
Conv::X86_64SysV => FnAttribute::X86SysvAbi,
|
||||
Conv::X86_64Win64 => FnAttribute::X86MsAbi,
|
||||
CanonAbi::Interrupt(interrupt_kind) => match interrupt_kind {
|
||||
InterruptKind::Avr => FnAttribute::AvrSignal,
|
||||
InterruptKind::AvrNonBlocking => FnAttribute::AvrInterrupt,
|
||||
InterruptKind::Msp430 => FnAttribute::Msp430Interrupt,
|
||||
InterruptKind::RiscvMachine => FnAttribute::RiscvInterrupt("machine"),
|
||||
InterruptKind::RiscvSupervisor => FnAttribute::RiscvInterrupt("supervisor"),
|
||||
InterruptKind::X86 => FnAttribute::X86Interrupt,
|
||||
},
|
||||
CanonAbi::X86(x86_call) => match x86_call {
|
||||
X86Call::Fastcall => FnAttribute::X86FastCall,
|
||||
X86Call::Stdcall => FnAttribute::X86Stdcall,
|
||||
X86Call::Thiscall => FnAttribute::X86ThisCall,
|
||||
// // NOTE: the vectorcall calling convention is not yet implemented in GCC:
|
||||
// // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=89485
|
||||
X86Call::Vectorcall => return None,
|
||||
X86Call::SysV64 => FnAttribute::X86SysvAbi,
|
||||
X86Call::Win64 => FnAttribute::X86MsAbi,
|
||||
},
|
||||
};
|
||||
Some(attribute)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,10 +52,6 @@ impl<'a, 'gcc, 'tcx> DebugInfoBuilderMethods for Builder<'a, 'gcc, 'tcx> {
|
|||
fn clear_dbg_loc(&mut self) {
|
||||
self.location = None;
|
||||
}
|
||||
|
||||
fn get_dbg_loc(&self) -> Option<Self::DILocation> {
|
||||
self.location
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate the `debug_context` in an MIR Body.
|
||||
|
|
|
|||
|
|
@ -3,11 +3,11 @@
|
|||
//! 128-bit integers on 32-bit platforms and thus require to be handled manually.
|
||||
|
||||
use gccjit::{BinaryOp, ComparisonOp, FunctionType, Location, RValue, ToRValue, Type, UnaryOp};
|
||||
use rustc_abi::{Endian, ExternAbi};
|
||||
use rustc_abi::{CanonAbi, Endian, ExternAbi};
|
||||
use rustc_codegen_ssa::common::{IntPredicate, TypeKind};
|
||||
use rustc_codegen_ssa::traits::{BackendTypes, BaseTypeCodegenMethods, BuilderMethods, OverflowOp};
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_target::callconv::{ArgAbi, ArgAttributes, Conv, FnAbi, PassMode};
|
||||
use rustc_target::callconv::{ArgAbi, ArgAttributes, FnAbi, PassMode};
|
||||
|
||||
use crate::builder::{Builder, ToGccComp};
|
||||
use crate::common::{SignType, TypeReflection};
|
||||
|
|
@ -397,7 +397,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
|
|||
ret: arg_abi,
|
||||
c_variadic: false,
|
||||
fixed_count: 3,
|
||||
conv: Conv::C,
|
||||
conv: CanonAbi::C,
|
||||
can_unwind: false,
|
||||
};
|
||||
fn_abi.adjust_for_foreign_abi(self.cx, ExternAbi::C { unwind: false });
|
||||
|
|
|
|||
|
|
@ -524,11 +524,6 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc
|
|||
cond
|
||||
}
|
||||
|
||||
fn type_test(&mut self, _pointer: Self::Value, _typeid: Self::Value) -> Self::Value {
|
||||
// Unsupported.
|
||||
self.context.new_rvalue_from_int(self.int_type, 0)
|
||||
}
|
||||
|
||||
fn type_checked_load(
|
||||
&mut self,
|
||||
_llvtable: Self::Value,
|
||||
|
|
|
|||
|
|
@ -2,7 +2,10 @@ use std::borrow::Borrow;
|
|||
use std::cmp;
|
||||
|
||||
use libc::c_uint;
|
||||
use rustc_abi::{BackendRepr, HasDataLayout, Primitive, Reg, RegKind, Size};
|
||||
use rustc_abi::{
|
||||
ArmCall, BackendRepr, CanonAbi, HasDataLayout, InterruptKind, Primitive, Reg, RegKind, Size,
|
||||
X86Call,
|
||||
};
|
||||
use rustc_codegen_ssa::MemFlags;
|
||||
use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue};
|
||||
use rustc_codegen_ssa::mir::place::{PlaceRef, PlaceValue};
|
||||
|
|
@ -12,7 +15,7 @@ use rustc_middle::ty::layout::LayoutOf;
|
|||
use rustc_middle::{bug, ty};
|
||||
use rustc_session::config;
|
||||
use rustc_target::callconv::{
|
||||
ArgAbi, ArgAttribute, ArgAttributes, ArgExtension, CastTarget, Conv, FnAbi, PassMode,
|
||||
ArgAbi, ArgAttribute, ArgAttributes, ArgExtension, CastTarget, FnAbi, PassMode,
|
||||
};
|
||||
use rustc_target::spec::SanitizerSet;
|
||||
use smallvec::SmallVec;
|
||||
|
|
@ -409,11 +412,17 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
|
|||
if !self.can_unwind {
|
||||
func_attrs.push(llvm::AttributeKind::NoUnwind.create_attr(cx.llcx));
|
||||
}
|
||||
if let Conv::RiscvInterrupt { kind } = self.conv {
|
||||
func_attrs.push(llvm::CreateAttrStringValue(cx.llcx, "interrupt", kind.as_str()));
|
||||
}
|
||||
if let Conv::CCmseNonSecureEntry = self.conv {
|
||||
func_attrs.push(llvm::CreateAttrString(cx.llcx, "cmse_nonsecure_entry"))
|
||||
match self.conv {
|
||||
CanonAbi::Interrupt(InterruptKind::RiscvMachine) => {
|
||||
func_attrs.push(llvm::CreateAttrStringValue(cx.llcx, "interrupt", "machine"))
|
||||
}
|
||||
CanonAbi::Interrupt(InterruptKind::RiscvSupervisor) => {
|
||||
func_attrs.push(llvm::CreateAttrStringValue(cx.llcx, "interrupt", "supervisor"))
|
||||
}
|
||||
CanonAbi::Arm(ArmCall::CCmseNonSecureEntry) => {
|
||||
func_attrs.push(llvm::CreateAttrString(cx.llcx, "cmse_nonsecure_entry"))
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
attributes::apply_to_llfn(llfn, llvm::AttributePlace::Function, &{ func_attrs });
|
||||
|
||||
|
|
@ -600,7 +609,7 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
|
|||
llvm::SetInstructionCallConv(callsite, cconv);
|
||||
}
|
||||
|
||||
if self.conv == Conv::CCmseNonSecureCall {
|
||||
if self.conv == CanonAbi::Arm(ArmCall::CCmseNonSecureCall) {
|
||||
// This will probably get ignored on all targets but those supporting the TrustZone-M
|
||||
// extension (thumbv8m targets).
|
||||
let cmse_nonsecure_call = llvm::CreateAttrString(bx.cx.llcx, "cmse_nonsecure_call");
|
||||
|
|
@ -636,17 +645,11 @@ impl AbiBuilderMethods for Builder<'_, '_, '_> {
|
|||
}
|
||||
|
||||
impl llvm::CallConv {
|
||||
pub(crate) fn from_conv(conv: Conv, arch: &str) -> Self {
|
||||
pub(crate) fn from_conv(conv: CanonAbi, arch: &str) -> Self {
|
||||
match conv {
|
||||
Conv::C
|
||||
| Conv::Rust
|
||||
| Conv::CCmseNonSecureCall
|
||||
| Conv::CCmseNonSecureEntry
|
||||
| Conv::RiscvInterrupt { .. } => llvm::CCallConv,
|
||||
Conv::Cold => llvm::ColdCallConv,
|
||||
Conv::PreserveMost => llvm::PreserveMost,
|
||||
Conv::PreserveAll => llvm::PreserveAll,
|
||||
Conv::GpuKernel => {
|
||||
CanonAbi::C | CanonAbi::Rust => llvm::CCallConv,
|
||||
CanonAbi::RustCold => llvm::PreserveMost,
|
||||
CanonAbi::GpuKernel => {
|
||||
if arch == "amdgpu" {
|
||||
llvm::AmdgpuKernel
|
||||
} else if arch == "nvptx64" {
|
||||
|
|
@ -655,17 +658,25 @@ impl llvm::CallConv {
|
|||
panic!("Architecture {arch} does not support GpuKernel calling convention");
|
||||
}
|
||||
}
|
||||
Conv::AvrInterrupt => llvm::AvrInterrupt,
|
||||
Conv::AvrNonBlockingInterrupt => llvm::AvrNonBlockingInterrupt,
|
||||
Conv::ArmAapcs => llvm::ArmAapcsCallConv,
|
||||
Conv::Msp430Intr => llvm::Msp430Intr,
|
||||
Conv::X86Fastcall => llvm::X86FastcallCallConv,
|
||||
Conv::X86Intr => llvm::X86_Intr,
|
||||
Conv::X86Stdcall => llvm::X86StdcallCallConv,
|
||||
Conv::X86ThisCall => llvm::X86_ThisCall,
|
||||
Conv::X86VectorCall => llvm::X86_VectorCall,
|
||||
Conv::X86_64SysV => llvm::X86_64_SysV,
|
||||
Conv::X86_64Win64 => llvm::X86_64_Win64,
|
||||
CanonAbi::Interrupt(interrupt_kind) => match interrupt_kind {
|
||||
InterruptKind::Avr => llvm::AvrInterrupt,
|
||||
InterruptKind::AvrNonBlocking => llvm::AvrNonBlockingInterrupt,
|
||||
InterruptKind::Msp430 => llvm::Msp430Intr,
|
||||
InterruptKind::RiscvMachine | InterruptKind::RiscvSupervisor => llvm::CCallConv,
|
||||
InterruptKind::X86 => llvm::X86_Intr,
|
||||
},
|
||||
CanonAbi::Arm(arm_call) => match arm_call {
|
||||
ArmCall::Aapcs => llvm::ArmAapcsCallConv,
|
||||
ArmCall::CCmseNonSecureCall | ArmCall::CCmseNonSecureEntry => llvm::CCallConv,
|
||||
},
|
||||
CanonAbi::X86(x86_call) => match x86_call {
|
||||
X86Call::Fastcall => llvm::X86FastcallCallConv,
|
||||
X86Call::Stdcall => llvm::X86StdcallCallConv,
|
||||
X86Call::SysV64 => llvm::X86_64_SysV,
|
||||
X86Call::Thiscall => llvm::X86_ThisCall,
|
||||
X86Call::Vectorcall => llvm::X86_VectorCall,
|
||||
X86Call::Win64 => llvm::X86_64_Win64,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1815,8 +1815,11 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
|
|||
let typeid_metadata = self.cx.typeid_metadata(typeid).unwrap();
|
||||
let dbg_loc = self.get_dbg_loc();
|
||||
|
||||
// Test whether the function pointer is associated with the type identifier.
|
||||
let cond = self.type_test(llfn, typeid_metadata);
|
||||
// Test whether the function pointer is associated with the type identifier using the
|
||||
// llvm.type.test intrinsic. The LowerTypeTests link-time optimization pass replaces
|
||||
// calls to this intrinsic with code to test type membership.
|
||||
let typeid = self.get_metadata_value(typeid_metadata);
|
||||
let cond = self.call_intrinsic("llvm.type.test", &[llfn, typeid]);
|
||||
let bb_pass = self.append_sibling_block("type_test.pass");
|
||||
let bb_fail = self.append_sibling_block("type_test.fail");
|
||||
self.cond_br(cond, bb_pass, bb_fail);
|
||||
|
|
|
|||
|
|
@ -541,12 +541,12 @@ impl<'ll> CodegenCx<'ll, '_> {
|
|||
// in the handling of `.init_array` (the static constructor list) in versions of
|
||||
// the gold linker (prior to the one released with binutils 2.36).
|
||||
//
|
||||
// That said, we only ever emit these when compiling for ELF targets, unless
|
||||
// `#[used(compiler)]` is explicitly requested. This is to avoid similar breakage
|
||||
// on other targets, in particular MachO targets have *their* static constructor
|
||||
// lists broken if `llvm.compiler.used` is emitted rather than `llvm.used`. However,
|
||||
// that check happens when assigning the `CodegenFnAttrFlags` in
|
||||
// `rustc_hir_analysis`, so we don't need to take care of it here.
|
||||
// That said, we only ever emit these when `#[used(compiler)]` is explicitly
|
||||
// requested. This is to avoid similar breakage on other targets, in particular
|
||||
// MachO targets have *their* static constructor lists broken if `llvm.compiler.used`
|
||||
// is emitted rather than `llvm.used`. However, that check happens when assigning
|
||||
// the `CodegenFnAttrFlags` in the `codegen_fn_attrs` query, so we don't need to
|
||||
// take care of it here.
|
||||
self.add_compiler_used_global(g);
|
||||
}
|
||||
if attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER) {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use std::borrow::Cow;
|
||||
|
||||
use libc::c_uint;
|
||||
use rustc_abi::{Align, Endian, Size, TagEncoding, VariantIdx, Variants};
|
||||
use rustc_abi::{Align, Endian, FieldIdx, Size, TagEncoding, VariantIdx, Variants};
|
||||
use rustc_codegen_ssa::debuginfo::type_names::compute_debuginfo_type_name;
|
||||
use rustc_codegen_ssa::debuginfo::{tag_base_type, wants_c_like_enum_debuginfo};
|
||||
use rustc_codegen_ssa::traits::{ConstCodegenMethods, MiscCodegenMethods};
|
||||
|
|
@ -401,7 +401,7 @@ fn build_union_fields_for_enum<'ll, 'tcx>(
|
|||
enum_type_and_layout: TyAndLayout<'tcx>,
|
||||
enum_type_di_node: &'ll DIType,
|
||||
variant_indices: impl Iterator<Item = VariantIdx> + Clone,
|
||||
tag_field: usize,
|
||||
tag_field: FieldIdx,
|
||||
untagged_variant_index: Option<VariantIdx>,
|
||||
) -> SmallVec<&'ll DIType> {
|
||||
let tag_base_type = tag_base_type(cx.tcx, enum_type_and_layout);
|
||||
|
|
@ -805,7 +805,7 @@ fn build_union_fields_for_direct_tag_enum_or_coroutine<'ll, 'tcx>(
|
|||
variant_field_infos: &[VariantFieldInfo<'ll>],
|
||||
discr_type_di_node: &'ll DIType,
|
||||
tag_base_type: Ty<'tcx>,
|
||||
tag_field: usize,
|
||||
tag_field: FieldIdx,
|
||||
untagged_variant_index: Option<VariantIdx>,
|
||||
di_flags: DIFlags,
|
||||
) -> SmallVec<&'ll DIType> {
|
||||
|
|
@ -858,7 +858,7 @@ fn build_union_fields_for_direct_tag_enum_or_coroutine<'ll, 'tcx>(
|
|||
}));
|
||||
|
||||
assert_eq!(
|
||||
cx.size_and_align_of(enum_type_and_layout.field(cx, tag_field).ty),
|
||||
cx.size_and_align_of(enum_type_and_layout.field(cx, tag_field.as_usize()).ty),
|
||||
cx.size_and_align_of(self::tag_base_type(cx.tcx, enum_type_and_layout))
|
||||
);
|
||||
|
||||
|
|
@ -875,7 +875,7 @@ fn build_union_fields_for_direct_tag_enum_or_coroutine<'ll, 'tcx>(
|
|||
Endian::Big => (8, 0),
|
||||
};
|
||||
|
||||
let tag_field_offset = enum_type_and_layout.fields.offset(tag_field).bytes();
|
||||
let tag_field_offset = enum_type_and_layout.fields.offset(tag_field.as_usize()).bytes();
|
||||
let lo_offset = Size::from_bytes(tag_field_offset + lo_offset);
|
||||
let hi_offset = Size::from_bytes(tag_field_offset + hi_offset);
|
||||
|
||||
|
|
@ -905,8 +905,8 @@ fn build_union_fields_for_direct_tag_enum_or_coroutine<'ll, 'tcx>(
|
|||
cx,
|
||||
enum_type_di_node,
|
||||
TAG_FIELD_NAME,
|
||||
enum_type_and_layout.field(cx, tag_field),
|
||||
enum_type_and_layout.fields.offset(tag_field),
|
||||
enum_type_and_layout.field(cx, tag_field.as_usize()),
|
||||
enum_type_and_layout.fields.offset(tag_field.as_usize()),
|
||||
di_flags,
|
||||
tag_base_type_di_node,
|
||||
None,
|
||||
|
|
|
|||
|
|
@ -373,7 +373,7 @@ fn build_discr_member_di_node<'ll, 'tcx>(
|
|||
file,
|
||||
UNKNOWN_LINE_NUMBER,
|
||||
layout,
|
||||
enum_or_coroutine_type_and_layout.fields.offset(tag_field),
|
||||
enum_or_coroutine_type_and_layout.fields.offset(tag_field.as_usize()),
|
||||
DIFlags::FlagArtificial,
|
||||
ty,
|
||||
))
|
||||
|
|
|
|||
|
|
@ -147,6 +147,12 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'ll> Builder<'_, 'll, '_> {
|
||||
pub(crate) fn get_dbg_loc(&self) -> Option<&'ll DILocation> {
|
||||
unsafe { llvm::LLVMGetCurrentDebugLocation2(self.llbuilder) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ll> DebugInfoBuilderMethods for Builder<'_, 'll, '_> {
|
||||
// FIXME(eddyb) find a common convention for all of the debuginfo-related
|
||||
// names (choose between `dbg`, `debug`, `debuginfo`, `debug_info` etc.).
|
||||
|
|
@ -209,10 +215,6 @@ impl<'ll> DebugInfoBuilderMethods for Builder<'_, 'll, '_> {
|
|||
}
|
||||
}
|
||||
|
||||
fn get_dbg_loc(&self) -> Option<&'ll DILocation> {
|
||||
unsafe { llvm::LLVMGetCurrentDebugLocation2(self.llbuilder) }
|
||||
}
|
||||
|
||||
fn insert_reference_to_gdb_debug_scripts_section_global(&mut self) {
|
||||
gdb::insert_reference_to_gdb_debug_scripts_section_global(self)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -636,13 +636,6 @@ 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 = self.get_metadata_value(typeid);
|
||||
self.call_intrinsic("llvm.type.test", &[pointer, typeid])
|
||||
}
|
||||
|
||||
fn type_checked_load(
|
||||
&mut self,
|
||||
llvtable: &'ll Value,
|
||||
|
|
|
|||
|
|
@ -282,6 +282,14 @@ pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option<LLVMFea
|
|||
}
|
||||
// Filter out features that are not supported by the current LLVM version
|
||||
("riscv32" | "riscv64", "zacas") if get_version().0 < 20 => None,
|
||||
(
|
||||
"s390x",
|
||||
"message-security-assist-extension12"
|
||||
| "concurrent-functions"
|
||||
| "miscellaneous-extensions-4"
|
||||
| "vector-enhancements-3"
|
||||
| "vector-packed-decimal-enhancement-3",
|
||||
) if get_version().0 < 20 => None,
|
||||
// Enable the evex512 target feature if an avx512 target feature is enabled.
|
||||
("x86", s) if s.starts_with("avx512") => Some(LLVMFeature::with_dependencies(
|
||||
s,
|
||||
|
|
|
|||
|
|
@ -8,8 +8,6 @@ codegen_ssa_aix_strip_not_used = using host's `strip` binary to cross-compile to
|
|||
|
||||
codegen_ssa_archive_build_failure = failed to build archive at `{$path}`: {$error}
|
||||
|
||||
codegen_ssa_atomic_compare_exchange = Atomic compare-exchange intrinsic missing failure memory ordering
|
||||
|
||||
codegen_ssa_autodiff_without_lto = using the autodiff feature requires using fat-lto
|
||||
|
||||
codegen_ssa_bare_instruction_set = `#[instruction_set]` requires an argument
|
||||
|
|
@ -206,8 +204,6 @@ codegen_ssa_missing_cpp_build_tool_component = or a necessary component may be m
|
|||
|
||||
codegen_ssa_missing_features = add the missing features in a `target_feature` attribute
|
||||
|
||||
codegen_ssa_missing_memory_ordering = Atomic intrinsic missing memory ordering
|
||||
|
||||
codegen_ssa_missing_query_depgraph =
|
||||
found CGU-reuse attribute but `-Zquery-dep-graph` was not specified
|
||||
|
||||
|
|
@ -374,10 +370,6 @@ codegen_ssa_unexpected_parameter_name = unexpected parameter name
|
|||
codegen_ssa_unknown_archive_kind =
|
||||
Don't know how to build archive of type: {$kind}
|
||||
|
||||
codegen_ssa_unknown_atomic_operation = unknown atomic operation
|
||||
|
||||
codegen_ssa_unknown_atomic_ordering = unknown ordering in atomic intrinsic
|
||||
|
||||
codegen_ssa_unknown_reuse_kind = unknown cgu-reuse-kind `{$kind}` specified
|
||||
|
||||
codegen_ssa_unsupported_instruction_set = target does not support `#[instruction_set]`
|
||||
|
|
|
|||
|
|
@ -1961,7 +1961,7 @@ fn add_post_link_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor
|
|||
/// This method creates a synthetic object file, which contains undefined references to all symbols
|
||||
/// that are necessary for the linking. They are only present in symbol table but not actually
|
||||
/// used in any sections, so the linker will therefore pick relevant rlibs for linking, but
|
||||
/// unused `#[no_mangle]` or `#[used]` can still be discard by GC sections.
|
||||
/// unused `#[no_mangle]` or `#[used(compiler)]` can still be discard by GC sections.
|
||||
///
|
||||
/// There's a few internal crates in the standard library (aka libcore and
|
||||
/// libstd) which actually have a circular dependence upon one another. This
|
||||
|
|
@ -1995,7 +1995,8 @@ fn add_linked_symbol_object(
|
|||
|
||||
if file.format() == object::BinaryFormat::MachO {
|
||||
// Divide up the sections into sub-sections via symbols for dead code stripping.
|
||||
// Without this flag, unused `#[no_mangle]` or `#[used]` cannot be discard on MachO targets.
|
||||
// Without this flag, unused `#[no_mangle]` or `#[used(compiler)]` cannot be
|
||||
// discard on MachO targets.
|
||||
file.set_subsections_via_symbols();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use std::collections::hash_map::Entry::*;
|
||||
|
||||
use rustc_abi::{CanonAbi, X86Call};
|
||||
use rustc_ast::expand::allocator::{ALLOCATOR_METHODS, NO_ALLOC_SHIM_IS_UNSTABLE, global_fn_name};
|
||||
use rustc_data_structures::unord::UnordMap;
|
||||
use rustc_hir::def::DefKind;
|
||||
|
|
@ -14,7 +15,6 @@ use rustc_middle::ty::{self, GenericArgKind, GenericArgsRef, Instance, SymbolNam
|
|||
use rustc_middle::util::Providers;
|
||||
use rustc_session::config::{CrateType, OomStrategy};
|
||||
use rustc_symbol_mangling::mangle_internal_symbol;
|
||||
use rustc_target::callconv::Conv;
|
||||
use rustc_target::spec::{SanitizerSet, TlsModel};
|
||||
use tracing::debug;
|
||||
|
||||
|
|
@ -652,7 +652,7 @@ pub(crate) fn symbol_name_for_instance_in_crate<'tcx>(
|
|||
fn calling_convention_for_symbol<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
symbol: ExportedSymbol<'tcx>,
|
||||
) -> (Conv, &'tcx [rustc_target::callconv::ArgAbi<'tcx, Ty<'tcx>>]) {
|
||||
) -> (CanonAbi, &'tcx [rustc_target::callconv::ArgAbi<'tcx, Ty<'tcx>>]) {
|
||||
let instance = match symbol {
|
||||
ExportedSymbol::NonGeneric(def_id) | ExportedSymbol::Generic(def_id, _)
|
||||
if tcx.is_static(def_id) =>
|
||||
|
|
@ -683,7 +683,7 @@ fn calling_convention_for_symbol<'tcx>(
|
|||
})
|
||||
.map(|fnabi| (fnabi.conv, &fnabi.args[..]))
|
||||
// FIXME(workingjubilee): why don't we know the convention here?
|
||||
.unwrap_or((Conv::Rust, &[]))
|
||||
.unwrap_or((CanonAbi::Rust, &[]))
|
||||
}
|
||||
|
||||
/// This is the symbol name of the given instance as seen by the linker.
|
||||
|
|
@ -717,14 +717,14 @@ pub(crate) fn linking_symbol_name_for_instance_in_crate<'tcx>(
|
|||
_ => return undecorated,
|
||||
};
|
||||
|
||||
let (conv, args) = calling_convention_for_symbol(tcx, symbol);
|
||||
let (callconv, args) = calling_convention_for_symbol(tcx, symbol);
|
||||
|
||||
// Decorate symbols with prefixes, suffixes and total number of bytes of arguments.
|
||||
// Reference: https://docs.microsoft.com/en-us/cpp/build/reference/decorated-names?view=msvc-170
|
||||
let (prefix, suffix) = match conv {
|
||||
Conv::X86Fastcall => ("@", "@"),
|
||||
Conv::X86Stdcall => ("_", "@"),
|
||||
Conv::X86VectorCall => ("", "@@"),
|
||||
let (prefix, suffix) = match callconv {
|
||||
CanonAbi::X86(X86Call::Fastcall) => ("@", "@"),
|
||||
CanonAbi::X86(X86Call::Stdcall) => ("_", "@"),
|
||||
CanonAbi::X86(X86Call::Vectorcall) => ("", "@@"),
|
||||
_ => {
|
||||
if let Some(prefix) = prefix {
|
||||
undecorated.insert(0, prefix);
|
||||
|
|
@ -758,9 +758,9 @@ pub(crate) fn extend_exported_symbols<'tcx>(
|
|||
symbol: ExportedSymbol<'tcx>,
|
||||
instantiating_crate: CrateNum,
|
||||
) {
|
||||
let (conv, _) = calling_convention_for_symbol(tcx, symbol);
|
||||
let (callconv, _) = calling_convention_for_symbol(tcx, symbol);
|
||||
|
||||
if conv != Conv::GpuKernel || tcx.sess.target.os != "amdhsa" {
|
||||
if callconv != CanonAbi::GpuKernel || tcx.sess.target.os != "amdhsa" {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -561,7 +561,7 @@ pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
|||
|
||||
let EntryFnType::Main { sigpipe } = entry_type;
|
||||
let (start_fn, start_ty, args, instance) = {
|
||||
let start_def_id = cx.tcx().require_lang_item(LangItem::Start, None);
|
||||
let start_def_id = cx.tcx().require_lang_item(LangItem::Start, DUMMY_SP);
|
||||
let start_instance = ty::Instance::expect_resolve(
|
||||
cx.tcx(),
|
||||
cx.typing_env(),
|
||||
|
|
|
|||
|
|
@ -195,35 +195,10 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
tcx.dcx().emit_err(errors::ExpectedUsedSymbol { span: attr.span() });
|
||||
}
|
||||
None => {
|
||||
// Unfortunately, unconditionally using `llvm.used` causes
|
||||
// issues in handling `.init_array` with the gold linker,
|
||||
// but using `llvm.compiler.used` caused a nontrivial amount
|
||||
// of unintentional ecosystem breakage -- particularly on
|
||||
// Mach-O targets.
|
||||
//
|
||||
// As a result, we emit `llvm.compiler.used` only on ELF
|
||||
// targets. This is somewhat ad-hoc, but actually follows
|
||||
// our pre-LLVM 13 behavior (prior to the ecosystem
|
||||
// breakage), and seems to match `clang`'s behavior as well
|
||||
// (both before and after LLVM 13), possibly because they
|
||||
// have similar compatibility concerns to us. See
|
||||
// https://github.com/rust-lang/rust/issues/47384#issuecomment-1019080146
|
||||
// and following comments for some discussion of this, as
|
||||
// well as the comments in `rustc_codegen_llvm` where these
|
||||
// flags are handled.
|
||||
//
|
||||
// Anyway, to be clear: this is still up in the air
|
||||
// somewhat, and is subject to change in the future (which
|
||||
// is a good thing, because this would ideally be a bit
|
||||
// more firmed up).
|
||||
let is_like_elf = !(tcx.sess.target.is_like_darwin
|
||||
|| tcx.sess.target.is_like_windows
|
||||
|| tcx.sess.target.is_like_wasm);
|
||||
codegen_fn_attrs.flags |= if is_like_elf {
|
||||
CodegenFnAttrFlags::USED_COMPILER
|
||||
} else {
|
||||
CodegenFnAttrFlags::USED_LINKER
|
||||
};
|
||||
// Unconditionally using `llvm.used` causes issues in handling
|
||||
// `.init_array` with the gold linker. Luckily gold has been
|
||||
// deprecated with GCC 15 and rustc now warns about using gold.
|
||||
codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED_LINKER
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@ mod temp_stable_hash_impls {
|
|||
|
||||
pub(crate) fn build_langcall<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
||||
bx: &Bx,
|
||||
span: Option<Span>,
|
||||
span: Span,
|
||||
li: LangItem,
|
||||
) -> (Bx::FnAbiOfResult, Bx::Value, Instance<'tcx>) {
|
||||
let tcx = bx.tcx();
|
||||
|
|
|
|||
|
|
@ -796,22 +796,6 @@ pub(crate) struct ShuffleIndicesEvaluation {
|
|||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(codegen_ssa_missing_memory_ordering)]
|
||||
pub(crate) struct MissingMemoryOrdering;
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(codegen_ssa_unknown_atomic_ordering)]
|
||||
pub(crate) struct UnknownAtomicOrdering;
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(codegen_ssa_atomic_compare_exchange)]
|
||||
pub(crate) struct AtomicCompareExchange;
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(codegen_ssa_unknown_atomic_operation)]
|
||||
pub(crate) struct UnknownAtomicOperation;
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
pub enum InvalidMonomorphization<'tcx> {
|
||||
#[diag(codegen_ssa_invalid_monomorphization_basic_integer_type, code = E0511)]
|
||||
|
|
|
|||
|
|
@ -783,7 +783,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
}
|
||||
};
|
||||
|
||||
let (fn_abi, llfn, instance) = common::build_langcall(bx, Some(span), lang_item);
|
||||
let (fn_abi, llfn, instance) = common::build_langcall(bx, span, lang_item);
|
||||
|
||||
// Codegen the actual panic invoke/call.
|
||||
let merging_succ =
|
||||
|
|
@ -803,7 +803,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
self.set_debug_loc(bx, terminator.source_info);
|
||||
|
||||
// Obtain the panic entry point.
|
||||
let (fn_abi, llfn, instance) = common::build_langcall(bx, Some(span), reason.lang_item());
|
||||
let (fn_abi, llfn, instance) = common::build_langcall(bx, span, reason.lang_item());
|
||||
|
||||
// Codegen the actual panic invoke/call.
|
||||
let merging_succ = helper.do_call(
|
||||
|
|
@ -871,7 +871,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
|
||||
// Obtain the panic entry point.
|
||||
let (fn_abi, llfn, instance) =
|
||||
common::build_langcall(bx, Some(source_info.span), LangItem::PanicNounwind);
|
||||
common::build_langcall(bx, source_info.span, LangItem::PanicNounwind);
|
||||
|
||||
// Codegen the actual panic invoke/call.
|
||||
Some(helper.do_call(
|
||||
|
|
@ -1077,7 +1077,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
let (idx, _) = op.layout.non_1zst_field(bx).expect(
|
||||
"not exactly one non-1-ZST field in a `DispatchFromDyn` type",
|
||||
);
|
||||
op = op.extract_field(self, bx, idx);
|
||||
op = op.extract_field(self, bx, idx.as_usize());
|
||||
}
|
||||
|
||||
// Now that we have `*dyn Trait` or `&dyn Trait`, split it up into its
|
||||
|
|
@ -1109,7 +1109,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
let (idx, _) = op.layout.non_1zst_field(bx).expect(
|
||||
"not exactly one non-1-ZST field in a `DispatchFromDyn` type",
|
||||
);
|
||||
op = op.extract_field(self, bx, idx);
|
||||
op = op.extract_field(self, bx, idx.as_usize());
|
||||
}
|
||||
|
||||
// Make sure that we've actually unwrapped the rcvr down
|
||||
|
|
@ -1830,7 +1830,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
|
||||
self.set_debug_loc(&mut bx, mir::SourceInfo::outermost(self.mir.span));
|
||||
|
||||
let (fn_abi, fn_ptr, instance) = common::build_langcall(&bx, None, reason.lang_item());
|
||||
let (fn_abi, fn_ptr, instance) =
|
||||
common::build_langcall(&bx, self.mir.span, reason.lang_item());
|
||||
if is_call_from_compiler_builtins_to_upstream_monomorphization(bx.tcx(), instance) {
|
||||
bx.abort();
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -8,9 +8,10 @@ use rustc_span::sym;
|
|||
use super::FunctionCx;
|
||||
use super::operand::OperandRef;
|
||||
use super::place::PlaceRef;
|
||||
use crate::common::{AtomicRmwBinOp, SynchronizationScope};
|
||||
use crate::errors::InvalidMonomorphization;
|
||||
use crate::traits::*;
|
||||
use crate::{MemFlags, errors, meth, size_of_val};
|
||||
use crate::{MemFlags, meth, size_of_val};
|
||||
|
||||
fn copy_intrinsic<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
||||
bx: &mut Bx,
|
||||
|
|
@ -62,7 +63,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
let span = source_info.span;
|
||||
|
||||
let name = bx.tcx().item_name(instance.def_id());
|
||||
let name_str = name.as_str();
|
||||
let fn_args = instance.args;
|
||||
|
||||
// If we're swapping something that's *not* an `OperandValue::Ref`,
|
||||
|
|
@ -89,14 +89,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
}
|
||||
}
|
||||
|
||||
let ret_llval = |bx: &mut Bx, llval| {
|
||||
if result.layout.ty.is_bool() {
|
||||
let val = bx.from_immediate(llval);
|
||||
bx.store_to_place(val, result.val);
|
||||
} else if !result.layout.ty.is_unit() {
|
||||
bx.store_to_place(llval, result.val);
|
||||
}
|
||||
Ok(())
|
||||
let invalid_monomorphization_int_type = |ty| {
|
||||
bx.tcx().dcx().emit_err(InvalidMonomorphization::BasicIntegerType { span, name, ty });
|
||||
};
|
||||
|
||||
let parse_atomic_ordering = |ord: ty::Value<'tcx>| {
|
||||
let discr = ord.valtree.unwrap_branch()[0].unwrap_leaf();
|
||||
discr.to_atomic_ordering()
|
||||
};
|
||||
|
||||
let llval = match name {
|
||||
|
|
@ -336,183 +335,144 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
}
|
||||
}
|
||||
|
||||
// This requires that atomic intrinsics follow a specific naming pattern:
|
||||
// "atomic_<operation>[_<ordering>]"
|
||||
name if let Some(atomic) = name_str.strip_prefix("atomic_") => {
|
||||
use rustc_middle::ty::AtomicOrdering::*;
|
||||
|
||||
use crate::common::{AtomicRmwBinOp, SynchronizationScope};
|
||||
|
||||
let invalid_monomorphization = |ty| {
|
||||
bx.tcx().dcx().emit_err(InvalidMonomorphization::BasicIntegerType {
|
||||
span,
|
||||
name,
|
||||
ty,
|
||||
});
|
||||
};
|
||||
|
||||
let parse_const_generic_ordering = |ord: ty::Value<'tcx>| {
|
||||
let discr = ord.valtree.unwrap_branch()[0].unwrap_leaf();
|
||||
discr.to_atomic_ordering()
|
||||
};
|
||||
|
||||
// Some intrinsics have the ordering already converted to a const generic parameter, we handle those first.
|
||||
match name {
|
||||
sym::atomic_load => {
|
||||
let ty = fn_args.type_at(0);
|
||||
let ordering = fn_args.const_at(1).to_value();
|
||||
if !(int_type_width_signed(ty, bx.tcx()).is_some() || ty.is_raw_ptr()) {
|
||||
invalid_monomorphization(ty);
|
||||
return Ok(());
|
||||
}
|
||||
let layout = bx.layout_of(ty);
|
||||
let source = args[0].immediate();
|
||||
let llval = bx.atomic_load(
|
||||
bx.backend_type(layout),
|
||||
source,
|
||||
parse_const_generic_ordering(ordering),
|
||||
layout.size,
|
||||
);
|
||||
|
||||
return ret_llval(bx, llval);
|
||||
}
|
||||
|
||||
// The rest falls back to below.
|
||||
_ => {}
|
||||
sym::atomic_load => {
|
||||
let ty = fn_args.type_at(0);
|
||||
if !(int_type_width_signed(ty, bx.tcx()).is_some() || ty.is_raw_ptr()) {
|
||||
invalid_monomorphization_int_type(ty);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let Some((instruction, ordering)) = atomic.split_once('_') else {
|
||||
bx.sess().dcx().emit_fatal(errors::MissingMemoryOrdering);
|
||||
};
|
||||
|
||||
let parse_ordering = |bx: &Bx, s| match s {
|
||||
"relaxed" => Relaxed,
|
||||
"acquire" => Acquire,
|
||||
"release" => Release,
|
||||
"acqrel" => AcqRel,
|
||||
"seqcst" => SeqCst,
|
||||
_ => bx.sess().dcx().emit_fatal(errors::UnknownAtomicOrdering),
|
||||
};
|
||||
|
||||
match instruction {
|
||||
"cxchg" | "cxchgweak" => {
|
||||
let Some((success, failure)) = ordering.split_once('_') else {
|
||||
bx.sess().dcx().emit_fatal(errors::AtomicCompareExchange);
|
||||
};
|
||||
let ty = fn_args.type_at(0);
|
||||
if int_type_width_signed(ty, bx.tcx()).is_some() || ty.is_raw_ptr() {
|
||||
let weak = instruction == "cxchgweak";
|
||||
let dst = args[0].immediate();
|
||||
let cmp = args[1].immediate();
|
||||
let src = args[2].immediate();
|
||||
let (val, success) = bx.atomic_cmpxchg(
|
||||
dst,
|
||||
cmp,
|
||||
src,
|
||||
parse_ordering(bx, success),
|
||||
parse_ordering(bx, failure),
|
||||
weak,
|
||||
);
|
||||
let val = bx.from_immediate(val);
|
||||
let success = bx.from_immediate(success);
|
||||
|
||||
let dest = result.project_field(bx, 0);
|
||||
bx.store_to_place(val, dest.val);
|
||||
let dest = result.project_field(bx, 1);
|
||||
bx.store_to_place(success, dest.val);
|
||||
} else {
|
||||
invalid_monomorphization(ty);
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
"store" => {
|
||||
let ty = fn_args.type_at(0);
|
||||
if int_type_width_signed(ty, bx.tcx()).is_some() || ty.is_raw_ptr() {
|
||||
let size = bx.layout_of(ty).size;
|
||||
let val = args[1].immediate();
|
||||
let ptr = args[0].immediate();
|
||||
bx.atomic_store(val, ptr, parse_ordering(bx, ordering), size);
|
||||
} else {
|
||||
invalid_monomorphization(ty);
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
"fence" => {
|
||||
bx.atomic_fence(
|
||||
parse_ordering(bx, ordering),
|
||||
SynchronizationScope::CrossThread,
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
"singlethreadfence" => {
|
||||
bx.atomic_fence(
|
||||
parse_ordering(bx, ordering),
|
||||
SynchronizationScope::SingleThread,
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// These are all AtomicRMW ops
|
||||
"max" | "min" => {
|
||||
let atom_op = if instruction == "max" {
|
||||
AtomicRmwBinOp::AtomicMax
|
||||
} else {
|
||||
AtomicRmwBinOp::AtomicMin
|
||||
};
|
||||
|
||||
let ty = fn_args.type_at(0);
|
||||
if matches!(ty.kind(), ty::Int(_)) {
|
||||
let ptr = args[0].immediate();
|
||||
let val = args[1].immediate();
|
||||
bx.atomic_rmw(atom_op, ptr, val, parse_ordering(bx, ordering))
|
||||
} else {
|
||||
invalid_monomorphization(ty);
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
"umax" | "umin" => {
|
||||
let atom_op = if instruction == "umax" {
|
||||
AtomicRmwBinOp::AtomicUMax
|
||||
} else {
|
||||
AtomicRmwBinOp::AtomicUMin
|
||||
};
|
||||
|
||||
let ty = fn_args.type_at(0);
|
||||
if matches!(ty.kind(), ty::Uint(_)) {
|
||||
let ptr = args[0].immediate();
|
||||
let val = args[1].immediate();
|
||||
bx.atomic_rmw(atom_op, ptr, val, parse_ordering(bx, ordering))
|
||||
} else {
|
||||
invalid_monomorphization(ty);
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
op => {
|
||||
let atom_op = match op {
|
||||
"xchg" => AtomicRmwBinOp::AtomicXchg,
|
||||
"xadd" => AtomicRmwBinOp::AtomicAdd,
|
||||
"xsub" => AtomicRmwBinOp::AtomicSub,
|
||||
"and" => AtomicRmwBinOp::AtomicAnd,
|
||||
"nand" => AtomicRmwBinOp::AtomicNand,
|
||||
"or" => AtomicRmwBinOp::AtomicOr,
|
||||
"xor" => AtomicRmwBinOp::AtomicXor,
|
||||
_ => bx.sess().dcx().emit_fatal(errors::UnknownAtomicOperation),
|
||||
};
|
||||
|
||||
let ty = fn_args.type_at(0);
|
||||
if int_type_width_signed(ty, bx.tcx()).is_some() || ty.is_raw_ptr() {
|
||||
let ptr = args[0].immediate();
|
||||
let val = args[1].immediate();
|
||||
bx.atomic_rmw(atom_op, ptr, val, parse_ordering(bx, ordering))
|
||||
} else {
|
||||
invalid_monomorphization(ty);
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
let ordering = fn_args.const_at(1).to_value();
|
||||
let layout = bx.layout_of(ty);
|
||||
let source = args[0].immediate();
|
||||
bx.atomic_load(
|
||||
bx.backend_type(layout),
|
||||
source,
|
||||
parse_atomic_ordering(ordering),
|
||||
layout.size,
|
||||
)
|
||||
}
|
||||
sym::atomic_store => {
|
||||
let ty = fn_args.type_at(0);
|
||||
if !(int_type_width_signed(ty, bx.tcx()).is_some() || ty.is_raw_ptr()) {
|
||||
invalid_monomorphization_int_type(ty);
|
||||
return Ok(());
|
||||
}
|
||||
let ordering = fn_args.const_at(1).to_value();
|
||||
let size = bx.layout_of(ty).size;
|
||||
let val = args[1].immediate();
|
||||
let ptr = args[0].immediate();
|
||||
bx.atomic_store(val, ptr, parse_atomic_ordering(ordering), size);
|
||||
return Ok(());
|
||||
}
|
||||
sym::atomic_cxchg | sym::atomic_cxchgweak => {
|
||||
let ty = fn_args.type_at(0);
|
||||
if !(int_type_width_signed(ty, bx.tcx()).is_some() || ty.is_raw_ptr()) {
|
||||
invalid_monomorphization_int_type(ty);
|
||||
return Ok(());
|
||||
}
|
||||
let succ_ordering = fn_args.const_at(1).to_value();
|
||||
let fail_ordering = fn_args.const_at(2).to_value();
|
||||
let weak = name == sym::atomic_cxchgweak;
|
||||
let dst = args[0].immediate();
|
||||
let cmp = args[1].immediate();
|
||||
let src = args[2].immediate();
|
||||
let (val, success) = bx.atomic_cmpxchg(
|
||||
dst,
|
||||
cmp,
|
||||
src,
|
||||
parse_atomic_ordering(succ_ordering),
|
||||
parse_atomic_ordering(fail_ordering),
|
||||
weak,
|
||||
);
|
||||
let val = bx.from_immediate(val);
|
||||
let success = bx.from_immediate(success);
|
||||
|
||||
let dest = result.project_field(bx, 0);
|
||||
bx.store_to_place(val, dest.val);
|
||||
let dest = result.project_field(bx, 1);
|
||||
bx.store_to_place(success, dest.val);
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
// These are all AtomicRMW ops
|
||||
sym::atomic_max | sym::atomic_min => {
|
||||
let atom_op = if name == sym::atomic_max {
|
||||
AtomicRmwBinOp::AtomicMax
|
||||
} else {
|
||||
AtomicRmwBinOp::AtomicMin
|
||||
};
|
||||
|
||||
let ty = fn_args.type_at(0);
|
||||
if matches!(ty.kind(), ty::Int(_)) {
|
||||
let ordering = fn_args.const_at(1).to_value();
|
||||
let ptr = args[0].immediate();
|
||||
let val = args[1].immediate();
|
||||
bx.atomic_rmw(atom_op, ptr, val, parse_atomic_ordering(ordering))
|
||||
} else {
|
||||
invalid_monomorphization_int_type(ty);
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
sym::atomic_umax | sym::atomic_umin => {
|
||||
let atom_op = if name == sym::atomic_umax {
|
||||
AtomicRmwBinOp::AtomicUMax
|
||||
} else {
|
||||
AtomicRmwBinOp::AtomicUMin
|
||||
};
|
||||
|
||||
let ty = fn_args.type_at(0);
|
||||
if matches!(ty.kind(), ty::Uint(_)) {
|
||||
let ordering = fn_args.const_at(1).to_value();
|
||||
let ptr = args[0].immediate();
|
||||
let val = args[1].immediate();
|
||||
bx.atomic_rmw(atom_op, ptr, val, parse_atomic_ordering(ordering))
|
||||
} else {
|
||||
invalid_monomorphization_int_type(ty);
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
sym::atomic_xchg
|
||||
| sym::atomic_xadd
|
||||
| sym::atomic_xsub
|
||||
| sym::atomic_and
|
||||
| sym::atomic_nand
|
||||
| sym::atomic_or
|
||||
| sym::atomic_xor => {
|
||||
let atom_op = match name {
|
||||
sym::atomic_xchg => AtomicRmwBinOp::AtomicXchg,
|
||||
sym::atomic_xadd => AtomicRmwBinOp::AtomicAdd,
|
||||
sym::atomic_xsub => AtomicRmwBinOp::AtomicSub,
|
||||
sym::atomic_and => AtomicRmwBinOp::AtomicAnd,
|
||||
sym::atomic_nand => AtomicRmwBinOp::AtomicNand,
|
||||
sym::atomic_or => AtomicRmwBinOp::AtomicOr,
|
||||
sym::atomic_xor => AtomicRmwBinOp::AtomicXor,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let ty = fn_args.type_at(0);
|
||||
if int_type_width_signed(ty, bx.tcx()).is_some() || ty.is_raw_ptr() {
|
||||
let ordering = fn_args.const_at(1).to_value();
|
||||
let ptr = args[0].immediate();
|
||||
let val = args[1].immediate();
|
||||
bx.atomic_rmw(atom_op, ptr, val, parse_atomic_ordering(ordering))
|
||||
} else {
|
||||
invalid_monomorphization_int_type(ty);
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
sym::atomic_fence => {
|
||||
let ordering = fn_args.const_at(0).to_value();
|
||||
bx.atomic_fence(parse_atomic_ordering(ordering), SynchronizationScope::CrossThread);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
sym::atomic_singlethreadfence => {
|
||||
let ordering = fn_args.const_at(0).to_value();
|
||||
bx.atomic_fence(
|
||||
parse_atomic_ordering(ordering),
|
||||
SynchronizationScope::SingleThread,
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
sym::nontemporal_store => {
|
||||
|
|
@ -556,7 +516,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
}
|
||||
};
|
||||
|
||||
ret_llval(bx, llval)
|
||||
if result.layout.ty.is_bool() {
|
||||
let val = bx.from_immediate(llval);
|
||||
bx.store_to_place(val, result.val);
|
||||
} else if !result.layout.ty.is_unit() {
|
||||
bx.store_to_place(llval, result.val);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -45,9 +45,15 @@ pub enum OperandValue<V> {
|
|||
Immediate(V),
|
||||
/// A pair of immediate LLVM values. Used by wide pointers too.
|
||||
///
|
||||
/// An `OperandValue` *must* be this variant for any type for which
|
||||
/// # Invariants
|
||||
/// - For `Pair(a, b)`, `a` is always at offset 0, but may have `FieldIdx(1..)`
|
||||
/// - `b` is not at offset 0, because `V` is not a 1ZST type.
|
||||
/// - `a` and `b` will have a different FieldIdx, but otherwise `b`'s may be lower
|
||||
/// or they may not be adjacent, due to arbitrary numbers of 1ZST fields that
|
||||
/// will not affect the shape of the data which determines if `Pair` will be used.
|
||||
/// - An `OperandValue` *must* be this variant for any type for which
|
||||
/// [`LayoutTypeCodegenMethods::is_backend_scalar_pair`] returns `true`.
|
||||
/// The backend values in this variant must be the *immediate* backend types,
|
||||
/// - The backend values in this variant must be the *immediate* backend types,
|
||||
/// as returned by [`LayoutTypeCodegenMethods::scalar_pair_element_backend_type`]
|
||||
/// with `immediate: true`.
|
||||
Pair(V, V),
|
||||
|
|
@ -462,10 +468,10 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
|
|||
let tag_op = match self.val {
|
||||
OperandValue::ZeroSized => bug!(),
|
||||
OperandValue::Immediate(_) | OperandValue::Pair(_, _) => {
|
||||
self.extract_field(fx, bx, tag_field)
|
||||
self.extract_field(fx, bx, tag_field.as_usize())
|
||||
}
|
||||
OperandValue::Ref(place) => {
|
||||
let tag = place.with_type(self.layout).project_field(bx, tag_field);
|
||||
let tag = place.with_type(self.layout).project_field(bx, tag_field.as_usize());
|
||||
bx.load_operand(tag)
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -250,7 +250,7 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
|
|||
Variants::Single { index } => assert_eq!(index, variant_index),
|
||||
|
||||
Variants::Multiple { tag_encoding: TagEncoding::Direct, tag_field, .. } => {
|
||||
let ptr = self.project_field(bx, tag_field);
|
||||
let ptr = self.project_field(bx, tag_field.as_usize());
|
||||
let to =
|
||||
self.layout.ty.discriminant_for_variant(bx.tcx(), variant_index).unwrap().val;
|
||||
bx.store_to_place(
|
||||
|
|
@ -265,7 +265,7 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
|
|||
..
|
||||
} => {
|
||||
if variant_index != untagged_variant {
|
||||
let niche = self.project_field(bx, tag_field);
|
||||
let niche = self.project_field(bx, tag_field.as_usize());
|
||||
let niche_llty = bx.cx().immediate_backend_type(niche.layout);
|
||||
let BackendRepr::Scalar(scalar) = niche.layout.backend_repr else {
|
||||
bug!("expected a scalar placeref for the niche");
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ use rustc_hir::LangItem;
|
|||
use rustc_middle::bug;
|
||||
use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths};
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_span::DUMMY_SP;
|
||||
use tracing::{debug, trace};
|
||||
|
||||
use crate::common::IntPredicate;
|
||||
|
|
@ -62,7 +63,7 @@ pub fn size_and_align_of_dst<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
|||
|
||||
// Obtain the panic entry point.
|
||||
let (fn_abi, llfn, _instance) =
|
||||
common::build_langcall(bx, None, LangItem::PanicNounwind);
|
||||
common::build_langcall(bx, DUMMY_SP, LangItem::PanicNounwind);
|
||||
|
||||
// Generate the call. Cannot use `do_call` since we don't have a MIR terminator so we
|
||||
// can't create a `TerminationCodegenHelper`. (But we are in good company, this code is
|
||||
|
|
|
|||
|
|
@ -81,7 +81,6 @@ pub trait DebugInfoBuilderMethods: BackendTypes {
|
|||
);
|
||||
fn set_dbg_loc(&mut self, dbg_loc: Self::DILocation);
|
||||
fn clear_dbg_loc(&mut self);
|
||||
fn get_dbg_loc(&self) -> Option<Self::DILocation>;
|
||||
fn insert_reference_to_gdb_debug_scripts_section_global(&mut self);
|
||||
fn set_var_name(&mut self, value: Self::Value, name: &str);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,8 +22,6 @@ pub trait IntrinsicCallBuilderMethods<'tcx>: BackendTypes {
|
|||
fn abort(&mut self);
|
||||
fn assume(&mut self, val: Self::Value);
|
||||
fn expect(&mut self, cond: Self::Value, expected: bool) -> Self::Value;
|
||||
/// Trait method used to test whether a given pointer is associated with a type identifier.
|
||||
fn type_test(&mut self, pointer: Self::Value, typeid: Self::Metadata) -> Self::Value;
|
||||
/// Trait method used to load a function while testing if it is associated with a type
|
||||
/// identifier.
|
||||
fn type_checked_load(
|
||||
|
|
|
|||
|
|
@ -88,11 +88,9 @@ const_eval_division_overflow =
|
|||
const_eval_dyn_call_not_a_method =
|
||||
`dyn` call trying to call something that is not a method
|
||||
|
||||
const_eval_error = {$error_kind ->
|
||||
[static] could not evaluate static initializer
|
||||
[const] evaluation of constant value failed
|
||||
[const_with_path] evaluation of `{$instance}` failed
|
||||
*[other] {""}
|
||||
const_eval_error = evaluation of `{$instance}` failed {$num_frames ->
|
||||
[0] here
|
||||
*[other] inside this call
|
||||
}
|
||||
|
||||
const_eval_exact_div_has_remainder =
|
||||
|
|
@ -118,7 +116,7 @@ const_eval_frame_note_inner = inside {$where_ ->
|
|||
const_eval_frame_note_last = the failure occurred here
|
||||
|
||||
const_eval_incompatible_calling_conventions =
|
||||
calling a function with calling convention {$callee_conv} using calling convention {$caller_conv}
|
||||
calling a function with calling convention "{$callee_conv}" using calling convention "{$caller_conv}"
|
||||
|
||||
const_eval_incompatible_return_types =
|
||||
calling a function with return type {$callee_ty} passing return place of type {$caller_ty}
|
||||
|
|
|
|||
|
|
@ -589,12 +589,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
|
|||
|
||||
Rvalue::Aggregate(kind, ..) => {
|
||||
if let AggregateKind::Coroutine(def_id, ..) = kind.as_ref()
|
||||
&& let Some(
|
||||
coroutine_kind @ hir::CoroutineKind::Desugared(
|
||||
hir::CoroutineDesugaring::Async,
|
||||
_,
|
||||
),
|
||||
) = self.tcx.coroutine_kind(def_id)
|
||||
&& let Some(coroutine_kind) = self.tcx.coroutine_kind(def_id)
|
||||
{
|
||||
self.check_op(ops::Coroutine(coroutine_kind));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -345,11 +345,7 @@ fn build_error_for_const_call<'tcx>(
|
|||
non_or_conditionally,
|
||||
});
|
||||
|
||||
note_trait_if_possible(
|
||||
&mut err,
|
||||
self_ty,
|
||||
tcx.require_lang_item(LangItem::Deref, Some(span)),
|
||||
);
|
||||
note_trait_if_possible(&mut err, self_ty, tcx.require_lang_item(LangItem::Deref, span));
|
||||
err
|
||||
}
|
||||
_ if tcx.opt_parent(callee) == tcx.get_diagnostic_item(sym::FmtArgumentsNew) => {
|
||||
|
|
@ -486,24 +482,25 @@ impl<'tcx> NonConstOp<'tcx> for IntrinsicUnstable {
|
|||
pub(crate) struct Coroutine(pub hir::CoroutineKind);
|
||||
impl<'tcx> NonConstOp<'tcx> for Coroutine {
|
||||
fn status_in_item(&self, _: &ConstCx<'_, 'tcx>) -> Status {
|
||||
if let hir::CoroutineKind::Desugared(
|
||||
hir::CoroutineDesugaring::Async,
|
||||
hir::CoroutineSource::Block,
|
||||
) = self.0
|
||||
{
|
||||
Status::Unstable {
|
||||
match self.0 {
|
||||
hir::CoroutineKind::Desugared(
|
||||
hir::CoroutineDesugaring::Async,
|
||||
hir::CoroutineSource::Block,
|
||||
)
|
||||
// FIXME(coroutines): eventually we want to gate const coroutine coroutines behind a
|
||||
// different feature.
|
||||
| hir::CoroutineKind::Coroutine(_) => Status::Unstable {
|
||||
gate: sym::const_async_blocks,
|
||||
gate_already_checked: false,
|
||||
safe_to_expose_on_stable: false,
|
||||
is_function_call: false,
|
||||
}
|
||||
} else {
|
||||
Status::Forbidden
|
||||
},
|
||||
_ => Status::Forbidden,
|
||||
}
|
||||
}
|
||||
|
||||
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
|
||||
let msg = format!("{:#}s are not allowed in {}s", self.0, ccx.const_kind());
|
||||
let msg = format!("{} are not allowed in {}s", self.0.to_plural_string(), ccx.const_kind());
|
||||
if let Status::Unstable { gate, .. } = self.status_in_item(ccx) {
|
||||
ccx.tcx.sess.create_feature_err(errors::UnallowedOpInConstContext { span, msg }, gate)
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ impl Qualif for HasMutInterior {
|
|||
// requires borrowck, which in turn will invoke mir_const_qualifs again, causing a cycle error.
|
||||
// Instead we invoke an obligation context manually, and provide the opaque type inference settings
|
||||
// that allow the trait solver to just error out instead of cycling.
|
||||
let freeze_def_id = cx.tcx.require_lang_item(LangItem::Freeze, Some(cx.body.span));
|
||||
let freeze_def_id = cx.tcx.require_lang_item(LangItem::Freeze, cx.body.span);
|
||||
// FIXME(#132279): Once we've got a typing mode which reveals opaque types using the HIR
|
||||
// typeck results without causing query cycles, we should use this here instead of defining
|
||||
// opaque types.
|
||||
|
|
@ -180,7 +180,7 @@ impl Qualif for NeedsNonConstDrop {
|
|||
// that the components of this type are also `~const Destruct`. This
|
||||
// amounts to verifying that there are no values in this ADT that may have
|
||||
// a non-const drop.
|
||||
let destruct_def_id = cx.tcx.require_lang_item(LangItem::Destruct, Some(cx.body.span));
|
||||
let destruct_def_id = cx.tcx.require_lang_item(LangItem::Destruct, cx.body.span);
|
||||
let (infcx, param_env) = cx.tcx.infer_ctxt().build_with_typing_env(cx.typing_env);
|
||||
let ocx = ObligationCtxt::new(&infcx);
|
||||
ocx.register_obligation(Obligation::new(
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use std::mem;
|
||||
|
||||
use rustc_errors::{DiagArgName, DiagArgValue, DiagMessage, Diagnostic, IntoDiagArg};
|
||||
use rustc_errors::{Diag, DiagArgName, DiagArgValue, DiagMessage, IntoDiagArg};
|
||||
use rustc_middle::mir::AssertKind;
|
||||
use rustc_middle::mir::interpret::{Provenance, ReportedErrorInfo};
|
||||
use rustc_middle::query::TyCtxtAt;
|
||||
|
|
@ -131,10 +131,10 @@ pub fn get_span_and_frames<'tcx>(
|
|||
|
||||
/// Create a diagnostic for a const eval error.
|
||||
///
|
||||
/// This will use the `mk` function for creating the error which will get passed labels according to
|
||||
/// the `InterpError` and the span and a stacktrace of current execution according to
|
||||
/// `get_span_and_frames`.
|
||||
pub(super) fn report<'tcx, C, F, E>(
|
||||
/// This will use the `mk` function for adding more information to the error.
|
||||
/// You can use it to add a stacktrace of current execution according to
|
||||
/// `get_span_and_frames` or just give context on where the const eval error happened.
|
||||
pub(super) fn report<'tcx, C, F>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
error: InterpErrorKind<'tcx>,
|
||||
span: Span,
|
||||
|
|
@ -143,8 +143,7 @@ pub(super) fn report<'tcx, C, F, E>(
|
|||
) -> ErrorHandled
|
||||
where
|
||||
C: FnOnce() -> (Span, Vec<FrameNote>),
|
||||
F: FnOnce(Span, Vec<FrameNote>) -> E,
|
||||
E: Diagnostic<'tcx>,
|
||||
F: FnOnce(&mut Diag<'_>, Span, Vec<FrameNote>),
|
||||
{
|
||||
// Special handling for certain errors
|
||||
match error {
|
||||
|
|
@ -163,8 +162,7 @@ where
|
|||
_ => {
|
||||
let (our_span, frames) = get_span_and_frames();
|
||||
let span = span.substitute_dummy(our_span);
|
||||
let err = mk(span, frames);
|
||||
let mut err = tcx.dcx().create_err(err);
|
||||
let mut err = tcx.dcx().struct_span_err(our_span, error.diagnostic_message());
|
||||
// We allow invalid programs in infallible promoteds since invalid layouts can occur
|
||||
// anyway (e.g. due to size overflow). And we allow OOM as that can happen any time.
|
||||
let allowed_in_infallible = matches!(
|
||||
|
|
@ -172,11 +170,9 @@ where
|
|||
InterpErrorKind::ResourceExhaustion(_) | InterpErrorKind::InvalidProgram(_)
|
||||
);
|
||||
|
||||
let msg = error.diagnostic_message();
|
||||
error.add_args(&mut err);
|
||||
|
||||
// Use *our* span to label the interp error
|
||||
err.span_label(our_span, msg);
|
||||
mk(&mut err, span, frames);
|
||||
let g = err.emit();
|
||||
let reported = if allowed_in_infallible {
|
||||
ReportedErrorInfo::allowed_in_infallible(g)
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ use std::sync::atomic::Ordering::Relaxed;
|
|||
|
||||
use either::{Left, Right};
|
||||
use rustc_abi::{self as abi, BackendRepr};
|
||||
use rustc_errors::E0080;
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_middle::mir::interpret::{AllocId, ErrorHandled, InterpErrorInfo, ReportedErrorInfo};
|
||||
use rustc_middle::mir::{self, ConstAlloc, ConstValue};
|
||||
|
|
@ -290,12 +291,18 @@ pub fn eval_to_const_value_raw_provider<'tcx>(
|
|||
|error| {
|
||||
let span = tcx.def_span(def_id);
|
||||
|
||||
// FIXME(oli-obk): why don't we have any tests for this code path?
|
||||
super::report(
|
||||
tcx,
|
||||
error.into_kind(),
|
||||
span,
|
||||
|| (span, vec![]),
|
||||
|span, _| errors::NullaryIntrinsicError { span },
|
||||
|diag, span, _| {
|
||||
diag.span_label(
|
||||
span,
|
||||
crate::fluent_generated::const_eval_nullary_intrinsic_fail,
|
||||
);
|
||||
},
|
||||
)
|
||||
},
|
||||
);
|
||||
|
|
@ -423,31 +430,24 @@ fn report_eval_error<'tcx>(
|
|||
let (error, backtrace) = error.into_parts();
|
||||
backtrace.print_backtrace();
|
||||
|
||||
let (kind, instance) = if ecx.tcx.is_static(cid.instance.def_id()) {
|
||||
("static", String::new())
|
||||
} else {
|
||||
// If the current item has generics, we'd like to enrich the message with the
|
||||
// instance and its args: to show the actual compile-time values, in addition to
|
||||
// the expression, leading to the const eval error.
|
||||
let instance = &cid.instance;
|
||||
if !instance.args.is_empty() {
|
||||
let instance = with_no_trimmed_paths!(instance.to_string());
|
||||
("const_with_path", instance)
|
||||
} else {
|
||||
("const", String::new())
|
||||
}
|
||||
};
|
||||
let instance = with_no_trimmed_paths!(cid.instance.to_string());
|
||||
|
||||
super::report(
|
||||
*ecx.tcx,
|
||||
error,
|
||||
DUMMY_SP,
|
||||
|| super::get_span_and_frames(ecx.tcx, ecx.stack()),
|
||||
|span, frames| errors::ConstEvalError {
|
||||
span,
|
||||
error_kind: kind,
|
||||
instance,
|
||||
frame_notes: frames,
|
||||
|diag, span, frames| {
|
||||
let num_frames = frames.len();
|
||||
// FIXME(oli-obk): figure out how to use structured diagnostics again.
|
||||
diag.code(E0080);
|
||||
diag.span_label(span, crate::fluent_generated::const_eval_error);
|
||||
for frame in frames {
|
||||
diag.subdiagnostic(frame);
|
||||
}
|
||||
// Add after the frame rendering above, as it adds its own `instance` args.
|
||||
diag.arg("instance", instance);
|
||||
diag.arg("num_frames", num_frames);
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
@ -477,6 +477,15 @@ fn report_validation_error<'tcx>(
|
|||
error,
|
||||
DUMMY_SP,
|
||||
|| crate::const_eval::get_span_and_frames(ecx.tcx, ecx.stack()),
|
||||
move |span, frames| errors::ValidationFailure { span, ub_note: (), frames, raw_bytes },
|
||||
move |diag, span, frames| {
|
||||
// FIXME(oli-obk): figure out how to use structured diagnostics again.
|
||||
diag.code(E0080);
|
||||
diag.span_label(span, crate::fluent_generated::const_eval_validation_failure);
|
||||
diag.note(crate::fluent_generated::const_eval_validation_failure_note);
|
||||
for frame in frames {
|
||||
diag.subdiagnostic(frame);
|
||||
}
|
||||
diag.subdiagnostic(raw_bytes);
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -249,7 +249,7 @@ impl<'tcx> CompileTimeInterpCx<'tcx> {
|
|||
return Err(ConstEvalErrKind::Panic { msg, file, line, col }).into();
|
||||
} else if self.tcx.is_lang_item(def_id, LangItem::PanicFmt) {
|
||||
// For panic_fmt, call const_panic_fmt instead.
|
||||
let const_def_id = self.tcx.require_lang_item(LangItem::ConstPanicFmt, None);
|
||||
let const_def_id = self.tcx.require_lang_item(LangItem::ConstPanicFmt, self.tcx.span);
|
||||
let new_instance = ty::Instance::expect_resolve(
|
||||
*self.tcx,
|
||||
self.typing_env(),
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// Not in interpret to make sure we do not use private implementation details
|
||||
|
||||
use rustc_abi::VariantIdx;
|
||||
use rustc_abi::{FieldIdx, VariantIdx};
|
||||
use rustc_middle::query::Key;
|
||||
use rustc_middle::ty::layout::LayoutOf;
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||
|
|
@ -60,7 +60,7 @@ pub(crate) fn try_destructure_mir_constant_for_user_output<'tcx>(
|
|||
|
||||
let fields_iter = (0..field_count)
|
||||
.map(|i| {
|
||||
let field_op = ecx.project_field(&down, i).discard_err()?;
|
||||
let field_op = ecx.project_field(&down, FieldIdx::from_usize(i)).discard_err()?;
|
||||
let val = op_to_const(&ecx, &field_op, /* for diagnostics */ true);
|
||||
Some((val, field_op.layout.ty))
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use rustc_abi::{BackendRepr, VariantIdx};
|
||||
use rustc_abi::{BackendRepr, FieldIdx, VariantIdx};
|
||||
use rustc_data_structures::stack::ensure_sufficient_stack;
|
||||
use rustc_middle::mir::interpret::{EvalToValTreeResult, GlobalId, ReportedErrorInfo};
|
||||
use rustc_middle::ty::layout::{LayoutCx, LayoutOf, TyAndLayout};
|
||||
|
|
@ -40,7 +40,7 @@ fn branches<'tcx>(
|
|||
}
|
||||
|
||||
for i in 0..field_count {
|
||||
let field = ecx.project_field(&place, i).unwrap();
|
||||
let field = ecx.project_field(&place, FieldIdx::from_usize(i)).unwrap();
|
||||
let valtree = const_to_valtree_inner(ecx, &field, num_nodes)?;
|
||||
branches.push(valtree);
|
||||
}
|
||||
|
|
@ -437,7 +437,7 @@ fn valtree_into_mplace<'tcx>(
|
|||
ty::Str | ty::Slice(_) | ty::Array(..) => {
|
||||
ecx.project_index(place, i as u64).unwrap()
|
||||
}
|
||||
_ => ecx.project_field(&place_adjusted, i).unwrap(),
|
||||
_ => ecx.project_field(&place_adjusted, FieldIdx::from_usize(i)).unwrap(),
|
||||
};
|
||||
|
||||
debug!(?place_inner);
|
||||
|
|
|
|||
|
|
@ -439,38 +439,6 @@ pub struct LiveDrop<'tcx> {
|
|||
pub dropped_at: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(const_eval_error, code = E0080)]
|
||||
pub struct ConstEvalError {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
/// One of "const", "const_with_path", and "static"
|
||||
pub error_kind: &'static str,
|
||||
pub instance: String,
|
||||
#[subdiagnostic]
|
||||
pub frame_notes: Vec<FrameNote>,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(const_eval_nullary_intrinsic_fail)]
|
||||
pub struct NullaryIntrinsicError {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(const_eval_validation_failure, code = E0080)]
|
||||
pub struct ValidationFailure {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
#[note(const_eval_validation_failure_note)]
|
||||
pub ub_note: (),
|
||||
#[subdiagnostic]
|
||||
pub frames: Vec<FrameNote>,
|
||||
#[subdiagnostic]
|
||||
pub raw_bytes: RawBytesNote,
|
||||
}
|
||||
|
||||
pub trait ReportErrorExt {
|
||||
/// Returns the diagnostic message for this error.
|
||||
fn diagnostic_message(&self) -> DiagMessage;
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
pub(super) fn fn_arg_field(
|
||||
&self,
|
||||
arg: &FnArg<'tcx, M::Provenance>,
|
||||
field: usize,
|
||||
field: FieldIdx,
|
||||
) -> InterpResult<'tcx, FnArg<'tcx, M::Provenance>> {
|
||||
interp_ok(match arg {
|
||||
FnArg::Copy(op) => FnArg::Copy(self.project_field(op, field)?),
|
||||
|
|
@ -600,10 +600,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
Cow::from(
|
||||
args.iter()
|
||||
.map(|a| interp_ok(a.clone()))
|
||||
.chain(
|
||||
(0..untuple_arg.layout().fields.count())
|
||||
.map(|i| self.fn_arg_field(untuple_arg, i)),
|
||||
)
|
||||
.chain((0..untuple_arg.layout().fields.count()).map(|i| {
|
||||
self.fn_arg_field(untuple_arg, FieldIdx::from_usize(i))
|
||||
}))
|
||||
.collect::<InterpResult<'_, Vec<_>>>()?,
|
||||
)
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use std::assert_matches::assert_matches;
|
||||
|
||||
use rustc_abi::Integer;
|
||||
use rustc_abi::{FieldIdx, Integer};
|
||||
use rustc_apfloat::ieee::{Double, Half, Quad, Single};
|
||||
use rustc_apfloat::{Float, FloatConvert};
|
||||
use rustc_middle::mir::CastKind;
|
||||
|
|
@ -484,6 +484,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
let mut found_cast_field = false;
|
||||
for i in 0..src.layout.fields.count() {
|
||||
let cast_ty_field = cast_ty.field(self, i);
|
||||
let i = FieldIdx::from_usize(i);
|
||||
let src_field = self.project_field(src, i)?;
|
||||
let dst_field = self.project_field(dest, i)?;
|
||||
if src_field.layout.is_1zst() && cast_ty_field.is_1zst() {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
//! Functions for reading and writing discriminants of multi-variant layouts (enums and coroutines).
|
||||
|
||||
use rustc_abi::{self as abi, TagEncoding, VariantIdx, Variants};
|
||||
use rustc_abi::{self as abi, FieldIdx, TagEncoding, VariantIdx, Variants};
|
||||
use rustc_middle::ty::layout::{LayoutOf, PrimitiveExt, TyAndLayout};
|
||||
use rustc_middle::ty::{self, CoroutineArgsExt, ScalarInt, Ty};
|
||||
use rustc_middle::{mir, span_bug};
|
||||
|
|
@ -231,7 +231,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
&self,
|
||||
layout: TyAndLayout<'tcx>,
|
||||
variant_index: VariantIdx,
|
||||
) -> InterpResult<'tcx, Option<(ScalarInt, usize)>> {
|
||||
) -> InterpResult<'tcx, Option<(ScalarInt, FieldIdx)>> {
|
||||
// Layout computation excludes uninhabited variants from consideration.
|
||||
// Therefore, there's no way to represent those variants in the given layout.
|
||||
// Essentially, uninhabited variants do not have a tag that corresponds to their
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv, LayoutOf, TyAndLayout};
|
|||
use rustc_middle::ty::print::{FmtPrinter, PrettyPrinter};
|
||||
use rustc_middle::ty::{ConstInt, ScalarInt, Ty, TyCtxt};
|
||||
use rustc_middle::{bug, mir, span_bug, ty};
|
||||
use rustc_span::DUMMY_SP;
|
||||
use tracing::trace;
|
||||
|
||||
use super::{
|
||||
|
|
@ -307,7 +308,7 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> {
|
|||
#[inline]
|
||||
pub fn from_ordering(c: std::cmp::Ordering, tcx: TyCtxt<'tcx>) -> Self {
|
||||
// Can use any typing env, since `Ordering` is always monomorphic.
|
||||
let ty = tcx.ty_ordering_enum(None);
|
||||
let ty = tcx.ty_ordering_enum(DUMMY_SP);
|
||||
let layout =
|
||||
tcx.layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(ty)).unwrap();
|
||||
Self::from_scalar(Scalar::Int(c.into()), layout)
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue