Merge from rustc

This commit is contained in:
The rustc-dev-guide Cronjob Bot 2025-06-09 04:09:17 +00:00
commit 7565e75591
1754 changed files with 72486 additions and 16304 deletions

View file

@ -4673,6 +4673,7 @@ dependencies = [
"bincode",
"rustc-hash 2.1.1",
"serde",
"serde_derive",
"serde_json",
]

View file

@ -4,7 +4,6 @@ members = [
# tidy-alphabetical-start
"compiler/rustc",
"src/build_helper",
"src/etc/test-float-parse",
"src/rustc-std-workspace/rustc-std-workspace-alloc",
"src/rustc-std-workspace/rustc-std-workspace-core",
"src/rustc-std-workspace/rustc-std-workspace-std",
@ -41,6 +40,7 @@ members = [
"src/tools/rustdoc-themes",
"src/tools/rustfmt",
"src/tools/suggest-tests",
"src/tools/test-float-parse",
"src/tools/tidy",
"src/tools/tier-check",
"src/tools/unicode-table-generator",

View file

@ -0,0 +1,129 @@
use std::fmt;
#[cfg(feature = "nightly")]
use rustc_macros::HashStable_Generic;
use crate::ExternAbi;
/// Calling convention to determine codegen
///
/// CanonAbi erases certain distinctions ExternAbi preserves, but remains target-dependent.
/// There are still both target-specific variants and aliasing variants, though much fewer.
/// The reason for this step is the frontend may wish to show an ExternAbi but implement that ABI
/// using a different ABI than the string per se, or describe irrelevant differences, e.g.
/// - extern "system"
/// - extern "cdecl"
/// - extern "C-unwind"
/// In that sense, this erases mere syntactic distinctions to create a canonical *directive*,
/// rather than picking the "actual" ABI.
#[derive(Copy, Clone, Debug)]
#[derive(PartialOrd, Ord, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "nightly", derive(HashStable_Generic))]
pub enum CanonAbi {
// NOTE: the use of nested variants for some ABIs is for many targets they don't matter,
// and this pushes the complexity of their reasoning to target-specific code,
// allowing a `match` to easily exhaustively ignore these subcategories of variants.
// Otherwise it is very tempting to avoid matching exhaustively!
C,
Rust,
RustCold,
/// ABIs relevant to 32-bit Arm targets
Arm(ArmCall),
/// ABI relevant to GPUs: the entry point for a GPU kernel
GpuKernel,
/// ABIs relevant to bare-metal interrupt targets
// FIXME(workingjubilee): a particular reason for this nesting is we might not need these?
// interrupt ABIs should have the same properties:
// - uncallable by Rust calls, as LLVM rejects it in most cases
// - uses a preserve-all-registers *callee* convention
// - should always return `-> !` (effectively... it can't use normal `ret`)
// what differs between targets is
// - allowed arguments: x86 differs slightly, having 2-3 arguments which are handled magically
// - may need special prologues/epilogues for some interrupts, without affecting "call ABI"
Interrupt(InterruptKind),
/// ABIs relevant to Windows or x86 targets
X86(X86Call),
}
impl fmt::Display for CanonAbi {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// convert to the ExternAbi that *shares a string* with this CanonAbi.
// FIXME: ideally we'd avoid printing `CanonAbi`, and preserve `ExternAbi` everywhere
// that we need to generate error messages.
let erased_abi = match self {
CanonAbi::C => ExternAbi::C { unwind: false },
CanonAbi::Rust => ExternAbi::Rust,
CanonAbi::RustCold => ExternAbi::RustCold,
CanonAbi::Arm(arm_call) => match arm_call {
ArmCall::Aapcs => ExternAbi::Aapcs { unwind: false },
ArmCall::CCmseNonSecureCall => ExternAbi::CCmseNonSecureCall,
ArmCall::CCmseNonSecureEntry => ExternAbi::CCmseNonSecureEntry,
},
CanonAbi::GpuKernel => ExternAbi::GpuKernel,
CanonAbi::Interrupt(interrupt_kind) => match interrupt_kind {
InterruptKind::Avr => ExternAbi::AvrInterrupt,
InterruptKind::AvrNonBlocking => ExternAbi::AvrNonBlockingInterrupt,
InterruptKind::Msp430 => ExternAbi::Msp430Interrupt,
InterruptKind::RiscvMachine => ExternAbi::RiscvInterruptM,
InterruptKind::RiscvSupervisor => ExternAbi::RiscvInterruptS,
InterruptKind::X86 => ExternAbi::X86Interrupt,
},
CanonAbi::X86(x86_call) => match x86_call {
X86Call::Fastcall => ExternAbi::Fastcall { unwind: false },
X86Call::Stdcall => ExternAbi::Stdcall { unwind: false },
X86Call::SysV64 => ExternAbi::SysV64 { unwind: false },
X86Call::Thiscall => ExternAbi::Thiscall { unwind: false },
X86Call::Vectorcall => ExternAbi::Vectorcall { unwind: false },
X86Call::Win64 => ExternAbi::Win64 { unwind: false },
},
};
erased_abi.as_str().fmt(f)
}
}
/// Callee codegen for interrupts
///
/// This is named differently from the "Call" enums because it is different:
/// these "ABI" differences are not relevant to callers, since there is "no caller".
/// These only affect callee codegen. making their categorization as distinct ABIs a bit peculiar.
#[derive(Copy, Clone, Debug)]
#[derive(PartialOrd, Ord, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "nightly", derive(HashStable_Generic))]
pub enum InterruptKind {
Avr,
AvrNonBlocking,
Msp430,
RiscvMachine,
RiscvSupervisor,
X86,
}
/// ABIs defined for x86-{32,64}
///
/// One of SysV64 or Win64 may alias the C ABI, and arguably Win64 is cross-platform now?
#[derive(Clone, Copy, Debug)]
#[derive(PartialOrd, Ord, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "nightly", derive(HashStable_Generic))]
pub enum X86Call {
/// "fastcall" has both GNU and Windows variants
Fastcall,
/// "stdcall" has both GNU and Windows variants
Stdcall,
SysV64,
Thiscall,
Vectorcall,
Win64,
}
/// ABIs defined for 32-bit Arm
#[derive(Copy, Clone, Debug)]
#[derive(PartialOrd, Ord, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "nightly", derive(HashStable_Generic))]
pub enum ArmCall {
Aapcs,
CCmseNonSecureCall,
CCmseNonSecureEntry,
}

View file

@ -7,69 +7,98 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher, StableOrd};
#[cfg(feature = "nightly")]
use rustc_macros::{Decodable, Encodable};
use crate::AbiFromStrErr;
#[cfg(test)]
mod tests;
use ExternAbi as Abi;
/// ABI we expect to see within `extern "{abi}"`
#[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "nightly", derive(Encodable, Decodable))]
pub enum ExternAbi {
// Some of the ABIs come first because every time we add a new ABI, we have to re-bless all the
// hashing tests. These are used in many places, so giving them stable values reduces test
// churn. The specific values are meaningless.
Rust,
/* universal */
/// presumed C ABI for the platform
C {
unwind: bool,
},
/// ABI of the "system" interface, e.g. the Win32 API, always "aliasing"
System {
unwind: bool,
},
/// that's us!
Rust,
/// the mostly-unused `unboxed_closures` ABI, effectively now an impl detail unless someone
/// puts in the work to make it viable again... but would we need a special ABI?
RustCall,
/// For things unlikely to be called, where reducing register pressure in
/// `extern "Rust"` callers is worth paying extra cost in the callee.
/// Stronger than just `#[cold]` because `fn` pointers might be incompatible.
RustCold,
/// Unstable impl detail that directly uses Rust types to describe the ABI to LLVM.
/// Even normally-compatible Rust types can become ABI-incompatible with this ABI!
Unadjusted,
/// UEFI ABI, usually an alias of C, but sometimes an arch-specific alias
/// and only valid on platforms that have a UEFI standard
EfiApi,
/* arm */
/// Arm Architecture Procedure Call Standard, sometimes `ExternAbi::C` is an alias for this
Aapcs {
unwind: bool,
},
/// extremely constrained barely-C ABI for TrustZone
CCmseNonSecureCall,
/// extremely constrained barely-C ABI for TrustZone
CCmseNonSecureEntry,
/* gpu */
/// An entry-point function called by the GPU's host
// FIXME: should not be callable from Rust on GPU targets, is for host's use only
GpuKernel,
/// An entry-point function called by the GPU's host
// FIXME: why do we have two of these?
PtxKernel,
/* interrupt */
AvrInterrupt,
AvrNonBlockingInterrupt,
Msp430Interrupt,
RiscvInterruptM,
RiscvInterruptS,
X86Interrupt,
/* x86 */
/// `ExternAbi::C` but spelled funny because x86
Cdecl {
unwind: bool,
},
/// gnu-stdcall on "unix" and win-stdcall on "windows"
Stdcall {
unwind: bool,
},
/// gnu-fastcall on "unix" and win-fastcall on "windows"
Fastcall {
unwind: bool,
},
Vectorcall {
unwind: bool,
},
/// windows C++ ABI
Thiscall {
unwind: bool,
},
Aapcs {
/// uses AVX and stuff
Vectorcall {
unwind: bool,
},
/* x86_64 */
SysV64 {
unwind: bool,
},
Win64 {
unwind: bool,
},
SysV64 {
unwind: bool,
},
PtxKernel,
Msp430Interrupt,
X86Interrupt,
/// An entry-point function called by the GPU's host
// FIXME: should not be callable from Rust on GPU targets, is for host's use only
GpuKernel,
EfiApi,
AvrInterrupt,
AvrNonBlockingInterrupt,
CCmseNonSecureCall,
CCmseNonSecureEntry,
System {
unwind: bool,
},
RustCall,
/// *Not* a stable ABI, just directly use the Rust types to describe the ABI for LLVM. Even
/// normally ABI-compatible Rust types can become ABI-incompatible with this ABI!
Unadjusted,
/// For things unlikely to be called, where reducing register pressure in
/// `extern "Rust"` callers is worth paying extra cost in the callee.
/// Stronger than just `#[cold]` because `fn` pointers might be incompatible.
RustCold,
RiscvInterruptM,
RiscvInterruptS,
}
macro_rules! abi_impls {
@ -99,11 +128,6 @@ macro_rules! abi_impls {
}
}
#[derive(Debug)]
pub enum AbiFromStrErr {
Unknown,
}
abi_impls! {
ExternAbi = {
C { unwind: false } =><= "C",
@ -227,7 +251,7 @@ pub fn all_names() -> Vec<&'static str> {
impl ExternAbi {
/// Default ABI chosen for `extern fn` declarations without an explicit ABI.
pub const FALLBACK: Abi = Abi::C { unwind: false };
pub const FALLBACK: ExternAbi = ExternAbi::C { unwind: false };
pub fn name(self) -> &'static str {
self.as_str()

View file

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

View file

@ -158,7 +158,7 @@ pub(super) fn layout<
// Build a prefix layout, including "promoting" all ineligible
// locals as part of the prefix. We compute the layout of all of
// these fields at once to get optimal packing.
let tag_index = prefix_layouts.len();
let tag_index = prefix_layouts.next_index();
// `variant_fields` already accounts for the reserved variants, so no need to add them.
let max_discr = (variant_fields.len() - 1) as u128;
@ -187,7 +187,7 @@ pub(super) fn layout<
// "a" (`0..b_start`) and "b" (`b_start..`) correspond to
// "outer" and "promoted" fields respectively.
let b_start = FieldIdx::new(tag_index + 1);
let b_start = tag_index.plus(1);
let offsets_b = IndexVec::from_raw(offsets.raw.split_off(b_start.index()));
let offsets_a = offsets;

View file

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

View file

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

View file

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

View file

@ -63,7 +63,7 @@ impl Attribute {
pub fn unwrap_normal_item(self) -> AttrItem {
match self.kind {
AttrKind::Normal(normal) => normal.into_inner().item,
AttrKind::Normal(normal) => normal.item,
AttrKind::DocComment(..) => panic!("unexpected doc comment"),
}
}

View file

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

View file

@ -1,209 +1,11 @@
//! The AST pointer.
//!
//! Provides [`P<T>`][struct@P], an owned smart pointer.
//!
//! # Motivations and benefits
//!
//! * **Identity**: sharing AST nodes is problematic for the various analysis
//! passes (e.g., one may be able to bypass the borrow checker with a shared
//! `ExprKind::AddrOf` node taking a mutable borrow).
//!
//! * **Efficiency**: folding can reuse allocation space for `P<T>` and `Vec<T>`,
//! the latter even when the input and output types differ (as it would be the
//! case with arenas or a GADT AST using type parameters to toggle features).
//!
//! * **Maintainability**: `P<T>` provides an interface, which can remain fully
//! functional even if the implementation changes (using a special thread-local
//! heap, for example). Moreover, a switch to, e.g., `P<'a, T>` would be easy
//! and mostly automated.
use std::fmt::{self, Debug, Display};
use std::ops::{Deref, DerefMut};
use std::{slice, vec};
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
/// An owned smart pointer.
/// A pointer type that uniquely owns a heap allocation of type T.
///
/// See the [module level documentation][crate::ptr] for details.
pub struct P<T: ?Sized> {
ptr: Box<T>,
}
/// This used to be its own type, but now it's just a typedef for `Box` and we are planning to
/// remove it soon.
pub type P<T> = Box<T>;
/// Construct a `P<T>` from a `T` value.
#[allow(non_snake_case)]
pub fn P<T: 'static>(value: T) -> P<T> {
P { ptr: Box::new(value) }
}
impl<T: 'static> P<T> {
/// Move out of the pointer.
/// Intended for chaining transformations not covered by `map`.
pub fn and_then<U, F>(self, f: F) -> U
where
F: FnOnce(T) -> U,
{
f(*self.ptr)
}
/// Equivalent to `and_then(|x| x)`.
pub fn into_inner(self) -> T {
*self.ptr
}
/// Produce a new `P<T>` from `self` without reallocating.
pub fn map<F>(mut self, f: F) -> P<T>
where
F: FnOnce(T) -> T,
{
let x = f(*self.ptr);
*self.ptr = x;
self
}
/// Optionally produce a new `P<T>` from `self` without reallocating.
pub fn filter_map<F>(mut self, f: F) -> Option<P<T>>
where
F: FnOnce(T) -> Option<T>,
{
*self.ptr = f(*self.ptr)?;
Some(self)
}
}
impl<T: ?Sized> Deref for P<T> {
type Target = T;
fn deref(&self) -> &T {
&self.ptr
}
}
impl<T: ?Sized> DerefMut for P<T> {
fn deref_mut(&mut self) -> &mut T {
&mut self.ptr
}
}
impl<T: 'static + Clone> Clone for P<T> {
fn clone(&self) -> P<T> {
P((**self).clone())
}
}
impl<T: ?Sized + Debug> Debug for P<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Debug::fmt(&self.ptr, f)
}
}
impl<T: Display> Display for P<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Display::fmt(&**self, f)
}
}
impl<T> fmt::Pointer for P<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Pointer::fmt(&self.ptr, f)
}
}
impl<D: Decoder, T: 'static + Decodable<D>> Decodable<D> for P<T> {
fn decode(d: &mut D) -> P<T> {
P(Decodable::decode(d))
}
}
impl<S: Encoder, T: Encodable<S>> Encodable<S> for P<T> {
fn encode(&self, s: &mut S) {
(**self).encode(s);
}
}
impl<T> P<[T]> {
// FIXME(const-hack) make this const again
pub fn new() -> P<[T]> {
P { ptr: Box::default() }
}
#[inline(never)]
pub fn from_vec(v: Vec<T>) -> P<[T]> {
P { ptr: v.into_boxed_slice() }
}
#[inline(never)]
pub fn into_vec(self) -> Vec<T> {
self.ptr.into_vec()
}
}
impl<T> Default for P<[T]> {
/// Creates an empty `P<[T]>`.
fn default() -> P<[T]> {
P::new()
}
}
impl<T: Clone> Clone for P<[T]> {
fn clone(&self) -> P<[T]> {
P::from_vec(self.to_vec())
}
}
impl<T> From<Vec<T>> for P<[T]> {
fn from(v: Vec<T>) -> Self {
P::from_vec(v)
}
}
impl<T> From<P<[T]>> for Vec<T> {
fn from(val: P<[T]>) -> Self {
val.into_vec()
}
}
impl<T> FromIterator<T> for P<[T]> {
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> P<[T]> {
P::from_vec(iter.into_iter().collect())
}
}
impl<T> IntoIterator for P<[T]> {
type Item = T;
type IntoIter = vec::IntoIter<T>;
fn into_iter(self) -> Self::IntoIter {
self.into_vec().into_iter()
}
}
impl<'a, T> IntoIterator for &'a P<[T]> {
type Item = &'a T;
type IntoIter = slice::Iter<'a, T>;
fn into_iter(self) -> Self::IntoIter {
self.ptr.iter()
}
}
impl<S: Encoder, T: Encodable<S>> Encodable<S> for P<[T]> {
fn encode(&self, s: &mut S) {
Encodable::encode(&**self, s);
}
}
impl<D: Decoder, T: Decodable<D>> Decodable<D> for P<[T]> {
fn decode(d: &mut D) -> P<[T]> {
P::from_vec(Decodable::decode(d))
}
}
impl<CTX, T> HashStable<CTX> for P<T>
where
T: ?Sized + HashStable<CTX>,
{
fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) {
(**self).hash_stable(hcx, hasher);
}
pub fn P<T>(value: T) -> P<T> {
Box::new(value)
}

View file

@ -57,7 +57,9 @@ impl TokenTree {
match (self, other) {
(TokenTree::Token(token, _), TokenTree::Token(token2, _)) => token.kind == token2.kind,
(TokenTree::Delimited(.., delim, tts), TokenTree::Delimited(.., delim2, tts2)) => {
delim == delim2 && tts.eq_unspanned(tts2)
delim == delim2
&& tts.len() == tts2.len()
&& tts.iter().zip(tts2.iter()).all(|(a, b)| a.eq_unspanned(b))
}
_ => false,
}
@ -694,18 +696,6 @@ impl TokenStream {
TokenStreamIter::new(self)
}
/// Compares two `TokenStream`s, checking equality without regarding span information.
pub fn eq_unspanned(&self, other: &TokenStream) -> bool {
let mut iter1 = self.iter();
let mut iter2 = other.iter();
for (tt1, tt2) in iter::zip(&mut iter1, &mut iter2) {
if !tt1.eq_unspanned(tt2) {
return false;
}
}
iter1.next().is_none() && iter2.next().is_none()
}
/// Create a token stream containing a single token with alone spacing. The
/// spacing used for the final token in a constructed stream doesn't matter
/// because it's never used. In practice we arbitrarily use

View file

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

View file

@ -446,13 +446,7 @@ impl<'a> SelfResolver<'a> {
}
impl<'ast, 'a> Visitor<'ast> for SelfResolver<'a> {
fn visit_path(&mut self, path: &'ast Path, id: NodeId) {
fn visit_id(&mut self, id: NodeId) {
self.try_replace_id(id);
visit::walk_path(self, path);
}
fn visit_path_segment(&mut self, seg: &'ast PathSegment) {
self.try_replace_id(seg.id);
visit::walk_path_segment(self, seg);
}
}

View file

@ -1,4 +1,3 @@
use std::assert_matches::assert_matches;
use std::ops::ControlFlow;
use std::sync::Arc;
@ -1199,11 +1198,13 @@ impl<'hir> LoweringContext<'_, 'hir> {
let closure_def_id = self.local_def_id(closure_id);
let (binder_clause, generic_params) = self.lower_closure_binder(binder);
assert_matches!(
coroutine_kind,
CoroutineKind::Async { .. },
"only async closures are supported currently"
);
let coroutine_desugaring = match coroutine_kind {
CoroutineKind::Async { .. } => hir::CoroutineDesugaring::Async,
CoroutineKind::Gen { .. } => hir::CoroutineDesugaring::Gen,
CoroutineKind::AsyncGen { span, .. } => {
span_bug!(span, "only async closures and `iter!` closures are supported currently")
}
};
let body = self.with_new_scopes(fn_decl_span, |this| {
let inner_decl =
@ -1247,7 +1248,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
// Lower this as a `CoroutineClosure`. That will ensure that HIR typeck
// knows that a `FnDecl` output type like `-> &str` actually means
// "coroutine that returns &str", rather than directly returning a `&str`.
kind: hir::ClosureKind::CoroutineClosure(hir::CoroutineDesugaring::Async),
kind: hir::ClosureKind::CoroutineClosure(coroutine_desugaring),
constness: hir::Constness::NotConst,
});
hir::ExprKind::Closure(c)

View file

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

View file

@ -477,11 +477,12 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
for span in spans {
if (!visitor.features.coroutines() && !span.allows_unstable(sym::coroutines))
&& (!visitor.features.gen_blocks() && !span.allows_unstable(sym::gen_blocks))
&& (!visitor.features.yield_expr() && !span.allows_unstable(sym::yield_expr))
{
#[allow(rustc::untranslatable_diagnostic)]
// Don't know which of the two features to include in the
// error message, so I am arbitrarily picking one.
feature_err(&visitor.sess, sym::coroutines, *span, "yield syntax is experimental")
// Emit yield_expr as the error, since that will be sufficient. You can think of it
// as coroutines and gen_blocks imply yield_expr.
feature_err(&visitor.sess, sym::yield_expr, *span, "yield syntax is experimental")
.emit();
}
}

View file

@ -53,7 +53,7 @@ fn parse_unstable<'a>(
for param in list.mixed() {
let param_span = param.span();
if let Some(ident) = param.meta_item().and_then(|i| i.path_without_args().word()) {
if let Some(ident) = param.meta_item().and_then(|i| i.path().word()) {
res.push(ident.name);
} else {
cx.emit_err(session_diagnostics::ExpectsFeatures {

View file

@ -79,7 +79,7 @@ impl SingleAttributeParser for DeprecationParser {
return None;
};
let ident_name = param.path_without_args().word_sym();
let ident_name = param.path().word_sym();
match ident_name {
Some(name @ sym::since) => {
@ -102,7 +102,7 @@ impl SingleAttributeParser for DeprecationParser {
_ => {
cx.emit_err(session_diagnostics::UnknownMetaItem {
span: param_span,
item: param.path_without_args().to_string(),
item: param.path().to_string(),
expected: if features.deprecated_suggestion() {
&["since", "note", "suggestion"]
} else {

View file

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

View file

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

View file

@ -204,7 +204,7 @@ fn insert_value_into_option_or_error(
if item.is_some() {
cx.emit_err(session_diagnostics::MultipleItem {
span: param.span(),
item: param.path_without_args().to_string(),
item: param.path().to_string(),
});
None
} else if let Some(v) = param.args().name_value()
@ -242,13 +242,13 @@ pub(crate) fn parse_stability(
return None;
};
match param.path_without_args().word_sym() {
match param.path().word_sym() {
Some(sym::feature) => insert_value_into_option_or_error(cx, &param, &mut feature)?,
Some(sym::since) => insert_value_into_option_or_error(cx, &param, &mut since)?,
_ => {
cx.emit_err(session_diagnostics::UnknownMetaItem {
span: param_span,
item: param.path_without_args().to_string(),
item: param.path().to_string(),
expected: &["feature", "since"],
});
return None;
@ -310,7 +310,7 @@ pub(crate) fn parse_unstability(
return None;
};
match param.path_without_args().word_sym() {
match param.path().word_sym() {
Some(sym::feature) => insert_value_into_option_or_error(cx, &param, &mut feature)?,
Some(sym::reason) => insert_value_into_option_or_error(cx, &param, &mut reason)?,
Some(sym::issue) => {
@ -349,7 +349,7 @@ pub(crate) fn parse_unstability(
_ => {
cx.emit_err(session_diagnostics::UnknownMetaItem {
span: param.span(),
item: param.path_without_args().to_string(),
item: param.path().to_string(),
expected: &["feature", "reason", "issue", "soft", "implied_by"],
});
return None;

View file

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

View file

@ -1,31 +1,38 @@
//! Centralized logic for parsing and attributes.
//!
//! Part of a series of crates:
//! - rustc_attr_data_structures: contains types that the parsers parse into
//! - rustc_attr_parsing: this crate
//! - (in the future): rustc_attr_validation
//! ## Architecture
//! This crate is part of a series of crates that handle attribute processing.
//! - [rustc_attr_data_structures](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_attr_data_structures/index.html): Defines the data structures that store parsed attributes
//! - [rustc_attr_parsing](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_attr_parsing/index.html): This crate, handles the parsing of attributes
//! - (planned) rustc_attr_validation: Will handle attribute validation
//!
//! History: Check out [#131229](https://github.com/rust-lang/rust/issues/131229).
//! There used to be only one definition of attributes in the compiler: `ast::Attribute`.
//! These were then parsed or validated or both in places distributed all over the compiler.
//! This was a mess...
//! The separation between data structures and parsing follows the principle of separation of concerns.
//! Data structures (`rustc_attr_data_structures`) define what attributes look like after parsing.
//! This crate (`rustc_attr_parsing`) handles how to convert raw tokens into those structures.
//! This split allows other parts of the compiler to use the data structures without needing
//! the parsing logic, making the codebase more modular and maintainable.
//!
//! Attributes are markers on items.
//! Many of them are actually attribute-like proc-macros, and are expanded to some other rust syntax.
//! This could either be a user provided proc macro, or something compiler provided.
//! `derive` is an example of one that the compiler provides.
//! These are built-in, but they have a valid expansion to Rust tokens and are thus called "active".
//! I personally like calling these *active* compiler-provided attributes, built-in *macros*,
//! because they still expand, and this helps to differentiate them from built-in *attributes*.
//! However, I'll be the first to admit that the naming here can be confusing.
//! ## Background
//! Previously, the compiler had a single attribute definition (`ast::Attribute`) with parsing and
//! validation scattered throughout the codebase. This was reorganized for better maintainability
//! (see [#131229](https://github.com/rust-lang/rust/issues/131229)).
//!
//! The alternative to active attributes, are inert attributes.
//! These can occur in user code (proc-macro helper attributes).
//! But what's important is, many built-in attributes are inert like this.
//! There is nothing they expand to during the macro expansion process,
//! sometimes because they literally cannot expand to something that is valid Rust.
//! They are really just markers to guide the compilation process.
//! An example is `#[inline(...)]` which changes how code for functions is generated.
//! ## Types of Attributes
//! In Rust, attributes are markers that can be attached to items. They come in two main categories.
//!
//! ### 1. Active Attributes
//! These are attribute-like proc-macros that expand into other Rust code.
//! They can be either user-defined or compiler-provided. Examples of compiler-provided active attributes:
//! - `#[derive(...)]`: Expands into trait implementations
//! - `#[cfg()]`: Expands based on configuration
//! - `#[cfg_attr()]`: Conditional attribute application
//!
//! ### 2. Inert Attributes
//! These are pure markers that don't expand into other code. They guide the compilation process.
//! They can be user-defined (in proc-macro helpers) or built-in. Examples of built-in inert attributes:
//! - `#[stable()]`: Marks stable API items
//! - `#[inline()]`: Suggests function inlining
//! - `#[repr()]`: Controls type representation
//!
//! ```text
//! Active Inert
@ -33,27 +40,21 @@
//! │ (mostly in) │ these are parsed │
//! │ rustc_builtin_macros │ here! │
//! │ │ │
//! │ │ │
//! │ #[derive(...)] │ #[stable()] │
//! Built-in │ #[cfg()] │ #[inline()] │
//! │ #[cfg_attr()] │ #[repr()] │
//! │ │ │
//! │ │ │
//! │ │ │
//! ├──────────────────────┼──────────────────────┤
//! │ │ │
//! │ │ │
//! │ │ `b` in │
//! │ │ #[proc_macro_derive( │
//! User created │ #[proc_macro_attr()] │ a, │
//! │ │ attributes(b) │
//! │ │ ] │
//! │ │ │
//! │ │ │
//! │ │ │
//! └──────────────────────┴──────────────────────┘
//! ```
//!
//! ## How This Crate Works
//! In this crate, syntactical attributes (sequences of tokens that look like
//! `#[something(something else)]`) are parsed into more semantic attributes, markers on items.
//! Multiple syntactic attributes might influence a single semantic attribute. For example,
@ -63,18 +64,17 @@
//! and `#[unstable()]` syntactic attributes, and at the end produce a single
//! [`AttributeKind::Stability`](rustc_attr_data_structures::AttributeKind::Stability).
//!
//! As a rule of thumb, when a syntactical attribute can be applied more than once, they should be
//! combined into a single semantic attribute. For example:
//! When multiple instances of the same attribute are allowed, they're combined into a single
//! semantic attribute. For example:
//!
//! ```
//! ```rust
//! #[repr(C)]
//! #[repr(packed)]
//! struct Meow {}
//! ```
//!
//! should result in a single `AttributeKind::Repr` containing a list of repr annotations, in this
//! case `C` and `packed`. This is equivalent to writing `#[repr(C, packed)]` in a single
//! syntactical annotation.
//! This is equivalent to `#[repr(C, packed)]` and results in a single `AttributeKind::Repr`
//! containing both `C` and `packed` annotations.
// tidy-alphabetical-start
#![allow(internal_features)]

View file

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

View file

@ -5,11 +5,9 @@ use rustc_index::{IndexSlice, IndexVec};
use rustc_middle::mir::ConstraintCategory;
use rustc_middle::ty::{RegionVid, TyCtxt, VarianceDiagInfo};
use rustc_span::Span;
use tracing::{debug, instrument};
use tracing::debug;
use crate::region_infer::{AnnotatedSccs, ConstraintSccs, RegionDefinition, SccAnnotations};
use crate::type_check::Locations;
use crate::universal_regions::UniversalRegions;
pub(crate) mod graph;
@ -53,112 +51,6 @@ impl<'tcx> OutlivesConstraintSet<'tcx> {
) -> &IndexSlice<OutlivesConstraintIndex, OutlivesConstraint<'tcx>> {
&self.outlives
}
/// Computes cycles (SCCs) in the graph of regions. In particular,
/// find all regions R1, R2 such that R1: R2 and R2: R1 and group
/// them into an SCC, and find the relationships between SCCs.
pub(crate) fn compute_sccs(
&self,
static_region: RegionVid,
definitions: &IndexVec<RegionVid, RegionDefinition<'tcx>>,
) -> AnnotatedSccs {
let constraint_graph = self.graph(definitions.len());
let region_graph = &constraint_graph.region_graph(self, static_region);
let mut annotation_visitor = SccAnnotations::new(definitions);
(
ConstraintSccs::new_with_annotation(&region_graph, &mut annotation_visitor),
annotation_visitor.scc_to_annotation,
)
}
/// This method handles Universe errors by rewriting the constraint
/// graph. For each strongly connected component in the constraint
/// graph such that there is a series of constraints
/// A: B: C: ... : X where
/// A's universe is smaller than X's and A is a placeholder,
/// add a constraint that A: 'static. This is a safe upper bound
/// in the face of borrow checker/trait solver limitations that will
/// eventually go away.
///
/// For a more precise definition, see the documentation for
/// [`crate::region_infer::RegionTracker`].
///
/// This edge case used to be handled during constraint propagation
/// by iterating over the strongly connected components in the constraint
/// graph while maintaining a set of bookkeeping mappings similar
/// to what is stored in `RegionTracker` and manually adding 'static as
/// needed.
///
/// It was rewritten as part of the Polonius project with the goal of moving
/// higher-kindedness concerns out of the path of the borrow checker,
/// for two reasons:
///
/// 1. Implementing Polonius is difficult enough without also
/// handling them.
/// 2. The long-term goal is to handle higher-kinded concerns
/// in the trait solver, where they belong. This avoids
/// logic duplication and allows future trait solvers
/// to compute better bounds than for example our
/// "must outlive 'static" here.
///
/// This code is a stop-gap measure in preparation for the future trait solver.
///
/// Every constraint added by this method is an
/// internal `IllegalUniverse` constraint.
#[instrument(skip(self, universal_regions, definitions))]
pub(crate) fn add_outlives_static(
&mut self,
universal_regions: &UniversalRegions<'tcx>,
definitions: &IndexVec<RegionVid, RegionDefinition<'tcx>>,
) -> AnnotatedSccs {
let fr_static = universal_regions.fr_static;
let (sccs, annotations) = self.compute_sccs(fr_static, definitions);
// Changed to `true` if we added any constraints to `self` and need to
// recompute SCCs.
let mut added_constraints = false;
for scc in sccs.all_sccs() {
// No point in adding 'static: 'static!
// This micro-optimisation makes somewhat sense
// because static outlives *everything*.
if scc == sccs.scc(fr_static) {
continue;
}
let annotation = annotations[scc];
// If this SCC participates in a universe violation,
// e.g. if it reaches a region with a universe smaller than
// the largest region reached, add a requirement that it must
// outlive `'static`.
if annotation.has_incompatible_universes() {
// Optimisation opportunity: this will add more constraints than
// needed for correctness, since an SCC upstream of another with
// a universe violation will "infect" its downstream SCCs to also
// outlive static.
added_constraints = true;
let scc_representative_outlives_static = OutlivesConstraint {
sup: annotation.representative,
sub: fr_static,
category: ConstraintCategory::IllegalUniverse,
locations: Locations::All(rustc_span::DUMMY_SP),
span: rustc_span::DUMMY_SP,
variance_info: VarianceDiagInfo::None,
from_closure: false,
};
self.push(scc_representative_outlives_static);
}
}
if added_constraints {
// We changed the constraint set and so must recompute SCCs.
self.compute_sccs(fr_static, definitions)
} else {
// If we didn't add any back-edges; no more work needs doing
(sccs, annotations)
}
}
}
impl<'tcx> Index<OutlivesConstraintIndex> for OutlivesConstraintSet<'tcx> {

View file

@ -263,7 +263,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
// something that already has `Fn`-like bounds (or is a closure), so we can't
// restrict anyways.
} else {
let copy_did = self.infcx.tcx.require_lang_item(LangItem::Copy, Some(span));
let copy_did = self.infcx.tcx.require_lang_item(LangItem::Copy, span);
self.suggest_adding_bounds(&mut err, ty, copy_did, span);
}
@ -1915,7 +1915,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
let local_ty = self.body.local_decls[place.local].ty;
let typeck_results = tcx.typeck(self.mir_def_id());
let clone = tcx.require_lang_item(LangItem::Clone, Some(body.span));
let clone = tcx.require_lang_item(LangItem::Clone, body.span);
for expr in expr_finder.clones {
if let hir::ExprKind::MethodCall(_, rcvr, _, span) = expr.kind
&& let Some(rcvr_ty) = typeck_results.node_type_opt(rcvr.hir_id)
@ -3314,7 +3314,13 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
"function parameter".to_string(),
"function parameter borrowed here".to_string(),
),
LocalKind::Temp if self.body.local_decls[local].is_user_variable() => {
LocalKind::Temp
if self.body.local_decls[local].is_user_variable()
&& !self.body.local_decls[local]
.source_info
.span
.in_external_macro(self.infcx.tcx.sess.source_map()) =>
{
("local binding".to_string(), "local binding introduced here".to_string())
}
LocalKind::ReturnPointer | LocalKind::Temp => {

View file

@ -0,0 +1,348 @@
//! Logic for lowering higher-kinded outlives constraints
//! (with placeholders and universes) and turn them into regular
//! outlives constraints.
use rustc_data_structures::frozen::Frozen;
use rustc_data_structures::fx::FxIndexMap;
use rustc_data_structures::graph::scc;
use rustc_data_structures::graph::scc::Sccs;
use rustc_index::IndexVec;
use rustc_infer::infer::RegionVariableOrigin;
use rustc_middle::mir::ConstraintCategory;
use rustc_middle::ty::{RegionVid, UniverseIndex};
use tracing::debug;
use crate::constraints::{ConstraintSccIndex, OutlivesConstraintSet};
use crate::consumers::OutlivesConstraint;
use crate::diagnostics::UniverseInfo;
use crate::member_constraints::MemberConstraintSet;
use crate::region_infer::values::{LivenessValues, PlaceholderIndices};
use crate::region_infer::{ConstraintSccs, RegionDefinition, Representative, TypeTest};
use crate::ty::VarianceDiagInfo;
use crate::type_check::free_region_relations::UniversalRegionRelations;
use crate::type_check::{Locations, MirTypeckRegionConstraints};
use crate::universal_regions::UniversalRegions;
use crate::{BorrowckInferCtxt, NllRegionVariableOrigin};
/// A set of outlives constraints after rewriting to remove
/// higher-kinded constraints.
pub(crate) struct LoweredConstraints<'tcx> {
pub(crate) constraint_sccs: Sccs<RegionVid, ConstraintSccIndex>,
pub(crate) definitions: Frozen<IndexVec<RegionVid, RegionDefinition<'tcx>>>,
pub(crate) scc_annotations: IndexVec<ConstraintSccIndex, RegionTracker>,
pub(crate) member_constraints: MemberConstraintSet<'tcx, RegionVid>,
pub(crate) outlives_constraints: Frozen<OutlivesConstraintSet<'tcx>>,
pub(crate) type_tests: Vec<TypeTest<'tcx>>,
pub(crate) liveness_constraints: LivenessValues,
pub(crate) universe_causes: FxIndexMap<UniverseIndex, UniverseInfo<'tcx>>,
pub(crate) placeholder_indices: PlaceholderIndices,
}
impl<'d, 'tcx, A: scc::Annotation> SccAnnotations<'d, 'tcx, A> {
pub(crate) fn init(definitions: &'d IndexVec<RegionVid, RegionDefinition<'tcx>>) -> Self {
Self { scc_to_annotation: IndexVec::new(), definitions }
}
}
/// A Visitor for SCC annotation construction.
pub(crate) struct SccAnnotations<'d, 'tcx, A: scc::Annotation> {
pub(crate) scc_to_annotation: IndexVec<ConstraintSccIndex, A>,
definitions: &'d IndexVec<RegionVid, RegionDefinition<'tcx>>,
}
impl scc::Annotations<RegionVid> for SccAnnotations<'_, '_, RegionTracker> {
fn new(&self, element: RegionVid) -> RegionTracker {
RegionTracker::new(element, &self.definitions[element])
}
fn annotate_scc(&mut self, scc: ConstraintSccIndex, annotation: RegionTracker) {
let idx = self.scc_to_annotation.push(annotation);
assert!(idx == scc);
}
type Ann = RegionTracker;
type SccIdx = ConstraintSccIndex;
}
/// An annotation for region graph SCCs that tracks
/// the values of its elements. This annotates a single SCC.
#[derive(Copy, Debug, Clone)]
pub(crate) struct RegionTracker {
/// The largest universe of a placeholder reached from this SCC.
/// This includes placeholders within this SCC.
max_placeholder_universe_reached: UniverseIndex,
/// The largest universe nameable from this SCC.
/// It is the smallest nameable universes of all
/// existential regions reachable from it.
max_nameable_universe: UniverseIndex,
/// The representative Region Variable Id for this SCC.
pub(crate) representative: Representative,
}
impl RegionTracker {
pub(crate) fn new(rvid: RegionVid, definition: &RegionDefinition<'_>) -> Self {
let placeholder_universe =
if matches!(definition.origin, NllRegionVariableOrigin::Placeholder(_)) {
definition.universe
} else {
UniverseIndex::ROOT
};
Self {
max_placeholder_universe_reached: placeholder_universe,
max_nameable_universe: definition.universe,
representative: Representative::new(rvid, definition),
}
}
/// The largest universe this SCC can name. It's the smallest
/// largest nameable uninverse of any reachable region.
pub(crate) fn max_nameable_universe(self) -> UniverseIndex {
self.max_nameable_universe
}
fn merge_min_max_seen(&mut self, other: &Self) {
self.max_placeholder_universe_reached = std::cmp::max(
self.max_placeholder_universe_reached,
other.max_placeholder_universe_reached,
);
self.max_nameable_universe =
std::cmp::min(self.max_nameable_universe, other.max_nameable_universe);
}
/// Returns `true` if during the annotated SCC reaches a placeholder
/// with a universe larger than the smallest nameable universe of any
/// reachable existential region.
pub(crate) fn has_incompatible_universes(&self) -> bool {
self.max_nameable_universe().cannot_name(self.max_placeholder_universe_reached)
}
/// Determine if the tracked universes of the two SCCs are compatible.
pub(crate) fn universe_compatible_with(&self, other: Self) -> bool {
self.max_nameable_universe().can_name(other.max_nameable_universe())
|| self.max_nameable_universe().can_name(other.max_placeholder_universe_reached)
}
}
impl scc::Annotation for RegionTracker {
fn merge_scc(mut self, other: Self) -> Self {
self.representative = self.representative.merge_scc(other.representative);
self.merge_min_max_seen(&other);
self
}
fn merge_reached(mut self, other: Self) -> Self {
// No update to in-component values, only add seen values.
self.merge_min_max_seen(&other);
self
}
}
/// Determines if the region variable definitions contain
/// placeholders, and compute them for later use.
fn region_definitions<'tcx>(
universal_regions: &UniversalRegions<'tcx>,
infcx: &BorrowckInferCtxt<'tcx>,
) -> (Frozen<IndexVec<RegionVid, RegionDefinition<'tcx>>>, bool) {
let var_infos = infcx.get_region_var_infos();
// Create a RegionDefinition for each inference variable. This happens here because
// it allows us to sneak in a cheap check for placeholders. Otherwise, its proper home
// is in `RegionInferenceContext::new()`, probably.
let mut definitions = IndexVec::with_capacity(var_infos.len());
let mut has_placeholders = false;
for info in var_infos.iter() {
let origin = match info.origin {
RegionVariableOrigin::Nll(origin) => origin,
_ => NllRegionVariableOrigin::Existential { from_forall: false },
};
let definition = RegionDefinition { origin, universe: info.universe, external_name: None };
has_placeholders |= matches!(origin, NllRegionVariableOrigin::Placeholder(_));
definitions.push(definition);
}
// Add external names from universal regions in fun function definitions.
// FIXME: this two-step method is annoying, but I don't know how to avoid it.
for (external_name, variable) in universal_regions.named_universal_regions_iter() {
debug!("region {:?} has external name {:?}", variable, external_name);
definitions[variable].external_name = Some(external_name);
}
(Frozen::freeze(definitions), has_placeholders)
}
/// This method handles placeholders by rewriting the constraint
/// graph. For each strongly connected component in the constraint
/// graph such that there is a series of constraints
/// A: B: C: ... : X where
/// A contains a placeholder whose universe cannot be named by X,
/// add a constraint that A: 'static. This is a safe upper bound
/// in the face of borrow checker/trait solver limitations that will
/// eventually go away.
///
/// For a more precise definition, see the documentation for
/// [`RegionTracker`] and its methods!
///
/// This edge case used to be handled during constraint propagation.
/// It was rewritten as part of the Polonius project with the goal of moving
/// higher-kindedness concerns out of the path of the borrow checker,
/// for two reasons:
///
/// 1. Implementing Polonius is difficult enough without also
/// handling them.
/// 2. The long-term goal is to handle higher-kinded concerns
/// in the trait solver, where they belong. This avoids
/// logic duplication and allows future trait solvers
/// to compute better bounds than for example our
/// "must outlive 'static" here.
///
/// This code is a stop-gap measure in preparation for the future trait solver.
///
/// Every constraint added by this method is an internal `IllegalUniverse` constraint.
pub(crate) fn compute_sccs_applying_placeholder_outlives_constraints<'tcx>(
constraints: MirTypeckRegionConstraints<'tcx>,
universal_region_relations: &Frozen<UniversalRegionRelations<'tcx>>,
infcx: &BorrowckInferCtxt<'tcx>,
) -> LoweredConstraints<'tcx> {
let universal_regions = &universal_region_relations.universal_regions;
let (definitions, has_placeholders) = region_definitions(universal_regions, infcx);
let MirTypeckRegionConstraints {
placeholder_indices,
placeholder_index_to_region: _,
liveness_constraints,
mut outlives_constraints,
mut member_constraints,
universe_causes,
type_tests,
} = constraints;
if let Some(guar) = universal_regions.tainted_by_errors() {
debug!("Universal regions tainted by errors; removing constraints!");
// Suppress unhelpful extra errors in `infer_opaque_types` by clearing out all
// outlives bounds that we may end up checking.
outlives_constraints = Default::default();
member_constraints = Default::default();
// Also taint the entire scope.
infcx.set_tainted_by_errors(guar);
}
let fr_static = universal_regions.fr_static;
let compute_sccs =
|constraints: &OutlivesConstraintSet<'tcx>,
annotations: &mut SccAnnotations<'_, 'tcx, RegionTracker>| {
ConstraintSccs::new_with_annotation(
&constraints.graph(definitions.len()).region_graph(constraints, fr_static),
annotations,
)
};
let mut scc_annotations = SccAnnotations::init(&definitions);
let constraint_sccs = compute_sccs(&outlives_constraints, &mut scc_annotations);
// This code structure is a bit convoluted because it allows for a planned
// future change where the early return here has a different type of annotation
// that does much less work.
if !has_placeholders {
debug!("No placeholder regions found; skipping rewriting logic!");
return LoweredConstraints {
type_tests,
member_constraints,
constraint_sccs,
scc_annotations: scc_annotations.scc_to_annotation,
definitions,
outlives_constraints: Frozen::freeze(outlives_constraints),
liveness_constraints,
universe_causes,
placeholder_indices,
};
}
debug!("Placeholders present; activating placeholder handling logic!");
let added_constraints = rewrite_placeholder_outlives(
&constraint_sccs,
&scc_annotations,
fr_static,
&mut outlives_constraints,
);
let (constraint_sccs, scc_annotations) = if added_constraints {
let mut annotations = SccAnnotations::init(&definitions);
// We changed the constraint set and so must recompute SCCs.
// Optimisation opportunity: if we can add them incrementally (and that's
// possible because edges to 'static always only merge SCCs into 'static),
// we would potentially save a lot of work here.
(compute_sccs(&outlives_constraints, &mut annotations), annotations.scc_to_annotation)
} else {
// If we didn't add any back-edges; no more work needs doing
debug!("No constraints rewritten!");
(constraint_sccs, scc_annotations.scc_to_annotation)
};
LoweredConstraints {
constraint_sccs,
definitions,
scc_annotations,
member_constraints,
outlives_constraints: Frozen::freeze(outlives_constraints),
type_tests,
liveness_constraints,
universe_causes,
placeholder_indices,
}
}
fn rewrite_placeholder_outlives<'tcx>(
sccs: &Sccs<RegionVid, ConstraintSccIndex>,
annotations: &SccAnnotations<'_, '_, RegionTracker>,
fr_static: RegionVid,
outlives_constraints: &mut OutlivesConstraintSet<'tcx>,
) -> bool {
// Changed to `true` if we added any constraints and need to
// recompute SCCs.
let mut added_constraints = false;
let annotations = &annotations.scc_to_annotation;
for scc in sccs.all_sccs() {
// No point in adding 'static: 'static!
// This micro-optimisation makes somewhat sense
// because static outlives *everything*.
if scc == sccs.scc(fr_static) {
continue;
}
let annotation = annotations[scc];
// If this SCC participates in a universe violation,
// e.g. if it reaches a region with a universe smaller than
// the largest region reached, add a requirement that it must
// outlive `'static`.
if annotation.has_incompatible_universes() {
// Optimisation opportunity: this will add more constraints than
// needed for correctness, since an SCC upstream of another with
// a universe violation will "infect" its downstream SCCs to also
// outlive static.
let scc_representative_outlives_static = OutlivesConstraint {
sup: annotation.representative.rvid(),
sub: fr_static,
category: ConstraintCategory::IllegalUniverse,
locations: Locations::All(rustc_span::DUMMY_SP),
span: rustc_span::DUMMY_SP,
variance_info: VarianceDiagInfo::None,
from_closure: false,
};
outlives_constraints.push(scc_representative_outlives_static);
added_constraints = true;
debug!("Added {:?}: 'static!", annotation.representative.rvid());
}
}
added_constraints
}

View file

@ -72,6 +72,7 @@ mod constraints;
mod dataflow;
mod def_use;
mod diagnostics;
mod handle_placeholders;
mod member_constraints;
mod nll;
mod path_utils;

View file

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

View file

@ -13,7 +13,7 @@ use tracing::debug;
use crate::borrow_set::BorrowSet;
use crate::constraints::OutlivesConstraint;
use crate::type_check::MirTypeckRegionConstraints;
use crate::handle_placeholders::LoweredConstraints;
use crate::type_check::free_region_relations::UniversalRegionRelations;
use crate::universal_regions::UniversalRegions;
@ -43,7 +43,7 @@ pub(crate) fn emit_facts<'tcx>(
borrow_set: &BorrowSet<'tcx>,
move_data: &MoveData<'tcx>,
universal_region_relations: &UniversalRegionRelations<'tcx>,
constraints: &MirTypeckRegionConstraints<'tcx>,
constraints: &LoweredConstraints<'tcx>,
) {
let Some(facts) = facts else {
// We don't do anything if there are no facts to fill.
@ -203,7 +203,7 @@ pub(crate) fn emit_drop_facts<'tcx>(
fn emit_outlives_facts<'tcx>(
facts: &mut PoloniusFacts,
location_table: &PoloniusLocationTable,
constraints: &MirTypeckRegionConstraints<'tcx>,
constraints: &LoweredConstraints<'tcx>,
) {
facts.subset_base.extend(constraints.outlives_constraints.outlives().iter().flat_map(
|constraint: &OutlivesConstraint<'_>| {

View file

@ -46,7 +46,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
"| {r:rw$?} | {ui:4?} | {v}",
r = region,
rw = REGION_WIDTH,
ui = self.region_universe(region),
ui = self.max_nameable_universe(self.constraint_sccs.scc(region)),
v = self.region_value_str(region),
)?;
}

View file

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

View file

@ -191,7 +191,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
let scc = self.constraint_sccs.scc(vid);
// Special handling of higher-ranked regions.
if !self.scc_universe(scc).is_root() {
if !self.max_nameable_universe(scc).is_root() {
match self.scc_values.placeholders_contained_in(scc).enumerate().last() {
// If the region contains a single placeholder then they're equal.
Some((0, placeholder)) => {

View file

@ -52,7 +52,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
assert_matches!(
self.tcx().coroutine_kind(self.tcx().coroutine_for_closure(mir_def_id)),
Some(hir::CoroutineKind::Desugared(
hir::CoroutineDesugaring::Async,
hir::CoroutineDesugaring::Async | hir::CoroutineDesugaring::Gen,
hir::CoroutineSource::Closure
)),
"this needs to be modified if we're lowering non-async closures"

View file

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

View file

@ -544,10 +544,10 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> {
// (as it's created inside the body itself, not passed in from outside).
if let DefiningTy::FnDef(def_id, _) = defining_ty {
if self.infcx.tcx.fn_sig(def_id).skip_binder().c_variadic() {
let va_list_did = self.infcx.tcx.require_lang_item(
LangItem::VaList,
Some(self.infcx.tcx.def_span(self.mir_def)),
);
let va_list_did = self
.infcx
.tcx
.require_lang_item(LangItem::VaList, self.infcx.tcx.def_span(self.mir_def));
let reg_vid = self
.infcx

View file

@ -8,6 +8,8 @@ use crate::deriving::generic::ty::*;
use crate::deriving::generic::*;
use crate::deriving::{path_local, path_std};
/// Expands a `#[derive(PartialEq)]` attribute into an implementation for the
/// target item.
pub(crate) fn expand_deriving_partial_eq(
cx: &ExtCtxt<'_>,
span: Span,
@ -16,62 +18,6 @@ pub(crate) fn expand_deriving_partial_eq(
push: &mut dyn FnMut(Annotatable),
is_const: bool,
) {
fn cs_eq(cx: &ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> BlockOrExpr {
let base = true;
let expr = cs_fold(
true, // use foldl
cx,
span,
substr,
|cx, fold| match fold {
CsFold::Single(field) => {
let [other_expr] = &field.other_selflike_exprs[..] else {
cx.dcx()
.span_bug(field.span, "not exactly 2 arguments in `derive(PartialEq)`");
};
// We received arguments of type `&T`. Convert them to type `T` by stripping
// any leading `&`. This isn't necessary for type checking, but
// it results in better error messages if something goes wrong.
//
// Note: for arguments that look like `&{ x }`, which occur with packed
// structs, this would cause expressions like `{ self.x } == { other.x }`,
// which isn't valid Rust syntax. This wouldn't break compilation because these
// AST nodes are constructed within the compiler. But it would mean that code
// printed by `-Zunpretty=expanded` (or `cargo expand`) would have invalid
// syntax, which would be suboptimal. So we wrap these in parens, giving
// `({ self.x }) == ({ other.x })`, which is valid syntax.
let convert = |expr: &P<Expr>| {
if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) =
&expr.kind
{
if let ExprKind::Block(..) = &inner.kind {
// `&{ x }` form: remove the `&`, add parens.
cx.expr_paren(field.span, inner.clone())
} else {
// `&x` form: remove the `&`.
inner.clone()
}
} else {
expr.clone()
}
};
cx.expr_binary(
field.span,
BinOpKind::Eq,
convert(&field.self_expr),
convert(other_expr),
)
}
CsFold::Combine(span, expr1, expr2) => {
cx.expr_binary(span, BinOpKind::And, expr1, expr2)
}
CsFold::Fieldless => cx.expr_bool(span, base),
},
);
BlockOrExpr::new_expr(expr)
}
let structural_trait_def = TraitDef {
span,
path: path_std!(marker::StructuralPartialEq),
@ -97,7 +43,9 @@ pub(crate) fn expand_deriving_partial_eq(
ret_ty: Path(path_local!(bool)),
attributes: thin_vec![cx.attr_word(sym::inline, span)],
fieldless_variants_strategy: FieldlessVariantsStrategy::Unify,
combine_substructure: combine_substructure(Box::new(|a, b, c| cs_eq(a, b, c))),
combine_substructure: combine_substructure(Box::new(|a, b, c| {
BlockOrExpr::new_expr(get_substructure_equality_expr(a, b, c))
})),
}];
let trait_def = TraitDef {
@ -113,3 +61,156 @@ pub(crate) fn expand_deriving_partial_eq(
};
trait_def.expand(cx, mitem, item, push)
}
/// Generates the equality expression for a struct or enum variant when deriving
/// `PartialEq`.
///
/// This function generates an expression that checks if all fields of a struct
/// or enum variant are equal.
/// - Scalar fields are compared first for efficiency, followed by compound
/// fields.
/// - If there are no fields, returns `true` (fieldless types are always equal).
///
/// Whether a field is considered "scalar" is determined by comparing the symbol
/// of its type to a set of known scalar type symbols (e.g., `i32`, `u8`, etc).
/// This check is based on the type's symbol.
///
/// ### Example 1
/// ```
/// #[derive(PartialEq)]
/// struct i32;
///
/// // Here, `field_2` is of type `i32`, but since it's a user-defined type (not
/// // the primitive), it will not be treated as scalar. The function will still
/// // check equality of `field_2` first because the symbol matches `i32`.
/// #[derive(PartialEq)]
/// struct Struct {
/// field_1: &'static str,
/// field_2: i32,
/// }
/// ```
///
/// ### Example 2
/// ```
/// mod ty {
/// pub type i32 = i32;
/// }
///
/// // Here, `field_2` is of type `ty::i32`, which is a type alias for `i32`.
/// // However, the function will not reorder the fields because the symbol for
/// // `ty::i32` does not match the symbol for the primitive `i32`
/// // ("ty::i32" != "i32").
/// #[derive(PartialEq)]
/// struct Struct {
/// field_1: &'static str,
/// field_2: ty::i32,
/// }
/// ```
///
/// For enums, the discriminant is compared first, then the rest of the fields.
///
/// # Panics
///
/// If called on static or all-fieldless enums/structs, which should not occur
/// during derive expansion.
fn get_substructure_equality_expr(
cx: &ExtCtxt<'_>,
span: Span,
substructure: &Substructure<'_>,
) -> P<Expr> {
use SubstructureFields::*;
match substructure.fields {
EnumMatching(.., fields) | Struct(.., fields) => {
let combine = move |acc, field| {
let rhs = get_field_equality_expr(cx, field);
if let Some(lhs) = acc {
// Combine the previous comparison with the current field
// using logical AND.
return Some(cx.expr_binary(field.span, BinOpKind::And, lhs, rhs));
}
// Start the chain with the first field's comparison.
Some(rhs)
};
// First compare scalar fields, then compound fields, combining all
// with logical AND.
return fields
.iter()
.filter(|field| !field.maybe_scalar)
.fold(fields.iter().filter(|field| field.maybe_scalar).fold(None, combine), combine)
// If there are no fields, treat as always equal.
.unwrap_or_else(|| cx.expr_bool(span, true));
}
EnumDiscr(disc, match_expr) => {
let lhs = get_field_equality_expr(cx, disc);
let Some(match_expr) = match_expr else {
return lhs;
};
// Compare the discriminant first (cheaper), then the rest of the
// fields.
return cx.expr_binary(disc.span, BinOpKind::And, lhs, match_expr.clone());
}
StaticEnum(..) => cx.dcx().span_bug(
span,
"unexpected static enum encountered during `derive(PartialEq)` expansion",
),
StaticStruct(..) => cx.dcx().span_bug(
span,
"unexpected static struct encountered during `derive(PartialEq)` expansion",
),
AllFieldlessEnum(..) => cx.dcx().span_bug(
span,
"unexpected all-fieldless enum encountered during `derive(PartialEq)` expansion",
),
}
}
/// Generates an equality comparison expression for a single struct or enum
/// field.
///
/// This function produces an AST expression that compares the `self` and
/// `other` values for a field using `==`. It removes any leading references
/// from both sides for readability. If the field is a block expression, it is
/// wrapped in parentheses to ensure valid syntax.
///
/// # Panics
///
/// Panics if there are not exactly two arguments to compare (should be `self`
/// and `other`).
fn get_field_equality_expr(cx: &ExtCtxt<'_>, field: &FieldInfo) -> P<Expr> {
let [rhs] = &field.other_selflike_exprs[..] else {
cx.dcx().span_bug(field.span, "not exactly 2 arguments in `derive(PartialEq)`");
};
cx.expr_binary(
field.span,
BinOpKind::Eq,
wrap_block_expr(cx, peel_refs(&field.self_expr)),
wrap_block_expr(cx, peel_refs(rhs)),
)
}
/// Removes all leading immutable references from an expression.
///
/// This is used to strip away any number of leading `&` from an expression
/// (e.g., `&&&T` becomes `T`). Only removes immutable references; mutable
/// references are preserved.
fn peel_refs(mut expr: &P<Expr>) -> P<Expr> {
while let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = &expr.kind {
expr = &inner;
}
expr.clone()
}
/// Wraps a block expression in parentheses to ensure valid AST in macro
/// expansion output.
///
/// If the given expression is a block, it is wrapped in parentheses; otherwise,
/// it is returned unchanged.
fn wrap_block_expr(cx: &ExtCtxt<'_>, expr: P<Expr>) -> P<Expr> {
if matches!(&expr.kind, ExprKind::Block(..)) {
return cx.expr_paren(expr.span, expr);
}
expr
}

View file

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

View file

@ -57,7 +57,7 @@ impl MultiItemModifier for BuiltinDerive {
let mut items = Vec::new();
match item {
Annotatable::Stmt(stmt) => {
if let ast::StmtKind::Item(item) = stmt.into_inner().kind {
if let ast::StmtKind::Item(item) = stmt.kind {
(self.0)(
ecx,
span,

View file

@ -0,0 +1,53 @@
use rustc_ast::ptr::P;
use rustc_ast::tokenstream::TokenStream;
use rustc_ast::{CoroutineKind, DUMMY_NODE_ID, Expr, ast, token};
use rustc_errors::PResult;
use rustc_expand::base::{self, DummyResult, ExpandResult, ExtCtxt, MacroExpanderResult};
use rustc_span::Span;
pub(crate) fn expand<'cx>(
cx: &'cx mut ExtCtxt<'_>,
sp: Span,
tts: TokenStream,
) -> MacroExpanderResult<'cx> {
let closure = match parse_closure(cx, sp, tts) {
Ok(parsed) => parsed,
Err(err) => {
return ExpandResult::Ready(DummyResult::any(sp, err.emit()));
}
};
ExpandResult::Ready(base::MacEager::expr(closure))
}
fn parse_closure<'a>(
cx: &mut ExtCtxt<'a>,
span: Span,
stream: TokenStream,
) -> PResult<'a, P<Expr>> {
let mut closure_parser = cx.new_parser_from_tts(stream);
let coroutine_kind = Some(CoroutineKind::Gen {
span,
closure_id: DUMMY_NODE_ID,
return_impl_trait_id: DUMMY_NODE_ID,
});
let mut closure = closure_parser.parse_expr()?;
match &mut closure.kind {
ast::ExprKind::Closure(c) => {
if let Some(kind) = c.coroutine_kind {
cx.dcx().span_err(kind.span(), "only plain closures allowed in `iter!`");
}
c.coroutine_kind = coroutine_kind;
if closure_parser.token != token::Eof {
closure_parser.unexpected()?;
}
Ok(closure)
}
_ => {
cx.dcx().span_err(closure.span, "`iter!` body must be a closure");
Err(closure_parser.unexpected().unwrap_err())
}
}
}

View file

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

View file

@ -30,14 +30,12 @@ fn parse_pat_ty<'a>(cx: &mut ExtCtxt<'a>, stream: TokenStream) -> PResult<'a, (P
let pat = pat_to_ty_pat(
cx,
parser
.parse_pat_no_top_guard(
None,
RecoverComma::No,
RecoverColon::No,
CommaRecoveryMode::EitherTupleOrPipe,
)?
.into_inner(),
*parser.parse_pat_no_top_guard(
None,
RecoverComma::No,
RecoverColon::No,
CommaRecoveryMode::EitherTupleOrPipe,
)?,
);
if parser.token != token::Eof {
@ -58,9 +56,9 @@ fn pat_to_ty_pat(cx: &mut ExtCtxt<'_>, pat: ast::Pat) -> P<TyPat> {
end.map(|value| P(AnonConst { id: DUMMY_NODE_ID, value })),
include_end,
),
ast::PatKind::Or(variants) => TyPatKind::Or(
variants.into_iter().map(|pat| pat_to_ty_pat(cx, pat.into_inner())).collect(),
),
ast::PatKind::Or(variants) => {
TyPatKind::Or(variants.into_iter().map(|pat| pat_to_ty_pat(cx, *pat)).collect())
}
ast::PatKind::Err(guar) => TyPatKind::Err(guar),
_ => TyPatKind::Err(cx.dcx().span_err(pat.span, "pattern not supported in pattern types")),
};

View file

@ -354,30 +354,28 @@ fn mk_decls(cx: &mut ExtCtxt<'_>, macros: &[ProcMacro]) -> P<ast::Item> {
})
.collect();
let decls_static = cx
.item_static(
let mut decls_static = cx.item_static(
span,
Ident::new(sym::_DECLS, span),
cx.ty_ref(
span,
Ident::new(sym::_DECLS, span),
cx.ty_ref(
cx.ty(
span,
cx.ty(
span,
ast::TyKind::Slice(
cx.ty_path(cx.path(span, vec![proc_macro, bridge, client, proc_macro_ty])),
),
ast::TyKind::Slice(
cx.ty_path(cx.path(span, vec![proc_macro, bridge, client, proc_macro_ty])),
),
None,
ast::Mutability::Not,
),
None,
ast::Mutability::Not,
cx.expr_array_ref(span, decls),
)
.map(|mut i| {
i.attrs.push(cx.attr_word(sym::rustc_proc_macro_decls, span));
i.attrs.push(cx.attr_word(sym::used, span));
i.attrs.push(cx.attr_nested_word(sym::allow, sym::deprecated, span));
i
});
),
ast::Mutability::Not,
cx.expr_array_ref(span, decls),
);
decls_static.attrs.extend([
cx.attr_word(sym::rustc_proc_macro_decls, span),
cx.attr_word(sym::used, span),
cx.attr_nested_word(sym::allow, sym::deprecated, span),
]);
let block = cx.expr_block(
cx.block(span, thin_vec![cx.stmt_item(span, krate), cx.stmt_item(span, decls_static)]),

View file

@ -40,7 +40,7 @@ pub(crate) fn expand_test_case(
let (mut item, is_stmt) = match anno_item {
Annotatable::Item(item) => (item, false),
Annotatable::Stmt(stmt) if let ast::StmtKind::Item(_) = stmt.kind => {
if let ast::StmtKind::Item(i) = stmt.into_inner().kind {
if let ast::StmtKind::Item(i) = stmt.kind {
(i, true)
} else {
unreachable!()
@ -120,11 +120,7 @@ pub(crate) fn expand_test_or_bench(
Annotatable::Item(i) => (i, false),
Annotatable::Stmt(stmt) if matches!(stmt.kind, ast::StmtKind::Item(_)) => {
// FIXME: Use an 'if let' guard once they are implemented
if let ast::StmtKind::Item(i) = stmt.into_inner().kind {
(i, true)
} else {
unreachable!()
}
if let ast::StmtKind::Item(i) = stmt.kind { (i, true) } else { unreachable!() }
}
other => {
not_testable_error(cx, attr_sp, None);
@ -381,10 +377,7 @@ pub(crate) fn expand_test_or_bench(
.into(),
),
);
test_const = test_const.map(|mut tc| {
tc.vis.kind = ast::VisibilityKind::Public;
tc
});
test_const.vis.kind = ast::VisibilityKind::Public;
// extern crate test
let test_extern =

View file

@ -213,11 +213,13 @@ pub(crate) fn copy_dir_recursively(from: &Path, to: &Path) {
if filename == "." || filename == ".." {
continue;
}
let src = from.join(&filename);
let dst = to.join(&filename);
if entry.metadata().unwrap().is_dir() {
fs::create_dir(to.join(&filename)).unwrap();
copy_dir_recursively(&from.join(&filename), &to.join(&filename));
fs::create_dir(&dst).unwrap_or_else(|e| panic!("failed to create {dst:?}: {e}"));
copy_dir_recursively(&src, &dst);
} else {
fs::copy(from.join(&filename), to.join(&filename)).unwrap();
fs::copy(&src, &dst).unwrap_or_else(|e| panic!("failed to copy {src:?}->{dst:?}: {e}"));
}
}
}

View file

@ -233,7 +233,7 @@ unsafe fn test_vaddvq_f32() {
#[cfg(target_arch = "aarch64")]
unsafe fn test_vrndnq_f32() {
// AArch64 llvm intrinsic: llvm.aarch64.neon.frintn.v4f32
// llvm intrinsic: llvm.roundeven.v4f32
let a = f32x4::from([0.1, -1.9, 4.5, 5.5]);
let e = f32x4::from([0., -2., 4., 6.]);
let r: f32x4 = transmute(vrndnq_f32(transmute(a)));

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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