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 codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED_LINKER
202 }
203 }
204 }
205 sym::thread_local => codegen_fn_attrs.flags |= CodegenFnAttrFlags::THREAD_LOCAL,
206 sym::track_caller => {
207 let is_closure = tcx.is_closure_like(did.to_def_id());
208
209 if !is_closure
210 && let Some(fn_sig) = fn_sig()
211 && fn_sig.skip_binder().abi() != ExternAbi::Rust
212 {
213 tcx.dcx().emit_err(errors::RequiresRustAbi { span: attr.span() });
214 }
215 if is_closure
216 && !tcx.features().closure_track_caller()
217 && !attr.span().allows_unstable(sym::closure_track_caller)
218 {
219 feature_err(
220 &tcx.sess,
221 sym::closure_track_caller,
222 attr.span(),
223 "`#[track_caller]` on closures is currently unstable",
224 )
225 .emit();
226 }
227 codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER
228 }
229 sym::export_name => {
230 if let Some(s) = attr.value_str() {
231 if s.as_str().contains('\0') {
232 tcx.dcx().emit_err(errors::NullOnExport { span: attr.span() });
235 }
236 codegen_fn_attrs.export_name = Some(s);
237 mixed_export_name_no_mangle_lint_state.track_export_name(attr.span());
238 }
239 }
240 sym::target_feature => {
241 let Some(sig) = tcx.hir_node_by_def_id(did).fn_sig() else {
242 tcx.dcx().span_delayed_bug(attr.span(), "target_feature applied to non-fn");
243 continue;
244 };
245 let safe_target_features =
246 matches!(sig.header.safety, hir::HeaderSafety::SafeTargetFeatures);
247 codegen_fn_attrs.safe_target_features = safe_target_features;
248 if safe_target_features {
249 if tcx.sess.target.is_like_wasm || tcx.sess.opts.actually_rustdoc {
250 } else {
272 check_target_feature_trait_unsafe(tcx, did, attr.span());
273 }
274 }
275 from_target_feature_attr(
276 tcx,
277 did,
278 attr,
279 rust_target_features,
280 &mut codegen_fn_attrs.target_features,
281 );
282 }
283 sym::linkage => {
284 if let Some(val) = attr.value_str() {
285 let linkage = Some(linkage_by_name(tcx, did, val.as_str()));
286 if tcx.is_foreign_item(did) {
287 codegen_fn_attrs.import_linkage = linkage;
288
289 if tcx.is_mutable_static(did.into()) {
290 let mut diag = tcx.dcx().struct_span_err(
291 attr.span(),
292 "extern mutable statics are not allowed with `#[linkage]`",
293 );
294 diag.note(
295 "marking the extern static mutable would allow changing which \
296 symbol the static references rather than make the target of the \
297 symbol mutable",
298 );
299 diag.emit();
300 }
301 } else {
302 codegen_fn_attrs.linkage = linkage;
303 }
304 }
305 }
306 sym::link_section => {
307 if let Some(val) = attr.value_str() {
308 if val.as_str().bytes().any(|b| b == 0) {
309 let msg = format!("illegal null byte in link_section value: `{val}`");
310 tcx.dcx().span_err(attr.span(), msg);
311 } else {
312 codegen_fn_attrs.link_section = Some(val);
313 }
314 }
315 }
316 sym::link_name => codegen_fn_attrs.link_name = attr.value_str(),
317 sym::link_ordinal => {
318 link_ordinal_span = Some(attr.span());
319 if let ordinal @ Some(_) = check_link_ordinal(tcx, attr) {
320 codegen_fn_attrs.link_ordinal = ordinal;
321 }
322 }
323 sym::no_sanitize => {
324 no_sanitize_span = Some(attr.span());
325 if let Some(list) = attr.meta_item_list() {
326 for item in list.iter() {
327 match item.name() {
328 Some(sym::address) => {
329 codegen_fn_attrs.no_sanitize |=
330 SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS
331 }
332 Some(sym::cfi) => codegen_fn_attrs.no_sanitize |= SanitizerSet::CFI,
333 Some(sym::kcfi) => codegen_fn_attrs.no_sanitize |= SanitizerSet::KCFI,
334 Some(sym::memory) => {
335 codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMORY
336 }
337 Some(sym::memtag) => {
338 codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMTAG
339 }
340 Some(sym::shadow_call_stack) => {
341 codegen_fn_attrs.no_sanitize |= SanitizerSet::SHADOWCALLSTACK
342 }
343 Some(sym::thread) => {
344 codegen_fn_attrs.no_sanitize |= SanitizerSet::THREAD
345 }
346 Some(sym::hwaddress) => {
347 codegen_fn_attrs.no_sanitize |= SanitizerSet::HWADDRESS
348 }
349 _ => {
350 tcx.dcx().emit_err(errors::InvalidNoSanitize { span: item.span() });
351 }
352 }
353 }
354 }
355 }
356 sym::instruction_set => {
357 codegen_fn_attrs.instruction_set =
358 attr.meta_item_list().and_then(|l| match &l[..] {
359 [MetaItemInner::MetaItem(set)] => {
360 let segments =
361 set.path.segments.iter().map(|x| x.ident.name).collect::<Vec<_>>();
362 match segments.as_slice() {
363 [sym::arm, sym::a32 | sym::t32]
364 if !tcx.sess.target.has_thumb_interworking =>
365 {
366 tcx.dcx().emit_err(errors::UnsupportedInstructionSet {
367 span: attr.span(),
368 });
369 None
370 }
371 [sym::arm, sym::a32] => Some(InstructionSetAttr::ArmA32),
372 [sym::arm, sym::t32] => Some(InstructionSetAttr::ArmT32),
373 _ => {
374 tcx.dcx().emit_err(errors::InvalidInstructionSet {
375 span: attr.span(),
376 });
377 None
378 }
379 }
380 }
381 [] => {
382 tcx.dcx().emit_err(errors::BareInstructionSet { span: attr.span() });
383 None
384 }
385 _ => {
386 tcx.dcx()
387 .emit_err(errors::MultipleInstructionSet { span: attr.span() });
388 None
389 }
390 })
391 }
392 sym::patchable_function_entry => {
393 codegen_fn_attrs.patchable_function_entry = attr.meta_item_list().and_then(|l| {
394 let mut prefix = None;
395 let mut entry = None;
396 for item in l {
397 let Some(meta_item) = item.meta_item() else {
398 tcx.dcx().emit_err(errors::ExpectedNameValuePair { span: item.span() });
399 continue;
400 };
401
402 let Some(name_value_lit) = meta_item.name_value_literal() else {
403 tcx.dcx().emit_err(errors::ExpectedNameValuePair { span: item.span() });
404 continue;
405 };
406
407 let attrib_to_write = match meta_item.name() {
408 Some(sym::prefix_nops) => &mut prefix,
409 Some(sym::entry_nops) => &mut entry,
410 _ => {
411 tcx.dcx().emit_err(errors::UnexpectedParameterName {
412 span: item.span(),
413 prefix_nops: sym::prefix_nops,
414 entry_nops: sym::entry_nops,
415 });
416 continue;
417 }
418 };
419
420 let rustc_ast::LitKind::Int(val, _) = name_value_lit.kind else {
421 tcx.dcx().emit_err(errors::InvalidLiteralValue {
422 span: name_value_lit.span,
423 });
424 continue;
425 };
426
427 let Ok(val) = val.get().try_into() else {
428 tcx.dcx()
429 .emit_err(errors::OutOfRangeInteger { span: name_value_lit.span });
430 continue;
431 };
432
433 *attrib_to_write = Some(val);
434 }
435
436 if let (None, None) = (prefix, entry) {
437 tcx.dcx().span_err(attr.span(), "must specify at least one parameter");
438 }
439
440 Some(PatchableFunctionEntry::from_prefix_and_entry(
441 prefix.unwrap_or(0),
442 entry.unwrap_or(0),
443 ))
444 })
445 }
446 _ => {}
447 }
448 }
449
450 mixed_export_name_no_mangle_lint_state.lint_if_mixed(tcx);
451
452 codegen_fn_attrs.inline = attrs.iter().fold(InlineAttr::None, |ia, attr| {
453 if !attr.has_name(sym::inline) {
454 return ia;
455 }
456
457 if attr.is_word() {
458 return InlineAttr::Hint;
459 }
460 let Some(ref items) = attr.meta_item_list() else {
461 return ia;
462 };
463 inline_span = Some(attr.span());
464
465 let [item] = &items[..] else {
466 tcx.dcx().emit_err(errors::ExpectedOneArgument { span: attr.span() });
467 return InlineAttr::None;
468 };
469
470 if item.has_name(sym::always) {
471 InlineAttr::Always
472 } else if item.has_name(sym::never) {
473 InlineAttr::Never
474 } else {
475 tcx.dcx().emit_err(errors::InvalidArgument { span: items[0].span() });
476
477 InlineAttr::None
478 }
479 });
480 codegen_fn_attrs.inline = attrs.iter().fold(codegen_fn_attrs.inline, |ia, attr| {
481 if !attr.has_name(sym::rustc_force_inline) || !tcx.features().rustc_attrs() {
482 return ia;
483 }
484
485 if attr.is_word() {
486 InlineAttr::Force { attr_span: attr.span(), reason: None }
487 } else if let Some(val) = attr.value_str() {
488 InlineAttr::Force { attr_span: attr.span(), reason: Some(val) }
489 } else {
490 debug!("`rustc_force_inline` not checked by attribute validation");
491 ia
492 }
493 });
494
495 if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) {
499 codegen_fn_attrs.inline = InlineAttr::Never;
500 }
501
502 codegen_fn_attrs.optimize = attrs.iter().fold(OptimizeAttr::Default, |ia, attr| {
503 if !attr.has_name(sym::optimize) {
504 return ia;
505 }
506 if attr.is_word() {
507 tcx.dcx().emit_err(errors::ExpectedOneArgumentOptimize { span: attr.span() });
508 return ia;
509 }
510 let Some(ref items) = attr.meta_item_list() else {
511 return OptimizeAttr::Default;
512 };
513
514 inline_span = Some(attr.span());
515 let [item] = &items[..] else {
516 tcx.dcx().emit_err(errors::ExpectedOneArgumentOptimize { span: attr.span() });
517 return OptimizeAttr::Default;
518 };
519 if item.has_name(sym::size) {
520 OptimizeAttr::Size
521 } else if item.has_name(sym::speed) {
522 OptimizeAttr::Speed
523 } else if item.has_name(sym::none) {
524 OptimizeAttr::DoNotOptimize
525 } else {
526 tcx.dcx().emit_err(errors::InvalidArgumentOptimize { span: item.span() });
527 OptimizeAttr::Default
528 }
529 });
530
531 if tcx.is_closure_like(did.to_def_id()) && codegen_fn_attrs.inline != InlineAttr::Always {
545 let owner_id = tcx.parent(did.to_def_id());
546 if tcx.def_kind(owner_id).has_codegen_attrs() {
547 codegen_fn_attrs
548 .target_features
549 .extend(tcx.codegen_fn_attrs(owner_id).target_features.iter().copied());
550 }
551 }
552
553 if !codegen_fn_attrs.target_features.is_empty()
567 && matches!(codegen_fn_attrs.inline, InlineAttr::Always)
568 && let Some(span) = inline_span
569 {
570 tcx.dcx().span_err(span, "cannot use `#[inline(always)]` with `#[target_feature]`");
571 }
572
573 if !codegen_fn_attrs.no_sanitize.is_empty()
574 && codegen_fn_attrs.inline.always()
575 && let (Some(no_sanitize_span), Some(inline_span)) = (no_sanitize_span, inline_span)
576 {
577 let hir_id = tcx.local_def_id_to_hir_id(did);
578 tcx.node_span_lint(lint::builtin::INLINE_NO_SANITIZE, hir_id, no_sanitize_span, |lint| {
579 lint.primary_message("`no_sanitize` will have no effect after inlining");
580 lint.span_note(inline_span, "inlining requested here");
581 })
582 }
583
584 if let Some((name, _)) = lang_items::extract(attrs)
590 && let Some(lang_item) = LangItem::from_name(name)
591 {
592 if WEAK_LANG_ITEMS.contains(&lang_item) {
593 codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL;
594 }
595 if let Some(link_name) = lang_item.link_name() {
596 codegen_fn_attrs.export_name = Some(link_name);
597 codegen_fn_attrs.link_name = Some(link_name);
598 }
599 }
600 check_link_name_xor_ordinal(tcx, &codegen_fn_attrs, link_ordinal_span);
601
602 if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL)
603 && codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NO_MANGLE)
604 {
605 let lang_item =
606 lang_items::extract(attrs).map_or(None, |(name, _span)| LangItem::from_name(name));
607 let mut err = tcx
608 .dcx()
609 .struct_span_err(
610 no_mangle_span.unwrap_or_default(),
611 "`#[no_mangle]` cannot be used on internal language items",
612 )
613 .with_note("Rustc requires this item to have a specific mangled name.")
614 .with_span_label(tcx.def_span(did), "should be the internal language item");
615 if let Some(lang_item) = lang_item {
616 if let Some(link_name) = lang_item.link_name() {
617 err = err
618 .with_note("If you are trying to prevent mangling to ease debugging, many")
619 .with_note(format!(
620 "debuggers support a command such as `rbreak {link_name}` to"
621 ))
622 .with_note(format!(
623 "match `.*{link_name}.*` instead of `break {link_name}` on a specific name"
624 ))
625 }
626 }
627 err.emit();
628 }
629
630 if let Some(name) = &codegen_fn_attrs.link_name
634 && name.as_str().starts_with("llvm.")
635 {
636 codegen_fn_attrs.flags |= CodegenFnAttrFlags::NEVER_UNWIND;
637 }
638
639 if let Some(features) = check_tied_features(
640 tcx.sess,
641 &codegen_fn_attrs
642 .target_features
643 .iter()
644 .map(|features| (features.name.as_str(), true))
645 .collect(),
646 ) {
647 let span = tcx
648 .get_attrs(did, sym::target_feature)
649 .next()
650 .map_or_else(|| tcx.def_span(did), |a| a.span());
651 tcx.dcx()
652 .create_err(errors::TargetFeatureDisableOrEnable {
653 features,
654 span: Some(span),
655 missing_features: Some(errors::MissingFeatures),
656 })
657 .emit();
658 }
659
660 codegen_fn_attrs
661}
662
663pub fn check_tied_features(
666 sess: &Session,
667 features: &FxHashMap<&str, bool>,
668) -> Option<&'static [&'static str]> {
669 if !features.is_empty() {
670 for tied in sess.target.tied_target_features() {
671 let mut tied_iter = tied.iter();
673 let enabled = features.get(tied_iter.next().unwrap());
674 if tied_iter.any(|f| enabled != features.get(f)) {
675 return Some(tied);
676 }
677 }
678 }
679 None
680}
681
682fn should_inherit_track_caller(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
685 if let Some(impl_item) = tcx.opt_associated_item(def_id)
686 && let ty::AssocItemContainer::Impl = impl_item.container
687 && let Some(trait_item) = impl_item.trait_item_def_id
688 {
689 return tcx.codegen_fn_attrs(trait_item).flags.intersects(CodegenFnAttrFlags::TRACK_CALLER);
690 }
691
692 false
693}
694
695fn check_link_ordinal(tcx: TyCtxt<'_>, attr: &hir::Attribute) -> Option<u16> {
696 use rustc_ast::{LitIntType, LitKind, MetaItemLit};
697 let meta_item_list = attr.meta_item_list()?;
698 let [sole_meta_list] = &meta_item_list[..] else {
699 tcx.dcx().emit_err(errors::InvalidLinkOrdinalNargs { span: attr.span() });
700 return None;
701 };
702 if let Some(MetaItemLit { kind: LitKind::Int(ordinal, LitIntType::Unsuffixed), .. }) =
703 sole_meta_list.lit()
704 {
705 if *ordinal <= u16::MAX as u128 {
719 Some(ordinal.get() as u16)
720 } else {
721 let msg = format!("ordinal value in `link_ordinal` is too large: `{ordinal}`");
722 tcx.dcx()
723 .struct_span_err(attr.span(), msg)
724 .with_note("the value may not exceed `u16::MAX`")
725 .emit();
726 None
727 }
728 } else {
729 tcx.dcx().emit_err(errors::InvalidLinkOrdinalFormat { span: attr.span() });
730 None
731 }
732}
733
734fn check_link_name_xor_ordinal(
735 tcx: TyCtxt<'_>,
736 codegen_fn_attrs: &CodegenFnAttrs,
737 inline_span: Option<Span>,
738) {
739 if codegen_fn_attrs.link_name.is_none() || codegen_fn_attrs.link_ordinal.is_none() {
740 return;
741 }
742 let msg = "cannot use `#[link_name]` with `#[link_ordinal]`";
743 if let Some(span) = inline_span {
744 tcx.dcx().span_err(span, msg);
745 } else {
746 tcx.dcx().err(msg);
747 }
748}
749
750#[derive(Default)]
751struct MixedExportNameAndNoMangleState<'a> {
752 export_name: Option<Span>,
753 hir_id: Option<HirId>,
754 no_mangle: Option<Span>,
755 no_mangle_attr: Option<&'a hir::Attribute>,
756}
757
758impl<'a> MixedExportNameAndNoMangleState<'a> {
759 fn track_export_name(&mut self, span: Span) {
760 self.export_name = Some(span);
761 }
762
763 fn track_no_mangle(&mut self, span: Span, hir_id: HirId, attr_name: &'a hir::Attribute) {
764 self.no_mangle = Some(span);
765 self.hir_id = Some(hir_id);
766 self.no_mangle_attr = Some(attr_name);
767 }
768
769 fn lint_if_mixed(self, tcx: TyCtxt<'_>) {
771 if let Self {
772 export_name: Some(export_name),
773 no_mangle: Some(no_mangle),
774 hir_id: Some(hir_id),
775 no_mangle_attr: Some(_),
776 } = self
777 {
778 tcx.emit_node_span_lint(
779 lint::builtin::UNUSED_ATTRIBUTES,
780 hir_id,
781 no_mangle,
782 errors::MixedExportNameAndNoMangle {
783 no_mangle,
784 no_mangle_attr: "#[unsafe(no_mangle)]".to_string(),
785 export_name,
786 removal_span: no_mangle,
787 },
788 );
789 }
790 }
791}
792
793fn autodiff_attrs(tcx: TyCtxt<'_>, id: DefId) -> Option<AutoDiffAttrs> {
799 let attrs = tcx.get_attrs(id, sym::rustc_autodiff);
800
801 let attrs = attrs.filter(|attr| attr.has_name(sym::rustc_autodiff)).collect::<Vec<_>>();
802
803 let attr = match &attrs[..] {
806 [] => return None,
807 [attr] => attr,
808 _ => {
809 span_bug!(attrs[1].span(), "cg_ssa: rustc_autodiff should only exist once per source");
810 }
811 };
812
813 let list = attr.meta_item_list().unwrap_or_default();
814
815 if list.is_empty() {
817 return Some(AutoDiffAttrs::source());
818 }
819
820 let [mode, width_meta, input_activities @ .., ret_activity] = &list[..] else {
821 span_bug!(attr.span(), "rustc_autodiff attribute must contain mode, width and activities");
822 };
823 let mode = if let MetaItemInner::MetaItem(MetaItem { path: p1, .. }) = mode {
824 p1.segments.first().unwrap().ident
825 } else {
826 span_bug!(attr.span(), "rustc_autodiff attribute must contain mode");
827 };
828
829 let mode = match mode.as_str() {
831 "Forward" => DiffMode::Forward,
832 "Reverse" => DiffMode::Reverse,
833 _ => {
834 span_bug!(mode.span, "rustc_autodiff attribute contains invalid mode");
835 }
836 };
837
838 let width: u32 = match width_meta {
839 MetaItemInner::MetaItem(MetaItem { path: p1, .. }) => {
840 let w = p1.segments.first().unwrap().ident;
841 match w.as_str().parse() {
842 Ok(val) => val,
843 Err(_) => {
844 span_bug!(w.span, "rustc_autodiff width should fit u32");
845 }
846 }
847 }
848 MetaItemInner::Lit(lit) => {
849 if let LitKind::Int(val, _) = lit.kind {
850 match val.get().try_into() {
851 Ok(val) => val,
852 Err(_) => {
853 span_bug!(lit.span, "rustc_autodiff width should fit u32");
854 }
855 }
856 } else {
857 span_bug!(lit.span, "rustc_autodiff width should be an integer");
858 }
859 }
860 };
861
862 let ret_symbol = if let MetaItemInner::MetaItem(MetaItem { path: p1, .. }) = ret_activity {
864 p1.segments.first().unwrap().ident
865 } else {
866 span_bug!(attr.span(), "rustc_autodiff attribute must contain the return activity");
867 };
868
869 let Ok(ret_activity) = DiffActivity::from_str(ret_symbol.as_str()) else {
871 span_bug!(ret_symbol.span, "invalid return activity");
872 };
873
874 let mut arg_activities: Vec<DiffActivity> = vec![];
876 for arg in input_activities {
877 let arg_symbol = if let MetaItemInner::MetaItem(MetaItem { path: p2, .. }) = arg {
878 match p2.segments.first() {
879 Some(x) => x.ident,
880 None => {
881 span_bug!(
882 arg.span(),
883 "rustc_autodiff attribute must contain the input activity"
884 );
885 }
886 }
887 } else {
888 span_bug!(arg.span(), "rustc_autodiff attribute must contain the input activity");
889 };
890
891 match DiffActivity::from_str(arg_symbol.as_str()) {
892 Ok(arg_activity) => arg_activities.push(arg_activity),
893 Err(_) => {
894 span_bug!(arg_symbol.span, "invalid input activity");
895 }
896 }
897 }
898
899 Some(AutoDiffAttrs { mode, width, ret_activity, input_activity: arg_activities })
900}
901
902pub(crate) fn provide(providers: &mut Providers) {
903 *providers = Providers { codegen_fn_attrs, should_inherit_track_caller, ..*providers };
904}