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