]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_codegen_ssa/src/codegen_attrs.rs
Rollup merge of #105710 - compiler-errors:dyn-star-rigid-cast, r=eholk
[rust.git] / compiler / rustc_codegen_ssa / src / codegen_attrs.rs
1 use rustc_ast::{ast, MetaItemKind, NestedMetaItem};
2 use rustc_attr::{list_contains_name, InlineAttr, InstructionSetAttr, OptimizeAttr};
3 use rustc_errors::struct_span_err;
4 use rustc_hir as hir;
5 use rustc_hir::def_id::{DefId, LocalDefId, LOCAL_CRATE};
6 use rustc_hir::{lang_items, weak_lang_items::WEAK_LANG_ITEMS, LangItem};
7 use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs};
8 use rustc_middle::mir::mono::Linkage;
9 use rustc_middle::ty::query::Providers;
10 use rustc_middle::ty::{self as ty, DefIdTree, TyCtxt};
11 use rustc_session::{lint, parse::feature_err};
12 use rustc_span::{sym, Span};
13 use rustc_target::spec::{abi, SanitizerSet};
14
15 use crate::target_features::from_target_feature;
16 use crate::{errors::ExpectedUsedSymbol, target_features::check_target_feature_trait_unsafe};
17
18 fn linkage_by_name(tcx: TyCtxt<'_>, def_id: LocalDefId, name: &str) -> Linkage {
19     use rustc_middle::mir::mono::Linkage::*;
20
21     // Use the names from src/llvm/docs/LangRef.rst here. Most types are only
22     // applicable to variable declarations and may not really make sense for
23     // Rust code in the first place but allow them anyway and trust that the
24     // user knows what they're doing. Who knows, unanticipated use cases may pop
25     // up in the future.
26     //
27     // ghost, dllimport, dllexport and linkonce_odr_autohide are not supported
28     // and don't have to be, LLVM treats them as no-ops.
29     match name {
30         "appending" => Appending,
31         "available_externally" => AvailableExternally,
32         "common" => Common,
33         "extern_weak" => ExternalWeak,
34         "external" => External,
35         "internal" => Internal,
36         "linkonce" => LinkOnceAny,
37         "linkonce_odr" => LinkOnceODR,
38         "private" => Private,
39         "weak" => WeakAny,
40         "weak_odr" => WeakODR,
41         _ => tcx.sess.span_fatal(tcx.def_span(def_id), "invalid linkage specified"),
42     }
43 }
44
45 fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: DefId) -> CodegenFnAttrs {
46     if cfg!(debug_assertions) {
47         let def_kind = tcx.def_kind(did);
48         assert!(
49             def_kind.has_codegen_attrs(),
50             "unexpected `def_kind` in `codegen_fn_attrs`: {def_kind:?}",
51         );
52     }
53
54     let did = did.expect_local();
55     let attrs = tcx.hir().attrs(tcx.hir().local_def_id_to_hir_id(did));
56     let mut codegen_fn_attrs = CodegenFnAttrs::new();
57     if tcx.should_inherit_track_caller(did) {
58         codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER;
59     }
60
61     let supported_target_features = tcx.supported_target_features(LOCAL_CRATE);
62
63     let mut inline_span = None;
64     let mut link_ordinal_span = None;
65     let mut no_sanitize_span = None;
66     for attr in attrs.iter() {
67         if attr.has_name(sym::cold) {
68             codegen_fn_attrs.flags |= CodegenFnAttrFlags::COLD;
69         } else if attr.has_name(sym::rustc_allocator) {
70             codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR;
71         } else if attr.has_name(sym::ffi_returns_twice) {
72             if tcx.is_foreign_item(did) {
73                 codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_RETURNS_TWICE;
74             } else {
75                 // `#[ffi_returns_twice]` is only allowed `extern fn`s.
76                 struct_span_err!(
77                     tcx.sess,
78                     attr.span,
79                     E0724,
80                     "`#[ffi_returns_twice]` may only be used on foreign functions"
81                 )
82                 .emit();
83             }
84         } else if attr.has_name(sym::ffi_pure) {
85             if tcx.is_foreign_item(did) {
86                 if attrs.iter().any(|a| a.has_name(sym::ffi_const)) {
87                     // `#[ffi_const]` functions cannot be `#[ffi_pure]`
88                     struct_span_err!(
89                         tcx.sess,
90                         attr.span,
91                         E0757,
92                         "`#[ffi_const]` function cannot be `#[ffi_pure]`"
93                     )
94                     .emit();
95                 } else {
96                     codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_PURE;
97                 }
98             } else {
99                 // `#[ffi_pure]` is only allowed on foreign functions
100                 struct_span_err!(
101                     tcx.sess,
102                     attr.span,
103                     E0755,
104                     "`#[ffi_pure]` may only be used on foreign functions"
105                 )
106                 .emit();
107             }
108         } else if attr.has_name(sym::ffi_const) {
109             if tcx.is_foreign_item(did) {
110                 codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_CONST;
111             } else {
112                 // `#[ffi_const]` is only allowed on foreign functions
113                 struct_span_err!(
114                     tcx.sess,
115                     attr.span,
116                     E0756,
117                     "`#[ffi_const]` may only be used on foreign functions"
118                 )
119                 .emit();
120             }
121         } else if attr.has_name(sym::rustc_nounwind) {
122             codegen_fn_attrs.flags |= CodegenFnAttrFlags::NEVER_UNWIND;
123         } else if attr.has_name(sym::rustc_reallocator) {
124             codegen_fn_attrs.flags |= CodegenFnAttrFlags::REALLOCATOR;
125         } else if attr.has_name(sym::rustc_deallocator) {
126             codegen_fn_attrs.flags |= CodegenFnAttrFlags::DEALLOCATOR;
127         } else if attr.has_name(sym::rustc_allocator_zeroed) {
128             codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR_ZEROED;
129         } else if attr.has_name(sym::naked) {
130             codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED;
131         } else if attr.has_name(sym::no_mangle) {
132             codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE;
133         } else if attr.has_name(sym::no_coverage) {
134             codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_COVERAGE;
135         } else if attr.has_name(sym::rustc_std_internal_symbol) {
136             codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL;
137         } else if attr.has_name(sym::used) {
138             let inner = attr.meta_item_list();
139             match inner.as_deref() {
140                 Some([item]) if item.has_name(sym::linker) => {
141                     if !tcx.features().used_with_arg {
142                         feature_err(
143                             &tcx.sess.parse_sess,
144                             sym::used_with_arg,
145                             attr.span,
146                             "`#[used(linker)]` is currently unstable",
147                         )
148                         .emit();
149                     }
150                     codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED_LINKER;
151                 }
152                 Some([item]) if item.has_name(sym::compiler) => {
153                     if !tcx.features().used_with_arg {
154                         feature_err(
155                             &tcx.sess.parse_sess,
156                             sym::used_with_arg,
157                             attr.span,
158                             "`#[used(compiler)]` is currently unstable",
159                         )
160                         .emit();
161                     }
162                     codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED;
163                 }
164                 Some(_) => {
165                     tcx.sess.emit_err(ExpectedUsedSymbol { span: attr.span });
166                 }
167                 None => {
168                     // Unfortunately, unconditionally using `llvm.used` causes
169                     // issues in handling `.init_array` with the gold linker,
170                     // but using `llvm.compiler.used` caused a nontrival amount
171                     // of unintentional ecosystem breakage -- particularly on
172                     // Mach-O targets.
173                     //
174                     // As a result, we emit `llvm.compiler.used` only on ELF
175                     // targets. This is somewhat ad-hoc, but actually follows
176                     // our pre-LLVM 13 behavior (prior to the ecosystem
177                     // breakage), and seems to match `clang`'s behavior as well
178                     // (both before and after LLVM 13), possibly because they
179                     // have similar compatibility concerns to us. See
180                     // https://github.com/rust-lang/rust/issues/47384#issuecomment-1019080146
181                     // and following comments for some discussion of this, as
182                     // well as the comments in `rustc_codegen_llvm` where these
183                     // flags are handled.
184                     //
185                     // Anyway, to be clear: this is still up in the air
186                     // somewhat, and is subject to change in the future (which
187                     // is a good thing, because this would ideally be a bit
188                     // more firmed up).
189                     let is_like_elf = !(tcx.sess.target.is_like_osx
190                         || tcx.sess.target.is_like_windows
191                         || tcx.sess.target.is_like_wasm);
192                     codegen_fn_attrs.flags |= if is_like_elf {
193                         CodegenFnAttrFlags::USED
194                     } else {
195                         CodegenFnAttrFlags::USED_LINKER
196                     };
197                 }
198             }
199         } else if attr.has_name(sym::cmse_nonsecure_entry) {
200             if !matches!(tcx.fn_sig(did).abi(), abi::Abi::C { .. }) {
201                 struct_span_err!(
202                     tcx.sess,
203                     attr.span,
204                     E0776,
205                     "`#[cmse_nonsecure_entry]` requires C ABI"
206                 )
207                 .emit();
208             }
209             if !tcx.sess.target.llvm_target.contains("thumbv8m") {
210                 struct_span_err!(tcx.sess, attr.span, E0775, "`#[cmse_nonsecure_entry]` is only valid for targets with the TrustZone-M extension")
211                     .emit();
212             }
213             codegen_fn_attrs.flags |= CodegenFnAttrFlags::CMSE_NONSECURE_ENTRY;
214         } else if attr.has_name(sym::thread_local) {
215             codegen_fn_attrs.flags |= CodegenFnAttrFlags::THREAD_LOCAL;
216         } else if attr.has_name(sym::track_caller) {
217             if !tcx.is_closure(did.to_def_id()) && tcx.fn_sig(did).abi() != abi::Abi::Rust {
218                 struct_span_err!(tcx.sess, attr.span, E0737, "`#[track_caller]` requires Rust ABI")
219                     .emit();
220             }
221             if tcx.is_closure(did.to_def_id()) && !tcx.features().closure_track_caller {
222                 feature_err(
223                     &tcx.sess.parse_sess,
224                     sym::closure_track_caller,
225                     attr.span,
226                     "`#[track_caller]` on closures is currently unstable",
227                 )
228                 .emit();
229             }
230             codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER;
231         } else if attr.has_name(sym::export_name) {
232             if let Some(s) = attr.value_str() {
233                 if s.as_str().contains('\0') {
234                     // `#[export_name = ...]` will be converted to a null-terminated string,
235                     // so it may not contain any null characters.
236                     struct_span_err!(
237                         tcx.sess,
238                         attr.span,
239                         E0648,
240                         "`export_name` may not contain null characters"
241                     )
242                     .emit();
243                 }
244                 codegen_fn_attrs.export_name = Some(s);
245             }
246         } else if attr.has_name(sym::target_feature) {
247             if !tcx.is_closure(did.to_def_id())
248                 && tcx.fn_sig(did).unsafety() == hir::Unsafety::Normal
249             {
250                 if tcx.sess.target.is_like_wasm || tcx.sess.opts.actually_rustdoc {
251                     // The `#[target_feature]` attribute is allowed on
252                     // WebAssembly targets on all functions, including safe
253                     // ones. Other targets require that `#[target_feature]` is
254                     // only applied to unsafe functions (pending the
255                     // `target_feature_11` feature) because on most targets
256                     // execution of instructions that are not supported is
257                     // considered undefined behavior. For WebAssembly which is a
258                     // 100% safe target at execution time it's not possible to
259                     // execute undefined instructions, and even if a future
260                     // feature was added in some form for this it would be a
261                     // deterministic trap. There is no undefined behavior when
262                     // executing WebAssembly so `#[target_feature]` is allowed
263                     // on safe functions (but again, only for WebAssembly)
264                     //
265                     // Note that this is also allowed if `actually_rustdoc` so
266                     // if a target is documenting some wasm-specific code then
267                     // it's not spuriously denied.
268                 } else if !tcx.features().target_feature_11 {
269                     let mut err = feature_err(
270                         &tcx.sess.parse_sess,
271                         sym::target_feature_11,
272                         attr.span,
273                         "`#[target_feature(..)]` can only be applied to `unsafe` functions",
274                     );
275                     err.span_label(tcx.def_span(did), "not an `unsafe` function");
276                     err.emit();
277                 } else {
278                     check_target_feature_trait_unsafe(tcx, did, attr.span);
279                 }
280             }
281             from_target_feature(
282                 tcx,
283                 attr,
284                 supported_target_features,
285                 &mut codegen_fn_attrs.target_features,
286             );
287         } else if attr.has_name(sym::linkage) {
288             if let Some(val) = attr.value_str() {
289                 let linkage = Some(linkage_by_name(tcx, did, val.as_str()));
290                 if tcx.is_foreign_item(did) {
291                     codegen_fn_attrs.import_linkage = linkage;
292                 } else {
293                     codegen_fn_attrs.linkage = linkage;
294                 }
295             }
296         } else if attr.has_name(sym::link_section) {
297             if let Some(val) = attr.value_str() {
298                 if val.as_str().bytes().any(|b| b == 0) {
299                     let msg = format!(
300                         "illegal null byte in link_section \
301                          value: `{}`",
302                         &val
303                     );
304                     tcx.sess.span_err(attr.span, &msg);
305                 } else {
306                     codegen_fn_attrs.link_section = Some(val);
307                 }
308             }
309         } else if attr.has_name(sym::link_name) {
310             codegen_fn_attrs.link_name = attr.value_str();
311         } else if attr.has_name(sym::link_ordinal) {
312             link_ordinal_span = Some(attr.span);
313             if let ordinal @ Some(_) = check_link_ordinal(tcx, attr) {
314                 codegen_fn_attrs.link_ordinal = ordinal;
315             }
316         } else if attr.has_name(sym::no_sanitize) {
317             no_sanitize_span = Some(attr.span);
318             if let Some(list) = attr.meta_item_list() {
319                 for item in list.iter() {
320                     if item.has_name(sym::address) {
321                         codegen_fn_attrs.no_sanitize |= SanitizerSet::ADDRESS;
322                     } else if item.has_name(sym::cfi) {
323                         codegen_fn_attrs.no_sanitize |= SanitizerSet::CFI;
324                     } else if item.has_name(sym::kcfi) {
325                         codegen_fn_attrs.no_sanitize |= SanitizerSet::KCFI;
326                     } else if item.has_name(sym::memory) {
327                         codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMORY;
328                     } else if item.has_name(sym::memtag) {
329                         codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMTAG;
330                     } else if item.has_name(sym::shadow_call_stack) {
331                         codegen_fn_attrs.no_sanitize |= SanitizerSet::SHADOWCALLSTACK;
332                     } else if item.has_name(sym::thread) {
333                         codegen_fn_attrs.no_sanitize |= SanitizerSet::THREAD;
334                     } else if item.has_name(sym::hwaddress) {
335                         codegen_fn_attrs.no_sanitize |= SanitizerSet::HWADDRESS;
336                     } else {
337                         tcx.sess
338                             .struct_span_err(item.span(), "invalid argument for `no_sanitize`")
339                             .note("expected one of: `address`, `cfi`, `hwaddress`, `kcfi`, `memory`, `memtag`, `shadow-call-stack`, or `thread`")
340                             .emit();
341                     }
342                 }
343             }
344         } else if attr.has_name(sym::instruction_set) {
345             codegen_fn_attrs.instruction_set = match attr.meta_kind() {
346                 Some(MetaItemKind::List(ref items)) => match items.as_slice() {
347                     [NestedMetaItem::MetaItem(set)] => {
348                         let segments =
349                             set.path.segments.iter().map(|x| x.ident.name).collect::<Vec<_>>();
350                         match segments.as_slice() {
351                             [sym::arm, sym::a32] | [sym::arm, sym::t32] => {
352                                 if !tcx.sess.target.has_thumb_interworking {
353                                     struct_span_err!(
354                                         tcx.sess.diagnostic(),
355                                         attr.span,
356                                         E0779,
357                                         "target does not support `#[instruction_set]`"
358                                     )
359                                     .emit();
360                                     None
361                                 } else if segments[1] == sym::a32 {
362                                     Some(InstructionSetAttr::ArmA32)
363                                 } else if segments[1] == sym::t32 {
364                                     Some(InstructionSetAttr::ArmT32)
365                                 } else {
366                                     unreachable!()
367                                 }
368                             }
369                             _ => {
370                                 struct_span_err!(
371                                     tcx.sess.diagnostic(),
372                                     attr.span,
373                                     E0779,
374                                     "invalid instruction set specified",
375                                 )
376                                 .emit();
377                                 None
378                             }
379                         }
380                     }
381                     [] => {
382                         struct_span_err!(
383                             tcx.sess.diagnostic(),
384                             attr.span,
385                             E0778,
386                             "`#[instruction_set]` requires an argument"
387                         )
388                         .emit();
389                         None
390                     }
391                     _ => {
392                         struct_span_err!(
393                             tcx.sess.diagnostic(),
394                             attr.span,
395                             E0779,
396                             "cannot specify more than one instruction set"
397                         )
398                         .emit();
399                         None
400                     }
401                 },
402                 _ => {
403                     struct_span_err!(
404                         tcx.sess.diagnostic(),
405                         attr.span,
406                         E0778,
407                         "must specify an instruction set"
408                     )
409                     .emit();
410                     None
411                 }
412             };
413         } else if attr.has_name(sym::repr) {
414             codegen_fn_attrs.alignment = match attr.meta_item_list() {
415                 Some(items) => match items.as_slice() {
416                     [item] => match item.name_value_literal() {
417                         Some((sym::align, literal)) => {
418                             let alignment = rustc_attr::parse_alignment(&literal.kind);
419
420                             match alignment {
421                                 Ok(align) => Some(align),
422                                 Err(msg) => {
423                                     struct_span_err!(
424                                         tcx.sess.diagnostic(),
425                                         attr.span,
426                                         E0589,
427                                         "invalid `repr(align)` attribute: {}",
428                                         msg
429                                     )
430                                     .emit();
431
432                                     None
433                                 }
434                             }
435                         }
436                         _ => None,
437                     },
438                     [] => None,
439                     _ => None,
440                 },
441                 None => None,
442             };
443         }
444     }
445
446     codegen_fn_attrs.inline = attrs.iter().fold(InlineAttr::None, |ia, attr| {
447         if !attr.has_name(sym::inline) {
448             return ia;
449         }
450         match attr.meta_kind() {
451             Some(MetaItemKind::Word) => InlineAttr::Hint,
452             Some(MetaItemKind::List(ref items)) => {
453                 inline_span = Some(attr.span);
454                 if items.len() != 1 {
455                     struct_span_err!(
456                         tcx.sess.diagnostic(),
457                         attr.span,
458                         E0534,
459                         "expected one argument"
460                     )
461                     .emit();
462                     InlineAttr::None
463                 } else if list_contains_name(&items, sym::always) {
464                     InlineAttr::Always
465                 } else if list_contains_name(&items, sym::never) {
466                     InlineAttr::Never
467                 } else {
468                     struct_span_err!(
469                         tcx.sess.diagnostic(),
470                         items[0].span(),
471                         E0535,
472                         "invalid argument"
473                     )
474                     .help("valid inline arguments are `always` and `never`")
475                     .emit();
476
477                     InlineAttr::None
478                 }
479             }
480             Some(MetaItemKind::NameValue(_)) => ia,
481             None => ia,
482         }
483     });
484
485     codegen_fn_attrs.optimize = attrs.iter().fold(OptimizeAttr::None, |ia, attr| {
486         if !attr.has_name(sym::optimize) {
487             return ia;
488         }
489         let err = |sp, s| struct_span_err!(tcx.sess.diagnostic(), sp, E0722, "{}", s).emit();
490         match attr.meta_kind() {
491             Some(MetaItemKind::Word) => {
492                 err(attr.span, "expected one argument");
493                 ia
494             }
495             Some(MetaItemKind::List(ref items)) => {
496                 inline_span = Some(attr.span);
497                 if items.len() != 1 {
498                     err(attr.span, "expected one argument");
499                     OptimizeAttr::None
500                 } else if list_contains_name(&items, sym::size) {
501                     OptimizeAttr::Size
502                 } else if list_contains_name(&items, sym::speed) {
503                     OptimizeAttr::Speed
504                 } else {
505                     err(items[0].span(), "invalid argument");
506                     OptimizeAttr::None
507                 }
508             }
509             Some(MetaItemKind::NameValue(_)) => ia,
510             None => ia,
511         }
512     });
513
514     // #73631: closures inherit `#[target_feature]` annotations
515     if tcx.features().target_feature_11 && tcx.is_closure(did.to_def_id()) {
516         let owner_id = tcx.parent(did.to_def_id());
517         if tcx.def_kind(owner_id).has_codegen_attrs() {
518             codegen_fn_attrs
519                 .target_features
520                 .extend(tcx.codegen_fn_attrs(owner_id).target_features.iter().copied());
521         }
522     }
523
524     // If a function uses #[target_feature] it can't be inlined into general
525     // purpose functions as they wouldn't have the right target features
526     // enabled. For that reason we also forbid #[inline(always)] as it can't be
527     // respected.
528     if !codegen_fn_attrs.target_features.is_empty() {
529         if codegen_fn_attrs.inline == InlineAttr::Always {
530             if let Some(span) = inline_span {
531                 tcx.sess.span_err(
532                     span,
533                     "cannot use `#[inline(always)]` with \
534                      `#[target_feature]`",
535                 );
536             }
537         }
538     }
539
540     if !codegen_fn_attrs.no_sanitize.is_empty() {
541         if codegen_fn_attrs.inline == InlineAttr::Always {
542             if let (Some(no_sanitize_span), Some(inline_span)) = (no_sanitize_span, inline_span) {
543                 let hir_id = tcx.hir().local_def_id_to_hir_id(did);
544                 tcx.struct_span_lint_hir(
545                     lint::builtin::INLINE_NO_SANITIZE,
546                     hir_id,
547                     no_sanitize_span,
548                     "`no_sanitize` will have no effect after inlining",
549                     |lint| lint.span_note(inline_span, "inlining requested here"),
550                 )
551             }
552         }
553     }
554
555     if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) {
556         codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_COVERAGE;
557         codegen_fn_attrs.inline = InlineAttr::Never;
558     }
559
560     // Weak lang items have the same semantics as "std internal" symbols in the
561     // sense that they're preserved through all our LTO passes and only
562     // strippable by the linker.
563     //
564     // Additionally weak lang items have predetermined symbol names.
565     if WEAK_LANG_ITEMS.iter().any(|&l| tcx.lang_items().get(l) == Some(did.to_def_id())) {
566         codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL;
567     }
568     if let Some((name, _)) = lang_items::extract(attrs)
569         && let Some(lang_item) = LangItem::from_name(name)
570         && let Some(link_name) = lang_item.link_name()
571     {
572         codegen_fn_attrs.export_name = Some(link_name);
573         codegen_fn_attrs.link_name = Some(link_name);
574     }
575     check_link_name_xor_ordinal(tcx, &codegen_fn_attrs, link_ordinal_span);
576
577     // Internal symbols to the standard library all have no_mangle semantics in
578     // that they have defined symbol names present in the function name. This
579     // also applies to weak symbols where they all have known symbol names.
580     if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL) {
581         codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE;
582     }
583
584     // Any linkage to LLVM intrinsics for now forcibly marks them all as never
585     // unwinds since LLVM sometimes can't handle codegen which `invoke`s
586     // intrinsic functions.
587     if let Some(name) = &codegen_fn_attrs.link_name {
588         if name.as_str().starts_with("llvm.") {
589             codegen_fn_attrs.flags |= CodegenFnAttrFlags::NEVER_UNWIND;
590         }
591     }
592
593     codegen_fn_attrs
594 }
595
596 /// Checks if the provided DefId is a method in a trait impl for a trait which has track_caller
597 /// applied to the method prototype.
598 fn should_inherit_track_caller(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
599     if let Some(impl_item) = tcx.opt_associated_item(def_id)
600         && let ty::AssocItemContainer::ImplContainer = impl_item.container
601         && let Some(trait_item) = impl_item.trait_item_def_id
602     {
603         return tcx
604             .codegen_fn_attrs(trait_item)
605             .flags
606             .intersects(CodegenFnAttrFlags::TRACK_CALLER);
607     }
608
609     false
610 }
611
612 fn check_link_ordinal(tcx: TyCtxt<'_>, attr: &ast::Attribute) -> Option<u16> {
613     use rustc_ast::{LitIntType, LitKind, MetaItemLit};
614     if !tcx.features().raw_dylib && tcx.sess.target.arch == "x86" {
615         feature_err(
616             &tcx.sess.parse_sess,
617             sym::raw_dylib,
618             attr.span,
619             "`#[link_ordinal]` is unstable on x86",
620         )
621         .emit();
622     }
623     let meta_item_list = attr.meta_item_list();
624     let meta_item_list = meta_item_list.as_deref();
625     let sole_meta_list = match meta_item_list {
626         Some([item]) => item.lit(),
627         Some(_) => {
628             tcx.sess
629                 .struct_span_err(attr.span, "incorrect number of arguments to `#[link_ordinal]`")
630                 .note("the attribute requires exactly one argument")
631                 .emit();
632             return None;
633         }
634         _ => None,
635     };
636     if let Some(MetaItemLit { kind: LitKind::Int(ordinal, LitIntType::Unsuffixed), .. }) =
637         sole_meta_list
638     {
639         // According to the table at https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#import-header,
640         // the ordinal must fit into 16 bits.  Similarly, the Ordinal field in COFFShortExport (defined
641         // in llvm/include/llvm/Object/COFFImportFile.h), which we use to communicate import information
642         // to LLVM for `#[link(kind = "raw-dylib"_])`, is also defined to be uint16_t.
643         //
644         // FIXME: should we allow an ordinal of 0?  The MSVC toolchain has inconsistent support for this:
645         // both LINK.EXE and LIB.EXE signal errors and abort when given a .DEF file that specifies
646         // a zero ordinal.  However, llvm-dlltool is perfectly happy to generate an import library
647         // for such a .DEF file, and MSVC's LINK.EXE is also perfectly happy to consume an import
648         // library produced by LLVM with an ordinal of 0, and it generates an .EXE.  (I don't know yet
649         // if the resulting EXE runs, as I haven't yet built the necessary DLL -- see earlier comment
650         // about LINK.EXE failing.)
651         if *ordinal <= u16::MAX as u128 {
652             Some(*ordinal as u16)
653         } else {
654             let msg = format!("ordinal value in `link_ordinal` is too large: `{}`", &ordinal);
655             tcx.sess
656                 .struct_span_err(attr.span, &msg)
657                 .note("the value may not exceed `u16::MAX`")
658                 .emit();
659             None
660         }
661     } else {
662         tcx.sess
663             .struct_span_err(attr.span, "illegal ordinal format in `link_ordinal`")
664             .note("an unsuffixed integer value, e.g., `1`, is expected")
665             .emit();
666         None
667     }
668 }
669
670 fn check_link_name_xor_ordinal(
671     tcx: TyCtxt<'_>,
672     codegen_fn_attrs: &CodegenFnAttrs,
673     inline_span: Option<Span>,
674 ) {
675     if codegen_fn_attrs.link_name.is_none() || codegen_fn_attrs.link_ordinal.is_none() {
676         return;
677     }
678     let msg = "cannot use `#[link_name]` with `#[link_ordinal]`";
679     if let Some(span) = inline_span {
680         tcx.sess.span_err(span, msg);
681     } else {
682         tcx.sess.err(msg);
683     }
684 }
685
686 pub fn provide(providers: &mut Providers) {
687     *providers = Providers { codegen_fn_attrs, should_inherit_track_caller, ..*providers };
688 }