diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index f70c34199acec..50a8f4111195d 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -1148,9 +1148,12 @@ macro_rules! uint_impl { pub const fn checked_ilog(self, base: Self) -> Option { if self <= 0 || base <= 1 { None + } else if self < base { + Some(0) } else { - let mut n = 0; - let mut r = 1; + // Since base >= self, n >= 1 + let mut n = 1; + let mut r = base; // Optimization for 128 bit wide integers. if Self::BITS == 128 { diff --git a/src/tools/run-make-support/src/cc.rs b/src/tools/run-make-support/src/cc.rs index 799c36b104999..0012e7b66af35 100644 --- a/src/tools/run-make-support/src/cc.rs +++ b/src/tools/run-make-support/src/cc.rs @@ -80,6 +80,13 @@ impl Cc { self } + /// Specify path of the output binary. + pub fn output>(&mut self, path: P) -> &mut Self { + self.cmd.arg("-o"); + self.cmd.arg(path.as_ref()); + self + } + /// Get the [`Output`][::std::process::Output] of the finished process. pub fn command_output(&mut self) -> ::std::process::Output { self.cmd.output().expect("failed to get output of finished process") diff --git a/src/tools/run-make-support/src/lib.rs b/src/tools/run-make-support/src/lib.rs index 0cf64db6ac9c0..323fc40e6481e 100644 --- a/src/tools/run-make-support/src/lib.rs +++ b/src/tools/run-make-support/src/lib.rs @@ -123,12 +123,23 @@ pub fn dynamic_lib_name(name: &str) -> String { // ``` assert!(!name.contains(char::is_whitespace), "dynamic library name cannot contain whitespace"); + let extension = dynamic_lib_extension(); if is_darwin() { - format!("lib{name}.dylib") + format!("lib{name}.{extension}") } else if is_windows() { - format!("{name}.dll") + format!("{name}.{extension}") } else { - format!("lib{name}.so") + format!("lib{name}.{extension}") + } +} + +pub fn dynamic_lib_extension() -> &'static str { + if is_darwin() { + "dylib" + } else if is_windows() { + "dll" + } else { + "so" } } @@ -249,16 +260,13 @@ pub fn recursive_diff(dir1: impl AsRef, dir2: impl AsRef) { } let dir2 = dir2.as_ref(); - for entry in fs::read_dir(dir1).unwrap() { - let entry = entry.unwrap(); - let entry_name = entry.file_name(); - let path = entry.path(); - - if path.is_dir() { - recursive_diff(&path, &dir2.join(entry_name)); + read_dir(dir1, |entry_path| { + let entry_name = entry_path.file_name().unwrap(); + if entry_path.is_dir() { + recursive_diff(&entry_path, &dir2.join(entry_name)); } else { let path2 = dir2.join(entry_name); - let file1 = read_file(&path); + let file1 = read_file(&entry_path); let file2 = read_file(&path2); // We don't use `assert_eq!` because they are `Vec`, so not great for display. @@ -267,10 +275,16 @@ pub fn recursive_diff(dir1: impl AsRef, dir2: impl AsRef) { assert!( file1 == file2, "`{}` and `{}` have different content", - path.display(), + entry_path.display(), path2.display(), ); } + }); +} + +pub fn read_dir(dir: impl AsRef, callback: F) { + for entry in fs::read_dir(dir).unwrap() { + callback(&entry.unwrap().path()); } } diff --git a/src/tools/tidy/src/allowed_run_make_makefiles.txt b/src/tools/tidy/src/allowed_run_make_makefiles.txt index 009200aca15de..57637f7ae642b 100644 --- a/src/tools/tidy/src/allowed_run_make_makefiles.txt +++ b/src/tools/tidy/src/allowed_run_make_makefiles.txt @@ -6,7 +6,6 @@ run-make/bare-outfile/Makefile run-make/branch-protection-check-IBT/Makefile run-make/c-dynamic-dylib/Makefile run-make/c-dynamic-rlib/Makefile -run-make/c-link-to-rust-dylib/Makefile run-make/c-static-dylib/Makefile run-make/c-static-rlib/Makefile run-make/c-unwind-abi-catch-lib-panic/Makefile @@ -14,7 +13,6 @@ run-make/c-unwind-abi-catch-panic/Makefile run-make/cat-and-grep-sanity-check/Makefile run-make/cdylib-dylib-linkage/Makefile run-make/cdylib-fewer-symbols/Makefile -run-make/cdylib/Makefile run-make/codegen-options-parsing/Makefile run-make/comment-section/Makefile run-make/compiler-lookup-paths-2/Makefile diff --git a/tests/codegen/checked_ilog.rs b/tests/codegen/checked_ilog.rs new file mode 100644 index 0000000000000..8f3c07119feeb --- /dev/null +++ b/tests/codegen/checked_ilog.rs @@ -0,0 +1,20 @@ +//@ compile-flags: -O + +#![crate_type = "lib"] + +// Ensure that when val < base, we do not divide or multiply. + +// CHECK-LABEL: @checked_ilog +// CHECK-SAME: (i16 noundef %val, i16 noundef %base) +#[no_mangle] +pub fn checked_ilog(val: u16, base: u16) -> Option { + // CHECK-NOT: udiv + // CHECK-NOT: mul + // CHECK: %[[IS_LESS:.+]] = icmp ult i16 %val, %base + // CHECK-NEXT: br i1 %[[IS_LESS]], label %[[TRUE:.+]], label %[[FALSE:.+]] + // CHECK: [[TRUE]]: + // CHECK-NOT: udiv + // CHECK-NOT: mul + // CHECK: ret { i32, i32 } + val.checked_ilog(base) +} diff --git a/tests/run-make/c-link-to-rust-dylib/Makefile b/tests/run-make/c-link-to-rust-dylib/Makefile deleted file mode 100644 index 201f717ece49c..0000000000000 --- a/tests/run-make/c-link-to-rust-dylib/Makefile +++ /dev/null @@ -1,21 +0,0 @@ -# This test checks that C linking with Rust does not encounter any errors, with dynamic libraries. -# See https://github.com/rust-lang/rust/issues/10434 - -# ignore-cross-compile -include ../tools.mk - -all: $(TMPDIR)/$(call BIN,bar) - $(call RUN,bar) - $(call REMOVE_DYLIBS,foo) - $(call FAIL,bar) - -ifdef IS_MSVC -$(TMPDIR)/$(call BIN,bar): $(call DYLIB,foo) - $(CC) bar.c $(TMPDIR)/foo.dll.lib $(call OUT_EXE,bar) -else -$(TMPDIR)/$(call BIN,bar): $(call DYLIB,foo) - $(CC) bar.c -lfoo -o $(call RUN_BINFILE,bar) -L $(TMPDIR) -endif - -$(call DYLIB,foo): foo.rs - $(RUSTC) foo.rs diff --git a/tests/run-make/c-link-to-rust-dylib/rmake.rs b/tests/run-make/c-link-to-rust-dylib/rmake.rs new file mode 100644 index 0000000000000..076bf56334d33 --- /dev/null +++ b/tests/run-make/c-link-to-rust-dylib/rmake.rs @@ -0,0 +1,41 @@ +// This test checks that C linking with Rust does not encounter any errors, with dynamic libraries. +// See . + +//@ ignore-cross-compile + +use std::fs::remove_file; + +use run_make_support::{ + dynamic_lib_extension, is_msvc, read_dir, run, run_fail, rustc, tmp_dir, Cc, +}; + +fn main() { + rustc().input("foo.rs").run(); + + if is_msvc() { + Cc::new().input("bar.c").arg(tmp_dir().join("foo.dll.lib")).out_exe("bar").run(); + } else { + Cc::new() + .input("bar.c") + .arg("-lfoo") + .output(tmp_dir().join("bar")) + .library_search_path(tmp_dir()) + .run(); + } + + run("bar"); + + let expected_extension = dynamic_lib_extension(); + read_dir(tmp_dir(), |path| { + if path.is_file() + && path.extension().is_some_and(|ext| ext == expected_extension) + && path + .file_name() + .and_then(|name| name.to_str()) + .is_some_and(|name| name.starts_with("lib")) + { + remove_file(path).unwrap(); + } + }); + run_fail("bar"); +} diff --git a/tests/run-make/cdylib/Makefile b/tests/run-make/cdylib/Makefile deleted file mode 100644 index 2c6414c32553c..0000000000000 --- a/tests/run-make/cdylib/Makefile +++ /dev/null @@ -1,23 +0,0 @@ -# When the cdylib crate type was added as a variation of dylib, it needed a test to check its function. -# See https://github.com/rust-lang/rust/pull/33553 - -# ignore-cross-compile -include ../tools.mk - -all: $(call RUN_BINFILE,foo) - $(call RUN,foo) - rm $(call DYLIB,foo) - $(RUSTC) foo.rs -C lto - $(call RUN,foo) - -ifdef IS_MSVC -$(call RUN_BINFILE,foo): $(call DYLIB,foo) - $(CC) $(CFLAGS) foo.c $(TMPDIR)/foo.dll.lib $(call OUT_EXE,foo) -else -$(call RUN_BINFILE,foo): $(call DYLIB,foo) - $(CC) $(CFLAGS) foo.c -lfoo -o $(call RUN_BINFILE,foo) -L $(TMPDIR) -endif - -$(call DYLIB,foo): - $(RUSTC) bar.rs - $(RUSTC) foo.rs diff --git a/tests/run-make/cdylib/rmake.rs b/tests/run-make/cdylib/rmake.rs new file mode 100644 index 0000000000000..fcb4f56621f65 --- /dev/null +++ b/tests/run-make/cdylib/rmake.rs @@ -0,0 +1,36 @@ +// This test tries to check that basic cdylib libraries can be compiled and linked successfully +// with C code, that the cdylib itself can depend on another rlib, and that the library can be built +// with LTO. +// +// - `bar.rs` is a rlib +// - `foo.rs` is a cdylib that relies on an extern crate `bar` and defines two `extern "C"` +// functions: +// - `foo()` which calls `bar::bar()`. +// - `bar()` which implements basic addition. + +//@ ignore-cross-compile + +use std::fs::remove_file; + +use run_make_support::{cc, dynamic_lib, is_msvc, run, rustc, tmp_dir}; + +fn main() { + rustc().input("bar.rs").run(); + rustc().input("foo.rs").run(); + + if is_msvc() { + cc().input("foo.c").arg(tmp_dir().join("foo.dll.lib")).out_exe("foo").run(); + } else { + cc().input("foo.c") + .arg("-lfoo") + .output(tmp_dir().join("foo")) + .library_search_path(tmp_dir()) + .run(); + } + + run("foo"); + remove_file(dynamic_lib("foo")).unwrap(); + + rustc().input("foo.rs").arg("-Clto").run(); + run("foo"); +} diff --git a/tests/run-make/print-check-cfg/rmake.rs b/tests/run-make/print-check-cfg/rmake.rs index f7f5fcf2340bb..2d52216442653 100644 --- a/tests/run-make/print-check-cfg/rmake.rs +++ b/tests/run-make/print-check-cfg/rmake.rs @@ -8,61 +8,70 @@ use std::ops::Deref; use run_make_support::rustc; +struct CheckCfg { + args: &'static [&'static str], + contains: Contains, +} + +enum Contains { + Some { contains: &'static [&'static str], doesnt_contain: &'static [&'static str] }, + Only(&'static str), +} + fn main() { - check( - /*args*/ &[], - /*has_any*/ false, - /*has_any_any*/ true, - /*contains*/ &[], - ); - check( - /*args*/ &["--check-cfg=cfg()"], - /*has_any*/ false, - /*has_any_any*/ false, - /*contains*/ &["unix", "miri"], - ); - check( - /*args*/ &["--check-cfg=cfg(any())"], - /*has_any*/ true, - /*has_any_any*/ false, - /*contains*/ &["windows", "test"], - ); - check( - /*args*/ &["--check-cfg=cfg(feature)"], - /*has_any*/ false, - /*has_any_any*/ false, - /*contains*/ &["unix", "miri", "feature"], - ); - check( - /*args*/ &[r#"--check-cfg=cfg(feature, values(none(), "", "test", "lol"))"#], - /*has_any*/ false, - /*has_any_any*/ false, - /*contains*/ &["feature", "feature=\"\"", "feature=\"test\"", "feature=\"lol\""], - ); - check( - /*args*/ - &[ + check(CheckCfg { args: &[], contains: Contains::Only("any()=any()") }); + check(CheckCfg { + args: &["--check-cfg=cfg()"], + contains: Contains::Some { + contains: &["unix", "miri"], + doesnt_contain: &["any()", "any()=any()"], + }, + }); + check(CheckCfg { + args: &["--check-cfg=cfg(any())"], + contains: Contains::Some { + contains: &["any()", "unix", r#"target_feature="crt-static""#], + doesnt_contain: &["any()=any()"], + }, + }); + check(CheckCfg { + args: &["--check-cfg=cfg(feature)"], + contains: Contains::Some { + contains: &["unix", "miri", "feature"], + doesnt_contain: &["any()", "any()=any()", "feature=none()", "feature="], + }, + }); + check(CheckCfg { + args: &[r#"--check-cfg=cfg(feature, values(none(), "", "test", "lol"))"#], + contains: Contains::Some { + contains: &["feature", "feature=\"\"", "feature=\"test\"", "feature=\"lol\""], + doesnt_contain: &["any()", "any()=any()", "feature=none()", "feature="], + }, + }); + check(CheckCfg { + args: &[ r#"--check-cfg=cfg(feature, values(any()))"#, r#"--check-cfg=cfg(feature, values("tmp"))"#, ], - /*has_any*/ false, - /*has_any_any*/ false, - /*contains*/ &["unix", "miri", "feature=any()"], - ); - check( - /*args*/ - &[ + contains: Contains::Some { + contains: &["unix", "miri", "feature=any()"], + doesnt_contain: &["any()", "any()=any()", "feature", "feature=", "feature=\"tmp\""], + }, + }); + check(CheckCfg { + args: &[ r#"--check-cfg=cfg(has_foo, has_bar)"#, r#"--check-cfg=cfg(feature, values("tmp"))"#, r#"--check-cfg=cfg(feature, values("tmp"))"#, ], - /*has_any*/ false, - /*has_any_any*/ false, - /*contains*/ &["has_foo", "has_bar", "feature=\"tmp\""], - ); + contains: Contains::Some { + contains: &["has_foo", "has_bar", "feature=\"tmp\""], + doesnt_contain: &["any()", "any()=any()", "feature"], + }, + }); } -fn check(args: &[&str], has_any: bool, has_any_any: bool, contains: &[&str]) { +fn check(CheckCfg { args, contains }: CheckCfg) { let output = rustc() .input("lib.rs") .arg("-Zunstable-options") @@ -72,18 +81,11 @@ fn check(args: &[&str], has_any: bool, has_any_any: bool, contains: &[&str]) { let stdout = String::from_utf8(output.stdout).unwrap(); - let mut found_any = false; - let mut found_any_any = false; let mut found = HashSet::::new(); - let mut recorded = HashSet::::new(); for l in stdout.lines() { assert!(l == l.trim()); - if l == "any()" { - found_any = true; - } else if l == "any()=any()" { - found_any_any = true; - } else if let Some((left, right)) = l.split_once('=') { + if let Some((left, right)) = l.split_once('=') { if right != "any()" && right != "" { assert!(right.starts_with("\"")); assert!(right.ends_with("\"")); @@ -92,17 +94,37 @@ fn check(args: &[&str], has_any: bool, has_any_any: bool, contains: &[&str]) { } else { assert!(!l.contains("\"")); } - assert!(recorded.insert(l.to_string()), "{}", &l); - if contains.contains(&l) { - assert!(found.insert(l.to_string()), "{}", &l); - } + assert!(found.insert(l.to_string()), "{}", &l); } - let should_found = HashSet::::from_iter(contains.iter().map(|s| s.to_string())); - let diff: Vec<_> = should_found.difference(&found).collect(); - - assert_eq!(found_any, has_any); - assert_eq!(found_any_any, has_any_any); - assert_eq!(found_any_any, recorded.len() == 1); - assert!(diff.is_empty(), "{:?} != {:?} (~ {:?})", &should_found, &found, &diff); + match contains { + Contains::Some { contains, doesnt_contain } => { + { + let should_found = + HashSet::::from_iter(contains.iter().map(|s| s.to_string())); + let diff: Vec<_> = should_found.difference(&found).collect(); + assert!( + diff.is_empty(), + "should found: {:?}, didn't found {:?}", + &should_found, + &diff + ); + } + { + let should_not_find = + HashSet::::from_iter(doesnt_contain.iter().map(|s| s.to_string())); + let diff: Vec<_> = should_not_find.intersection(&found).collect(); + assert!( + diff.is_empty(), + "should not find {:?}, did found {:?}", + &should_not_find, + &diff + ); + } + } + Contains::Only(only) => { + assert!(found.contains(&only.to_string()), "{:?} != {:?}", &only, &found); + assert!(found.len() == 1, "len: {}, instead of 1", found.len()); + } + } }