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