diff --git a/library/core/src/ffi/mod.rs b/library/core/src/ffi/mod.rs index 5f32775822be6..2ba4370c7b7ee 100644 --- a/library/core/src/ffi/mod.rs +++ b/library/core/src/ffi/mod.rs @@ -66,7 +66,7 @@ type_alias! { "c_longlong.md", c_longlong = i64; } type_alias! { "c_ulonglong.md", c_ulonglong = u64; } type_alias! { "c_float.md", c_float = f32; } -type_alias! { "c_double.md", c_double = f64; } +type_alias! { "c_double.md", c_double = c_double_definition::c_double; #[doc(cfg(all()))]} /// Equivalent to C's `size_t` type, from `stddef.h` (or `cstddef` for C++). /// @@ -205,6 +205,16 @@ mod c_long_definition { } } +mod c_double_definition { + cfg_if! { + if #[cfg(all(target_arch = "avr"))] { + pub type c_double = f32; + } else { + pub type c_double = f64; + } + } +} + // N.B., for LLVM to recognize the void pointer type and by extension // functions like malloc(), we need to have it represented as i8* in // LLVM bitcode. The enum used here ensures this and prevents misuse diff --git a/tests/auxiliary/minicore.rs b/tests/auxiliary/minicore.rs index a68552175c318..34f78f48ac593 100644 --- a/tests/auxiliary/minicore.rs +++ b/tests/auxiliary/minicore.rs @@ -108,3 +108,127 @@ macro_rules! stringify { /* compiler built-in */ }; } + +macro_rules! cfg_if { + // match if/else chains with a final `else` + ( + $( + if #[cfg( $i_meta:meta )] { $( $i_tokens:tt )* } + ) else+ + else { $( $e_tokens:tt )* } + ) => { + cfg_if! { + @__items () ; + $( + (( $i_meta ) ( $( $i_tokens )* )) , + )+ + (() ( $( $e_tokens )* )) , + } + }; + + // Internal and recursive macro to emit all the items + // + // Collects all the previous cfgs in a list at the beginning, so they can be + // negated. After the semicolon is all the remaining items. + (@__items ( $( $_:meta , )* ) ; ) => {}; + ( + @__items ( $( $no:meta , )* ) ; + (( $( $yes:meta )? ) ( $( $tokens:tt )* )) , + $( $rest:tt , )* + ) => { + // Emit all items within one block, applying an appropriate #[cfg]. The + // #[cfg] will require all `$yes` matchers specified and must also negate + // all previous matchers. + #[cfg(all( + $( $yes , )? + not(any( $( $no ),* )) + ))] + cfg_if! { @__identity $( $tokens )* } + + // Recurse to emit all other items in `$rest`, and when we do so add all + // our `$yes` matchers to the list of `$no` matchers as future emissions + // will have to negate everything we just matched as well. + cfg_if! { + @__items ( $( $no , )* $( $yes , )? ) ; + $( $rest , )* + } + }; + + // Internal macro to make __apply work out right for different match types, + // because of how macros match/expand stuff. + (@__identity $( $tokens:tt )* ) => { + $( $tokens )* + }; +} + +#[macro_export] +macro_rules! panic { + ($msg:literal) => { + $crate::panic(&$msg) + }; +} + +#[rustc_intrinsic] +#[rustc_intrinsic_const_stable_indirect] +#[rustc_intrinsic_must_be_overridden] +pub const fn size_of() -> usize { + loop {} +} + +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +pub const fn abort() -> ! { + loop {} +} + +#[lang = "panic"] +#[rustc_const_panic_str] +const fn panic(_expr: &&'static str) -> ! { + abort(); +} + +#[lang = "eq"] +pub trait PartialEq { + fn eq(&self, other: &Rhs) -> bool; + fn ne(&self, other: &Rhs) -> bool { + !self.eq(other) + } +} + +impl PartialEq for usize { + fn eq(&self, other: &usize) -> bool { + (*self) == (*other) + } +} + +impl PartialEq for bool { + fn eq(&self, other: &bool) -> bool { + (*self) == (*other) + } +} + +#[lang = "bitxor"] +pub trait BitXor { + type Output; + fn bitxor(self, rhs: Rhs) -> Self::Output; +} + +impl BitXor for bool { + type Output = bool; + fn bitxor(self, rhs: bool) -> bool { + (self || rhs) && !(self && rhs) + } +} + +#[lang = "not"] +pub trait Not { + type Output; + fn not(self) -> Self::Output; +} + +impl Not for bool { + type Output = bool; + fn not(self) -> Self { + !self + } +} diff --git a/tests/run-make/core-ffi-typecheck/rmake.rs b/tests/run-make/core-ffi-typecheck/rmake.rs new file mode 100644 index 0000000000000..0856b5b4d362e --- /dev/null +++ b/tests/run-make/core-ffi-typecheck/rmake.rs @@ -0,0 +1,163 @@ +use run_make_support::{clang, regex, rfs, rustc}; + +const SKIPPED_TARGETS: &[&str] = &[ + "riscv", //error: unknown target triple 'riscv32e-unknown-none-elf' + "wasm", //error: unknown target triple 'wasm32v1-none' + "xtensa", //error: unknown target triple 'xtensa-esp32-espidf' +]; + +fn main() { + let targets = get_target_list(); + + let minicore_path = run_make_support::source_root().join("tests/auxiliary/minicore.rs"); + + regex_mod(); + + for target in targets.lines() { + if SKIPPED_TARGETS.iter().any(|prefix| target.starts_with(prefix)) { + continue; + } + + let clang_output = + clang().args(&["-E", "-dM", "-x", "c", "/dev/null", "-target", target]).run(); + + let defines = String::from_utf8(clang_output.stdout()).expect("Invalid UTF-8"); + + let minicore_content = rfs::read_to_string(&minicore_path); + let mut rmake_content = format!( + r#" + #![no_std] + #![no_core] + #![feature(intrinsics)] + #![feature(link_cfg)] + #![allow(unused)] + #![crate_type = "rlib"] + {} + #[path = "processed_mod.rs"] + mod ffi; + #[path = "tests.rs"] + mod tests; + "#, + minicore_content + ); + + rmake_content.push_str(&format!( + " + const CLANG_C_CHAR_SIZE: usize = {}; + const CLANG_C_CHAR_SIGNED: bool = {}; + const CLANG_C_SHORT_SIZE: usize = {}; + const CLANG_C_INT_SIZE: usize = {}; + const CLANG_C_LONG_SIZE: usize = {}; + const CLANG_C_LONGLONG_SIZE: usize = {}; + const CLANG_C_FLOAT_SIZE: usize = {}; + const CLANG_C_DOUBLE_SIZE: usize = {}; + ", + parse_size(&defines, "CHAR"), + parse_signed(&defines, "CHAR"), + parse_size(&defines, "SHORT"), + parse_size(&defines, "INT"), + parse_size(&defines, "LONG"), + parse_size(&defines, "LONG_LONG"), + parse_size(&defines, "FLOAT"), + parse_size(&defines, "DOUBLE"), + )); + + // Write to target-specific rmake file + let mut file_name = format!("{}_rmake.rs", target.replace("-", "_")); + + if target.starts_with("thumbv8m") { + file_name = String::from("thumbv8m_rmake.rs"); + } + + rfs::create_file(&file_name); + rfs::write(&file_name, rmake_content); + let rustc_output = rustc() + .arg("-Zunstable-options") + .arg("--emit=metadata") + .arg("--target") + .arg(target) + .arg(&file_name) + .run(); + rfs::remove_file(&file_name); + if !rustc_output.status().success() { + panic!("Failed for target {}", target); + } + } + + // Cleanup + rfs::remove_file("processed_mod.rs"); +} + +fn get_target_list() -> String { + let completed_process = rustc().arg("--print").arg("target-list").run(); + String::from_utf8(completed_process.stdout()).expect("error not a string") +} + +// Helper to parse size from clang defines +fn parse_size(defines: &str, type_name: &str) -> usize { + let search_pattern = format!("__SIZEOF_{}__ ", type_name.to_uppercase()); + for line in defines.lines() { + if line.contains(&search_pattern) { + if let Some(size_str) = line.split_whitespace().last() { + return size_str.parse().unwrap_or(0); + } + } + } + + // Only allow CHAR to default to 1 + if type_name.to_uppercase() == "CHAR" { + return 1; + } + + panic!("Could not find size definition for type: {}", type_name); +} + +// Helper to parse signedness from clang defines +fn parse_signed(defines: &str, type_name: &str) -> bool { + match type_name.to_uppercase().as_str() { + "CHAR" => { + // Check if char is explicitly unsigned + !defines.lines().any(|line| line.contains("__CHAR_UNSIGNED__")) + } + _ => true, + } +} + +// Parse core/ffi/mod.rs to retrieve only necessary macros and type defines +fn regex_mod() { + let mod_path = run_make_support::source_root().join("library/core/src/ffi/mod.rs"); + let mut content = rfs::read_to_string(&mod_path); + + //remove stability features #![unstable] + let mut re = regex::Regex::new(r"#!?\[(un)?stable[^]]*?\]").unwrap(); + content = re.replace_all(&content, "").to_string(); + + //remove doc features #[doc...] + re = regex::Regex::new(r"#\[doc[^]]*?\]").unwrap(); + content = re.replace_all(&content, "").to_string(); + + //remove lang feature #[lang...] + re = regex::Regex::new(r"#\[lang[^]]*?\]").unwrap(); + content = re.replace_all(&content, "").to_string(); + + //remove non inline modules + re = regex::Regex::new(r".*mod.*;").unwrap(); + content = re.replace_all(&content, "").to_string(); + + //remove use + re = regex::Regex::new(r".*use.*;").unwrap(); + content = re.replace_all(&content, "").to_string(); + + //remove fn fmt {...} + re = regex::Regex::new(r"(?s)fn fmt.*?\{.*?\}").unwrap(); + content = re.replace_all(&content, "").to_string(); + + //rmv impl fmt {...} + re = regex::Regex::new(r"(?s)impl fmt::Debug for.*?\{.*?\}").unwrap(); + content = re.replace_all(&content, "").to_string(); + + let file_name = format!("processed_mod.rs"); + + rfs::create_file(&file_name); + rfs::write(&file_name, content); +} diff --git a/tests/run-make/core-ffi-typecheck/tests.rs b/tests/run-make/core-ffi-typecheck/tests.rs new file mode 100644 index 0000000000000..6946db2fe586f --- /dev/null +++ b/tests/run-make/core-ffi-typecheck/tests.rs @@ -0,0 +1,147 @@ +// tests.rs + +use super::*; // `super` will include everything from `smallcore` once glued together + +cfg_if! { + if #[cfg(all(target_os = "windows", target_arch = "aarch64"))] { + const XFAIL_C_LONG_SIZE: usize = 4; + pub const TEST_C_LONG_SIZE: () = if size_of::() != XFAIL_C_LONG_SIZE { + panic!("wrong c_long size test windows aarch64"); + }; + } + else if #[cfg(all(target_arch = "aarch64", target_abi = "ilp32"))] { + const XFAIL_C_LONG_SIZE: usize = 4; + pub const TEST_C_LONG_SIZE: () = if size_of::() != XFAIL_C_LONG_SIZE { + panic!("wrong c_long size test ilp32"); + }; + } + else if #[cfg(all(target_pointer_width = "32", target_os = "watchos"))] { + const XFAIL_C_LONG_SIZE: usize = 4; + pub const TEST_C_LONG_SIZE: () = if size_of::() != XFAIL_C_LONG_SIZE { + panic!("wrong c_long size test watchos"); + }; + } + else if #[cfg(any( + target_arch = "arm", + target_arch = "csky", + target_arch = "hexagon", + target_arch = "x86", + target_arch = "m68k", + target_arch = "mips", + target_arch = "mips32r6", + target_arch = "powerpc", + target_arch = "sparc" + ))] { + const XFAIL_C_LONG_SIZE: usize = 4; + pub const TEST_C_LONG_SIZE: () = if size_of::() != XFAIL_C_LONG_SIZE { + panic!("wrong c_long size test for 32-bit architecture"); + }; + } + else if #[cfg(any( + target_arch = "avr", + target_arch = "msp430" + ))] { + const XFAIL_C_LONG_SIZE: usize = 4; + pub const TEST_C_LONG_SIZE: () = if size_of::() != XFAIL_C_LONG_SIZE { + panic!("wrong c_long size test for embedded architecture"); + }; + } + else if #[cfg(all(target_arch = "x86_64", target_abi="x32"))] { + const XFAIL_C_LONG_SIZE: usize = 4; + pub const TEST_C_LONG_SIZE: () = if size_of::() != XFAIL_C_LONG_SIZE { + panic!("wrong c_long size test x86_64 x32 ABI"); + }; + } + else { + // Default test + pub const TEST_C_LONG_SIZE: () = if size_of::() != CLANG_C_LONG_SIZE { + panic!("wrong c_long size"); + }; + } +} + +cfg_if! { + if #[cfg(target_arch = "csky")] { + const XFAIL_C_CHAR_SIGNED: bool = false; // Change to true for darwin + pub const TEST_C_CHAR_UNSIGNED: () = if ffi::c_char::SIGNED ^ XFAIL_C_CHAR_SIGNED { + panic!("mismatched c_char signed, target_arch: csky"); + }; + } + else if #[cfg(target_arch = "msp430")] { + const XFAIL_C_CHAR_SIGNED: bool = false; // Change to true for darwin + pub const TEST_C_CHAR_UNSIGNED: () = if ffi::c_char::SIGNED ^ XFAIL_C_CHAR_SIGNED { + panic!("mismatched c_char signed, target_arch: msp430"); + }; + } + else { + pub const TEST_C_CHAR_UNSIGNED: () = if ffi::c_char::SIGNED ^ CLANG_C_CHAR_SIGNED { + panic!("mismatched c_char sign"); + }; + } +} + +cfg_if! { + if #[cfg(target_arch = "avr")] { + const XFAIL_C_INT_SIZE: usize = 2; + pub const TEST_C_INT_SIZE: () = if size_of::() != XFAIL_C_INT_SIZE { + panic!("mismatched c_int size, target_arch: avr"); + }; + } + else if #[cfg(target_arch = "msp430")] { + const XFAIL_C_INT_SIZE: usize = 2; // Change to true for darwin + pub const TEST_C_INT_SIZE: () = if size_of::() != XFAIL_C_INT_SIZE { + panic!("mismatched c_int size, target_arch: msp430"); + }; + } + else { + pub const TEST_C_INT_SIZE: () = if size_of::() != CLANG_C_INT_SIZE { + panic!("wrong c_int size"); + }; + } +} + +cfg_if! { + if #[cfg(target_arch = "avr")] { + const XFAIL_C_DOUBLE_SIZE: usize = 4; + pub const TEST_C_DOUBLE_SIZE: () = if size_of::() != XFAIL_C_DOUBLE_SIZE { + panic!("wrong c_double size, target_arch: avr"); + }; + } + else { + pub const TEST_C_DOUBLE_SIZE: () = if size_of::() != CLANG_C_DOUBLE_SIZE { + panic!("wrong c_double size"); + }; + } +} + +trait Signed { + const SIGNED: bool; +} + +impl Signed for i8 { + const SIGNED: bool = true; +} + +impl Signed for u8 { + const SIGNED: bool = false; +} + +//c_char size +pub const TEST_C_CHAR_SIZE: () = if size_of::() != CLANG_C_CHAR_SIZE { + panic!("wrong c_char size"); +}; + +//c_short size +pub const TEST_C_SHORT_SIZE: () = if size_of::() != CLANG_C_SHORT_SIZE { + panic!("wrong c_short size"); +}; + +//c_longlong size +pub const TEST_C_LONGLONG_SIZE: () = if size_of::() != CLANG_C_LONGLONG_SIZE { + panic!("wrong c_longlong size"); +}; + +//c_float size +pub const TEST_C_FLOAT_SIZE: () = if size_of::() != CLANG_C_FLOAT_SIZE { + panic!("wrong c_float size"); +};