Auto merge of #109091 - Nilstrieb:match-on-attr, r=cjgillot
Cleanup `codegen_fn_attrs` The `match` control flow construct has been stable since 1.0, we should use it here. Sorry for the hard to review diff, I did try to at least split it into two commits. But looking at before-after side-by-side (instead of whatever github is doing) is probably the easiest way to make sure that I didn't forget about anything. On top of #109088, you can wait for that
This commit is contained in:
commit
7a0600714a
1 changed files with 332 additions and 299 deletions
|
@ -10,6 +10,7 @@ use rustc_middle::mir::mono::Linkage;
|
||||||
use rustc_middle::ty::query::Providers;
|
use rustc_middle::ty::query::Providers;
|
||||||
use rustc_middle::ty::{self as ty, TyCtxt};
|
use rustc_middle::ty::{self as ty, TyCtxt};
|
||||||
use rustc_session::{lint, parse::feature_err};
|
use rustc_session::{lint, parse::feature_err};
|
||||||
|
use rustc_span::symbol::Ident;
|
||||||
use rustc_span::{sym, Span};
|
use rustc_span::{sym, Span};
|
||||||
use rustc_target::spec::{abi, SanitizerSet};
|
use rustc_target::spec::{abi, SanitizerSet};
|
||||||
|
|
||||||
|
@ -83,336 +84,368 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if attr.has_name(sym::cold) {
|
let Some(Ident { name, .. }) = attr.ident() else {
|
||||||
codegen_fn_attrs.flags |= CodegenFnAttrFlags::COLD;
|
continue;
|
||||||
} else if attr.has_name(sym::rustc_allocator) {
|
};
|
||||||
codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR;
|
|
||||||
} else if attr.has_name(sym::ffi_returns_twice) {
|
match name {
|
||||||
codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_RETURNS_TWICE;
|
sym::cold => {
|
||||||
} else if attr.has_name(sym::ffi_pure) {
|
codegen_fn_attrs.flags |= CodegenFnAttrFlags::COLD;
|
||||||
codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_PURE;
|
}
|
||||||
} else if attr.has_name(sym::ffi_const) {
|
sym::rustc_allocator => {
|
||||||
codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_CONST;
|
codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR;
|
||||||
} else if attr.has_name(sym::rustc_nounwind) {
|
}
|
||||||
codegen_fn_attrs.flags |= CodegenFnAttrFlags::NEVER_UNWIND;
|
sym::ffi_returns_twice => {
|
||||||
} else if attr.has_name(sym::rustc_reallocator) {
|
codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_RETURNS_TWICE;
|
||||||
codegen_fn_attrs.flags |= CodegenFnAttrFlags::REALLOCATOR;
|
}
|
||||||
} else if attr.has_name(sym::rustc_deallocator) {
|
sym::ffi_pure => {
|
||||||
codegen_fn_attrs.flags |= CodegenFnAttrFlags::DEALLOCATOR;
|
codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_PURE;
|
||||||
} else if attr.has_name(sym::rustc_allocator_zeroed) {
|
}
|
||||||
codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR_ZEROED;
|
sym::ffi_const => {
|
||||||
} else if attr.has_name(sym::naked) {
|
codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_CONST;
|
||||||
codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED;
|
}
|
||||||
} else if attr.has_name(sym::no_mangle) {
|
sym::rustc_nounwind => {
|
||||||
codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE;
|
codegen_fn_attrs.flags |= CodegenFnAttrFlags::NEVER_UNWIND;
|
||||||
} else if attr.has_name(sym::no_coverage) {
|
}
|
||||||
codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_COVERAGE;
|
sym::rustc_reallocator => {
|
||||||
} else if attr.has_name(sym::rustc_std_internal_symbol) {
|
codegen_fn_attrs.flags |= CodegenFnAttrFlags::REALLOCATOR;
|
||||||
codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL;
|
}
|
||||||
} else if attr.has_name(sym::used) {
|
sym::rustc_deallocator => {
|
||||||
let inner = attr.meta_item_list();
|
codegen_fn_attrs.flags |= CodegenFnAttrFlags::DEALLOCATOR;
|
||||||
match inner.as_deref() {
|
}
|
||||||
Some([item]) if item.has_name(sym::linker) => {
|
sym::rustc_allocator_zeroed => {
|
||||||
if !tcx.features().used_with_arg {
|
codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR_ZEROED;
|
||||||
feature_err(
|
}
|
||||||
&tcx.sess.parse_sess,
|
sym::naked => {
|
||||||
sym::used_with_arg,
|
codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED;
|
||||||
attr.span,
|
}
|
||||||
"`#[used(linker)]` is currently unstable",
|
sym::no_mangle => {
|
||||||
)
|
codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE;
|
||||||
.emit();
|
}
|
||||||
|
sym::no_coverage => {
|
||||||
|
codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_COVERAGE;
|
||||||
|
}
|
||||||
|
sym::rustc_std_internal_symbol => {
|
||||||
|
codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL;
|
||||||
|
}
|
||||||
|
sym::used => {
|
||||||
|
let inner = attr.meta_item_list();
|
||||||
|
match inner.as_deref() {
|
||||||
|
Some([item]) if item.has_name(sym::linker) => {
|
||||||
|
if !tcx.features().used_with_arg {
|
||||||
|
feature_err(
|
||||||
|
&tcx.sess.parse_sess,
|
||||||
|
sym::used_with_arg,
|
||||||
|
attr.span,
|
||||||
|
"`#[used(linker)]` is currently unstable",
|
||||||
|
)
|
||||||
|
.emit();
|
||||||
|
}
|
||||||
|
codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED_LINKER;
|
||||||
}
|
}
|
||||||
codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED_LINKER;
|
Some([item]) if item.has_name(sym::compiler) => {
|
||||||
}
|
if !tcx.features().used_with_arg {
|
||||||
Some([item]) if item.has_name(sym::compiler) => {
|
feature_err(
|
||||||
if !tcx.features().used_with_arg {
|
&tcx.sess.parse_sess,
|
||||||
feature_err(
|
sym::used_with_arg,
|
||||||
&tcx.sess.parse_sess,
|
attr.span,
|
||||||
sym::used_with_arg,
|
"`#[used(compiler)]` is currently unstable",
|
||||||
attr.span,
|
)
|
||||||
"`#[used(compiler)]` is currently unstable",
|
.emit();
|
||||||
)
|
}
|
||||||
.emit();
|
codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED;
|
||||||
|
}
|
||||||
|
Some(_) => {
|
||||||
|
tcx.sess.emit_err(ExpectedUsedSymbol { span: attr.span });
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
// Unfortunately, unconditionally using `llvm.used` causes
|
||||||
|
// issues in handling `.init_array` with the gold linker,
|
||||||
|
// but using `llvm.compiler.used` caused a nontrival amount
|
||||||
|
// of unintentional ecosystem breakage -- particularly on
|
||||||
|
// Mach-O targets.
|
||||||
|
//
|
||||||
|
// As a result, we emit `llvm.compiler.used` only on ELF
|
||||||
|
// targets. This is somewhat ad-hoc, but actually follows
|
||||||
|
// our pre-LLVM 13 behavior (prior to the ecosystem
|
||||||
|
// breakage), and seems to match `clang`'s behavior as well
|
||||||
|
// (both before and after LLVM 13), possibly because they
|
||||||
|
// have similar compatibility concerns to us. See
|
||||||
|
// https://github.com/rust-lang/rust/issues/47384#issuecomment-1019080146
|
||||||
|
// and following comments for some discussion of this, as
|
||||||
|
// well as the comments in `rustc_codegen_llvm` where these
|
||||||
|
// flags are handled.
|
||||||
|
//
|
||||||
|
// Anyway, to be clear: this is still up in the air
|
||||||
|
// somewhat, and is subject to change in the future (which
|
||||||
|
// is a good thing, because this would ideally be a bit
|
||||||
|
// more firmed up).
|
||||||
|
let is_like_elf = !(tcx.sess.target.is_like_osx
|
||||||
|
|| tcx.sess.target.is_like_windows
|
||||||
|
|| tcx.sess.target.is_like_wasm);
|
||||||
|
codegen_fn_attrs.flags |= if is_like_elf {
|
||||||
|
CodegenFnAttrFlags::USED
|
||||||
|
} else {
|
||||||
|
CodegenFnAttrFlags::USED_LINKER
|
||||||
|
};
|
||||||
}
|
}
|
||||||
codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED;
|
|
||||||
}
|
|
||||||
Some(_) => {
|
|
||||||
tcx.sess.emit_err(ExpectedUsedSymbol { span: attr.span });
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
// Unfortunately, unconditionally using `llvm.used` causes
|
|
||||||
// issues in handling `.init_array` with the gold linker,
|
|
||||||
// but using `llvm.compiler.used` caused a nontrival amount
|
|
||||||
// of unintentional ecosystem breakage -- particularly on
|
|
||||||
// Mach-O targets.
|
|
||||||
//
|
|
||||||
// As a result, we emit `llvm.compiler.used` only on ELF
|
|
||||||
// targets. This is somewhat ad-hoc, but actually follows
|
|
||||||
// our pre-LLVM 13 behavior (prior to the ecosystem
|
|
||||||
// breakage), and seems to match `clang`'s behavior as well
|
|
||||||
// (both before and after LLVM 13), possibly because they
|
|
||||||
// have similar compatibility concerns to us. See
|
|
||||||
// https://github.com/rust-lang/rust/issues/47384#issuecomment-1019080146
|
|
||||||
// and following comments for some discussion of this, as
|
|
||||||
// well as the comments in `rustc_codegen_llvm` where these
|
|
||||||
// flags are handled.
|
|
||||||
//
|
|
||||||
// Anyway, to be clear: this is still up in the air
|
|
||||||
// somewhat, and is subject to change in the future (which
|
|
||||||
// is a good thing, because this would ideally be a bit
|
|
||||||
// more firmed up).
|
|
||||||
let is_like_elf = !(tcx.sess.target.is_like_osx
|
|
||||||
|| tcx.sess.target.is_like_windows
|
|
||||||
|| tcx.sess.target.is_like_wasm);
|
|
||||||
codegen_fn_attrs.flags |= if is_like_elf {
|
|
||||||
CodegenFnAttrFlags::USED
|
|
||||||
} else {
|
|
||||||
CodegenFnAttrFlags::USED_LINKER
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if attr.has_name(sym::cmse_nonsecure_entry) {
|
sym::cmse_nonsecure_entry => {
|
||||||
if let Some(fn_sig) = fn_sig()
|
if let Some(fn_sig) = fn_sig()
|
||||||
&& !matches!(fn_sig.skip_binder().abi(), abi::Abi::C { .. })
|
&& !matches!(fn_sig.skip_binder().abi(), abi::Abi::C { .. })
|
||||||
{
|
{
|
||||||
struct_span_err!(
|
|
||||||
tcx.sess,
|
|
||||||
attr.span,
|
|
||||||
E0776,
|
|
||||||
"`#[cmse_nonsecure_entry]` requires C ABI"
|
|
||||||
)
|
|
||||||
.emit();
|
|
||||||
}
|
|
||||||
if !tcx.sess.target.llvm_target.contains("thumbv8m") {
|
|
||||||
struct_span_err!(tcx.sess, attr.span, E0775, "`#[cmse_nonsecure_entry]` is only valid for targets with the TrustZone-M extension")
|
|
||||||
.emit();
|
|
||||||
}
|
|
||||||
codegen_fn_attrs.flags |= CodegenFnAttrFlags::CMSE_NONSECURE_ENTRY;
|
|
||||||
} else if attr.has_name(sym::thread_local) {
|
|
||||||
codegen_fn_attrs.flags |= CodegenFnAttrFlags::THREAD_LOCAL;
|
|
||||||
} else if attr.has_name(sym::track_caller) {
|
|
||||||
if !tcx.is_closure(did.to_def_id())
|
|
||||||
&& let Some(fn_sig) = fn_sig()
|
|
||||||
&& fn_sig.skip_binder().abi() != abi::Abi::Rust
|
|
||||||
{
|
|
||||||
struct_span_err!(tcx.sess, attr.span, E0737, "`#[track_caller]` requires Rust ABI")
|
|
||||||
.emit();
|
|
||||||
}
|
|
||||||
if tcx.is_closure(did.to_def_id()) && !tcx.features().closure_track_caller {
|
|
||||||
feature_err(
|
|
||||||
&tcx.sess.parse_sess,
|
|
||||||
sym::closure_track_caller,
|
|
||||||
attr.span,
|
|
||||||
"`#[track_caller]` on closures is currently unstable",
|
|
||||||
)
|
|
||||||
.emit();
|
|
||||||
}
|
|
||||||
codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER;
|
|
||||||
} else if attr.has_name(sym::export_name) {
|
|
||||||
if let Some(s) = attr.value_str() {
|
|
||||||
if s.as_str().contains('\0') {
|
|
||||||
// `#[export_name = ...]` will be converted to a null-terminated string,
|
|
||||||
// so it may not contain any null characters.
|
|
||||||
struct_span_err!(
|
struct_span_err!(
|
||||||
tcx.sess,
|
tcx.sess,
|
||||||
attr.span,
|
attr.span,
|
||||||
E0648,
|
E0776,
|
||||||
"`export_name` may not contain null characters"
|
"`#[cmse_nonsecure_entry]` requires C ABI"
|
||||||
)
|
)
|
||||||
.emit();
|
.emit();
|
||||||
}
|
}
|
||||||
codegen_fn_attrs.export_name = Some(s);
|
if !tcx.sess.target.llvm_target.contains("thumbv8m") {
|
||||||
|
struct_span_err!(tcx.sess, attr.span, E0775, "`#[cmse_nonsecure_entry]` is only valid for targets with the TrustZone-M extension")
|
||||||
|
.emit();
|
||||||
|
}
|
||||||
|
codegen_fn_attrs.flags |= CodegenFnAttrFlags::CMSE_NONSECURE_ENTRY;
|
||||||
}
|
}
|
||||||
} else if attr.has_name(sym::target_feature) {
|
sym::thread_local => {
|
||||||
if !tcx.is_closure(did.to_def_id())
|
codegen_fn_attrs.flags |= CodegenFnAttrFlags::THREAD_LOCAL;
|
||||||
&& let Some(fn_sig) = fn_sig()
|
}
|
||||||
&& fn_sig.skip_binder().unsafety() == hir::Unsafety::Normal
|
sym::track_caller => {
|
||||||
{
|
if !tcx.is_closure(did.to_def_id())
|
||||||
if tcx.sess.target.is_like_wasm || tcx.sess.opts.actually_rustdoc {
|
&& let Some(fn_sig) = fn_sig()
|
||||||
// The `#[target_feature]` attribute is allowed on
|
&& fn_sig.skip_binder().abi() != abi::Abi::Rust
|
||||||
// WebAssembly targets on all functions, including safe
|
{
|
||||||
// ones. Other targets require that `#[target_feature]` is
|
struct_span_err!(tcx.sess, attr.span, E0737, "`#[track_caller]` requires Rust ABI")
|
||||||
// only applied to unsafe functions (pending the
|
.emit();
|
||||||
// `target_feature_11` feature) because on most targets
|
}
|
||||||
// execution of instructions that are not supported is
|
if tcx.is_closure(did.to_def_id()) && !tcx.features().closure_track_caller {
|
||||||
// considered undefined behavior. For WebAssembly which is a
|
feature_err(
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
// This exception needs to be kept in sync with allowing
|
|
||||||
// `#[target_feature]` on `main` and `start`.
|
|
||||||
} else if !tcx.features().target_feature_11 {
|
|
||||||
let mut err = feature_err(
|
|
||||||
&tcx.sess.parse_sess,
|
&tcx.sess.parse_sess,
|
||||||
sym::target_feature_11,
|
sym::closure_track_caller,
|
||||||
attr.span,
|
attr.span,
|
||||||
"`#[target_feature(..)]` can only be applied to `unsafe` functions",
|
"`#[track_caller]` on closures is currently unstable",
|
||||||
);
|
)
|
||||||
err.span_label(tcx.def_span(did), "not an `unsafe` function");
|
.emit();
|
||||||
err.emit();
|
}
|
||||||
} else {
|
codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER;
|
||||||
check_target_feature_trait_unsafe(tcx, did, attr.span);
|
}
|
||||||
|
sym::export_name => {
|
||||||
|
if let Some(s) = attr.value_str() {
|
||||||
|
if s.as_str().contains('\0') {
|
||||||
|
// `#[export_name = ...]` will be converted to a null-terminated string,
|
||||||
|
// so it may not contain any null characters.
|
||||||
|
struct_span_err!(
|
||||||
|
tcx.sess,
|
||||||
|
attr.span,
|
||||||
|
E0648,
|
||||||
|
"`export_name` may not contain null characters"
|
||||||
|
)
|
||||||
|
.emit();
|
||||||
|
}
|
||||||
|
codegen_fn_attrs.export_name = Some(s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
from_target_feature(
|
sym::target_feature => {
|
||||||
tcx,
|
if !tcx.is_closure(did.to_def_id())
|
||||||
attr,
|
&& let Some(fn_sig) = fn_sig()
|
||||||
supported_target_features,
|
&& fn_sig.skip_binder().unsafety() == hir::Unsafety::Normal
|
||||||
&mut codegen_fn_attrs.target_features,
|
{
|
||||||
);
|
if tcx.sess.target.is_like_wasm || tcx.sess.opts.actually_rustdoc {
|
||||||
} else if attr.has_name(sym::linkage) {
|
// The `#[target_feature]` attribute is allowed on
|
||||||
if let Some(val) = attr.value_str() {
|
// WebAssembly targets on all functions, including safe
|
||||||
let linkage = Some(linkage_by_name(tcx, did, val.as_str()));
|
// ones. Other targets require that `#[target_feature]` is
|
||||||
if tcx.is_foreign_item(did) {
|
// only applied to unsafe functions (pending the
|
||||||
codegen_fn_attrs.import_linkage = linkage;
|
// `target_feature_11` feature) because on most targets
|
||||||
} else {
|
// execution of instructions that are not supported is
|
||||||
codegen_fn_attrs.linkage = linkage;
|
// 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
|
||||||
} else if attr.has_name(sym::link_section) {
|
// feature was added in some form for this it would be a
|
||||||
if let Some(val) = attr.value_str() {
|
// deterministic trap. There is no undefined behavior when
|
||||||
if val.as_str().bytes().any(|b| b == 0) {
|
// executing WebAssembly so `#[target_feature]` is allowed
|
||||||
let msg = format!(
|
// on safe functions (but again, only for WebAssembly)
|
||||||
"illegal null byte in link_section \
|
//
|
||||||
value: `{}`",
|
// Note that this is also allowed if `actually_rustdoc` so
|
||||||
&val
|
// if a target is documenting some wasm-specific code then
|
||||||
);
|
// it's not spuriously denied.
|
||||||
tcx.sess.span_err(attr.span, &msg);
|
//
|
||||||
} else {
|
// This exception needs to be kept in sync with allowing
|
||||||
codegen_fn_attrs.link_section = Some(val);
|
// `#[target_feature]` on `main` and `start`.
|
||||||
}
|
} else if !tcx.features().target_feature_11 {
|
||||||
}
|
let mut err = feature_err(
|
||||||
} else if attr.has_name(sym::link_name) {
|
&tcx.sess.parse_sess,
|
||||||
codegen_fn_attrs.link_name = attr.value_str();
|
sym::target_feature_11,
|
||||||
} else if attr.has_name(sym::link_ordinal) {
|
attr.span,
|
||||||
link_ordinal_span = Some(attr.span);
|
"`#[target_feature(..)]` can only be applied to `unsafe` functions",
|
||||||
if let ordinal @ Some(_) = check_link_ordinal(tcx, attr) {
|
);
|
||||||
codegen_fn_attrs.link_ordinal = ordinal;
|
err.span_label(tcx.def_span(did), "not an `unsafe` function");
|
||||||
}
|
err.emit();
|
||||||
} else if attr.has_name(sym::no_sanitize) {
|
|
||||||
no_sanitize_span = Some(attr.span);
|
|
||||||
if let Some(list) = attr.meta_item_list() {
|
|
||||||
for item in list.iter() {
|
|
||||||
if item.has_name(sym::address) {
|
|
||||||
codegen_fn_attrs.no_sanitize |=
|
|
||||||
SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS;
|
|
||||||
} else if item.has_name(sym::cfi) {
|
|
||||||
codegen_fn_attrs.no_sanitize |= SanitizerSet::CFI;
|
|
||||||
} else if item.has_name(sym::kcfi) {
|
|
||||||
codegen_fn_attrs.no_sanitize |= SanitizerSet::KCFI;
|
|
||||||
} else if item.has_name(sym::memory) {
|
|
||||||
codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMORY;
|
|
||||||
} else if item.has_name(sym::memtag) {
|
|
||||||
codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMTAG;
|
|
||||||
} else if item.has_name(sym::shadow_call_stack) {
|
|
||||||
codegen_fn_attrs.no_sanitize |= SanitizerSet::SHADOWCALLSTACK;
|
|
||||||
} else if item.has_name(sym::thread) {
|
|
||||||
codegen_fn_attrs.no_sanitize |= SanitizerSet::THREAD;
|
|
||||||
} else if item.has_name(sym::hwaddress) {
|
|
||||||
codegen_fn_attrs.no_sanitize |= SanitizerSet::HWADDRESS;
|
|
||||||
} else {
|
} else {
|
||||||
tcx.sess
|
check_target_feature_trait_unsafe(tcx, did, attr.span);
|
||||||
.struct_span_err(item.span(), "invalid argument for `no_sanitize`")
|
}
|
||||||
.note("expected one of: `address`, `cfi`, `hwaddress`, `kcfi`, `memory`, `memtag`, `shadow-call-stack`, or `thread`")
|
}
|
||||||
.emit();
|
from_target_feature(
|
||||||
|
tcx,
|
||||||
|
attr,
|
||||||
|
supported_target_features,
|
||||||
|
&mut codegen_fn_attrs.target_features,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
sym::linkage => {
|
||||||
|
if let Some(val) = attr.value_str() {
|
||||||
|
let linkage = Some(linkage_by_name(tcx, did, val.as_str()));
|
||||||
|
if tcx.is_foreign_item(did) {
|
||||||
|
codegen_fn_attrs.import_linkage = linkage;
|
||||||
|
} else {
|
||||||
|
codegen_fn_attrs.linkage = linkage;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if attr.has_name(sym::instruction_set) {
|
sym::link_section => {
|
||||||
codegen_fn_attrs.instruction_set = attr.meta_item_list().and_then(|l| match &l[..] {
|
if let Some(val) = attr.value_str() {
|
||||||
[NestedMetaItem::MetaItem(set)] => {
|
if val.as_str().bytes().any(|b| b == 0) {
|
||||||
let segments =
|
let msg = format!(
|
||||||
set.path.segments.iter().map(|x| x.ident.name).collect::<Vec<_>>();
|
"illegal null byte in link_section \
|
||||||
match segments.as_slice() {
|
value: `{}`",
|
||||||
[sym::arm, sym::a32] | [sym::arm, sym::t32] => {
|
&val
|
||||||
if !tcx.sess.target.has_thumb_interworking {
|
);
|
||||||
struct_span_err!(
|
tcx.sess.span_err(attr.span, &msg);
|
||||||
tcx.sess.diagnostic(),
|
} else {
|
||||||
attr.span,
|
codegen_fn_attrs.link_section = Some(val);
|
||||||
E0779,
|
}
|
||||||
"target does not support `#[instruction_set]`"
|
}
|
||||||
)
|
}
|
||||||
.emit();
|
sym::link_name => {
|
||||||
None
|
codegen_fn_attrs.link_name = attr.value_str();
|
||||||
} else if segments[1] == sym::a32 {
|
}
|
||||||
Some(InstructionSetAttr::ArmA32)
|
sym::link_ordinal => {
|
||||||
} else if segments[1] == sym::t32 {
|
link_ordinal_span = Some(attr.span);
|
||||||
Some(InstructionSetAttr::ArmT32)
|
if let ordinal @ Some(_) = check_link_ordinal(tcx, attr) {
|
||||||
} else {
|
codegen_fn_attrs.link_ordinal = ordinal;
|
||||||
unreachable!()
|
}
|
||||||
|
}
|
||||||
|
sym::no_sanitize => {
|
||||||
|
no_sanitize_span = Some(attr.span);
|
||||||
|
if let Some(list) = attr.meta_item_list() {
|
||||||
|
for item in list.iter() {
|
||||||
|
match item.ident().map(|ident| ident.name) {
|
||||||
|
Some(sym::address) => {
|
||||||
|
codegen_fn_attrs.no_sanitize |=
|
||||||
|
SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS;
|
||||||
}
|
}
|
||||||
|
Some(sym::cfi) => {
|
||||||
|
codegen_fn_attrs.no_sanitize |= SanitizerSet::CFI;
|
||||||
|
}
|
||||||
|
Some(sym::kcfi) => {
|
||||||
|
codegen_fn_attrs.no_sanitize |= SanitizerSet::KCFI;
|
||||||
|
}
|
||||||
|
Some(sym::memory) => {
|
||||||
|
codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMORY;
|
||||||
|
}
|
||||||
|
Some(sym::memtag) => {
|
||||||
|
codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMTAG;
|
||||||
|
}
|
||||||
|
Some(sym::shadow_call_stack) => {
|
||||||
|
codegen_fn_attrs.no_sanitize |= SanitizerSet::SHADOWCALLSTACK;
|
||||||
|
}
|
||||||
|
Some(sym::thread) => {
|
||||||
|
codegen_fn_attrs.no_sanitize |= SanitizerSet::THREAD;
|
||||||
|
}
|
||||||
|
Some(sym::hwaddress) => {
|
||||||
|
codegen_fn_attrs.no_sanitize |= SanitizerSet::HWADDRESS;
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
tcx.sess
|
||||||
|
.struct_span_err(item.span(), "invalid argument for `no_sanitize`")
|
||||||
|
.note("expected one of: `address`, `cfi`, `hwaddress`, `kcfi`, `memory`, `memtag`, `shadow-call-stack`, or `thread`")
|
||||||
|
.emit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sym::instruction_set => {
|
||||||
|
codegen_fn_attrs.instruction_set =
|
||||||
|
attr.meta_item_list().and_then(|l| match &l[..] {
|
||||||
|
[NestedMetaItem::MetaItem(set)] => {
|
||||||
|
let segments =
|
||||||
|
set.path.segments.iter().map(|x| x.ident.name).collect::<Vec<_>>();
|
||||||
|
match segments.as_slice() {
|
||||||
|
[sym::arm, sym::a32] | [sym::arm, sym::t32] => {
|
||||||
|
if !tcx.sess.target.has_thumb_interworking {
|
||||||
|
struct_span_err!(
|
||||||
|
tcx.sess.diagnostic(),
|
||||||
|
attr.span,
|
||||||
|
E0779,
|
||||||
|
"target does not support `#[instruction_set]`"
|
||||||
|
)
|
||||||
|
.emit();
|
||||||
|
None
|
||||||
|
} else if segments[1] == sym::a32 {
|
||||||
|
Some(InstructionSetAttr::ArmA32)
|
||||||
|
} else if segments[1] == sym::t32 {
|
||||||
|
Some(InstructionSetAttr::ArmT32)
|
||||||
|
} else {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
struct_span_err!(
|
||||||
|
tcx.sess.diagnostic(),
|
||||||
|
attr.span,
|
||||||
|
E0779,
|
||||||
|
"invalid instruction set specified",
|
||||||
|
)
|
||||||
|
.emit();
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
[] => {
|
||||||
|
struct_span_err!(
|
||||||
|
tcx.sess.diagnostic(),
|
||||||
|
attr.span,
|
||||||
|
E0778,
|
||||||
|
"`#[instruction_set]` requires an argument"
|
||||||
|
)
|
||||||
|
.emit();
|
||||||
|
None
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
struct_span_err!(
|
struct_span_err!(
|
||||||
tcx.sess.diagnostic(),
|
tcx.sess.diagnostic(),
|
||||||
attr.span,
|
attr.span,
|
||||||
E0779,
|
E0779,
|
||||||
"invalid instruction set specified",
|
"cannot specify more than one instruction set"
|
||||||
)
|
)
|
||||||
.emit();
|
.emit();
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
[] => {
|
sym::repr => {
|
||||||
struct_span_err!(
|
codegen_fn_attrs.alignment = if let Some(items) = attr.meta_item_list()
|
||||||
tcx.sess.diagnostic(),
|
&& let [item] = items.as_slice()
|
||||||
attr.span,
|
&& let Some((sym::align, literal)) = item.name_value_literal()
|
||||||
E0778,
|
{
|
||||||
"`#[instruction_set]` requires an argument"
|
rustc_attr::parse_alignment(&literal.kind).map_err(|msg| {
|
||||||
)
|
struct_span_err!(
|
||||||
.emit();
|
tcx.sess.diagnostic(),
|
||||||
|
attr.span,
|
||||||
|
E0589,
|
||||||
|
"invalid `repr(align)` attribute: {}",
|
||||||
|
msg
|
||||||
|
)
|
||||||
|
.emit();
|
||||||
|
})
|
||||||
|
.ok()
|
||||||
|
} else {
|
||||||
None
|
None
|
||||||
}
|
};
|
||||||
_ => {
|
}
|
||||||
struct_span_err!(
|
_ => {}
|
||||||
tcx.sess.diagnostic(),
|
|
||||||
attr.span,
|
|
||||||
E0779,
|
|
||||||
"cannot specify more than one instruction set"
|
|
||||||
)
|
|
||||||
.emit();
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else if attr.has_name(sym::repr) {
|
|
||||||
codegen_fn_attrs.alignment = match attr.meta_item_list() {
|
|
||||||
Some(items) => match items.as_slice() {
|
|
||||||
[item] => match item.name_value_literal() {
|
|
||||||
Some((sym::align, literal)) => {
|
|
||||||
let alignment = rustc_attr::parse_alignment(&literal.kind);
|
|
||||||
|
|
||||||
match alignment {
|
|
||||||
Ok(align) => Some(align),
|
|
||||||
Err(msg) => {
|
|
||||||
struct_span_err!(
|
|
||||||
tcx.sess.diagnostic(),
|
|
||||||
attr.span,
|
|
||||||
E0589,
|
|
||||||
"invalid `repr(align)` attribute: {}",
|
|
||||||
msg
|
|
||||||
)
|
|
||||||
.emit();
|
|
||||||
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => None,
|
|
||||||
},
|
|
||||||
[] => None,
|
|
||||||
_ => None,
|
|
||||||
},
|
|
||||||
None => None,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue