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