1use std::str::FromStr;
2
3use rustc_abi::ExternAbi;
4use rustc_ast::expand::autodiff_attrs::{AutoDiffAttrs, DiffActivity, DiffMode};
5use rustc_ast::{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
91 for attr in attrs.iter() {
92 let fn_sig = || {
98 use DefKind::*;
99
100 let def_kind = tcx.def_kind(did);
101 if let Fn | AssocFn | Variant | Ctor(..) = def_kind {
102 Some(tcx.fn_sig(did))
103 } else {
104 tcx.dcx().span_delayed_bug(
105 attr.span(),
106 "this attribute can only be applied to functions",
107 );
108 None
109 }
110 };
111
112 if let hir::Attribute::Parsed(p) = attr {
113 match p {
114 AttributeKind::Repr(reprs) => {
115 codegen_fn_attrs.alignment = reprs
116 .iter()
117 .find_map(|(r, _)| if let ReprAlign(x) = r { Some(*x) } else { None });
118 }
119
120 _ => {}
121 }
122 }
123
124 let Some(Ident { name, .. }) = attr.ident() else {
125 continue;
126 };
127
128 match name {
129 sym::cold => codegen_fn_attrs.flags |= CodegenFnAttrFlags::COLD,
130 sym::rustc_allocator => codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR,
131 sym::ffi_pure => codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_PURE,
132 sym::ffi_const => codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_CONST,
133 sym::rustc_nounwind => codegen_fn_attrs.flags |= CodegenFnAttrFlags::NEVER_UNWIND,
134 sym::rustc_reallocator => codegen_fn_attrs.flags |= CodegenFnAttrFlags::REALLOCATOR,
135 sym::rustc_deallocator => codegen_fn_attrs.flags |= CodegenFnAttrFlags::DEALLOCATOR,
136 sym::rustc_allocator_zeroed => {
137 codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR_ZEROED
138 }
139 sym::naked => codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED,
140 sym::no_mangle => {
141 if tcx.opt_item_name(did.to_def_id()).is_some() {
142 codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE;
143 mixed_export_name_no_mangle_lint_state.track_no_mangle(
144 attr.span(),
145 tcx.local_def_id_to_hir_id(did),
146 attr,
147 );
148 } else {
149 tcx.dcx()
150 .struct_span_err(
151 attr.span(),
152 format!(
153 "`#[no_mangle]` cannot be used on {} {} as it has no name",
154 tcx.def_descr_article(did.to_def_id()),
155 tcx.def_descr(did.to_def_id()),
156 ),
157 )
158 .emit();
159 }
160 }
161 sym::rustc_std_internal_symbol => {
162 codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL
163 }
164 sym::used => {
165 let inner = attr.meta_item_list();
166 match inner.as_deref() {
167 Some([item]) if item.has_name(sym::linker) => {
168 if !tcx.features().used_with_arg() {
169 feature_err(
170 &tcx.sess,
171 sym::used_with_arg,
172 attr.span(),
173 "`#[used(linker)]` is currently unstable",
174 )
175 .emit();
176 }
177 codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED_LINKER;
178 }
179 Some([item]) if item.has_name(sym::compiler) => {
180 if !tcx.features().used_with_arg() {
181 feature_err(
182 &tcx.sess,
183 sym::used_with_arg,
184 attr.span(),
185 "`#[used(compiler)]` is currently unstable",
186 )
187 .emit();
188 }
189 codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED;
190 }
191 Some(_) => {
192 tcx.dcx().emit_err(errors::ExpectedUsedSymbol { span: attr.span() });
193 }
194 None => {
195 let is_like_elf = !(tcx.sess.target.is_like_osx
217 || tcx.sess.target.is_like_windows
218 || tcx.sess.target.is_like_wasm);
219 codegen_fn_attrs.flags |= if is_like_elf {
220 CodegenFnAttrFlags::USED
221 } else {
222 CodegenFnAttrFlags::USED_LINKER
223 };
224 }
225 }
226 }
227 sym::thread_local => codegen_fn_attrs.flags |= CodegenFnAttrFlags::THREAD_LOCAL,
228 sym::track_caller => {
229 let is_closure = tcx.is_closure_like(did.to_def_id());
230
231 if !is_closure
232 && let Some(fn_sig) = fn_sig()
233 && fn_sig.skip_binder().abi() != ExternAbi::Rust
234 {
235 tcx.dcx().emit_err(errors::RequiresRustAbi { span: attr.span() });
236 }
237 if is_closure
238 && !tcx.features().closure_track_caller()
239 && !attr.span().allows_unstable(sym::closure_track_caller)
240 {
241 feature_err(
242 &tcx.sess,
243 sym::closure_track_caller,
244 attr.span(),
245 "`#[track_caller]` on closures is currently unstable",
246 )
247 .emit();
248 }
249 codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER
250 }
251 sym::export_name => {
252 if let Some(s) = attr.value_str() {
253 if s.as_str().contains('\0') {
254 tcx.dcx().emit_err(errors::NullOnExport { span: attr.span() });
257 }
258 codegen_fn_attrs.export_name = Some(s);
259 mixed_export_name_no_mangle_lint_state.track_export_name(attr.span());
260 }
261 }
262 sym::target_feature => {
263 let Some(sig) = tcx.hir_node_by_def_id(did).fn_sig() else {
264 tcx.dcx().span_delayed_bug(attr.span(), "target_feature applied to non-fn");
265 continue;
266 };
267 let safe_target_features =
268 matches!(sig.header.safety, hir::HeaderSafety::SafeTargetFeatures);
269 codegen_fn_attrs.safe_target_features = safe_target_features;
270 if safe_target_features {
271 if tcx.sess.target.is_like_wasm || tcx.sess.opts.actually_rustdoc {
272 } else {
294 check_target_feature_trait_unsafe(tcx, did, attr.span());
295 }
296 }
297 from_target_feature_attr(
298 tcx,
299 attr,
300 rust_target_features,
301 &mut codegen_fn_attrs.target_features,
302 );
303 }
304 sym::linkage => {
305 if let Some(val) = attr.value_str() {
306 let linkage = Some(linkage_by_name(tcx, did, val.as_str()));
307 if tcx.is_foreign_item(did) {
308 codegen_fn_attrs.import_linkage = linkage;
309
310 if tcx.is_mutable_static(did.into()) {
311 let mut diag = tcx.dcx().struct_span_err(
312 attr.span(),
313 "extern mutable statics are not allowed with `#[linkage]`",
314 );
315 diag.note(
316 "marking the extern static mutable would allow changing which \
317 symbol the static references rather than make the target of the \
318 symbol mutable",
319 );
320 diag.emit();
321 }
322 } else {
323 codegen_fn_attrs.linkage = linkage;
324 }
325 }
326 }
327 sym::link_section => {
328 if let Some(val) = attr.value_str() {
329 if val.as_str().bytes().any(|b| b == 0) {
330 let msg = format!("illegal null byte in link_section value: `{val}`");
331 tcx.dcx().span_err(attr.span(), msg);
332 } else {
333 codegen_fn_attrs.link_section = Some(val);
334 }
335 }
336 }
337 sym::link_name => codegen_fn_attrs.link_name = attr.value_str(),
338 sym::link_ordinal => {
339 link_ordinal_span = Some(attr.span());
340 if let ordinal @ Some(_) = check_link_ordinal(tcx, attr) {
341 codegen_fn_attrs.link_ordinal = ordinal;
342 }
343 }
344 sym::no_sanitize => {
345 no_sanitize_span = Some(attr.span());
346 if let Some(list) = attr.meta_item_list() {
347 for item in list.iter() {
348 match item.name_or_empty() {
349 sym::address => {
350 codegen_fn_attrs.no_sanitize |=
351 SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS
352 }
353 sym::cfi => codegen_fn_attrs.no_sanitize |= SanitizerSet::CFI,
354 sym::kcfi => codegen_fn_attrs.no_sanitize |= SanitizerSet::KCFI,
355 sym::memory => codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMORY,
356 sym::memtag => codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMTAG,
357 sym::shadow_call_stack => {
358 codegen_fn_attrs.no_sanitize |= SanitizerSet::SHADOWCALLSTACK
359 }
360 sym::thread => codegen_fn_attrs.no_sanitize |= SanitizerSet::THREAD,
361 sym::hwaddress => {
362 codegen_fn_attrs.no_sanitize |= SanitizerSet::HWADDRESS
363 }
364 _ => {
365 tcx.dcx().emit_err(errors::InvalidNoSanitize { span: item.span() });
366 }
367 }
368 }
369 }
370 }
371 sym::instruction_set => {
372 codegen_fn_attrs.instruction_set =
373 attr.meta_item_list().and_then(|l| match &l[..] {
374 [MetaItemInner::MetaItem(set)] => {
375 let segments =
376 set.path.segments.iter().map(|x| x.ident.name).collect::<Vec<_>>();
377 match segments.as_slice() {
378 [sym::arm, sym::a32 | sym::t32]
379 if !tcx.sess.target.has_thumb_interworking =>
380 {
381 tcx.dcx().emit_err(errors::UnsuportedInstructionSet {
382 span: attr.span(),
383 });
384 None
385 }
386 [sym::arm, sym::a32] => Some(InstructionSetAttr::ArmA32),
387 [sym::arm, sym::t32] => Some(InstructionSetAttr::ArmT32),
388 _ => {
389 tcx.dcx().emit_err(errors::InvalidInstructionSet {
390 span: attr.span(),
391 });
392 None
393 }
394 }
395 }
396 [] => {
397 tcx.dcx().emit_err(errors::BareInstructionSet { span: attr.span() });
398 None
399 }
400 _ => {
401 tcx.dcx()
402 .emit_err(errors::MultipleInstructionSet { span: attr.span() });
403 None
404 }
405 })
406 }
407 sym::patchable_function_entry => {
408 codegen_fn_attrs.patchable_function_entry = attr.meta_item_list().and_then(|l| {
409 let mut prefix = None;
410 let mut entry = None;
411 for item in l {
412 let Some(meta_item) = item.meta_item() else {
413 tcx.dcx().emit_err(errors::ExpectedNameValuePair { span: item.span() });
414 continue;
415 };
416
417 let Some(name_value_lit) = meta_item.name_value_literal() else {
418 tcx.dcx().emit_err(errors::ExpectedNameValuePair { span: item.span() });
419 continue;
420 };
421
422 let attrib_to_write = match meta_item.name_or_empty() {
423 sym::prefix_nops => &mut prefix,
424 sym::entry_nops => &mut entry,
425 _ => {
426 tcx.dcx().emit_err(errors::UnexpectedParameterName {
427 span: item.span(),
428 prefix_nops: sym::prefix_nops,
429 entry_nops: sym::entry_nops,
430 });
431 continue;
432 }
433 };
434
435 let rustc_ast::LitKind::Int(val, _) = name_value_lit.kind else {
436 tcx.dcx().emit_err(errors::InvalidLiteralValue {
437 span: name_value_lit.span,
438 });
439 continue;
440 };
441
442 let Ok(val) = val.get().try_into() else {
443 tcx.dcx()
444 .emit_err(errors::OutOfRangeInteger { span: name_value_lit.span });
445 continue;
446 };
447
448 *attrib_to_write = Some(val);
449 }
450
451 if let (None, None) = (prefix, entry) {
452 tcx.dcx().span_err(attr.span(), "must specify at least one parameter");
453 }
454
455 Some(PatchableFunctionEntry::from_prefix_and_entry(
456 prefix.unwrap_or(0),
457 entry.unwrap_or(0),
458 ))
459 })
460 }
461 _ => {}
462 }
463 }
464
465 mixed_export_name_no_mangle_lint_state.lint_if_mixed(tcx);
466
467 codegen_fn_attrs.inline = attrs.iter().fold(InlineAttr::None, |ia, attr| {
468 if !attr.has_name(sym::inline) {
469 return ia;
470 }
471
472 if attr.is_word() {
473 return InlineAttr::Hint;
474 }
475 let Some(ref items) = attr.meta_item_list() else {
476 return ia;
477 };
478 inline_span = Some(attr.span());
479
480 let [item] = &items[..] else {
481 tcx.dcx().emit_err(errors::ExpectedOneArgument { span: attr.span() });
482 return InlineAttr::None;
483 };
484
485 if item.has_name(sym::always) {
486 InlineAttr::Always
487 } else if item.has_name(sym::never) {
488 InlineAttr::Never
489 } else {
490 tcx.dcx().emit_err(errors::InvalidArgument { span: items[0].span() });
491
492 InlineAttr::None
493 }
494 });
495 codegen_fn_attrs.inline = attrs.iter().fold(codegen_fn_attrs.inline, |ia, attr| {
496 if !attr.has_name(sym::rustc_force_inline) || !tcx.features().rustc_attrs() {
497 return ia;
498 }
499
500 if attr.is_word() {
501 InlineAttr::Force { attr_span: attr.span(), reason: None }
502 } else if let Some(val) = attr.value_str() {
503 InlineAttr::Force { attr_span: attr.span(), reason: Some(val) }
504 } else {
505 debug!("`rustc_force_inline` not checked by attribute validation");
506 ia
507 }
508 });
509
510 if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) {
514 codegen_fn_attrs.inline = InlineAttr::Never;
515 }
516
517 codegen_fn_attrs.optimize = attrs.iter().fold(OptimizeAttr::Default, |ia, attr| {
518 if !attr.has_name(sym::optimize) {
519 return ia;
520 }
521 if attr.is_word() {
522 tcx.dcx().emit_err(errors::ExpectedOneArgumentOptimize { span: attr.span() });
523 return ia;
524 }
525 let Some(ref items) = attr.meta_item_list() else {
526 return OptimizeAttr::Default;
527 };
528
529 inline_span = Some(attr.span());
530 let [item] = &items[..] else {
531 tcx.dcx().emit_err(errors::ExpectedOneArgumentOptimize { span: attr.span() });
532 return OptimizeAttr::Default;
533 };
534 if item.has_name(sym::size) {
535 OptimizeAttr::Size
536 } else if item.has_name(sym::speed) {
537 OptimizeAttr::Speed
538 } else if item.has_name(sym::none) {
539 OptimizeAttr::DoNotOptimize
540 } else {
541 tcx.dcx().emit_err(errors::InvalidArgumentOptimize { span: item.span() });
542 OptimizeAttr::Default
543 }
544 });
545
546 if tcx.is_closure_like(did.to_def_id()) && codegen_fn_attrs.inline != InlineAttr::Always {
560 let owner_id = tcx.parent(did.to_def_id());
561 if tcx.def_kind(owner_id).has_codegen_attrs() {
562 codegen_fn_attrs
563 .target_features
564 .extend(tcx.codegen_fn_attrs(owner_id).target_features.iter().copied());
565 }
566 }
567
568 if !codegen_fn_attrs.target_features.is_empty()
582 && matches!(codegen_fn_attrs.inline, InlineAttr::Always)
583 && let Some(span) = inline_span
584 {
585 tcx.dcx().span_err(span, "cannot use `#[inline(always)]` with `#[target_feature]`");
586 }
587
588 if !codegen_fn_attrs.no_sanitize.is_empty()
589 && codegen_fn_attrs.inline.always()
590 && let (Some(no_sanitize_span), Some(inline_span)) = (no_sanitize_span, inline_span)
591 {
592 let hir_id = tcx.local_def_id_to_hir_id(did);
593 tcx.node_span_lint(lint::builtin::INLINE_NO_SANITIZE, hir_id, no_sanitize_span, |lint| {
594 lint.primary_message("`no_sanitize` will have no effect after inlining");
595 lint.span_note(inline_span, "inlining requested here");
596 })
597 }
598
599 if let Some((name, _)) = lang_items::extract(attrs)
605 && let Some(lang_item) = LangItem::from_name(name)
606 {
607 if WEAK_LANG_ITEMS.contains(&lang_item) {
608 codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL;
609 }
610 if let Some(link_name) = lang_item.link_name() {
611 codegen_fn_attrs.export_name = Some(link_name);
612 codegen_fn_attrs.link_name = Some(link_name);
613 }
614 }
615 check_link_name_xor_ordinal(tcx, &codegen_fn_attrs, link_ordinal_span);
616
617 if let Some(name) = &codegen_fn_attrs.link_name
621 && name.as_str().starts_with("llvm.")
622 {
623 codegen_fn_attrs.flags |= CodegenFnAttrFlags::NEVER_UNWIND;
624 }
625
626 if let Some(features) = check_tied_features(
627 tcx.sess,
628 &codegen_fn_attrs
629 .target_features
630 .iter()
631 .map(|features| (features.name.as_str(), true))
632 .collect(),
633 ) {
634 let span = tcx
635 .get_attrs(did, sym::target_feature)
636 .next()
637 .map_or_else(|| tcx.def_span(did), |a| a.span());
638 tcx.dcx()
639 .create_err(errors::TargetFeatureDisableOrEnable {
640 features,
641 span: Some(span),
642 missing_features: Some(errors::MissingFeatures),
643 })
644 .emit();
645 }
646
647 codegen_fn_attrs
648}
649
650pub fn check_tied_features(
653 sess: &Session,
654 features: &FxHashMap<&str, bool>,
655) -> Option<&'static [&'static str]> {
656 if !features.is_empty() {
657 for tied in sess.target.tied_target_features() {
658 let mut tied_iter = tied.iter();
660 let enabled = features.get(tied_iter.next().unwrap());
661 if tied_iter.any(|f| enabled != features.get(f)) {
662 return Some(tied);
663 }
664 }
665 }
666 None
667}
668
669fn should_inherit_track_caller(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
672 if let Some(impl_item) = tcx.opt_associated_item(def_id)
673 && let ty::AssocItemContainer::Impl = impl_item.container
674 && let Some(trait_item) = impl_item.trait_item_def_id
675 {
676 return tcx.codegen_fn_attrs(trait_item).flags.intersects(CodegenFnAttrFlags::TRACK_CALLER);
677 }
678
679 false
680}
681
682fn check_link_ordinal(tcx: TyCtxt<'_>, attr: &hir::Attribute) -> Option<u16> {
683 use rustc_ast::{LitIntType, LitKind, MetaItemLit};
684 let meta_item_list = attr.meta_item_list()?;
685 let [sole_meta_list] = &meta_item_list[..] else {
686 tcx.dcx().emit_err(errors::InvalidLinkOrdinalNargs { span: attr.span() });
687 return None;
688 };
689 if let Some(MetaItemLit { kind: LitKind::Int(ordinal, LitIntType::Unsuffixed), .. }) =
690 sole_meta_list.lit()
691 {
692 if *ordinal <= u16::MAX as u128 {
706 Some(ordinal.get() as u16)
707 } else {
708 let msg = format!("ordinal value in `link_ordinal` is too large: `{ordinal}`");
709 tcx.dcx()
710 .struct_span_err(attr.span(), msg)
711 .with_note("the value may not exceed `u16::MAX`")
712 .emit();
713 None
714 }
715 } else {
716 tcx.dcx().emit_err(errors::InvalidLinkOrdinalFormat { span: attr.span() });
717 None
718 }
719}
720
721fn check_link_name_xor_ordinal(
722 tcx: TyCtxt<'_>,
723 codegen_fn_attrs: &CodegenFnAttrs,
724 inline_span: Option<Span>,
725) {
726 if codegen_fn_attrs.link_name.is_none() || codegen_fn_attrs.link_ordinal.is_none() {
727 return;
728 }
729 let msg = "cannot use `#[link_name]` with `#[link_ordinal]`";
730 if let Some(span) = inline_span {
731 tcx.dcx().span_err(span, msg);
732 } else {
733 tcx.dcx().err(msg);
734 }
735}
736
737#[derive(Default)]
738struct MixedExportNameAndNoMangleState<'a> {
739 export_name: Option<Span>,
740 hir_id: Option<HirId>,
741 no_mangle: Option<Span>,
742 no_mangle_attr: Option<&'a hir::Attribute>,
743}
744
745impl<'a> MixedExportNameAndNoMangleState<'a> {
746 fn track_export_name(&mut self, span: Span) {
747 self.export_name = Some(span);
748 }
749
750 fn track_no_mangle(&mut self, span: Span, hir_id: HirId, attr_name: &'a hir::Attribute) {
751 self.no_mangle = Some(span);
752 self.hir_id = Some(hir_id);
753 self.no_mangle_attr = Some(attr_name);
754 }
755
756 fn lint_if_mixed(self, tcx: TyCtxt<'_>) {
758 if let Self {
759 export_name: Some(export_name),
760 no_mangle: Some(no_mangle),
761 hir_id: Some(hir_id),
762 no_mangle_attr: Some(_),
763 } = self
764 {
765 tcx.emit_node_span_lint(
766 lint::builtin::UNUSED_ATTRIBUTES,
767 hir_id,
768 no_mangle,
769 errors::MixedExportNameAndNoMangle {
770 no_mangle,
771 no_mangle_attr: "#[unsafe(no_mangle)]".to_string(),
772 export_name,
773 removal_span: no_mangle,
774 },
775 );
776 }
777 }
778}
779
780fn autodiff_attrs(tcx: TyCtxt<'_>, id: DefId) -> Option<AutoDiffAttrs> {
786 let attrs = tcx.get_attrs(id, sym::rustc_autodiff);
787
788 let attrs =
789 attrs.filter(|attr| attr.name_or_empty() == sym::rustc_autodiff).collect::<Vec<_>>();
790
791 let attr = match &attrs[..] {
794 [] => return None,
795 [attr] => attr,
796 _ => {
797 span_bug!(attrs[1].span(), "cg_ssa: rustc_autodiff should only exist once per source");
798 }
799 };
800
801 let list = attr.meta_item_list().unwrap_or_default();
802
803 if list.is_empty() {
805 return Some(AutoDiffAttrs::source());
806 }
807
808 let [mode, input_activities @ .., ret_activity] = &list[..] else {
809 span_bug!(attr.span(), "rustc_autodiff attribute must contain mode and activities");
810 };
811 let mode = if let MetaItemInner::MetaItem(MetaItem { path: p1, .. }) = mode {
812 p1.segments.first().unwrap().ident
813 } else {
814 span_bug!(attr.span(), "rustc_autodiff attribute must contain mode");
815 };
816
817 let mode = match mode.as_str() {
819 "Forward" => DiffMode::Forward,
820 "Reverse" => DiffMode::Reverse,
821 _ => {
822 span_bug!(mode.span, "rustc_autodiff attribute contains invalid mode");
823 }
824 };
825
826 let ret_symbol = if let MetaItemInner::MetaItem(MetaItem { path: p1, .. }) = ret_activity {
828 p1.segments.first().unwrap().ident
829 } else {
830 span_bug!(attr.span(), "rustc_autodiff attribute must contain the return activity");
831 };
832
833 let Ok(ret_activity) = DiffActivity::from_str(ret_symbol.as_str()) else {
835 span_bug!(ret_symbol.span, "invalid return activity");
836 };
837
838 let mut arg_activities: Vec<DiffActivity> = vec![];
840 for arg in input_activities {
841 let arg_symbol = if let MetaItemInner::MetaItem(MetaItem { path: p2, .. }) = arg {
842 match p2.segments.first() {
843 Some(x) => x.ident,
844 None => {
845 span_bug!(
846 arg.span(),
847 "rustc_autodiff attribute must contain the input activity"
848 );
849 }
850 }
851 } else {
852 span_bug!(arg.span(), "rustc_autodiff attribute must contain the input activity");
853 };
854
855 match DiffActivity::from_str(arg_symbol.as_str()) {
856 Ok(arg_activity) => arg_activities.push(arg_activity),
857 Err(_) => {
858 span_bug!(arg_symbol.span, "invalid input activity");
859 }
860 }
861 }
862
863 Some(AutoDiffAttrs { mode, ret_activity, input_activity: arg_activities })
864}
865
866pub(crate) fn provide(providers: &mut Providers) {
867 *providers = Providers { codegen_fn_attrs, should_inherit_track_caller, ..*providers };
868}