1use std::str::FromStr;
2
3use rustc_abi::ExternAbi;
4use rustc_ast::expand::autodiff_attrs::{AutoDiffAttrs, DiffActivity, DiffMode};
5use rustc_ast::{LitKind, MetaItem, MetaItemInner, attr};
6use rustc_attr_parsing::ReprAttr::ReprAlign;
7use rustc_attr_parsing::{AttributeKind, InlineAttr, InstructionSetAttr, OptimizeAttr};
8use rustc_data_structures::fx::FxHashMap;
9use rustc_hir::def::DefKind;
10use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId};
11use rustc_hir::weak_lang_items::WEAK_LANG_ITEMS;
12use rustc_hir::{self as hir, HirId, LangItem, lang_items};
13use rustc_middle::middle::codegen_fn_attrs::{
14 CodegenFnAttrFlags, CodegenFnAttrs, PatchableFunctionEntry,
15};
16use rustc_middle::mir::mono::Linkage;
17use rustc_middle::query::Providers;
18use rustc_middle::span_bug;
19use rustc_middle::ty::{self as ty, TyCtxt};
20use rustc_session::parse::feature_err;
21use rustc_session::{Session, lint};
22use rustc_span::{Ident, Span, sym};
23use rustc_target::spec::SanitizerSet;
24use tracing::debug;
25
26use crate::errors;
27use crate::target_features::{check_target_feature_trait_unsafe, from_target_feature_attr};
28
29fn linkage_by_name(tcx: TyCtxt<'_>, def_id: LocalDefId, name: &str) -> Linkage {
30 use rustc_middle::mir::mono::Linkage::*;
31
32 match name {
41 "available_externally" => AvailableExternally,
42 "common" => Common,
43 "extern_weak" => ExternalWeak,
44 "external" => External,
45 "internal" => Internal,
46 "linkonce" => LinkOnceAny,
47 "linkonce_odr" => LinkOnceODR,
48 "weak" => WeakAny,
49 "weak_odr" => WeakODR,
50 _ => tcx.dcx().span_fatal(tcx.def_span(def_id), "invalid linkage specified"),
51 }
52}
53
54fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
55 if cfg!(debug_assertions) {
56 let def_kind = tcx.def_kind(did);
57 assert!(
58 def_kind.has_codegen_attrs(),
59 "unexpected `def_kind` in `codegen_fn_attrs`: {def_kind:?}",
60 );
61 }
62
63 let attrs = tcx.hir_attrs(tcx.local_def_id_to_hir_id(did));
64 let mut codegen_fn_attrs = CodegenFnAttrs::new();
65 if tcx.should_inherit_track_caller(did) {
66 codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER;
67 }
68
69 if cfg!(llvm_enzyme) {
72 let ad = autodiff_attrs(tcx, did.into());
73 codegen_fn_attrs.autodiff_item = ad;
74 }
75
76 let crate_attrs = tcx.hir_attrs(rustc_hir::CRATE_HIR_ID);
79 let no_builtins = attr::contains_name(crate_attrs, sym::no_builtins);
80 if no_builtins {
81 codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_BUILTINS;
82 }
83
84 let rust_target_features = tcx.rust_target_features(LOCAL_CRATE);
85
86 let mut inline_span = None;
87 let mut link_ordinal_span = None;
88 let mut no_sanitize_span = None;
89 let mut mixed_export_name_no_mangle_lint_state = MixedExportNameAndNoMangleState::default();
90 let mut no_mangle_span = None;
91
92 for attr in attrs.iter() {
93 let fn_sig = || {
99 use DefKind::*;
100
101 let def_kind = tcx.def_kind(did);
102 if let Fn | AssocFn | Variant | Ctor(..) = def_kind {
103 Some(tcx.fn_sig(did))
104 } else {
105 tcx.dcx().span_delayed_bug(
106 attr.span(),
107 "this attribute can only be applied to functions",
108 );
109 None
110 }
111 };
112
113 if let hir::Attribute::Parsed(p) = attr {
114 match p {
115 AttributeKind::Repr(reprs) => {
116 codegen_fn_attrs.alignment = reprs
117 .iter()
118 .filter_map(|(r, _)| if let ReprAlign(x) = r { Some(*x) } else { None })
119 .max();
120 }
121
122 _ => {}
123 }
124 }
125
126 let Some(Ident { name, .. }) = attr.ident() else {
127 continue;
128 };
129
130 match name {
131 sym::cold => codegen_fn_attrs.flags |= CodegenFnAttrFlags::COLD,
132 sym::rustc_allocator => codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR,
133 sym::ffi_pure => codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_PURE,
134 sym::ffi_const => codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_CONST,
135 sym::rustc_nounwind => codegen_fn_attrs.flags |= CodegenFnAttrFlags::NEVER_UNWIND,
136 sym::rustc_reallocator => codegen_fn_attrs.flags |= CodegenFnAttrFlags::REALLOCATOR,
137 sym::rustc_deallocator => codegen_fn_attrs.flags |= CodegenFnAttrFlags::DEALLOCATOR,
138 sym::rustc_allocator_zeroed => {
139 codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR_ZEROED
140 }
141 sym::naked => codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED,
142 sym::no_mangle => {
143 no_mangle_span = Some(attr.span());
144 if tcx.opt_item_name(did.to_def_id()).is_some() {
145 codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE;
146 mixed_export_name_no_mangle_lint_state.track_no_mangle(
147 attr.span(),
148 tcx.local_def_id_to_hir_id(did),
149 attr,
150 );
151 } else {
152 tcx.dcx()
153 .struct_span_err(
154 attr.span(),
155 format!(
156 "`#[no_mangle]` cannot be used on {} {} as it has no name",
157 tcx.def_descr_article(did.to_def_id()),
158 tcx.def_descr(did.to_def_id()),
159 ),
160 )
161 .emit();
162 }
163 }
164 sym::rustc_std_internal_symbol => {
165 codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL
166 }
167 sym::used => {
168 let inner = attr.meta_item_list();
169 match inner.as_deref() {
170 Some([item]) if item.has_name(sym::linker) => {
171 if !tcx.features().used_with_arg() {
172 feature_err(
173 &tcx.sess,
174 sym::used_with_arg,
175 attr.span(),
176 "`#[used(linker)]` is currently unstable",
177 )
178 .emit();
179 }
180 codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED_LINKER;
181 }
182 Some([item]) if item.has_name(sym::compiler) => {
183 if !tcx.features().used_with_arg() {
184 feature_err(
185 &tcx.sess,
186 sym::used_with_arg,
187 attr.span(),
188 "`#[used(compiler)]` is currently unstable",
189 )
190 .emit();
191 }
192 codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED;
193 }
194 Some(_) => {
195 tcx.dcx().emit_err(errors::ExpectedUsedSymbol { span: attr.span() });
196 }
197 None => {
198 let is_like_elf = !(tcx.sess.target.is_like_darwin
220 || tcx.sess.target.is_like_windows
221 || tcx.sess.target.is_like_wasm);
222 codegen_fn_attrs.flags |= if is_like_elf {
223 CodegenFnAttrFlags::USED
224 } else {
225 CodegenFnAttrFlags::USED_LINKER
226 };
227 }
228 }
229 }
230 sym::thread_local => codegen_fn_attrs.flags |= CodegenFnAttrFlags::THREAD_LOCAL,
231 sym::track_caller => {
232 let is_closure = tcx.is_closure_like(did.to_def_id());
233
234 if !is_closure
235 && let Some(fn_sig) = fn_sig()
236 && fn_sig.skip_binder().abi() != ExternAbi::Rust
237 {
238 tcx.dcx().emit_err(errors::RequiresRustAbi { span: attr.span() });
239 }
240 if is_closure
241 && !tcx.features().closure_track_caller()
242 && !attr.span().allows_unstable(sym::closure_track_caller)
243 {
244 feature_err(
245 &tcx.sess,
246 sym::closure_track_caller,
247 attr.span(),
248 "`#[track_caller]` on closures is currently unstable",
249 )
250 .emit();
251 }
252 codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER
253 }
254 sym::export_name => {
255 if let Some(s) = attr.value_str() {
256 if s.as_str().contains('\0') {
257 tcx.dcx().emit_err(errors::NullOnExport { span: attr.span() });
260 }
261 codegen_fn_attrs.export_name = Some(s);
262 mixed_export_name_no_mangle_lint_state.track_export_name(attr.span());
263 }
264 }
265 sym::target_feature => {
266 let Some(sig) = tcx.hir_node_by_def_id(did).fn_sig() else {
267 tcx.dcx().span_delayed_bug(attr.span(), "target_feature applied to non-fn");
268 continue;
269 };
270 let safe_target_features =
271 matches!(sig.header.safety, hir::HeaderSafety::SafeTargetFeatures);
272 codegen_fn_attrs.safe_target_features = safe_target_features;
273 if safe_target_features {
274 if tcx.sess.target.is_like_wasm || tcx.sess.opts.actually_rustdoc {
275 } else {
297 check_target_feature_trait_unsafe(tcx, did, attr.span());
298 }
299 }
300 from_target_feature_attr(
301 tcx,
302 attr,
303 rust_target_features,
304 &mut codegen_fn_attrs.target_features,
305 );
306 }
307 sym::linkage => {
308 if let Some(val) = attr.value_str() {
309 let linkage = Some(linkage_by_name(tcx, did, val.as_str()));
310 if tcx.is_foreign_item(did) {
311 codegen_fn_attrs.import_linkage = linkage;
312
313 if tcx.is_mutable_static(did.into()) {
314 let mut diag = tcx.dcx().struct_span_err(
315 attr.span(),
316 "extern mutable statics are not allowed with `#[linkage]`",
317 );
318 diag.note(
319 "marking the extern static mutable would allow changing which \
320 symbol the static references rather than make the target of the \
321 symbol mutable",
322 );
323 diag.emit();
324 }
325 } else {
326 codegen_fn_attrs.linkage = linkage;
327 }
328 }
329 }
330 sym::link_section => {
331 if let Some(val) = attr.value_str() {
332 if val.as_str().bytes().any(|b| b == 0) {
333 let msg = format!("illegal null byte in link_section value: `{val}`");
334 tcx.dcx().span_err(attr.span(), msg);
335 } else {
336 codegen_fn_attrs.link_section = Some(val);
337 }
338 }
339 }
340 sym::link_name => codegen_fn_attrs.link_name = attr.value_str(),
341 sym::link_ordinal => {
342 link_ordinal_span = Some(attr.span());
343 if let ordinal @ Some(_) = check_link_ordinal(tcx, attr) {
344 codegen_fn_attrs.link_ordinal = ordinal;
345 }
346 }
347 sym::no_sanitize => {
348 no_sanitize_span = Some(attr.span());
349 if let Some(list) = attr.meta_item_list() {
350 for item in list.iter() {
351 match item.name() {
352 Some(sym::address) => {
353 codegen_fn_attrs.no_sanitize |=
354 SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS
355 }
356 Some(sym::cfi) => codegen_fn_attrs.no_sanitize |= SanitizerSet::CFI,
357 Some(sym::kcfi) => codegen_fn_attrs.no_sanitize |= SanitizerSet::KCFI,
358 Some(sym::memory) => {
359 codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMORY
360 }
361 Some(sym::memtag) => {
362 codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMTAG
363 }
364 Some(sym::shadow_call_stack) => {
365 codegen_fn_attrs.no_sanitize |= SanitizerSet::SHADOWCALLSTACK
366 }
367 Some(sym::thread) => {
368 codegen_fn_attrs.no_sanitize |= SanitizerSet::THREAD
369 }
370 Some(sym::hwaddress) => {
371 codegen_fn_attrs.no_sanitize |= SanitizerSet::HWADDRESS
372 }
373 _ => {
374 tcx.dcx().emit_err(errors::InvalidNoSanitize { span: item.span() });
375 }
376 }
377 }
378 }
379 }
380 sym::instruction_set => {
381 codegen_fn_attrs.instruction_set =
382 attr.meta_item_list().and_then(|l| match &l[..] {
383 [MetaItemInner::MetaItem(set)] => {
384 let segments =
385 set.path.segments.iter().map(|x| x.ident.name).collect::<Vec<_>>();
386 match segments.as_slice() {
387 [sym::arm, sym::a32 | sym::t32]
388 if !tcx.sess.target.has_thumb_interworking =>
389 {
390 tcx.dcx().emit_err(errors::UnsuportedInstructionSet {
391 span: attr.span(),
392 });
393 None
394 }
395 [sym::arm, sym::a32] => Some(InstructionSetAttr::ArmA32),
396 [sym::arm, sym::t32] => Some(InstructionSetAttr::ArmT32),
397 _ => {
398 tcx.dcx().emit_err(errors::InvalidInstructionSet {
399 span: attr.span(),
400 });
401 None
402 }
403 }
404 }
405 [] => {
406 tcx.dcx().emit_err(errors::BareInstructionSet { span: attr.span() });
407 None
408 }
409 _ => {
410 tcx.dcx()
411 .emit_err(errors::MultipleInstructionSet { span: attr.span() });
412 None
413 }
414 })
415 }
416 sym::patchable_function_entry => {
417 codegen_fn_attrs.patchable_function_entry = attr.meta_item_list().and_then(|l| {
418 let mut prefix = None;
419 let mut entry = None;
420 for item in l {
421 let Some(meta_item) = item.meta_item() else {
422 tcx.dcx().emit_err(errors::ExpectedNameValuePair { span: item.span() });
423 continue;
424 };
425
426 let Some(name_value_lit) = meta_item.name_value_literal() else {
427 tcx.dcx().emit_err(errors::ExpectedNameValuePair { span: item.span() });
428 continue;
429 };
430
431 let attrib_to_write = match meta_item.name() {
432 Some(sym::prefix_nops) => &mut prefix,
433 Some(sym::entry_nops) => &mut entry,
434 _ => {
435 tcx.dcx().emit_err(errors::UnexpectedParameterName {
436 span: item.span(),
437 prefix_nops: sym::prefix_nops,
438 entry_nops: sym::entry_nops,
439 });
440 continue;
441 }
442 };
443
444 let rustc_ast::LitKind::Int(val, _) = name_value_lit.kind else {
445 tcx.dcx().emit_err(errors::InvalidLiteralValue {
446 span: name_value_lit.span,
447 });
448 continue;
449 };
450
451 let Ok(val) = val.get().try_into() else {
452 tcx.dcx()
453 .emit_err(errors::OutOfRangeInteger { span: name_value_lit.span });
454 continue;
455 };
456
457 *attrib_to_write = Some(val);
458 }
459
460 if let (None, None) = (prefix, entry) {
461 tcx.dcx().span_err(attr.span(), "must specify at least one parameter");
462 }
463
464 Some(PatchableFunctionEntry::from_prefix_and_entry(
465 prefix.unwrap_or(0),
466 entry.unwrap_or(0),
467 ))
468 })
469 }
470 _ => {}
471 }
472 }
473
474 mixed_export_name_no_mangle_lint_state.lint_if_mixed(tcx);
475
476 codegen_fn_attrs.inline = attrs.iter().fold(InlineAttr::None, |ia, attr| {
477 if !attr.has_name(sym::inline) {
478 return ia;
479 }
480
481 if attr.is_word() {
482 return InlineAttr::Hint;
483 }
484 let Some(ref items) = attr.meta_item_list() else {
485 return ia;
486 };
487 inline_span = Some(attr.span());
488
489 let [item] = &items[..] else {
490 tcx.dcx().emit_err(errors::ExpectedOneArgument { span: attr.span() });
491 return InlineAttr::None;
492 };
493
494 if item.has_name(sym::always) {
495 InlineAttr::Always
496 } else if item.has_name(sym::never) {
497 InlineAttr::Never
498 } else {
499 tcx.dcx().emit_err(errors::InvalidArgument { span: items[0].span() });
500
501 InlineAttr::None
502 }
503 });
504 codegen_fn_attrs.inline = attrs.iter().fold(codegen_fn_attrs.inline, |ia, attr| {
505 if !attr.has_name(sym::rustc_force_inline) || !tcx.features().rustc_attrs() {
506 return ia;
507 }
508
509 if attr.is_word() {
510 InlineAttr::Force { attr_span: attr.span(), reason: None }
511 } else if let Some(val) = attr.value_str() {
512 InlineAttr::Force { attr_span: attr.span(), reason: Some(val) }
513 } else {
514 debug!("`rustc_force_inline` not checked by attribute validation");
515 ia
516 }
517 });
518
519 if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) {
523 codegen_fn_attrs.inline = InlineAttr::Never;
524 }
525
526 codegen_fn_attrs.optimize = attrs.iter().fold(OptimizeAttr::Default, |ia, attr| {
527 if !attr.has_name(sym::optimize) {
528 return ia;
529 }
530 if attr.is_word() {
531 tcx.dcx().emit_err(errors::ExpectedOneArgumentOptimize { span: attr.span() });
532 return ia;
533 }
534 let Some(ref items) = attr.meta_item_list() else {
535 return OptimizeAttr::Default;
536 };
537
538 inline_span = Some(attr.span());
539 let [item] = &items[..] else {
540 tcx.dcx().emit_err(errors::ExpectedOneArgumentOptimize { span: attr.span() });
541 return OptimizeAttr::Default;
542 };
543 if item.has_name(sym::size) {
544 OptimizeAttr::Size
545 } else if item.has_name(sym::speed) {
546 OptimizeAttr::Speed
547 } else if item.has_name(sym::none) {
548 OptimizeAttr::DoNotOptimize
549 } else {
550 tcx.dcx().emit_err(errors::InvalidArgumentOptimize { span: item.span() });
551 OptimizeAttr::Default
552 }
553 });
554
555 if tcx.is_closure_like(did.to_def_id()) && codegen_fn_attrs.inline != InlineAttr::Always {
569 let owner_id = tcx.parent(did.to_def_id());
570 if tcx.def_kind(owner_id).has_codegen_attrs() {
571 codegen_fn_attrs
572 .target_features
573 .extend(tcx.codegen_fn_attrs(owner_id).target_features.iter().copied());
574 }
575 }
576
577 if !codegen_fn_attrs.target_features.is_empty()
591 && matches!(codegen_fn_attrs.inline, InlineAttr::Always)
592 && let Some(span) = inline_span
593 {
594 tcx.dcx().span_err(span, "cannot use `#[inline(always)]` with `#[target_feature]`");
595 }
596
597 if !codegen_fn_attrs.no_sanitize.is_empty()
598 && codegen_fn_attrs.inline.always()
599 && let (Some(no_sanitize_span), Some(inline_span)) = (no_sanitize_span, inline_span)
600 {
601 let hir_id = tcx.local_def_id_to_hir_id(did);
602 tcx.node_span_lint(lint::builtin::INLINE_NO_SANITIZE, hir_id, no_sanitize_span, |lint| {
603 lint.primary_message("`no_sanitize` will have no effect after inlining");
604 lint.span_note(inline_span, "inlining requested here");
605 })
606 }
607
608 if let Some((name, _)) = lang_items::extract(attrs)
614 && let Some(lang_item) = LangItem::from_name(name)
615 {
616 if WEAK_LANG_ITEMS.contains(&lang_item) {
617 codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL;
618 }
619 if let Some(link_name) = lang_item.link_name() {
620 codegen_fn_attrs.export_name = Some(link_name);
621 codegen_fn_attrs.link_name = Some(link_name);
622 }
623 }
624 check_link_name_xor_ordinal(tcx, &codegen_fn_attrs, link_ordinal_span);
625
626 if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL)
627 && codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NO_MANGLE)
628 {
629 let lang_item =
630 lang_items::extract(attrs).map_or(None, |(name, _span)| LangItem::from_name(name));
631 let mut err = tcx
632 .dcx()
633 .struct_span_err(
634 no_mangle_span.unwrap_or_default(),
635 "`#[no_mangle]` cannot be used on internal language items",
636 )
637 .with_note("Rustc requires this item to have a specific mangled name.")
638 .with_span_label(tcx.def_span(did), "should be the internal language item");
639 if let Some(lang_item) = lang_item {
640 if let Some(link_name) = lang_item.link_name() {
641 err = err
642 .with_note("If you are trying to prevent mangling to ease debugging, many")
643 .with_note(format!(
644 "debuggers support a command such as `rbreak {link_name}` to"
645 ))
646 .with_note(format!(
647 "match `.*{link_name}.*` instead of `break {link_name}` on a specific name"
648 ))
649 }
650 }
651 err.emit();
652 }
653
654 if let Some(name) = &codegen_fn_attrs.link_name
658 && name.as_str().starts_with("llvm.")
659 {
660 codegen_fn_attrs.flags |= CodegenFnAttrFlags::NEVER_UNWIND;
661 }
662
663 if let Some(features) = check_tied_features(
664 tcx.sess,
665 &codegen_fn_attrs
666 .target_features
667 .iter()
668 .map(|features| (features.name.as_str(), true))
669 .collect(),
670 ) {
671 let span = tcx
672 .get_attrs(did, sym::target_feature)
673 .next()
674 .map_or_else(|| tcx.def_span(did), |a| a.span());
675 tcx.dcx()
676 .create_err(errors::TargetFeatureDisableOrEnable {
677 features,
678 span: Some(span),
679 missing_features: Some(errors::MissingFeatures),
680 })
681 .emit();
682 }
683
684 codegen_fn_attrs
685}
686
687pub fn check_tied_features(
690 sess: &Session,
691 features: &FxHashMap<&str, bool>,
692) -> Option<&'static [&'static str]> {
693 if !features.is_empty() {
694 for tied in sess.target.tied_target_features() {
695 let mut tied_iter = tied.iter();
697 let enabled = features.get(tied_iter.next().unwrap());
698 if tied_iter.any(|f| enabled != features.get(f)) {
699 return Some(tied);
700 }
701 }
702 }
703 None
704}
705
706fn should_inherit_track_caller(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
709 if let Some(impl_item) = tcx.opt_associated_item(def_id)
710 && let ty::AssocItemContainer::Impl = impl_item.container
711 && let Some(trait_item) = impl_item.trait_item_def_id
712 {
713 return tcx.codegen_fn_attrs(trait_item).flags.intersects(CodegenFnAttrFlags::TRACK_CALLER);
714 }
715
716 false
717}
718
719fn check_link_ordinal(tcx: TyCtxt<'_>, attr: &hir::Attribute) -> Option<u16> {
720 use rustc_ast::{LitIntType, LitKind, MetaItemLit};
721 let meta_item_list = attr.meta_item_list()?;
722 let [sole_meta_list] = &meta_item_list[..] else {
723 tcx.dcx().emit_err(errors::InvalidLinkOrdinalNargs { span: attr.span() });
724 return None;
725 };
726 if let Some(MetaItemLit { kind: LitKind::Int(ordinal, LitIntType::Unsuffixed), .. }) =
727 sole_meta_list.lit()
728 {
729 if *ordinal <= u16::MAX as u128 {
743 Some(ordinal.get() as u16)
744 } else {
745 let msg = format!("ordinal value in `link_ordinal` is too large: `{ordinal}`");
746 tcx.dcx()
747 .struct_span_err(attr.span(), msg)
748 .with_note("the value may not exceed `u16::MAX`")
749 .emit();
750 None
751 }
752 } else {
753 tcx.dcx().emit_err(errors::InvalidLinkOrdinalFormat { span: attr.span() });
754 None
755 }
756}
757
758fn check_link_name_xor_ordinal(
759 tcx: TyCtxt<'_>,
760 codegen_fn_attrs: &CodegenFnAttrs,
761 inline_span: Option<Span>,
762) {
763 if codegen_fn_attrs.link_name.is_none() || codegen_fn_attrs.link_ordinal.is_none() {
764 return;
765 }
766 let msg = "cannot use `#[link_name]` with `#[link_ordinal]`";
767 if let Some(span) = inline_span {
768 tcx.dcx().span_err(span, msg);
769 } else {
770 tcx.dcx().err(msg);
771 }
772}
773
774#[derive(Default)]
775struct MixedExportNameAndNoMangleState<'a> {
776 export_name: Option<Span>,
777 hir_id: Option<HirId>,
778 no_mangle: Option<Span>,
779 no_mangle_attr: Option<&'a hir::Attribute>,
780}
781
782impl<'a> MixedExportNameAndNoMangleState<'a> {
783 fn track_export_name(&mut self, span: Span) {
784 self.export_name = Some(span);
785 }
786
787 fn track_no_mangle(&mut self, span: Span, hir_id: HirId, attr_name: &'a hir::Attribute) {
788 self.no_mangle = Some(span);
789 self.hir_id = Some(hir_id);
790 self.no_mangle_attr = Some(attr_name);
791 }
792
793 fn lint_if_mixed(self, tcx: TyCtxt<'_>) {
795 if let Self {
796 export_name: Some(export_name),
797 no_mangle: Some(no_mangle),
798 hir_id: Some(hir_id),
799 no_mangle_attr: Some(_),
800 } = self
801 {
802 tcx.emit_node_span_lint(
803 lint::builtin::UNUSED_ATTRIBUTES,
804 hir_id,
805 no_mangle,
806 errors::MixedExportNameAndNoMangle {
807 no_mangle,
808 no_mangle_attr: "#[unsafe(no_mangle)]".to_string(),
809 export_name,
810 removal_span: no_mangle,
811 },
812 );
813 }
814 }
815}
816
817fn autodiff_attrs(tcx: TyCtxt<'_>, id: DefId) -> Option<AutoDiffAttrs> {
823 let attrs = tcx.get_attrs(id, sym::rustc_autodiff);
824
825 let attrs = attrs.filter(|attr| attr.has_name(sym::rustc_autodiff)).collect::<Vec<_>>();
826
827 let attr = match &attrs[..] {
830 [] => return None,
831 [attr] => attr,
832 _ => {
833 span_bug!(attrs[1].span(), "cg_ssa: rustc_autodiff should only exist once per source");
834 }
835 };
836
837 let list = attr.meta_item_list().unwrap_or_default();
838
839 if list.is_empty() {
841 return Some(AutoDiffAttrs::source());
842 }
843
844 let [mode, width_meta, input_activities @ .., ret_activity] = &list[..] else {
845 span_bug!(attr.span(), "rustc_autodiff attribute must contain mode, width and activities");
846 };
847 let mode = if let MetaItemInner::MetaItem(MetaItem { path: p1, .. }) = mode {
848 p1.segments.first().unwrap().ident
849 } else {
850 span_bug!(attr.span(), "rustc_autodiff attribute must contain mode");
851 };
852
853 let mode = match mode.as_str() {
855 "Forward" => DiffMode::Forward,
856 "Reverse" => DiffMode::Reverse,
857 _ => {
858 span_bug!(mode.span, "rustc_autodiff attribute contains invalid mode");
859 }
860 };
861
862 let width: u32 = match width_meta {
863 MetaItemInner::MetaItem(MetaItem { path: p1, .. }) => {
864 let w = p1.segments.first().unwrap().ident;
865 match w.as_str().parse() {
866 Ok(val) => val,
867 Err(_) => {
868 span_bug!(w.span, "rustc_autodiff width should fit u32");
869 }
870 }
871 }
872 MetaItemInner::Lit(lit) => {
873 if let LitKind::Int(val, _) = lit.kind {
874 match val.get().try_into() {
875 Ok(val) => val,
876 Err(_) => {
877 span_bug!(lit.span, "rustc_autodiff width should fit u32");
878 }
879 }
880 } else {
881 span_bug!(lit.span, "rustc_autodiff width should be an integer");
882 }
883 }
884 };
885
886 let ret_symbol = if let MetaItemInner::MetaItem(MetaItem { path: p1, .. }) = ret_activity {
888 p1.segments.first().unwrap().ident
889 } else {
890 span_bug!(attr.span(), "rustc_autodiff attribute must contain the return activity");
891 };
892
893 let Ok(ret_activity) = DiffActivity::from_str(ret_symbol.as_str()) else {
895 span_bug!(ret_symbol.span, "invalid return activity");
896 };
897
898 let mut arg_activities: Vec<DiffActivity> = vec![];
900 for arg in input_activities {
901 let arg_symbol = if let MetaItemInner::MetaItem(MetaItem { path: p2, .. }) = arg {
902 match p2.segments.first() {
903 Some(x) => x.ident,
904 None => {
905 span_bug!(
906 arg.span(),
907 "rustc_autodiff attribute must contain the input activity"
908 );
909 }
910 }
911 } else {
912 span_bug!(arg.span(), "rustc_autodiff attribute must contain the input activity");
913 };
914
915 match DiffActivity::from_str(arg_symbol.as_str()) {
916 Ok(arg_activity) => arg_activities.push(arg_activity),
917 Err(_) => {
918 span_bug!(arg_symbol.span, "invalid input activity");
919 }
920 }
921 }
922
923 Some(AutoDiffAttrs { mode, width, ret_activity, input_activity: arg_activities })
924}
925
926pub(crate) fn provide(providers: &mut Providers) {
927 *providers = Providers { codegen_fn_attrs, should_inherit_track_caller, ..*providers };
928}