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