blob: 552b9dfabc2932c6304f2bd72623ddc1ca5c28f3 [file] [log] [blame]
use rustc_feature::{AttributeTemplate, template};
use rustc_hir::attrs::AttributeKind::{LinkName, LinkOrdinal, LinkSection};
use rustc_hir::attrs::{AttributeKind, Linkage};
use rustc_hir::{MethodKind, Target};
use rustc_span::{Span, Symbol, sym};
use crate::attributes::{
AttributeOrder, NoArgsAttributeParser, OnDuplicate, SingleAttributeParser,
};
use crate::context::MaybeWarn::Allow;
use crate::context::{ALL_TARGETS, AcceptContext, AllowedTargets, Stage, parse_single_integer};
use crate::parser::ArgParser;
use crate::session_diagnostics::{LinkOrdinalOutOfRange, NullOnLinkSection};
pub(crate) struct LinkNameParser;
impl<S: Stage> SingleAttributeParser<S> for LinkNameParser {
const PATH: &[Symbol] = &[sym::link_name];
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost;
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowListWarnRest(&[
Allow(Target::ForeignFn),
Allow(Target::ForeignStatic),
]);
const TEMPLATE: AttributeTemplate = template!(
NameValueStr: "name",
"https://doc.rust-lang.org/reference/items/external-blocks.html#the-link_name-attribute"
);
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
let Some(nv) = args.name_value() else {
cx.expected_name_value(cx.attr_span, None);
return None;
};
let Some(name) = nv.value_as_str() else {
cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit()));
return None;
};
Some(LinkName { name, span: cx.attr_span })
}
}
pub(crate) struct LinkSectionParser;
impl<S: Stage> SingleAttributeParser<S> for LinkSectionParser {
const PATH: &[Symbol] = &[sym::link_section];
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost;
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
const ALLOWED_TARGETS: AllowedTargets =
AllowedTargets::AllowListWarnRest(&[Allow(Target::Static), Allow(Target::Fn)]);
const TEMPLATE: AttributeTemplate = template!(
NameValueStr: "name",
"https://doc.rust-lang.org/reference/abi.html#the-link_section-attribute"
);
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
let Some(nv) = args.name_value() else {
cx.expected_name_value(cx.attr_span, None);
return None;
};
let Some(name) = nv.value_as_str() else {
cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit()));
return None;
};
if name.as_str().contains('\0') {
// `#[link_section = ...]` will be converted to a null-terminated string,
// so it may not contain any null characters.
cx.emit_err(NullOnLinkSection { span: cx.attr_span });
return None;
}
Some(LinkSection { name, span: cx.attr_span })
}
}
pub(crate) struct ExportStableParser;
impl<S: Stage> NoArgsAttributeParser<S> for ExportStableParser {
const PATH: &[Symbol] = &[sym::export_stable];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); //FIXME Still checked fully in `check_attr.rs`
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::ExportStable;
}
pub(crate) struct FfiConstParser;
impl<S: Stage> NoArgsAttributeParser<S> for FfiConstParser {
const PATH: &[Symbol] = &[sym::ffi_const];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::ForeignFn)]);
const CREATE: fn(Span) -> AttributeKind = AttributeKind::FfiConst;
}
pub(crate) struct FfiPureParser;
impl<S: Stage> NoArgsAttributeParser<S> for FfiPureParser {
const PATH: &[Symbol] = &[sym::ffi_pure];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::ForeignFn)]);
const CREATE: fn(Span) -> AttributeKind = AttributeKind::FfiPure;
}
pub(crate) struct StdInternalSymbolParser;
impl<S: Stage> NoArgsAttributeParser<S> for StdInternalSymbolParser {
const PATH: &[Symbol] = &[sym::rustc_std_internal_symbol];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
Allow(Target::Fn),
Allow(Target::ForeignFn),
Allow(Target::Static),
Allow(Target::ForeignStatic),
]);
const CREATE: fn(Span) -> AttributeKind = AttributeKind::StdInternalSymbol;
}
pub(crate) struct LinkOrdinalParser;
impl<S: Stage> SingleAttributeParser<S> for LinkOrdinalParser {
const PATH: &[Symbol] = &[sym::link_ordinal];
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ALLOWED_TARGETS: AllowedTargets =
AllowedTargets::AllowList(&[Allow(Target::ForeignFn), Allow(Target::ForeignStatic)]);
const TEMPLATE: AttributeTemplate = template!(
List: &["ordinal"],
"https://doc.rust-lang.org/reference/items/external-blocks.html#the-link_ordinal-attribute"
);
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
let ordinal = parse_single_integer(cx, args)?;
// According to the table at
// https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#import-header, the
// ordinal must fit into 16 bits. Similarly, the Ordinal field in COFFShortExport (defined
// in llvm/include/llvm/Object/COFFImportFile.h), which we use to communicate import
// information to LLVM for `#[link(kind = "raw-dylib"_])`, is also defined to be uint16_t.
//
// FIXME: should we allow an ordinal of 0? The MSVC toolchain has inconsistent support for
// this: both LINK.EXE and LIB.EXE signal errors and abort when given a .DEF file that
// specifies a zero ordinal. However, llvm-dlltool is perfectly happy to generate an import
// library for such a .DEF file, and MSVC's LINK.EXE is also perfectly happy to consume an
// import library produced by LLVM with an ordinal of 0, and it generates an .EXE. (I
// don't know yet if the resulting EXE runs, as I haven't yet built the necessary DLL --
// see earlier comment about LINK.EXE failing.)
let Ok(ordinal) = ordinal.try_into() else {
cx.emit_err(LinkOrdinalOutOfRange { span: cx.attr_span, ordinal });
return None;
};
Some(LinkOrdinal { ordinal, span: cx.attr_span })
}
}
pub(crate) struct LinkageParser;
impl<S: Stage> SingleAttributeParser<S> for LinkageParser {
const PATH: &[Symbol] = &[sym::linkage];
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
Allow(Target::Fn),
Allow(Target::Method(MethodKind::Inherent)),
Allow(Target::Method(MethodKind::Trait { body: false })),
Allow(Target::Method(MethodKind::Trait { body: true })),
Allow(Target::Method(MethodKind::TraitImpl)),
Allow(Target::Static),
Allow(Target::ForeignStatic),
Allow(Target::ForeignFn),
]);
const TEMPLATE: AttributeTemplate = template!(NameValueStr: [
"available_externally",
"common",
"extern_weak",
"external",
"internal",
"linkonce",
"linkonce_odr",
"weak",
"weak_odr",
]);
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
let Some(name_value) = args.name_value() else {
cx.expected_name_value(cx.attr_span, Some(sym::linkage));
return None;
};
let Some(value) = name_value.value_as_str() else {
cx.expected_string_literal(name_value.value_span, Some(name_value.value_as_lit()));
return None;
};
// Use the names from src/llvm/docs/LangRef.rst here. Most types are only
// applicable to variable declarations and may not really make sense for
// Rust code in the first place but allow them anyway and trust that the
// user knows what they're doing. Who knows, unanticipated use cases may pop
// up in the future.
//
// ghost, dllimport, dllexport and linkonce_odr_autohide are not supported
// and don't have to be, LLVM treats them as no-ops.
let linkage = match value {
sym::available_externally => Linkage::AvailableExternally,
sym::common => Linkage::Common,
sym::extern_weak => Linkage::ExternalWeak,
sym::external => Linkage::External,
sym::internal => Linkage::Internal,
sym::linkonce => Linkage::LinkOnceAny,
sym::linkonce_odr => Linkage::LinkOnceODR,
sym::weak => Linkage::WeakAny,
sym::weak_odr => Linkage::WeakODR,
_ => {
cx.expected_specific_argument(
name_value.value_span,
vec![
"available_externally",
"common",
"extern_weak",
"external",
"internal",
"linkonce",
"linkonce_odr",
"weak",
"weak_odr",
],
);
return None;
}
};
Some(AttributeKind::Linkage(linkage, cx.attr_span))
}
}