use our own alternative to STD_INTERNAL_SYMBOL and make sure we mangle EIIs properly

This commit is contained in:
Jana Dönszelmann 2025-12-08 11:46:59 +01:00
parent 13de732df5
commit 5768b234de
No known key found for this signature in database
15 changed files with 91 additions and 14 deletions

View file

@ -699,3 +699,12 @@ impl<S: Stage> NoArgsAttributeParser<S> for RustcPassIndirectlyInNonRusticAbisPa
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Struct)]);
const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcPassIndirectlyInNonRusticAbis;
}
pub(crate) struct EiiExternItemParser;
impl<S: Stage> NoArgsAttributeParser<S> for EiiExternItemParser {
const PATH: &[Symbol] = &[sym::rustc_eii_extern_item];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::ForeignFn)]);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::EiiExternItem;
}

View file

@ -20,8 +20,8 @@ use crate::attributes::allow_unstable::{
};
use crate::attributes::body::CoroutineParser;
use crate::attributes::codegen_attrs::{
ColdParser, CoverageParser, ExportNameParser, ForceTargetFeatureParser, NakedParser,
NoMangleParser, ObjcClassParser, ObjcSelectorParser, OptimizeParser,
ColdParser, CoverageParser, EiiExternItemParser, ExportNameParser, ForceTargetFeatureParser,
NakedParser, NoMangleParser, ObjcClassParser, ObjcSelectorParser, OptimizeParser,
RustcPassIndirectlyInNonRusticAbisParser, SanitizeParser, TargetFeatureParser,
TrackCallerParser, UsedParser,
};
@ -227,6 +227,7 @@ attribute_parsers!(
Single<WithoutArgs<CoroutineParser>>,
Single<WithoutArgs<DenyExplicitImplParser>>,
Single<WithoutArgs<DoNotImplementViaObjectParser>>,
Single<WithoutArgs<EiiExternItemParser>>,
Single<WithoutArgs<ExportStableParser>>,
Single<WithoutArgs<FfiConstParser>>,
Single<WithoutArgs<FfiPureParser>>,

View file

@ -209,6 +209,24 @@ fn eii_(
}
// extern "…" { safe fn item(); }
let mut extern_item_attrs = attrs.clone();
extern_item_attrs.push(ast::Attribute {
kind: ast::AttrKind::Normal(Box::new(ast::NormalAttr {
item: ast::AttrItem {
unsafety: ast::Safety::Default,
// Add the rustc_eii_extern_item on the foreign item. Usually, foreign items are mangled.
// This attribute makes sure that we later know that this foreign item's symbol should not be.
path: ast::Path::from_ident(Ident::new(sym::rustc_eii_extern_item, span)),
args: ast::AttrArgs::Empty,
tokens: None,
},
tokens: None,
})),
id: ecx.sess.psess.attr_id_generator.mk_attr_id(),
style: ast::AttrStyle::Outer,
span,
});
let extern_block = Box::new(ast::Item {
attrs: ast::AttrVec::default(),
id: ast::DUMMY_NODE_ID,
@ -219,7 +237,7 @@ fn eii_(
safety: ast::Safety::Unsafe(span),
abi,
items: From::from([Box::new(ast::ForeignItem {
attrs: attrs.clone(),
attrs: extern_item_attrs,
id: ast::DUMMY_NODE_ID,
span: item_span,
vis,

View file

@ -127,7 +127,10 @@ fn reachable_non_generics_provider(tcx: TyCtxt<'_>, _: LocalCrate) -> DefIdMap<S
|| codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER),
rustc_std_internal_symbol: codegen_attrs
.flags
.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL),
.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL)
|| codegen_attrs
.flags
.contains(CodegenFnAttrFlags::EXTERNALLY_IMPLEMENTABLE_ITEM),
};
(def_id.to_def_id(), info)
})
@ -519,10 +522,12 @@ fn symbol_export_level(tcx: TyCtxt<'_>, sym_def_id: DefId) -> SymbolExportLevel
let is_extern = codegen_fn_attrs.contains_extern_indicator();
let std_internal =
codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL);
let eii = codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::EXTERNALLY_IMPLEMENTABLE_ITEM);
if is_extern && !std_internal {
if is_extern && !std_internal && !eii {
let target = &tcx.sess.target.llvm_target;
// WebAssembly cannot export data symbols, so reduce their export level
// FIXME(jdonszelmann) don't do a substring match here.
if target.contains("emscripten") {
if let DefKind::Static { .. } = tcx.def_kind(sym_def_id) {
return SymbolExportLevel::Rust;

View file

@ -313,6 +313,9 @@ fn process_builtin_attrs(
AttributeKind::ObjcSelector { methname, .. } => {
codegen_fn_attrs.objc_selector = Some(*methname);
}
AttributeKind::EiiExternItem => {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::EXTERNALLY_IMPLEMENTABLE_ITEM;
}
AttributeKind::EiiImpls(impls) => {
for i in impls {
let extern_item = find_attr!(
@ -344,7 +347,7 @@ fn process_builtin_attrs(
if i.is_default { Linkage::LinkOnceAny } else { Linkage::External },
Visibility::Default,
));
codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL;
codegen_fn_attrs.flags |= CodegenFnAttrFlags::EXTERNALLY_IMPLEMENTABLE_ITEM;
}
}
_ => {}
@ -448,6 +451,12 @@ fn apply_overrides(tcx: TyCtxt<'_>, did: LocalDefId, codegen_fn_attrs: &mut Code
// * `#[rustc_std_internal_symbol]` mangles the symbol name in a special way
// both for exports and imports through foreign items. This is handled further,
// during symbol mangling logic.
} else if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::EXTERNALLY_IMPLEMENTABLE_ITEM)
{
// * externally implementable items keep their mangled symbol name.
// multiple EIIs can have the same name, so not mangling them would be a bug.
// Implementing an EII does the appropriate name resolution to make sure the implementations
// get the same symbol name as the *mangled* foreign item they refer to so that's all good.
} else if codegen_fn_attrs.symbol_name.is_some() {
// * This can be overridden with the `#[link_name]` attribute
} else {

View file

@ -961,6 +961,11 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
allow_internal_unsafe, Normal, template!(Word), WarnFollowing,
EncodeCrossCrate::No, "allow_internal_unsafe side-steps the unsafe_code lint",
),
gated!(
rustc_eii_extern_item, Normal, template!(Word),
ErrorFollowing, EncodeCrossCrate::Yes, eii_internals,
"used internally to mark types with a `transparent` representation when it is guaranteed by the documentation",
),
rustc_attr!(
rustc_allowed_through_unstable_modules, Normal, template!(NameValueStr: "deprecation message"),
WarnFollowing, EncodeCrossCrate::No,

View file

@ -709,6 +709,9 @@ pub enum AttributeKind {
/// Represents `#[rustc_dummy]`.
Dummy,
/// Implementation detail of `#[eii]`
EiiExternItem,
/// Implementation detail of `#[eii]`
EiiExternTarget(EiiDecl),

View file

@ -43,6 +43,7 @@ impl AttributeKind {
Doc(_) => Yes,
DocComment { .. } => Yes,
Dummy => No,
EiiExternItem => No,
EiiExternTarget(_) => Yes,
EiiImpls(..) => No,
ExportName { .. } => Yes,

View file

@ -43,6 +43,10 @@ impl<'tcx> TyCtxt<'tcx> {
attrs.to_mut().flags.remove(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL);
}
if attrs.flags.contains(CodegenFnAttrFlags::EXTERNALLY_IMPLEMENTABLE_ITEM) {
attrs.to_mut().flags.remove(CodegenFnAttrFlags::EXTERNALLY_IMPLEMENTABLE_ITEM);
}
if attrs.symbol_name.is_some() {
attrs.to_mut().symbol_name = None;
}
@ -199,6 +203,12 @@ bitflags::bitflags! {
const FOREIGN_ITEM = 1 << 16;
/// `#[rustc_offload_kernel]`: indicates that this is an offload kernel, an extra ptr arg will be added.
const OFFLOAD_KERNEL = 1 << 17;
/// Externally implementable item symbols act a little like `RUSTC_STD_INTERNAL_SYMBOL`.
/// When a crate declares an EII and dependencies expect the symbol to exist,
/// they will refer to this symbol name before a definition is given.
/// As such, we must make sure these symbols really do exist in the final binary/library.
/// This flag is put on both the implementations of EIIs and the foreign item they implement.
const EXTERNALLY_IMPLEMENTABLE_ITEM = 1 << 18;
}
}
rustc_data_structures::external_bitflags_debug! { CodegenFnAttrFlags }
@ -242,6 +252,9 @@ impl CodegenFnAttrs {
self.flags.contains(CodegenFnAttrFlags::NO_MANGLE)
|| self.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL)
// note: for these we do also set a symbol name so technically also handled by the
// condition below. However, I think that regardless these should be treated as extern.
|| self.flags.contains(CodegenFnAttrFlags::EXTERNALLY_IMPLEMENTABLE_ITEM)
|| self.symbol_name.is_some()
|| match self.linkage {
// These are private, so make sure we don't try to consider

View file

@ -41,6 +41,7 @@ pub struct SymbolExportInfo {
/// Was the symbol marked as `#[used(compiler)]` or `#[used(linker)]`?
pub used: bool,
/// Was the symbol marked as `#[rustc_std_internal_symbol]`?
/// We also use this for externally implementable items.
pub rustc_std_internal_symbol: bool,
}

View file

@ -1643,11 +1643,13 @@ impl<'v> RootCollector<'_, 'v> {
MonoItemCollectionStrategy::Lazy => {
self.entry_fn.and_then(|(id, _)| id.as_local()) == Some(def_id)
|| self.tcx.is_reachable_non_generic(def_id)
|| self
.tcx
.codegen_fn_attrs(def_id)
.flags
.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL)
|| {
let flags = self.tcx.codegen_fn_attrs(def_id).flags;
flags.intersects(
CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL
| CodegenFnAttrFlags::EXTERNALLY_IMPLEMENTABLE_ITEM,
)
}
}
}
}

View file

@ -872,7 +872,7 @@ fn mono_item_visibility<'tcx>(
// visibility. In some situations though we'll want to prevent this
// symbol from being internalized.
//
// There's two categories of items here:
// There's three categories of items here:
//
// * First is weak lang items. These are basically mechanisms for
// libcore to forward-reference symbols defined later in crates like
@ -902,8 +902,16 @@ fn mono_item_visibility<'tcx>(
// visibility below. Like the weak lang items, though, we can't let
// LLVM internalize them as this decision is left up to the linker to
// omit them, so prevent them from being internalized.
//
// * Externally implementable items. They work (in this case) pretty much the same as
// RUSTC_STD_INTERNAL_SYMBOL in that their implementation is also chosen later in
// the compilation process and we can't let them be internalized and they can't
// show up as an external interface.
let attrs = tcx.codegen_fn_attrs(def_id);
if attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL) {
if attrs.flags.intersects(
CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL
| CodegenFnAttrFlags::EXTERNALLY_IMPLEMENTABLE_ITEM,
) {
*can_be_internalized = false;
}

View file

@ -217,6 +217,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
},
Attribute::Parsed(
AttributeKind::EiiExternTarget { .. }
| AttributeKind::EiiExternItem
| AttributeKind::BodyStability { .. }
| AttributeKind::ConstStabilityIndirect
| AttributeKind::MacroTransparency(_)

View file

@ -1944,6 +1944,7 @@ symbols! {
rustc_dump_user_args,
rustc_dump_vtable,
rustc_effective_visibility,
rustc_eii_extern_item,
rustc_evaluate_where_clauses,
rustc_expected_cgu_reuse,
rustc_force_inline,

View file

@ -1,2 +1,2 @@
foo1
foo1
foo2