1#[derive(Clone, Copy, PartialEq, Eq, Debug)]
20pub enum Attribute {
21 Noreturn,
23 AlwaysInline,
25 NoInline,
27 Cold,
29 Hot,
31 Deprecated,
33 Fallthrough,
36 Packed,
38 Aligned,
40 Section,
42 Constructor,
44 Destructor,
46 Cleanup,
48 Mode,
50 Safe,
53 Weak,
58 Unsupported,
61 Ignored,
64}
65
66pub const UNSUPPORTED_ATTRIBUTES: &[(&str, &str)] = &[
71 (
72 "weakref",
73 "is not supported: Rust's `#[linkage]` is unstable, so weak linkage cannot be asked for",
74 ),
75 (
76 "alias",
77 "is not supported: write a function that forwards to the other one instead",
78 ),
79 (
80 "ifunc",
81 "is not supported: choosing an implementation at load time has no stable Rust counterpart",
82 ),
83 (
84 "vector_size",
85 "is not supported: the vector extensions need `core::simd`, which is unstable",
86 ),
87 (
88 "scalar_storage_order",
89 "is not supported: it reverses the byte order of every scalar in the record, and \
90 nothing in the generated Rust could carry that",
91 ),
92];
93
94pub fn attribute(name: &str) -> Option<Attribute> {
97 let bare = name
98 .strip_prefix("__")
99 .and_then(|rest| rest.strip_suffix("__"))
100 .unwrap_or(name);
101 if UNSUPPORTED_ATTRIBUTES.iter().any(|(n, _)| *n == bare) {
102 return Some(Attribute::Unsupported);
103 }
104 Some(match bare {
105 "noreturn" => Attribute::Noreturn,
106 "always_inline" => Attribute::AlwaysInline,
107 "noinline" => Attribute::NoInline,
108 "cold" => Attribute::Cold,
109 "hot" => Attribute::Hot,
110 "deprecated" => Attribute::Deprecated,
111 "fallthrough" => Attribute::Fallthrough,
112 "packed" => Attribute::Packed,
113 "aligned" | "alignas" => Attribute::Aligned,
114 "section" => Attribute::Section,
115 "constructor" => Attribute::Constructor,
116 "destructor" => Attribute::Destructor,
117 "cleanup" => Attribute::Cleanup,
118 "mode" => Attribute::Mode,
119 "weak" => Attribute::Weak,
120 "cinrs_safe" => Attribute::Safe,
123 _ if IGNORED_ATTRIBUTES.contains(&bare) => Attribute::Ignored,
124 _ => return None,
125 })
126}
127
128pub const CINRS_ATTRIBUTES: &[&str] = &["safe"];
135
136pub fn cinrs_attribute(name: &str) -> Option<Attribute> {
141 match name {
142 "safe" => Some(Attribute::Safe),
143 _ => None,
144 }
145}
146
147pub fn unsupported_reason(name: &str) -> Option<&'static str> {
149 let bare = name
150 .strip_prefix("__")
151 .and_then(|rest| rest.strip_suffix("__"))
152 .unwrap_or(name);
153 UNSUPPORTED_ATTRIBUTES
154 .iter()
155 .find(|(n, _)| *n == bare)
156 .map(|(_, reason)| *reason)
157}
158
159const IGNORED_ATTRIBUTES: &[&str] = &[
162 "access",
163 "alloc_align",
164 "alloc_size",
165 "artificial",
166 "assume_aligned",
167 "cdecl",
168 "const",
169 "designated_init",
170 "error",
171 "externally_visible",
172 "fastcall",
173 "flatten",
174 "format",
175 "format_arg",
176 "gnu_inline",
177 "leaf",
178 "malloc",
179 "may_alias",
180 "maybe_unused",
181 "no_instrument_function",
182 "no_sanitize",
183 "no_split_stack",
184 "noclone",
185 "nodiscard",
186 "noipa",
187 "nonnull",
188 "nonstring",
189 "nothrow",
190 "optimize",
191 "pure",
192 "reproducible",
193 "returns_nonnull",
194 "returns_twice",
195 "sentinel",
196 "stdcall",
197 "target",
198 "target_clones",
199 "transparent_union",
200 "unavailable",
201 "unsequenced",
202 "unused",
203 "used",
204 "visibility",
205 "warn_unused_result",
206 "warning",
207];
208
209pub fn has_attribute(name: &str) -> bool {
218 attribute(name).is_some_and(|a| !matches!(a, Attribute::Unsupported | Attribute::Weak))
219}
220
221pub fn has_c_attribute(name: &str) -> u64 {
227 let standard = matches!(
228 name,
229 "deprecated"
230 | "fallthrough"
231 | "maybe_unused"
232 | "nodiscard"
233 | "noreturn"
234 | "unsequenced"
235 | "reproducible"
236 );
237 if standard { 202_311 } else { 0 }
238}
239
240pub fn has_builtin(name: &str) -> bool {
246 if crate::sema::is_atomic_builtin(name) {
247 return true;
248 }
249 let Some(rest) = name.strip_prefix("__builtin_") else {
250 return false;
251 };
252 SPECIAL_BUILTINS.contains(&rest)
253 || LIBRARY_BUILTINS.contains(&rest)
254 || long_double_math(rest).is_some()
255}
256
257pub fn has_feature(name: &str) -> bool {
268 SUPPORTED_FEATURES.contains(&name)
269}
270
271const SUPPORTED_FEATURES: &[&str] = &[
273 "c_alignas",
274 "c_alignof",
275 "c_atomic",
276 "c_attributes",
277 "c_generic_selection",
278 "c_generic_selections",
279 "c_static_assert",
280 "cinrs",
281];
282
283pub const SPECIAL_BUILTINS: &[&str] = &[
288 "add_overflow",
289 "add_overflow_p",
290 "alloca",
291 "alloca_with_align",
292 "assume",
293 "assume_aligned",
294 "bswap16",
295 "bswap32",
296 "bswap64",
297 "choose_expr",
298 "cimag",
299 "cimagf",
300 "cimagl",
301 "classify_type",
302 "clrsb",
303 "clrsbl",
304 "clrsbll",
305 "clz",
306 "clzl",
307 "clzll",
308 "complex",
309 "conj",
310 "conjf",
311 "conjl",
312 "constant_p",
313 "copysign",
314 "copysignf",
315 "copysignl",
316 "cproj",
317 "cprojf",
318 "cprojl",
319 "creal",
320 "crealf",
321 "creall",
322 "ctz",
323 "ctzl",
324 "ctzll",
325 "dynamic_object_size",
326 "expect",
327 "expect_with_probability",
328 "fabs",
329 "fabsf",
330 "fabsl",
331 "ffs",
332 "ffsl",
333 "ffsll",
334 "fpclassify",
335 "huge_val",
336 "huge_valf",
337 "huge_vall",
338 "inf",
339 "inff",
340 "infl",
341 "isfinite",
342 "isgreater",
343 "isgreaterequal",
344 "isinf",
345 "isinf_sign",
346 "isinff",
347 "isinfl",
348 "isless",
349 "islessequal",
350 "islessgreater",
351 "isnan",
352 "isnanf",
353 "isnanl",
354 "isnormal",
355 "issignaling",
356 "isunordered",
357 "mul_overflow",
358 "mul_overflow_p",
359 "nan",
360 "nanf",
361 "nanl",
362 "nans",
363 "nansf",
364 "nansl",
365 "object_size",
366 "offsetof",
367 "parity",
368 "parityl",
369 "parityll",
370 "popcount",
371 "popcountl",
372 "popcountll",
373 "prefetch",
374 "signbit",
375 "signbitf",
376 "signbitl",
377 "sub_overflow",
378 "sub_overflow_p",
379 "trap",
380 "types_compatible_p",
381 "unreachable",
382 "va_arg",
383 "va_copy",
384 "va_end",
385 "va_start",
386 "FILE",
387 "FUNCTION",
388 "LINE",
389];
390
391pub fn long_double_math(rest: &str) -> Option<&'static str> {
399 let base = rest.strip_suffix('l')?;
400 LONG_DOUBLE_MATH.iter().copied().find(|name| *name == base)
401}
402
403const LONG_DOUBLE_MATH: &[&str] = &[
408 "acos",
409 "asin",
410 "atan",
411 "atan2",
412 "cbrt",
413 "ceil",
414 "cos",
415 "cosh",
416 "erf",
417 "erfc",
418 "exp",
419 "exp2",
420 "expm1",
421 "fdim",
422 "floor",
423 "fma",
424 "fmax",
425 "fmin",
426 "fmod",
427 "frexp",
428 "hypot",
429 "ldexp",
430 "lgamma",
431 "log",
432 "log10",
433 "log1p",
434 "log2",
435 "modf",
436 "nearbyint",
437 "nextafter",
438 "pow",
439 "remainder",
440 "rint",
441 "round",
442 "scalbn",
443 "sin",
444 "sinh",
445 "sqrt",
446 "tan",
447 "tanh",
448 "tgamma",
449 "trunc",
450];
451
452pub fn typed_overflow(rest: &str) -> Option<&'static str> {
458 let rest = rest.strip_prefix('s').or_else(|| rest.strip_prefix('u'))?;
459 let (op, rest) = if let Some(rest) = rest.strip_prefix("add") {
460 ("add", rest)
461 } else if let Some(rest) = rest.strip_prefix("sub") {
462 ("sub", rest)
463 } else {
464 ("mul", rest.strip_prefix("mul")?)
465 };
466 matches!(rest, "_overflow" | "l_overflow" | "ll_overflow").then_some(op)
467}
468
469pub const LIBRARY_BUILTINS: &[&str] = &[
475 "_Exit",
476 "abort",
477 "abs",
478 "acos",
479 "acosf",
480 "asin",
481 "asinf",
482 "atan",
483 "atan2",
484 "atan2f",
485 "atanf",
486 "atof",
487 "atoi",
488 "atol",
489 "atoll",
490 "bcmp",
491 "bcopy",
492 "bzero",
493 "calloc",
494 "cbrt",
495 "cbrtf",
496 "ceil",
497 "ceilf",
498 "cos",
499 "cosf",
500 "cosh",
501 "coshf",
502 "erf",
503 "erfc",
504 "exit",
505 "exp",
506 "exp2",
507 "exp2f",
508 "expf",
509 "expm1",
510 "expm1f",
511 "fdim",
512 "fdimf",
513 "floor",
514 "floorf",
515 "fma",
516 "fmaf",
517 "fmax",
518 "fmaxf",
519 "fmin",
520 "fminf",
521 "fmod",
522 "fmodf",
523 "fprintf",
524 "fputc",
525 "fputs",
526 "free",
527 "frexp",
528 "frexpf",
529 "hypot",
530 "hypotf",
531 "imaxabs",
532 "index",
533 "isalnum",
534 "isalpha",
535 "isblank",
536 "iscntrl",
537 "isdigit",
538 "isgraph",
539 "islower",
540 "isprint",
541 "ispunct",
542 "isspace",
543 "isupper",
544 "isxdigit",
545 "labs",
546 "ldexp",
547 "ldexpf",
548 "lgamma",
549 "llabs",
550 "log",
551 "log10",
552 "log10f",
553 "log1p",
554 "log1pf",
555 "log2",
556 "log2f",
557 "logf",
558 "malloc",
559 "memchr",
560 "memcmp",
561 "memcpy",
562 "memmove",
563 "mempcpy",
564 "memset",
565 "modf",
566 "modff",
567 "nearbyint",
568 "nearbyintf",
569 "nextafter",
570 "nextafterf",
571 "pow",
572 "powf",
573 "printf",
574 "putchar",
575 "puts",
576 "realloc",
577 "remainder",
578 "remainderf",
579 "rindex",
580 "rint",
581 "rintf",
582 "round",
583 "roundf",
584 "scalbn",
585 "scalbnf",
586 "sin",
587 "sinf",
588 "sinh",
589 "sinhf",
590 "snprintf",
591 "sprintf",
592 "sqrt",
593 "sqrtf",
594 "stpcpy",
595 "stpncpy",
596 "strcasecmp",
597 "strcat",
598 "strchr",
599 "strcmp",
600 "strcoll",
601 "strcpy",
602 "strcspn",
603 "strdup",
604 "strlen",
605 "strncasecmp",
606 "strncat",
607 "strncmp",
608 "strncpy",
609 "strpbrk",
610 "strrchr",
611 "strspn",
612 "strstr",
613 "strtod",
614 "strtof",
615 "strtol",
616 "strtoll",
617 "strtoul",
618 "strtoull",
619 "tan",
620 "tanf",
621 "tanh",
622 "tanhf",
623 "tgamma",
624 "tolower",
625 "toupper",
626 "trunc",
627 "truncf",
628];