rustc_codegen_ssa/
codegen_attrs.rs

1use std::str::FromStr;
2
3use rustc_abi::ExternAbi;
4use rustc_ast::attr::list_contains_name;
5use rustc_ast::expand::autodiff_attrs::{
6    AutoDiffAttrs, DiffActivity, DiffMode, valid_input_activity, valid_ret_activity,
7};
8use rustc_ast::{MetaItem, MetaItemInner, attr};
9use rustc_attr_parsing::{InlineAttr, InstructionSetAttr, OptimizeAttr};
10use rustc_data_structures::fx::FxHashMap;
11use rustc_errors::codes::*;
12use rustc_errors::{DiagMessage, SubdiagMessage, struct_span_code_err};
13use rustc_hir::def::DefKind;
14use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId};
15use rustc_hir::weak_lang_items::WEAK_LANG_ITEMS;
16use rustc_hir::{self as hir, HirId, LangItem, lang_items};
17use rustc_middle::middle::codegen_fn_attrs::{
18    CodegenFnAttrFlags, CodegenFnAttrs, PatchableFunctionEntry,
19};
20use rustc_middle::mir::mono::Linkage;
21use rustc_middle::query::Providers;
22use rustc_middle::span_bug;
23use rustc_middle::ty::{self as ty, TyCtxt};
24use rustc_session::parse::feature_err;
25use rustc_session::{Session, lint};
26use rustc_span::{Ident, Span, sym};
27use rustc_target::spec::SanitizerSet;
28use tracing::debug;
29
30use crate::errors;
31use crate::target_features::{check_target_feature_trait_unsafe, from_target_feature_attr};
32
33fn linkage_by_name(tcx: TyCtxt<'_>, def_id: LocalDefId, name: &str) -> Linkage {
34    use rustc_middle::mir::mono::Linkage::*;
35
36    // Use the names from src/llvm/docs/LangRef.rst here. Most types are only
37    // applicable to variable declarations and may not really make sense for
38    // Rust code in the first place but allow them anyway and trust that the
39    // user knows what they're doing. Who knows, unanticipated use cases may pop
40    // up in the future.
41    //
42    // ghost, dllimport, dllexport and linkonce_odr_autohide are not supported
43    // and don't have to be, LLVM treats them as no-ops.
44    match name {
45        "available_externally" => AvailableExternally,
46        "common" => Common,
47        "extern_weak" => ExternalWeak,
48        "external" => External,
49        "internal" => Internal,
50        "linkonce" => LinkOnceAny,
51        "linkonce_odr" => LinkOnceODR,
52        "weak" => WeakAny,
53        "weak_odr" => WeakODR,
54        _ => tcx.dcx().span_fatal(tcx.def_span(def_id), "invalid linkage specified"),
55    }
56}
57
58fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
59    if cfg!(debug_assertions) {
60        let def_kind = tcx.def_kind(did);
61        assert!(
62            def_kind.has_codegen_attrs(),
63            "unexpected `def_kind` in `codegen_fn_attrs`: {def_kind:?}",
64        );
65    }
66
67    let attrs = tcx.hir().attrs(tcx.local_def_id_to_hir_id(did));
68    let mut codegen_fn_attrs = CodegenFnAttrs::new();
69    if tcx.should_inherit_track_caller(did) {
70        codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER;
71    }
72
73    // If our rustc version supports autodiff/enzyme, then we call our handler
74    // to check for any `#[rustc_autodiff(...)]` attributes.
75    if cfg!(llvm_enzyme) {
76        let ad = autodiff_attrs(tcx, did.into());
77        codegen_fn_attrs.autodiff_item = ad;
78    }
79
80    // When `no_builtins` is applied at the crate level, we should add the
81    // `no-builtins` attribute to each function to ensure it takes effect in LTO.
82    let crate_attrs = tcx.hir().attrs(rustc_hir::CRATE_HIR_ID);
83    let no_builtins = attr::contains_name(crate_attrs, sym::no_builtins);
84    if no_builtins {
85        codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_BUILTINS;
86    }
87
88    let rust_target_features = tcx.rust_target_features(LOCAL_CRATE);
89
90    let mut inline_span = None;
91    let mut link_ordinal_span = None;
92    let mut no_sanitize_span = None;
93    let mut mixed_export_name_no_mangle_lint_state = MixedExportNameAndNoMangleState::default();
94
95    for attr in attrs.iter() {
96        // In some cases, attribute are only valid on functions, but it's the `check_attr`
97        // pass that check that they aren't used anywhere else, rather this module.
98        // In these cases, we bail from performing further checks that are only meaningful for
99        // functions (such as calling `fn_sig`, which ICEs if given a non-function). We also
100        // report a delayed bug, just in case `check_attr` isn't doing its job.
101        let fn_sig = || {
102            use DefKind::*;
103
104            let def_kind = tcx.def_kind(did);
105            if let Fn | AssocFn | Variant | Ctor(..) = def_kind {
106                Some(tcx.fn_sig(did))
107            } else {
108                tcx.dcx()
109                    .span_delayed_bug(attr.span, "this attribute can only be applied to functions");
110                None
111            }
112        };
113
114        let Some(Ident { name, .. }) = attr.ident() else {
115            continue;
116        };
117
118        match name {
119            sym::cold => codegen_fn_attrs.flags |= CodegenFnAttrFlags::COLD,
120            sym::rustc_allocator => codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR,
121            sym::ffi_pure => codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_PURE,
122            sym::ffi_const => codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_CONST,
123            sym::rustc_nounwind => codegen_fn_attrs.flags |= CodegenFnAttrFlags::NEVER_UNWIND,
124            sym::rustc_reallocator => codegen_fn_attrs.flags |= CodegenFnAttrFlags::REALLOCATOR,
125            sym::rustc_deallocator => codegen_fn_attrs.flags |= CodegenFnAttrFlags::DEALLOCATOR,
126            sym::rustc_allocator_zeroed => {
127                codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR_ZEROED
128            }
129            sym::naked => codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED,
130            sym::no_mangle => {
131                if tcx.opt_item_name(did.to_def_id()).is_some() {
132                    codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE;
133                    mixed_export_name_no_mangle_lint_state.track_no_mangle(
134                        attr.span,
135                        tcx.local_def_id_to_hir_id(did),
136                        attr,
137                    );
138                } else {
139                    tcx.dcx()
140                        .struct_span_err(
141                            attr.span,
142                            format!(
143                                "`#[no_mangle]` cannot be used on {} {} as it has no name",
144                                tcx.def_descr_article(did.to_def_id()),
145                                tcx.def_descr(did.to_def_id()),
146                            ),
147                        )
148                        .emit();
149                }
150            }
151            sym::rustc_std_internal_symbol => {
152                codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL
153            }
154            sym::used => {
155                let inner = attr.meta_item_list();
156                match inner.as_deref() {
157                    Some([item]) if item.has_name(sym::linker) => {
158                        if !tcx.features().used_with_arg() {
159                            feature_err(
160                                &tcx.sess,
161                                sym::used_with_arg,
162                                attr.span,
163                                "`#[used(linker)]` is currently unstable",
164                            )
165                            .emit();
166                        }
167                        codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED_LINKER;
168                    }
169                    Some([item]) if item.has_name(sym::compiler) => {
170                        if !tcx.features().used_with_arg() {
171                            feature_err(
172                                &tcx.sess,
173                                sym::used_with_arg,
174                                attr.span,
175                                "`#[used(compiler)]` is currently unstable",
176                            )
177                            .emit();
178                        }
179                        codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED;
180                    }
181                    Some(_) => {
182                        tcx.dcx().emit_err(errors::ExpectedUsedSymbol { span: attr.span });
183                    }
184                    None => {
185                        // Unfortunately, unconditionally using `llvm.used` causes
186                        // issues in handling `.init_array` with the gold linker,
187                        // but using `llvm.compiler.used` caused a nontrivial amount
188                        // of unintentional ecosystem breakage -- particularly on
189                        // Mach-O targets.
190                        //
191                        // As a result, we emit `llvm.compiler.used` only on ELF
192                        // targets. This is somewhat ad-hoc, but actually follows
193                        // our pre-LLVM 13 behavior (prior to the ecosystem
194                        // breakage), and seems to match `clang`'s behavior as well
195                        // (both before and after LLVM 13), possibly because they
196                        // have similar compatibility concerns to us. See
197                        // https://github.com/rust-lang/rust/issues/47384#issuecomment-1019080146
198                        // and following comments for some discussion of this, as
199                        // well as the comments in `rustc_codegen_llvm` where these
200                        // flags are handled.
201                        //
202                        // Anyway, to be clear: this is still up in the air
203                        // somewhat, and is subject to change in the future (which
204                        // is a good thing, because this would ideally be a bit
205                        // more firmed up).
206                        let is_like_elf = !(tcx.sess.target.is_like_osx
207                            || tcx.sess.target.is_like_windows
208                            || tcx.sess.target.is_like_wasm);
209                        codegen_fn_attrs.flags |= if is_like_elf {
210                            CodegenFnAttrFlags::USED
211                        } else {
212                            CodegenFnAttrFlags::USED_LINKER
213                        };
214                    }
215                }
216            }
217            sym::thread_local => codegen_fn_attrs.flags |= CodegenFnAttrFlags::THREAD_LOCAL,
218            sym::track_caller => {
219                let is_closure = tcx.is_closure_like(did.to_def_id());
220
221                if !is_closure
222                    && let Some(fn_sig) = fn_sig()
223                    && fn_sig.skip_binder().abi() != ExternAbi::Rust
224                {
225                    struct_span_code_err!(
226                        tcx.dcx(),
227                        attr.span,
228                        E0737,
229                        "`#[track_caller]` requires Rust ABI"
230                    )
231                    .emit();
232                }
233                if is_closure
234                    && !tcx.features().closure_track_caller()
235                    && !attr.span.allows_unstable(sym::closure_track_caller)
236                {
237                    feature_err(
238                        &tcx.sess,
239                        sym::closure_track_caller,
240                        attr.span,
241                        "`#[track_caller]` on closures is currently unstable",
242                    )
243                    .emit();
244                }
245                codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER
246            }
247            sym::export_name => {
248                if let Some(s) = attr.value_str() {
249                    if s.as_str().contains('\0') {
250                        // `#[export_name = ...]` will be converted to a null-terminated string,
251                        // so it may not contain any null characters.
252                        struct_span_code_err!(
253                            tcx.dcx(),
254                            attr.span,
255                            E0648,
256                            "`export_name` may not contain null characters"
257                        )
258                        .emit();
259                    }
260                    codegen_fn_attrs.export_name = Some(s);
261                    mixed_export_name_no_mangle_lint_state.track_export_name(attr.span);
262                }
263            }
264            sym::target_feature => {
265                let Some(sig) = tcx.hir_node_by_def_id(did).fn_sig() else {
266                    tcx.dcx().span_delayed_bug(attr.span, "target_feature applied to non-fn");
267                    continue;
268                };
269                let safe_target_features =
270                    matches!(sig.header.safety, hir::HeaderSafety::SafeTargetFeatures);
271                codegen_fn_attrs.safe_target_features = safe_target_features;
272                if safe_target_features {
273                    if tcx.sess.target.is_like_wasm || tcx.sess.opts.actually_rustdoc {
274                        // The `#[target_feature]` attribute is allowed on
275                        // WebAssembly targets on all functions. Prior to stabilizing
276                        // the `target_feature_11` feature, `#[target_feature]` was
277                        // only permitted on unsafe functions because on most targets
278                        // execution of instructions that are not supported is
279                        // considered undefined behavior. For WebAssembly which is a
280                        // 100% safe target at execution time it's not possible to
281                        // execute undefined instructions, and even if a future
282                        // feature was added in some form for this it would be a
283                        // deterministic trap. There is no undefined behavior when
284                        // executing WebAssembly so `#[target_feature]` is allowed
285                        // on safe functions (but again, only for WebAssembly)
286                        //
287                        // Note that this is also allowed if `actually_rustdoc` so
288                        // if a target is documenting some wasm-specific code then
289                        // it's not spuriously denied.
290                        //
291                        // Now that `#[target_feature]` is permitted on safe functions,
292                        // this exception must still exist for allowing the attribute on
293                        // `main`, `start`, and other functions that are not usually
294                        // allowed.
295                    } else {
296                        check_target_feature_trait_unsafe(tcx, did, attr.span);
297                    }
298                }
299                from_target_feature_attr(
300                    tcx,
301                    attr,
302                    rust_target_features,
303                    &mut codegen_fn_attrs.target_features,
304                );
305            }
306            sym::linkage => {
307                if let Some(val) = attr.value_str() {
308                    let linkage = Some(linkage_by_name(tcx, did, val.as_str()));
309                    if tcx.is_foreign_item(did) {
310                        codegen_fn_attrs.import_linkage = linkage;
311
312                        if tcx.is_mutable_static(did.into()) {
313                            let mut diag = tcx.dcx().struct_span_err(
314                                attr.span,
315                                "extern mutable statics are not allowed with `#[linkage]`",
316                            );
317                            diag.note(
318                                "marking the extern static mutable would allow changing which \
319                                 symbol the static references rather than make the target of the \
320                                 symbol mutable",
321                            );
322                            diag.emit();
323                        }
324                    } else {
325                        codegen_fn_attrs.linkage = linkage;
326                    }
327                }
328            }
329            sym::link_section => {
330                if let Some(val) = attr.value_str() {
331                    if val.as_str().bytes().any(|b| b == 0) {
332                        let msg = format!("illegal null byte in link_section value: `{val}`");
333                        tcx.dcx().span_err(attr.span, msg);
334                    } else {
335                        codegen_fn_attrs.link_section = Some(val);
336                    }
337                }
338            }
339            sym::link_name => codegen_fn_attrs.link_name = attr.value_str(),
340            sym::link_ordinal => {
341                link_ordinal_span = Some(attr.span);
342                if let ordinal @ Some(_) = check_link_ordinal(tcx, attr) {
343                    codegen_fn_attrs.link_ordinal = ordinal;
344                }
345            }
346            sym::no_sanitize => {
347                no_sanitize_span = Some(attr.span);
348                if let Some(list) = attr.meta_item_list() {
349                    for item in list.iter() {
350                        match item.name_or_empty() {
351                            sym::address => {
352                                codegen_fn_attrs.no_sanitize |=
353                                    SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS
354                            }
355                            sym::cfi => codegen_fn_attrs.no_sanitize |= SanitizerSet::CFI,
356                            sym::kcfi => codegen_fn_attrs.no_sanitize |= SanitizerSet::KCFI,
357                            sym::memory => codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMORY,
358                            sym::memtag => codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMTAG,
359                            sym::shadow_call_stack => {
360                                codegen_fn_attrs.no_sanitize |= SanitizerSet::SHADOWCALLSTACK
361                            }
362                            sym::thread => codegen_fn_attrs.no_sanitize |= SanitizerSet::THREAD,
363                            sym::hwaddress => {
364                                codegen_fn_attrs.no_sanitize |= SanitizerSet::HWADDRESS
365                            }
366                            _ => {
367                                tcx.dcx().emit_err(errors::InvalidNoSanitize { span: item.span() });
368                            }
369                        }
370                    }
371                }
372            }
373            sym::instruction_set => {
374                codegen_fn_attrs.instruction_set =
375                    attr.meta_item_list().and_then(|l| match &l[..] {
376                        [MetaItemInner::MetaItem(set)] => {
377                            let segments =
378                                set.path.segments.iter().map(|x| x.ident.name).collect::<Vec<_>>();
379                            match segments.as_slice() {
380                                [sym::arm, sym::a32] | [sym::arm, sym::t32] => {
381                                    if !tcx.sess.target.has_thumb_interworking {
382                                        struct_span_code_err!(
383                                            tcx.dcx(),
384                                            attr.span,
385                                            E0779,
386                                            "target does not support `#[instruction_set]`"
387                                        )
388                                        .emit();
389                                        None
390                                    } else if segments[1] == sym::a32 {
391                                        Some(InstructionSetAttr::ArmA32)
392                                    } else if segments[1] == sym::t32 {
393                                        Some(InstructionSetAttr::ArmT32)
394                                    } else {
395                                        unreachable!()
396                                    }
397                                }
398                                _ => {
399                                    struct_span_code_err!(
400                                        tcx.dcx(),
401                                        attr.span,
402                                        E0779,
403                                        "invalid instruction set specified",
404                                    )
405                                    .emit();
406                                    None
407                                }
408                            }
409                        }
410                        [] => {
411                            struct_span_code_err!(
412                                tcx.dcx(),
413                                attr.span,
414                                E0778,
415                                "`#[instruction_set]` requires an argument"
416                            )
417                            .emit();
418                            None
419                        }
420                        _ => {
421                            struct_span_code_err!(
422                                tcx.dcx(),
423                                attr.span,
424                                E0779,
425                                "cannot specify more than one instruction set"
426                            )
427                            .emit();
428                            None
429                        }
430                    })
431            }
432            sym::repr => {
433                codegen_fn_attrs.alignment = if let Some(items) = attr.meta_item_list()
434                    && let [item] = items.as_slice()
435                    && let Some((sym::align, literal)) = item.singleton_lit_list()
436                {
437                    rustc_attr_parsing::parse_alignment(&literal.kind)
438                        .map_err(|msg| {
439                            struct_span_code_err!(
440                                tcx.dcx(),
441                                literal.span,
442                                E0589,
443                                "invalid `repr(align)` attribute: {}",
444                                msg
445                            )
446                            .emit();
447                        })
448                        .ok()
449                } else {
450                    None
451                };
452            }
453            sym::patchable_function_entry => {
454                codegen_fn_attrs.patchable_function_entry = attr.meta_item_list().and_then(|l| {
455                    let mut prefix = None;
456                    let mut entry = None;
457                    for item in l {
458                        let Some(meta_item) = item.meta_item() else {
459                            tcx.dcx().span_err(item.span(), "expected name value pair");
460                            continue;
461                        };
462
463                        let Some(name_value_lit) = meta_item.name_value_literal() else {
464                            tcx.dcx().span_err(item.span(), "expected name value pair");
465                            continue;
466                        };
467
468                        fn emit_error_with_label(
469                            tcx: TyCtxt<'_>,
470                            span: Span,
471                            error: impl Into<DiagMessage>,
472                            label: impl Into<SubdiagMessage>,
473                        ) {
474                            let mut err: rustc_errors::Diag<'_, _> =
475                                tcx.dcx().struct_span_err(span, error);
476                            err.span_label(span, label);
477                            err.emit();
478                        }
479
480                        let attrib_to_write = match meta_item.name_or_empty() {
481                            sym::prefix_nops => &mut prefix,
482                            sym::entry_nops => &mut entry,
483                            _ => {
484                                emit_error_with_label(
485                                    tcx,
486                                    item.span(),
487                                    "unexpected parameter name",
488                                    format!("expected {} or {}", sym::prefix_nops, sym::entry_nops),
489                                );
490                                continue;
491                            }
492                        };
493
494                        let rustc_ast::LitKind::Int(val, _) = name_value_lit.kind else {
495                            emit_error_with_label(
496                                tcx,
497                                name_value_lit.span,
498                                "invalid literal value",
499                                "value must be an integer between `0` and `255`",
500                            );
501                            continue;
502                        };
503
504                        let Ok(val) = val.get().try_into() else {
505                            emit_error_with_label(
506                                tcx,
507                                name_value_lit.span,
508                                "integer value out of range",
509                                "value must be between `0` and `255`",
510                            );
511                            continue;
512                        };
513
514                        *attrib_to_write = Some(val);
515                    }
516
517                    if let (None, None) = (prefix, entry) {
518                        tcx.dcx().span_err(attr.span, "must specify at least one parameter");
519                    }
520
521                    Some(PatchableFunctionEntry::from_prefix_and_entry(
522                        prefix.unwrap_or(0),
523                        entry.unwrap_or(0),
524                    ))
525                })
526            }
527            _ => {}
528        }
529    }
530
531    mixed_export_name_no_mangle_lint_state.lint_if_mixed(tcx);
532
533    codegen_fn_attrs.inline = attrs.iter().fold(InlineAttr::None, |ia, attr| {
534        if !attr.has_name(sym::inline) {
535            return ia;
536        }
537
538        if attr.is_word() {
539            InlineAttr::Hint
540        } else if let Some(ref items) = attr.meta_item_list() {
541            inline_span = Some(attr.span);
542            if items.len() != 1 {
543                struct_span_code_err!(tcx.dcx(), attr.span, E0534, "expected one argument").emit();
544                InlineAttr::None
545            } else if list_contains_name(items, sym::always) {
546                InlineAttr::Always
547            } else if list_contains_name(items, sym::never) {
548                InlineAttr::Never
549            } else {
550                struct_span_code_err!(tcx.dcx(), items[0].span(), E0535, "invalid argument")
551                    .with_help("valid inline arguments are `always` and `never`")
552                    .emit();
553
554                InlineAttr::None
555            }
556        } else {
557            ia
558        }
559    });
560    codegen_fn_attrs.inline = attrs.iter().fold(codegen_fn_attrs.inline, |ia, attr| {
561        if !attr.has_name(sym::rustc_force_inline) || !tcx.features().rustc_attrs() {
562            return ia;
563        }
564
565        if attr.is_word() {
566            InlineAttr::Force { attr_span: attr.span, reason: None }
567        } else if let Some(val) = attr.value_str() {
568            InlineAttr::Force { attr_span: attr.span, reason: Some(val) }
569        } else {
570            debug!("`rustc_force_inline` not checked by attribute validation");
571            ia
572        }
573    });
574
575    // naked function MUST NOT be inlined! This attribute is required for the rust compiler itself,
576    // but not for the code generation backend because at that point the naked function will just be
577    // a declaration, with a definition provided in global assembly.
578    if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) {
579        codegen_fn_attrs.inline = InlineAttr::Never;
580    }
581
582    codegen_fn_attrs.optimize = attrs.iter().fold(OptimizeAttr::Default, |ia, attr| {
583        if !attr.has_name(sym::optimize) {
584            return ia;
585        }
586        let err = |sp, s| struct_span_code_err!(tcx.dcx(), sp, E0722, "{}", s).emit();
587        if attr.is_word() {
588            err(attr.span, "expected one argument");
589            ia
590        } else if let Some(ref items) = attr.meta_item_list() {
591            inline_span = Some(attr.span);
592            if items.len() != 1 {
593                err(attr.span, "expected one argument");
594                OptimizeAttr::Default
595            } else if list_contains_name(items, sym::size) {
596                OptimizeAttr::Size
597            } else if list_contains_name(items, sym::speed) {
598                OptimizeAttr::Speed
599            } else if list_contains_name(items, sym::none) {
600                OptimizeAttr::DoNotOptimize
601            } else {
602                err(items[0].span(), "invalid argument");
603                OptimizeAttr::Default
604            }
605        } else {
606            OptimizeAttr::Default
607        }
608    });
609
610    // #73631: closures inherit `#[target_feature]` annotations
611    //
612    // If this closure is marked `#[inline(always)]`, simply skip adding `#[target_feature]`.
613    //
614    // At this point, `unsafe` has already been checked and `#[target_feature]` only affects codegen.
615    // Due to LLVM limitations, emitting both `#[inline(always)]` and `#[target_feature]` is *unsound*:
616    // the function may be inlined into a caller with fewer target features. Also see
617    // <https://github.com/rust-lang/rust/issues/116573>.
618    //
619    // Using `#[inline(always)]` implies that this closure will most likely be inlined into
620    // its parent function, which effectively inherits the features anyway. Boxing this closure
621    // would result in this closure being compiled without the inherited target features, but this
622    // is probably a poor usage of `#[inline(always)]` and easily avoided by not using the attribute.
623    if tcx.is_closure_like(did.to_def_id()) && codegen_fn_attrs.inline != InlineAttr::Always {
624        let owner_id = tcx.parent(did.to_def_id());
625        if tcx.def_kind(owner_id).has_codegen_attrs() {
626            codegen_fn_attrs
627                .target_features
628                .extend(tcx.codegen_fn_attrs(owner_id).target_features.iter().copied());
629        }
630    }
631
632    // If a function uses `#[target_feature]` it can't be inlined into general
633    // purpose functions as they wouldn't have the right target features
634    // enabled. For that reason we also forbid `#[inline(always)]` as it can't be
635    // respected.
636    //
637    // `#[rustc_force_inline]` doesn't need to be prohibited here, only
638    // `#[inline(always)]`, as forced inlining is implemented entirely within
639    // rustc (and so the MIR inliner can do any necessary checks for compatible target
640    // features).
641    //
642    // This sidesteps the LLVM blockers in enabling `target_features` +
643    // `inline(always)` to be used together (see rust-lang/rust#116573 and
644    // llvm/llvm-project#70563).
645    if !codegen_fn_attrs.target_features.is_empty()
646        && matches!(codegen_fn_attrs.inline, InlineAttr::Always)
647    {
648        if let Some(span) = inline_span {
649            tcx.dcx().span_err(span, "cannot use `#[inline(always)]` with `#[target_feature]`");
650        }
651    }
652
653    if !codegen_fn_attrs.no_sanitize.is_empty() && codegen_fn_attrs.inline.always() {
654        if let (Some(no_sanitize_span), Some(inline_span)) = (no_sanitize_span, inline_span) {
655            let hir_id = tcx.local_def_id_to_hir_id(did);
656            tcx.node_span_lint(
657                lint::builtin::INLINE_NO_SANITIZE,
658                hir_id,
659                no_sanitize_span,
660                |lint| {
661                    lint.primary_message("`no_sanitize` will have no effect after inlining");
662                    lint.span_note(inline_span, "inlining requested here");
663                },
664            )
665        }
666    }
667
668    // Weak lang items have the same semantics as "std internal" symbols in the
669    // sense that they're preserved through all our LTO passes and only
670    // strippable by the linker.
671    //
672    // Additionally weak lang items have predetermined symbol names.
673    if WEAK_LANG_ITEMS.iter().any(|&l| tcx.lang_items().get(l) == Some(did.to_def_id())) {
674        codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL;
675    }
676    if let Some((name, _)) = lang_items::extract(attrs)
677        && let Some(lang_item) = LangItem::from_name(name)
678        && let Some(link_name) = lang_item.link_name()
679    {
680        codegen_fn_attrs.export_name = Some(link_name);
681        codegen_fn_attrs.link_name = Some(link_name);
682    }
683    check_link_name_xor_ordinal(tcx, &codegen_fn_attrs, link_ordinal_span);
684
685    // Internal symbols to the standard library all have no_mangle semantics in
686    // that they have defined symbol names present in the function name. This
687    // also applies to weak symbols where they all have known symbol names.
688    if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL) {
689        codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE;
690    }
691
692    // Any linkage to LLVM intrinsics for now forcibly marks them all as never
693    // unwinds since LLVM sometimes can't handle codegen which `invoke`s
694    // intrinsic functions.
695    if let Some(name) = &codegen_fn_attrs.link_name {
696        if name.as_str().starts_with("llvm.") {
697            codegen_fn_attrs.flags |= CodegenFnAttrFlags::NEVER_UNWIND;
698        }
699    }
700
701    if let Some(features) = check_tied_features(
702        tcx.sess,
703        &codegen_fn_attrs
704            .target_features
705            .iter()
706            .map(|features| (features.name.as_str(), true))
707            .collect(),
708    ) {
709        let span = tcx
710            .get_attrs(did, sym::target_feature)
711            .next()
712            .map_or_else(|| tcx.def_span(did), |a| a.span);
713        tcx.dcx()
714            .create_err(errors::TargetFeatureDisableOrEnable {
715                features,
716                span: Some(span),
717                missing_features: Some(errors::MissingFeatures),
718            })
719            .emit();
720    }
721
722    codegen_fn_attrs
723}
724
725/// Given a map from target_features to whether they are enabled or disabled, ensure only valid
726/// combinations are allowed.
727pub fn check_tied_features(
728    sess: &Session,
729    features: &FxHashMap<&str, bool>,
730) -> Option<&'static [&'static str]> {
731    if !features.is_empty() {
732        for tied in sess.target.tied_target_features() {
733            // Tied features must be set to the same value, or not set at all
734            let mut tied_iter = tied.iter();
735            let enabled = features.get(tied_iter.next().unwrap());
736            if tied_iter.any(|f| enabled != features.get(f)) {
737                return Some(tied);
738            }
739        }
740    }
741    None
742}
743
744/// Checks if the provided DefId is a method in a trait impl for a trait which has track_caller
745/// applied to the method prototype.
746fn should_inherit_track_caller(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
747    if let Some(impl_item) = tcx.opt_associated_item(def_id)
748        && let ty::AssocItemContainer::Impl = impl_item.container
749        && let Some(trait_item) = impl_item.trait_item_def_id
750    {
751        return tcx.codegen_fn_attrs(trait_item).flags.intersects(CodegenFnAttrFlags::TRACK_CALLER);
752    }
753
754    false
755}
756
757fn check_link_ordinal(tcx: TyCtxt<'_>, attr: &hir::Attribute) -> Option<u16> {
758    use rustc_ast::{LitIntType, LitKind, MetaItemLit};
759    let meta_item_list = attr.meta_item_list();
760    let meta_item_list = meta_item_list.as_deref();
761    let sole_meta_list = match meta_item_list {
762        Some([item]) => item.lit(),
763        Some(_) => {
764            tcx.dcx().emit_err(errors::InvalidLinkOrdinalNargs { span: attr.span });
765            return None;
766        }
767        _ => None,
768    };
769    if let Some(MetaItemLit { kind: LitKind::Int(ordinal, LitIntType::Unsuffixed), .. }) =
770        sole_meta_list
771    {
772        // According to the table at
773        // https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#import-header, the
774        // ordinal must fit into 16 bits. Similarly, the Ordinal field in COFFShortExport (defined
775        // in llvm/include/llvm/Object/COFFImportFile.h), which we use to communicate import
776        // information to LLVM for `#[link(kind = "raw-dylib"_])`, is also defined to be uint16_t.
777        //
778        // FIXME: should we allow an ordinal of 0?  The MSVC toolchain has inconsistent support for
779        // this: both LINK.EXE and LIB.EXE signal errors and abort when given a .DEF file that
780        // specifies a zero ordinal. However, llvm-dlltool is perfectly happy to generate an import
781        // library for such a .DEF file, and MSVC's LINK.EXE is also perfectly happy to consume an
782        // import library produced by LLVM with an ordinal of 0, and it generates an .EXE.  (I
783        // don't know yet if the resulting EXE runs, as I haven't yet built the necessary DLL --
784        // see earlier comment about LINK.EXE failing.)
785        if *ordinal <= u16::MAX as u128 {
786            Some(ordinal.get() as u16)
787        } else {
788            let msg = format!("ordinal value in `link_ordinal` is too large: `{ordinal}`");
789            tcx.dcx()
790                .struct_span_err(attr.span, msg)
791                .with_note("the value may not exceed `u16::MAX`")
792                .emit();
793            None
794        }
795    } else {
796        tcx.dcx().emit_err(errors::InvalidLinkOrdinalFormat { span: attr.span });
797        None
798    }
799}
800
801fn check_link_name_xor_ordinal(
802    tcx: TyCtxt<'_>,
803    codegen_fn_attrs: &CodegenFnAttrs,
804    inline_span: Option<Span>,
805) {
806    if codegen_fn_attrs.link_name.is_none() || codegen_fn_attrs.link_ordinal.is_none() {
807        return;
808    }
809    let msg = "cannot use `#[link_name]` with `#[link_ordinal]`";
810    if let Some(span) = inline_span {
811        tcx.dcx().span_err(span, msg);
812    } else {
813        tcx.dcx().err(msg);
814    }
815}
816
817#[derive(Default)]
818struct MixedExportNameAndNoMangleState<'a> {
819    export_name: Option<Span>,
820    hir_id: Option<HirId>,
821    no_mangle: Option<Span>,
822    no_mangle_attr: Option<&'a hir::Attribute>,
823}
824
825impl<'a> MixedExportNameAndNoMangleState<'a> {
826    fn track_export_name(&mut self, span: Span) {
827        self.export_name = Some(span);
828    }
829
830    fn track_no_mangle(&mut self, span: Span, hir_id: HirId, attr_name: &'a hir::Attribute) {
831        self.no_mangle = Some(span);
832        self.hir_id = Some(hir_id);
833        self.no_mangle_attr = Some(attr_name);
834    }
835
836    /// Emit diagnostics if the lint condition is met.
837    fn lint_if_mixed(self, tcx: TyCtxt<'_>) {
838        if let Self {
839            export_name: Some(export_name),
840            no_mangle: Some(no_mangle),
841            hir_id: Some(hir_id),
842            no_mangle_attr: Some(no_mangle_attr),
843        } = self
844        {
845            tcx.emit_node_span_lint(
846                lint::builtin::UNUSED_ATTRIBUTES,
847                hir_id,
848                no_mangle,
849                errors::MixedExportNameAndNoMangle {
850                    no_mangle,
851                    no_mangle_attr: rustc_hir_pretty::attribute_to_string(&tcx, no_mangle_attr),
852                    export_name,
853                    removal_span: no_mangle,
854                },
855            );
856        }
857    }
858}
859
860/// We now check the #\[rustc_autodiff\] attributes which we generated from the #[autodiff(...)]
861/// macros. There are two forms. The pure one without args to mark primal functions (the functions
862/// being differentiated). The other form is #[rustc_autodiff(Mode, ActivityList)] on top of the
863/// placeholder functions. We wrote the rustc_autodiff attributes ourself, so this should never
864/// panic, unless we introduced a bug when parsing the autodiff macro.
865fn autodiff_attrs(tcx: TyCtxt<'_>, id: DefId) -> Option<AutoDiffAttrs> {
866    let attrs = tcx.get_attrs(id, sym::rustc_autodiff);
867
868    let attrs =
869        attrs.filter(|attr| attr.name_or_empty() == sym::rustc_autodiff).collect::<Vec<_>>();
870
871    // check for exactly one autodiff attribute on placeholder functions.
872    // There should only be one, since we generate a new placeholder per ad macro.
873    // FIXME(ZuseZ4): re-enable this check. Currently we add multiple, which doesn't cause harm but
874    // looks strange e.g. under cargo-expand.
875    let attr = match &attrs[..] {
876        [] => return None,
877        [attr] => attr,
878        // These two attributes are the same and unfortunately duplicated due to a previous bug.
879        [attr, _attr2] => attr,
880        _ => {
881            //FIXME(ZuseZ4): Once we fixed our parser, we should also prohibit the two-attribute
882            //branch above.
883            span_bug!(attrs[1].span, "cg_ssa: rustc_autodiff should only exist once per source");
884        }
885    };
886
887    let list = attr.meta_item_list().unwrap_or_default();
888
889    // empty autodiff attribute macros (i.e. `#[autodiff]`) are used to mark source functions
890    if list.is_empty() {
891        return Some(AutoDiffAttrs::source());
892    }
893
894    let [mode, input_activities @ .., ret_activity] = &list[..] else {
895        span_bug!(attr.span, "rustc_autodiff attribute must contain mode and activities");
896    };
897    let mode = if let MetaItemInner::MetaItem(MetaItem { path: ref p1, .. }) = mode {
898        p1.segments.first().unwrap().ident
899    } else {
900        span_bug!(attr.span, "rustc_autodiff attribute must contain mode");
901    };
902
903    // parse mode
904    let mode = match mode.as_str() {
905        "Forward" => DiffMode::Forward,
906        "Reverse" => DiffMode::Reverse,
907        _ => {
908            span_bug!(mode.span, "rustc_autodiff attribute contains invalid mode");
909        }
910    };
911
912    // First read the ret symbol from the attribute
913    let ret_symbol = if let MetaItemInner::MetaItem(MetaItem { path: ref p1, .. }) = ret_activity {
914        p1.segments.first().unwrap().ident
915    } else {
916        span_bug!(attr.span, "rustc_autodiff attribute must contain the return activity");
917    };
918
919    // Then parse it into an actual DiffActivity
920    let Ok(ret_activity) = DiffActivity::from_str(ret_symbol.as_str()) else {
921        span_bug!(ret_symbol.span, "invalid return activity");
922    };
923
924    // Now parse all the intermediate (input) activities
925    let mut arg_activities: Vec<DiffActivity> = vec![];
926    for arg in input_activities {
927        let arg_symbol = if let MetaItemInner::MetaItem(MetaItem { path: ref p2, .. }) = arg {
928            match p2.segments.first() {
929                Some(x) => x.ident,
930                None => {
931                    span_bug!(
932                        arg.span(),
933                        "rustc_autodiff attribute must contain the input activity"
934                    );
935                }
936            }
937        } else {
938            span_bug!(arg.span(), "rustc_autodiff attribute must contain the input activity");
939        };
940
941        match DiffActivity::from_str(arg_symbol.as_str()) {
942            Ok(arg_activity) => arg_activities.push(arg_activity),
943            Err(_) => {
944                span_bug!(arg_symbol.span, "invalid input activity");
945            }
946        }
947    }
948
949    for &input in &arg_activities {
950        if !valid_input_activity(mode, input) {
951            span_bug!(attr.span, "Invalid input activity {} for {} mode", input, mode);
952        }
953    }
954    if !valid_ret_activity(mode, ret_activity) {
955        span_bug!(attr.span, "Invalid return activity {} for {} mode", ret_activity, mode);
956    }
957
958    Some(AutoDiffAttrs { mode, ret_activity, input_activity: arg_activities })
959}
960
961pub(crate) fn provide(providers: &mut Providers) {
962    *providers = Providers { codegen_fn_attrs, should_inherit_track_caller, ..*providers };
963}