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