#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum Attribute {
Noreturn,
AlwaysInline,
NoInline,
Cold,
Hot,
Deprecated,
Fallthrough,
Packed,
Aligned,
Section,
Target,
Constructor,
Destructor,
Cleanup,
Mode,
Safe,
Weak,
Unsupported,
Ignored,
}
pub const UNSUPPORTED_ATTRIBUTES: &[(&str, &str)] = &[
(
"weakref",
"is not supported: Rust's `#[linkage]` is unstable, so weak linkage cannot be asked for",
),
(
"alias",
"is not supported: write a function that forwards to the other one instead",
),
(
"ifunc",
"is not supported: choosing an implementation at load time has no stable Rust counterpart",
),
(
"vector_size",
"is not supported: the vector extensions need `core::simd`, which is unstable. \
The Intel intrinsics are the SIMD cinrs has: <immintrin.h> declares them and a call \
becomes the `core::arch` function of the same name",
),
(
"scalar_storage_order",
"is not supported: it reverses the byte order of every scalar in the record, and \
nothing in the generated Rust could carry that",
),
];
pub fn attribute(name: &str) -> Option<Attribute> {
let bare = name
.strip_prefix("__")
.and_then(|rest| rest.strip_suffix("__"))
.unwrap_or(name);
if UNSUPPORTED_ATTRIBUTES.iter().any(|(n, _)| *n == bare) {
return Some(Attribute::Unsupported);
}
Some(match bare {
"noreturn" => Attribute::Noreturn,
"always_inline" => Attribute::AlwaysInline,
"noinline" => Attribute::NoInline,
"cold" => Attribute::Cold,
"hot" => Attribute::Hot,
"deprecated" => Attribute::Deprecated,
"fallthrough" => Attribute::Fallthrough,
"packed" => Attribute::Packed,
"aligned" | "alignas" => Attribute::Aligned,
"section" => Attribute::Section,
"target" => Attribute::Target,
"constructor" => Attribute::Constructor,
"destructor" => Attribute::Destructor,
"cleanup" => Attribute::Cleanup,
"mode" => Attribute::Mode,
"weak" => Attribute::Weak,
"cinrs_safe" => Attribute::Safe,
_ if IGNORED_ATTRIBUTES.contains(&bare) => Attribute::Ignored,
_ => return None,
})
}
pub const CINRS_ATTRIBUTES: &[&str] = &["safe"];
pub fn cinrs_attribute(name: &str) -> Option<Attribute> {
match name {
"safe" => Some(Attribute::Safe),
_ => None,
}
}
pub fn unsupported_reason(name: &str) -> Option<&'static str> {
let bare = name
.strip_prefix("__")
.and_then(|rest| rest.strip_suffix("__"))
.unwrap_or(name);
UNSUPPORTED_ATTRIBUTES
.iter()
.find(|(n, _)| *n == bare)
.map(|(_, reason)| *reason)
}
const IGNORED_ATTRIBUTES: &[&str] = &[
"access",
"alloc_align",
"alloc_size",
"artificial",
"assume_aligned",
"cdecl",
"const",
"designated_init",
"error",
"externally_visible",
"fastcall",
"flatten",
"format",
"format_arg",
"gnu_inline",
"leaf",
"malloc",
"may_alias",
"maybe_unused",
"no_instrument_function",
"no_sanitize",
"no_split_stack",
"noclone",
"nodiscard",
"noipa",
"nonnull",
"nonstring",
"nothrow",
"optimize",
"pure",
"reproducible",
"returns_nonnull",
"returns_twice",
"sentinel",
"stdcall",
"target_clones",
"transparent_union",
"unavailable",
"unsequenced",
"unused",
"used",
"visibility",
"warn_unused_result",
"warning",
];
pub fn has_attribute(name: &str) -> bool {
attribute(name).is_some_and(|a| !matches!(a, Attribute::Unsupported | Attribute::Weak))
}
pub fn has_c_attribute(name: &str) -> u64 {
let standard = matches!(
name,
"deprecated"
| "fallthrough"
| "maybe_unused"
| "nodiscard"
| "noreturn"
| "unsequenced"
| "reproducible"
);
if standard { 202_311 } else { 0 }
}
pub fn has_builtin(name: &str) -> bool {
if crate::sema::is_atomic_builtin(name) {
return true;
}
let Some(rest) = name.strip_prefix("__builtin_") else {
return false;
};
SPECIAL_BUILTINS.contains(&rest)
|| LIBRARY_BUILTINS.contains(&rest)
|| long_double_math(rest).is_some()
}
pub fn has_feature(name: &str) -> bool {
SUPPORTED_FEATURES.contains(&name)
}
const SUPPORTED_FEATURES: &[&str] = &[
"c_alignas",
"c_alignof",
"c_atomic",
"c_attributes",
"c_generic_selection",
"c_generic_selections",
"c_static_assert",
"cinrs",
];
pub const SPECIAL_BUILTINS: &[&str] = &[
"add_overflow",
"add_overflow_p",
"alloca",
"alloca_with_align",
"assume",
"assume_aligned",
"bswap16",
"bswap32",
"bswap64",
"choose_expr",
"cimag",
"cimagf",
"cimagl",
"classify_type",
"clrsb",
"clrsbl",
"clrsbll",
"clz",
"clzl",
"clzll",
"complex",
"conj",
"conjf",
"conjl",
"constant_p",
"copysign",
"copysignf",
"copysignl",
"cpu_init",
"cpu_supports",
"cproj",
"cprojf",
"cprojl",
"creal",
"crealf",
"creall",
"ctz",
"ctzl",
"ctzll",
"dynamic_object_size",
"expect",
"expect_with_probability",
"fabs",
"fabsf",
"fabsl",
"ffs",
"ffsl",
"ffsll",
"fpclassify",
"huge_val",
"huge_valf",
"huge_valf32",
"huge_valf32x",
"huge_valf64",
"huge_valf64x",
"huge_vall",
"inf",
"inff",
"inff32",
"inff32x",
"inff64",
"inff64x",
"infl",
"isfinite",
"isgreater",
"isgreaterequal",
"isinf",
"isinf_sign",
"isinff",
"isinfl",
"isless",
"islessequal",
"islessgreater",
"isnan",
"isnanf",
"isnanl",
"isnormal",
"issignaling",
"isunordered",
"mul_overflow",
"mul_overflow_p",
"nan",
"nanf",
"nanf32",
"nanf32x",
"nanf64",
"nanf64x",
"nanl",
"nans",
"nansf",
"nansf32",
"nansf32x",
"nansf64",
"nansf64x",
"nansl",
"object_size",
"offsetof",
"parity",
"parityl",
"parityll",
"popcount",
"popcountl",
"popcountll",
"prefetch",
"signbit",
"signbitf",
"signbitl",
"sub_overflow",
"sub_overflow_p",
"trap",
"types_compatible_p",
"unreachable",
"va_arg",
"va_copy",
"va_end",
"va_start",
"FILE",
"FUNCTION",
"LINE",
];
pub fn long_double_math(rest: &str) -> Option<&'static str> {
let base = rest.strip_suffix('l')?;
LONG_DOUBLE_MATH.iter().copied().find(|name| *name == base)
}
const LONG_DOUBLE_MATH: &[&str] = &[
"acos",
"asin",
"atan",
"atan2",
"cbrt",
"ceil",
"cos",
"cosh",
"erf",
"erfc",
"exp",
"exp2",
"expm1",
"fdim",
"floor",
"fma",
"fmax",
"fmin",
"fmod",
"frexp",
"hypot",
"ldexp",
"lgamma",
"log",
"log10",
"log1p",
"log2",
"modf",
"nearbyint",
"nextafter",
"pow",
"remainder",
"rint",
"round",
"scalbn",
"sin",
"sinh",
"sqrt",
"tan",
"tanh",
"tgamma",
"trunc",
];
pub fn typed_overflow(rest: &str) -> Option<&'static str> {
let rest = rest.strip_prefix('s').or_else(|| rest.strip_prefix('u'))?;
let (op, rest) = if let Some(rest) = rest.strip_prefix("add") {
("add", rest)
} else if let Some(rest) = rest.strip_prefix("sub") {
("sub", rest)
} else {
("mul", rest.strip_prefix("mul")?)
};
matches!(rest, "_overflow" | "l_overflow" | "ll_overflow").then_some(op)
}
pub const LIBRARY_BUILTINS: &[&str] = &[
"_Exit",
"abort",
"abs",
"acos",
"acosf",
"asin",
"asinf",
"atan",
"atan2",
"atan2f",
"atanf",
"atof",
"atoi",
"atol",
"atoll",
"bcmp",
"bcopy",
"bzero",
"calloc",
"cbrt",
"cbrtf",
"ceil",
"ceilf",
"cos",
"cosf",
"cosh",
"coshf",
"erf",
"erfc",
"exit",
"exp",
"exp2",
"exp2f",
"expf",
"expm1",
"expm1f",
"fdim",
"fdimf",
"floor",
"floorf",
"fma",
"fmaf",
"fmax",
"fmaxf",
"fmin",
"fminf",
"fmod",
"fmodf",
"fprintf",
"fputc",
"fputs",
"free",
"frexp",
"frexpf",
"hypot",
"hypotf",
"imaxabs",
"index",
"isalnum",
"isalpha",
"isblank",
"iscntrl",
"isdigit",
"isgraph",
"islower",
"isprint",
"ispunct",
"isspace",
"isupper",
"isxdigit",
"labs",
"ldexp",
"ldexpf",
"lgamma",
"llabs",
"log",
"log10",
"log10f",
"log1p",
"log1pf",
"log2",
"log2f",
"logf",
"malloc",
"memchr",
"memcmp",
"memcpy",
"memmove",
"mempcpy",
"memset",
"modf",
"modff",
"nearbyint",
"nearbyintf",
"nextafter",
"nextafterf",
"pow",
"powf",
"printf",
"putchar",
"puts",
"realloc",
"remainder",
"remainderf",
"rindex",
"rint",
"rintf",
"round",
"roundf",
"scalbn",
"scalbnf",
"sin",
"sinf",
"sinh",
"sinhf",
"snprintf",
"sprintf",
"sqrt",
"sqrtf",
"stpcpy",
"stpncpy",
"strcasecmp",
"strcat",
"strchr",
"strcmp",
"strcoll",
"strcpy",
"strcspn",
"strdup",
"strlen",
"strncasecmp",
"strncat",
"strncmp",
"strncpy",
"strpbrk",
"strrchr",
"strspn",
"strstr",
"strtod",
"strtof",
"strtol",
"strtoll",
"strtoul",
"strtoull",
"tan",
"tanf",
"tanh",
"tanhf",
"tgamma",
"tolower",
"toupper",
"trunc",
"truncf",
];