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