rustc_codegen_ssa/
codegen_attrs.rs

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::{
7    AttributeKind, InlineAttr, InstructionSetAttr, OptimizeAttr, ReprAttr, UsedBy, find_attr,
8};
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, 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::lint;
21use rustc_session::parse::feature_err;
22use rustc_span::{Ident, Span, sym};
23use rustc_target::spec::SanitizerSet;
24
25use crate::errors;
26use crate::errors::NoMangleNameless;
27use crate::target_features::{
28    check_target_feature_trait_unsafe, check_tied_features, from_target_feature_attr,
29};
30
31fn linkage_by_name(tcx: TyCtxt<'_>, def_id: LocalDefId, name: &str) -> Linkage {
32    use rustc_middle::mir::mono::Linkage::*;
33
34    // Use the names from src/llvm/docs/LangRef.rst here. Most types are only
35    // applicable to variable declarations and may not really make sense for
36    // Rust code in the first place but allow them anyway and trust that the
37    // user knows what they're doing. Who knows, unanticipated use cases may pop
38    // up in the future.
39    //
40    // ghost, dllimport, dllexport and linkonce_odr_autohide are not supported
41    // and don't have to be, LLVM treats them as no-ops.
42    match name {
43        "available_externally" => AvailableExternally,
44        "common" => Common,
45        "extern_weak" => ExternalWeak,
46        "external" => External,
47        "internal" => Internal,
48        "linkonce" => LinkOnceAny,
49        "linkonce_odr" => LinkOnceODR,
50        "weak" => WeakAny,
51        "weak_odr" => WeakODR,
52        _ => tcx.dcx().span_fatal(tcx.def_span(def_id), "invalid linkage specified"),
53    }
54}
55
56fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
57    if cfg!(debug_assertions) {
58        let def_kind = tcx.def_kind(did);
59        assert!(
60            def_kind.has_codegen_attrs(),
61            "unexpected `def_kind` in `codegen_fn_attrs`: {def_kind:?}",
62        );
63    }
64
65    let attrs = tcx.hir_attrs(tcx.local_def_id_to_hir_id(did));
66    let mut codegen_fn_attrs = CodegenFnAttrs::new();
67    if tcx.should_inherit_track_caller(did) {
68        codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER;
69    }
70
71    // If our rustc version supports autodiff/enzyme, then we call our handler
72    // to check for any `#[rustc_autodiff(...)]` attributes.
73    if cfg!(llvm_enzyme) {
74        let ad = autodiff_attrs(tcx, did.into());
75        codegen_fn_attrs.autodiff_item = ad;
76    }
77
78    // When `no_builtins` is applied at the crate level, we should add the
79    // `no-builtins` attribute to each function to ensure it takes effect in LTO.
80    let crate_attrs = tcx.hir_attrs(rustc_hir::CRATE_HIR_ID);
81    let no_builtins = attr::contains_name(crate_attrs, sym::no_builtins);
82    if no_builtins {
83        codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_BUILTINS;
84    }
85
86    let rust_target_features = tcx.rust_target_features(LOCAL_CRATE);
87
88    let mut link_ordinal_span = None;
89    let mut no_sanitize_span = None;
90
91    for attr in attrs.iter() {
92        // In some cases, attribute are only valid on functions, but it's the `check_attr`
93        // pass that check that they aren't used anywhere else, rather this module.
94        // In these cases, we bail from performing further checks that are only meaningful for
95        // functions (such as calling `fn_sig`, which ICEs if given a non-function). We also
96        // report a delayed bug, just in case `check_attr` isn't doing its job.
97        let fn_sig = |attr_span| {
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()
105                    .span_delayed_bug(attr_span, "this attribute can only be applied to functions");
106                None
107            }
108        };
109
110        if let hir::Attribute::Parsed(p) = attr {
111            match p {
112                AttributeKind::Repr(reprs) => {
113                    codegen_fn_attrs.alignment = reprs
114                        .iter()
115                        .filter_map(
116                            |(r, _)| if let ReprAttr::ReprAlign(x) = r { Some(*x) } else { None },
117                        )
118                        .max();
119                }
120                AttributeKind::Cold(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::COLD,
121                AttributeKind::ExportName { name, .. } => {
122                    codegen_fn_attrs.export_name = Some(*name);
123                }
124                AttributeKind::Naked(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED,
125                AttributeKind::Align { align, .. } => codegen_fn_attrs.alignment = Some(*align),
126                AttributeKind::LinkName { name, .. } => codegen_fn_attrs.link_name = Some(*name),
127                AttributeKind::NoMangle(attr_span) => {
128                    if tcx.opt_item_name(did.to_def_id()).is_some() {
129                        codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE;
130                    } else {
131                        tcx.dcx().emit_err(NoMangleNameless {
132                            span: *attr_span,
133                            definition: format!(
134                                "{} {}",
135                                tcx.def_descr_article(did.to_def_id()),
136                                tcx.def_descr(did.to_def_id())
137                            ),
138                        });
139                    }
140                }
141                AttributeKind::TrackCaller(attr_span) => {
142                    let is_closure = tcx.is_closure_like(did.to_def_id());
143
144                    if !is_closure
145                        && let Some(fn_sig) = fn_sig(*attr_span)
146                        && fn_sig.skip_binder().abi() != ExternAbi::Rust
147                    {
148                        tcx.dcx().emit_err(errors::RequiresRustAbi { span: *attr_span });
149                    }
150                    if is_closure
151                        && !tcx.features().closure_track_caller()
152                        && !attr_span.allows_unstable(sym::closure_track_caller)
153                    {
154                        feature_err(
155                            &tcx.sess,
156                            sym::closure_track_caller,
157                            *attr_span,
158                            "`#[track_caller]` on closures is currently unstable",
159                        )
160                        .emit();
161                    }
162                    codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER
163                }
164                AttributeKind::Used { used_by, .. } => match used_by {
165                    UsedBy::Compiler => codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED_COMPILER,
166                    UsedBy::Linker => codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED_LINKER,
167                },
168                _ => {}
169            }
170        }
171
172        let Some(Ident { name, .. }) = attr.ident() else {
173            continue;
174        };
175
176        match name {
177            sym::rustc_allocator => codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR,
178            sym::ffi_pure => codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_PURE,
179            sym::ffi_const => codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_CONST,
180            sym::rustc_nounwind => codegen_fn_attrs.flags |= CodegenFnAttrFlags::NEVER_UNWIND,
181            sym::rustc_reallocator => codegen_fn_attrs.flags |= CodegenFnAttrFlags::REALLOCATOR,
182            sym::rustc_deallocator => codegen_fn_attrs.flags |= CodegenFnAttrFlags::DEALLOCATOR,
183            sym::rustc_allocator_zeroed => {
184                codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR_ZEROED
185            }
186            sym::rustc_std_internal_symbol => {
187                codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL
188            }
189            sym::thread_local => codegen_fn_attrs.flags |= CodegenFnAttrFlags::THREAD_LOCAL,
190            sym::target_feature => {
191                let Some(sig) = tcx.hir_node_by_def_id(did).fn_sig() else {
192                    tcx.dcx().span_delayed_bug(attr.span(), "target_feature applied to non-fn");
193                    continue;
194                };
195                let safe_target_features =
196                    matches!(sig.header.safety, hir::HeaderSafety::SafeTargetFeatures);
197                codegen_fn_attrs.safe_target_features = safe_target_features;
198                if safe_target_features {
199                    if tcx.sess.target.is_like_wasm || tcx.sess.opts.actually_rustdoc {
200                        // The `#[target_feature]` attribute is allowed on
201                        // WebAssembly targets on all functions. Prior to stabilizing
202                        // the `target_feature_11` feature, `#[target_feature]` was
203                        // only permitted on unsafe functions because on most targets
204                        // execution of instructions that are not supported is
205                        // considered undefined behavior. For WebAssembly which is a
206                        // 100% safe target at execution time it's not possible to
207                        // execute undefined instructions, and even if a future
208                        // feature was added in some form for this it would be a
209                        // deterministic trap. There is no undefined behavior when
210                        // executing WebAssembly so `#[target_feature]` is allowed
211                        // on safe functions (but again, only for WebAssembly)
212                        //
213                        // Note that this is also allowed if `actually_rustdoc` so
214                        // if a target is documenting some wasm-specific code then
215                        // it's not spuriously denied.
216                        //
217                        // Now that `#[target_feature]` is permitted on safe functions,
218                        // this exception must still exist for allowing the attribute on
219                        // `main`, `start`, and other functions that are not usually
220                        // allowed.
221                    } else {
222                        check_target_feature_trait_unsafe(tcx, did, attr.span());
223                    }
224                }
225                from_target_feature_attr(
226                    tcx,
227                    did,
228                    attr,
229                    rust_target_features,
230                    &mut codegen_fn_attrs.target_features,
231                );
232            }
233            sym::linkage => {
234                if let Some(val) = attr.value_str() {
235                    let linkage = Some(linkage_by_name(tcx, did, val.as_str()));
236                    if tcx.is_foreign_item(did) {
237                        codegen_fn_attrs.import_linkage = linkage;
238
239                        if tcx.is_mutable_static(did.into()) {
240                            let mut diag = tcx.dcx().struct_span_err(
241                                attr.span(),
242                                "extern mutable statics are not allowed with `#[linkage]`",
243                            );
244                            diag.note(
245                                "marking the extern static mutable would allow changing which \
246                                 symbol the static references rather than make the target of the \
247                                 symbol mutable",
248                            );
249                            diag.emit();
250                        }
251                    } else {
252                        codegen_fn_attrs.linkage = linkage;
253                    }
254                }
255            }
256            sym::link_section => {
257                if let Some(val) = attr.value_str() {
258                    if val.as_str().bytes().any(|b| b == 0) {
259                        let msg = format!("illegal null byte in link_section value: `{val}`");
260                        tcx.dcx().span_err(attr.span(), msg);
261                    } else {
262                        codegen_fn_attrs.link_section = Some(val);
263                    }
264                }
265            }
266            sym::link_ordinal => {
267                link_ordinal_span = Some(attr.span());
268                if let ordinal @ Some(_) = check_link_ordinal(tcx, attr) {
269                    codegen_fn_attrs.link_ordinal = ordinal;
270                }
271            }
272            sym::no_sanitize => {
273                no_sanitize_span = Some(attr.span());
274                if let Some(list) = attr.meta_item_list() {
275                    for item in list.iter() {
276                        match item.name() {
277                            Some(sym::address) => {
278                                codegen_fn_attrs.no_sanitize |=
279                                    SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS
280                            }
281                            Some(sym::cfi) => codegen_fn_attrs.no_sanitize |= SanitizerSet::CFI,
282                            Some(sym::kcfi) => codegen_fn_attrs.no_sanitize |= SanitizerSet::KCFI,
283                            Some(sym::memory) => {
284                                codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMORY
285                            }
286                            Some(sym::memtag) => {
287                                codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMTAG
288                            }
289                            Some(sym::shadow_call_stack) => {
290                                codegen_fn_attrs.no_sanitize |= SanitizerSet::SHADOWCALLSTACK
291                            }
292                            Some(sym::thread) => {
293                                codegen_fn_attrs.no_sanitize |= SanitizerSet::THREAD
294                            }
295                            Some(sym::hwaddress) => {
296                                codegen_fn_attrs.no_sanitize |= SanitizerSet::HWADDRESS
297                            }
298                            _ => {
299                                tcx.dcx().emit_err(errors::InvalidNoSanitize { span: item.span() });
300                            }
301                        }
302                    }
303                }
304            }
305            sym::instruction_set => {
306                codegen_fn_attrs.instruction_set =
307                    attr.meta_item_list().and_then(|l| match &l[..] {
308                        [MetaItemInner::MetaItem(set)] => {
309                            let segments =
310                                set.path.segments.iter().map(|x| x.ident.name).collect::<Vec<_>>();
311                            match segments.as_slice() {
312                                [sym::arm, sym::a32 | sym::t32]
313                                    if !tcx.sess.target.has_thumb_interworking =>
314                                {
315                                    tcx.dcx().emit_err(errors::UnsupportedInstructionSet {
316                                        span: attr.span(),
317                                    });
318                                    None
319                                }
320                                [sym::arm, sym::a32] => Some(InstructionSetAttr::ArmA32),
321                                [sym::arm, sym::t32] => Some(InstructionSetAttr::ArmT32),
322                                _ => {
323                                    tcx.dcx().emit_err(errors::InvalidInstructionSet {
324                                        span: attr.span(),
325                                    });
326                                    None
327                                }
328                            }
329                        }
330                        [] => {
331                            tcx.dcx().emit_err(errors::BareInstructionSet { span: attr.span() });
332                            None
333                        }
334                        _ => {
335                            tcx.dcx()
336                                .emit_err(errors::MultipleInstructionSet { span: attr.span() });
337                            None
338                        }
339                    })
340            }
341            sym::patchable_function_entry => {
342                codegen_fn_attrs.patchable_function_entry = attr.meta_item_list().and_then(|l| {
343                    let mut prefix = None;
344                    let mut entry = None;
345                    for item in l {
346                        let Some(meta_item) = item.meta_item() else {
347                            tcx.dcx().emit_err(errors::ExpectedNameValuePair { span: item.span() });
348                            continue;
349                        };
350
351                        let Some(name_value_lit) = meta_item.name_value_literal() else {
352                            tcx.dcx().emit_err(errors::ExpectedNameValuePair { span: item.span() });
353                            continue;
354                        };
355
356                        let attrib_to_write = match meta_item.name() {
357                            Some(sym::prefix_nops) => &mut prefix,
358                            Some(sym::entry_nops) => &mut entry,
359                            _ => {
360                                tcx.dcx().emit_err(errors::UnexpectedParameterName {
361                                    span: item.span(),
362                                    prefix_nops: sym::prefix_nops,
363                                    entry_nops: sym::entry_nops,
364                                });
365                                continue;
366                            }
367                        };
368
369                        let rustc_ast::LitKind::Int(val, _) = name_value_lit.kind else {
370                            tcx.dcx().emit_err(errors::InvalidLiteralValue {
371                                span: name_value_lit.span,
372                            });
373                            continue;
374                        };
375
376                        let Ok(val) = val.get().try_into() else {
377                            tcx.dcx()
378                                .emit_err(errors::OutOfRangeInteger { span: name_value_lit.span });
379                            continue;
380                        };
381
382                        *attrib_to_write = Some(val);
383                    }
384
385                    if let (None, None) = (prefix, entry) {
386                        tcx.dcx().span_err(attr.span(), "must specify at least one parameter");
387                    }
388
389                    Some(PatchableFunctionEntry::from_prefix_and_entry(
390                        prefix.unwrap_or(0),
391                        entry.unwrap_or(0),
392                    ))
393                })
394            }
395            _ => {}
396        }
397    }
398
399    // Apply the minimum function alignment here. This ensures that a function's alignment is
400    // determined by the `-C` flags of the crate it is defined in, not the `-C` flags of the crate
401    // it happens to be codegen'd (or const-eval'd) in.
402    codegen_fn_attrs.alignment =
403        Ord::max(codegen_fn_attrs.alignment, tcx.sess.opts.unstable_opts.min_function_alignment);
404
405    let inline_span;
406    (codegen_fn_attrs.inline, inline_span) = if let Some((inline_attr, span)) =
407        find_attr!(attrs, AttributeKind::Inline(i, span) => (*i, *span))
408    {
409        (inline_attr, Some(span))
410    } else {
411        (InlineAttr::None, None)
412    };
413
414    // naked function MUST NOT be inlined! This attribute is required for the rust compiler itself,
415    // but not for the code generation backend because at that point the naked function will just be
416    // a declaration, with a definition provided in global assembly.
417    if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) {
418        codegen_fn_attrs.inline = InlineAttr::Never;
419    }
420
421    codegen_fn_attrs.optimize =
422        find_attr!(attrs, AttributeKind::Optimize(i, _) => *i).unwrap_or(OptimizeAttr::Default);
423
424    // #73631: closures inherit `#[target_feature]` annotations
425    //
426    // If this closure is marked `#[inline(always)]`, simply skip adding `#[target_feature]`.
427    //
428    // At this point, `unsafe` has already been checked and `#[target_feature]` only affects codegen.
429    // Due to LLVM limitations, emitting both `#[inline(always)]` and `#[target_feature]` is *unsound*:
430    // the function may be inlined into a caller with fewer target features. Also see
431    // <https://github.com/rust-lang/rust/issues/116573>.
432    //
433    // Using `#[inline(always)]` implies that this closure will most likely be inlined into
434    // its parent function, which effectively inherits the features anyway. Boxing this closure
435    // would result in this closure being compiled without the inherited target features, but this
436    // is probably a poor usage of `#[inline(always)]` and easily avoided by not using the attribute.
437    if tcx.is_closure_like(did.to_def_id()) && codegen_fn_attrs.inline != InlineAttr::Always {
438        let owner_id = tcx.parent(did.to_def_id());
439        if tcx.def_kind(owner_id).has_codegen_attrs() {
440            codegen_fn_attrs
441                .target_features
442                .extend(tcx.codegen_fn_attrs(owner_id).target_features.iter().copied());
443        }
444    }
445
446    // If a function uses `#[target_feature]` it can't be inlined into general
447    // purpose functions as they wouldn't have the right target features
448    // enabled. For that reason we also forbid `#[inline(always)]` as it can't be
449    // respected.
450    //
451    // `#[rustc_force_inline]` doesn't need to be prohibited here, only
452    // `#[inline(always)]`, as forced inlining is implemented entirely within
453    // rustc (and so the MIR inliner can do any necessary checks for compatible target
454    // features).
455    //
456    // This sidesteps the LLVM blockers in enabling `target_features` +
457    // `inline(always)` to be used together (see rust-lang/rust#116573 and
458    // llvm/llvm-project#70563).
459    if !codegen_fn_attrs.target_features.is_empty()
460        && matches!(codegen_fn_attrs.inline, InlineAttr::Always)
461        && let Some(span) = inline_span
462    {
463        tcx.dcx().span_err(span, "cannot use `#[inline(always)]` with `#[target_feature]`");
464    }
465
466    if !codegen_fn_attrs.no_sanitize.is_empty()
467        && codegen_fn_attrs.inline.always()
468        && let (Some(no_sanitize_span), Some(inline_span)) = (no_sanitize_span, inline_span)
469    {
470        let hir_id = tcx.local_def_id_to_hir_id(did);
471        tcx.node_span_lint(lint::builtin::INLINE_NO_SANITIZE, hir_id, no_sanitize_span, |lint| {
472            lint.primary_message("`no_sanitize` will have no effect after inlining");
473            lint.span_note(inline_span, "inlining requested here");
474        })
475    }
476
477    // Weak lang items have the same semantics as "std internal" symbols in the
478    // sense that they're preserved through all our LTO passes and only
479    // strippable by the linker.
480    //
481    // Additionally weak lang items have predetermined symbol names.
482    if let Some((name, _)) = lang_items::extract(attrs)
483        && let Some(lang_item) = LangItem::from_name(name)
484    {
485        if WEAK_LANG_ITEMS.contains(&lang_item) {
486            codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL;
487        }
488        if let Some(link_name) = lang_item.link_name() {
489            codegen_fn_attrs.export_name = Some(link_name);
490            codegen_fn_attrs.link_name = Some(link_name);
491        }
492    }
493    check_link_name_xor_ordinal(tcx, &codegen_fn_attrs, link_ordinal_span);
494
495    if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL)
496        && codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NO_MANGLE)
497    {
498        let no_mangle_span =
499            find_attr!(attrs, AttributeKind::NoMangle(no_mangle_span) => *no_mangle_span)
500                .unwrap_or_default();
501        let lang_item =
502            lang_items::extract(attrs).map_or(None, |(name, _span)| LangItem::from_name(name));
503        let mut err = tcx
504            .dcx()
505            .struct_span_err(
506                no_mangle_span,
507                "`#[no_mangle]` cannot be used on internal language items",
508            )
509            .with_note("Rustc requires this item to have a specific mangled name.")
510            .with_span_label(tcx.def_span(did), "should be the internal language item");
511        if let Some(lang_item) = lang_item {
512            if let Some(link_name) = lang_item.link_name() {
513                err = err
514                    .with_note("If you are trying to prevent mangling to ease debugging, many")
515                    .with_note(format!(
516                        "debuggers support a command such as `rbreak {link_name}` to"
517                    ))
518                    .with_note(format!(
519                        "match `.*{link_name}.*` instead of `break {link_name}` on a specific name"
520                    ))
521            }
522        }
523        err.emit();
524    }
525
526    // Any linkage to LLVM intrinsics for now forcibly marks them all as never
527    // unwinds since LLVM sometimes can't handle codegen which `invoke`s
528    // intrinsic functions.
529    if let Some(name) = &codegen_fn_attrs.link_name
530        && name.as_str().starts_with("llvm.")
531    {
532        codegen_fn_attrs.flags |= CodegenFnAttrFlags::NEVER_UNWIND;
533    }
534
535    if let Some(features) = check_tied_features(
536        tcx.sess,
537        &codegen_fn_attrs
538            .target_features
539            .iter()
540            .map(|features| (features.name.as_str(), true))
541            .collect(),
542    ) {
543        let span = tcx
544            .get_attrs(did, sym::target_feature)
545            .next()
546            .map_or_else(|| tcx.def_span(did), |a| a.span());
547        tcx.dcx()
548            .create_err(errors::TargetFeatureDisableOrEnable {
549                features,
550                span: Some(span),
551                missing_features: Some(errors::MissingFeatures),
552            })
553            .emit();
554    }
555
556    codegen_fn_attrs
557}
558
559/// Checks if the provided DefId is a method in a trait impl for a trait which has track_caller
560/// applied to the method prototype.
561fn should_inherit_track_caller(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
562    if let Some(impl_item) = tcx.opt_associated_item(def_id)
563        && let ty::AssocItemContainer::Impl = impl_item.container
564        && let Some(trait_item) = impl_item.trait_item_def_id
565    {
566        return tcx.codegen_fn_attrs(trait_item).flags.intersects(CodegenFnAttrFlags::TRACK_CALLER);
567    }
568
569    false
570}
571
572fn check_link_ordinal(tcx: TyCtxt<'_>, attr: &hir::Attribute) -> Option<u16> {
573    use rustc_ast::{LitIntType, LitKind, MetaItemLit};
574    let meta_item_list = attr.meta_item_list()?;
575    let [sole_meta_list] = &meta_item_list[..] else {
576        tcx.dcx().emit_err(errors::InvalidLinkOrdinalNargs { span: attr.span() });
577        return None;
578    };
579    if let Some(MetaItemLit { kind: LitKind::Int(ordinal, LitIntType::Unsuffixed), .. }) =
580        sole_meta_list.lit()
581    {
582        // According to the table at
583        // https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#import-header, the
584        // ordinal must fit into 16 bits. Similarly, the Ordinal field in COFFShortExport (defined
585        // in llvm/include/llvm/Object/COFFImportFile.h), which we use to communicate import
586        // information to LLVM for `#[link(kind = "raw-dylib"_])`, is also defined to be uint16_t.
587        //
588        // FIXME: should we allow an ordinal of 0?  The MSVC toolchain has inconsistent support for
589        // this: both LINK.EXE and LIB.EXE signal errors and abort when given a .DEF file that
590        // specifies a zero ordinal. However, llvm-dlltool is perfectly happy to generate an import
591        // library for such a .DEF file, and MSVC's LINK.EXE is also perfectly happy to consume an
592        // import library produced by LLVM with an ordinal of 0, and it generates an .EXE.  (I
593        // don't know yet if the resulting EXE runs, as I haven't yet built the necessary DLL --
594        // see earlier comment about LINK.EXE failing.)
595        if *ordinal <= u16::MAX as u128 {
596            Some(ordinal.get() as u16)
597        } else {
598            let msg = format!("ordinal value in `link_ordinal` is too large: `{ordinal}`");
599            tcx.dcx()
600                .struct_span_err(attr.span(), msg)
601                .with_note("the value may not exceed `u16::MAX`")
602                .emit();
603            None
604        }
605    } else {
606        tcx.dcx().emit_err(errors::InvalidLinkOrdinalFormat { span: attr.span() });
607        None
608    }
609}
610
611fn check_link_name_xor_ordinal(
612    tcx: TyCtxt<'_>,
613    codegen_fn_attrs: &CodegenFnAttrs,
614    inline_span: Option<Span>,
615) {
616    if codegen_fn_attrs.link_name.is_none() || codegen_fn_attrs.link_ordinal.is_none() {
617        return;
618    }
619    let msg = "cannot use `#[link_name]` with `#[link_ordinal]`";
620    if let Some(span) = inline_span {
621        tcx.dcx().span_err(span, msg);
622    } else {
623        tcx.dcx().err(msg);
624    }
625}
626
627/// We now check the #\[rustc_autodiff\] attributes which we generated from the #[autodiff(...)]
628/// macros. There are two forms. The pure one without args to mark primal functions (the functions
629/// being differentiated). The other form is #[rustc_autodiff(Mode, ActivityList)] on top of the
630/// placeholder functions. We wrote the rustc_autodiff attributes ourself, so this should never
631/// panic, unless we introduced a bug when parsing the autodiff macro.
632fn autodiff_attrs(tcx: TyCtxt<'_>, id: DefId) -> Option<AutoDiffAttrs> {
633    let attrs = tcx.get_attrs(id, sym::rustc_autodiff);
634
635    let attrs = attrs.filter(|attr| attr.has_name(sym::rustc_autodiff)).collect::<Vec<_>>();
636
637    // check for exactly one autodiff attribute on placeholder functions.
638    // There should only be one, since we generate a new placeholder per ad macro.
639    let attr = match &attrs[..] {
640        [] => return None,
641        [attr] => attr,
642        _ => {
643            span_bug!(attrs[1].span(), "cg_ssa: rustc_autodiff should only exist once per source");
644        }
645    };
646
647    let list = attr.meta_item_list().unwrap_or_default();
648
649    // empty autodiff attribute macros (i.e. `#[autodiff]`) are used to mark source functions
650    if list.is_empty() {
651        return Some(AutoDiffAttrs::source());
652    }
653
654    let [mode, width_meta, input_activities @ .., ret_activity] = &list[..] else {
655        span_bug!(attr.span(), "rustc_autodiff attribute must contain mode, width and activities");
656    };
657    let mode = if let MetaItemInner::MetaItem(MetaItem { path: p1, .. }) = mode {
658        p1.segments.first().unwrap().ident
659    } else {
660        span_bug!(attr.span(), "rustc_autodiff attribute must contain mode");
661    };
662
663    // parse mode
664    let mode = match mode.as_str() {
665        "Forward" => DiffMode::Forward,
666        "Reverse" => DiffMode::Reverse,
667        _ => {
668            span_bug!(mode.span, "rustc_autodiff attribute contains invalid mode");
669        }
670    };
671
672    let width: u32 = match width_meta {
673        MetaItemInner::MetaItem(MetaItem { path: p1, .. }) => {
674            let w = p1.segments.first().unwrap().ident;
675            match w.as_str().parse() {
676                Ok(val) => val,
677                Err(_) => {
678                    span_bug!(w.span, "rustc_autodiff width should fit u32");
679                }
680            }
681        }
682        MetaItemInner::Lit(lit) => {
683            if let LitKind::Int(val, _) = lit.kind {
684                match val.get().try_into() {
685                    Ok(val) => val,
686                    Err(_) => {
687                        span_bug!(lit.span, "rustc_autodiff width should fit u32");
688                    }
689                }
690            } else {
691                span_bug!(lit.span, "rustc_autodiff width should be an integer");
692            }
693        }
694    };
695
696    // First read the ret symbol from the attribute
697    let ret_symbol = if let MetaItemInner::MetaItem(MetaItem { path: p1, .. }) = ret_activity {
698        p1.segments.first().unwrap().ident
699    } else {
700        span_bug!(attr.span(), "rustc_autodiff attribute must contain the return activity");
701    };
702
703    // Then parse it into an actual DiffActivity
704    let Ok(ret_activity) = DiffActivity::from_str(ret_symbol.as_str()) else {
705        span_bug!(ret_symbol.span, "invalid return activity");
706    };
707
708    // Now parse all the intermediate (input) activities
709    let mut arg_activities: Vec<DiffActivity> = vec![];
710    for arg in input_activities {
711        let arg_symbol = if let MetaItemInner::MetaItem(MetaItem { path: p2, .. }) = arg {
712            match p2.segments.first() {
713                Some(x) => x.ident,
714                None => {
715                    span_bug!(
716                        arg.span(),
717                        "rustc_autodiff attribute must contain the input activity"
718                    );
719                }
720            }
721        } else {
722            span_bug!(arg.span(), "rustc_autodiff attribute must contain the input activity");
723        };
724
725        match DiffActivity::from_str(arg_symbol.as_str()) {
726            Ok(arg_activity) => arg_activities.push(arg_activity),
727            Err(_) => {
728                span_bug!(arg_symbol.span, "invalid input activity");
729            }
730        }
731    }
732
733    Some(AutoDiffAttrs { mode, width, ret_activity, input_activity: arg_activities })
734}
735
736pub(crate) fn provide(providers: &mut Providers) {
737    *providers = Providers { codegen_fn_attrs, should_inherit_track_caller, ..*providers };
738}