Merge pull request #2740 from rust-lang/rustc-pull

Rustc pull update
This commit is contained in:
Tshepang Mbambo 2026-01-26 13:33:21 +02:00 committed by GitHub
commit 248ffb67dc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
834 changed files with 15927 additions and 10890 deletions

View file

@ -630,7 +630,7 @@ checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d"
[[package]]
name = "clippy"
version = "0.1.94"
version = "0.1.95"
dependencies = [
"anstream",
"askama",
@ -657,7 +657,7 @@ dependencies = [
[[package]]
name = "clippy_config"
version = "0.1.94"
version = "0.1.95"
dependencies = [
"clippy_utils",
"itertools",
@ -681,7 +681,7 @@ dependencies = [
[[package]]
name = "clippy_lints"
version = "0.1.94"
version = "0.1.95"
dependencies = [
"arrayvec",
"cargo_metadata 0.18.1",
@ -713,7 +713,7 @@ dependencies = [
[[package]]
name = "clippy_utils"
version = "0.1.94"
version = "0.1.95"
dependencies = [
"arrayvec",
"itertools",
@ -1117,7 +1117,7 @@ dependencies = [
[[package]]
name = "declare_clippy_lint"
version = "0.1.94"
version = "0.1.95"
[[package]]
name = "derive-where"
@ -3356,9 +3356,9 @@ dependencies = [
[[package]]
name = "rustc-demangle"
version = "0.1.26"
version = "0.1.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace"
checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d"
[[package]]
name = "rustc-hash"
@ -3865,7 +3865,6 @@ dependencies = [
"rustc_fluent_macro",
"rustc_hashes",
"rustc_index",
"rustc_lexer",
"rustc_lint_defs",
"rustc_macros",
"rustc_serialize",

View file

@ -62,9 +62,9 @@ Stabilized APIs
- [`<uN>::unchecked_shl`](https://doc.rust-lang.org/stable/std/primitive.usize.html#method.unchecked_shl)
- [`<uN>::unchecked_shr`](https://doc.rust-lang.org/stable/std/primitive.usize.html#method.unchecked_shr)
- [`<[T]>::as_array`](https://doc.rust-lang.org/stable/std/primitive.slice.html#method.as_array)
- [`<[T]>::as_array_mut`](https://doc.rust-lang.org/stable/std/primitive.slice.html#method.as_mut_array)
- [`<[T]>::as_mut_array`](https://doc.rust-lang.org/stable/std/primitive.slice.html#method.as_mut_array)
- [`<*const [T]>::as_array`](https://doc.rust-lang.org/stable/std/primitive.pointer.html#method.as_array)
- [`<*mut [T]>::as_array_mut`](https://doc.rust-lang.org/stable/std/primitive.pointer.html#method.as_mut_array)
- [`<*mut [T]>::as_mut_array`](https://doc.rust-lang.org/stable/std/primitive.pointer.html#method.as_mut_array)
- [`VecDeque::pop_front_if`](https://doc.rust-lang.org/stable/std/collections/struct.VecDeque.html#method.pop_front_if)
- [`VecDeque::pop_back_if`](https://doc.rust-lang.org/stable/std/collections/struct.VecDeque.html#method.pop_back_if)
- [`Duration::from_nanos_u128`](https://doc.rust-lang.org/stable/std/time/struct.Duration.html#method.from_nanos_u128)

View file

@ -27,6 +27,7 @@ pub enum CanonAbi {
C,
Rust,
RustCold,
RustPreserveNone,
/// An ABI that rustc does not know how to call or define.
Custom,
@ -54,7 +55,7 @@ pub enum CanonAbi {
impl CanonAbi {
pub fn is_rustic_abi(self) -> bool {
match self {
CanonAbi::Rust | CanonAbi::RustCold => true,
CanonAbi::Rust | CanonAbi::RustCold | CanonAbi::RustPreserveNone => true,
CanonAbi::C
| CanonAbi::Custom
| CanonAbi::Arm(_)
@ -74,6 +75,7 @@ impl fmt::Display for CanonAbi {
CanonAbi::C => ExternAbi::C { unwind: false },
CanonAbi::Rust => ExternAbi::Rust,
CanonAbi::RustCold => ExternAbi::RustCold,
CanonAbi::RustPreserveNone => ExternAbi::RustPreserveNone,
CanonAbi::Custom => ExternAbi::Custom,
CanonAbi::Arm(arm_call) => match arm_call {
ArmCall::Aapcs => ExternAbi::Aapcs { unwind: false },

View file

@ -42,6 +42,13 @@ pub enum ExternAbi {
/// in a platform-agnostic way.
RustInvalid,
/// Preserves no registers.
///
/// Note, that this ABI is not stable in the registers it uses, is intended as an optimization
/// and may fall-back to a more conservative calling convention if the backend does not support
/// forcing callers to save all registers.
RustPreserveNone,
/// 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,
@ -163,6 +170,7 @@ abi_impls! {
RustCall =><= "rust-call",
RustCold =><= "rust-cold",
RustInvalid =><= "rust-invalid",
RustPreserveNone =><= "rust-preserve-none",
Stdcall { unwind: false } =><= "stdcall",
Stdcall { unwind: true } =><= "stdcall-unwind",
System { unwind: false } =><= "system",
@ -243,7 +251,7 @@ impl ExternAbi {
/// - are subject to change between compiler versions
pub fn is_rustic_abi(self) -> bool {
use ExternAbi::*;
matches!(self, Rust | RustCall | RustCold)
matches!(self, Rust | RustCall | RustCold | RustPreserveNone)
}
/// Returns whether the ABI supports C variadics. This only controls whether we allow *imports*
@ -315,7 +323,8 @@ impl ExternAbi {
| Self::Thiscall { .. }
| Self::Vectorcall { .. }
| Self::SysV64 { .. }
| Self::Win64 { .. } => true,
| Self::Win64 { .. }
| Self::RustPreserveNone => true,
}
}
}

View file

@ -3626,6 +3626,7 @@ impl Item {
pub fn opt_generics(&self) -> Option<&Generics> {
match &self.kind {
ItemKind::ExternCrate(..)
| ItemKind::ConstBlock(_)
| ItemKind::Use(_)
| ItemKind::Mod(..)
| ItemKind::ForeignMod(_)
@ -3895,6 +3896,17 @@ impl ConstItemRhs {
}
}
#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
pub struct ConstBlockItem {
pub id: NodeId,
pub span: Span,
pub block: Box<Block>,
}
impl ConstBlockItem {
pub const IDENT: Ident = Ident { name: kw::Underscore, span: DUMMY_SP };
}
// Adding a new variant? Please update `test_item` in `tests/ui/macros/stringify.rs`.
#[derive(Clone, Encodable, Decodable, Debug)]
pub enum ItemKind {
@ -3914,6 +3926,11 @@ pub enum ItemKind {
///
/// E.g., `const FOO: i32 = 42;`.
Const(Box<ConstItem>),
/// A module-level const block.
/// Equivalent to `const _: () = const { ... };`.
///
/// E.g., `const { assert!(true) }`.
ConstBlock(ConstBlockItem),
/// A function declaration (`fn`).
///
/// E.g., `fn foo(bar: usize) -> usize { .. }`.
@ -3990,6 +4007,8 @@ impl ItemKind {
| ItemKind::MacroDef(ident, _)
| ItemKind::Delegation(box Delegation { ident, .. }) => Some(ident),
ItemKind::ConstBlock(_) => Some(ConstBlockItem::IDENT),
ItemKind::Use(_)
| ItemKind::ForeignMod(_)
| ItemKind::GlobalAsm(_)
@ -4003,9 +4022,9 @@ impl ItemKind {
pub fn article(&self) -> &'static str {
use ItemKind::*;
match self {
Use(..) | Static(..) | Const(..) | Fn(..) | Mod(..) | GlobalAsm(..) | TyAlias(..)
| Struct(..) | Union(..) | Trait(..) | TraitAlias(..) | MacroDef(..)
| Delegation(..) | DelegationMac(..) => "a",
Use(..) | Static(..) | Const(..) | ConstBlock(..) | Fn(..) | Mod(..)
| GlobalAsm(..) | TyAlias(..) | Struct(..) | Union(..) | Trait(..) | TraitAlias(..)
| MacroDef(..) | Delegation(..) | DelegationMac(..) => "a",
ExternCrate(..) | ForeignMod(..) | MacCall(..) | Enum(..) | Impl { .. } => "an",
}
}
@ -4016,6 +4035,7 @@ impl ItemKind {
ItemKind::Use(..) => "`use` import",
ItemKind::Static(..) => "static item",
ItemKind::Const(..) => "constant item",
ItemKind::ConstBlock(..) => "const block",
ItemKind::Fn(..) => "function",
ItemKind::Mod(..) => "module",
ItemKind::ForeignMod(..) => "extern block",
@ -4045,7 +4065,18 @@ impl ItemKind {
| Self::Trait(box Trait { generics, .. })
| Self::TraitAlias(box TraitAlias { generics, .. })
| Self::Impl(Impl { generics, .. }) => Some(generics),
_ => None,
Self::ExternCrate(..)
| Self::Use(..)
| Self::Static(..)
| Self::ConstBlock(..)
| Self::Mod(..)
| Self::ForeignMod(..)
| Self::GlobalAsm(..)
| Self::MacCall(..)
| Self::MacroDef(..)
| Self::Delegation(..)
| Self::DelegationMac(..) => None,
}
}
}

View file

@ -5,7 +5,6 @@
//! This API is completely unstable and subject to change.
// tidy-alphabetical-start
#![cfg_attr(bootstrap, feature(array_windows))]
#![doc(test(attr(deny(warnings), allow(internal_features))))]
#![feature(associated_type_defaults)]
#![feature(box_patterns)]

View file

@ -425,6 +425,7 @@ macro_rules! common_visitor_and_walkers {
ByRef,
Closure,
Const,
ConstBlockItem,
ConstItem,
ConstItemRhs,
Defaultness,
@ -825,6 +826,8 @@ macro_rules! common_visitor_and_walkers {
visit_visitable!($($mut)? vis, use_tree),
ItemKind::Static(item) =>
visit_visitable!($($mut)? vis, item),
ItemKind::ConstBlock(item) =>
visit_visitable!($($mut)? vis, item),
ItemKind::Const(item) =>
visit_visitable!($($mut)? vis, item),
ItemKind::Mod(safety, ident, mod_kind) =>

View file

@ -205,6 +205,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
| ItemKind::Use(..)
| ItemKind::Static(..)
| ItemKind::Const(..)
| ItemKind::ConstBlock(..)
| ItemKind::Mod(..)
| ItemKind::ForeignMod(..)
| ItemKind::GlobalAsm(..)
@ -282,8 +283,13 @@ impl<'hir> LoweringContext<'_, 'hir> {
self.lower_define_opaque(hir_id, define_opaque);
hir::ItemKind::Static(*m, ident, ty, body_id)
}
ItemKind::Const(box ast::ConstItem {
ident, generics, ty, rhs, define_opaque, ..
ItemKind::Const(box ConstItem {
defaultness: _,
ident,
generics,
ty,
rhs,
define_opaque,
}) => {
let ident = self.lower_ident(*ident);
let (generics, (ty, rhs)) = self.lower_generics(
@ -302,6 +308,19 @@ impl<'hir> LoweringContext<'_, 'hir> {
self.lower_define_opaque(hir_id, &define_opaque);
hir::ItemKind::Const(ident, generics, ty, rhs)
}
ItemKind::ConstBlock(ConstBlockItem { span, id, block }) => hir::ItemKind::Const(
self.lower_ident(ConstBlockItem::IDENT),
hir::Generics::empty(),
self.arena.alloc(self.ty_tup(DUMMY_SP, &[])),
hir::ConstItemRhs::Body({
let body = hir::Expr {
hir_id: self.lower_node_id(*id),
kind: hir::ExprKind::Block(self.lower_block(block, false), None),
span: self.lower_span(*span),
};
self.record_body(&[], body)
}),
),
ItemKind::Fn(box Fn {
sig: FnSig { decl, header, span: fn_sig_span },
ident,

View file

@ -95,6 +95,11 @@ pub fn extern_abi_stability(abi: ExternAbi) -> Result<(), UnstableAbi> {
ExternAbi::RustCold => {
Err(UnstableAbi { abi, feature: sym::rust_cold_cc, explain: GateReason::Experimental })
}
ExternAbi::RustPreserveNone => Err(UnstableAbi {
abi,
feature: sym::rust_preserve_none_cc,
explain: GateReason::Experimental,
}),
ExternAbi::RustInvalid => {
Err(UnstableAbi { abi, feature: sym::rustc_attrs, explain: GateReason::ImplDetail })
}

View file

@ -400,6 +400,7 @@ impl<'a> AstValidator<'a> {
CanonAbi::C
| CanonAbi::Rust
| CanonAbi::RustCold
| CanonAbi::RustPreserveNone
| CanonAbi::Arm(_)
| CanonAbi::X86(_) => { /* nothing to check */ }

View file

@ -537,6 +537,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
gate_all!(super_let, "`super let` is experimental");
gate_all!(frontmatter, "frontmatters are experimental");
gate_all!(coroutines, "coroutine syntax is experimental");
gate_all!(const_block_items, "const block items are experimental");
if !visitor.features.never_patterns() {
if let Some(spans) = spans.get(&sym::never_patterns) {

View file

@ -205,6 +205,17 @@ impl<'a> State<'a> {
define_opaque.as_deref(),
);
}
ast::ItemKind::ConstBlock(ast::ConstBlockItem { id: _, span: _, block }) => {
let ib = self.ibox(INDENT_UNIT);
self.word("const");
self.nbsp();
{
let cb = self.cbox(0);
let ib = self.ibox(0);
self.print_block_with_attrs(block, &[], cb, ib);
}
self.end(ib);
}
ast::ItemKind::Const(box ast::ConstItem {
defaultness,
ident,

View file

@ -360,7 +360,8 @@ fn parse_cfg_attr_internal<'a>(
) -> PResult<'a, (CfgEntry, Vec<(ast::AttrItem, Span)>)> {
// Parse cfg predicate
let pred_start = parser.token.span;
let meta = MetaItemOrLitParser::parse_single(parser, ShouldEmit::ErrorsAndLints)?;
let meta =
MetaItemOrLitParser::parse_single(parser, ShouldEmit::ErrorsAndLints { recover: true })?;
let pred_span = pred_start.with_hi(parser.token.span.hi());
let cfg_predicate = AttributeParser::parse_single_args(
@ -375,7 +376,7 @@ fn parse_cfg_attr_internal<'a>(
CRATE_NODE_ID,
Target::Crate,
features,
ShouldEmit::ErrorsAndLints,
ShouldEmit::ErrorsAndLints { recover: true },
&meta,
parse_cfg_entry,
&CFG_ATTR_TEMPLATE,

View file

@ -78,8 +78,9 @@ pub fn parse_cfg_select(
}
}
} else {
let meta = MetaItemOrLitParser::parse_single(p, ShouldEmit::ErrorsAndLints)
.map_err(|diag| diag.emit())?;
let meta =
MetaItemOrLitParser::parse_single(p, ShouldEmit::ErrorsAndLints { recover: true })
.map_err(|diag| diag.emit())?;
let cfg_span = meta.span();
let cfg = AttributeParser::parse_single_args(
sess,
@ -94,7 +95,7 @@ pub fn parse_cfg_select(
// Doesn't matter what the target actually is here.
Target::Crate,
features,
ShouldEmit::ErrorsAndLints,
ShouldEmit::ErrorsAndLints { recover: true },
&meta,
parse_cfg_entry,
&AttributeTemplate::default(),

View file

@ -717,3 +717,100 @@ impl<S: Stage> NoArgsAttributeParser<S> for EiiForeignItemParser {
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::ForeignFn)]);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::EiiForeignItem;
}
pub(crate) struct PatchableFunctionEntryParser;
impl<S: Stage> SingleAttributeParser<S> for PatchableFunctionEntryParser {
const PATH: &[Symbol] = &[sym::patchable_function_entry];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]);
const TEMPLATE: AttributeTemplate = template!(List: &["prefix_nops = m, entry_nops = n"]);
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
let Some(meta_item_list) = args.list() else {
cx.expected_list(cx.attr_span, args);
return None;
};
let mut prefix = None;
let mut entry = None;
if meta_item_list.len() == 0 {
cx.expected_list(meta_item_list.span, args);
return None;
}
let mut errored = false;
for item in meta_item_list.mixed() {
let Some(meta_item) = item.meta_item() else {
errored = true;
cx.expected_name_value(item.span(), None);
continue;
};
let Some(name_value_lit) = meta_item.args().name_value() else {
errored = true;
cx.expected_name_value(item.span(), None);
continue;
};
let attrib_to_write = match meta_item.ident().map(|ident| ident.name) {
Some(sym::prefix_nops) => {
// Duplicate prefixes are not allowed
if prefix.is_some() {
errored = true;
cx.duplicate_key(meta_item.path().span(), sym::prefix_nops);
continue;
}
&mut prefix
}
Some(sym::entry_nops) => {
// Duplicate entries are not allowed
if entry.is_some() {
errored = true;
cx.duplicate_key(meta_item.path().span(), sym::entry_nops);
continue;
}
&mut entry
}
_ => {
errored = true;
cx.expected_specific_argument(
meta_item.path().span(),
&[sym::prefix_nops, sym::entry_nops],
);
continue;
}
};
let rustc_ast::LitKind::Int(val, _) = name_value_lit.value_as_lit().kind else {
errored = true;
cx.expected_integer_literal(name_value_lit.value_span);
continue;
};
let Ok(val) = val.get().try_into() else {
errored = true;
cx.expected_integer_literal_in_range(
name_value_lit.value_span,
u8::MIN as isize,
u8::MAX as isize,
);
continue;
};
*attrib_to_write = Some(val);
}
if errored {
None
} else {
Some(AttributeKind::PatchableFunctionEntry {
prefix: prefix.unwrap_or(0),
entry: entry.unwrap_or(0),
})
}
}
}

View file

@ -1,4 +1,8 @@
use rustc_hir::attrs::WindowsSubsystemKind;
use rustc_hir::attrs::{CrateType, WindowsSubsystemKind};
use rustc_hir::lints::AttributeLintKind;
use rustc_session::lint::builtin::UNKNOWN_CRATE_TYPES;
use rustc_span::Symbol;
use rustc_span::edit_distance::find_best_match_for_name;
use super::prelude::*;
@ -26,6 +30,56 @@ impl<S: Stage> SingleAttributeParser<S> for CrateNameParser {
}
}
pub(crate) struct CrateTypeParser;
impl<S: Stage> CombineAttributeParser<S> for CrateTypeParser {
const PATH: &[Symbol] = &[sym::crate_type];
type Item = CrateType;
const CONVERT: ConvertFn<Self::Item> = |items, _| AttributeKind::CrateType(items);
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
const TEMPLATE: AttributeTemplate =
template!(NameValueStr: "crate type", "https://doc.rust-lang.org/reference/linkage.html");
fn extend(
cx: &mut AcceptContext<'_, '_, S>,
args: &ArgParser,
) -> impl IntoIterator<Item = Self::Item> {
let ArgParser::NameValue(n) = args else {
cx.expected_name_value(cx.attr_span, None);
return None;
};
let Some(crate_type) = n.value_as_str() else {
cx.expected_string_literal(n.value_span, Some(n.value_as_lit()));
return None;
};
let Ok(crate_type) = crate_type.try_into() else {
// We don't error on invalid `#![crate_type]` when not applied to a crate
if cx.shared.target == Target::Crate {
let candidate = find_best_match_for_name(
&CrateType::all_stable().iter().map(|(name, _)| *name).collect::<Vec<_>>(),
crate_type,
None,
);
cx.emit_lint(
UNKNOWN_CRATE_TYPES,
AttributeLintKind::CrateTypeUnknown {
span: n.value_span,
suggested: candidate,
},
n.value_span,
);
}
return None;
};
Some(crate_type)
}
}
pub(crate) struct RecursionLimitParser;
impl<S: Stage> SingleAttributeParser<S> for RecursionLimitParser {
@ -184,3 +238,39 @@ impl<S: Stage> SingleAttributeParser<S> for WindowsSubsystemParser {
Some(AttributeKind::WindowsSubsystem(kind, cx.attr_span))
}
}
pub(crate) struct PanicRuntimeParser;
impl<S: Stage> NoArgsAttributeParser<S> for PanicRuntimeParser {
const PATH: &[Symbol] = &[sym::panic_runtime];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::PanicRuntime;
}
pub(crate) struct NeedsPanicRuntimeParser;
impl<S: Stage> NoArgsAttributeParser<S> for NeedsPanicRuntimeParser {
const PATH: &[Symbol] = &[sym::needs_panic_runtime];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::NeedsPanicRuntime;
}
pub(crate) struct ProfilerRuntimeParser;
impl<S: Stage> NoArgsAttributeParser<S> for ProfilerRuntimeParser {
const PATH: &[Symbol] = &[sym::profiler_runtime];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::ProfilerRuntime;
}
pub(crate) struct NoBuiltinsParser;
impl<S: Stage> NoArgsAttributeParser<S> for NoBuiltinsParser {
const PATH: &[Symbol] = &[sym::no_builtins];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::NoBuiltins;
}

View file

@ -665,3 +665,12 @@ impl<S: Stage> NoArgsAttributeParser<S> for NeedsAllocatorParser {
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::NeedsAllocator;
}
pub(crate) struct CompilerBuiltinsParser;
impl<S: Stage> NoArgsAttributeParser<S> for CompilerBuiltinsParser {
const PATH: &[Symbol] = &[sym::compiler_builtins];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::CompilerBuiltins;
}

View file

@ -91,3 +91,25 @@ impl<S: Stage> SingleAttributeParser<S> for ShouldPanicParser {
})
}
}
pub(crate) struct RustcVarianceParser;
impl<S: Stage> NoArgsAttributeParser<S> for RustcVarianceParser {
const PATH: &[Symbol] = &[sym::rustc_variance];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
Allow(Target::Struct),
Allow(Target::Enum),
Allow(Target::Union),
]);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcVariance;
}
pub(crate) struct RustcVarianceOfOpaquesParser;
impl<S: Stage> NoArgsAttributeParser<S> for RustcVarianceOfOpaquesParser {
const PATH: &[Symbol] = &[sym::rustc_variance_of_opaques];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcVarianceOfOpaques;
}

View file

@ -94,12 +94,12 @@ impl<S: Stage> NoArgsAttributeParser<S> for DenyExplicitImplParser {
const CREATE: fn(Span) -> AttributeKind = AttributeKind::DenyExplicitImpl;
}
pub(crate) struct DoNotImplementViaObjectParser;
impl<S: Stage> NoArgsAttributeParser<S> for DoNotImplementViaObjectParser {
const PATH: &[Symbol] = &[sym::rustc_do_not_implement_via_object];
pub(crate) struct DynIncompatibleTraitParser;
impl<S: Stage> NoArgsAttributeParser<S> for DynIncompatibleTraitParser {
const PATH: &[Symbol] = &[sym::rustc_dyn_incompatible_trait];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Trait)]);
const CREATE: fn(Span) -> AttributeKind = AttributeKind::DoNotImplementViaObject;
const CREATE: fn(Span) -> AttributeKind = AttributeKind::DynIncompatibleTrait;
}
// Specialization

View file

@ -23,14 +23,15 @@ use crate::attributes::cfi_encoding::CfiEncodingParser;
use crate::attributes::codegen_attrs::{
ColdParser, CoverageParser, EiiForeignItemParser, ExportNameParser, ForceTargetFeatureParser,
NakedParser, NoMangleParser, ObjcClassParser, ObjcSelectorParser, OptimizeParser,
RustcPassIndirectlyInNonRusticAbisParser, SanitizeParser, TargetFeatureParser,
ThreadLocalParser, TrackCallerParser, UsedParser,
PatchableFunctionEntryParser, RustcPassIndirectlyInNonRusticAbisParser, SanitizeParser,
TargetFeatureParser, ThreadLocalParser, TrackCallerParser, UsedParser,
};
use crate::attributes::confusables::ConfusablesParser;
use crate::attributes::crate_level::{
CrateNameParser, MoveSizeLimitParser, NoCoreParser, NoMainParser, NoStdParser,
PatternComplexityLimitParser, RecursionLimitParser, RustcCoherenceIsCoreParser,
TypeLengthLimitParser, WindowsSubsystemParser,
CrateNameParser, CrateTypeParser, MoveSizeLimitParser, NeedsPanicRuntimeParser,
NoBuiltinsParser, NoCoreParser, NoMainParser, NoStdParser, PanicRuntimeParser,
PatternComplexityLimitParser, ProfilerRuntimeParser, RecursionLimitParser,
RustcCoherenceIsCoreParser, TypeLengthLimitParser, WindowsSubsystemParser,
};
use crate::attributes::debugger::DebuggerViualizerParser;
use crate::attributes::deprecation::DeprecationParser;
@ -40,8 +41,9 @@ use crate::attributes::dummy::DummyParser;
use crate::attributes::inline::{InlineParser, RustcForceInlineParser};
use crate::attributes::instruction_set::InstructionSetParser;
use crate::attributes::link_attrs::{
ExportStableParser, FfiConstParser, FfiPureParser, LinkNameParser, LinkOrdinalParser,
LinkParser, LinkSectionParser, LinkageParser, NeedsAllocatorParser, StdInternalSymbolParser,
CompilerBuiltinsParser, ExportStableParser, FfiConstParser, FfiPureParser, LinkNameParser,
LinkOrdinalParser, LinkParser, LinkSectionParser, LinkageParser, NeedsAllocatorParser,
StdInternalSymbolParser,
};
use crate::attributes::lint_helpers::{
AsPtrParser, AutomaticallyDerivedParser, PassByValueParser, PubTransparentParser,
@ -85,11 +87,13 @@ use crate::attributes::semantics::MayDangleParser;
use crate::attributes::stability::{
BodyStabilityParser, ConstStabilityIndirectParser, ConstStabilityParser, StabilityParser,
};
use crate::attributes::test_attrs::{IgnoreParser, ShouldPanicParser};
use crate::attributes::test_attrs::{
IgnoreParser, RustcVarianceOfOpaquesParser, RustcVarianceParser, ShouldPanicParser,
};
use crate::attributes::traits::{
AllowIncoherentImplParser, CoinductiveParser, DenyExplicitImplParser,
DoNotImplementViaObjectParser, FundamentalParser, MarkerParser, ParenSugarParser,
PointeeParser, SkipDuringMethodDispatchParser, SpecializationTraitParser, TypeConstParser,
DynIncompatibleTraitParser, FundamentalParser, MarkerParser, ParenSugarParser, PointeeParser,
SkipDuringMethodDispatchParser, SpecializationTraitParser, TypeConstParser,
UnsafeSpecializationMarkerParser,
};
use crate::attributes::transparency::TransparencyParser;
@ -103,18 +107,18 @@ type GroupType<S> = LazyLock<GroupTypeInner<S>>;
pub(super) struct GroupTypeInner<S: Stage> {
pub(super) accepters: BTreeMap<&'static [Symbol], Vec<GroupTypeInnerAccept<S>>>,
pub(super) finalizers: Vec<FinalizeFn<S>>,
}
pub(super) struct GroupTypeInnerAccept<S: Stage> {
pub(super) template: AttributeTemplate,
pub(super) accept_fn: AcceptFn<S>,
pub(super) allowed_targets: AllowedTargets,
pub(super) finalizer: FinalizeFn<S>,
}
type AcceptFn<S> =
pub(crate) type AcceptFn<S> =
Box<dyn for<'sess, 'a> Fn(&mut AcceptContext<'_, 'sess, S>, &ArgParser) + Send + Sync>;
type FinalizeFn<S> =
pub(crate) type FinalizeFn<S> =
Box<dyn Send + Sync + Fn(&mut FinalizeContext<'_, '_, S>) -> Option<AttributeKind>>;
macro_rules! attribute_parsers {
@ -142,8 +146,7 @@ macro_rules! attribute_parsers {
@[$stage: ty] pub(crate) static $name: ident = [$($names: ty),* $(,)?];
) => {
pub(crate) static $name: GroupType<$stage> = LazyLock::new(|| {
let mut accepts = BTreeMap::<_, Vec<GroupTypeInnerAccept<$stage>>>::new();
let mut finalizes = Vec::<FinalizeFn<$stage>>::new();
let mut accepters = BTreeMap::<_, Vec<GroupTypeInnerAccept<$stage>>>::new();
$(
{
thread_local! {
@ -151,7 +154,7 @@ macro_rules! attribute_parsers {
};
for (path, template, accept_fn) in <$names>::ATTRIBUTES {
accepts.entry(*path).or_default().push(GroupTypeInnerAccept {
accepters.entry(*path).or_default().push(GroupTypeInnerAccept {
template: *template,
accept_fn: Box::new(|cx, args| {
STATE_OBJECT.with_borrow_mut(|s| {
@ -159,17 +162,16 @@ macro_rules! attribute_parsers {
})
}),
allowed_targets: <$names as crate::attributes::AttributeParser<$stage>>::ALLOWED_TARGETS,
finalizer: Box::new(|cx| {
let state = STATE_OBJECT.take();
state.finalize(cx)
}),
});
}
finalizes.push(Box::new(|cx| {
let state = STATE_OBJECT.take();
state.finalize(cx)
}));
}
)*
GroupTypeInner { accepters:accepts, finalizers:finalizes }
GroupTypeInner { accepters }
});
};
}
@ -191,6 +193,7 @@ attribute_parsers!(
// tidy-alphabetical-start
Combine<AllowConstFnUnstableParser>,
Combine<AllowInternalUnstableParser>,
Combine<CrateTypeParser>,
Combine<DebuggerViualizerParser>,
Combine<ForceTargetFeatureParser>,
Combine<LinkParser>,
@ -223,6 +226,7 @@ attribute_parsers!(
Single<ObjcClassParser>,
Single<ObjcSelectorParser>,
Single<OptimizeParser>,
Single<PatchableFunctionEntryParser>,
Single<PathAttributeParser>,
Single<PatternComplexityLimitParser>,
Single<ProcMacroDeriveParser>,
@ -250,11 +254,12 @@ attribute_parsers!(
Single<WithoutArgs<AutomaticallyDerivedParser>>,
Single<WithoutArgs<CoinductiveParser>>,
Single<WithoutArgs<ColdParser>>,
Single<WithoutArgs<CompilerBuiltinsParser>>,
Single<WithoutArgs<ConstContinueParser>>,
Single<WithoutArgs<ConstStabilityIndirectParser>>,
Single<WithoutArgs<CoroutineParser>>,
Single<WithoutArgs<DenyExplicitImplParser>>,
Single<WithoutArgs<DoNotImplementViaObjectParser>>,
Single<WithoutArgs<DynIncompatibleTraitParser>>,
Single<WithoutArgs<EiiForeignItemParser>>,
Single<WithoutArgs<ExportStableParser>>,
Single<WithoutArgs<FfiConstParser>>,
@ -265,6 +270,8 @@ attribute_parsers!(
Single<WithoutArgs<MarkerParser>>,
Single<WithoutArgs<MayDangleParser>>,
Single<WithoutArgs<NeedsAllocatorParser>>,
Single<WithoutArgs<NeedsPanicRuntimeParser>>,
Single<WithoutArgs<NoBuiltinsParser>>,
Single<WithoutArgs<NoCoreParser>>,
Single<WithoutArgs<NoImplicitPreludeParser>>,
Single<WithoutArgs<NoLinkParser>>,
@ -272,12 +279,14 @@ attribute_parsers!(
Single<WithoutArgs<NoMangleParser>>,
Single<WithoutArgs<NoStdParser>>,
Single<WithoutArgs<NonExhaustiveParser>>,
Single<WithoutArgs<PanicRuntimeParser>>,
Single<WithoutArgs<ParenSugarParser>>,
Single<WithoutArgs<PassByValueParser>>,
Single<WithoutArgs<PinV2Parser>>,
Single<WithoutArgs<PointeeParser>>,
Single<WithoutArgs<ProcMacroAttributeParser>>,
Single<WithoutArgs<ProcMacroParser>>,
Single<WithoutArgs<ProfilerRuntimeParser>>,
Single<WithoutArgs<PubTransparentParser>>,
Single<WithoutArgs<RustcAllocatorParser>>,
Single<WithoutArgs<RustcAllocatorZeroedParser>>,
@ -300,6 +309,8 @@ attribute_parsers!(
Single<WithoutArgs<RustcPassIndirectlyInNonRusticAbisParser>>,
Single<WithoutArgs<RustcReallocatorParser>>,
Single<WithoutArgs<RustcShouldNotBeCalledOnConstItems>>,
Single<WithoutArgs<RustcVarianceOfOpaquesParser>>,
Single<WithoutArgs<RustcVarianceParser>>,
Single<WithoutArgs<SpecializationTraitParser>>,
Single<WithoutArgs<StdInternalSymbolParser>>,
Single<WithoutArgs<ThreadLocalParser>>,
@ -370,7 +381,7 @@ impl Stage for Late {
}
fn should_emit(&self) -> ShouldEmit {
ShouldEmit::ErrorsAndLints
ShouldEmit::ErrorsAndLints { recover: true }
}
}
@ -427,7 +438,7 @@ impl<'f, 'sess: 'f, S: Stage> SharedContext<'f, 'sess, S> {
pub(crate) fn emit_lint(&mut self, lint: &'static Lint, kind: AttributeLintKind, span: Span) {
if !matches!(
self.stage.should_emit(),
ShouldEmit::ErrorsAndLints | ShouldEmit::EarlyFatal { also_emit_lints: true }
ShouldEmit::ErrorsAndLints { .. } | ShouldEmit::EarlyFatal { also_emit_lints: true }
) {
return;
}
@ -503,6 +514,18 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
self.emit_parse_error(span, AttributeParseErrorReason::ExpectedIntegerLiteral)
}
pub(crate) fn expected_integer_literal_in_range(
&self,
span: Span,
lower_bound: isize,
upper_bound: isize,
) -> ErrorGuaranteed {
self.emit_parse_error(
span,
AttributeParseErrorReason::ExpectedIntegerLiteralInRange { lower_bound, upper_bound },
)
}
pub(crate) fn expected_list(&self, span: Span, args: &ArgParser) -> ErrorGuaranteed {
let span = match args {
ArgParser::NoArgs => span,
@ -742,9 +765,18 @@ pub enum ShouldEmit {
EarlyFatal { also_emit_lints: bool },
/// The operation will emit errors and lints.
/// This is usually what you need.
ErrorsAndLints,
/// The operation will emit *not* errors and lints.
/// Use this if you are *sure* that this operation will be called at a different time with `ShouldEmit::ErrorsAndLints`.
ErrorsAndLints {
/// Whether [`ArgParser`] will attempt to recover from errors.
///
/// If true, it will attempt to recover from bad input (like an invalid literal). Setting
/// this to false will instead return early, and not raise errors except at the top level
/// (in [`ArgParser::from_attr_args`]).
recover: bool,
},
/// The operation will *not* emit errors and lints.
///
/// The parser can still call `delay_bug`, so you *must* ensure that this operation will also be
/// called with `ShouldEmit::ErrorsAndLints`.
Nothing,
}
@ -753,7 +785,7 @@ impl ShouldEmit {
match self {
ShouldEmit::EarlyFatal { .. } if diag.level() == Level::DelayedBug => diag.emit(),
ShouldEmit::EarlyFatal { .. } => diag.upgrade_to_fatal().emit(),
ShouldEmit::ErrorsAndLints => diag.emit(),
ShouldEmit::ErrorsAndLints { .. } => diag.emit(),
ShouldEmit::Nothing => diag.delay_as_bug(),
}
}

View file

@ -12,7 +12,7 @@ use rustc_session::Session;
use rustc_session::lint::{BuiltinLintDiag, LintId};
use rustc_span::{DUMMY_SP, Span, Symbol, sym};
use crate::context::{AcceptContext, FinalizeContext, SharedContext, Stage};
use crate::context::{AcceptContext, FinalizeContext, FinalizeFn, SharedContext, Stage};
use crate::early_parsed::{EARLY_PARSED_ATTRIBUTES, EarlyParsedState};
use crate::parser::{ArgParser, PathParser, RefPathParser};
use crate::session_diagnostics::ParsedDescription;
@ -270,6 +270,8 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
let mut attr_paths: Vec<RefPathParser<'_>> = Vec::new();
let mut early_parsed_state = EarlyParsedState::default();
let mut finalizers: Vec<&FinalizeFn<S>> = Vec::with_capacity(attrs.len());
for attr in attrs {
// If we're only looking for a single attribute, skip all the ones we don't care about.
if let Some(expected) = self.parse_only {
@ -383,6 +385,8 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
};
(accept.accept_fn)(&mut cx, &args);
finalizers.push(&accept.finalizer);
if !matches!(cx.stage.should_emit(), ShouldEmit::Nothing) {
Self::check_target(&accept.allowed_targets, target, &mut cx);
}
@ -417,7 +421,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
}
early_parsed_state.finalize_early_parsed_attributes(&mut attributes);
for f in &S::parsers().finalizers {
for f in &finalizers {
if let Some(attr) = f(&mut FinalizeContext {
shared: SharedContext { cx: self, target_span, target, emit_lint: &mut emit_lint },
all_attrs: &attr_paths,

View file

@ -16,7 +16,7 @@ use rustc_errors::{Diag, PResult};
use rustc_hir::{self as hir, AttrPath};
use rustc_parse::exp;
use rustc_parse::parser::{ForceCollect, Parser, PathStyle, token_descr};
use rustc_session::errors::{create_lit_error, report_lit_error};
use rustc_session::errors::create_lit_error;
use rustc_session::parse::ParseSess;
use rustc_span::{Ident, Span, Symbol, sym};
use thin_vec::ThinVec;
@ -113,16 +113,29 @@ impl ArgParser {
Some(match value {
AttrArgs::Empty => Self::NoArgs,
AttrArgs::Delimited(args) => {
// The arguments of rustc_dummy and diagnostic::do_not_recommend are not validated
// if the arguments are delimited.
// See https://doc.rust-lang.org/reference/attributes/diagnostics.html#r-attributes.diagnostic.namespace.unknown-invalid-syntax
if parts == &[sym::rustc_dummy]
|| parts == &[sym::diagnostic, sym::do_not_recommend]
{
return Some(ArgParser::List(MetaItemListParser {
sub_parsers: ThinVec::new(),
span: args.dspan.entire(),
}));
// Diagnostic attributes can't error if they encounter non meta item syntax.
// However, the current syntax for diagnostic attributes is meta item syntax.
// Therefore we can substitute with a dummy value on invalid syntax.
if matches!(parts, [sym::rustc_dummy] | [sym::diagnostic, ..]) {
match MetaItemListParser::new(
&args.tokens,
args.dspan.entire(),
psess,
ShouldEmit::ErrorsAndLints { recover: false },
) {
Ok(p) => return Some(ArgParser::List(p)),
Err(e) => {
// We can just dispose of the diagnostic and not bother with a lint,
// because this will look like `#[diagnostic::attr()]` was used. This
// is invalid for all diagnostic attrs, so a lint explaining the proper
// form will be issued later.
e.cancel();
return Some(ArgParser::List(MetaItemListParser {
sub_parsers: ThinVec::new(),
span: args.dspan.entire(),
}));
}
}
}
if args.delim != Delimiter::Parenthesis {
@ -141,7 +154,9 @@ impl ArgParser {
}
AttrArgs::Eq { eq_span, expr } => Self::NameValue(NameValueParser {
eq_span: *eq_span,
value: expr_to_lit(psess, &expr, expr.span, should_emit)?,
value: expr_to_lit(psess, &expr, expr.span, should_emit)
.map_err(|e| should_emit.emit_err(e))
.ok()??,
value_span: expr.span,
}),
})
@ -336,58 +351,53 @@ impl NameValueParser {
}
}
fn expr_to_lit(
psess: &ParseSess,
fn expr_to_lit<'sess>(
psess: &'sess ParseSess,
expr: &Expr,
span: Span,
should_emit: ShouldEmit,
) -> Option<MetaItemLit> {
) -> PResult<'sess, Option<MetaItemLit>> {
if let ExprKind::Lit(token_lit) = expr.kind {
let res = MetaItemLit::from_token_lit(token_lit, expr.span);
match res {
Ok(lit) => {
if token_lit.suffix.is_some() {
should_emit.emit_err(
psess.dcx().create_err(SuffixedLiteralInAttribute { span: lit.span }),
);
None
Err(psess.dcx().create_err(SuffixedLiteralInAttribute { span: lit.span }))
} else {
if !lit.kind.is_unsuffixed() {
// Emit error and continue, we can still parse the attribute as if the suffix isn't there
should_emit.emit_err(
psess.dcx().create_err(SuffixedLiteralInAttribute { span: lit.span }),
);
if lit.kind.is_unsuffixed() {
Ok(Some(lit))
} else {
Err(psess.dcx().create_err(SuffixedLiteralInAttribute { span: lit.span }))
}
Some(lit)
}
}
Err(err) => {
let guar = report_lit_error(psess, err, token_lit, expr.span);
let lit = MetaItemLit {
symbol: token_lit.symbol,
suffix: token_lit.suffix,
kind: LitKind::Err(guar),
span: expr.span,
};
Some(lit)
let err = create_lit_error(psess, err, token_lit, expr.span);
if matches!(should_emit, ShouldEmit::ErrorsAndLints { recover: false }) {
Err(err)
} else {
let lit = MetaItemLit {
symbol: token_lit.symbol,
suffix: token_lit.suffix,
kind: LitKind::Err(err.emit()),
span: expr.span,
};
Ok(Some(lit))
}
}
}
} else {
if matches!(should_emit, ShouldEmit::Nothing) {
return None;
return Ok(None);
}
// Example cases:
// - `#[foo = 1+1]`: results in `ast::ExprKind::BinOp`.
// - `#[foo = include_str!("nonexistent-file.rs")]`:
// results in `ast::ExprKind::Err`. In that case we delay
// the error because an earlier error will have already
// been reported.
// results in `ast::ExprKind::Err`.
let msg = "attribute value must be a literal";
let err = psess.dcx().struct_span_err(span, msg);
should_emit.emit_err(err);
None
Err(err)
}
}
@ -420,9 +430,12 @@ impl<'a, 'sess> MetaItemListParserContext<'a, 'sess> {
if !lit.kind.is_unsuffixed() {
// Emit error and continue, we can still parse the attribute as if the suffix isn't there
self.should_emit.emit_err(
self.parser.dcx().create_err(SuffixedLiteralInAttribute { span: lit.span }),
);
let err = self.parser.dcx().create_err(SuffixedLiteralInAttribute { span: lit.span });
if matches!(self.should_emit, ShouldEmit::ErrorsAndLints { recover: false }) {
return Err(err);
} else {
self.should_emit.emit_err(err)
};
}
Ok(lit)

View file

@ -525,6 +525,10 @@ pub(crate) enum AttributeParseErrorReason<'a> {
byte_string: Option<Span>,
},
ExpectedIntegerLiteral,
ExpectedIntegerLiteralInRange {
lower_bound: isize,
upper_bound: isize,
},
ExpectedAtLeastOneArgument,
ExpectedSingleArgument,
ExpectedList,
@ -596,6 +600,17 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError<'_> {
AttributeParseErrorReason::ExpectedIntegerLiteral => {
diag.span_label(self.span, "expected an integer literal here");
}
AttributeParseErrorReason::ExpectedIntegerLiteralInRange {
lower_bound,
upper_bound,
} => {
diag.span_label(
self.span,
format!(
"expected an integer literal in the range of {lower_bound}..={upper_bound}"
),
);
}
AttributeParseErrorReason::ExpectedSingleArgument => {
diag.span_label(self.span, "expected a single argument here");
diag.code(E0805);

View file

@ -45,7 +45,6 @@ pub(super) fn convert_typeck_constraints<'tcx>(
{
localize_statement_constraint(
tcx,
body,
stmt,
&outlives_constraint,
point,
@ -74,7 +73,6 @@ pub(super) fn convert_typeck_constraints<'tcx>(
/// needed CFG `from`-`to` intra-block nodes.
fn localize_statement_constraint<'tcx>(
tcx: TyCtxt<'tcx>,
body: &Body<'tcx>,
stmt: &Statement<'tcx>,
outlives_constraint: &OutlivesConstraint<'tcx>,
current_point: PointIndex,
@ -114,28 +112,22 @@ fn localize_statement_constraint<'tcx>(
},
"there should be no common regions between the LHS and RHS of an assignment"
);
let lhs_ty = body.local_decls[lhs.local].ty;
let successor_point = current_point;
compute_constraint_direction(
tcx,
outlives_constraint,
&lhs_ty,
current_point,
successor_point,
universal_regions,
)
}
_ => {
// For the other cases, we localize an outlives constraint to where it arises.
LocalizedOutlivesConstraint {
source: outlives_constraint.sup,
from: current_point,
target: outlives_constraint.sub,
to: current_point,
}
// Assignments should be the only statement that can both generate constraints that
// apply on entry (specific to the RHS place) *and* others that only apply on exit (the
// subset of RHS regions that actually flow into the LHS): i.e., where midpoints would
// be used to ensure the former happen before the latter, within the same MIR Location.
}
}
// We generally localize an outlives constraint to where it arises.
LocalizedOutlivesConstraint {
source: outlives_constraint.sup,
from: current_point,
target: outlives_constraint.sub,
to: current_point,
}
}
/// For a given outlives constraint arising from a MIR terminator, localize the constraint with the
@ -150,14 +142,12 @@ fn localize_terminator_constraint<'tcx>(
universal_regions: &UniversalRegions<'tcx>,
) -> LocalizedOutlivesConstraint {
// FIXME: check if other terminators need the same handling as `Call`s, in particular
// Assert/Yield/Drop. A handful of tests are failing with Drop related issues, as well as some
// coroutine tests, and that may be why.
// Assert/Yield/Drop.
match &terminator.kind {
// FIXME: also handle diverging calls.
TerminatorKind::Call { destination, target: Some(target), .. } => {
// Calls are similar to assignments, and thus follow the same pattern. If there is a
// target for the call we also relate what flows into the destination here to entry to
// that successor.
// If there is a target for the call we also relate what flows into the destination here
// to entry to that successor.
let destination_ty = destination.ty(&body.local_decls, tcx);
let successor_location = Location { block: *target, statement_index: 0 };
let successor_point = liveness.point_from_location(successor_location);

View file

@ -40,8 +40,11 @@ fn parse_cfg(cx: &ExtCtxt<'_>, span: Span, tts: TokenStream) -> Result<CfgEntry,
return Err(cx.dcx().emit_err(errors::RequiresCfgPattern { span }));
}
let meta = MetaItemOrLitParser::parse_single(&mut parser, ShouldEmit::ErrorsAndLints)
.map_err(|diag| diag.emit())?;
let meta = MetaItemOrLitParser::parse_single(
&mut parser,
ShouldEmit::ErrorsAndLints { recover: true },
)
.map_err(|diag| diag.emit())?;
let cfg = AttributeParser::parse_single_args(
cx.sess,
span,
@ -55,7 +58,7 @@ fn parse_cfg(cx: &ExtCtxt<'_>, span: Span, tts: TokenStream) -> Result<CfgEntry,
// Doesn't matter what the target actually is here.
Target::Crate,
Some(cx.ecfg.features),
ShouldEmit::ErrorsAndLints,
ShouldEmit::ErrorsAndLints { recover: true },
&meta,
parse_cfg_entry,
&CFG_TEMPLATE,

View file

@ -9,7 +9,7 @@ use rustc_expand::base::{Annotatable, ExtCtxt};
use rustc_expand::config::StripUnconfigured;
use rustc_expand::configure;
use rustc_feature::Features;
use rustc_parse::parser::{ForceCollect, Parser};
use rustc_parse::parser::{AllowConstBlockItems, ForceCollect, Parser};
use rustc_session::Session;
use rustc_span::{Span, sym};
use smallvec::SmallVec;
@ -110,7 +110,8 @@ impl CfgEval<'_> {
let res: PResult<'_, Annotatable> = try {
match annotatable {
Annotatable::Item(_) => {
let item = parser.parse_item(ForceCollect::Yes)?.unwrap();
let item =
parser.parse_item(ForceCollect::Yes, AllowConstBlockItems::Yes)?.unwrap();
Annotatable::Item(self.flat_map_item(item).pop().unwrap())
}
Annotatable::AssocItem(_, ctxt) => {

View file

@ -986,16 +986,6 @@ impl<'a> MethodDef<'a> {
f(cx, span, &substructure)
}
fn get_ret_ty(
&self,
cx: &ExtCtxt<'_>,
trait_: &TraitDef<'_>,
generics: &Generics,
type_ident: Ident,
) -> Box<ast::Ty> {
self.ret_ty.to_ty(cx, trait_.span, type_ident, generics)
}
fn is_static(&self) -> bool {
!self.explicit_self
}
@ -1068,10 +1058,14 @@ impl<'a> MethodDef<'a> {
self_arg.into_iter().chain(nonself_args).collect()
};
let ret_type = self.get_ret_ty(cx, trait_, generics, type_ident);
let ret_type = if let Ty::Unit = &self.ret_ty {
ast::FnRetTy::Default(span)
} else {
ast::FnRetTy::Ty(self.ret_ty.to_ty(cx, span, type_ident, generics))
};
let method_ident = Ident::new(self.name, span);
let fn_decl = cx.fn_decl(args, ast::FnRetTy::Ty(ret_type));
let fn_decl = cx.fn_decl(args, ret_type);
let body_block = body.into_block(cx, span);
let trait_lo_sp = span.shrink_to_lo();

View file

@ -13,7 +13,7 @@ use rustc_expand::base::{
};
use rustc_expand::module::DirOwnership;
use rustc_parse::lexer::StripTokens;
use rustc_parse::parser::ForceCollect;
use rustc_parse::parser::{AllowConstBlockItems, ForceCollect};
use rustc_parse::{new_parser_from_file, unwrap_or_emit_fatal, utf8_error};
use rustc_session::lint::builtin::INCOMPLETE_INCLUDE;
use rustc_session::parse::ParseSess;
@ -168,7 +168,7 @@ pub(crate) fn expand_include<'cx>(
));
let mut ret = SmallVec::new();
loop {
match p.parse_item(ForceCollect::No) {
match p.parse_item(ForceCollect::No, AllowConstBlockItems::Yes) {
Err(err) => {
err.emit();
break;

View file

@ -56,6 +56,9 @@ pub(crate) fn conv_to_call_conv(
CanonAbi::Rust | CanonAbi::C => default_call_conv,
CanonAbi::RustCold => CallConv::Cold,
// Cranelift doesn't currently have anything for this.
CanonAbi::RustPreserveNone => default_call_conv,
// Functions with this calling convention can only be called from assembly, but it is
// possible to declare an `extern "custom"` block, so the backend still needs a calling
// convention for declaring foreign functions.

View file

@ -242,7 +242,7 @@ impl DebugContext {
let generics = tcx.generics_of(enclosing_fn_def_id);
let args = instance.args.truncate_to(tcx, generics);
type_names::push_generic_params(
type_names::push_generic_args(
tcx,
tcx.normalize_erasing_regions(ty::TypingEnv::fully_monomorphized(), args),
&mut name,

View file

@ -1506,7 +1506,7 @@ fn codegen_regular_intrinsic_call<'tcx>(
}
// FIXME implement variadics in cranelift
sym::va_copy | sym::va_arg | sym::va_end => {
sym::va_arg | sym::va_end => {
fx.tcx.dcx().span_fatal(
source_info.span,
"Defining variadic functions is not yet supported by Cranelift",

View file

@ -348,6 +348,31 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
ret.write_cvalue(fx, ret_lane);
}
sym::simd_splat => {
intrinsic_args!(fx, args => (value); intrinsic);
if !ret.layout().ty.is_simd() {
report_simd_type_validation_error(fx, intrinsic, span, ret.layout().ty);
return;
}
let (lane_count, lane_ty) = ret.layout().ty.simd_size_and_type(fx.tcx);
if value.layout().ty != lane_ty {
fx.tcx.dcx().span_fatal(
span,
format!(
"[simd_splat] expected element type {lane_ty:?}, got {got:?}",
got = value.layout().ty
),
);
}
for i in 0..lane_count {
let ret_lane = ret.place_lane(fx, i.into());
ret_lane.write_cvalue(fx, value);
}
}
sym::simd_neg
| sym::simd_bswap
| sym::simd_bitreverse

View file

@ -243,6 +243,8 @@ impl<'gcc, 'tcx> FnAbiGccExt<'gcc, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
pub fn conv_to_fn_attribute<'gcc>(conv: CanonAbi, arch: &Arch) -> Option<FnAttribute<'gcc>> {
let attribute = match conv {
CanonAbi::C | CanonAbi::Rust => return None,
// gcc/gccjit does not have anything for this.
CanonAbi::RustPreserveNone => return None,
CanonAbi::RustCold => FnAttribute::Cold,
// Functions with this calling convention can only be called from assembly, but it is
// possible to declare an `extern "custom"` block, so the backend still needs a calling

View file

@ -26,11 +26,11 @@ use std::sync::atomic::Ordering;
use gccjit::{Context, OutputKind};
use object::read::archive::ArchiveFile;
use rustc_codegen_ssa::back::lto::{SerializedModule, ThinModule, ThinShared};
use rustc_codegen_ssa::back::write::{CodegenContext, FatLtoInput};
use rustc_codegen_ssa::back::write::{CodegenContext, FatLtoInput, SharedEmitter};
use rustc_codegen_ssa::traits::*;
use rustc_codegen_ssa::{ModuleCodegen, ModuleKind, looks_like_rust_object_file};
use rustc_data_structures::memmap::Mmap;
use rustc_errors::DiagCtxtHandle;
use rustc_errors::{DiagCtxt, DiagCtxtHandle};
use rustc_log::tracing::info;
use rustc_middle::bug;
use rustc_middle::dep_graph::WorkProduct;
@ -112,10 +112,11 @@ fn save_as_file(obj: &[u8], path: &Path) -> Result<(), LtoBitcodeFromRlib> {
/// for further optimization.
pub(crate) fn run_fat(
cgcx: &CodegenContext<GccCodegenBackend>,
shared_emitter: &SharedEmitter,
each_linked_rlib_for_lto: &[PathBuf],
modules: Vec<FatLtoInput<GccCodegenBackend>>,
) -> ModuleCodegen<GccContext> {
let dcx = cgcx.create_dcx();
let dcx = DiagCtxt::new(Box::new(shared_emitter.clone()));
let dcx = dcx.handle();
let lto_data = prepare_lto(cgcx, each_linked_rlib_for_lto, dcx);
/*let symbols_below_threshold =
@ -283,14 +284,13 @@ impl ModuleBufferMethods for ModuleBuffer {
/// can simply be copied over from the incr. comp. cache.
pub(crate) fn run_thin(
cgcx: &CodegenContext<GccCodegenBackend>,
dcx: DiagCtxtHandle<'_>,
each_linked_rlib_for_lto: &[PathBuf],
modules: Vec<(String, ThinBuffer)>,
cached_modules: Vec<(SerializedModule<ModuleBuffer>, WorkProduct)>,
) -> (Vec<ThinModule<GccCodegenBackend>>, Vec<WorkProduct>) {
let dcx = cgcx.create_dcx();
let dcx = dcx.handle();
let lto_data = prepare_lto(cgcx, each_linked_rlib_for_lto, dcx);
if cgcx.opts.cg.linker_plugin_lto.enabled() {
if cgcx.use_linker_plugin_lto {
unreachable!(
"We should never reach this case if the LTO step \
is deferred to the linker"
@ -522,8 +522,6 @@ pub fn optimize_thin_module(
thin_module: ThinModule<GccCodegenBackend>,
_cgcx: &CodegenContext<GccCodegenBackend>,
) -> ModuleCodegen<GccContext> {
//let dcx = cgcx.create_dcx();
//let module_name = &thin_module.shared.module_names[thin_module.idx];
/*let tm_factory_config = TargetMachineFactoryConfig::new(cgcx, module_name.to_str().unwrap());
let tm = (cgcx.tm_factory)(tm_factory_config).map_err(|e| write::llvm_err(&dcx, e))?;*/

View file

@ -2,8 +2,11 @@ use std::{env, fs};
use gccjit::{Context, OutputKind};
use rustc_codegen_ssa::back::link::ensure_removed;
use rustc_codegen_ssa::back::write::{BitcodeSection, CodegenContext, EmitObj, ModuleConfig};
use rustc_codegen_ssa::back::write::{
BitcodeSection, CodegenContext, EmitObj, ModuleConfig, SharedEmitter,
};
use rustc_codegen_ssa::{CompiledModule, ModuleCodegen};
use rustc_errors::DiagCtxt;
use rustc_fs_util::link_or_copy;
use rustc_log::tracing::debug;
use rustc_session::config::OutputType;
@ -15,10 +18,11 @@ use crate::{GccCodegenBackend, GccContext, LtoMode};
pub(crate) fn codegen(
cgcx: &CodegenContext<GccCodegenBackend>,
shared_emitter: &SharedEmitter,
module: ModuleCodegen<GccContext>,
config: &ModuleConfig,
) -> CompiledModule {
let dcx = cgcx.create_dcx();
let dcx = DiagCtxt::new(Box::new(shared_emitter.clone()));
let dcx = dcx.handle();
let _timer = cgcx.prof.generic_activity_with_arg("GCC_module_codegen", &*module.name);

View file

@ -391,9 +391,6 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc
sym::breakpoint => {
unimplemented!();
}
sym::va_copy => {
unimplemented!();
}
sym::va_arg => {
unimplemented!();
}

View file

@ -121,6 +121,42 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
return Ok(bx.vector_select(vector_mask, arg1, args[2].immediate()));
}
#[cfg(feature = "master")]
if name == sym::simd_splat {
let (out_len, out_ty) = require_simd2!(ret_ty, SimdReturn);
require!(
args[0].layout.ty == out_ty,
InvalidMonomorphization::ExpectedVectorElementType {
span,
name,
expected_element: out_ty,
vector_type: ret_ty,
}
);
let vec_ty = llret_ty.unqualified().dyncast_vector().expect("vector return type");
let elem_ty = vec_ty.get_element_type();
// Cast pointer type to usize (GCC does not support pointer SIMD vectors).
let value = args[0];
let scalar = if value.layout.ty.is_numeric() {
value.immediate()
} else if value.layout.ty.is_raw_ptr() {
bx.ptrtoint(value.immediate(), elem_ty)
} else {
return_error!(InvalidMonomorphization::UnsupportedOperation {
span,
name,
in_ty: ret_ty,
in_elem: value.layout.ty
});
};
let elements = vec![scalar; out_len as usize];
return Ok(bx.context.new_rvalue_from_vector(bx.location, llret_ty, &elements));
}
// every intrinsic below takes a SIMD vector as its first argument
require_simd!(
args[0].layout.ty,

View file

@ -84,7 +84,7 @@ use gccjit::{TargetInfo, Version};
use rustc_ast::expand::allocator::AllocatorMethod;
use rustc_codegen_ssa::back::lto::{SerializedModule, ThinModule};
use rustc_codegen_ssa::back::write::{
CodegenContext, FatLtoInput, ModuleConfig, TargetMachineFactoryFn,
CodegenContext, FatLtoInput, ModuleConfig, SharedEmitter, TargetMachineFactoryFn,
};
use rustc_codegen_ssa::base::codegen_crate;
use rustc_codegen_ssa::target_features::cfg_target_feature;
@ -435,23 +435,25 @@ impl WriteBackendMethods for GccCodegenBackend {
fn run_and_optimize_fat_lto(
cgcx: &CodegenContext<Self>,
shared_emitter: &SharedEmitter,
// FIXME(bjorn3): Limit LTO exports to these symbols
_exported_symbols_for_lto: &[String],
each_linked_rlib_for_lto: &[PathBuf],
modules: Vec<FatLtoInput<Self>>,
) -> ModuleCodegen<Self::Module> {
back::lto::run_fat(cgcx, each_linked_rlib_for_lto, modules)
back::lto::run_fat(cgcx, shared_emitter, each_linked_rlib_for_lto, modules)
}
fn run_thin_lto(
cgcx: &CodegenContext<Self>,
dcx: DiagCtxtHandle<'_>,
// FIXME(bjorn3): Limit LTO exports to these symbols
_exported_symbols_for_lto: &[String],
each_linked_rlib_for_lto: &[PathBuf],
modules: Vec<(String, Self::ThinBuffer)>,
cached_modules: Vec<(SerializedModule<Self::ModuleBuffer>, WorkProduct)>,
) -> (Vec<ThinModule<Self>>, Vec<WorkProduct>) {
back::lto::run_thin(cgcx, each_linked_rlib_for_lto, modules, cached_modules)
back::lto::run_thin(cgcx, dcx, each_linked_rlib_for_lto, modules, cached_modules)
}
fn print_pass_timings(&self) {
@ -464,7 +466,7 @@ impl WriteBackendMethods for GccCodegenBackend {
fn optimize(
_cgcx: &CodegenContext<Self>,
_dcx: DiagCtxtHandle<'_>,
_shared_emitter: &SharedEmitter,
module: &mut ModuleCodegen<Self::Module>,
config: &ModuleConfig,
) {
@ -473,6 +475,7 @@ impl WriteBackendMethods for GccCodegenBackend {
fn optimize_thin(
cgcx: &CodegenContext<Self>,
_shared_emitter: &SharedEmitter,
thin: ThinModule<Self>,
) -> ModuleCodegen<Self::Module> {
back::lto::optimize_thin_module(thin, cgcx)
@ -480,10 +483,11 @@ impl WriteBackendMethods for GccCodegenBackend {
fn codegen(
cgcx: &CodegenContext<Self>,
shared_emitter: &SharedEmitter,
module: ModuleCodegen<Self::Module>,
config: &ModuleConfig,
) -> CompiledModule {
back::write::codegen(cgcx, module, config)
back::write::codegen(cgcx, shared_emitter, module, config)
}
fn prepare_thin(module: ModuleCodegen<Self::Module>) -> (String, Self::ThinBuffer) {

View file

@ -694,6 +694,10 @@ pub(crate) fn to_llvm_calling_convention(sess: &Session, abi: CanonAbi) -> llvm:
match abi {
CanonAbi::C | CanonAbi::Rust => llvm::CCallConv,
CanonAbi::RustCold => llvm::PreserveMost,
CanonAbi::RustPreserveNone => match &sess.target.arch {
Arch::X86_64 | Arch::AArch64 => llvm::PreserveNone,
_ => llvm::CCallConv,
},
// Functions with this calling convention can only be called from assembly, but it is
// possible to declare an `extern "custom"` block, so the backend still needs a calling
// convention for declaring foreign functions.

View file

@ -370,7 +370,7 @@ fn create_alloc_family_attr(llcx: &llvm::Context) -> &llvm::Attribute {
llvm::CreateAttrStringValue(llcx, "alloc-family", "__rust_alloc")
}
/// Helper for `FnAbi::apply_attrs_llfn`:
/// Helper for `FnAbiLlvmExt::apply_attrs_llfn`:
/// Composite function which sets LLVM attributes for function depending on its AST (`#[attribute]`)
/// attributes.
pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>(
@ -516,7 +516,16 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>(
to_add.push(llvm::CreateAllocKindAttr(cx.llcx, AllocKindFlags::Free));
// applies to argument place instead of function place
let allocated_pointer = AttributeKind::AllocatedPointer.create_attr(cx.llcx);
attributes::apply_to_llfn(llfn, AttributePlace::Argument(0), &[allocated_pointer]);
let attrs: &[_] = if llvm_util::get_version() >= (21, 0, 0) {
// "Does not capture provenance" means "if the function call stashes the pointer somewhere,
// accessing that pointer after the function returns is UB". That is definitely the case here since
// freeing will destroy the provenance.
let captures_addr = AttributeKind::CapturesAddress.create_attr(cx.llcx);
&[allocated_pointer, captures_addr]
} else {
&[allocated_pointer]
};
attributes::apply_to_llfn(llfn, AttributePlace::Argument(0), attrs);
}
if let Some(align) = codegen_fn_attrs.alignment {
llvm::set_alignment(llfn, align);

View file

@ -9,12 +9,12 @@ use std::{io, iter, slice};
use object::read::archive::ArchiveFile;
use object::{Object, ObjectSection};
use rustc_codegen_ssa::back::lto::{SerializedModule, ThinModule, ThinShared};
use rustc_codegen_ssa::back::write::{CodegenContext, FatLtoInput};
use rustc_codegen_ssa::back::write::{CodegenContext, FatLtoInput, SharedEmitter};
use rustc_codegen_ssa::traits::*;
use rustc_codegen_ssa::{ModuleCodegen, ModuleKind, looks_like_rust_object_file};
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::memmap::Mmap;
use rustc_errors::DiagCtxtHandle;
use rustc_errors::{DiagCtxt, DiagCtxtHandle};
use rustc_hir::attrs::SanitizerSet;
use rustc_middle::bug;
use rustc_middle::dep_graph::WorkProduct;
@ -150,17 +150,18 @@ fn get_bitcode_slice_from_object_data<'a>(
/// for further optimization.
pub(crate) fn run_fat(
cgcx: &CodegenContext<LlvmCodegenBackend>,
shared_emitter: &SharedEmitter,
exported_symbols_for_lto: &[String],
each_linked_rlib_for_lto: &[PathBuf],
modules: Vec<FatLtoInput<LlvmCodegenBackend>>,
) -> ModuleCodegen<ModuleLlvm> {
let dcx = cgcx.create_dcx();
let dcx = DiagCtxt::new(Box::new(shared_emitter.clone()));
let dcx = dcx.handle();
let (symbols_below_threshold, upstream_modules) =
prepare_lto(cgcx, exported_symbols_for_lto, each_linked_rlib_for_lto, dcx);
let symbols_below_threshold =
symbols_below_threshold.iter().map(|c| c.as_ptr()).collect::<Vec<_>>();
fat_lto(cgcx, dcx, modules, upstream_modules, &symbols_below_threshold)
fat_lto(cgcx, dcx, shared_emitter, modules, upstream_modules, &symbols_below_threshold)
}
/// Performs thin LTO by performing necessary global analysis and returning two
@ -168,18 +169,17 @@ pub(crate) fn run_fat(
/// can simply be copied over from the incr. comp. cache.
pub(crate) fn run_thin(
cgcx: &CodegenContext<LlvmCodegenBackend>,
dcx: DiagCtxtHandle<'_>,
exported_symbols_for_lto: &[String],
each_linked_rlib_for_lto: &[PathBuf],
modules: Vec<(String, ThinBuffer)>,
cached_modules: Vec<(SerializedModule<ModuleBuffer>, WorkProduct)>,
) -> (Vec<ThinModule<LlvmCodegenBackend>>, Vec<WorkProduct>) {
let dcx = cgcx.create_dcx();
let dcx = dcx.handle();
let (symbols_below_threshold, upstream_modules) =
prepare_lto(cgcx, exported_symbols_for_lto, each_linked_rlib_for_lto, dcx);
let symbols_below_threshold =
symbols_below_threshold.iter().map(|c| c.as_ptr()).collect::<Vec<_>>();
if cgcx.opts.cg.linker_plugin_lto.enabled() {
if cgcx.use_linker_plugin_lto {
unreachable!(
"We should never reach this case if the LTO step \
is deferred to the linker"
@ -197,6 +197,7 @@ pub(crate) fn prepare_thin(module: ModuleCodegen<ModuleLlvm>) -> (String, ThinBu
fn fat_lto(
cgcx: &CodegenContext<LlvmCodegenBackend>,
dcx: DiagCtxtHandle<'_>,
shared_emitter: &SharedEmitter,
modules: Vec<FatLtoInput<LlvmCodegenBackend>>,
mut serialized_modules: Vec<(SerializedModule<ModuleBuffer>, CString)>,
symbols_below_threshold: &[*const libc::c_char],
@ -265,8 +266,13 @@ fn fat_lto(
// The linking steps below may produce errors and diagnostics within LLVM
// which we'd like to handle and print, so set up our diagnostic handlers
// (which get unregistered when they go out of scope below).
let _handler =
DiagnosticHandlers::new(cgcx, dcx, llcx, &module, CodegenDiagnosticsStage::LTO);
let _handler = DiagnosticHandlers::new(
cgcx,
shared_emitter,
llcx,
&module,
CodegenDiagnosticsStage::LTO,
);
// For all other modules we codegened we'll need to link them into our own
// bitcode. All modules were codegened in their own LLVM context, however,
@ -720,10 +726,11 @@ impl Drop for ThinBuffer {
}
pub(crate) fn optimize_thin_module(
thin_module: ThinModule<LlvmCodegenBackend>,
cgcx: &CodegenContext<LlvmCodegenBackend>,
shared_emitter: &SharedEmitter,
thin_module: ThinModule<LlvmCodegenBackend>,
) -> ModuleCodegen<ModuleLlvm> {
let dcx = cgcx.create_dcx();
let dcx = DiagCtxt::new(Box::new(shared_emitter.clone()));
let dcx = dcx.handle();
let module_name = &thin_module.shared.module_names[thin_module.idx];

View file

@ -39,6 +39,7 @@ impl OwnedTargetMachine {
debug_info_compression: llvm::CompressionKind,
use_emulated_tls: bool,
use_wasm_eh: bool,
large_data_threshold: u64,
) -> Result<Self, LlvmError<'static>> {
// SAFETY: llvm::LLVMRustCreateTargetMachine copies pointed to data
let tm_ptr = unsafe {
@ -65,6 +66,7 @@ impl OwnedTargetMachine {
debug_info_compression,
use_emulated_tls,
use_wasm_eh,
large_data_threshold,
)
};

View file

@ -9,7 +9,7 @@ use libc::{c_char, c_int, c_void, size_t};
use rustc_codegen_ssa::back::link::ensure_removed;
use rustc_codegen_ssa::back::versioned_llvm_target;
use rustc_codegen_ssa::back::write::{
BitcodeSection, CodegenContext, EmitObj, InlineAsmError, ModuleConfig,
BitcodeSection, CodegenContext, EmitObj, InlineAsmError, ModuleConfig, SharedEmitter,
TargetMachineFactoryConfig, TargetMachineFactoryFn,
};
use rustc_codegen_ssa::base::wants_wasm_eh;
@ -18,7 +18,7 @@ use rustc_codegen_ssa::traits::*;
use rustc_codegen_ssa::{CompiledModule, ModuleCodegen, ModuleKind};
use rustc_data_structures::profiling::SelfProfilerRef;
use rustc_data_structures::small_c_str::SmallCStr;
use rustc_errors::{DiagCtxtHandle, Level};
use rustc_errors::{DiagCtxt, DiagCtxtHandle, Level};
use rustc_fs_util::{link_or_copy, path_to_c_string};
use rustc_middle::ty::TyCtxt;
use rustc_session::Session;
@ -275,6 +275,8 @@ pub(crate) fn target_machine_factory(
let use_wasm_eh = wants_wasm_eh(sess);
let large_data_threshold = sess.opts.unstable_opts.large_data_threshold.unwrap_or(0);
let prof = SelfProfilerRef::clone(&sess.prof);
Arc::new(move |config: TargetMachineFactoryConfig| {
// Self-profile timer for invoking a factory to create a target machine.
@ -316,6 +318,7 @@ pub(crate) fn target_machine_factory(
debuginfo_compression,
use_emulated_tls,
use_wasm_eh,
large_data_threshold,
)
})
}
@ -356,7 +359,7 @@ pub(crate) enum CodegenDiagnosticsStage {
}
pub(crate) struct DiagnosticHandlers<'a> {
data: *mut (&'a CodegenContext<LlvmCodegenBackend>, DiagCtxtHandle<'a>),
data: *mut (&'a CodegenContext<LlvmCodegenBackend>, &'a SharedEmitter),
llcx: &'a llvm::Context,
old_handler: Option<&'a llvm::DiagnosticHandler>,
}
@ -364,7 +367,7 @@ pub(crate) struct DiagnosticHandlers<'a> {
impl<'a> DiagnosticHandlers<'a> {
pub(crate) fn new(
cgcx: &'a CodegenContext<LlvmCodegenBackend>,
dcx: DiagCtxtHandle<'a>,
shared_emitter: &'a SharedEmitter,
llcx: &'a llvm::Context,
module: &ModuleCodegen<ModuleLlvm>,
stage: CodegenDiagnosticsStage,
@ -398,8 +401,8 @@ impl<'a> DiagnosticHandlers<'a> {
})
.and_then(|dir| dir.to_str().and_then(|p| CString::new(p).ok()));
let pgo_available = cgcx.opts.cg.profile_use.is_some();
let data = Box::into_raw(Box::new((cgcx, dcx)));
let pgo_available = cgcx.module_config.pgo_use.is_some();
let data = Box::into_raw(Box::new((cgcx, shared_emitter)));
unsafe {
let old_handler = llvm::LLVMRustContextGetDiagnosticHandler(llcx);
llvm::LLVMRustContextConfigureDiagnosticHandler(
@ -461,12 +464,16 @@ unsafe extern "C" fn diagnostic_handler(info: &DiagnosticInfo, user: *mut c_void
if user.is_null() {
return;
}
let (cgcx, dcx) =
unsafe { *(user as *const (&CodegenContext<LlvmCodegenBackend>, DiagCtxtHandle<'_>)) };
let (cgcx, shared_emitter) =
unsafe { *(user as *const (&CodegenContext<LlvmCodegenBackend>, &SharedEmitter)) };
let dcx = DiagCtxt::new(Box::new(shared_emitter.clone()));
let dcx = dcx.handle();
match unsafe { llvm::diagnostic::Diagnostic::unpack(info) } {
llvm::diagnostic::InlineAsm(inline) => {
cgcx.diag_emitter.inline_asm_error(report_inline_asm(
// FIXME use dcx
shared_emitter.inline_asm_error(report_inline_asm(
cgcx,
inline.message,
inline.level,
@ -776,7 +783,7 @@ pub(crate) unsafe fn llvm_optimize(
&*module.module_llvm.tm.raw(),
to_pass_builder_opt_level(opt_level),
opt_stage,
cgcx.opts.cg.linker_plugin_lto.enabled(),
cgcx.use_linker_plugin_lto,
config.no_prepopulate_passes,
config.verify_llvm_ir,
config.lint_llvm_ir,
@ -887,14 +894,18 @@ pub(crate) unsafe fn llvm_optimize(
// Unsafe due to LLVM calls.
pub(crate) fn optimize(
cgcx: &CodegenContext<LlvmCodegenBackend>,
dcx: DiagCtxtHandle<'_>,
shared_emitter: &SharedEmitter,
module: &mut ModuleCodegen<ModuleLlvm>,
config: &ModuleConfig,
) {
let _timer = cgcx.prof.generic_activity_with_arg("LLVM_module_optimize", &*module.name);
let dcx = DiagCtxt::new(Box::new(shared_emitter.clone()));
let dcx = dcx.handle();
let llcx = &*module.module_llvm.llcx;
let _handlers = DiagnosticHandlers::new(cgcx, dcx, llcx, module, CodegenDiagnosticsStage::Opt);
let _handlers =
DiagnosticHandlers::new(cgcx, shared_emitter, llcx, module, CodegenDiagnosticsStage::Opt);
if config.emit_no_opt_bc {
let out = cgcx.output_filenames.temp_path_ext_for_cgu(
@ -911,7 +922,7 @@ pub(crate) fn optimize(
let opt_stage = match cgcx.lto {
Lto::Fat => llvm::OptStage::PreLinkFatLTO,
Lto::Thin | Lto::ThinLocal => llvm::OptStage::PreLinkThinLTO,
_ if cgcx.opts.cg.linker_plugin_lto.enabled() => llvm::OptStage::PreLinkThinLTO,
_ if cgcx.use_linker_plugin_lto => llvm::OptStage::PreLinkThinLTO,
_ => llvm::OptStage::PreLinkNoLTO,
};
@ -974,19 +985,26 @@ pub(crate) fn optimize(
pub(crate) fn codegen(
cgcx: &CodegenContext<LlvmCodegenBackend>,
shared_emitter: &SharedEmitter,
module: ModuleCodegen<ModuleLlvm>,
config: &ModuleConfig,
) -> CompiledModule {
let dcx = cgcx.create_dcx();
let _timer = cgcx.prof.generic_activity_with_arg("LLVM_module_codegen", &*module.name);
let dcx = DiagCtxt::new(Box::new(shared_emitter.clone()));
let dcx = dcx.handle();
let _timer = cgcx.prof.generic_activity_with_arg("LLVM_module_codegen", &*module.name);
{
let llmod = module.module_llvm.llmod();
let llcx = &*module.module_llvm.llcx;
let tm = &*module.module_llvm.tm;
let _handlers =
DiagnosticHandlers::new(cgcx, dcx, llcx, &module, CodegenDiagnosticsStage::Codegen);
let _handlers = DiagnosticHandlers::new(
cgcx,
shared_emitter,
llcx,
&module,
CodegenDiagnosticsStage::Codegen,
);
if cgcx.msvc_imps_needed {
create_msvc_imps(cgcx, llcx, llmod);

View file

@ -1384,12 +1384,12 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
fn call(
&mut self,
llty: &'ll Type,
fn_call_attrs: Option<&CodegenFnAttrs>,
caller_attrs: Option<&CodegenFnAttrs>,
fn_abi: Option<&FnAbi<'tcx, Ty<'tcx>>>,
llfn: &'ll Value,
args: &[&'ll Value],
funclet: Option<&Funclet<'ll>>,
instance: Option<Instance<'tcx>>,
callee_instance: Option<Instance<'tcx>>,
) -> &'ll Value {
debug!("call {:?} with args ({:?})", llfn, args);
@ -1401,10 +1401,10 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
}
// Emit CFI pointer type membership test
self.cfi_type_test(fn_call_attrs, fn_abi, instance, llfn);
self.cfi_type_test(caller_attrs, fn_abi, callee_instance, llfn);
// Emit KCFI operand bundle
let kcfi_bundle = self.kcfi_operand_bundle(fn_call_attrs, fn_abi, instance, llfn);
let kcfi_bundle = self.kcfi_operand_bundle(caller_attrs, fn_abi, callee_instance, llfn);
if let Some(kcfi_bundle) = kcfi_bundle.as_ref().map(|b| b.as_ref()) {
bundles.push(kcfi_bundle);
}
@ -1422,17 +1422,17 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
)
};
if let Some(instance) = instance {
if let Some(callee_instance) = callee_instance {
// Attributes on the function definition being called
let fn_defn_attrs = self.cx.tcx.codegen_fn_attrs(instance.def_id());
if let Some(fn_call_attrs) = fn_call_attrs
let callee_attrs = self.cx.tcx.codegen_fn_attrs(callee_instance.def_id());
if let Some(caller_attrs) = caller_attrs
// If there is an inline attribute and a target feature that matches
// we will add the attribute to the callsite otherwise we'll omit
// this and not add the attribute to prevent soundness issues.
&& let Some(inlining_rule) = attributes::inline_attr(&self.cx, self.cx.tcx, instance)
&& let Some(inlining_rule) = attributes::inline_attr(&self.cx, self.cx.tcx, callee_instance)
&& self.cx.tcx.is_target_feature_call_safe(
&fn_defn_attrs.target_features,
&fn_call_attrs.target_features.iter().cloned().chain(
&callee_attrs.target_features,
&caller_attrs.target_features.iter().cloned().chain(
self.cx.tcx.sess.target_features.iter().map(|feat| TargetFeature {
name: *feat,
kind: TargetFeatureKind::Implied,
@ -1457,14 +1457,15 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
fn tail_call(
&mut self,
llty: Self::Type,
fn_attrs: Option<&CodegenFnAttrs>,
caller_attrs: Option<&CodegenFnAttrs>,
fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
llfn: Self::Value,
args: &[Self::Value],
funclet: Option<&Self::Funclet>,
instance: Option<Instance<'tcx>>,
callee_instance: Option<Instance<'tcx>>,
) {
let call = self.call(llty, fn_attrs, Some(fn_abi), llfn, args, funclet, instance);
let call =
self.call(llty, caller_attrs, Some(fn_abi), llfn, args, funclet, callee_instance);
llvm::LLVMSetTailCallKind(call, llvm::TailCallKind::MustTail);
match &fn_abi.ret.mode {

View file

@ -221,6 +221,12 @@ pub(crate) unsafe fn create_module<'ll>(
target_data_layout = target_data_layout.replace("-m:e", "");
}
}
if llvm_version < (23, 0, 0) {
if sess.target.arch == Arch::S390x {
// LLVM 23 updated the s390x layout to specify the stack alignment: https://github.com/llvm/llvm-project/pull/176041
target_data_layout = target_data_layout.replace("-S64", "");
}
}
// Ensure the data-layout values hardcoded remain the defaults.
{

View file

@ -92,7 +92,7 @@ pub(crate) fn needs_gdb_debug_scripts_section(cx: &CodegenCx<'_, '_>) -> bool {
CrateType::Executable
| CrateType::Dylib
| CrateType::Cdylib
| CrateType::Staticlib
| CrateType::StaticLib
| CrateType::Sdylib => {
// These are crate types for which we will embed pretty printers since they
// are treated as leaf crates.

View file

@ -665,8 +665,8 @@ impl MsvcBasicName for ty::UintTy {
impl MsvcBasicName for ty::FloatTy {
fn msvc_basic_name(self) -> &'static str {
// FIXME(f16_f128): `f16` and `f128` have no MSVC representation. We could improve the
// debuginfo. See: <https://github.com/rust-lang/rust/issues/121837>
// FIXME(f128): `f128` has no MSVC representation. We could improve the debuginfo.
// See: <https://github.com/rust-lang/rust/issues/121837>
match self {
ty::FloatTy::F16 => {
bug!("`f16` should have been handled in `build_basic_type_di_node`")

View file

@ -456,7 +456,7 @@ impl<'ll, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> {
let generics = tcx.generics_of(enclosing_fn_def_id);
let args = instance.args.truncate_to(tcx, generics);
type_names::push_generic_params(
type_names::push_generic_args(
tcx,
tcx.normalize_erasing_regions(self.typing_env(), args),
&mut name,

View file

@ -271,14 +271,6 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
return Ok(());
}
sym::breakpoint => self.call_intrinsic("llvm.debugtrap", &[], &[]),
sym::va_copy => {
let dest = args[0].immediate();
self.call_intrinsic(
"llvm.va_copy",
&[self.val_ty(dest)],
&[dest, args[1].immediate()],
)
}
sym::va_arg => {
match result.layout.backend_repr {
BackendRepr::Scalar(scalar) => {
@ -1592,6 +1584,31 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
return Ok(bx.select(m_i1s, args[1].immediate(), args[2].immediate()));
}
if name == sym::simd_splat {
let (_out_len, out_ty) = require_simd!(ret_ty, SimdReturn);
require!(
args[0].layout.ty == out_ty,
InvalidMonomorphization::ExpectedVectorElementType {
span,
name,
expected_element: out_ty,
vector_type: ret_ty,
}
);
// `insertelement <N x elem> poison, elem %x, i32 0`
let poison_vec = bx.const_poison(llret_ty);
let idx0 = bx.const_i32(0);
let v0 = bx.insert_element(poison_vec, args[0].immediate(), idx0);
// `shufflevector <N x elem> v0, <N x elem> poison, <N x i32> zeroinitializer`
// The masks is all zeros, so this splats lane 0 (which has our element in it).
let splat = bx.shuffle_vector(v0, poison_vec, bx.const_null(llret_ty));
return Ok(splat);
}
// every intrinsic below takes a SIMD vector as its first argument
let (in_len, in_elem) = require_simd!(args[0].layout.ty, SimdInput);
let in_ty = args[0].layout.ty;

View file

@ -30,12 +30,13 @@ use llvm_util::target_config;
use rustc_ast::expand::allocator::AllocatorMethod;
use rustc_codegen_ssa::back::lto::{SerializedModule, ThinModule};
use rustc_codegen_ssa::back::write::{
CodegenContext, FatLtoInput, ModuleConfig, TargetMachineFactoryConfig, TargetMachineFactoryFn,
CodegenContext, FatLtoInput, ModuleConfig, SharedEmitter, TargetMachineFactoryConfig,
TargetMachineFactoryFn,
};
use rustc_codegen_ssa::traits::*;
use rustc_codegen_ssa::{CodegenResults, CompiledModule, ModuleCodegen, TargetConfig};
use rustc_data_structures::fx::FxIndexMap;
use rustc_errors::DiagCtxtHandle;
use rustc_errors::{DiagCtxt, DiagCtxtHandle};
use rustc_metadata::EncodedMetadata;
use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
use rustc_middle::ty::TyCtxt;
@ -166,14 +167,20 @@ impl WriteBackendMethods for LlvmCodegenBackend {
}
fn run_and_optimize_fat_lto(
cgcx: &CodegenContext<Self>,
shared_emitter: &SharedEmitter,
exported_symbols_for_lto: &[String],
each_linked_rlib_for_lto: &[PathBuf],
modules: Vec<FatLtoInput<Self>>,
) -> ModuleCodegen<Self::Module> {
let mut module =
back::lto::run_fat(cgcx, exported_symbols_for_lto, each_linked_rlib_for_lto, modules);
let mut module = back::lto::run_fat(
cgcx,
shared_emitter,
exported_symbols_for_lto,
each_linked_rlib_for_lto,
modules,
);
let dcx = cgcx.create_dcx();
let dcx = DiagCtxt::new(Box::new(shared_emitter.clone()));
let dcx = dcx.handle();
back::lto::run_pass_manager(cgcx, dcx, &mut module, false);
@ -181,6 +188,7 @@ impl WriteBackendMethods for LlvmCodegenBackend {
}
fn run_thin_lto(
cgcx: &CodegenContext<Self>,
dcx: DiagCtxtHandle<'_>,
exported_symbols_for_lto: &[String],
each_linked_rlib_for_lto: &[PathBuf],
modules: Vec<(String, Self::ThinBuffer)>,
@ -188,6 +196,7 @@ impl WriteBackendMethods for LlvmCodegenBackend {
) -> (Vec<ThinModule<Self>>, Vec<WorkProduct>) {
back::lto::run_thin(
cgcx,
dcx,
exported_symbols_for_lto,
each_linked_rlib_for_lto,
modules,
@ -196,24 +205,26 @@ impl WriteBackendMethods for LlvmCodegenBackend {
}
fn optimize(
cgcx: &CodegenContext<Self>,
dcx: DiagCtxtHandle<'_>,
shared_emitter: &SharedEmitter,
module: &mut ModuleCodegen<Self::Module>,
config: &ModuleConfig,
) {
back::write::optimize(cgcx, dcx, module, config)
back::write::optimize(cgcx, shared_emitter, module, config)
}
fn optimize_thin(
cgcx: &CodegenContext<Self>,
shared_emitter: &SharedEmitter,
thin: ThinModule<Self>,
) -> ModuleCodegen<Self::Module> {
back::lto::optimize_thin_module(thin, cgcx)
back::lto::optimize_thin_module(cgcx, shared_emitter, thin)
}
fn codegen(
cgcx: &CodegenContext<Self>,
shared_emitter: &SharedEmitter,
module: ModuleCodegen<Self::Module>,
config: &ModuleConfig,
) -> CompiledModule {
back::write::codegen(cgcx, module, config)
back::write::codegen(cgcx, shared_emitter, module, config)
}
fn prepare_thin(module: ModuleCodegen<Self::Module>) -> (String, Self::ThinBuffer) {
back::lto::prepare_thin(module)

View file

@ -167,6 +167,7 @@ pub(crate) enum CallConv {
PreserveMost = 14,
PreserveAll = 15,
Tail = 18,
PreserveNone = 21,
X86StdcallCallConv = 64,
X86FastcallCallConv = 65,
ArmAapcsCallConv = 67,
@ -2347,6 +2348,7 @@ unsafe extern "C" {
DebugInfoCompression: CompressionKind,
UseEmulatedTls: bool,
UseWasmEH: bool,
LargeDataThreshold: u64,
) -> *mut TargetMachine;
pub(crate) fn LLVMRustAddLibraryInfo<'a>(

View file

@ -48,8 +48,6 @@ codegen_ssa_error_creating_remark_dir = failed to create remark directory: {$err
codegen_ssa_error_writing_def_file =
error writing .DEF file: {$error}
codegen_ssa_expected_name_value_pair = expected name value pair
codegen_ssa_extern_funcs_not_found = some `extern` functions couldn't be found; some native libraries may need to be installed or have their path specified
codegen_ssa_extract_bundled_libs_archive_member = failed to get data from archive member '{$rlib}': {$error}
@ -90,9 +88,6 @@ codegen_ssa_incorrect_cgu_reuse_type =
codegen_ssa_insufficient_vs_code_product = VS Code is a different product, and is not sufficient.
codegen_ssa_invalid_literal_value = invalid literal value
.label = value must be an integer between `0` and `255`
codegen_ssa_invalid_monomorphization_basic_float_type = invalid monomorphization of `{$name}` intrinsic: expected basic float type, found `{$ty}`
codegen_ssa_invalid_monomorphization_basic_integer_or_ptr_type = invalid monomorphization of `{$name}` intrinsic: expected basic integer or pointer type, found `{$ty}`
@ -225,9 +220,6 @@ codegen_ssa_no_natvis_directory = error enumerating natvis directory: {$error}
codegen_ssa_no_saved_object_file = cached cgu {$cgu_name} should have an object file, but doesn't
codegen_ssa_out_of_range_integer = integer value out of range
.label = value must be between `0` and `255`
codegen_ssa_processing_dymutil_failed = processing debug info with `dsymutil` failed: {$status}
.note = {$output}
@ -357,9 +349,6 @@ codegen_ssa_unable_to_run_dsymutil = unable to run `dsymutil`: {$error}
codegen_ssa_unable_to_write_debugger_visualizer = unable to write debugger visualizer file `{$path}`: {$error}
codegen_ssa_unexpected_parameter_name = unexpected parameter name
.label = expected `{$prefix_nops}` or `{$entry_nops}`
codegen_ssa_unknown_archive_kind =
don't know how to build archive of type: {$kind}

View file

@ -136,7 +136,7 @@ pub fn link_binary(
)
.build(&out_filename);
}
CrateType::Staticlib => {
CrateType::StaticLib => {
link_staticlib(
sess,
archive_builder_builder,
@ -474,7 +474,7 @@ fn link_staticlib(
let res = each_linked_rlib(
&codegen_results.crate_info,
Some(CrateType::Staticlib),
Some(CrateType::StaticLib),
&mut |cnum, path| {
let lto = are_upstream_rust_objects_already_included(sess)
&& !ignored_for_lto(sess, &codegen_results.crate_info, cnum);
@ -532,7 +532,7 @@ fn link_staticlib(
let fmts = codegen_results
.crate_info
.dependency_formats
.get(&CrateType::Staticlib)
.get(&CrateType::StaticLib)
.expect("no dependency formats for staticlib");
let mut all_rust_dylibs = vec![];
@ -1210,7 +1210,7 @@ fn add_sanitizer_libraries(
return;
}
if matches!(crate_type, CrateType::Rlib | CrateType::Staticlib) {
if matches!(crate_type, CrateType::Rlib | CrateType::StaticLib) {
return;
}

View file

@ -1827,9 +1827,9 @@ fn exported_symbols_for_non_proc_macro(
// Mark allocator shim symbols as exported only if they were generated.
if export_threshold == SymbolExportLevel::Rust
&& needs_allocator_shim_for_linking(tcx.dependency_formats(()), crate_type)
&& tcx.allocator_kind(()).is_some()
&& let Some(kind) = tcx.allocator_kind(())
{
symbols.extend(allocator_shim_symbols(tcx));
symbols.extend(allocator_shim_symbols(tcx, kind));
}
symbols
@ -1857,7 +1857,7 @@ pub(crate) fn linked_symbols(
| CrateType::Cdylib
| CrateType::Dylib
| CrateType::Sdylib => (),
CrateType::Staticlib | CrateType::Rlib => {
CrateType::StaticLib | CrateType::Rlib => {
// These are not linked, so no need to generate symbols.o for them.
return Vec::new();
}

View file

@ -2,6 +2,7 @@ use std::ffi::CString;
use std::sync::Arc;
use rustc_data_structures::memmap::Mmap;
use rustc_errors::DiagCtxtHandle;
use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo, SymbolExportLevel};
use rustc_middle::ty::TyCtxt;
@ -66,7 +67,7 @@ fn crate_type_allows_lto(crate_type: CrateType) -> bool {
match crate_type {
CrateType::Executable
| CrateType::Dylib
| CrateType::Staticlib
| CrateType::StaticLib
| CrateType::Cdylib
| CrateType::ProcMacro
| CrateType::Sdylib => true,
@ -117,35 +118,38 @@ pub(super) fn exported_symbols_for_lto(
}
// Mark allocator shim symbols as exported only if they were generated.
if export_threshold == SymbolExportLevel::Rust && allocator_kind_for_codegen(tcx).is_some() {
symbols_below_threshold.extend(allocator_shim_symbols(tcx).map(|(name, _kind)| name));
if export_threshold == SymbolExportLevel::Rust
&& let Some(kind) = allocator_kind_for_codegen(tcx)
{
symbols_below_threshold.extend(allocator_shim_symbols(tcx, kind).map(|(name, _kind)| name));
}
symbols_below_threshold
}
pub(super) fn check_lto_allowed<B: WriteBackendMethods>(cgcx: &CodegenContext<B>) {
pub(super) fn check_lto_allowed<B: WriteBackendMethods>(
cgcx: &CodegenContext<B>,
dcx: DiagCtxtHandle<'_>,
) {
if cgcx.lto == Lto::ThinLocal {
// Crate local LTO is always allowed
return;
}
let dcx = cgcx.create_dcx();
// Make sure we actually can run LTO
for crate_type in cgcx.crate_types.iter() {
if !crate_type_allows_lto(*crate_type) {
dcx.handle().emit_fatal(LtoDisallowed);
} else if *crate_type == CrateType::Dylib {
if !cgcx.opts.unstable_opts.dylib_lto {
if !cgcx.dylib_lto {
dcx.handle().emit_fatal(LtoDylib);
}
} else if *crate_type == CrateType::ProcMacro && !cgcx.opts.unstable_opts.dylib_lto {
} else if *crate_type == CrateType::ProcMacro && !cgcx.dylib_lto {
dcx.handle().emit_fatal(LtoProcMacro);
}
}
if cgcx.opts.cg.prefer_dynamic && !cgcx.opts.unstable_opts.dylib_lto {
if cgcx.prefer_dynamic && !cgcx.dylib_lto {
dcx.handle().emit_fatal(DynamicLinkingWithLTO);
}
}

View file

@ -1,9 +1,7 @@
use std::collections::hash_map::Entry::*;
use rustc_abi::{CanonAbi, X86Call};
use rustc_ast::expand::allocator::{
ALLOC_ERROR_HANDLER, ALLOCATOR_METHODS, NO_ALLOC_SHIM_IS_UNSTABLE, global_fn_name,
};
use rustc_ast::expand::allocator::{AllocatorKind, NO_ALLOC_SHIM_IS_UNSTABLE, global_fn_name};
use rustc_data_structures::unord::UnordMap;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LOCAL_CRATE, LocalDefId};
@ -21,6 +19,7 @@ use rustc_target::spec::{Arch, Os, TlsModel};
use tracing::debug;
use crate::back::symbol_export;
use crate::base::allocator_shim_contents;
fn threshold(tcx: TyCtxt<'_>) -> SymbolExportLevel {
crates_export_threshold(tcx.crate_types())
@ -28,7 +27,7 @@ fn threshold(tcx: TyCtxt<'_>) -> SymbolExportLevel {
fn crate_export_threshold(crate_type: CrateType) -> SymbolExportLevel {
match crate_type {
CrateType::Executable | CrateType::Staticlib | CrateType::ProcMacro | CrateType::Cdylib => {
CrateType::Executable | CrateType::StaticLib | CrateType::ProcMacro | CrateType::Cdylib => {
SymbolExportLevel::C
}
CrateType::Rlib | CrateType::Dylib | CrateType::Sdylib => SymbolExportLevel::Rust,
@ -490,14 +489,12 @@ pub(crate) fn provide(providers: &mut Providers) {
pub(crate) fn allocator_shim_symbols(
tcx: TyCtxt<'_>,
kind: AllocatorKind,
) -> impl Iterator<Item = (String, SymbolExportKind)> {
ALLOCATOR_METHODS
.iter()
allocator_shim_contents(tcx, kind)
.into_iter()
.map(move |method| mangle_internal_symbol(tcx, global_fn_name(method.name).as_str()))
.chain([
mangle_internal_symbol(tcx, global_fn_name(ALLOC_ERROR_HANDLER).as_str()),
mangle_internal_symbol(tcx, NO_ALLOC_SHIM_IS_UNSTABLE),
])
.chain([mangle_internal_symbol(tcx, NO_ALLOC_SHIM_IS_UNSTABLE)])
.map(move |symbol_name| {
let exported_symbol = ExportedSymbol::NoDefId(SymbolName::new(tcx, &symbol_name));

View file

@ -6,7 +6,6 @@ use std::sync::mpsc::{Receiver, Sender, channel};
use std::{fs, io, mem, str, thread};
use rustc_abi::Size;
use rustc_ast::attr;
use rustc_data_structures::assert_matches;
use rustc_data_structures::fx::FxIndexMap;
use rustc_data_structures::jobserver::{self, Acquired};
@ -15,10 +14,12 @@ use rustc_data_structures::profiling::{SelfProfilerRef, VerboseTimingGuard};
use rustc_errors::emitter::Emitter;
use rustc_errors::translation::Translator;
use rustc_errors::{
Diag, DiagArgMap, DiagCtxt, DiagMessage, ErrCode, FatalError, FatalErrorMarker, Level,
MultiSpan, Style, Suggestions,
Diag, DiagArgMap, DiagCtxt, DiagCtxtHandle, DiagMessage, ErrCode, FatalError, FatalErrorMarker,
Level, MultiSpan, Style, Suggestions, catch_fatal_errors,
};
use rustc_fs_util::link_or_copy;
use rustc_hir::attrs::AttributeKind;
use rustc_hir::find_attr;
use rustc_incremental::{
copy_cgu_workproduct_to_incr_comp_cache_dir, in_incr_comp_dir, in_incr_comp_dir_sess,
};
@ -31,7 +32,7 @@ use rustc_session::config::{
self, CrateType, Lto, OutFileName, OutputFilenames, OutputType, Passes, SwitchWithOptPath,
};
use rustc_span::source_map::SourceMap;
use rustc_span::{FileName, InnerSpan, Span, SpanData, sym};
use rustc_span::{FileName, InnerSpan, Span, SpanData};
use rustc_target::spec::{MergeFunctions, SanitizerSet};
use tracing::debug;
@ -326,15 +327,16 @@ pub struct CodegenContext<B: WriteBackendMethods> {
// Resources needed when running LTO
pub prof: SelfProfilerRef,
pub lto: Lto,
pub use_linker_plugin_lto: bool,
pub dylib_lto: bool,
pub prefer_dynamic: bool,
pub save_temps: bool,
pub fewer_names: bool,
pub time_trace: bool,
pub opts: Arc<config::Options>,
pub crate_types: Vec<CrateType>,
pub output_filenames: Arc<OutputFilenames>,
pub invocation_temp: Option<String>,
pub module_config: Arc<ModuleConfig>,
pub allocator_config: Arc<ModuleConfig>,
pub tm_factory: TargetMachineFactoryFn<B>,
pub msvc_imps_needed: bool,
pub is_pe_coff: bool,
@ -347,8 +349,6 @@ pub struct CodegenContext<B: WriteBackendMethods> {
pub split_dwarf_kind: rustc_session::config::SplitDwarfKind,
pub pointer_size: Size,
/// Emitter to use for diagnostics produced during codegen.
pub diag_emitter: SharedEmitter,
/// LLVM optimizations for which we want to print remarks.
pub remark: Passes,
/// Directory into which should the LLVM optimization remarks be written.
@ -363,14 +363,9 @@ pub struct CodegenContext<B: WriteBackendMethods> {
pub parallel: bool,
}
impl<B: WriteBackendMethods> CodegenContext<B> {
pub fn create_dcx(&self) -> DiagCtxt {
DiagCtxt::new(Box::new(self.diag_emitter.clone()))
}
}
fn generate_thin_lto_work<B: ExtraBackendMethods>(
cgcx: &CodegenContext<B>,
dcx: DiagCtxtHandle<'_>,
exported_symbols_for_lto: &[String],
each_linked_rlib_for_lto: &[PathBuf],
needs_thin_lto: Vec<(String, B::ThinBuffer)>,
@ -380,6 +375,7 @@ fn generate_thin_lto_work<B: ExtraBackendMethods>(
let (lto_modules, copy_jobs) = B::run_thin_lto(
cgcx,
dcx,
exported_symbols_for_lto,
each_linked_rlib_for_lto,
needs_thin_lto,
@ -408,6 +404,29 @@ struct CompiledModules {
allocator_module: Option<CompiledModule>,
}
enum MaybeLtoModules<B: WriteBackendMethods> {
NoLto {
modules: Vec<CompiledModule>,
allocator_module: Option<CompiledModule>,
},
FatLto {
cgcx: CodegenContext<B>,
exported_symbols_for_lto: Arc<Vec<String>>,
each_linked_rlib_file_for_lto: Vec<PathBuf>,
needs_fat_lto: Vec<FatLtoInput<B>>,
lto_import_only_modules:
Vec<(SerializedModule<<B as WriteBackendMethods>::ModuleBuffer>, WorkProduct)>,
},
ThinLto {
cgcx: CodegenContext<B>,
exported_symbols_for_lto: Arc<Vec<String>>,
each_linked_rlib_file_for_lto: Vec<PathBuf>,
needs_thin_lto: Vec<(String, <B as WriteBackendMethods>::ThinBuffer)>,
lto_import_only_modules:
Vec<(SerializedModule<<B as WriteBackendMethods>::ModuleBuffer>, WorkProduct)>,
},
}
fn need_bitcode_in_object(tcx: TyCtxt<'_>) -> bool {
let sess = tcx.sess;
sess.opts.cg.embed_bitcode
@ -435,7 +454,7 @@ pub(crate) fn start_async_codegen<B: ExtraBackendMethods>(
let (coordinator_send, coordinator_receive) = channel();
let crate_attrs = tcx.hir_attrs(rustc_hir::CRATE_HIR_ID);
let no_builtins = attr::contains_name(crate_attrs, sym::no_builtins);
let no_builtins = find_attr!(crate_attrs, AttributeKind::NoBuiltins);
let crate_info = CrateInfo::new(tcx, target_cpu);
@ -797,20 +816,12 @@ pub(crate) enum ComputedLtoType {
pub(crate) fn compute_per_cgu_lto_type(
sess_lto: &Lto,
opts: &config::Options,
linker_does_lto: bool,
sess_crate_types: &[CrateType],
module_kind: ModuleKind,
) -> ComputedLtoType {
// If the linker does LTO, we don't have to do it. Note that we
// keep doing full LTO, if it is requested, as not to break the
// assumption that the output will be a single module.
let linker_does_lto = opts.cg.linker_plugin_lto.enabled();
// When we're automatically doing ThinLTO for multi-codegen-unit
// builds we don't actually want to LTO the allocator module if
// it shows up. This is due to various linker shenanigans that
// we'll encounter later.
let is_allocator = module_kind == ModuleKind::Allocator;
// We ignore a request for full crate graph LTO if the crate type
// is only an rlib, as there is no full crate graph to process,
@ -823,7 +834,7 @@ pub(crate) fn compute_per_cgu_lto_type(
let is_rlib = matches!(sess_crate_types, [CrateType::Rlib]);
match sess_lto {
Lto::ThinLocal if !linker_does_lto && !is_allocator => ComputedLtoType::Thin,
Lto::ThinLocal if !linker_does_lto => ComputedLtoType::Thin,
Lto::Thin if !linker_does_lto && !is_rlib => ComputedLtoType::Thin,
Lto::Fat if !is_rlib => ComputedLtoType::Fat,
_ => ComputedLtoType::No,
@ -832,30 +843,24 @@ pub(crate) fn compute_per_cgu_lto_type(
fn execute_optimize_work_item<B: ExtraBackendMethods>(
cgcx: &CodegenContext<B>,
shared_emitter: SharedEmitter,
mut module: ModuleCodegen<B::Module>,
) -> WorkItemResult<B> {
let _timer = cgcx.prof.generic_activity_with_arg("codegen_module_optimize", &*module.name);
let dcx = cgcx.create_dcx();
let dcx = dcx.handle();
let module_config = match module.kind {
ModuleKind::Regular => &cgcx.module_config,
ModuleKind::Allocator => &cgcx.allocator_config,
};
B::optimize(cgcx, dcx, &mut module, module_config);
B::optimize(cgcx, &shared_emitter, &mut module, &cgcx.module_config);
// After we've done the initial round of optimizations we need to
// decide whether to synchronously codegen this module or ship it
// back to the coordinator thread for further LTO processing (which
// has to wait for all the initial modules to be optimized).
let lto_type = compute_per_cgu_lto_type(&cgcx.lto, &cgcx.opts, &cgcx.crate_types, module.kind);
let lto_type =
compute_per_cgu_lto_type(&cgcx.lto, cgcx.use_linker_plugin_lto, &cgcx.crate_types);
// If we're doing some form of incremental LTO then we need to be sure to
// save our module to disk first.
let bitcode = if module_config.emit_pre_lto_bc {
let bitcode = if cgcx.module_config.emit_pre_lto_bc {
let filename = pre_lto_bitcode_filename(&module.name);
cgcx.incr_comp_session_dir.as_ref().map(|path| path.join(&filename))
} else {
@ -864,7 +869,7 @@ fn execute_optimize_work_item<B: ExtraBackendMethods>(
match lto_type {
ComputedLtoType::No => {
let module = B::codegen(cgcx, module, module_config);
let module = B::codegen(cgcx, &shared_emitter, module, &cgcx.module_config);
WorkItemResult::Finished(module)
}
ComputedLtoType::Thin => {
@ -894,12 +899,16 @@ fn execute_optimize_work_item<B: ExtraBackendMethods>(
fn execute_copy_from_cache_work_item<B: ExtraBackendMethods>(
cgcx: &CodegenContext<B>,
shared_emitter: SharedEmitter,
module: CachedModuleCodegen,
) -> CompiledModule {
let _timer = cgcx
.prof
.generic_activity_with_arg("codegen_copy_artifacts_from_incr_cache", &*module.name);
let dcx = DiagCtxt::new(Box::new(shared_emitter));
let dcx = dcx.handle();
let incr_comp_session_dir = cgcx.incr_comp_session_dir.as_ref().unwrap();
let mut links_from_incr_cache = Vec::new();
@ -918,11 +927,7 @@ fn execute_copy_from_cache_work_item<B: ExtraBackendMethods>(
Some(output_path)
}
Err(error) => {
cgcx.create_dcx().handle().emit_err(errors::CopyPathBuf {
source_file,
output_path,
error,
});
dcx.emit_err(errors::CopyPathBuf { source_file, output_path, error });
None
}
}
@ -965,7 +970,7 @@ fn execute_copy_from_cache_work_item<B: ExtraBackendMethods>(
let bytecode = load_from_incr_cache(module_config.emit_bc, OutputType::Bitcode);
let object = load_from_incr_cache(should_emit_obj, OutputType::Object);
if should_emit_obj && object.is_none() {
cgcx.create_dcx().handle().emit_fatal(errors::NoSavedObjectFile { cgu_name: &module.name })
dcx.emit_fatal(errors::NoSavedObjectFile { cgu_name: &module.name })
}
CompiledModule {
@ -982,6 +987,7 @@ fn execute_copy_from_cache_work_item<B: ExtraBackendMethods>(
fn do_fat_lto<B: ExtraBackendMethods>(
cgcx: &CodegenContext<B>,
shared_emitter: SharedEmitter,
exported_symbols_for_lto: &[String],
each_linked_rlib_for_lto: &[PathBuf],
mut needs_fat_lto: Vec<FatLtoInput<B>>,
@ -989,7 +995,10 @@ fn do_fat_lto<B: ExtraBackendMethods>(
) -> CompiledModule {
let _timer = cgcx.prof.verbose_generic_activity("LLVM_fatlto");
check_lto_allowed(&cgcx);
let dcx = DiagCtxt::new(Box::new(shared_emitter.clone()));
let dcx = dcx.handle();
check_lto_allowed(&cgcx, dcx);
for (module, wp) in import_only_modules {
needs_fat_lto.push(FatLtoInput::Serialized { name: wp.cgu_name, buffer: module })
@ -997,15 +1006,17 @@ fn do_fat_lto<B: ExtraBackendMethods>(
let module = B::run_and_optimize_fat_lto(
cgcx,
&shared_emitter,
exported_symbols_for_lto,
each_linked_rlib_for_lto,
needs_fat_lto,
);
B::codegen(cgcx, module, &cgcx.module_config)
B::codegen(cgcx, &shared_emitter, module, &cgcx.module_config)
}
fn do_thin_lto<'a, B: ExtraBackendMethods>(
cgcx: &'a CodegenContext<B>,
shared_emitter: SharedEmitter,
exported_symbols_for_lto: Arc<Vec<String>>,
each_linked_rlib_for_lto: Vec<PathBuf>,
needs_thin_lto: Vec<(String, <B as WriteBackendMethods>::ThinBuffer)>,
@ -1016,7 +1027,10 @@ fn do_thin_lto<'a, B: ExtraBackendMethods>(
) -> Vec<CompiledModule> {
let _timer = cgcx.prof.verbose_generic_activity("LLVM_thinlto");
check_lto_allowed(&cgcx);
let dcx = DiagCtxt::new(Box::new(shared_emitter.clone()));
let dcx = dcx.handle();
check_lto_allowed(&cgcx, dcx);
let (coordinator_send, coordinator_receive) = channel();
@ -1041,6 +1055,7 @@ fn do_thin_lto<'a, B: ExtraBackendMethods>(
// we don't worry about tokens.
for (work, cost) in generate_thin_lto_work(
cgcx,
dcx,
&exported_symbols_for_lto,
&each_linked_rlib_for_lto,
needs_thin_lto,
@ -1082,7 +1097,7 @@ fn do_thin_lto<'a, B: ExtraBackendMethods>(
while used_token_count < tokens.len() + 1
&& let Some((item, _)) = work_items.pop()
{
spawn_thin_lto_work(&cgcx, coordinator_send.clone(), item);
spawn_thin_lto_work(&cgcx, shared_emitter.clone(), coordinator_send.clone(), item);
used_token_count += 1;
}
} else {
@ -1106,7 +1121,7 @@ fn do_thin_lto<'a, B: ExtraBackendMethods>(
}
Err(e) => {
let msg = &format!("failed to acquire jobserver token: {e}");
cgcx.diag_emitter.fatal(msg);
shared_emitter.fatal(msg);
codegen_aborted = Some(FatalError);
}
},
@ -1144,12 +1159,13 @@ fn do_thin_lto<'a, B: ExtraBackendMethods>(
fn execute_thin_lto_work_item<B: ExtraBackendMethods>(
cgcx: &CodegenContext<B>,
shared_emitter: SharedEmitter,
module: lto::ThinModule<B>,
) -> CompiledModule {
let _timer = cgcx.prof.generic_activity_with_arg("codegen_module_perform_lto", module.name());
let module = B::optimize_thin(cgcx, module);
B::codegen(cgcx, module, &cgcx.module_config)
let module = B::optimize_thin(cgcx, &shared_emitter, module);
B::codegen(cgcx, &shared_emitter, module, &cgcx.module_config)
}
/// Messages sent to the coordinator.
@ -1245,9 +1261,9 @@ fn start_executing_work<B: ExtraBackendMethods>(
coordinator_receive: Receiver<Message<B>>,
regular_config: Arc<ModuleConfig>,
allocator_config: Arc<ModuleConfig>,
allocator_module: Option<ModuleCodegen<B::Module>>,
mut allocator_module: Option<ModuleCodegen<B::Module>>,
coordinator_send: Sender<Message<B>>,
) -> thread::JoinHandle<Result<CompiledModules, ()>> {
) -> thread::JoinHandle<Result<MaybeLtoModules<B>, ()>> {
let sess = tcx.sess;
let mut each_linked_rlib_for_lto = Vec::new();
@ -1292,18 +1308,18 @@ fn start_executing_work<B: ExtraBackendMethods>(
let cgcx = CodegenContext::<B> {
crate_types: tcx.crate_types().to_vec(),
lto: sess.lto(),
use_linker_plugin_lto: sess.opts.cg.linker_plugin_lto.enabled(),
dylib_lto: sess.opts.unstable_opts.dylib_lto,
prefer_dynamic: sess.opts.cg.prefer_dynamic,
fewer_names: sess.fewer_names(),
save_temps: sess.opts.cg.save_temps,
time_trace: sess.opts.unstable_opts.llvm_time_trace,
opts: Arc::new(sess.opts.clone()),
prof: sess.prof.clone(),
remark: sess.opts.cg.remark.clone(),
remark_dir,
incr_comp_session_dir: sess.incr_comp_session_dir_opt().map(|r| r.clone()),
diag_emitter: shared_emitter.clone(),
output_filenames: Arc::clone(tcx.output_filenames(())),
module_config: regular_config,
allocator_config,
tm_factory: backend.target_machine_factory(tcx.sess, ol, backend_features),
msvc_imps_needed: msvc_imps_needed(tcx),
is_pe_coff: tcx.sess.target.is_like_windows,
@ -1497,16 +1513,9 @@ fn start_executing_work<B: ExtraBackendMethods>(
let mut llvm_start_time: Option<VerboseTimingGuard<'_>> = None;
let compiled_allocator_module = allocator_module.and_then(|allocator_module| {
match execute_optimize_work_item(&cgcx, allocator_module) {
WorkItemResult::Finished(compiled_module) => return Some(compiled_module),
WorkItemResult::NeedsFatLto(fat_lto_input) => needs_fat_lto.push(fat_lto_input),
WorkItemResult::NeedsThinLto(name, thin_buffer) => {
needs_thin_lto.push((name, thin_buffer))
}
}
None
});
if let Some(allocator_module) = &mut allocator_module {
B::optimize(&cgcx, &shared_emitter, allocator_module, &allocator_config);
}
// Run the message loop while there's still anything that needs message
// processing. Note that as soon as codegen is aborted we simply want to
@ -1543,7 +1552,13 @@ fn start_executing_work<B: ExtraBackendMethods>(
let (item, _) =
work_items.pop().expect("queue empty - queue_full_enough() broken?");
main_thread_state = MainThreadState::Lending;
spawn_work(&cgcx, coordinator_send.clone(), &mut llvm_start_time, item);
spawn_work(
&cgcx,
shared_emitter.clone(),
coordinator_send.clone(),
&mut llvm_start_time,
item,
);
}
}
} else if codegen_state == Completed {
@ -1561,7 +1576,13 @@ fn start_executing_work<B: ExtraBackendMethods>(
MainThreadState::Idle => {
if let Some((item, _)) = work_items.pop() {
main_thread_state = MainThreadState::Lending;
spawn_work(&cgcx, coordinator_send.clone(), &mut llvm_start_time, item);
spawn_work(
&cgcx,
shared_emitter.clone(),
coordinator_send.clone(),
&mut llvm_start_time,
item,
);
} else {
// There is no unstarted work, so let the main thread
// take over for a running worker. Otherwise the
@ -1597,7 +1618,13 @@ fn start_executing_work<B: ExtraBackendMethods>(
while running_with_own_token < tokens.len()
&& let Some((item, _)) = work_items.pop()
{
spawn_work(&cgcx, coordinator_send.clone(), &mut llvm_start_time, item);
spawn_work(
&cgcx,
shared_emitter.clone(),
coordinator_send.clone(),
&mut llvm_start_time,
item,
);
running_with_own_token += 1;
}
}
@ -1733,36 +1760,51 @@ fn start_executing_work<B: ExtraBackendMethods>(
assert!(compiled_modules.is_empty());
assert!(needs_thin_lto.is_empty());
// This uses the implicit token
let module = do_fat_lto(
&cgcx,
&exported_symbols_for_lto,
&each_linked_rlib_file_for_lto,
if let Some(allocator_module) = allocator_module.take() {
needs_fat_lto.push(FatLtoInput::InMemory(allocator_module));
}
return Ok(MaybeLtoModules::FatLto {
cgcx,
exported_symbols_for_lto,
each_linked_rlib_file_for_lto,
needs_fat_lto,
lto_import_only_modules,
);
compiled_modules.push(module);
});
} else if !needs_thin_lto.is_empty() || !lto_import_only_modules.is_empty() {
assert!(compiled_modules.is_empty());
assert!(needs_fat_lto.is_empty());
compiled_modules.extend(do_thin_lto(
&cgcx,
exported_symbols_for_lto,
each_linked_rlib_file_for_lto,
needs_thin_lto,
lto_import_only_modules,
));
if cgcx.lto == Lto::ThinLocal {
compiled_modules.extend(do_thin_lto(
&cgcx,
shared_emitter.clone(),
exported_symbols_for_lto,
each_linked_rlib_file_for_lto,
needs_thin_lto,
lto_import_only_modules,
));
} else {
if let Some(allocator_module) = allocator_module.take() {
let (name, thin_buffer) = B::prepare_thin(allocator_module);
needs_thin_lto.push((name, thin_buffer));
}
return Ok(MaybeLtoModules::ThinLto {
cgcx,
exported_symbols_for_lto,
each_linked_rlib_file_for_lto,
needs_thin_lto,
lto_import_only_modules,
});
}
}
// Regardless of what order these modules completed in, report them to
// the backend in the same order every time to ensure that we're handing
// out deterministic results.
compiled_modules.sort_by(|a, b| a.name.cmp(&b.name));
Ok(CompiledModules {
Ok(MaybeLtoModules::NoLto {
modules: compiled_modules,
allocator_module: compiled_allocator_module,
allocator_module: allocator_module.map(|allocator_module| {
B::codegen(&cgcx, &shared_emitter, allocator_module, &allocator_config)
}),
})
})
.expect("failed to spawn coordinator thread");
@ -1831,6 +1873,7 @@ pub(crate) struct WorkerFatalError;
fn spawn_work<'a, B: ExtraBackendMethods>(
cgcx: &'a CodegenContext<B>,
shared_emitter: SharedEmitter,
coordinator_send: Sender<Message<B>>,
llvm_start_time: &mut Option<VerboseTimingGuard<'a>>,
work: WorkItem<B>,
@ -1843,10 +1886,10 @@ fn spawn_work<'a, B: ExtraBackendMethods>(
B::spawn_named_thread(cgcx.time_trace, work.short_description(), move || {
let result = std::panic::catch_unwind(AssertUnwindSafe(|| match work {
WorkItem::Optimize(m) => execute_optimize_work_item(&cgcx, m),
WorkItem::CopyPostLtoArtifacts(m) => {
WorkItemResult::Finished(execute_copy_from_cache_work_item(&cgcx, m))
}
WorkItem::Optimize(m) => execute_optimize_work_item(&cgcx, shared_emitter, m),
WorkItem::CopyPostLtoArtifacts(m) => WorkItemResult::Finished(
execute_copy_from_cache_work_item(&cgcx, shared_emitter, m),
),
}));
let msg = match result {
@ -1868,6 +1911,7 @@ fn spawn_work<'a, B: ExtraBackendMethods>(
fn spawn_thin_lto_work<'a, B: ExtraBackendMethods>(
cgcx: &'a CodegenContext<B>,
shared_emitter: SharedEmitter,
coordinator_send: Sender<ThinLtoMessage>,
work: ThinLtoWorkItem<B>,
) {
@ -1875,8 +1919,10 @@ fn spawn_thin_lto_work<'a, B: ExtraBackendMethods>(
B::spawn_named_thread(cgcx.time_trace, work.short_description(), move || {
let result = std::panic::catch_unwind(AssertUnwindSafe(|| match work {
ThinLtoWorkItem::CopyPostLtoArtifacts(m) => execute_copy_from_cache_work_item(&cgcx, m),
ThinLtoWorkItem::ThinLto(m) => execute_thin_lto_work_item(&cgcx, m),
ThinLtoWorkItem::CopyPostLtoArtifacts(m) => {
execute_copy_from_cache_work_item(&cgcx, shared_emitter, m)
}
ThinLtoWorkItem::ThinLto(m) => execute_thin_lto_work_item(&cgcx, shared_emitter, m),
}));
let msg = match result {
@ -2052,13 +2098,13 @@ impl SharedEmitterMain {
pub struct Coordinator<B: ExtraBackendMethods> {
sender: Sender<Message<B>>,
future: Option<thread::JoinHandle<Result<CompiledModules, ()>>>,
future: Option<thread::JoinHandle<Result<MaybeLtoModules<B>, ()>>>,
// Only used for the Message type.
phantom: PhantomData<B>,
}
impl<B: ExtraBackendMethods> Coordinator<B> {
fn join(mut self) -> std::thread::Result<Result<CompiledModules, ()>> {
fn join(mut self) -> std::thread::Result<Result<MaybeLtoModules<B>, ()>> {
self.future.take().unwrap().join()
}
}
@ -2089,8 +2135,9 @@ pub struct OngoingCodegen<B: ExtraBackendMethods> {
impl<B: ExtraBackendMethods> OngoingCodegen<B> {
pub fn join(self, sess: &Session) -> (CodegenResults, FxIndexMap<WorkProductId, WorkProduct>) {
self.shared_emitter_main.check(sess, true);
let compiled_modules = sess.time("join_worker_thread", || match self.coordinator.join() {
Ok(Ok(compiled_modules)) => compiled_modules,
let maybe_lto_modules = sess.time("join_worker_thread", || match self.coordinator.join() {
Ok(Ok(maybe_lto_modules)) => maybe_lto_modules,
Ok(Err(())) => {
sess.dcx().abort_if_errors();
panic!("expected abort due to worker thread errors")
@ -2102,6 +2149,62 @@ impl<B: ExtraBackendMethods> OngoingCodegen<B> {
sess.dcx().abort_if_errors();
let (shared_emitter, shared_emitter_main) = SharedEmitter::new();
// Catch fatal errors to ensure shared_emitter_main.check() can emit the actual diagnostics
let compiled_modules = catch_fatal_errors(|| match maybe_lto_modules {
MaybeLtoModules::NoLto { modules, allocator_module } => {
drop(shared_emitter);
CompiledModules { modules, allocator_module }
}
MaybeLtoModules::FatLto {
cgcx,
exported_symbols_for_lto,
each_linked_rlib_file_for_lto,
needs_fat_lto,
lto_import_only_modules,
} => CompiledModules {
modules: vec![do_fat_lto(
&cgcx,
shared_emitter,
&exported_symbols_for_lto,
&each_linked_rlib_file_for_lto,
needs_fat_lto,
lto_import_only_modules,
)],
allocator_module: None,
},
MaybeLtoModules::ThinLto {
cgcx,
exported_symbols_for_lto,
each_linked_rlib_file_for_lto,
needs_thin_lto,
lto_import_only_modules,
} => CompiledModules {
modules: do_thin_lto(
&cgcx,
shared_emitter,
exported_symbols_for_lto,
each_linked_rlib_file_for_lto,
needs_thin_lto,
lto_import_only_modules,
),
allocator_module: None,
},
});
shared_emitter_main.check(sess, true);
sess.dcx().abort_if_errors();
let mut compiled_modules =
compiled_modules.expect("fatal error emitted but not sent to SharedEmitter");
// Regardless of what order these modules completed in, report them to
// the backend in the same order every time to ensure that we're handing
// out deterministic results.
compiled_modules.modules.sort_by(|a, b| a.name.cmp(&b.name));
let work_products =
copy_all_cgu_workproducts_to_incr_comp_cache_dir(sess, &compiled_modules);
produce_final_output_artifacts(sess, &compiled_modules, &self.output_filenames);

View file

@ -49,9 +49,7 @@ use crate::meth::load_vtable;
use crate::mir::operand::OperandValue;
use crate::mir::place::PlaceRef;
use crate::traits::*;
use crate::{
CachedModuleCodegen, CodegenLintLevels, CrateInfo, ModuleCodegen, ModuleKind, errors, meth, mir,
};
use crate::{CachedModuleCodegen, CodegenLintLevels, CrateInfo, ModuleCodegen, errors, meth, mir};
pub(crate) fn bin_op_to_icmp_predicate(op: BinOp, signed: bool) -> IntPredicate {
match (op, signed) {
@ -1011,7 +1009,7 @@ impl CrateInfo {
info.linked_symbols
.iter_mut()
.filter(|(crate_type, _)| {
!matches!(crate_type, CrateType::Rlib | CrateType::Staticlib)
!matches!(crate_type, CrateType::Rlib | CrateType::StaticLib)
})
.for_each(|(_, linked_symbols)| {
let mut symbols = missing_weak_lang_items
@ -1043,7 +1041,7 @@ impl CrateInfo {
// this is a rare use case and we don't want to slow down the common case.
false
}
CrateType::Staticlib | CrateType::Rlib => {
CrateType::StaticLib | CrateType::Rlib => {
// We don't invoke the linker for these, so we don't need to collect the NatVis for
// them.
false
@ -1126,9 +1124,8 @@ pub fn determine_cgu_reuse<'tcx>(tcx: TyCtxt<'tcx>, cgu: &CodegenUnit<'tcx>) ->
// reuse pre-LTO artifacts
match compute_per_cgu_lto_type(
&tcx.sess.lto(),
&tcx.sess.opts,
tcx.sess.opts.cg.linker_plugin_lto.enabled(),
tcx.crate_types(),
ModuleKind::Regular,
) {
ComputedLtoType::No => CguReuse::PostLto,
_ => CguReuse::PreLto,

View file

@ -2,7 +2,7 @@ use std::str::FromStr;
use rustc_abi::{Align, ExternAbi};
use rustc_ast::expand::autodiff_attrs::{AutoDiffAttrs, DiffActivity, DiffMode};
use rustc_ast::{LitKind, MetaItem, MetaItemInner, attr};
use rustc_ast::{LitKind, MetaItem, MetaItemInner};
use rustc_hir::attrs::{
AttributeKind, EiiImplResolution, InlineAttr, Linkage, RtsanSetting, UsedBy,
};
@ -47,59 +47,6 @@ fn try_fn_sig<'tcx>(
}
}
// FIXME(jdonszelmann): remove when patchable_function_entry becomes a parsed attr
fn parse_patchable_function_entry(
tcx: TyCtxt<'_>,
attr: &Attribute,
) -> Option<PatchableFunctionEntry> {
attr.meta_item_list().and_then(|l| {
let mut prefix = None;
let mut entry = None;
for item in l {
let Some(meta_item) = item.meta_item() else {
tcx.dcx().emit_err(errors::ExpectedNameValuePair { span: item.span() });
continue;
};
let Some(name_value_lit) = meta_item.name_value_literal() else {
tcx.dcx().emit_err(errors::ExpectedNameValuePair { span: item.span() });
continue;
};
let attrib_to_write = match meta_item.name() {
Some(sym::prefix_nops) => &mut prefix,
Some(sym::entry_nops) => &mut entry,
_ => {
tcx.dcx().emit_err(errors::UnexpectedParameterName {
span: item.span(),
prefix_nops: sym::prefix_nops,
entry_nops: sym::entry_nops,
});
continue;
}
};
let rustc_ast::LitKind::Int(val, _) = name_value_lit.kind else {
tcx.dcx().emit_err(errors::InvalidLiteralValue { span: name_value_lit.span });
continue;
};
let Ok(val) = val.get().try_into() else {
tcx.dcx().emit_err(errors::OutOfRangeInteger { span: name_value_lit.span });
continue;
};
*attrib_to_write = Some(val);
}
if let (None, None) = (prefix, entry) {
tcx.dcx().span_err(attr.span(), "must specify at least one parameter");
}
Some(PatchableFunctionEntry::from_prefix_and_entry(prefix.unwrap_or(0), entry.unwrap_or(0)))
})
}
/// Spans that are collected when processing built-in attributes,
/// that are useful for emitting diagnostics later.
#[derive(Default)]
@ -121,250 +68,235 @@ fn process_builtin_attrs(
let mut interesting_spans = InterestingAttributeDiagnosticSpans::default();
let rust_target_features = tcx.rust_target_features(LOCAL_CRATE);
for attr in attrs.iter() {
if let hir::Attribute::Parsed(p) = attr {
match p {
AttributeKind::Cold(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::COLD,
AttributeKind::ExportName { name, .. } => {
codegen_fn_attrs.symbol_name = Some(*name)
let parsed_attrs = attrs
.iter()
.filter_map(|attr| if let hir::Attribute::Parsed(attr) = attr { Some(attr) } else { None });
for attr in parsed_attrs {
match attr {
AttributeKind::Cold(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::COLD,
AttributeKind::ExportName { name, .. } => codegen_fn_attrs.symbol_name = Some(*name),
AttributeKind::Inline(inline, span) => {
codegen_fn_attrs.inline = *inline;
interesting_spans.inline = Some(*span);
}
AttributeKind::Naked(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED,
AttributeKind::Align { align, .. } => codegen_fn_attrs.alignment = Some(*align),
AttributeKind::LinkName { name, .. } => {
// FIXME Remove check for foreign functions once #[link_name] on non-foreign
// functions is a hard error
if tcx.is_foreign_item(did) {
codegen_fn_attrs.symbol_name = Some(*name);
}
AttributeKind::Inline(inline, span) => {
codegen_fn_attrs.inline = *inline;
interesting_spans.inline = Some(*span);
}
AttributeKind::LinkOrdinal { ordinal, span } => {
codegen_fn_attrs.link_ordinal = Some(*ordinal);
interesting_spans.link_ordinal = Some(*span);
}
AttributeKind::LinkSection { name, .. } => codegen_fn_attrs.link_section = Some(*name),
AttributeKind::NoMangle(attr_span) => {
interesting_spans.no_mangle = Some(*attr_span);
if tcx.opt_item_name(did.to_def_id()).is_some() {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE;
} else {
tcx.dcx()
.span_delayed_bug(*attr_span, "no_mangle should be on a named function");
}
AttributeKind::Naked(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED,
AttributeKind::Align { align, .. } => codegen_fn_attrs.alignment = Some(*align),
AttributeKind::LinkName { name, .. } => {
// FIXME Remove check for foreign functions once #[link_name] on non-foreign
// functions is a hard error
if tcx.is_foreign_item(did) {
codegen_fn_attrs.symbol_name = Some(*name);
}
}
AttributeKind::LinkOrdinal { ordinal, span } => {
codegen_fn_attrs.link_ordinal = Some(*ordinal);
interesting_spans.link_ordinal = Some(*span);
}
AttributeKind::LinkSection { name, .. } => {
codegen_fn_attrs.link_section = Some(*name)
}
AttributeKind::NoMangle(attr_span) => {
interesting_spans.no_mangle = Some(*attr_span);
if tcx.opt_item_name(did.to_def_id()).is_some() {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE;
}
AttributeKind::Optimize(optimize, _) => codegen_fn_attrs.optimize = *optimize,
AttributeKind::TargetFeature { features, attr_span, was_forced } => {
let Some(sig) = tcx.hir_node_by_def_id(did).fn_sig() else {
tcx.dcx().span_delayed_bug(*attr_span, "target_feature applied to non-fn");
continue;
};
let safe_target_features =
matches!(sig.header.safety, hir::HeaderSafety::SafeTargetFeatures);
codegen_fn_attrs.safe_target_features = safe_target_features;
if safe_target_features && !was_forced {
if tcx.sess.target.is_like_wasm || tcx.sess.opts.actually_rustdoc {
// The `#[target_feature]` attribute is allowed on
// WebAssembly targets on all functions. Prior to stabilizing
// the `target_feature_11` feature, `#[target_feature]` was
// only permitted on unsafe functions because on most targets
// execution of instructions that are not supported is
// considered undefined behavior. For WebAssembly which is a
// 100% safe target at execution time it's not possible to
// execute undefined instructions, and even if a future
// feature was added in some form for this it would be a
// deterministic trap. There is no undefined behavior when
// executing WebAssembly so `#[target_feature]` is allowed
// on safe functions (but again, only for WebAssembly)
//
// Note that this is also allowed if `actually_rustdoc` so
// if a target is documenting some wasm-specific code then
// it's not spuriously denied.
//
// Now that `#[target_feature]` is permitted on safe functions,
// this exception must still exist for allowing the attribute on
// `main`, `start`, and other functions that are not usually
// allowed.
} else {
tcx.dcx().span_delayed_bug(
*attr_span,
"no_mangle should be on a named function",
);
check_target_feature_trait_unsafe(tcx, did, *attr_span);
}
}
AttributeKind::Optimize(optimize, _) => codegen_fn_attrs.optimize = *optimize,
AttributeKind::TargetFeature { features, attr_span, was_forced } => {
let Some(sig) = tcx.hir_node_by_def_id(did).fn_sig() else {
tcx.dcx().span_delayed_bug(*attr_span, "target_feature applied to non-fn");
continue;
from_target_feature_attr(
tcx,
did,
features,
*was_forced,
rust_target_features,
&mut codegen_fn_attrs.target_features,
);
}
AttributeKind::TrackCaller(attr_span) => {
let is_closure = tcx.is_closure_like(did.to_def_id());
if !is_closure
&& let Some(fn_sig) = try_fn_sig(tcx, did, *attr_span)
&& fn_sig.skip_binder().abi() != ExternAbi::Rust
{
tcx.dcx().emit_err(errors::RequiresRustAbi { span: *attr_span });
}
if is_closure
&& !tcx.features().closure_track_caller()
&& !attr_span.allows_unstable(sym::closure_track_caller)
{
feature_err(
&tcx.sess,
sym::closure_track_caller,
*attr_span,
"`#[track_caller]` on closures is currently unstable",
)
.emit();
}
codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER
}
AttributeKind::Used { used_by, .. } => match used_by {
UsedBy::Compiler => codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED_COMPILER,
UsedBy::Linker => codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED_LINKER,
UsedBy::Default => {
let used_form = if tcx.sess.target.os == Os::Illumos {
// illumos' `ld` doesn't support a section header that would represent
// `#[used(linker)]`, see
// https://github.com/rust-lang/rust/issues/146169. For that target,
// downgrade as if `#[used(compiler)]` was requested and hope for the
// best.
CodegenFnAttrFlags::USED_COMPILER
} else {
CodegenFnAttrFlags::USED_LINKER
};
let safe_target_features =
matches!(sig.header.safety, hir::HeaderSafety::SafeTargetFeatures);
codegen_fn_attrs.safe_target_features = safe_target_features;
if safe_target_features && !was_forced {
if tcx.sess.target.is_like_wasm || tcx.sess.opts.actually_rustdoc {
// The `#[target_feature]` attribute is allowed on
// WebAssembly targets on all functions. Prior to stabilizing
// the `target_feature_11` feature, `#[target_feature]` was
// only permitted on unsafe functions because on most targets
// execution of instructions that are not supported is
// considered undefined behavior. For WebAssembly which is a
// 100% safe target at execution time it's not possible to
// execute undefined instructions, and even if a future
// feature was added in some form for this it would be a
// deterministic trap. There is no undefined behavior when
// executing WebAssembly so `#[target_feature]` is allowed
// on safe functions (but again, only for WebAssembly)
//
// Note that this is also allowed if `actually_rustdoc` so
// if a target is documenting some wasm-specific code then
// it's not spuriously denied.
//
// Now that `#[target_feature]` is permitted on safe functions,
// this exception must still exist for allowing the attribute on
// `main`, `start`, and other functions that are not usually
// allowed.
} else {
check_target_feature_trait_unsafe(tcx, did, *attr_span);
codegen_fn_attrs.flags |= used_form;
}
},
AttributeKind::FfiConst(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_CONST,
AttributeKind::FfiPure(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_PURE,
AttributeKind::StdInternalSymbol(_) => {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL
}
AttributeKind::Linkage(linkage, span) => {
let linkage = Some(*linkage);
if tcx.is_foreign_item(did) {
codegen_fn_attrs.import_linkage = linkage;
if tcx.is_mutable_static(did.into()) {
let mut diag = tcx.dcx().struct_span_err(
*span,
"extern mutable statics are not allowed with `#[linkage]`",
);
diag.note(
"marking the extern static mutable would allow changing which \
symbol the static references rather than make the target of the \
symbol mutable",
);
diag.emit();
}
} else {
codegen_fn_attrs.linkage = linkage;
}
}
AttributeKind::Sanitize { span, .. } => {
interesting_spans.sanitize = Some(*span);
}
AttributeKind::ObjcClass { classname, .. } => {
codegen_fn_attrs.objc_class = Some(*classname);
}
AttributeKind::ObjcSelector { methname, .. } => {
codegen_fn_attrs.objc_selector = Some(*methname);
}
AttributeKind::EiiForeignItem => {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::EXTERNALLY_IMPLEMENTABLE_ITEM;
}
AttributeKind::EiiImpls(impls) => {
for i in impls {
let foreign_item = match i.resolution {
EiiImplResolution::Macro(def_id) => {
let Some(extern_item) = find_attr!(
tcx.get_all_attrs(def_id),
AttributeKind::EiiDeclaration(target) => target.foreign_item
) else {
tcx.dcx().span_delayed_bug(
i.span,
"resolved to something that's not an EII",
);
continue;
};
extern_item
}
}
from_target_feature_attr(
tcx,
did,
features,
*was_forced,
rust_target_features,
&mut codegen_fn_attrs.target_features,
);
}
AttributeKind::TrackCaller(attr_span) => {
let is_closure = tcx.is_closure_like(did.to_def_id());
EiiImplResolution::Known(decl) => decl.foreign_item,
EiiImplResolution::Error(_eg) => continue,
};
if !is_closure
&& let Some(fn_sig) = try_fn_sig(tcx, did, *attr_span)
&& fn_sig.skip_binder().abi() != ExternAbi::Rust
// this is to prevent a bug where a single crate defines both the default and explicit implementation
// for an EII. In that case, both of them may be part of the same final object file. I'm not 100% sure
// what happens, either rustc deduplicates the symbol or llvm, or it's random/order-dependent.
// However, the fact that the default one of has weak linkage isn't considered and you sometimes get that
// the default implementation is used while an explicit implementation is given.
if
// if this is a default impl
i.is_default
// iterate over all implementations *in the current crate*
// (this is ok since we generate codegen fn attrs in the local crate)
// if any of them is *not default* then don't emit the alias.
&& tcx.externally_implementable_items(LOCAL_CRATE).get(&foreign_item).expect("at least one").1.iter().any(|(_, imp)| !imp.is_default)
{
tcx.dcx().emit_err(errors::RequiresRustAbi { span: *attr_span });
continue;
}
if is_closure
&& !tcx.features().closure_track_caller()
&& !attr_span.allows_unstable(sym::closure_track_caller)
{
feature_err(
&tcx.sess,
sym::closure_track_caller,
*attr_span,
"`#[track_caller]` on closures is currently unstable",
)
.emit();
}
codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER
}
AttributeKind::Used { used_by, .. } => match used_by {
UsedBy::Compiler => codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED_COMPILER,
UsedBy::Linker => codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED_LINKER,
UsedBy::Default => {
let used_form = if tcx.sess.target.os == Os::Illumos {
// illumos' `ld` doesn't support a section header that would represent
// `#[used(linker)]`, see
// https://github.com/rust-lang/rust/issues/146169. For that target,
// downgrade as if `#[used(compiler)]` was requested and hope for the
// best.
CodegenFnAttrFlags::USED_COMPILER
} else {
CodegenFnAttrFlags::USED_LINKER
};
codegen_fn_attrs.flags |= used_form;
}
},
AttributeKind::FfiConst(_) => {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_CONST
}
AttributeKind::FfiPure(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_PURE,
AttributeKind::StdInternalSymbol(_) => {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL
}
AttributeKind::Linkage(linkage, span) => {
let linkage = Some(*linkage);
if tcx.is_foreign_item(did) {
codegen_fn_attrs.import_linkage = linkage;
if tcx.is_mutable_static(did.into()) {
let mut diag = tcx.dcx().struct_span_err(
*span,
"extern mutable statics are not allowed with `#[linkage]`",
);
diag.note(
"marking the extern static mutable would allow changing which \
symbol the static references rather than make the target of the \
symbol mutable",
);
diag.emit();
}
} else {
codegen_fn_attrs.linkage = linkage;
}
}
AttributeKind::Sanitize { span, .. } => {
interesting_spans.sanitize = Some(*span);
}
AttributeKind::ObjcClass { classname, .. } => {
codegen_fn_attrs.objc_class = Some(*classname);
}
AttributeKind::ObjcSelector { methname, .. } => {
codegen_fn_attrs.objc_selector = Some(*methname);
}
AttributeKind::EiiForeignItem => {
codegen_fn_attrs.foreign_item_symbol_aliases.push((
foreign_item,
if i.is_default { Linkage::LinkOnceAny } else { Linkage::External },
Visibility::Default,
));
codegen_fn_attrs.flags |= CodegenFnAttrFlags::EXTERNALLY_IMPLEMENTABLE_ITEM;
}
AttributeKind::EiiImpls(impls) => {
for i in impls {
let foreign_item = match i.resolution {
EiiImplResolution::Macro(def_id) => {
let Some(extern_item) = find_attr!(
tcx.get_all_attrs(def_id),
AttributeKind::EiiDeclaration(target) => target.foreign_item
) else {
tcx.dcx().span_delayed_bug(
i.span,
"resolved to something that's not an EII",
);
continue;
};
extern_item
}
EiiImplResolution::Known(decl) => decl.foreign_item,
EiiImplResolution::Error(_eg) => continue,
};
// this is to prevent a bug where a single crate defines both the default and explicit implementation
// for an EII. In that case, both of them may be part of the same final object file. I'm not 100% sure
// what happens, either rustc deduplicates the symbol or llvm, or it's random/order-dependent.
// However, the fact that the default one of has weak linkage isn't considered and you sometimes get that
// the default implementation is used while an explicit implementation is given.
if
// if this is a default impl
i.is_default
// iterate over all implementations *in the current crate*
// (this is ok since we generate codegen fn attrs in the local crate)
// if any of them is *not default* then don't emit the alias.
&& tcx.externally_implementable_items(LOCAL_CRATE).get(&foreign_item).expect("at least one").1.iter().any(|(_, imp)| !imp.is_default)
{
continue;
}
codegen_fn_attrs.foreign_item_symbol_aliases.push((
foreign_item,
if i.is_default { Linkage::LinkOnceAny } else { Linkage::External },
Visibility::Default,
));
codegen_fn_attrs.flags |= CodegenFnAttrFlags::EXTERNALLY_IMPLEMENTABLE_ITEM;
}
}
AttributeKind::ThreadLocal => {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::THREAD_LOCAL
}
AttributeKind::InstructionSet(instruction_set) => {
codegen_fn_attrs.instruction_set = Some(*instruction_set)
}
AttributeKind::RustcAllocator => {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR
}
AttributeKind::RustcDeallocator => {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::DEALLOCATOR
}
AttributeKind::RustcReallocator => {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::REALLOCATOR
}
AttributeKind::RustcAllocatorZeroed => {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR_ZEROED
}
AttributeKind::RustcNounwind => {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::NEVER_UNWIND
}
AttributeKind::RustcOffloadKernel => {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::OFFLOAD_KERNEL
}
_ => {}
}
}
let Some(name) = attr.name() else {
continue;
};
match name {
sym::patchable_function_entry => {
AttributeKind::ThreadLocal => {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::THREAD_LOCAL
}
AttributeKind::InstructionSet(instruction_set) => {
codegen_fn_attrs.instruction_set = Some(*instruction_set)
}
AttributeKind::RustcAllocator => {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR
}
AttributeKind::RustcDeallocator => {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::DEALLOCATOR
}
AttributeKind::RustcReallocator => {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::REALLOCATOR
}
AttributeKind::RustcAllocatorZeroed => {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR_ZEROED
}
AttributeKind::RustcNounwind => {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::NEVER_UNWIND
}
AttributeKind::RustcOffloadKernel => {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::OFFLOAD_KERNEL
}
AttributeKind::PatchableFunctionEntry { prefix, entry } => {
codegen_fn_attrs.patchable_function_entry =
parse_patchable_function_entry(tcx, attr);
Some(PatchableFunctionEntry::from_prefix_and_entry(*prefix, *entry));
}
_ => {}
}
@ -421,7 +353,7 @@ fn apply_overrides(tcx: TyCtxt<'_>, did: LocalDefId, codegen_fn_attrs: &mut Code
// When `no_builtins` is applied at the crate level, we should add the
// `no-builtins` attribute to each function to ensure it takes effect in LTO.
let crate_attrs = tcx.hir_attrs(rustc_hir::CRATE_HIR_ID);
let no_builtins = attr::contains_name(crate_attrs, sym::no_builtins);
let no_builtins = find_attr!(crate_attrs, AttributeKind::NoBuiltins);
if no_builtins {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_BUILTINS;
}

View file

@ -109,14 +109,14 @@ fn push_debuginfo_type_name<'tcx>(
ty_and_layout,
&|output, visited| {
push_item_name(tcx, def.did(), true, output);
push_generic_params_internal(tcx, args, output, visited);
push_generic_args_internal(tcx, args, output, visited);
},
output,
visited,
);
} else {
push_item_name(tcx, def.did(), qualified, output);
push_generic_params_internal(tcx, args, output, visited);
push_generic_args_internal(tcx, args, output, visited);
}
}
ty::Tuple(component_types) => {
@ -253,19 +253,18 @@ fn push_debuginfo_type_name<'tcx>(
);
push_item_name(tcx, principal.def_id, qualified, output);
let principal_has_generic_params =
push_generic_params_internal(tcx, principal.args, output, visited);
push_generic_args_internal(tcx, principal.args, output, visited);
let projection_bounds: SmallVec<[_; 4]> = trait_data
.projection_bounds()
.map(|bound| {
let ExistentialProjection { def_id: item_def_id, term, .. } =
tcx.instantiate_bound_regions_with_erased(bound);
// FIXME(mgca): allow for consts here
(item_def_id, term.expect_type())
(item_def_id, term)
})
.collect();
if projection_bounds.len() != 0 {
if !projection_bounds.is_empty() {
if principal_has_generic_params {
// push_generic_params_internal() above added a `>` but we actually
// want to add more items to that list, so remove that again...
@ -279,17 +278,17 @@ fn push_debuginfo_type_name<'tcx>(
output.push('<');
}
for (item_def_id, ty) in projection_bounds {
for (item_def_id, term) in projection_bounds {
if cpp_like_debuginfo {
output.push_str("assoc$<");
push_item_name(tcx, item_def_id, false, output);
push_arg_separator(cpp_like_debuginfo, output);
push_debuginfo_type_name(tcx, ty, true, output, visited);
push_debuginfo_term_name(tcx, term, true, output, visited);
push_close_angle_bracket(cpp_like_debuginfo, output);
} else {
push_item_name(tcx, item_def_id, false, output);
output.push('=');
push_debuginfo_type_name(tcx, ty, true, output, visited);
push_debuginfo_term_name(tcx, term, true, output, visited);
}
push_arg_separator(cpp_like_debuginfo, output);
}
@ -533,7 +532,7 @@ pub fn compute_debuginfo_vtable_name<'tcx>(
tcx.normalize_erasing_regions(ty::TypingEnv::fully_monomorphized(), trait_ref);
push_item_name(tcx, trait_ref.def_id, true, &mut vtable_name);
visited.clear();
push_generic_params_internal(tcx, trait_ref.args, &mut vtable_name, &mut visited);
push_generic_args_internal(tcx, trait_ref.args, &mut vtable_name, &mut visited);
} else {
vtable_name.push('_');
}
@ -631,7 +630,13 @@ fn push_unqualified_item_name(
};
}
fn push_generic_params_internal<'tcx>(
pub fn push_generic_args<'tcx>(tcx: TyCtxt<'tcx>, args: GenericArgsRef<'tcx>, output: &mut String) {
let _prof = tcx.prof.generic_activity("compute_debuginfo_type_name");
let mut visited = FxHashSet::default();
push_generic_args_internal(tcx, args, output, &mut visited);
}
fn push_generic_args_internal<'tcx>(
tcx: TyCtxt<'tcx>,
args: GenericArgsRef<'tcx>,
output: &mut String,
@ -646,14 +651,10 @@ fn push_generic_params_internal<'tcx>(
output.push('<');
for type_parameter in args {
match type_parameter {
GenericArgKind::Type(type_parameter) => {
push_debuginfo_type_name(tcx, type_parameter, true, output, visited);
}
GenericArgKind::Const(ct) => {
push_const_param(tcx, ct, output);
}
for arg in args {
match arg {
GenericArgKind::Type(ty) => push_debuginfo_type_name(tcx, ty, true, output, visited),
GenericArgKind::Const(ct) => push_debuginfo_const_name(tcx, ct, output),
other => bug!("Unexpected non-erasable generic: {:?}", other),
}
@ -665,7 +666,20 @@ fn push_generic_params_internal<'tcx>(
true
}
fn push_const_param<'tcx>(tcx: TyCtxt<'tcx>, ct: ty::Const<'tcx>, output: &mut String) {
fn push_debuginfo_term_name<'tcx>(
tcx: TyCtxt<'tcx>,
term: ty::Term<'tcx>,
qualified: bool,
output: &mut String,
visited: &mut FxHashSet<Ty<'tcx>>,
) {
match term.kind() {
ty::TermKind::Ty(ty) => push_debuginfo_type_name(tcx, ty, qualified, output, visited),
ty::TermKind::Const(ct) => push_debuginfo_const_name(tcx, ct, output),
}
}
fn push_debuginfo_const_name<'tcx>(tcx: TyCtxt<'tcx>, ct: ty::Const<'tcx>, output: &mut String) {
match ct.kind() {
ty::ConstKind::Param(param) => {
write!(output, "{}", param.name)
@ -715,16 +729,6 @@ fn push_const_param<'tcx>(tcx: TyCtxt<'tcx>, ct: ty::Const<'tcx>, output: &mut S
.unwrap();
}
pub fn push_generic_params<'tcx>(
tcx: TyCtxt<'tcx>,
args: GenericArgsRef<'tcx>,
output: &mut String,
) {
let _prof = tcx.prof.generic_activity("compute_debuginfo_type_name");
let mut visited = FxHashSet::default();
push_generic_params_internal(tcx, args, output, &mut visited);
}
fn push_closure_or_coroutine_name<'tcx>(
tcx: TyCtxt<'tcx>,
def_id: DefId,
@ -767,7 +771,7 @@ fn push_closure_or_coroutine_name<'tcx>(
// FIXME(async_closures): This is probably not going to be correct w.r.t.
// multiple coroutine flavors. Maybe truncate to (parent + 1)?
let args = args.truncate_to(tcx, generics);
push_generic_params_internal(tcx, args, output, visited);
push_generic_args_internal(tcx, args, output, visited);
}
fn push_close_angle_bracket(cpp_like_debuginfo: bool, output: &mut String) {

View file

@ -136,39 +136,6 @@ pub(crate) struct RequiresRustAbi {
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(codegen_ssa_expected_name_value_pair)]
pub(crate) struct ExpectedNameValuePair {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(codegen_ssa_unexpected_parameter_name)]
pub(crate) struct UnexpectedParameterName {
#[primary_span]
#[label]
pub span: Span,
pub prefix_nops: Symbol,
pub entry_nops: Symbol,
}
#[derive(Diagnostic)]
#[diag(codegen_ssa_invalid_literal_value)]
pub(crate) struct InvalidLiteralValue {
#[primary_span]
#[label]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(codegen_ssa_out_of_range_integer)]
pub(crate) struct OutOfRangeInteger {
#[primary_span]
#[label]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(codegen_ssa_copy_path_buf)]
pub(crate) struct CopyPathBuf {

View file

@ -199,12 +199,12 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
// do an invoke, otherwise do a call.
let fn_ty = bx.fn_decl_backend_type(fn_abi);
let fn_attrs = if bx.tcx().def_kind(fx.instance.def_id()).has_codegen_attrs() {
let caller_attrs = if bx.tcx().def_kind(fx.instance.def_id()).has_codegen_attrs() {
Some(bx.tcx().codegen_instance_attrs(fx.instance.def))
} else {
None
};
let fn_attrs = fn_attrs.as_deref();
let caller_attrs = caller_attrs.as_deref();
if !fn_abi.can_unwind {
unwind = mir::UnwindAction::Unreachable;
@ -233,7 +233,7 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
};
if kind == CallKind::Tail {
bx.tail_call(fn_ty, fn_attrs, fn_abi, fn_ptr, llargs, self.funclet(fx), instance);
bx.tail_call(fn_ty, caller_attrs, fn_abi, fn_ptr, llargs, self.funclet(fx), instance);
return MergingSucc::False;
}
@ -245,7 +245,7 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
};
let invokeret = bx.invoke(
fn_ty,
fn_attrs,
caller_attrs,
Some(fn_abi),
fn_ptr,
llargs,
@ -268,8 +268,15 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
}
MergingSucc::False
} else {
let llret =
bx.call(fn_ty, fn_attrs, Some(fn_abi), fn_ptr, llargs, self.funclet(fx), instance);
let llret = bx.call(
fn_ty,
caller_attrs,
Some(fn_abi),
fn_ptr,
llargs,
self.funclet(fx),
instance,
);
if fx.mir[self.bb].is_cleanup {
bx.apply_attrs_to_cleanup_callsite(llret);
}

View file

@ -1074,8 +1074,14 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
if constant_ty.is_simd() {
// However, some SIMD types do not actually use the vector ABI
// (in particular, packed SIMD types do not). Ensure we exclude those.
//
// We also have to exclude vectors of pointers because `immediate_const_vector`
// does not work for those.
let layout = bx.layout_of(constant_ty);
if let BackendRepr::SimdVector { .. } = layout.backend_repr {
let (_, element_ty) = constant_ty.simd_size_and_type(bx.tcx());
if let BackendRepr::SimdVector { .. } = layout.backend_repr
&& element_ty.is_numeric()
{
let (llval, ty) = self.immediate_const_vector(bx, constant);
return OperandRef {
val: OperandValue::Immediate(llval),

View file

@ -67,7 +67,7 @@ pub trait CodegenBackend {
CrateType::Executable,
CrateType::Dylib,
CrateType::Rlib,
CrateType::Staticlib,
CrateType::StaticLib,
CrateType::Cdylib,
CrateType::ProcMacro,
CrateType::Sdylib,

View file

@ -600,10 +600,13 @@ pub trait BuilderMethods<'a, 'tcx>:
///
/// ## Arguments
///
/// The `fn_attrs`, `fn_abi`, and `instance` arguments are Options because they are advisory.
/// They relate to optional codegen enhancements like LLVM CFI, and do not affect ABI per se.
/// Any ABI-related transformations should be handled by different, earlier stages of codegen.
/// For instance, in the caller of `BuilderMethods::call`.
/// `caller_attrs` are the attributes of the surrounding caller; they have nothing to do with
/// the callee.
///
/// The `caller_attrs`, `fn_abi`, and `callee_instance` arguments are Options because they are
/// advisory. They relate to optional codegen enhancements like LLVM CFI, and do not affect ABI
/// per se. Any ABI-related transformations should be handled by different, earlier stages of
/// codegen. For instance, in the caller of `BuilderMethods::call`.
///
/// This means that a codegen backend which disregards `fn_attrs`, `fn_abi`, and `instance`
/// should still do correct codegen, and code should not be miscompiled if they are omitted.
@ -620,23 +623,23 @@ pub trait BuilderMethods<'a, 'tcx>:
fn call(
&mut self,
llty: Self::Type,
fn_attrs: Option<&CodegenFnAttrs>,
caller_attrs: Option<&CodegenFnAttrs>,
fn_abi: Option<&FnAbi<'tcx, Ty<'tcx>>>,
fn_val: Self::Value,
args: &[Self::Value],
funclet: Option<&Self::Funclet>,
instance: Option<Instance<'tcx>>,
callee_instance: Option<Instance<'tcx>>,
) -> Self::Value;
fn tail_call(
&mut self,
llty: Self::Type,
fn_attrs: Option<&CodegenFnAttrs>,
caller_attrs: Option<&CodegenFnAttrs>,
fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
llfn: Self::Value,
args: &[Self::Value],
funclet: Option<&Self::Funclet>,
instance: Option<Instance<'tcx>>,
callee_instance: Option<Instance<'tcx>>,
);
fn zext(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value;

View file

@ -4,7 +4,7 @@ use rustc_errors::DiagCtxtHandle;
use rustc_middle::dep_graph::WorkProduct;
use crate::back::lto::{SerializedModule, ThinModule};
use crate::back::write::{CodegenContext, FatLtoInput, ModuleConfig};
use crate::back::write::{CodegenContext, FatLtoInput, ModuleConfig, SharedEmitter};
use crate::{CompiledModule, ModuleCodegen};
pub trait WriteBackendMethods: Clone + 'static {
@ -19,6 +19,7 @@ pub trait WriteBackendMethods: Clone + 'static {
/// if necessary and running any further optimizations
fn run_and_optimize_fat_lto(
cgcx: &CodegenContext<Self>,
shared_emitter: &SharedEmitter,
exported_symbols_for_lto: &[String],
each_linked_rlib_for_lto: &[PathBuf],
modules: Vec<FatLtoInput<Self>>,
@ -28,6 +29,7 @@ pub trait WriteBackendMethods: Clone + 'static {
/// can simply be copied over from the incr. comp. cache.
fn run_thin_lto(
cgcx: &CodegenContext<Self>,
dcx: DiagCtxtHandle<'_>,
exported_symbols_for_lto: &[String],
each_linked_rlib_for_lto: &[PathBuf],
modules: Vec<(String, Self::ThinBuffer)>,
@ -37,16 +39,18 @@ pub trait WriteBackendMethods: Clone + 'static {
fn print_statistics(&self);
fn optimize(
cgcx: &CodegenContext<Self>,
dcx: DiagCtxtHandle<'_>,
shared_emitter: &SharedEmitter,
module: &mut ModuleCodegen<Self::Module>,
config: &ModuleConfig,
);
fn optimize_thin(
cgcx: &CodegenContext<Self>,
shared_emitter: &SharedEmitter,
thin: ThinModule<Self>,
) -> ModuleCodegen<Self::Module>;
fn codegen(
cgcx: &CodegenContext<Self>,
shared_emitter: &SharedEmitter,
module: ModuleCodegen<Self::Module>,
config: &ModuleConfig,
) -> CompiledModule;

View file

@ -343,17 +343,18 @@ where
// Check the qualifs of the value of `const` items.
let uneval = match constant.const_ {
Const::Ty(_, ct)
if matches!(
ct.kind(),
ty::ConstKind::Param(_) | ty::ConstKind::Error(_) | ty::ConstKind::Value(_)
) =>
{
None
}
Const::Ty(_, c) => {
bug!("expected ConstKind::Param or ConstKind::Value here, found {:?}", c)
}
Const::Ty(_, ct) => match ct.kind() {
ty::ConstKind::Param(_) | ty::ConstKind::Error(_) => None,
// Unevaluated consts in MIR bodies don't have associated MIR (e.g. `#[type_const]`).
ty::ConstKind::Unevaluated(_) => None,
// FIXME(mgca): Investigate whether using `None` for `ConstKind::Value` is overly
// strict, and if instead we should be doing some kind of value-based analysis.
ty::ConstKind::Value(_) => None,
_ => bug!(
"expected ConstKind::Param, ConstKind::Value, ConstKind::Unevaluated, or ConstKind::Error here, found {:?}",
ct
),
},
Const::Unevaluated(uv, _) => Some(uv),
Const::Val(..) => None,
};
@ -364,10 +365,8 @@ where
// check performed after the promotion. Verify that with an assertion.
assert!(promoted.is_none() || Q::ALLOW_PROMOTED);
// Don't peak inside trait associated constants, also `#[type_const] const` items
// don't have bodies so there's nothing to look at
if promoted.is_none() && cx.tcx.trait_of_assoc(def).is_none() && !cx.tcx.is_type_const(def)
{
// Don't peak inside trait associated constants.
if promoted.is_none() && cx.tcx.trait_of_assoc(def).is_none() {
let qualifs = cx.tcx.at(constant.span).mir_const_qualif(def);
if !Q::in_qualifs(&qualifs) {

View file

@ -66,6 +66,14 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
variant
}
ty::Slice(ty) => {
let (variant, variant_place) = downcast(sym::Slice)?;
let slice_place = self.project_field(&variant_place, FieldIdx::ZERO)?;
self.write_slice_type_info(slice_place, *ty)?;
variant
}
ty::Bool => {
let (variant, _variant_place) = downcast(sym::Bool)?;
variant
@ -124,7 +132,6 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
ty::Adt(_, _)
| ty::Foreign(_)
| ty::Pat(_, _)
| ty::Slice(_)
| ty::FnDef(..)
| ty::FnPtr(..)
| ty::UnsafeBinder(..)
@ -254,6 +261,27 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
interp_ok(())
}
pub(crate) fn write_slice_type_info(
&mut self,
place: impl Writeable<'tcx, CtfeProvenance>,
ty: Ty<'tcx>,
) -> InterpResult<'tcx> {
// Iterate over all fields of `type_info::Slice`.
for (field_idx, field) in
place.layout().ty.ty_adt_def().unwrap().non_enum_variant().fields.iter_enumerated()
{
let field_place = self.project_field(&place, field_idx)?;
match field.name {
// Write the `TypeId` of the slice's elements to the `element_ty` field.
sym::element_ty => self.write_type_id(ty, &field_place)?,
other => span_bug!(self.tcx.def_span(field.did), "unimplemented field {other}"),
}
}
interp_ok(())
}
fn write_int_type_info(
&mut self,
place: impl Writeable<'tcx, CtfeProvenance>,

View file

@ -30,7 +30,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
let dest = dest.force_mplace(self)?;
match intrinsic_name {
sym::simd_insert => {
sym::simd_insert | sym::simd_insert_dyn => {
let index = u64::from(self.read_scalar(&args[1])?.to_u32()?);
let elem = &args[2];
let (input, input_len) = self.project_to_simd(&args[0])?;
@ -39,7 +39,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
// Bounds are not checked by typeck so we have to do it ourselves.
if index >= input_len {
throw_ub_format!(
"`simd_insert` index {index} is out-of-bounds of vector with length {input_len}"
"`{intrinsic_name}` index {index} is out-of-bounds of vector with length {input_len}"
);
}
@ -50,17 +50,26 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
self.copy_op(&value, &place)?;
}
}
sym::simd_extract => {
sym::simd_extract | sym::simd_extract_dyn => {
let index = u64::from(self.read_scalar(&args[1])?.to_u32()?);
let (input, input_len) = self.project_to_simd(&args[0])?;
// Bounds are not checked by typeck so we have to do it ourselves.
if index >= input_len {
throw_ub_format!(
"`simd_extract` index {index} is out-of-bounds of vector with length {input_len}"
"`{intrinsic_name}` index {index} is out-of-bounds of vector with length {input_len}"
);
}
self.copy_op(&self.project_index(&input, index)?, &dest)?;
}
sym::simd_splat => {
let elem = &args[0];
let (dest, dest_len) = self.project_to_simd(&dest)?;
for i in 0..dest_len {
let place = self.project_index(&dest, i)?;
self.copy_op(elem, &place)?;
}
}
sym::simd_neg
| sym::simd_fabs
| sym::simd_ceil

View file

@ -10,7 +10,6 @@
#![allow(internal_features)]
#![allow(rustc::default_hash_types)]
#![allow(rustc::potential_query_instability)]
#![cfg_attr(bootstrap, feature(array_windows))]
#![deny(unsafe_op_in_unsafe_fn)]
#![feature(allocator_api)]
#![feature(ascii_char)]
@ -42,8 +41,11 @@
// have to worry about it being moved to a different module in std during stabilization.
// FIXME(#151359): Remove this when `feature(assert_matches)` is stable in stage0.
// (This doesn't necessarily need to be fixed during the beta bump itself.)
#[cfg(bootstrap)]
pub use std::assert_matches::{assert_matches, debug_assert_matches};
use std::fmt;
#[cfg(not(bootstrap))]
pub use std::{assert_matches, debug_assert_matches};
pub use atomic_ref::AtomicRef;
pub use ena::{snapshot_vec, undo_log, unify};

View file

@ -18,7 +18,7 @@ use std::ffi::OsString;
use std::fmt::Write as _;
use std::fs::{self, File};
use std::io::{self, IsTerminal, Read, Write};
use std::panic::{self, PanicHookInfo, catch_unwind};
use std::panic::{self, PanicHookInfo};
use std::path::{Path, PathBuf};
use std::process::{self, Command, Stdio};
use std::sync::OnceLock;
@ -32,15 +32,17 @@ use rustc_codegen_ssa::{CodegenErrors, CodegenResults};
use rustc_data_structures::profiling::{
TimePassesFormat, get_resident_set_size, print_time_passes_entry,
};
pub use rustc_errors::catch_fatal_errors;
use rustc_errors::emitter::stderr_destination;
use rustc_errors::registry::Registry;
use rustc_errors::translation::Translator;
use rustc_errors::{ColorConfig, DiagCtxt, ErrCode, FatalError, PResult, markdown};
use rustc_errors::{ColorConfig, DiagCtxt, ErrCode, PResult, markdown};
use rustc_feature::find_gated_cfg;
// This avoids a false positive with `-Wunused_crate_dependencies`.
// `rust_index` isn't used in this crate's code, but it must be named in the
// `Cargo.toml` for the `rustc_randomized_layouts` feature.
use rustc_index as _;
use rustc_interface::passes::collect_crate_types;
use rustc_interface::util::{self, get_codegen_backend};
use rustc_interface::{Linker, create_and_enter_global_ctxt, interface, passes};
use rustc_lint::unerased_lint_store;
@ -55,10 +57,10 @@ use rustc_session::config::{
};
use rustc_session::getopts::{self, Matches};
use rustc_session::lint::{Lint, LintId};
use rustc_session::output::{CRATE_TYPES, collect_crate_types, invalid_output_for_target};
use rustc_session::output::invalid_output_for_target;
use rustc_session::{EarlyDiagCtxt, Session, config};
use rustc_span::FileName;
use rustc_span::def_id::LOCAL_CRATE;
use rustc_span::{DUMMY_SP, FileName};
use rustc_target::json::ToJson;
use rustc_target::spec::{Target, TargetTuple};
use tracing::trace;
@ -697,6 +699,7 @@ fn print_crate_info(
&codegen_backend.supported_crate_types(sess),
codegen_backend.name(),
attrs,
DUMMY_SP,
);
for &style in &crate_types {
let fname = rustc_session::output::filename_for_input(
@ -848,7 +851,7 @@ fn print_crate_info(
}
}
SupportedCrateTypes => {
let supported_crate_types = CRATE_TYPES
let supported_crate_types = CrateType::all()
.iter()
.filter(|(_, crate_type)| !invalid_output_for_target(sess, *crate_type))
.filter(|(_, crate_type)| *crate_type != CrateType::Sdylib)
@ -1377,21 +1380,6 @@ fn parse_crate_attrs<'a>(sess: &'a Session) -> PResult<'a, ast::AttrVec> {
parser.parse_inner_attributes()
}
/// Runs a closure and catches unwinds triggered by fatal errors.
///
/// The compiler currently unwinds with a special sentinel value to abort
/// compilation on fatal errors. This function catches that sentinel and turns
/// the panic into a `Result` instead.
pub fn catch_fatal_errors<F: FnOnce() -> R, R>(f: F) -> Result<R, FatalError> {
catch_unwind(panic::AssertUnwindSafe(f)).map_err(|value| {
if value.is::<rustc_errors::FatalErrorMarker>() {
FatalError
} else {
panic::resume_unwind(value);
}
})
}
/// Variant of `catch_fatal_errors` for the `interface::Result` return type
/// that also computes the exit code.
pub fn catch_with_exit_code(f: impl FnOnce()) -> i32 {
@ -1548,10 +1536,11 @@ fn report_ice(
using_internal_features: &AtomicBool,
) {
let translator = default_translator();
let emitter = Box::new(rustc_errors::emitter::HumanEmitter::new(
stderr_destination(rustc_errors::ColorConfig::Auto),
translator,
));
let emitter =
Box::new(rustc_errors::annotate_snippet_emitter_writer::AnnotateSnippetEmitter::new(
stderr_destination(rustc_errors::ColorConfig::Auto),
translator,
));
let dcx = rustc_errors::DiagCtxt::new(emitter);
let dcx = dcx.handle();

View file

@ -17,7 +17,6 @@ rustc_error_messages = { path = "../rustc_error_messages" }
rustc_fluent_macro = { path = "../rustc_fluent_macro" }
rustc_hashes = { path = "../rustc_hashes" }
rustc_index = { path = "../rustc_index" }
rustc_lexer = { path = "../rustc_lexer" }
rustc_lint_defs = { path = "../rustc_lint_defs" }
rustc_macros = { path = "../rustc_macros" }
rustc_serialize = { path = "../rustc_serialize" }

View file

@ -15,10 +15,9 @@ use rustc_span::source_map::Spanned;
use rustc_span::{DUMMY_SP, Span, Symbol};
use tracing::debug;
use crate::snippet::Style;
use crate::{
CodeSuggestion, DiagCtxtHandle, DiagMessage, ErrCode, ErrorGuaranteed, ExplicitBug, Level,
MultiSpan, StashKey, SubdiagMessage, Substitution, SubstitutionPart, SuggestionStyle,
MultiSpan, StashKey, Style, SubdiagMessage, Substitution, SubstitutionPart, SuggestionStyle,
Suggestions,
};

File diff suppressed because it is too large Load diff

View file

@ -5,7 +5,6 @@
// tidy-alphabetical-start
#![allow(internal_features)]
#![allow(rustc::direct_use_of_rustc_type_ir)]
#![cfg_attr(bootstrap, feature(array_windows))]
#![feature(assert_matches)]
#![feature(associated_type_defaults)]
#![feature(box_patterns)]
@ -51,7 +50,7 @@ pub use diagnostic_impls::{
IndicateAnonymousLifetime, SingleLabelManySpans,
};
pub use emitter::ColorConfig;
use emitter::{ConfusionType, DynEmitter, Emitter, detect_confusion_type, is_different};
use emitter::{DynEmitter, Emitter};
use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
use rustc_data_structures::stable_hasher::StableHasher;
use rustc_data_structures::sync::{DynSend, Lock};
@ -66,10 +65,9 @@ use rustc_lint_defs::LintExpectationId;
pub use rustc_lint_defs::{Applicability, listify, pluralize};
use rustc_macros::{Decodable, Encodable};
pub use rustc_span::ErrorGuaranteed;
pub use rustc_span::fatal_error::{FatalError, FatalErrorMarker};
pub use rustc_span::fatal_error::{FatalError, FatalErrorMarker, catch_fatal_errors};
use rustc_span::source_map::SourceMap;
use rustc_span::{BytePos, DUMMY_SP, Loc, Span};
pub use snippet::Style;
use rustc_span::{DUMMY_SP, Span};
use tracing::debug;
use crate::emitter::TimingEvent;
@ -87,8 +85,6 @@ pub mod json;
mod lock;
pub mod markdown;
pub mod registry;
mod snippet;
mod styled_buffer;
#[cfg(test)]
mod tests;
pub mod timings;
@ -215,43 +211,6 @@ pub struct TrimmedSubstitutionPart {
pub snippet: String,
}
/// Used to translate between `Span`s and byte positions within a single output line in highlighted
/// code of structured suggestions.
#[derive(Debug, Clone, Copy)]
pub(crate) struct SubstitutionHighlight {
start: usize,
end: usize,
}
impl SubstitutionPart {
/// Try to turn a replacement into an addition when the span that is being
/// overwritten matches either the prefix or suffix of the replacement.
fn trim_trivial_replacements(self, sm: &SourceMap) -> TrimmedSubstitutionPart {
let mut trimmed_part = TrimmedSubstitutionPart {
original_span: self.span,
span: self.span,
snippet: self.snippet,
};
if trimmed_part.snippet.is_empty() {
return trimmed_part;
}
let Ok(snippet) = sm.span_to_snippet(trimmed_part.span) else {
return trimmed_part;
};
if let Some((prefix, substr, suffix)) = as_substr(&snippet, &trimmed_part.snippet) {
trimmed_part.span = Span::new(
trimmed_part.span.lo() + BytePos(prefix as u32),
trimmed_part.span.hi() - BytePos(suffix as u32),
trimmed_part.span.ctxt(),
trimmed_part.span.parent(),
);
trimmed_part.snippet = substr.to_string();
}
trimmed_part
}
}
impl TrimmedSubstitutionPart {
pub fn is_addition(&self, sm: &SourceMap) -> bool {
!self.snippet.is_empty() && !self.replaces_meaningful_content(sm)
@ -303,229 +262,6 @@ fn as_substr<'a>(original: &'a str, suggestion: &'a str) -> Option<(usize, &'a s
}
}
impl CodeSuggestion {
/// Returns the assembled code suggestions, whether they should be shown with an underline
/// and whether the substitution only differs in capitalization.
pub(crate) fn splice_lines(
&self,
sm: &SourceMap,
) -> Vec<(String, Vec<TrimmedSubstitutionPart>, Vec<Vec<SubstitutionHighlight>>, ConfusionType)>
{
// For the `Vec<Vec<SubstitutionHighlight>>` value, the first level of the vector
// corresponds to the output snippet's lines, while the second level corresponds to the
// substrings within that line that should be highlighted.
use rustc_span::{CharPos, Pos};
/// Extracts a substring from the provided `line_opt` based on the specified low and high
/// indices, appends it to the given buffer `buf`, and returns the count of newline
/// characters in the substring for accurate highlighting. If `line_opt` is `None`, a
/// newline character is appended to the buffer, and 0 is returned.
///
/// ## Returns
///
/// The count of newline characters in the extracted substring.
fn push_trailing(
buf: &mut String,
line_opt: Option<&Cow<'_, str>>,
lo: &Loc,
hi_opt: Option<&Loc>,
) -> usize {
let mut line_count = 0;
// Convert `CharPos` to `usize`, as `CharPos` is character offset
// Extract low index and high index
let (lo, hi_opt) = (lo.col.to_usize(), hi_opt.map(|hi| hi.col.to_usize()));
if let Some(line) = line_opt {
if let Some(lo) = line.char_indices().map(|(i, _)| i).nth(lo) {
// Get high index while account for rare unicode and emoji with char_indices
let hi_opt = hi_opt.and_then(|hi| line.char_indices().map(|(i, _)| i).nth(hi));
match hi_opt {
// If high index exist, take string from low to high index
Some(hi) if hi > lo => {
// count how many '\n' exist
line_count = line[lo..hi].matches('\n').count();
buf.push_str(&line[lo..hi])
}
Some(_) => (),
// If high index absence, take string from low index till end string.len
None => {
// count how many '\n' exist
line_count = line[lo..].matches('\n').count();
buf.push_str(&line[lo..])
}
}
}
// If high index is None
if hi_opt.is_none() {
buf.push('\n');
}
}
line_count
}
assert!(!self.substitutions.is_empty());
self.substitutions
.iter()
.filter(|subst| {
// Suggestions coming from macros can have malformed spans. This is a heavy
// handed approach to avoid ICEs by ignoring the suggestion outright.
let invalid = subst.parts.iter().any(|item| sm.is_valid_span(item.span).is_err());
if invalid {
debug!("splice_lines: suggestion contains an invalid span: {:?}", subst);
}
!invalid
})
.cloned()
.filter_map(|mut substitution| {
// Assumption: all spans are in the same file, and all spans
// are disjoint. Sort in ascending order.
substitution.parts.sort_by_key(|part| part.span.lo());
// Find the bounding span.
let lo = substitution.parts.iter().map(|part| part.span.lo()).min()?;
let hi = substitution.parts.iter().map(|part| part.span.hi()).max()?;
let bounding_span = Span::with_root_ctxt(lo, hi);
// The different spans might belong to different contexts, if so ignore suggestion.
let lines = sm.span_to_lines(bounding_span).ok()?;
assert!(!lines.lines.is_empty() || bounding_span.is_dummy());
// We can't splice anything if the source is unavailable.
if !sm.ensure_source_file_source_present(&lines.file) {
return None;
}
let mut highlights = vec![];
// To build up the result, we do this for each span:
// - push the line segment trailing the previous span
// (at the beginning a "phantom" span pointing at the start of the line)
// - push lines between the previous and current span (if any)
// - if the previous and current span are not on the same line
// push the line segment leading up to the current span
// - splice in the span substitution
//
// Finally push the trailing line segment of the last span
let sf = &lines.file;
let mut prev_hi = sm.lookup_char_pos(bounding_span.lo());
prev_hi.col = CharPos::from_usize(0);
let mut prev_line =
lines.lines.get(0).and_then(|line0| sf.get_line(line0.line_index));
let mut buf = String::new();
let mut line_highlight = vec![];
// We need to keep track of the difference between the existing code and the added
// or deleted code in order to point at the correct column *after* substitution.
let mut acc = 0;
let mut confusion_type = ConfusionType::None;
let trimmed_parts = substitution
.parts
.into_iter()
// If this is a replacement of, e.g. `"a"` into `"ab"`, adjust the
// suggestion and snippet to look as if we just suggested to add
// `"b"`, which is typically much easier for the user to understand.
.map(|part| part.trim_trivial_replacements(sm))
.collect::<Vec<_>>();
for part in &trimmed_parts {
let part_confusion = detect_confusion_type(sm, &part.snippet, part.span);
confusion_type = confusion_type.combine(part_confusion);
let cur_lo = sm.lookup_char_pos(part.span.lo());
if prev_hi.line == cur_lo.line {
let mut count =
push_trailing(&mut buf, prev_line.as_ref(), &prev_hi, Some(&cur_lo));
while count > 0 {
highlights.push(std::mem::take(&mut line_highlight));
acc = 0;
count -= 1;
}
} else {
acc = 0;
highlights.push(std::mem::take(&mut line_highlight));
let mut count = push_trailing(&mut buf, prev_line.as_ref(), &prev_hi, None);
while count > 0 {
highlights.push(std::mem::take(&mut line_highlight));
count -= 1;
}
// push lines between the previous and current span (if any)
for idx in prev_hi.line..(cur_lo.line - 1) {
if let Some(line) = sf.get_line(idx) {
buf.push_str(line.as_ref());
buf.push('\n');
highlights.push(std::mem::take(&mut line_highlight));
}
}
if let Some(cur_line) = sf.get_line(cur_lo.line - 1) {
let end = match cur_line.char_indices().nth(cur_lo.col.to_usize()) {
Some((i, _)) => i,
None => cur_line.len(),
};
buf.push_str(&cur_line[..end]);
}
}
// Add a whole line highlight per line in the snippet.
let len: isize = part
.snippet
.split('\n')
.next()
.unwrap_or(&part.snippet)
.chars()
.map(|c| match c {
'\t' => 4,
_ => 1,
})
.sum();
if !is_different(sm, &part.snippet, part.span) {
// Account for cases where we are suggesting the same code that's already
// there. This shouldn't happen often, but in some cases for multipart
// suggestions it's much easier to handle it here than in the origin.
} else {
line_highlight.push(SubstitutionHighlight {
start: (cur_lo.col.0 as isize + acc) as usize,
end: (cur_lo.col.0 as isize + acc + len) as usize,
});
}
buf.push_str(&part.snippet);
let cur_hi = sm.lookup_char_pos(part.span.hi());
// Account for the difference between the width of the current code and the
// snippet being suggested, so that the *later* suggestions are correctly
// aligned on the screen. Note that cur_hi and cur_lo can be on different
// lines, so cur_hi.col can be smaller than cur_lo.col
acc += len - (cur_hi.col.0 as isize - cur_lo.col.0 as isize);
prev_hi = cur_hi;
prev_line = sf.get_line(prev_hi.line - 1);
for line in part.snippet.split('\n').skip(1) {
acc = 0;
highlights.push(std::mem::take(&mut line_highlight));
let end: usize = line
.chars()
.map(|c| match c {
'\t' => 4,
_ => 1,
})
.sum();
line_highlight.push(SubstitutionHighlight { start: 0, end });
}
}
highlights.push(std::mem::take(&mut line_highlight));
// if the replacement already ends with a newline, don't print the next line
if !buf.ends_with('\n') {
push_trailing(&mut buf, prev_line.as_ref(), &prev_hi, None);
}
// remove trailing newlines
while buf.ends_with('\n') {
buf.pop();
}
if highlights.iter().all(|parts| parts.is_empty()) {
None
} else {
Some((buf, trimmed_parts, highlights, confusion_type))
}
})
.collect()
}
}
/// Signifies that the compiler died with an explicit call to `.bug`
/// or `.span_bug` rather than a failed assertion, etc.
pub struct ExplicitBug;
@ -1961,15 +1697,6 @@ impl Level {
pub fn is_failure_note(&self) -> bool {
matches!(*self, FailureNote)
}
// Can this level be used in a subdiagnostic message?
fn can_be_subdiag(&self) -> bool {
match self {
Bug | DelayedBug | Fatal | Error | ForceWarning | FailureNote | Allow | Expect => false,
Warning | Note | Help | OnceNote | OnceHelp => true,
}
}
}
impl IntoDiagArg for Level {
@ -1978,6 +1705,24 @@ impl IntoDiagArg for Level {
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Encodable, Decodable)]
pub enum Style {
MainHeaderMsg,
HeaderMsg,
LineAndColumn,
LineNumber,
Quotation,
UnderlinePrimary,
UnderlineSecondary,
LabelPrimary,
LabelSecondary,
NoStyle,
Level(Level),
Highlight,
Addition,
Removal,
}
// FIXME(eddyb) this doesn't belong here AFAICT, should be moved to callsite.
pub fn elided_lifetime_in_path_suggestion(
source_map: &SourceMap,

View file

@ -1,214 +0,0 @@
// Code for annotating snippets.
use rustc_macros::{Decodable, Encodable};
use crate::{Level, Loc};
#[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)]
pub(crate) struct Line {
pub line_index: usize,
pub annotations: Vec<Annotation>,
}
#[derive(Clone, Copy, Debug, PartialOrd, Ord, PartialEq, Eq, Default)]
pub(crate) struct AnnotationColumn {
/// the (0-indexed) column for *display* purposes, counted in characters, not utf-8 bytes
pub display: usize,
/// the (0-indexed) column in the file, counted in characters, not utf-8 bytes.
///
/// this may be different from `self.display`,
/// e.g. if the file contains hard tabs, because we convert tabs to spaces for error messages.
///
/// for example:
/// ```text
/// (hard tab)hello
/// ^ this is display column 4, but file column 1
/// ```
///
/// we want to keep around the correct file offset so that column numbers in error messages
/// are correct. (motivated by <https://github.com/rust-lang/rust/issues/109537>)
pub file: usize,
}
impl AnnotationColumn {
pub(crate) fn from_loc(loc: &Loc) -> AnnotationColumn {
AnnotationColumn { display: loc.col_display, file: loc.col.0 }
}
}
#[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)]
pub(crate) struct MultilineAnnotation {
pub depth: usize,
pub line_start: usize,
pub line_end: usize,
pub start_col: AnnotationColumn,
pub end_col: AnnotationColumn,
pub is_primary: bool,
pub label: Option<String>,
pub overlaps_exactly: bool,
}
impl MultilineAnnotation {
pub(crate) fn increase_depth(&mut self) {
self.depth += 1;
}
/// Compare two `MultilineAnnotation`s considering only the `Span` they cover.
pub(crate) fn same_span(&self, other: &MultilineAnnotation) -> bool {
self.line_start == other.line_start
&& self.line_end == other.line_end
&& self.start_col == other.start_col
&& self.end_col == other.end_col
}
pub(crate) fn as_start(&self) -> Annotation {
Annotation {
start_col: self.start_col,
end_col: AnnotationColumn {
// these might not correspond to the same place anymore,
// but that's okay for our purposes
display: self.start_col.display + 1,
file: self.start_col.file + 1,
},
is_primary: self.is_primary,
label: None,
annotation_type: AnnotationType::MultilineStart(self.depth),
}
}
pub(crate) fn as_end(&self) -> Annotation {
Annotation {
start_col: AnnotationColumn {
// these might not correspond to the same place anymore,
// but that's okay for our purposes
display: self.end_col.display.saturating_sub(1),
file: self.end_col.file.saturating_sub(1),
},
end_col: self.end_col,
is_primary: self.is_primary,
label: self.label.clone(),
annotation_type: AnnotationType::MultilineEnd(self.depth),
}
}
pub(crate) fn as_line(&self) -> Annotation {
Annotation {
start_col: Default::default(),
end_col: Default::default(),
is_primary: self.is_primary,
label: None,
annotation_type: AnnotationType::MultilineLine(self.depth),
}
}
}
#[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)]
pub(crate) enum AnnotationType {
/// Annotation under a single line of code
Singleline,
// The Multiline type above is replaced with the following three in order
// to reuse the current label drawing code.
//
// Each of these corresponds to one part of the following diagram:
//
// x | foo(1 + bar(x,
// | _________^ < MultilineStart
// x | | y), < MultilineLine
// | |______________^ label < MultilineEnd
// x | z);
/// Annotation marking the first character of a fully shown multiline span
MultilineStart(usize),
/// Annotation marking the last character of a fully shown multiline span
MultilineEnd(usize),
/// Line at the left enclosing the lines of a fully shown multiline span
// Just a placeholder for the drawing algorithm, to know that it shouldn't skip the first 4
// and last 2 lines of code. The actual line is drawn in `emit_message_default` and not in
// `draw_multiline_line`.
MultilineLine(usize),
}
#[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)]
pub(crate) struct Annotation {
/// Start column.
/// Note that it is important that this field goes
/// first, so that when we sort, we sort orderings by start
/// column.
pub start_col: AnnotationColumn,
/// End column within the line (exclusive)
pub end_col: AnnotationColumn,
/// Is this annotation derived from primary span
pub is_primary: bool,
/// Optional label to display adjacent to the annotation.
pub label: Option<String>,
/// Is this a single line, multiline or multiline span minimized down to a
/// smaller span.
pub annotation_type: AnnotationType,
}
impl Annotation {
/// Whether this annotation is a vertical line placeholder.
pub(crate) fn is_line(&self) -> bool {
matches!(self.annotation_type, AnnotationType::MultilineLine(_))
}
/// Length of this annotation as displayed in the stderr output
pub(crate) fn len(&self) -> usize {
// Account for usize underflows
self.end_col.display.abs_diff(self.start_col.display)
}
pub(crate) fn has_label(&self) -> bool {
if let Some(ref label) = self.label {
// Consider labels with no text as effectively not being there
// to avoid weird output with unnecessary vertical lines, like:
//
// X | fn foo(x: u32) {
// | -------^------
// | | |
// | |
// |
//
// Note that this would be the complete output users would see.
!label.is_empty()
} else {
false
}
}
pub(crate) fn takes_space(&self) -> bool {
// Multiline annotations always have to keep vertical space.
matches!(
self.annotation_type,
AnnotationType::MultilineStart(_) | AnnotationType::MultilineEnd(_)
)
}
}
#[derive(Debug)]
pub(crate) struct StyledString {
pub text: String,
pub style: Style,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Encodable, Decodable)]
pub enum Style {
MainHeaderMsg,
HeaderMsg,
LineAndColumn,
LineNumber,
Quotation,
UnderlinePrimary,
UnderlineSecondary,
LabelPrimary,
LabelSecondary,
NoStyle,
Level(Level),
Highlight,
Addition,
Removal,
}

View file

@ -1,163 +0,0 @@
// Code for creating styled buffers
use crate::snippet::{Style, StyledString};
#[derive(Debug)]
pub(crate) struct StyledBuffer {
lines: Vec<Vec<StyledChar>>,
}
#[derive(Debug, Clone)]
struct StyledChar {
chr: char,
style: Style,
}
impl StyledChar {
const SPACE: Self = StyledChar::new(' ', Style::NoStyle);
const fn new(chr: char, style: Style) -> Self {
StyledChar { chr, style }
}
}
impl StyledBuffer {
pub(crate) fn new() -> StyledBuffer {
StyledBuffer { lines: vec![] }
}
/// Returns content of `StyledBuffer` split by lines and line styles
pub(crate) fn render(&self) -> Vec<Vec<StyledString>> {
// Tabs are assumed to have been replaced by spaces in calling code.
debug_assert!(self.lines.iter().all(|r| !r.iter().any(|sc| sc.chr == '\t')));
let mut output: Vec<Vec<StyledString>> = vec![];
let mut styled_vec: Vec<StyledString> = vec![];
for styled_line in &self.lines {
let mut current_style = Style::NoStyle;
let mut current_text = String::new();
for sc in styled_line {
if sc.style != current_style {
if !current_text.is_empty() {
styled_vec.push(StyledString { text: current_text, style: current_style });
}
current_style = sc.style;
current_text = String::new();
}
current_text.push(sc.chr);
}
if !current_text.is_empty() {
styled_vec.push(StyledString { text: current_text, style: current_style });
}
// We're done with the row, push and keep going
output.push(styled_vec);
styled_vec = vec![];
}
output
}
fn ensure_lines(&mut self, line: usize) {
if line >= self.lines.len() {
self.lines.resize(line + 1, Vec::new());
}
}
/// Sets `chr` with `style` for given `line`, `col`.
/// If `line` does not exist in our buffer, adds empty lines up to the given
/// and fills the last line with unstyled whitespace.
pub(crate) fn putc(&mut self, line: usize, col: usize, chr: char, style: Style) {
self.ensure_lines(line);
if col >= self.lines[line].len() {
self.lines[line].resize(col + 1, StyledChar::SPACE);
}
self.lines[line][col] = StyledChar::new(chr, style);
}
/// Sets `string` with `style` for given `line`, starting from `col`.
/// If `line` does not exist in our buffer, adds empty lines up to the given
/// and fills the last line with unstyled whitespace.
pub(crate) fn puts(&mut self, line: usize, col: usize, string: &str, style: Style) {
let mut n = col;
for c in string.chars() {
self.putc(line, n, c, style);
n += 1;
}
}
pub(crate) fn replace(&mut self, line: usize, start: usize, end: usize, string: &str) {
if start == end {
return;
}
if start > self.lines[line].len() || end > self.lines[line].len() {
return;
}
let _ = self.lines[line].drain(start..(end - string.chars().count()));
for (i, c) in string.chars().enumerate() {
self.lines[line][start + i] = StyledChar::new(c, Style::LineNumber);
}
}
/// For given `line` inserts `string` with `style` before old content of that line,
/// adding lines if needed
pub(crate) fn prepend(&mut self, line: usize, string: &str, style: Style) {
self.ensure_lines(line);
let string_len = string.chars().count();
if !self.lines[line].is_empty() {
// Push the old content over to make room for new content
for _ in 0..string_len {
self.lines[line].insert(0, StyledChar::SPACE);
}
}
self.puts(line, 0, string, style);
}
/// For given `line` inserts `string` with `style` after old content of that line,
/// adding lines if needed
pub(crate) fn append(&mut self, line: usize, string: &str, style: Style) {
if line >= self.lines.len() {
self.puts(line, 0, string, style);
} else {
let col = self.lines[line].len();
self.puts(line, col, string, style);
}
}
pub(crate) fn num_lines(&self) -> usize {
self.lines.len()
}
/// Set `style` for `line`, `col_start..col_end` range if:
/// 1. That line and column range exist in `StyledBuffer`
/// 2. `overwrite` is `true` or existing style is `Style::NoStyle` or `Style::Quotation`
pub(crate) fn set_style_range(
&mut self,
line: usize,
col_start: usize,
col_end: usize,
style: Style,
overwrite: bool,
) {
for col in col_start..col_end {
self.set_style(line, col, style, overwrite);
}
}
/// Set `style` for `line`, `col` if:
/// 1. That line and column exist in `StyledBuffer`
/// 2. `overwrite` is `true` or existing style is `Style::NoStyle` or `Style::Quotation`
fn set_style(&mut self, line: usize, col: usize, style: Style, overwrite: bool) {
if let Some(ref mut line) = self.lines.get_mut(line)
&& let Some(StyledChar { style: s, .. }) = line.get_mut(col)
&& (overwrite || matches!(s, Style::NoStyle | Style::Quotation))
{
*s = style;
}
}
}

View file

@ -7,8 +7,7 @@ pub use rustc_error_messages::{FluentArgs, LazyFallbackBundle};
use tracing::{debug, trace};
use crate::error::{TranslateError, TranslateErrorKind};
use crate::snippet::Style;
use crate::{DiagArg, DiagMessage, FluentBundle};
use crate::{DiagArg, DiagMessage, FluentBundle, Style};
/// Convert diagnostic arguments (a rustc internal type that exists to implement
/// `Encodable`/`Decodable`) into `FluentArgs` which is necessary to perform translation.

View file

@ -22,7 +22,7 @@ use rustc_hir::limit::Limit;
use rustc_hir::{Stability, find_attr};
use rustc_lint_defs::RegisteredTools;
use rustc_parse::MACRO_ARGUMENTS;
use rustc_parse::parser::{ForceCollect, Parser};
use rustc_parse::parser::{AllowConstBlockItems, ForceCollect, Parser};
use rustc_session::Session;
use rustc_session::parse::ParseSess;
use rustc_span::def_id::{CrateNum, DefId, LocalDefId};
@ -1472,7 +1472,7 @@ pub(crate) fn stream_pretty_printing_compatibility_hack(
let mut parser = Parser::new(psess, stream.clone(), None);
// No need to collect tokens for this simple check.
parser
.parse_item(ForceCollect::No)
.parse_item(ForceCollect::No, AllowConstBlockItems::No)
.expect("failed to reparse item")
.expect("an actual item")
}

View file

@ -393,9 +393,10 @@ impl<'a> StripUnconfigured<'a> {
/// Determines if a node with the given attributes should be included in this configuration.
fn in_cfg(&self, attrs: &[Attribute]) -> bool {
attrs
.iter()
.all(|attr| !is_cfg(attr) || self.cfg_true(attr, ShouldEmit::ErrorsAndLints).as_bool())
attrs.iter().all(|attr| {
!is_cfg(attr)
|| self.cfg_true(attr, ShouldEmit::ErrorsAndLints { recover: true }).as_bool()
})
}
pub(crate) fn cfg_true(&self, attr: &Attribute, emit_errors: ShouldEmit) -> EvalConfigResult {

View file

@ -25,8 +25,8 @@ use rustc_hir::Target;
use rustc_hir::def::MacroKinds;
use rustc_hir::limit::Limit;
use rustc_parse::parser::{
AttemptLocalParseRecovery, CommaRecoveryMode, ForceCollect, Parser, RecoverColon, RecoverComma,
token_descr,
AllowConstBlockItems, AttemptLocalParseRecovery, CommaRecoveryMode, ForceCollect, Parser,
RecoverColon, RecoverComma, token_descr,
};
use rustc_session::Session;
use rustc_session::lint::builtin::{UNUSED_ATTRIBUTES, UNUSED_DOC_COMMENTS};
@ -1099,7 +1099,7 @@ pub fn parse_ast_fragment<'a>(
Ok(match kind {
AstFragmentKind::Items => {
let mut items = SmallVec::new();
while let Some(item) = this.parse_item(ForceCollect::No)? {
while let Some(item) = this.parse_item(ForceCollect::No, AllowConstBlockItems::Yes)? {
items.push(item);
}
AstFragment::Items(items)
@ -2170,7 +2170,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
call.span(),
self.cx.current_expansion.lint_node_id,
Some(self.cx.ecfg.features),
ShouldEmit::ErrorsAndLints,
ShouldEmit::ErrorsAndLints { recover: true },
);
let current_span = if let Some(sp) = span { sp.to(attr.span) } else { attr.span };
@ -2220,7 +2220,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
// Target doesn't matter for `cfg` parsing.
Target::Crate,
self.cfg().features,
ShouldEmit::ErrorsAndLints,
ShouldEmit::ErrorsAndLints { recover: true },
parse_cfg,
&CFG_TEMPLATE,
) else {

View file

@ -1,6 +1,5 @@
// tidy-alphabetical-start
#![allow(internal_features)]
#![cfg_attr(bootstrap, feature(array_windows))]
#![feature(associated_type_defaults)]
#![feature(if_let_guard)]
#![feature(macro_metavar_expr)]

View file

@ -1,7 +1,7 @@
use rustc_ast::tokenstream::TokenStream;
use rustc_errors::ErrorGuaranteed;
use rustc_middle::ty::{self, TyCtxt};
use rustc_parse::parser::{ForceCollect, Parser};
use rustc_parse::parser::{AllowConstBlockItems, ForceCollect, Parser};
use rustc_session::Session;
use rustc_session::config::ProcMacroExecutionStrategy;
use rustc_span::profiling::SpannedEventArgRecorder;
@ -160,7 +160,10 @@ impl MultiItemModifier for DeriveProcMacro {
let mut items = vec![];
loop {
match parser.parse_item(ForceCollect::No) {
match parser.parse_item(
ForceCollect::No,
if is_stmt { AllowConstBlockItems::No } else { AllowConstBlockItems::Yes },
) {
Ok(None) => break,
Ok(Some(item)) => {
if is_stmt {

View file

@ -431,8 +431,6 @@ impl ToInternal<rustc_errors::Level> for Level {
}
}
pub(crate) struct FreeFunctions;
pub(crate) struct Rustc<'a, 'b> {
ecx: &'a mut ExtCtxt<'b>,
def_site: Span,
@ -461,13 +459,28 @@ impl<'a, 'b> Rustc<'a, 'b> {
}
impl server::Types for Rustc<'_, '_> {
type FreeFunctions = FreeFunctions;
type TokenStream = TokenStream;
type Span = Span;
type Symbol = Symbol;
}
impl server::FreeFunctions for Rustc<'_, '_> {
impl server::Server for Rustc<'_, '_> {
fn globals(&mut self) -> ExpnGlobals<Self::Span> {
ExpnGlobals {
def_site: self.def_site,
call_site: self.call_site,
mixed_site: self.mixed_site,
}
}
fn intern_symbol(string: &str) -> Self::Symbol {
Symbol::intern(string)
}
fn with_symbol_string(symbol: &Self::Symbol, f: impl FnOnce(&str)) {
f(symbol.as_str())
}
fn injected_env_var(&mut self, var: &str) -> Option<String> {
self.ecx.sess.opts.logical_env.get(var).cloned()
}
@ -552,14 +565,20 @@ impl server::FreeFunctions for Rustc<'_, '_> {
}
diag.emit();
}
}
impl server::TokenStream for Rustc<'_, '_> {
fn is_empty(&mut self, stream: &Self::TokenStream) -> bool {
fn ts_drop(&mut self, stream: Self::TokenStream) {
drop(stream);
}
fn ts_clone(&mut self, stream: &Self::TokenStream) -> Self::TokenStream {
stream.clone()
}
fn ts_is_empty(&mut self, stream: &Self::TokenStream) -> bool {
stream.is_empty()
}
fn from_str(&mut self, src: &str) -> Self::TokenStream {
fn ts_from_str(&mut self, src: &str) -> Self::TokenStream {
unwrap_or_emit_fatal(source_str_to_stream(
self.psess(),
FileName::proc_macro_source_code(src),
@ -568,11 +587,11 @@ impl server::TokenStream for Rustc<'_, '_> {
))
}
fn to_string(&mut self, stream: &Self::TokenStream) -> String {
fn ts_to_string(&mut self, stream: &Self::TokenStream) -> String {
pprust::tts_to_string(stream)
}
fn expand_expr(&mut self, stream: &Self::TokenStream) -> Result<Self::TokenStream, ()> {
fn ts_expand_expr(&mut self, stream: &Self::TokenStream) -> Result<Self::TokenStream, ()> {
// Parse the expression from our tokenstream.
let expr: PResult<'_, _> = try {
let mut p = Parser::new(self.psess(), stream.clone(), Some("proc_macro expand expr"));
@ -633,14 +652,14 @@ impl server::TokenStream for Rustc<'_, '_> {
}
}
fn from_token_tree(
fn ts_from_token_tree(
&mut self,
tree: TokenTree<Self::TokenStream, Self::Span, Self::Symbol>,
) -> Self::TokenStream {
Self::TokenStream::new((tree, &mut *self).to_internal().into_iter().collect::<Vec<_>>())
}
fn concat_trees(
fn ts_concat_trees(
&mut self,
base: Option<Self::TokenStream>,
trees: Vec<TokenTree<Self::TokenStream, Self::Span, Self::Symbol>>,
@ -654,7 +673,7 @@ impl server::TokenStream for Rustc<'_, '_> {
stream
}
fn concat_streams(
fn ts_concat_streams(
&mut self,
base: Option<Self::TokenStream>,
streams: Vec<Self::TokenStream>,
@ -666,16 +685,14 @@ impl server::TokenStream for Rustc<'_, '_> {
stream
}
fn into_trees(
fn ts_into_trees(
&mut self,
stream: Self::TokenStream,
) -> Vec<TokenTree<Self::TokenStream, Self::Span, Self::Symbol>> {
FromInternal::from_internal((stream, self))
}
}
impl server::Span for Rustc<'_, '_> {
fn debug(&mut self, span: Self::Span) -> String {
fn span_debug(&mut self, span: Self::Span) -> String {
if self.ecx.ecfg.span_debug {
format!("{span:?}")
} else {
@ -683,7 +700,7 @@ impl server::Span for Rustc<'_, '_> {
}
}
fn file(&mut self, span: Self::Span) -> String {
fn span_file(&mut self, span: Self::Span) -> String {
self.psess()
.source_map()
.lookup_char_pos(span.lo())
@ -693,7 +710,7 @@ impl server::Span for Rustc<'_, '_> {
.to_string()
}
fn local_file(&mut self, span: Self::Span) -> Option<String> {
fn span_local_file(&mut self, span: Self::Span) -> Option<String> {
self.psess()
.source_map()
.lookup_char_pos(span.lo())
@ -708,15 +725,15 @@ impl server::Span for Rustc<'_, '_> {
})
}
fn parent(&mut self, span: Self::Span) -> Option<Self::Span> {
fn span_parent(&mut self, span: Self::Span) -> Option<Self::Span> {
span.parent_callsite()
}
fn source(&mut self, span: Self::Span) -> Self::Span {
fn span_source(&mut self, span: Self::Span) -> Self::Span {
span.source_callsite()
}
fn byte_range(&mut self, span: Self::Span) -> Range<usize> {
fn span_byte_range(&mut self, span: Self::Span) -> Range<usize> {
let source_map = self.psess().source_map();
let relative_start_pos = source_map.lookup_byte_offset(span.lo()).pos;
@ -724,25 +741,25 @@ impl server::Span for Rustc<'_, '_> {
Range { start: relative_start_pos.0 as usize, end: relative_end_pos.0 as usize }
}
fn start(&mut self, span: Self::Span) -> Self::Span {
fn span_start(&mut self, span: Self::Span) -> Self::Span {
span.shrink_to_lo()
}
fn end(&mut self, span: Self::Span) -> Self::Span {
fn span_end(&mut self, span: Self::Span) -> Self::Span {
span.shrink_to_hi()
}
fn line(&mut self, span: Self::Span) -> usize {
fn span_line(&mut self, span: Self::Span) -> usize {
let loc = self.psess().source_map().lookup_char_pos(span.lo());
loc.line
}
fn column(&mut self, span: Self::Span) -> usize {
fn span_column(&mut self, span: Self::Span) -> usize {
let loc = self.psess().source_map().lookup_char_pos(span.lo());
loc.col.to_usize() + 1
}
fn join(&mut self, first: Self::Span, second: Self::Span) -> Option<Self::Span> {
fn span_join(&mut self, first: Self::Span, second: Self::Span) -> Option<Self::Span> {
let self_loc = self.psess().source_map().lookup_char_pos(first.lo());
let other_loc = self.psess().source_map().lookup_char_pos(second.lo());
@ -753,7 +770,7 @@ impl server::Span for Rustc<'_, '_> {
Some(first.to(second))
}
fn subspan(
fn span_subspan(
&mut self,
span: Self::Span,
start: Bound<usize>,
@ -789,11 +806,11 @@ impl server::Span for Rustc<'_, '_> {
Some(span.with_lo(new_lo).with_hi(new_hi))
}
fn resolved_at(&mut self, span: Self::Span, at: Self::Span) -> Self::Span {
fn span_resolved_at(&mut self, span: Self::Span, at: Self::Span) -> Self::Span {
span.with_ctxt(at.ctxt())
}
fn source_text(&mut self, span: Self::Span) -> Option<String> {
fn span_source_text(&mut self, span: Self::Span) -> Option<String> {
self.psess().source_map().span_to_snippet(span).ok()
}
@ -821,11 +838,11 @@ impl server::Span for Rustc<'_, '_> {
/// span from the metadata of `my_proc_macro` (which we have access to,
/// since we've loaded `my_proc_macro` from disk in order to execute it).
/// In this way, we have obtained a span pointing into `my_proc_macro`
fn save_span(&mut self, span: Self::Span) -> usize {
fn span_save_span(&mut self, span: Self::Span) -> usize {
self.psess().save_proc_macro_span(span)
}
fn recover_proc_macro_span(&mut self, id: usize) -> Self::Span {
fn span_recover_proc_macro_span(&mut self, id: usize) -> Self::Span {
let (resolver, krate, def_site) = (&*self.ecx.resolver, self.krate, self.def_site);
*self.rebased_spans.entry(id).or_insert_with(|| {
// FIXME: `SyntaxContext` for spans from proc macro crates is lost during encoding,
@ -833,29 +850,9 @@ impl server::Span for Rustc<'_, '_> {
resolver.get_proc_macro_quoted_span(krate, id).with_ctxt(def_site.ctxt())
})
}
}
impl server::Symbol for Rustc<'_, '_> {
fn normalize_and_validate_ident(&mut self, string: &str) -> Result<Self::Symbol, ()> {
fn symbol_normalize_and_validate_ident(&mut self, string: &str) -> Result<Self::Symbol, ()> {
let sym = nfc_normalize(string);
if rustc_lexer::is_ident(sym.as_str()) { Ok(sym) } else { Err(()) }
}
}
impl server::Server for Rustc<'_, '_> {
fn globals(&mut self) -> ExpnGlobals<Self::Span> {
ExpnGlobals {
def_site: self.def_site,
call_site: self.call_site,
mixed_site: self.mixed_site,
}
}
fn intern_symbol(string: &str) -> Self::Symbol {
Symbol::intern(string)
}
fn with_symbol_string(symbol: &Self::Symbol, f: impl FnOnce(&str)) {
f(symbol.as_str())
}
}

View file

@ -1315,13 +1315,13 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
"`#[rustc_deny_explicit_impl]` enforces that a trait can have no user-provided impls"
),
rustc_attr!(
rustc_do_not_implement_via_object,
rustc_dyn_incompatible_trait,
AttributeType::Normal,
template!(Word),
ErrorFollowing,
EncodeCrossCrate::No,
"`#[rustc_do_not_implement_via_object]` opts out of the automatic trait impl for trait objects \
(`impl Trait for dyn Trait`)"
"`#[rustc_dyn_incompatible_trait]` marks a trait as dyn-incompatible, \
even if it otherwise satisfies the requirements to be dyn-compatible."
),
rustc_attr!(
rustc_has_incoherent_inherent_impls, AttributeType::Normal, template!(Word),

View file

@ -61,7 +61,7 @@ declare_features! (
/// Allows a test to fail without failing the whole suite.
(removed, allow_fail, "1.60.0", Some(46488), Some("removed due to no clear use cases"), 93416),
/// Allows users to enforce equality of associated constants `TraitImpl<AssocConst=3>`.
(removed, associated_const_equality, "CURRENT_RUSTC_VERSION", Some(92827),
(removed, associated_const_equality, "1.94.0", Some(92827),
Some("merged into `min_generic_const_args`")),
(removed, await_macro, "1.38.0", Some(50547),
Some("subsumed by `.await` syntax"), 62293),
@ -275,7 +275,7 @@ declare_features! (
(removed, static_nobundle, "1.63.0", Some(37403),
Some(r#"subsumed by `#[link(kind = "static", modifiers = "-bundle", ...)]`"#), 95818),
/// Allows string patterns to dereference values to match them.
(removed, string_deref_patterns, "CURRENT_RUSTC_VERSION", Some(87121), Some("superseded by `deref_patterns`"), 150530),
(removed, string_deref_patterns, "1.94.0", Some(87121), Some("superseded by `deref_patterns`"), 150530),
(removed, struct_inherit, "1.0.0", None, None),
(removed, test_removed_feature, "1.0.0", None, None),
/// Allows using items which are missing stability attributes

View file

@ -222,7 +222,7 @@ declare_features! (
/// Allows writing custom MIR
(internal, custom_mir, "1.65.0", None),
/// Implementation details of externally implementable items
(internal, eii_internals, "CURRENT_RUSTC_VERSION", None),
(internal, eii_internals, "1.94.0", None),
/// Outputs useful `assert!` messages
(unstable, generic_assert, "1.63.0", None),
/// Allows using the #[rustc_intrinsic] attribute.
@ -414,6 +414,8 @@ declare_features! (
(unstable, cmse_nonsecure_entry, "1.48.0", Some(75835)),
/// Allows `async {}` expressions in const contexts.
(unstable, const_async_blocks, "1.53.0", Some(85368)),
/// Allows `const { ... }` as a shorthand for `const _: () = const { ... };` for module items.
(unstable, const_block_items, "CURRENT_RUSTC_VERSION", Some(149226)),
/// Allows `const || {}` closures in const contexts.
(incomplete, const_closures, "1.68.0", Some(106003)),
/// Allows using `[const] Destruct` bounds and calling drop impls in const contexts.
@ -477,7 +479,7 @@ declare_features! (
/// Allows using `#[export_stable]` which indicates that an item is exportable.
(incomplete, export_stable, "1.88.0", Some(139939)),
/// Externally implementable items
(unstable, extern_item_impls, "CURRENT_RUSTC_VERSION", Some(125418)),
(unstable, extern_item_impls, "1.94.0", Some(125418)),
/// Allows defining `extern type`s.
(unstable, extern_types, "1.23.0", Some(43467)),
/// Allow using 128-bit (quad precision) floating point numbers.
@ -632,6 +634,8 @@ declare_features! (
(unstable, rtm_target_feature, "1.35.0", Some(150258)),
/// Allows `extern "rust-cold"`.
(unstable, rust_cold_cc, "1.63.0", Some(97544)),
/// Allows `extern "rust-preserve-none"`.
(unstable, rust_preserve_none_cc, "CURRENT_RUSTC_VERSION", Some(151401)),
/// Target features on s390x.
(unstable, s390x_target_feature, "1.82.0", Some(150259)),
/// Allows the use of the `sanitize` attribute.
@ -667,7 +671,7 @@ declare_features! (
/// Allows using `try {...}` expressions.
(unstable, try_blocks, "1.29.0", Some(31436)),
/// Allows using `try bikeshed TargetType {...}` expressions.
(unstable, try_blocks_heterogeneous, "CURRENT_RUSTC_VERSION", Some(149488)),
(unstable, try_blocks_heterogeneous, "1.94.0", Some(149488)),
/// Allows `impl Trait` to be used inside type aliases (RFC 2515).
(unstable, type_alias_impl_trait, "1.38.0", Some(63063)),
/// Allows creation of instances of a struct by moving fields that have

View file

@ -8,10 +8,7 @@ use fluent_syntax::ast::{
Attribute, Entry, Expression, Identifier, InlineExpression, Message, Pattern, PatternElement,
};
use fluent_syntax::parser::ParserError;
#[cfg(not(bootstrap))]
use proc_macro::tracked::path;
#[cfg(bootstrap)]
use proc_macro::tracked_path::path;
use proc_macro::{Diagnostic, Level, Span};
use proc_macro2::TokenStream;
use quote::quote;

View file

@ -1,8 +1,7 @@
// tidy-alphabetical-start
#![allow(rustc::default_hash_types)]
#![cfg_attr(bootstrap, feature(track_path))]
#![cfg_attr(not(bootstrap), feature(proc_macro_tracked_path))]
#![feature(proc_macro_diagnostic)]
#![feature(proc_macro_tracked_path)]
// tidy-alphabetical-end
use proc_macro::TokenStream;

View file

@ -588,6 +588,108 @@ pub enum CollapseMacroDebuginfo {
Yes = 3,
}
/// Crate type, as specified by `#![crate_type]`
#[derive(Copy, Clone, Debug, Hash, PartialEq, Default, PartialOrd, Eq, Ord)]
#[derive(HashStable_Generic, Encodable, Decodable, PrintAttribute)]
pub enum CrateType {
/// `#![crate_type = "bin"]`
Executable,
/// `#![crate_type = "dylib"]`
Dylib,
/// `#![crate_type = "rlib"]` or `#![crate_type = "lib"]`
#[default]
Rlib,
/// `#![crate_type = "staticlib"]`
StaticLib,
/// `#![crate_type = "cdylib"]`
Cdylib,
/// `#![crate_type = "proc-macro"]`
ProcMacro,
/// `#![crate_type = "sdylib"]`
// Unstable; feature(export_stable)
Sdylib,
}
impl CrateType {
/// Pairs of each `#[crate_type] = "..."` value and the crate type it resolves to
pub fn all() -> &'static [(Symbol, Self)] {
debug_assert_eq!(CrateType::default(), CrateType::Rlib);
&[
(rustc_span::sym::lib, CrateType::Rlib),
(rustc_span::sym::rlib, CrateType::Rlib),
(rustc_span::sym::dylib, CrateType::Dylib),
(rustc_span::sym::cdylib, CrateType::Cdylib),
(rustc_span::sym::staticlib, CrateType::StaticLib),
(rustc_span::sym::proc_dash_macro, CrateType::ProcMacro),
(rustc_span::sym::bin, CrateType::Executable),
(rustc_span::sym::sdylib, CrateType::Sdylib),
]
}
/// Same as [`CrateType::all`], but does not include unstable options.
/// Used for diagnostics.
pub fn all_stable() -> &'static [(Symbol, Self)] {
debug_assert_eq!(CrateType::default(), CrateType::Rlib);
&[
(rustc_span::sym::lib, CrateType::Rlib),
(rustc_span::sym::rlib, CrateType::Rlib),
(rustc_span::sym::dylib, CrateType::Dylib),
(rustc_span::sym::cdylib, CrateType::Cdylib),
(rustc_span::sym::staticlib, CrateType::StaticLib),
(rustc_span::sym::proc_dash_macro, CrateType::ProcMacro),
(rustc_span::sym::bin, CrateType::Executable),
]
}
pub fn has_metadata(self) -> bool {
match self {
CrateType::Rlib | CrateType::Dylib | CrateType::ProcMacro => true,
CrateType::Executable
| CrateType::Cdylib
| CrateType::StaticLib
| CrateType::Sdylib => false,
}
}
}
impl TryFrom<Symbol> for CrateType {
type Error = ();
fn try_from(value: Symbol) -> Result<Self, Self::Error> {
Ok(match value {
rustc_span::sym::bin => CrateType::Executable,
rustc_span::sym::dylib => CrateType::Dylib,
rustc_span::sym::staticlib => CrateType::StaticLib,
rustc_span::sym::cdylib => CrateType::Cdylib,
rustc_span::sym::rlib => CrateType::Rlib,
rustc_span::sym::lib => CrateType::default(),
rustc_span::sym::proc_dash_macro => CrateType::ProcMacro,
rustc_span::sym::sdylib => CrateType::Sdylib,
_ => return Err(()),
})
}
}
impl std::fmt::Display for CrateType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match *self {
CrateType::Executable => "bin".fmt(f),
CrateType::Dylib => "dylib".fmt(f),
CrateType::Rlib => "rlib".fmt(f),
CrateType::StaticLib => "staticlib".fmt(f),
CrateType::Cdylib => "cdylib".fmt(f),
CrateType::ProcMacro => "proc-macro".fmt(f),
CrateType::Sdylib => "sdylib".fmt(f),
}
}
}
impl IntoDiagArg for CrateType {
fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
self.to_string().into_diag_arg(&mut None)
}
}
/// Represents parsed *built-in* inert attributes.
///
/// ## Overview
@ -687,6 +789,9 @@ pub enum AttributeKind {
/// Represents `#[collapse_debuginfo]`.
CollapseDebugInfo(CollapseMacroDebuginfo),
/// Represents `#[compiler_builtins]`.
CompilerBuiltins,
/// Represents `#[rustc_confusables]`.
Confusables {
symbols: ThinVec<Symbol>,
@ -716,6 +821,9 @@ pub enum AttributeKind {
/// Represents `#[crate_name = ...]`
CrateName { name: Symbol, name_span: Span, attr_span: Span },
/// Represents `#![crate_type = ...]`
CrateType(ThinVec<CrateType>),
/// Represents `#[custom_mir]`.
CustomMir(Option<(MirDialect, Span)>, Option<(MirPhase, Span)>, Span),
@ -728,9 +836,6 @@ pub enum AttributeKind {
/// Represents [`#[deprecated]`](https://doc.rust-lang.org/stable/reference/attributes/diagnostics.html#the-deprecated-attribute).
Deprecation { deprecation: Deprecation, span: Span },
/// Represents `#[rustc_do_not_implement_via_object]`.
DoNotImplementViaObject(Span),
/// Represents `#[diagnostic::do_not_recommend]`.
DoNotRecommend { attr_span: Span },
@ -746,6 +851,9 @@ pub enum AttributeKind {
/// Represents `#[rustc_dummy]`.
Dummy,
/// Represents `#[rustc_dyn_incompatible_trait]`.
DynIncompatibleTrait(Span),
/// Implementation detail of `#[eii]`
EiiDeclaration(EiiDecl),
@ -843,6 +951,12 @@ pub enum AttributeKind {
/// Represents `#[needs_allocator]`
NeedsAllocator,
/// Represents `#[needs_panic_runtime]`
NeedsPanicRuntime,
/// Represents `#[no_builtins]`
NoBuiltins,
/// Represents `#[no_core]`
NoCore(Span),
@ -873,12 +987,18 @@ pub enum AttributeKind {
/// Represents `#[optimize(size|speed)]`
Optimize(OptimizeAttr, Span),
/// Represents `#[panic_runtime]`
PanicRuntime,
/// Represents `#[rustc_paren_sugar]`.
ParenSugar(Span),
/// Represents `#[rustc_pass_by_value]` (used by the `rustc_pass_by_value` lint).
PassByValue(Span),
/// Represents `#[patchable_function_entry]`
PatchableFunctionEntry { prefix: u8, entry: u8 },
/// Represents `#[path]`
Path(Symbol, Span),
@ -900,6 +1020,9 @@ pub enum AttributeKind {
/// Represents `#[proc_macro_derive]`
ProcMacroDerive { trait_name: Symbol, helper_attrs: ThinVec<Symbol>, span: Span },
/// Represents `#[profiler_runtime]`
ProfilerRuntime,
/// Represents `#[rustc_pub_transparent]` (used by the `repr_transparent_external_private_fields` lint).
PubTransparent(Span),
@ -1007,6 +1130,12 @@ pub enum AttributeKind {
/// Represents `#[rustc_simd_monomorphize_lane_limit = "N"]`.
RustcSimdMonomorphizeLaneLimit(Limit),
/// Represents `#[rustc_variance]`
RustcVariance,
/// Represents `#[rustc_variance_of_opaques]`
RustcVarianceOfOpaques,
/// Represents `#[sanitize]`
///
/// the on set and off set are distjoint since there's a third option: unset.

View file

@ -32,6 +32,7 @@ impl AttributeKind {
Coinductive(..) => No,
Cold(..) => No,
CollapseDebugInfo(..) => Yes,
CompilerBuiltins => No,
Confusables { .. } => Yes,
ConstContinue(..) => No,
ConstStability { .. } => Yes,
@ -39,15 +40,16 @@ impl AttributeKind {
Coroutine(..) => No,
Coverage(..) => No,
CrateName { .. } => No,
CrateType(_) => No,
CustomMir(_, _, _) => Yes,
DebuggerVisualizer(..) => No,
DenyExplicitImpl(..) => No,
Deprecation { .. } => Yes,
DoNotImplementViaObject(..) => No,
DoNotRecommend { .. } => Yes,
Doc(_) => Yes,
DocComment { .. } => Yes,
Dummy => No,
DynIncompatibleTrait(..) => No,
EiiDeclaration(_) => Yes,
EiiForeignItem => No,
EiiImpls(..) => No,
@ -76,6 +78,8 @@ impl AttributeKind {
MustUse { .. } => Yes,
Naked(..) => No,
NeedsAllocator => No,
NeedsPanicRuntime => No,
NoBuiltins => Yes,
NoCore(..) => No,
NoImplicitPrelude(..) => No,
NoLink => No,
@ -86,8 +90,10 @@ impl AttributeKind {
ObjcClass { .. } => No,
ObjcSelector { .. } => No,
Optimize(..) => No,
PanicRuntime => No,
ParenSugar(..) => No,
PassByValue(..) => Yes,
PatchableFunctionEntry { .. } => Yes,
Path(..) => No,
PatternComplexityLimit { .. } => No,
PinV2(..) => Yes,
@ -95,6 +101,7 @@ impl AttributeKind {
ProcMacro(..) => No,
ProcMacroAttribute(..) => No,
ProcMacroDerive { .. } => No,
ProfilerRuntime => No,
PubTransparent(..) => Yes,
RecursionLimit { .. } => No,
Repr { .. } => No,
@ -129,6 +136,8 @@ impl AttributeKind {
RustcScalableVector { .. } => Yes,
RustcShouldNotBeCalledOnConstItems(..) => Yes,
RustcSimdMonomorphizeLaneLimit(..) => Yes, // Affects layout computation, which needs to work cross-crate
RustcVariance => No,
RustcVarianceOfOpaques => No,
Sanitize { .. } => No,
ShouldPanic { .. } => No,
SkipDuringMethodDispatch { .. } => No,

View file

@ -171,7 +171,7 @@ macro_rules! print_tup {
print_tup!(A B C D E F G H);
print_skip!(Span, (), ErrorGuaranteed);
print_disp!(u16, u128, usize, bool, NonZero<u32>, Limit);
print_disp!(u8, u16, u128, usize, bool, NonZero<u32>, Limit);
print_debug!(
Symbol,
Ident,

View file

@ -171,6 +171,7 @@ impl Target {
ast::ItemKind::Use(..) => Target::Use,
ast::ItemKind::Static { .. } => Target::Static,
ast::ItemKind::Const(..) => Target::Const,
ast::ItemKind::ConstBlock(..) => Target::Const,
ast::ItemKind::Fn { .. } => Target::Fn,
ast::ItemKind::Mod(..) => Target::Mod,
ast::ItemKind::ForeignMod { .. } => Target::ForeignMod,

View file

@ -165,6 +165,11 @@ hir_analysis_drop_impl_reservation = reservation `Drop` impls are not supported
hir_analysis_duplicate_precise_capture = cannot capture parameter `{$name}` twice
.label = parameter captured again here
hir_analysis_dyn_trait_assoc_item_binding_mentions_self =
{$kind} binding in trait object type mentions `Self`
.label = contains a mention of `Self`
.binding_label = this binding mentions `Self`
hir_analysis_eii_with_generics =
`{$impl_name}` cannot have generic parameters other than lifetimes
.label = required by this attribute
@ -330,6 +335,31 @@ hir_analysis_manual_implementation =
hir_analysis_method_should_return_future = method should be `async` or return a future, but it is synchronous
.note = this method is `async` so it expects a future to be returned
hir_analysis_missing_generic_params =
the {$descr} {$parameterCount ->
[one] parameter
*[other] parameters
} {$parameters} must be explicitly specified
.label = {$descr} {$parameterCount ->
[one] parameter
*[other] parameters
} {$parameters} must be specified for this
.suggestion = explicitly specify the {$descr} {$parameterCount ->
[one] parameter
*[other] parameters
}
.no_suggestion_label = missing {$parameterCount ->
[one] reference
*[other] references
} to {$parameters}
.note = because the parameter {$parameterCount ->
[one] default references
*[other] defaults reference
} `Self`, the {$parameterCount ->
[one] parameter
*[other] parameters
} must be specified on the trait object type
hir_analysis_missing_one_of_trait_item = not all trait items implemented, missing one of: `{$missing_items_msg}`
.label = missing one of `{$missing_items_msg}` in implementation
.note = required because of this annotation
@ -346,34 +376,6 @@ hir_analysis_missing_trait_item_unstable = not all trait items implemented, miss
.some_note = use of unstable library feature `{$feature}`: {$reason}
.none_note = use of unstable library feature `{$feature}`
hir_analysis_missing_type_params =
the type {$parameterCount ->
[one] parameter
*[other] parameters
} {$parameters} must be explicitly specified
.label = type {$parameterCount ->
[one] parameter
*[other] parameters
} {$parameters} must be specified for this
.suggestion = set the type {$parameterCount ->
[one] parameter
*[other] parameters
} to the desired {$parameterCount ->
[one] type
*[other] types
}
.no_suggestion_label = missing {$parameterCount ->
[one] reference
*[other] references
} to {$parameters}
.note = because the parameter {$parameterCount ->
[one] default references
*[other] defaults reference
} `Self`, the {$parameterCount ->
[one] parameter
*[other] parameters
} must be specified on the object type
hir_analysis_no_variant_named = no variant named `{$ident}` found for enum `{$ty}`
hir_analysis_not_supported_delegation = {$descr}
@ -481,9 +483,6 @@ hir_analysis_self_in_impl_self =
`Self` is not valid in the self type of an impl block
.note = replace `Self` with a different type
hir_analysis_self_in_type_alias = `Self` is not allowed in type aliases
.label = `Self` is only available in impls, traits, and concrete type definitions
hir_analysis_self_ty_not_captured = `impl Trait` must mention the `Self` type of the trait in `use<...>`
.label = `Self` type parameter is implicitly captured by this `impl Trait`
.note = currently, all type parameters are required to be mentioned in the precise captures list

View file

@ -24,6 +24,7 @@ use rustc_middle::ty::{
TypeVisitable, TypeVisitableExt, fold_regions,
};
use rustc_session::lint::builtin::UNINHABITED_STATIC;
use rustc_span::source_map::Spanned;
use rustc_target::spec::{AbiMap, AbiMapping};
use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
use rustc_trait_selection::error_reporting::traits::on_unimplemented::OnUnimplementedDirective;
@ -192,6 +193,12 @@ fn check_static_inhabited(tcx: TyCtxt<'_>, def_id: LocalDefId) {
tcx.dcx().emit_err(errors::TooLargeStatic { span });
return;
}
// SIMD types with invalid layout (e.g., zero-length) should emit an error
Err(e @ LayoutError::InvalidSimd { .. }) => {
let ty_span = tcx.ty_span(def_id);
tcx.dcx().emit_err(Spanned { span: ty_span, node: e.into_diagnostic() });
return;
}
// Generic statics are rejected, but we still reach this case.
Err(e) => {
tcx.dcx().span_delayed_bug(span, format!("{e:?}"));

View file

@ -1960,7 +1960,7 @@ fn compare_generic_param_kinds<'tcx>(
trait_item: ty::AssocItem,
delay: bool,
) -> Result<(), ErrorGuaranteed> {
assert_eq!(impl_item.as_tag(), trait_item.as_tag());
assert_eq!(impl_item.tag(), trait_item.tag());
let ty_const_params_of = |def_id| {
tcx.generics_of(def_id).own_params.iter().filter(|param| {

View file

@ -216,6 +216,7 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi
| sym::type_name
| sym::type_of
| sym::ub_checks
| sym::va_copy
| sym::variant_count
| sym::vtable_for
| sym::wrapping_add
@ -629,14 +630,13 @@ pub(crate) fn check_intrinsic_type(
)
}
sym::va_start | sym::va_end => {
(0, 0, vec![mk_va_list_ty(hir::Mutability::Mut).0], tcx.types.unit)
}
sym::va_copy => {
let (va_list_ref_ty, va_list_ty) = mk_va_list_ty(hir::Mutability::Not);
let va_list_ptr_ty = Ty::new_mut_ptr(tcx, va_list_ty);
(0, 0, vec![va_list_ptr_ty, va_list_ref_ty], tcx.types.unit)
(0, 0, vec![va_list_ref_ty], va_list_ty)
}
sym::va_start | sym::va_end => {
(0, 0, vec![mk_va_list_ty(hir::Mutability::Mut).0], tcx.types.unit)
}
sym::va_arg => (1, 0, vec![mk_va_list_ty(hir::Mutability::Mut).0], param(0)),
@ -746,6 +746,7 @@ pub(crate) fn check_intrinsic_type(
sym::simd_extract | sym::simd_extract_dyn => {
(2, 0, vec![param(0), tcx.types.u32], param(1))
}
sym::simd_splat => (2, 0, vec![param(1)], param(0)),
sym::simd_cast
| sym::simd_as
| sym::simd_cast_ptr

View file

@ -211,9 +211,7 @@ fn check_object_overlap<'tcx>(
// This is a WF error tested by `coherence-impl-trait-for-trait-dyn-compatible.rs`.
} else {
let mut supertrait_def_ids = elaborate::supertrait_def_ids(tcx, component_def_id);
if supertrait_def_ids
.any(|d| d == trait_def_id && tcx.trait_def(d).implement_via_object)
{
if supertrait_def_ids.any(|d| d == trait_def_id) {
let span = tcx.def_span(impl_def_id);
return Err(struct_span_code_err!(
tcx.dcx(),

View file

@ -924,7 +924,8 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef {
);
let deny_explicit_impl = find_attr!(attrs, AttributeKind::DenyExplicitImpl(_));
let implement_via_object = !find_attr!(attrs, AttributeKind::DoNotImplementViaObject(_));
let force_dyn_incompatible =
find_attr!(attrs, AttributeKind::DynIncompatibleTrait(span) => *span);
ty::TraitDef {
def_id: def_id.to_def_id(),
@ -939,7 +940,7 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef {
skip_boxed_slice_during_method_dispatch,
specialization_kind,
must_implement_one_of,
implement_via_object,
force_dyn_incompatible,
deny_explicit_impl,
}
}

View file

@ -4,11 +4,11 @@ use rustc_abi::ExternAbi;
use rustc_errors::codes::*;
use rustc_errors::{
Applicability, Diag, DiagCtxtHandle, DiagSymbolList, Diagnostic, EmissionGuarantee, Level,
MultiSpan,
MultiSpan, listify,
};
use rustc_hir::limit::Limit;
use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
use rustc_middle::ty::Ty;
use rustc_middle::ty::{self, Ty};
use rustc_span::{Ident, Span, Symbol};
use crate::fluent_generated as fluent;
@ -400,35 +400,58 @@ pub(crate) struct UnconstrainedOpaqueType {
pub what: &'static str,
}
pub(crate) struct MissingTypeParams {
pub(crate) struct MissingGenericParams {
pub span: Span,
pub def_span: Span,
pub span_snippet: Option<String>,
pub missing_type_params: Vec<Symbol>,
pub missing_generic_params: Vec<(Symbol, ty::GenericParamDefKind)>,
pub empty_generic_args: bool,
}
// Manual implementation of `Diagnostic` to be able to call `span_to_snippet`.
impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for MissingTypeParams {
// FIXME: This doesn't need to be a manual impl!
impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for MissingGenericParams {
#[track_caller]
fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> {
let mut err = Diag::new(dcx, level, fluent::hir_analysis_missing_type_params);
let mut err = Diag::new(dcx, level, fluent::hir_analysis_missing_generic_params);
err.span(self.span);
err.code(E0393);
err.arg("parameterCount", self.missing_type_params.len());
err.arg(
"parameters",
self.missing_type_params
.iter()
.map(|n| format!("`{n}`"))
.collect::<Vec<_>>()
.join(", "),
);
err.span_label(self.def_span, fluent::hir_analysis_label);
enum Descr {
Generic,
Type,
Const,
}
let mut descr = None;
for (_, kind) in &self.missing_generic_params {
descr = match (&descr, kind) {
(None, ty::GenericParamDefKind::Type { .. }) => Some(Descr::Type),
(None, ty::GenericParamDefKind::Const { .. }) => Some(Descr::Const),
(Some(Descr::Type), ty::GenericParamDefKind::Const { .. })
| (Some(Descr::Const), ty::GenericParamDefKind::Type { .. }) => {
Some(Descr::Generic)
}
_ => continue,
}
}
err.arg(
"descr",
match descr.unwrap() {
Descr::Generic => "generic",
Descr::Type => "type",
Descr::Const => "const",
},
);
err.arg("parameterCount", self.missing_generic_params.len());
err.arg(
"parameters",
listify(&self.missing_generic_params, |(n, _)| format!("`{n}`")).unwrap(),
);
let mut suggested = false;
// Don't suggest setting the type params if there are some already: the order is
// Don't suggest setting the generic params if there are some already: The order is
// tricky to get right and the user will already know what the syntax is.
if let Some(snippet) = self.span_snippet
&& self.empty_generic_args
@ -438,16 +461,16 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for MissingTypeParams {
// we would have to preserve the right order. For now, as clearly the user is
// aware of the syntax, we do nothing.
} else {
// The user wrote `Iterator`, so we don't have a type we can suggest, but at
// least we can clue them to the correct syntax `Iterator<Type>`.
// The user wrote `Trait`, so we don't have a type we can suggest, but at
// least we can clue them to the correct syntax `Trait</* Term */>`.
err.span_suggestion_verbose(
self.span.shrink_to_hi(),
fluent::hir_analysis_suggestion,
format!(
"<{}>",
self.missing_type_params
self.missing_generic_params
.iter()
.map(|n| n.to_string())
.map(|(n, _)| format!("/* {n} */"))
.collect::<Vec<_>>()
.join(", ")
),
@ -1609,11 +1632,14 @@ pub(crate) enum SupertraitItemShadowee {
}
#[derive(Diagnostic)]
#[diag(hir_analysis_self_in_type_alias, code = E0411)]
pub(crate) struct SelfInTypeAlias {
#[diag(hir_analysis_dyn_trait_assoc_item_binding_mentions_self)]
pub(crate) struct DynTraitAssocItemBindingMentionsSelf {
#[primary_span]
#[label]
pub span: Span,
pub kind: &'static str,
#[label(hir_analysis_binding_label)]
pub binding: Span,
}
#[derive(Diagnostic)]

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