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::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    // Use the names from src/llvm/docs/LangRef.rst here. Most types are only
33    // applicable to variable declarations and may not really make sense for
34    // Rust code in the first place but allow them anyway and trust that the
35    // user knows what they're doing. Who knows, unanticipated use cases may pop
36    // up in the future.
37    //
38    // ghost, dllimport, dllexport and linkonce_odr_autohide are not supported
39    // and don't have to be, LLVM treats them as no-ops.
40    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 our rustc version supports autodiff/enzyme, then we call our handler
70    // to check for any `#[rustc_autodiff(...)]` attributes.
71    if cfg!(llvm_enzyme) {
72        let ad = autodiff_attrs(tcx, did.into());
73        codegen_fn_attrs.autodiff_item = ad;
74    }
75
76    // When `no_builtins` is applied at the crate level, we should add the
77    // `no-builtins` attribute to each function to ensure it takes effect in LTO.
78    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        // 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(|(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                        // Unconditionally using `llvm.used` causes issues in handling
199                        // `.init_array` with the gold linker. Luckily gold has been
200                        // deprecated with GCC 15 and rustc now warns about using gold.
201                        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                        // `#[export_name = ...]` will be converted to a null-terminated string,
233                        // so it may not contain any null characters.
234                        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                        // The `#[target_feature]` attribute is allowed on
251                        // WebAssembly targets on all functions. Prior to stabilizing
252                        // the `target_feature_11` feature, `#[target_feature]` was
253                        // only permitted on unsafe functions because on most targets
254                        // execution of instructions that are not supported is
255                        // considered undefined behavior. For WebAssembly which is a
256                        // 100% safe target at execution time it's not possible to
257                        // execute undefined instructions, and even if a future
258                        // feature was added in some form for this it would be a
259                        // deterministic trap. There is no undefined behavior when
260                        // executing WebAssembly so `#[target_feature]` is allowed
261                        // on safe functions (but again, only for WebAssembly)
262                        //
263                        // Note that this is also allowed if `actually_rustdoc` so
264                        // if a target is documenting some wasm-specific code then
265                        // it's not spuriously denied.
266                        //
267                        // Now that `#[target_feature]` is permitted on safe functions,
268                        // this exception must still exist for allowing the attribute on
269                        // `main`, `start`, and other functions that are not usually
270                        // allowed.
271                    } 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    // naked function MUST NOT be inlined! This attribute is required for the rust compiler itself,
496    // but not for the code generation backend because at that point the naked function will just be
497    // a declaration, with a definition provided in global assembly.
498    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    // #73631: closures inherit `#[target_feature]` annotations
532    //
533    // If this closure is marked `#[inline(always)]`, simply skip adding `#[target_feature]`.
534    //
535    // At this point, `unsafe` has already been checked and `#[target_feature]` only affects codegen.
536    // Due to LLVM limitations, emitting both `#[inline(always)]` and `#[target_feature]` is *unsound*:
537    // the function may be inlined into a caller with fewer target features. Also see
538    // <https://github.com/rust-lang/rust/issues/116573>.
539    //
540    // Using `#[inline(always)]` implies that this closure will most likely be inlined into
541    // its parent function, which effectively inherits the features anyway. Boxing this closure
542    // would result in this closure being compiled without the inherited target features, but this
543    // is probably a poor usage of `#[inline(always)]` and easily avoided by not using the attribute.
544    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 a function uses `#[target_feature]` it can't be inlined into general
554    // purpose functions as they wouldn't have the right target features
555    // enabled. For that reason we also forbid `#[inline(always)]` as it can't be
556    // respected.
557    //
558    // `#[rustc_force_inline]` doesn't need to be prohibited here, only
559    // `#[inline(always)]`, as forced inlining is implemented entirely within
560    // rustc (and so the MIR inliner can do any necessary checks for compatible target
561    // features).
562    //
563    // This sidesteps the LLVM blockers in enabling `target_features` +
564    // `inline(always)` to be used together (see rust-lang/rust#116573 and
565    // llvm/llvm-project#70563).
566    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    // Weak lang items have the same semantics as "std internal" symbols in the
585    // sense that they're preserved through all our LTO passes and only
586    // strippable by the linker.
587    //
588    // Additionally weak lang items have predetermined symbol names.
589    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    // Any linkage to LLVM intrinsics for now forcibly marks them all as never
631    // unwinds since LLVM sometimes can't handle codegen which `invoke`s
632    // intrinsic functions.
633    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
663/// Given a map from target_features to whether they are enabled or disabled, ensure only valid
664/// combinations are allowed.
665pub 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            // Tied features must be set to the same value, or not set at all
672            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
682/// Checks if the provided DefId is a method in a trait impl for a trait which has track_caller
683/// applied to the method prototype.
684fn 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        // According to the table at
706        // https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#import-header, the
707        // ordinal must fit into 16 bits. Similarly, the Ordinal field in COFFShortExport (defined
708        // in llvm/include/llvm/Object/COFFImportFile.h), which we use to communicate import
709        // information to LLVM for `#[link(kind = "raw-dylib"_])`, is also defined to be uint16_t.
710        //
711        // FIXME: should we allow an ordinal of 0?  The MSVC toolchain has inconsistent support for
712        // this: both LINK.EXE and LIB.EXE signal errors and abort when given a .DEF file that
713        // specifies a zero ordinal. However, llvm-dlltool is perfectly happy to generate an import
714        // library for such a .DEF file, and MSVC's LINK.EXE is also perfectly happy to consume an
715        // import library produced by LLVM with an ordinal of 0, and it generates an .EXE.  (I
716        // don't know yet if the resulting EXE runs, as I haven't yet built the necessary DLL --
717        // see earlier comment about LINK.EXE failing.)
718        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    /// Emit diagnostics if the lint condition is met.
770    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
793/// We now check the #\[rustc_autodiff\] attributes which we generated from the #[autodiff(...)]
794/// macros. There are two forms. The pure one without args to mark primal functions (the functions
795/// being differentiated). The other form is #[rustc_autodiff(Mode, ActivityList)] on top of the
796/// placeholder functions. We wrote the rustc_autodiff attributes ourself, so this should never
797/// panic, unless we introduced a bug when parsing the autodiff macro.
798fn 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    // check for exactly one autodiff attribute on placeholder functions.
804    // There should only be one, since we generate a new placeholder per ad macro.
805    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    // empty autodiff attribute macros (i.e. `#[autodiff]`) are used to mark source functions
816    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    // parse mode
830    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    // First read the ret symbol from the attribute
863    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    // Then parse it into an actual DiffActivity
870    let Ok(ret_activity) = DiffActivity::from_str(ret_symbol.as_str()) else {
871        span_bug!(ret_symbol.span, "invalid return activity");
872    };
873
874    // Now parse all the intermediate (input) activities
875    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}