1use std::str::FromStr;
2
3use rustc_abi::ExternAbi;
4use rustc_ast::attr::list_contains_name;
5use rustc_ast::expand::autodiff_attrs::{
6 AutoDiffAttrs, DiffActivity, DiffMode, valid_input_activity, valid_ret_activity,
7};
8use rustc_ast::{MetaItem, MetaItemInner, attr};
9use rustc_attr_parsing::{InlineAttr, InstructionSetAttr, OptimizeAttr};
10use rustc_data_structures::fx::FxHashMap;
11use rustc_errors::codes::*;
12use rustc_errors::{DiagMessage, SubdiagMessage, struct_span_code_err};
13use rustc_hir::def::DefKind;
14use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId};
15use rustc_hir::weak_lang_items::WEAK_LANG_ITEMS;
16use rustc_hir::{self as hir, HirId, LangItem, lang_items};
17use rustc_middle::middle::codegen_fn_attrs::{
18 CodegenFnAttrFlags, CodegenFnAttrs, PatchableFunctionEntry,
19};
20use rustc_middle::mir::mono::Linkage;
21use rustc_middle::query::Providers;
22use rustc_middle::span_bug;
23use rustc_middle::ty::{self as ty, TyCtxt};
24use rustc_session::parse::feature_err;
25use rustc_session::{Session, lint};
26use rustc_span::{Ident, Span, sym};
27use rustc_target::spec::SanitizerSet;
28use tracing::debug;
29
30use crate::errors;
31use crate::target_features::{check_target_feature_trait_unsafe, from_target_feature_attr};
32
33fn linkage_by_name(tcx: TyCtxt<'_>, def_id: LocalDefId, name: &str) -> Linkage {
34 use rustc_middle::mir::mono::Linkage::*;
35
36 match name {
45 "available_externally" => AvailableExternally,
46 "common" => Common,
47 "extern_weak" => ExternalWeak,
48 "external" => External,
49 "internal" => Internal,
50 "linkonce" => LinkOnceAny,
51 "linkonce_odr" => LinkOnceODR,
52 "weak" => WeakAny,
53 "weak_odr" => WeakODR,
54 _ => tcx.dcx().span_fatal(tcx.def_span(def_id), "invalid linkage specified"),
55 }
56}
57
58fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
59 if cfg!(debug_assertions) {
60 let def_kind = tcx.def_kind(did);
61 assert!(
62 def_kind.has_codegen_attrs(),
63 "unexpected `def_kind` in `codegen_fn_attrs`: {def_kind:?}",
64 );
65 }
66
67 let attrs = tcx.hir().attrs(tcx.local_def_id_to_hir_id(did));
68 let mut codegen_fn_attrs = CodegenFnAttrs::new();
69 if tcx.should_inherit_track_caller(did) {
70 codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER;
71 }
72
73 if cfg!(llvm_enzyme) {
76 let ad = autodiff_attrs(tcx, did.into());
77 codegen_fn_attrs.autodiff_item = ad;
78 }
79
80 let crate_attrs = tcx.hir().attrs(rustc_hir::CRATE_HIR_ID);
83 let no_builtins = attr::contains_name(crate_attrs, sym::no_builtins);
84 if no_builtins {
85 codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_BUILTINS;
86 }
87
88 let rust_target_features = tcx.rust_target_features(LOCAL_CRATE);
89
90 let mut inline_span = None;
91 let mut link_ordinal_span = None;
92 let mut no_sanitize_span = None;
93 let mut mixed_export_name_no_mangle_lint_state = MixedExportNameAndNoMangleState::default();
94
95 for attr in attrs.iter() {
96 let fn_sig = || {
102 use DefKind::*;
103
104 let def_kind = tcx.def_kind(did);
105 if let Fn | AssocFn | Variant | Ctor(..) = def_kind {
106 Some(tcx.fn_sig(did))
107 } else {
108 tcx.dcx()
109 .span_delayed_bug(attr.span, "this attribute can only be applied to functions");
110 None
111 }
112 };
113
114 let Some(Ident { name, .. }) = attr.ident() else {
115 continue;
116 };
117
118 match name {
119 sym::cold => codegen_fn_attrs.flags |= CodegenFnAttrFlags::COLD,
120 sym::rustc_allocator => codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR,
121 sym::ffi_pure => codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_PURE,
122 sym::ffi_const => codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_CONST,
123 sym::rustc_nounwind => codegen_fn_attrs.flags |= CodegenFnAttrFlags::NEVER_UNWIND,
124 sym::rustc_reallocator => codegen_fn_attrs.flags |= CodegenFnAttrFlags::REALLOCATOR,
125 sym::rustc_deallocator => codegen_fn_attrs.flags |= CodegenFnAttrFlags::DEALLOCATOR,
126 sym::rustc_allocator_zeroed => {
127 codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR_ZEROED
128 }
129 sym::naked => codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED,
130 sym::no_mangle => {
131 if tcx.opt_item_name(did.to_def_id()).is_some() {
132 codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE;
133 mixed_export_name_no_mangle_lint_state.track_no_mangle(
134 attr.span,
135 tcx.local_def_id_to_hir_id(did),
136 attr,
137 );
138 } else {
139 tcx.dcx()
140 .struct_span_err(
141 attr.span,
142 format!(
143 "`#[no_mangle]` cannot be used on {} {} as it has no name",
144 tcx.def_descr_article(did.to_def_id()),
145 tcx.def_descr(did.to_def_id()),
146 ),
147 )
148 .emit();
149 }
150 }
151 sym::rustc_std_internal_symbol => {
152 codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL
153 }
154 sym::used => {
155 let inner = attr.meta_item_list();
156 match inner.as_deref() {
157 Some([item]) if item.has_name(sym::linker) => {
158 if !tcx.features().used_with_arg() {
159 feature_err(
160 &tcx.sess,
161 sym::used_with_arg,
162 attr.span,
163 "`#[used(linker)]` is currently unstable",
164 )
165 .emit();
166 }
167 codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED_LINKER;
168 }
169 Some([item]) if item.has_name(sym::compiler) => {
170 if !tcx.features().used_with_arg() {
171 feature_err(
172 &tcx.sess,
173 sym::used_with_arg,
174 attr.span,
175 "`#[used(compiler)]` is currently unstable",
176 )
177 .emit();
178 }
179 codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED;
180 }
181 Some(_) => {
182 tcx.dcx().emit_err(errors::ExpectedUsedSymbol { span: attr.span });
183 }
184 None => {
185 let is_like_elf = !(tcx.sess.target.is_like_osx
207 || tcx.sess.target.is_like_windows
208 || tcx.sess.target.is_like_wasm);
209 codegen_fn_attrs.flags |= if is_like_elf {
210 CodegenFnAttrFlags::USED
211 } else {
212 CodegenFnAttrFlags::USED_LINKER
213 };
214 }
215 }
216 }
217 sym::thread_local => codegen_fn_attrs.flags |= CodegenFnAttrFlags::THREAD_LOCAL,
218 sym::track_caller => {
219 let is_closure = tcx.is_closure_like(did.to_def_id());
220
221 if !is_closure
222 && let Some(fn_sig) = fn_sig()
223 && fn_sig.skip_binder().abi() != ExternAbi::Rust
224 {
225 struct_span_code_err!(
226 tcx.dcx(),
227 attr.span,
228 E0737,
229 "`#[track_caller]` requires Rust ABI"
230 )
231 .emit();
232 }
233 if is_closure
234 && !tcx.features().closure_track_caller()
235 && !attr.span.allows_unstable(sym::closure_track_caller)
236 {
237 feature_err(
238 &tcx.sess,
239 sym::closure_track_caller,
240 attr.span,
241 "`#[track_caller]` on closures is currently unstable",
242 )
243 .emit();
244 }
245 codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER
246 }
247 sym::export_name => {
248 if let Some(s) = attr.value_str() {
249 if s.as_str().contains('\0') {
250 struct_span_code_err!(
253 tcx.dcx(),
254 attr.span,
255 E0648,
256 "`export_name` may not contain null characters"
257 )
258 .emit();
259 }
260 codegen_fn_attrs.export_name = Some(s);
261 mixed_export_name_no_mangle_lint_state.track_export_name(attr.span);
262 }
263 }
264 sym::target_feature => {
265 let Some(sig) = tcx.hir_node_by_def_id(did).fn_sig() else {
266 tcx.dcx().span_delayed_bug(attr.span, "target_feature applied to non-fn");
267 continue;
268 };
269 let safe_target_features =
270 matches!(sig.header.safety, hir::HeaderSafety::SafeTargetFeatures);
271 codegen_fn_attrs.safe_target_features = safe_target_features;
272 if safe_target_features {
273 if tcx.sess.target.is_like_wasm || tcx.sess.opts.actually_rustdoc {
274 } else {
296 check_target_feature_trait_unsafe(tcx, did, attr.span);
297 }
298 }
299 from_target_feature_attr(
300 tcx,
301 attr,
302 rust_target_features,
303 &mut codegen_fn_attrs.target_features,
304 );
305 }
306 sym::linkage => {
307 if let Some(val) = attr.value_str() {
308 let linkage = Some(linkage_by_name(tcx, did, val.as_str()));
309 if tcx.is_foreign_item(did) {
310 codegen_fn_attrs.import_linkage = linkage;
311
312 if tcx.is_mutable_static(did.into()) {
313 let mut diag = tcx.dcx().struct_span_err(
314 attr.span,
315 "extern mutable statics are not allowed with `#[linkage]`",
316 );
317 diag.note(
318 "marking the extern static mutable would allow changing which \
319 symbol the static references rather than make the target of the \
320 symbol mutable",
321 );
322 diag.emit();
323 }
324 } else {
325 codegen_fn_attrs.linkage = linkage;
326 }
327 }
328 }
329 sym::link_section => {
330 if let Some(val) = attr.value_str() {
331 if val.as_str().bytes().any(|b| b == 0) {
332 let msg = format!("illegal null byte in link_section value: `{val}`");
333 tcx.dcx().span_err(attr.span, msg);
334 } else {
335 codegen_fn_attrs.link_section = Some(val);
336 }
337 }
338 }
339 sym::link_name => codegen_fn_attrs.link_name = attr.value_str(),
340 sym::link_ordinal => {
341 link_ordinal_span = Some(attr.span);
342 if let ordinal @ Some(_) = check_link_ordinal(tcx, attr) {
343 codegen_fn_attrs.link_ordinal = ordinal;
344 }
345 }
346 sym::no_sanitize => {
347 no_sanitize_span = Some(attr.span);
348 if let Some(list) = attr.meta_item_list() {
349 for item in list.iter() {
350 match item.name_or_empty() {
351 sym::address => {
352 codegen_fn_attrs.no_sanitize |=
353 SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS
354 }
355 sym::cfi => codegen_fn_attrs.no_sanitize |= SanitizerSet::CFI,
356 sym::kcfi => codegen_fn_attrs.no_sanitize |= SanitizerSet::KCFI,
357 sym::memory => codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMORY,
358 sym::memtag => codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMTAG,
359 sym::shadow_call_stack => {
360 codegen_fn_attrs.no_sanitize |= SanitizerSet::SHADOWCALLSTACK
361 }
362 sym::thread => codegen_fn_attrs.no_sanitize |= SanitizerSet::THREAD,
363 sym::hwaddress => {
364 codegen_fn_attrs.no_sanitize |= SanitizerSet::HWADDRESS
365 }
366 _ => {
367 tcx.dcx().emit_err(errors::InvalidNoSanitize { span: item.span() });
368 }
369 }
370 }
371 }
372 }
373 sym::instruction_set => {
374 codegen_fn_attrs.instruction_set =
375 attr.meta_item_list().and_then(|l| match &l[..] {
376 [MetaItemInner::MetaItem(set)] => {
377 let segments =
378 set.path.segments.iter().map(|x| x.ident.name).collect::<Vec<_>>();
379 match segments.as_slice() {
380 [sym::arm, sym::a32] | [sym::arm, sym::t32] => {
381 if !tcx.sess.target.has_thumb_interworking {
382 struct_span_code_err!(
383 tcx.dcx(),
384 attr.span,
385 E0779,
386 "target does not support `#[instruction_set]`"
387 )
388 .emit();
389 None
390 } else if segments[1] == sym::a32 {
391 Some(InstructionSetAttr::ArmA32)
392 } else if segments[1] == sym::t32 {
393 Some(InstructionSetAttr::ArmT32)
394 } else {
395 unreachable!()
396 }
397 }
398 _ => {
399 struct_span_code_err!(
400 tcx.dcx(),
401 attr.span,
402 E0779,
403 "invalid instruction set specified",
404 )
405 .emit();
406 None
407 }
408 }
409 }
410 [] => {
411 struct_span_code_err!(
412 tcx.dcx(),
413 attr.span,
414 E0778,
415 "`#[instruction_set]` requires an argument"
416 )
417 .emit();
418 None
419 }
420 _ => {
421 struct_span_code_err!(
422 tcx.dcx(),
423 attr.span,
424 E0779,
425 "cannot specify more than one instruction set"
426 )
427 .emit();
428 None
429 }
430 })
431 }
432 sym::repr => {
433 codegen_fn_attrs.alignment = if let Some(items) = attr.meta_item_list()
434 && let [item] = items.as_slice()
435 && let Some((sym::align, literal)) = item.singleton_lit_list()
436 {
437 rustc_attr_parsing::parse_alignment(&literal.kind)
438 .map_err(|msg| {
439 struct_span_code_err!(
440 tcx.dcx(),
441 literal.span,
442 E0589,
443 "invalid `repr(align)` attribute: {}",
444 msg
445 )
446 .emit();
447 })
448 .ok()
449 } else {
450 None
451 };
452 }
453 sym::patchable_function_entry => {
454 codegen_fn_attrs.patchable_function_entry = attr.meta_item_list().and_then(|l| {
455 let mut prefix = None;
456 let mut entry = None;
457 for item in l {
458 let Some(meta_item) = item.meta_item() else {
459 tcx.dcx().span_err(item.span(), "expected name value pair");
460 continue;
461 };
462
463 let Some(name_value_lit) = meta_item.name_value_literal() else {
464 tcx.dcx().span_err(item.span(), "expected name value pair");
465 continue;
466 };
467
468 fn emit_error_with_label(
469 tcx: TyCtxt<'_>,
470 span: Span,
471 error: impl Into<DiagMessage>,
472 label: impl Into<SubdiagMessage>,
473 ) {
474 let mut err: rustc_errors::Diag<'_, _> =
475 tcx.dcx().struct_span_err(span, error);
476 err.span_label(span, label);
477 err.emit();
478 }
479
480 let attrib_to_write = match meta_item.name_or_empty() {
481 sym::prefix_nops => &mut prefix,
482 sym::entry_nops => &mut entry,
483 _ => {
484 emit_error_with_label(
485 tcx,
486 item.span(),
487 "unexpected parameter name",
488 format!("expected {} or {}", sym::prefix_nops, sym::entry_nops),
489 );
490 continue;
491 }
492 };
493
494 let rustc_ast::LitKind::Int(val, _) = name_value_lit.kind else {
495 emit_error_with_label(
496 tcx,
497 name_value_lit.span,
498 "invalid literal value",
499 "value must be an integer between `0` and `255`",
500 );
501 continue;
502 };
503
504 let Ok(val) = val.get().try_into() else {
505 emit_error_with_label(
506 tcx,
507 name_value_lit.span,
508 "integer value out of range",
509 "value must be between `0` and `255`",
510 );
511 continue;
512 };
513
514 *attrib_to_write = Some(val);
515 }
516
517 if let (None, None) = (prefix, entry) {
518 tcx.dcx().span_err(attr.span, "must specify at least one parameter");
519 }
520
521 Some(PatchableFunctionEntry::from_prefix_and_entry(
522 prefix.unwrap_or(0),
523 entry.unwrap_or(0),
524 ))
525 })
526 }
527 _ => {}
528 }
529 }
530
531 mixed_export_name_no_mangle_lint_state.lint_if_mixed(tcx);
532
533 codegen_fn_attrs.inline = attrs.iter().fold(InlineAttr::None, |ia, attr| {
534 if !attr.has_name(sym::inline) {
535 return ia;
536 }
537
538 if attr.is_word() {
539 InlineAttr::Hint
540 } else if let Some(ref items) = attr.meta_item_list() {
541 inline_span = Some(attr.span);
542 if items.len() != 1 {
543 struct_span_code_err!(tcx.dcx(), attr.span, E0534, "expected one argument").emit();
544 InlineAttr::None
545 } else if list_contains_name(items, sym::always) {
546 InlineAttr::Always
547 } else if list_contains_name(items, sym::never) {
548 InlineAttr::Never
549 } else {
550 struct_span_code_err!(tcx.dcx(), items[0].span(), E0535, "invalid argument")
551 .with_help("valid inline arguments are `always` and `never`")
552 .emit();
553
554 InlineAttr::None
555 }
556 } else {
557 ia
558 }
559 });
560 codegen_fn_attrs.inline = attrs.iter().fold(codegen_fn_attrs.inline, |ia, attr| {
561 if !attr.has_name(sym::rustc_force_inline) || !tcx.features().rustc_attrs() {
562 return ia;
563 }
564
565 if attr.is_word() {
566 InlineAttr::Force { attr_span: attr.span, reason: None }
567 } else if let Some(val) = attr.value_str() {
568 InlineAttr::Force { attr_span: attr.span, reason: Some(val) }
569 } else {
570 debug!("`rustc_force_inline` not checked by attribute validation");
571 ia
572 }
573 });
574
575 if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) {
579 codegen_fn_attrs.inline = InlineAttr::Never;
580 }
581
582 codegen_fn_attrs.optimize = attrs.iter().fold(OptimizeAttr::Default, |ia, attr| {
583 if !attr.has_name(sym::optimize) {
584 return ia;
585 }
586 let err = |sp, s| struct_span_code_err!(tcx.dcx(), sp, E0722, "{}", s).emit();
587 if attr.is_word() {
588 err(attr.span, "expected one argument");
589 ia
590 } else if let Some(ref items) = attr.meta_item_list() {
591 inline_span = Some(attr.span);
592 if items.len() != 1 {
593 err(attr.span, "expected one argument");
594 OptimizeAttr::Default
595 } else if list_contains_name(items, sym::size) {
596 OptimizeAttr::Size
597 } else if list_contains_name(items, sym::speed) {
598 OptimizeAttr::Speed
599 } else if list_contains_name(items, sym::none) {
600 OptimizeAttr::DoNotOptimize
601 } else {
602 err(items[0].span(), "invalid argument");
603 OptimizeAttr::Default
604 }
605 } else {
606 OptimizeAttr::Default
607 }
608 });
609
610 if tcx.is_closure_like(did.to_def_id()) && codegen_fn_attrs.inline != InlineAttr::Always {
624 let owner_id = tcx.parent(did.to_def_id());
625 if tcx.def_kind(owner_id).has_codegen_attrs() {
626 codegen_fn_attrs
627 .target_features
628 .extend(tcx.codegen_fn_attrs(owner_id).target_features.iter().copied());
629 }
630 }
631
632 if !codegen_fn_attrs.target_features.is_empty()
646 && matches!(codegen_fn_attrs.inline, InlineAttr::Always)
647 {
648 if let Some(span) = inline_span {
649 tcx.dcx().span_err(span, "cannot use `#[inline(always)]` with `#[target_feature]`");
650 }
651 }
652
653 if !codegen_fn_attrs.no_sanitize.is_empty() && codegen_fn_attrs.inline.always() {
654 if let (Some(no_sanitize_span), Some(inline_span)) = (no_sanitize_span, inline_span) {
655 let hir_id = tcx.local_def_id_to_hir_id(did);
656 tcx.node_span_lint(
657 lint::builtin::INLINE_NO_SANITIZE,
658 hir_id,
659 no_sanitize_span,
660 |lint| {
661 lint.primary_message("`no_sanitize` will have no effect after inlining");
662 lint.span_note(inline_span, "inlining requested here");
663 },
664 )
665 }
666 }
667
668 if WEAK_LANG_ITEMS.iter().any(|&l| tcx.lang_items().get(l) == Some(did.to_def_id())) {
674 codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL;
675 }
676 if let Some((name, _)) = lang_items::extract(attrs)
677 && let Some(lang_item) = LangItem::from_name(name)
678 && let Some(link_name) = lang_item.link_name()
679 {
680 codegen_fn_attrs.export_name = Some(link_name);
681 codegen_fn_attrs.link_name = Some(link_name);
682 }
683 check_link_name_xor_ordinal(tcx, &codegen_fn_attrs, link_ordinal_span);
684
685 if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL) {
689 codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE;
690 }
691
692 if let Some(name) = &codegen_fn_attrs.link_name {
696 if name.as_str().starts_with("llvm.") {
697 codegen_fn_attrs.flags |= CodegenFnAttrFlags::NEVER_UNWIND;
698 }
699 }
700
701 if let Some(features) = check_tied_features(
702 tcx.sess,
703 &codegen_fn_attrs
704 .target_features
705 .iter()
706 .map(|features| (features.name.as_str(), true))
707 .collect(),
708 ) {
709 let span = tcx
710 .get_attrs(did, sym::target_feature)
711 .next()
712 .map_or_else(|| tcx.def_span(did), |a| a.span);
713 tcx.dcx()
714 .create_err(errors::TargetFeatureDisableOrEnable {
715 features,
716 span: Some(span),
717 missing_features: Some(errors::MissingFeatures),
718 })
719 .emit();
720 }
721
722 codegen_fn_attrs
723}
724
725pub fn check_tied_features(
728 sess: &Session,
729 features: &FxHashMap<&str, bool>,
730) -> Option<&'static [&'static str]> {
731 if !features.is_empty() {
732 for tied in sess.target.tied_target_features() {
733 let mut tied_iter = tied.iter();
735 let enabled = features.get(tied_iter.next().unwrap());
736 if tied_iter.any(|f| enabled != features.get(f)) {
737 return Some(tied);
738 }
739 }
740 }
741 None
742}
743
744fn should_inherit_track_caller(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
747 if let Some(impl_item) = tcx.opt_associated_item(def_id)
748 && let ty::AssocItemContainer::Impl = impl_item.container
749 && let Some(trait_item) = impl_item.trait_item_def_id
750 {
751 return tcx.codegen_fn_attrs(trait_item).flags.intersects(CodegenFnAttrFlags::TRACK_CALLER);
752 }
753
754 false
755}
756
757fn check_link_ordinal(tcx: TyCtxt<'_>, attr: &hir::Attribute) -> Option<u16> {
758 use rustc_ast::{LitIntType, LitKind, MetaItemLit};
759 let meta_item_list = attr.meta_item_list();
760 let meta_item_list = meta_item_list.as_deref();
761 let sole_meta_list = match meta_item_list {
762 Some([item]) => item.lit(),
763 Some(_) => {
764 tcx.dcx().emit_err(errors::InvalidLinkOrdinalNargs { span: attr.span });
765 return None;
766 }
767 _ => None,
768 };
769 if let Some(MetaItemLit { kind: LitKind::Int(ordinal, LitIntType::Unsuffixed), .. }) =
770 sole_meta_list
771 {
772 if *ordinal <= u16::MAX as u128 {
786 Some(ordinal.get() as u16)
787 } else {
788 let msg = format!("ordinal value in `link_ordinal` is too large: `{ordinal}`");
789 tcx.dcx()
790 .struct_span_err(attr.span, msg)
791 .with_note("the value may not exceed `u16::MAX`")
792 .emit();
793 None
794 }
795 } else {
796 tcx.dcx().emit_err(errors::InvalidLinkOrdinalFormat { span: attr.span });
797 None
798 }
799}
800
801fn check_link_name_xor_ordinal(
802 tcx: TyCtxt<'_>,
803 codegen_fn_attrs: &CodegenFnAttrs,
804 inline_span: Option<Span>,
805) {
806 if codegen_fn_attrs.link_name.is_none() || codegen_fn_attrs.link_ordinal.is_none() {
807 return;
808 }
809 let msg = "cannot use `#[link_name]` with `#[link_ordinal]`";
810 if let Some(span) = inline_span {
811 tcx.dcx().span_err(span, msg);
812 } else {
813 tcx.dcx().err(msg);
814 }
815}
816
817#[derive(Default)]
818struct MixedExportNameAndNoMangleState<'a> {
819 export_name: Option<Span>,
820 hir_id: Option<HirId>,
821 no_mangle: Option<Span>,
822 no_mangle_attr: Option<&'a hir::Attribute>,
823}
824
825impl<'a> MixedExportNameAndNoMangleState<'a> {
826 fn track_export_name(&mut self, span: Span) {
827 self.export_name = Some(span);
828 }
829
830 fn track_no_mangle(&mut self, span: Span, hir_id: HirId, attr_name: &'a hir::Attribute) {
831 self.no_mangle = Some(span);
832 self.hir_id = Some(hir_id);
833 self.no_mangle_attr = Some(attr_name);
834 }
835
836 fn lint_if_mixed(self, tcx: TyCtxt<'_>) {
838 if let Self {
839 export_name: Some(export_name),
840 no_mangle: Some(no_mangle),
841 hir_id: Some(hir_id),
842 no_mangle_attr: Some(no_mangle_attr),
843 } = self
844 {
845 tcx.emit_node_span_lint(
846 lint::builtin::UNUSED_ATTRIBUTES,
847 hir_id,
848 no_mangle,
849 errors::MixedExportNameAndNoMangle {
850 no_mangle,
851 no_mangle_attr: rustc_hir_pretty::attribute_to_string(&tcx, no_mangle_attr),
852 export_name,
853 removal_span: no_mangle,
854 },
855 );
856 }
857 }
858}
859
860fn autodiff_attrs(tcx: TyCtxt<'_>, id: DefId) -> Option<AutoDiffAttrs> {
866 let attrs = tcx.get_attrs(id, sym::rustc_autodiff);
867
868 let attrs =
869 attrs.filter(|attr| attr.name_or_empty() == sym::rustc_autodiff).collect::<Vec<_>>();
870
871 let attr = match &attrs[..] {
876 [] => return None,
877 [attr] => attr,
878 [attr, _attr2] => attr,
880 _ => {
881 span_bug!(attrs[1].span, "cg_ssa: rustc_autodiff should only exist once per source");
884 }
885 };
886
887 let list = attr.meta_item_list().unwrap_or_default();
888
889 if list.is_empty() {
891 return Some(AutoDiffAttrs::source());
892 }
893
894 let [mode, input_activities @ .., ret_activity] = &list[..] else {
895 span_bug!(attr.span, "rustc_autodiff attribute must contain mode and activities");
896 };
897 let mode = if let MetaItemInner::MetaItem(MetaItem { path: ref p1, .. }) = mode {
898 p1.segments.first().unwrap().ident
899 } else {
900 span_bug!(attr.span, "rustc_autodiff attribute must contain mode");
901 };
902
903 let mode = match mode.as_str() {
905 "Forward" => DiffMode::Forward,
906 "Reverse" => DiffMode::Reverse,
907 _ => {
908 span_bug!(mode.span, "rustc_autodiff attribute contains invalid mode");
909 }
910 };
911
912 let ret_symbol = if let MetaItemInner::MetaItem(MetaItem { path: ref p1, .. }) = ret_activity {
914 p1.segments.first().unwrap().ident
915 } else {
916 span_bug!(attr.span, "rustc_autodiff attribute must contain the return activity");
917 };
918
919 let Ok(ret_activity) = DiffActivity::from_str(ret_symbol.as_str()) else {
921 span_bug!(ret_symbol.span, "invalid return activity");
922 };
923
924 let mut arg_activities: Vec<DiffActivity> = vec![];
926 for arg in input_activities {
927 let arg_symbol = if let MetaItemInner::MetaItem(MetaItem { path: ref p2, .. }) = arg {
928 match p2.segments.first() {
929 Some(x) => x.ident,
930 None => {
931 span_bug!(
932 arg.span(),
933 "rustc_autodiff attribute must contain the input activity"
934 );
935 }
936 }
937 } else {
938 span_bug!(arg.span(), "rustc_autodiff attribute must contain the input activity");
939 };
940
941 match DiffActivity::from_str(arg_symbol.as_str()) {
942 Ok(arg_activity) => arg_activities.push(arg_activity),
943 Err(_) => {
944 span_bug!(arg_symbol.span, "invalid input activity");
945 }
946 }
947 }
948
949 for &input in &arg_activities {
950 if !valid_input_activity(mode, input) {
951 span_bug!(attr.span, "Invalid input activity {} for {} mode", input, mode);
952 }
953 }
954 if !valid_ret_activity(mode, ret_activity) {
955 span_bug!(attr.span, "Invalid return activity {} for {} mode", ret_activity, mode);
956 }
957
958 Some(AutoDiffAttrs { mode, ret_activity, input_activity: arg_activities })
959}
960
961pub(crate) fn provide(providers: &mut Providers) {
962 *providers = Providers { codegen_fn_attrs, should_inherit_track_caller, ..*providers };
963}