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_data_structures::ReprAttr::ReprAlign;
7use rustc_attr_data_structures::{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_COMPILER;
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_COMPILER
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 did,
303 attr,
304 rust_target_features,
305 &mut codegen_fn_attrs.target_features,
306 );
307 }
308 sym::linkage => {
309 if let Some(val) = attr.value_str() {
310 let linkage = Some(linkage_by_name(tcx, did, val.as_str()));
311 if tcx.is_foreign_item(did) {
312 codegen_fn_attrs.import_linkage = linkage;
313
314 if tcx.is_mutable_static(did.into()) {
315 let mut diag = tcx.dcx().struct_span_err(
316 attr.span(),
317 "extern mutable statics are not allowed with `#[linkage]`",
318 );
319 diag.note(
320 "marking the extern static mutable would allow changing which \
321 symbol the static references rather than make the target of the \
322 symbol mutable",
323 );
324 diag.emit();
325 }
326 } else {
327 codegen_fn_attrs.linkage = linkage;
328 }
329 }
330 }
331 sym::link_section => {
332 if let Some(val) = attr.value_str() {
333 if val.as_str().bytes().any(|b| b == 0) {
334 let msg = format!("illegal null byte in link_section value: `{val}`");
335 tcx.dcx().span_err(attr.span(), msg);
336 } else {
337 codegen_fn_attrs.link_section = Some(val);
338 }
339 }
340 }
341 sym::link_name => codegen_fn_attrs.link_name = attr.value_str(),
342 sym::link_ordinal => {
343 link_ordinal_span = Some(attr.span());
344 if let ordinal @ Some(_) = check_link_ordinal(tcx, attr) {
345 codegen_fn_attrs.link_ordinal = ordinal;
346 }
347 }
348 sym::no_sanitize => {
349 no_sanitize_span = Some(attr.span());
350 if let Some(list) = attr.meta_item_list() {
351 for item in list.iter() {
352 match item.name() {
353 Some(sym::address) => {
354 codegen_fn_attrs.no_sanitize |=
355 SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS
356 }
357 Some(sym::cfi) => codegen_fn_attrs.no_sanitize |= SanitizerSet::CFI,
358 Some(sym::kcfi) => codegen_fn_attrs.no_sanitize |= SanitizerSet::KCFI,
359 Some(sym::memory) => {
360 codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMORY
361 }
362 Some(sym::memtag) => {
363 codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMTAG
364 }
365 Some(sym::shadow_call_stack) => {
366 codegen_fn_attrs.no_sanitize |= SanitizerSet::SHADOWCALLSTACK
367 }
368 Some(sym::thread) => {
369 codegen_fn_attrs.no_sanitize |= SanitizerSet::THREAD
370 }
371 Some(sym::hwaddress) => {
372 codegen_fn_attrs.no_sanitize |= SanitizerSet::HWADDRESS
373 }
374 _ => {
375 tcx.dcx().emit_err(errors::InvalidNoSanitize { span: item.span() });
376 }
377 }
378 }
379 }
380 }
381 sym::instruction_set => {
382 codegen_fn_attrs.instruction_set =
383 attr.meta_item_list().and_then(|l| match &l[..] {
384 [MetaItemInner::MetaItem(set)] => {
385 let segments =
386 set.path.segments.iter().map(|x| x.ident.name).collect::<Vec<_>>();
387 match segments.as_slice() {
388 [sym::arm, sym::a32 | sym::t32]
389 if !tcx.sess.target.has_thumb_interworking =>
390 {
391 tcx.dcx().emit_err(errors::UnsupportedInstructionSet {
392 span: attr.span(),
393 });
394 None
395 }
396 [sym::arm, sym::a32] => Some(InstructionSetAttr::ArmA32),
397 [sym::arm, sym::t32] => Some(InstructionSetAttr::ArmT32),
398 _ => {
399 tcx.dcx().emit_err(errors::InvalidInstructionSet {
400 span: attr.span(),
401 });
402 None
403 }
404 }
405 }
406 [] => {
407 tcx.dcx().emit_err(errors::BareInstructionSet { span: attr.span() });
408 None
409 }
410 _ => {
411 tcx.dcx()
412 .emit_err(errors::MultipleInstructionSet { span: attr.span() });
413 None
414 }
415 })
416 }
417 sym::patchable_function_entry => {
418 codegen_fn_attrs.patchable_function_entry = attr.meta_item_list().and_then(|l| {
419 let mut prefix = None;
420 let mut entry = None;
421 for item in l {
422 let Some(meta_item) = item.meta_item() else {
423 tcx.dcx().emit_err(errors::ExpectedNameValuePair { span: item.span() });
424 continue;
425 };
426
427 let Some(name_value_lit) = meta_item.name_value_literal() else {
428 tcx.dcx().emit_err(errors::ExpectedNameValuePair { span: item.span() });
429 continue;
430 };
431
432 let attrib_to_write = match meta_item.name() {
433 Some(sym::prefix_nops) => &mut prefix,
434 Some(sym::entry_nops) => &mut entry,
435 _ => {
436 tcx.dcx().emit_err(errors::UnexpectedParameterName {
437 span: item.span(),
438 prefix_nops: sym::prefix_nops,
439 entry_nops: sym::entry_nops,
440 });
441 continue;
442 }
443 };
444
445 let rustc_ast::LitKind::Int(val, _) = name_value_lit.kind else {
446 tcx.dcx().emit_err(errors::InvalidLiteralValue {
447 span: name_value_lit.span,
448 });
449 continue;
450 };
451
452 let Ok(val) = val.get().try_into() else {
453 tcx.dcx()
454 .emit_err(errors::OutOfRangeInteger { span: name_value_lit.span });
455 continue;
456 };
457
458 *attrib_to_write = Some(val);
459 }
460
461 if let (None, None) = (prefix, entry) {
462 tcx.dcx().span_err(attr.span(), "must specify at least one parameter");
463 }
464
465 Some(PatchableFunctionEntry::from_prefix_and_entry(
466 prefix.unwrap_or(0),
467 entry.unwrap_or(0),
468 ))
469 })
470 }
471 _ => {}
472 }
473 }
474
475 mixed_export_name_no_mangle_lint_state.lint_if_mixed(tcx);
476
477 codegen_fn_attrs.inline = attrs.iter().fold(InlineAttr::None, |ia, attr| {
478 if !attr.has_name(sym::inline) {
479 return ia;
480 }
481
482 if attr.is_word() {
483 return InlineAttr::Hint;
484 }
485 let Some(ref items) = attr.meta_item_list() else {
486 return ia;
487 };
488 inline_span = Some(attr.span());
489
490 let [item] = &items[..] else {
491 tcx.dcx().emit_err(errors::ExpectedOneArgument { span: attr.span() });
492 return InlineAttr::None;
493 };
494
495 if item.has_name(sym::always) {
496 InlineAttr::Always
497 } else if item.has_name(sym::never) {
498 InlineAttr::Never
499 } else {
500 tcx.dcx().emit_err(errors::InvalidArgument { span: items[0].span() });
501
502 InlineAttr::None
503 }
504 });
505 codegen_fn_attrs.inline = attrs.iter().fold(codegen_fn_attrs.inline, |ia, attr| {
506 if !attr.has_name(sym::rustc_force_inline) || !tcx.features().rustc_attrs() {
507 return ia;
508 }
509
510 if attr.is_word() {
511 InlineAttr::Force { attr_span: attr.span(), reason: None }
512 } else if let Some(val) = attr.value_str() {
513 InlineAttr::Force { attr_span: attr.span(), reason: Some(val) }
514 } else {
515 debug!("`rustc_force_inline` not checked by attribute validation");
516 ia
517 }
518 });
519
520 if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) {
524 codegen_fn_attrs.inline = InlineAttr::Never;
525 }
526
527 codegen_fn_attrs.optimize = attrs.iter().fold(OptimizeAttr::Default, |ia, attr| {
528 if !attr.has_name(sym::optimize) {
529 return ia;
530 }
531 if attr.is_word() {
532 tcx.dcx().emit_err(errors::ExpectedOneArgumentOptimize { span: attr.span() });
533 return ia;
534 }
535 let Some(ref items) = attr.meta_item_list() else {
536 return OptimizeAttr::Default;
537 };
538
539 inline_span = Some(attr.span());
540 let [item] = &items[..] else {
541 tcx.dcx().emit_err(errors::ExpectedOneArgumentOptimize { span: attr.span() });
542 return OptimizeAttr::Default;
543 };
544 if item.has_name(sym::size) {
545 OptimizeAttr::Size
546 } else if item.has_name(sym::speed) {
547 OptimizeAttr::Speed
548 } else if item.has_name(sym::none) {
549 OptimizeAttr::DoNotOptimize
550 } else {
551 tcx.dcx().emit_err(errors::InvalidArgumentOptimize { span: item.span() });
552 OptimizeAttr::Default
553 }
554 });
555
556 if tcx.is_closure_like(did.to_def_id()) && codegen_fn_attrs.inline != InlineAttr::Always {
570 let owner_id = tcx.parent(did.to_def_id());
571 if tcx.def_kind(owner_id).has_codegen_attrs() {
572 codegen_fn_attrs
573 .target_features
574 .extend(tcx.codegen_fn_attrs(owner_id).target_features.iter().copied());
575 }
576 }
577
578 if !codegen_fn_attrs.target_features.is_empty()
592 && matches!(codegen_fn_attrs.inline, InlineAttr::Always)
593 && let Some(span) = inline_span
594 {
595 tcx.dcx().span_err(span, "cannot use `#[inline(always)]` with `#[target_feature]`");
596 }
597
598 if !codegen_fn_attrs.no_sanitize.is_empty()
599 && codegen_fn_attrs.inline.always()
600 && let (Some(no_sanitize_span), Some(inline_span)) = (no_sanitize_span, inline_span)
601 {
602 let hir_id = tcx.local_def_id_to_hir_id(did);
603 tcx.node_span_lint(lint::builtin::INLINE_NO_SANITIZE, hir_id, no_sanitize_span, |lint| {
604 lint.primary_message("`no_sanitize` will have no effect after inlining");
605 lint.span_note(inline_span, "inlining requested here");
606 })
607 }
608
609 if let Some((name, _)) = lang_items::extract(attrs)
615 && let Some(lang_item) = LangItem::from_name(name)
616 {
617 if WEAK_LANG_ITEMS.contains(&lang_item) {
618 codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL;
619 }
620 if let Some(link_name) = lang_item.link_name() {
621 codegen_fn_attrs.export_name = Some(link_name);
622 codegen_fn_attrs.link_name = Some(link_name);
623 }
624 }
625 check_link_name_xor_ordinal(tcx, &codegen_fn_attrs, link_ordinal_span);
626
627 if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL)
628 && codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NO_MANGLE)
629 {
630 let lang_item =
631 lang_items::extract(attrs).map_or(None, |(name, _span)| LangItem::from_name(name));
632 let mut err = tcx
633 .dcx()
634 .struct_span_err(
635 no_mangle_span.unwrap_or_default(),
636 "`#[no_mangle]` cannot be used on internal language items",
637 )
638 .with_note("Rustc requires this item to have a specific mangled name.")
639 .with_span_label(tcx.def_span(did), "should be the internal language item");
640 if let Some(lang_item) = lang_item {
641 if let Some(link_name) = lang_item.link_name() {
642 err = err
643 .with_note("If you are trying to prevent mangling to ease debugging, many")
644 .with_note(format!(
645 "debuggers support a command such as `rbreak {link_name}` to"
646 ))
647 .with_note(format!(
648 "match `.*{link_name}.*` instead of `break {link_name}` on a specific name"
649 ))
650 }
651 }
652 err.emit();
653 }
654
655 if let Some(name) = &codegen_fn_attrs.link_name
659 && name.as_str().starts_with("llvm.")
660 {
661 codegen_fn_attrs.flags |= CodegenFnAttrFlags::NEVER_UNWIND;
662 }
663
664 if let Some(features) = check_tied_features(
665 tcx.sess,
666 &codegen_fn_attrs
667 .target_features
668 .iter()
669 .map(|features| (features.name.as_str(), true))
670 .collect(),
671 ) {
672 let span = tcx
673 .get_attrs(did, sym::target_feature)
674 .next()
675 .map_or_else(|| tcx.def_span(did), |a| a.span());
676 tcx.dcx()
677 .create_err(errors::TargetFeatureDisableOrEnable {
678 features,
679 span: Some(span),
680 missing_features: Some(errors::MissingFeatures),
681 })
682 .emit();
683 }
684
685 codegen_fn_attrs
686}
687
688pub fn check_tied_features(
691 sess: &Session,
692 features: &FxHashMap<&str, bool>,
693) -> Option<&'static [&'static str]> {
694 if !features.is_empty() {
695 for tied in sess.target.tied_target_features() {
696 let mut tied_iter = tied.iter();
698 let enabled = features.get(tied_iter.next().unwrap());
699 if tied_iter.any(|f| enabled != features.get(f)) {
700 return Some(tied);
701 }
702 }
703 }
704 None
705}
706
707fn should_inherit_track_caller(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
710 if let Some(impl_item) = tcx.opt_associated_item(def_id)
711 && let ty::AssocItemContainer::Impl = impl_item.container
712 && let Some(trait_item) = impl_item.trait_item_def_id
713 {
714 return tcx.codegen_fn_attrs(trait_item).flags.intersects(CodegenFnAttrFlags::TRACK_CALLER);
715 }
716
717 false
718}
719
720fn check_link_ordinal(tcx: TyCtxt<'_>, attr: &hir::Attribute) -> Option<u16> {
721 use rustc_ast::{LitIntType, LitKind, MetaItemLit};
722 let meta_item_list = attr.meta_item_list()?;
723 let [sole_meta_list] = &meta_item_list[..] else {
724 tcx.dcx().emit_err(errors::InvalidLinkOrdinalNargs { span: attr.span() });
725 return None;
726 };
727 if let Some(MetaItemLit { kind: LitKind::Int(ordinal, LitIntType::Unsuffixed), .. }) =
728 sole_meta_list.lit()
729 {
730 if *ordinal <= u16::MAX as u128 {
744 Some(ordinal.get() as u16)
745 } else {
746 let msg = format!("ordinal value in `link_ordinal` is too large: `{ordinal}`");
747 tcx.dcx()
748 .struct_span_err(attr.span(), msg)
749 .with_note("the value may not exceed `u16::MAX`")
750 .emit();
751 None
752 }
753 } else {
754 tcx.dcx().emit_err(errors::InvalidLinkOrdinalFormat { span: attr.span() });
755 None
756 }
757}
758
759fn check_link_name_xor_ordinal(
760 tcx: TyCtxt<'_>,
761 codegen_fn_attrs: &CodegenFnAttrs,
762 inline_span: Option<Span>,
763) {
764 if codegen_fn_attrs.link_name.is_none() || codegen_fn_attrs.link_ordinal.is_none() {
765 return;
766 }
767 let msg = "cannot use `#[link_name]` with `#[link_ordinal]`";
768 if let Some(span) = inline_span {
769 tcx.dcx().span_err(span, msg);
770 } else {
771 tcx.dcx().err(msg);
772 }
773}
774
775#[derive(Default)]
776struct MixedExportNameAndNoMangleState<'a> {
777 export_name: Option<Span>,
778 hir_id: Option<HirId>,
779 no_mangle: Option<Span>,
780 no_mangle_attr: Option<&'a hir::Attribute>,
781}
782
783impl<'a> MixedExportNameAndNoMangleState<'a> {
784 fn track_export_name(&mut self, span: Span) {
785 self.export_name = Some(span);
786 }
787
788 fn track_no_mangle(&mut self, span: Span, hir_id: HirId, attr_name: &'a hir::Attribute) {
789 self.no_mangle = Some(span);
790 self.hir_id = Some(hir_id);
791 self.no_mangle_attr = Some(attr_name);
792 }
793
794 fn lint_if_mixed(self, tcx: TyCtxt<'_>) {
796 if let Self {
797 export_name: Some(export_name),
798 no_mangle: Some(no_mangle),
799 hir_id: Some(hir_id),
800 no_mangle_attr: Some(_),
801 } = self
802 {
803 tcx.emit_node_span_lint(
804 lint::builtin::UNUSED_ATTRIBUTES,
805 hir_id,
806 no_mangle,
807 errors::MixedExportNameAndNoMangle {
808 no_mangle,
809 no_mangle_attr: "#[unsafe(no_mangle)]".to_string(),
810 export_name,
811 removal_span: no_mangle,
812 },
813 );
814 }
815 }
816}
817
818fn autodiff_attrs(tcx: TyCtxt<'_>, id: DefId) -> Option<AutoDiffAttrs> {
824 let attrs = tcx.get_attrs(id, sym::rustc_autodiff);
825
826 let attrs = attrs.filter(|attr| attr.has_name(sym::rustc_autodiff)).collect::<Vec<_>>();
827
828 let attr = match &attrs[..] {
831 [] => return None,
832 [attr] => attr,
833 _ => {
834 span_bug!(attrs[1].span(), "cg_ssa: rustc_autodiff should only exist once per source");
835 }
836 };
837
838 let list = attr.meta_item_list().unwrap_or_default();
839
840 if list.is_empty() {
842 return Some(AutoDiffAttrs::source());
843 }
844
845 let [mode, width_meta, input_activities @ .., ret_activity] = &list[..] else {
846 span_bug!(attr.span(), "rustc_autodiff attribute must contain mode, width and activities");
847 };
848 let mode = if let MetaItemInner::MetaItem(MetaItem { path: p1, .. }) = mode {
849 p1.segments.first().unwrap().ident
850 } else {
851 span_bug!(attr.span(), "rustc_autodiff attribute must contain mode");
852 };
853
854 let mode = match mode.as_str() {
856 "Forward" => DiffMode::Forward,
857 "Reverse" => DiffMode::Reverse,
858 _ => {
859 span_bug!(mode.span, "rustc_autodiff attribute contains invalid mode");
860 }
861 };
862
863 let width: u32 = match width_meta {
864 MetaItemInner::MetaItem(MetaItem { path: p1, .. }) => {
865 let w = p1.segments.first().unwrap().ident;
866 match w.as_str().parse() {
867 Ok(val) => val,
868 Err(_) => {
869 span_bug!(w.span, "rustc_autodiff width should fit u32");
870 }
871 }
872 }
873 MetaItemInner::Lit(lit) => {
874 if let LitKind::Int(val, _) = lit.kind {
875 match val.get().try_into() {
876 Ok(val) => val,
877 Err(_) => {
878 span_bug!(lit.span, "rustc_autodiff width should fit u32");
879 }
880 }
881 } else {
882 span_bug!(lit.span, "rustc_autodiff width should be an integer");
883 }
884 }
885 };
886
887 let ret_symbol = if let MetaItemInner::MetaItem(MetaItem { path: p1, .. }) = ret_activity {
889 p1.segments.first().unwrap().ident
890 } else {
891 span_bug!(attr.span(), "rustc_autodiff attribute must contain the return activity");
892 };
893
894 let Ok(ret_activity) = DiffActivity::from_str(ret_symbol.as_str()) else {
896 span_bug!(ret_symbol.span, "invalid return activity");
897 };
898
899 let mut arg_activities: Vec<DiffActivity> = vec![];
901 for arg in input_activities {
902 let arg_symbol = if let MetaItemInner::MetaItem(MetaItem { path: p2, .. }) = arg {
903 match p2.segments.first() {
904 Some(x) => x.ident,
905 None => {
906 span_bug!(
907 arg.span(),
908 "rustc_autodiff attribute must contain the input activity"
909 );
910 }
911 }
912 } else {
913 span_bug!(arg.span(), "rustc_autodiff attribute must contain the input activity");
914 };
915
916 match DiffActivity::from_str(arg_symbol.as_str()) {
917 Ok(arg_activity) => arg_activities.push(arg_activity),
918 Err(_) => {
919 span_bug!(arg_symbol.span, "invalid input activity");
920 }
921 }
922 }
923
924 Some(AutoDiffAttrs { mode, width, ret_activity, input_activity: arg_activities })
925}
926
927pub(crate) fn provide(providers: &mut Providers) {
928 *providers = Providers { codegen_fn_attrs, should_inherit_track_caller, ..*providers };
929}