From 22769917b0c55786540f2445c6e04fa95fe82c2f Mon Sep 17 00:00:00 2001 From: Mark Rousskov Date: Wed, 2 Sep 2020 16:00:47 -0400 Subject: Avoid spurious print outs during dryrun --- src/bootstrap/check.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/bootstrap') diff --git a/src/bootstrap/check.rs b/src/bootstrap/check.rs index 9f34bb4e6cc..1e26e7754aa 100644 --- a/src/bootstrap/check.rs +++ b/src/bootstrap/check.rs @@ -160,12 +160,12 @@ macro_rules! tool_check_step { &[], ); - println!( + builder.info(&format!( "Checking {} artifacts ({} -> {})", stringify!($name).to_lowercase(), &compiler.host.triple, target.triple - ); + )); run_cargo( builder, cargo, -- cgit 1.4.1-3-g733a5 From af133382f20b2e506b9e14b93e714fb76c7f6902 Mon Sep 17 00:00:00 2001 From: Mark Rousskov Date: Wed, 2 Sep 2020 16:41:36 -0400 Subject: Check test/example/benchmark on x.py check Often when modifying compiler code you'll miss that you've changed an API used by unit tests, since x.py check didn't previously catch that. It's also useful to have this for editing with rust-analyzer and similar tooling where editing tests previously didn't notify you of errors in test files. --- src/bootstrap/builder.rs | 2 +- src/bootstrap/check.rs | 61 +++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 61 insertions(+), 2 deletions(-) (limited to 'src/bootstrap') diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index cecc9ef75ea..4708b207156 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -382,7 +382,7 @@ impl<'a> Builder<'a> { native::Lld ), Kind::Check | Kind::Clippy | Kind::Fix | Kind::Format => { - describe!(check::Std, check::Rustc, check::Rustdoc, check::Clippy) + describe!(check::Std, check::Rustc, check::Rustdoc, check::Clippy, check::Bootstrap) } Kind::Test => describe!( crate::toolstate::ToolStateCheck, diff --git a/src/bootstrap/check.rs b/src/bootstrap/check.rs index 1e26e7754aa..ead0bd0413b 100644 --- a/src/bootstrap/check.rs +++ b/src/bootstrap/check.rs @@ -66,6 +66,43 @@ impl Step for Std { let libdir = builder.sysroot_libdir(compiler, target); let hostdir = builder.sysroot_libdir(compiler, compiler.host); add_to_sysroot(&builder, &libdir, &hostdir, &libstd_stamp(builder, compiler, target)); + + // Then run cargo again, once we've put the rmeta files for the library + // crates into the sysroot. This is needed because e.g., core's tests + // depend on `libtest` -- Cargo presumes it will exist, but it doesn't + // since we initialize with an empty sysroot. + // + // Currently only the "libtest" tree of crates does this. + + let mut cargo = builder.cargo( + compiler, + Mode::Std, + SourceType::InTree, + target, + cargo_subcommand(builder.kind), + ); + std_cargo(builder, target, compiler.stage, &mut cargo); + cargo.arg("--all-targets"); + + // Explicitly pass -p for all dependencies krates -- this will force cargo + // to also check the tests/benches/examples for these crates, rather + // than just the leaf crate. + for krate in builder.in_tree_crates("test") { + cargo.arg("-p").arg(krate.name); + } + + builder.info(&format!( + "Checking std test/bench/example targets ({} -> {})", + &compiler.host, target + )); + run_cargo( + builder, + cargo, + args(builder.kind), + &libstd_test_stamp(builder, compiler, target), + vec![], + true, + ); } } @@ -106,6 +143,14 @@ impl Step for Rustc { cargo_subcommand(builder.kind), ); rustc_cargo(builder, &mut cargo, target); + cargo.arg("--all-targets"); + + // Explicitly pass -p for all compiler krates -- this will force cargo + // to also check the tests/benches/examples for these crates, rather + // than just the leaf crate. + for krate in builder.in_tree_crates("rustc-main") { + cargo.arg("-p").arg(krate.name); + } builder.info(&format!("Checking compiler artifacts ({} -> {})", &compiler.host, target)); run_cargo( @@ -149,7 +194,7 @@ macro_rules! tool_check_step { builder.ensure(Rustc { target }); - let cargo = prepare_tool_cargo( + let mut cargo = prepare_tool_cargo( builder, compiler, Mode::ToolRustc, @@ -160,6 +205,8 @@ macro_rules! tool_check_step { &[], ); + cargo.arg("--all-targets"); + builder.info(&format!( "Checking {} artifacts ({} -> {})", stringify!($name).to_lowercase(), @@ -202,12 +249,24 @@ tool_check_step!(Rustdoc, "src/tools/rustdoc", SourceType::InTree); // rejected. tool_check_step!(Clippy, "src/tools/clippy", SourceType::InTree); +tool_check_step!(Bootstrap, "src/bootstrap", SourceType::InTree); + /// Cargo's output path for the standard library in a given stage, compiled /// by a particular compiler for the specified target. fn libstd_stamp(builder: &Builder<'_>, compiler: Compiler, target: TargetSelection) -> PathBuf { builder.cargo_out(compiler, Mode::Std, target).join(".libstd-check.stamp") } +/// Cargo's output path for the standard library in a given stage, compiled +/// by a particular compiler for the specified target. +fn libstd_test_stamp( + builder: &Builder<'_>, + compiler: Compiler, + target: TargetSelection, +) -> PathBuf { + builder.cargo_out(compiler, Mode::Std, target).join(".libstd-check-test.stamp") +} + /// Cargo's output path for librustc in a given stage, compiled by a particular /// compiler for the specified target. fn librustc_stamp(builder: &Builder<'_>, compiler: Compiler, target: TargetSelection) -> PathBuf { -- cgit 1.4.1-3-g733a5 From a2fbf39cc8b619ac5b469afd43d0ff8b520511aa Mon Sep 17 00:00:00 2001 From: Mateusz Mikuła Date: Fri, 4 Sep 2020 15:20:13 +0200 Subject: Fix rust.use-lld when linker is not set --- src/bootstrap/lib.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'src/bootstrap') diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs index 54651214363..1b655f55fb0 100644 --- a/src/bootstrap/lib.rs +++ b/src/bootstrap/lib.rs @@ -863,7 +863,12 @@ impl Build { && !target.contains("msvc") { Some(self.cc(target)) - } else if can_use_lld && self.config.use_lld && self.build == target { + } else if target.contains("msvc") + && can_use_lld + && self.config.use_lld + && self.build == target + { + // Currently we support using LLD directly via `rust.use_lld` option only with MSVC Some(&self.initial_lld) } else { None -- cgit 1.4.1-3-g733a5 From 4fff14d3d8fa0f75e745e3d6b68967b1caa3aa97 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sat, 5 Sep 2020 20:33:00 +0300 Subject: rustbuild: Remove `Mode::Codegen` --- src/bootstrap/builder.rs | 10 +++++----- src/bootstrap/lib.rs | 4 ---- 2 files changed, 5 insertions(+), 9 deletions(-) (limited to 'src/bootstrap') diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index 4708b207156..91b28b3bac4 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -796,7 +796,7 @@ impl<'a> Builder<'a> { if cmd == "doc" || cmd == "rustdoc" { let my_out = match mode { // This is the intended out directory for compiler documentation. - Mode::Rustc | Mode::ToolRustc | Mode::Codegen => self.compiler_doc_out(target), + Mode::Rustc | Mode::ToolRustc => self.compiler_doc_out(target), Mode::Std => out_dir.join(target.triple).join("doc"), _ => panic!("doc mode {:?} not expected", mode), }; @@ -874,7 +874,7 @@ impl<'a> Builder<'a> { match mode { Mode::Std | Mode::ToolBootstrap | Mode::ToolStd => {} - Mode::Rustc | Mode::Codegen | Mode::ToolRustc => { + Mode::Rustc | Mode::ToolRustc => { // Build proc macros both for the host and the target if target != compiler.host && cmd != "check" { cargo.arg("-Zdual-proc-macros"); @@ -1059,7 +1059,7 @@ impl<'a> Builder<'a> { } let debuginfo_level = match mode { - Mode::Rustc | Mode::Codegen => self.config.rust_debuginfo_level_rustc, + Mode::Rustc => self.config.rust_debuginfo_level_rustc, Mode::Std => self.config.rust_debuginfo_level_std, Mode::ToolBootstrap | Mode::ToolStd | Mode::ToolRustc => { self.config.rust_debuginfo_level_tools @@ -1196,7 +1196,7 @@ impl<'a> Builder<'a> { rustdocflags.arg("-Winvalid_codeblock_attributes"); } - if let Mode::Rustc | Mode::Codegen = mode { + if mode == Mode::Rustc { rustflags.arg("-Zunstable-options"); rustflags.arg("-Wrustc::internal"); } @@ -1359,7 +1359,7 @@ impl<'a> Builder<'a> { // When we build Rust dylibs they're all intended for intermediate // usage, so make sure we pass the -Cprefer-dynamic flag instead of // linking all deps statically into the dylib. - if let Mode::Std | Mode::Rustc | Mode::Codegen = mode { + if matches!(mode, Mode::Std | Mode::Rustc) { rustflags.arg("-Cprefer-dynamic"); } diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs index 54651214363..f7e456d2c44 100644 --- a/src/bootstrap/lib.rs +++ b/src/bootstrap/lib.rs @@ -300,9 +300,6 @@ pub enum Mode { /// Build librustc, and compiler libraries, placing output in the "stageN-rustc" directory. Rustc, - /// Build codegen libraries, placing output in the "stageN-codegen" directory - Codegen, - /// Build a tool, placing output in the "stage0-bootstrap-tools" /// directory. This is for miscellaneous sets of tools that are built /// using the bootstrap stage0 compiler in its entirety (target libraries @@ -572,7 +569,6 @@ impl Build { let suffix = match mode { Mode::Std => "-std", Mode::Rustc => "-rustc", - Mode::Codegen => "-codegen", Mode::ToolBootstrap => "-bootstrap-tools", Mode::ToolStd | Mode::ToolRustc => "-tools", }; -- cgit 1.4.1-3-g733a5 From 8754884b78ee6cff99bd72ebdab39629f6ff88c3 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sat, 5 Sep 2020 20:37:26 +0300 Subject: rustbuild: Do not use `rust-mingw` component when bootstrapping windows-gnu targets --- src/bootstrap/bootstrap.py | 8 -------- 1 file changed, 8 deletions(-) (limited to 'src/bootstrap') diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index c3f1bac177d..dd9fca4eb62 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -398,14 +398,6 @@ class RustBuild(object): with output(self.rustc_stamp()) as rust_stamp: rust_stamp.write(self.date) - # This is required so that we don't mix incompatible MinGW - # libraries/binaries that are included in rust-std with - # the system MinGW ones. - if "pc-windows-gnu" in self.build: - filename = "rust-mingw-{}-{}{}".format( - rustc_channel, self.build, tarball_suffix) - self._download_stage0_helper(filename, "rust-mingw", tarball_suffix) - if self.cargo().startswith(self.bin_root()) and \ (not os.path.exists(self.cargo()) or self.program_out_of_date(self.cargo_stamp())): -- cgit 1.4.1-3-g733a5 From 77d11c3ce2976adaee83cf5b9f60449467557868 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Fri, 4 Sep 2020 20:54:07 +0300 Subject: rustbuild: Build tests with LLD if `use-lld = true` was passed --- src/bootstrap/builder.rs | 6 +++--- src/bootstrap/lib.rs | 11 ++++------- src/bootstrap/test.rs | 5 ++--- src/test/run-make-fulldeps/tools.mk | 4 ++-- 4 files changed, 11 insertions(+), 15 deletions(-) (limited to 'src/bootstrap') diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index 4708b207156..33b03d57dd4 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -755,7 +755,7 @@ impl<'a> Builder<'a> { cmd.env_remove("MAKEFLAGS"); cmd.env_remove("MFLAGS"); - if let Some(linker) = self.linker(compiler.host, true) { + if let Some(linker) = self.linker(compiler.host) { cmd.env("RUSTC_TARGET_LINKER", linker); } cmd @@ -1041,11 +1041,11 @@ impl<'a> Builder<'a> { } } - if let Some(host_linker) = self.linker(compiler.host, true) { + if let Some(host_linker) = self.linker(compiler.host) { cargo.env("RUSTC_HOST_LINKER", host_linker); } - if let Some(target_linker) = self.linker(target, true) { + if let Some(target_linker) = self.linker(target) { let target = crate::envify(&target.triple); cargo.env(&format!("CARGO_TARGET_{}_LINKER", target), target_linker); } diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs index 1b655f55fb0..642f1ea9c00 100644 --- a/src/bootstrap/lib.rs +++ b/src/bootstrap/lib.rs @@ -850,7 +850,7 @@ impl Build { } /// Returns the path to the linker for the given target if it needs to be overridden. - fn linker(&self, target: TargetSelection, can_use_lld: bool) -> Option<&Path> { + fn linker(&self, target: TargetSelection) -> Option<&Path> { if let Some(linker) = self.config.target_config.get(&target).and_then(|c| c.linker.as_ref()) { Some(linker) @@ -863,12 +863,9 @@ impl Build { && !target.contains("msvc") { Some(self.cc(target)) - } else if target.contains("msvc") - && can_use_lld - && self.config.use_lld - && self.build == target - { - // Currently we support using LLD directly via `rust.use_lld` option only with MSVC + } else if target.contains("msvc") && self.config.use_lld && self.build == target { + // `rust.use_lld` means using LLD directly only for MSVC, for other targets it only + // adds `-fuse-ld=lld` to already selected linker. Some(&self.initial_lld) } else { None diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs index a7c9b99f45f..f7eb2aecc0d 100644 --- a/src/bootstrap/test.rs +++ b/src/bootstrap/test.rs @@ -600,7 +600,7 @@ impl Step for RustdocTheme { .env("CFG_RELEASE_CHANNEL", &builder.config.channel) .env("RUSTDOC_REAL", builder.rustdoc(self.compiler)) .env("RUSTC_BOOTSTRAP", "1"); - if let Some(linker) = builder.linker(self.compiler.host, true) { + if let Some(linker) = builder.linker(self.compiler.host) { cmd.env("RUSTC_TARGET_LINKER", linker); } try_run(builder, &mut cmd); @@ -1061,8 +1061,7 @@ impl Step for Compiletest { flags.push("-Zunstable-options".to_string()); flags.push(builder.config.cmd.rustc_args().join(" ")); - // Don't use LLD here since we want to test that rustc finds and uses a linker by itself. - if let Some(linker) = builder.linker(target, false) { + if let Some(linker) = builder.linker(target) { cmd.arg("--linker").arg(linker); } diff --git a/src/test/run-make-fulldeps/tools.mk b/src/test/run-make-fulldeps/tools.mk index f9b6d342295..634c9ece3f5 100644 --- a/src/test/run-make-fulldeps/tools.mk +++ b/src/test/run-make-fulldeps/tools.mk @@ -11,8 +11,8 @@ BARE_RUSTDOC := $(HOST_RPATH_ENV) '$(RUSTDOC)' RUSTC := $(BARE_RUSTC) --out-dir $(TMPDIR) -L $(TMPDIR) $(RUSTFLAGS) RUSTDOC := $(BARE_RUSTDOC) -L $(TARGET_RPATH_DIR) ifdef RUSTC_LINKER -RUSTC := $(RUSTC) -Clinker=$(RUSTC_LINKER) -RUSTDOC := $(RUSTDOC) -Clinker=$(RUSTC_LINKER) +RUSTC := $(RUSTC) -Clinker='$(RUSTC_LINKER)' +RUSTDOC := $(RUSTDOC) -Clinker='$(RUSTC_LINKER)' endif #CC := $(CC) -L $(TMPDIR) HTMLDOCCK := '$(PYTHON)' '$(S)/src/etc/htmldocck.py' -- cgit 1.4.1-3-g733a5 From 8e6b563b934c0f5b57b38c57efdd005ed100d64c Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sun, 6 Sep 2020 20:44:50 +0300 Subject: rustbuild: Build tests with LLD if `use-lld = true` was passed (non-msvc) --- src/bootstrap/test.rs | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src/bootstrap') diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs index f7eb2aecc0d..f56c994523c 100644 --- a/src/bootstrap/test.rs +++ b/src/bootstrap/test.rs @@ -1067,10 +1067,16 @@ impl Step for Compiletest { let mut hostflags = flags.clone(); hostflags.push(format!("-Lnative={}", builder.test_helpers_out(compiler.host).display())); + if builder.config.use_lld && !compiler.host.triple.contains("msvc") { + hostflags.push("-Clink-args=-fuse-ld=lld".to_string()); + } cmd.arg("--host-rustcflags").arg(hostflags.join(" ")); let mut targetflags = flags; targetflags.push(format!("-Lnative={}", builder.test_helpers_out(target).display())); + if builder.config.use_lld && !target.contains("msvc") { + targetflags.push("-Clink-args=-fuse-ld=lld".to_string()); + } cmd.arg("--target-rustcflags").arg(targetflags.join(" ")); cmd.arg("--docck-python").arg(builder.python()); -- cgit 1.4.1-3-g733a5 From 5118a51b4d49a139ea21d280001105948de298f7 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sun, 6 Sep 2020 22:07:14 +0300 Subject: rustbuild: Propagate LLD to more places when `use-lld` is enabled --- src/bootstrap/bin/rustc.rs | 3 +++ src/bootstrap/bin/rustdoc.rs | 5 ++++- src/bootstrap/builder.rs | 8 +++++++- src/bootstrap/test.rs | 5 ++++- 4 files changed, 18 insertions(+), 3 deletions(-) (limited to 'src/bootstrap') diff --git a/src/bootstrap/bin/rustc.rs b/src/bootstrap/bin/rustc.rs index 4dd71ebade1..3694bdbf670 100644 --- a/src/bootstrap/bin/rustc.rs +++ b/src/bootstrap/bin/rustc.rs @@ -112,6 +112,9 @@ fn main() { if let Ok(host_linker) = env::var("RUSTC_HOST_LINKER") { cmd.arg(format!("-Clinker={}", host_linker)); } + if env::var_os("RUSTC_HOST_FUSE_LD_LLD").is_some() { + cmd.arg("-Clink-args=-fuse-ld=lld"); + } if let Ok(s) = env::var("RUSTC_HOST_CRT_STATIC") { if s == "true" { diff --git a/src/bootstrap/bin/rustdoc.rs b/src/bootstrap/bin/rustdoc.rs index ab846adf942..cb58eb89ad8 100644 --- a/src/bootstrap/bin/rustdoc.rs +++ b/src/bootstrap/bin/rustdoc.rs @@ -42,11 +42,14 @@ fn main() { if env::var_os("RUSTC_FORCE_UNSTABLE").is_some() { cmd.arg("-Z").arg("force-unstable-if-unmarked"); } - if let Some(linker) = env::var_os("RUSTC_TARGET_LINKER") { + if let Some(linker) = env::var_os("RUSTDOC_LINKER") { let mut arg = OsString::from("-Clinker="); arg.push(&linker); cmd.arg(arg); } + if env::var_os("RUSTDOC_FUSE_LD_LLD").is_some() { + cmd.arg("-Clink-args=-fuse-ld=lld"); + } // Needed to be able to run all rustdoc tests. if let Some(ref x) = env::var_os("RUSTDOC_RESOURCE_SUFFIX") { diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index 33b03d57dd4..ca2e159d2d9 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -756,7 +756,10 @@ impl<'a> Builder<'a> { cmd.env_remove("MFLAGS"); if let Some(linker) = self.linker(compiler.host) { - cmd.env("RUSTC_TARGET_LINKER", linker); + cmd.env("RUSTDOC_LINKER", linker); + } + if self.config.use_lld && !compiler.host.contains("msvc") { + cmd.env("RUSTDOC_FUSE_LD_LLD", "1"); } cmd } @@ -1044,6 +1047,9 @@ impl<'a> Builder<'a> { if let Some(host_linker) = self.linker(compiler.host) { cargo.env("RUSTC_HOST_LINKER", host_linker); } + if self.config.use_lld && !compiler.host.contains("msvc") { + cargo.env("RUSTC_HOST_FUSE_LD_LLD", "1"); + } if let Some(target_linker) = self.linker(target) { let target = crate::envify(&target.triple); diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs index f56c994523c..98a219e3979 100644 --- a/src/bootstrap/test.rs +++ b/src/bootstrap/test.rs @@ -601,7 +601,10 @@ impl Step for RustdocTheme { .env("RUSTDOC_REAL", builder.rustdoc(self.compiler)) .env("RUSTC_BOOTSTRAP", "1"); if let Some(linker) = builder.linker(self.compiler.host) { - cmd.env("RUSTC_TARGET_LINKER", linker); + cmd.env("RUSTDOC_LINKER", linker); + } + if builder.config.use_lld && !self.compiler.host.contains("msvc") { + cmd.env("RUSTDOC_FUSE_LD_LLD", "1"); } try_run(builder, &mut cmd); } -- cgit 1.4.1-3-g733a5 From b27fca71d46b2da88611bb5cc8cfb6e90c0a4af0 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Mon, 7 Sep 2020 00:39:58 +0300 Subject: rustbuild: Deduplicate LLD checks slightly --- src/bootstrap/builder.rs | 7 +++---- src/bootstrap/lib.rs | 10 +++++++--- src/bootstrap/test.rs | 6 +++--- 3 files changed, 13 insertions(+), 10 deletions(-) (limited to 'src/bootstrap') diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index ca2e159d2d9..c7fc360c233 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -758,7 +758,7 @@ impl<'a> Builder<'a> { if let Some(linker) = self.linker(compiler.host) { cmd.env("RUSTDOC_LINKER", linker); } - if self.config.use_lld && !compiler.host.contains("msvc") { + if self.is_fuse_ld_lld(compiler.host) { cmd.env("RUSTDOC_FUSE_LD_LLD", "1"); } cmd @@ -1047,7 +1047,7 @@ impl<'a> Builder<'a> { if let Some(host_linker) = self.linker(compiler.host) { cargo.env("RUSTC_HOST_LINKER", host_linker); } - if self.config.use_lld && !compiler.host.contains("msvc") { + if self.is_fuse_ld_lld(compiler.host) { cargo.env("RUSTC_HOST_FUSE_LD_LLD", "1"); } @@ -1055,8 +1055,7 @@ impl<'a> Builder<'a> { let target = crate::envify(&target.triple); cargo.env(&format!("CARGO_TARGET_{}_LINKER", target), target_linker); } - - if self.config.use_lld && !target.contains("msvc") { + if self.is_fuse_ld_lld(target) { rustflags.arg("-Clink-args=-fuse-ld=lld"); } diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs index 642f1ea9c00..8d60284cd4f 100644 --- a/src/bootstrap/lib.rs +++ b/src/bootstrap/lib.rs @@ -863,15 +863,19 @@ impl Build { && !target.contains("msvc") { Some(self.cc(target)) - } else if target.contains("msvc") && self.config.use_lld && self.build == target { - // `rust.use_lld` means using LLD directly only for MSVC, for other targets it only - // adds `-fuse-ld=lld` to already selected linker. + } else if self.config.use_lld && !self.is_fuse_ld_lld(target) && self.build == target { Some(&self.initial_lld) } else { None } } + // LLD is used through `-fuse-ld=lld` rather than directly. + // Only MSVC targets use LLD directly at the moment. + fn is_fuse_ld_lld(&self, target: TargetSelection) -> bool { + self.config.use_lld && !target.contains("msvc") + } + /// Returns if this target should statically link the C runtime, if specified fn crt_static(&self, target: TargetSelection) -> Option { if target.contains("pc-windows-msvc") { diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs index 98a219e3979..732028fb6ed 100644 --- a/src/bootstrap/test.rs +++ b/src/bootstrap/test.rs @@ -603,7 +603,7 @@ impl Step for RustdocTheme { if let Some(linker) = builder.linker(self.compiler.host) { cmd.env("RUSTDOC_LINKER", linker); } - if builder.config.use_lld && !self.compiler.host.contains("msvc") { + if builder.is_fuse_ld_lld(self.compiler.host) { cmd.env("RUSTDOC_FUSE_LD_LLD", "1"); } try_run(builder, &mut cmd); @@ -1070,14 +1070,14 @@ impl Step for Compiletest { let mut hostflags = flags.clone(); hostflags.push(format!("-Lnative={}", builder.test_helpers_out(compiler.host).display())); - if builder.config.use_lld && !compiler.host.triple.contains("msvc") { + if builder.is_fuse_ld_lld(compiler.host) { hostflags.push("-Clink-args=-fuse-ld=lld".to_string()); } cmd.arg("--host-rustcflags").arg(hostflags.join(" ")); let mut targetflags = flags; targetflags.push(format!("-Lnative={}", builder.test_helpers_out(target).display())); - if builder.config.use_lld && !target.contains("msvc") { + if builder.is_fuse_ld_lld(target) { targetflags.push("-Clink-args=-fuse-ld=lld".to_string()); } cmd.arg("--target-rustcflags").arg(targetflags.join(" ")); -- cgit 1.4.1-3-g733a5 From 2656d3414c960174b29d2e57122126672d1c747c Mon Sep 17 00:00:00 2001 From: Mark Rousskov Date: Sun, 6 Sep 2020 19:38:26 -0400 Subject: Make bootstrap build on stable This is generally a good idea, and will help with being able to build bootstrap without Python over time as it means we can "just" build with cargo +beta build rather than needing the user to set environment variables. This is a minor step, but a necessary one on that road. --- src/bootstrap/bootstrap.py | 1 - src/bootstrap/lib.rs | 2 -- src/bootstrap/tool.rs | 6 ++++-- 3 files changed, 4 insertions(+), 5 deletions(-) (limited to 'src/bootstrap') diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index c3f1bac177d..4ab23b40ac3 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -714,7 +714,6 @@ class RustBuild(object): # See also: . if "CARGO_BUILD_TARGET" in env: del env["CARGO_BUILD_TARGET"] - env["RUSTC_BOOTSTRAP"] = '1' env["CARGO_TARGET_DIR"] = build_dir env["RUSTC"] = self.rustc() env["LD_LIBRARY_PATH"] = os.path.join(self.bin_root(), "lib") + \ diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs index 1b655f55fb0..3b8c9c2d58e 100644 --- a/src/bootstrap/lib.rs +++ b/src/bootstrap/lib.rs @@ -103,8 +103,6 @@ //! More documentation can be found in each respective module below, and you can //! also check out the `src/bootstrap/README.md` file for more information. -#![feature(drain_filter)] - use std::cell::{Cell, RefCell}; use std::collections::{HashMap, HashSet}; use std::env; diff --git a/src/bootstrap/tool.rs b/src/bootstrap/tool.rs index fe3f1e78029..e8d7de7a5dc 100644 --- a/src/bootstrap/tool.rs +++ b/src/bootstrap/tool.rs @@ -162,13 +162,15 @@ impl Step for ToolBuild { "the following dependencies are duplicated although they \ have the same features enabled:" ); - for (id, cur, prev) in duplicates.drain_filter(|(_, cur, prev)| cur.2 == prev.2) { + let (same, different): (Vec<_>, Vec<_>) = + duplicates.into_iter().partition(|(_, cur, prev)| cur.2 == prev.2); + for (id, cur, prev) in same { println!(" {}", id); // same features println!(" `{}` ({:?})\n `{}` ({:?})", cur.0, cur.1, prev.0, prev.1); } println!("the following dependencies have different features:"); - for (id, cur, prev) in duplicates { + for (id, cur, prev) in different { println!(" {}", id); let cur_features: HashSet<_> = cur.2.into_iter().collect(); let prev_features: HashSet<_> = prev.2.into_iter().collect(); -- cgit 1.4.1-3-g733a5 From aa4554f42deec0d81f89251960f6abb5acc2f6e2 Mon Sep 17 00:00:00 2001 From: Mark Rousskov Date: Fri, 4 Sep 2020 13:59:14 -0400 Subject: Dedicated rust development tarball This currently includes libLLVM, llvm-config, and FileCheck, but will perhaps expand to more tooling overtime. It should be considered entirely unstable and may change at any time. --- src/bootstrap/builder.rs | 1 + src/bootstrap/dist.rs | 82 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+) (limited to 'src/bootstrap') diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index 4708b207156..04b90bba1ae 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -471,6 +471,7 @@ impl<'a> Builder<'a> { dist::Clippy, dist::Miri, dist::LlvmTools, + dist::RustDev, dist::Extended, dist::HashSign ), diff --git a/src/bootstrap/dist.rs b/src/bootstrap/dist.rs index c1022099a02..579bacb0d90 100644 --- a/src/bootstrap/dist.rs +++ b/src/bootstrap/dist.rs @@ -2499,3 +2499,85 @@ impl Step for LlvmTools { Some(distdir(builder).join(format!("{}-{}.tar.gz", name, target.triple))) } } + +// Tarball intended for internal consumption to ease rustc/std development. +// +// Should not be considered stable by end users. +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +pub struct RustDev { + pub target: TargetSelection, +} + +impl Step for RustDev { + type Output = Option; + const DEFAULT: bool = true; + const ONLY_HOSTS: bool = true; + + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.path("rust-dev") + } + + fn make_run(run: RunConfig<'_>) { + run.builder.ensure(RustDev { target: run.target }); + } + + fn run(self, builder: &Builder<'_>) -> Option { + let target = self.target; + + builder.info(&format!("Dist RustDev ({})", target)); + let _time = timeit(builder); + let src = builder.src.join("src/llvm-project/llvm"); + let name = pkgname(builder, "rust-dev"); + + let tmp = tmpdir(builder); + let image = tmp.join("rust-dev-image"); + drop(fs::remove_dir_all(&image)); + + // Prepare the image directory + let dst_bindir = image.join("bin"); + t!(fs::create_dir_all(&dst_bindir)); + let exe = builder.llvm_out(target).join("bin").join(exe("llvm-config", target)); + builder.install(&exe, &dst_bindir, 0o755); + builder.install(&builder.llvm_filecheck(target), &dst_bindir, 0o755); + + // Copy the include directory as well; needed mostly to build + // librustc_llvm properly (e.g., llvm-config.h is in here). But also + // just broadly useful to be able to link against the bundled LLVM. + builder.cp_r(&builder.llvm_out(target).join("include"), &image.join("include")); + + // Copy libLLVM.so to the target lib dir as well, so the RPATH like + // `$ORIGIN/../lib` can find it. It may also be used as a dependency + // of `rustc-dev` to support the inherited `-lLLVM` when using the + // compiler libraries. + maybe_install_llvm(builder, target, &image.join("lib")); + + // Prepare the overlay + let overlay = tmp.join("rust-dev-overlay"); + drop(fs::remove_dir_all(&overlay)); + builder.create_dir(&overlay); + builder.install(&src.join("README.txt"), &overlay, 0o644); + builder.install(&src.join("LICENSE.TXT"), &overlay, 0o644); + builder.create(&overlay.join("version"), &builder.rust_version()); + + // Generate the installer tarball + let mut cmd = rust_installer(builder); + cmd.arg("generate") + .arg("--product-name=Rust") + .arg("--rel-manifest-dir=rustlib") + .arg("--success-message=rust-dev-installed.") + .arg("--image-dir") + .arg(&image) + .arg("--work-dir") + .arg(&tmpdir(builder)) + .arg("--output-dir") + .arg(&distdir(builder)) + .arg("--non-installed-overlay") + .arg(&overlay) + .arg(format!("--package-name={}-{}", name, target.triple)) + .arg("--legacy-manifest-dirs=rustlib,cargo") + .arg("--component-name=rust-dev"); + + builder.run(&mut cmd); + Some(distdir(builder).join(format!("{}-{}.tar.gz", name, target.triple))) + } +} -- cgit 1.4.1-3-g733a5 From c8262fd87b91e6a699483d8d66ef1be3ced00a71 Mon Sep 17 00:00:00 2001 From: ortem Date: Tue, 8 Sep 2020 12:20:49 +0300 Subject: Add missed spaces to GCC-WARNING.txt --- src/bootstrap/dist.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/bootstrap') diff --git a/src/bootstrap/dist.rs b/src/bootstrap/dist.rs index c1022099a02..b3ab498e73f 100644 --- a/src/bootstrap/dist.rs +++ b/src/bootstrap/dist.rs @@ -323,8 +323,8 @@ fn make_win_dist( // Warn windows-gnu users that the bundled GCC cannot compile C files builder.create( &target_bin_dir.join("GCC-WARNING.txt"), - "gcc.exe contained in this folder cannot be used for compiling C files - it is only\ - used as a linker. In order to be able to compile projects containing C code use\ + "gcc.exe contained in this folder cannot be used for compiling C files - it is only \ + used as a linker. In order to be able to compile projects containing C code use \ the GCC provided by MinGW or Cygwin.", ); -- cgit 1.4.1-3-g733a5 From 0065e33c24213c538ffd76b0cc771e4668819969 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Tue, 8 Sep 2020 11:32:25 +0200 Subject: rustbuild: don't set PYTHON_EXECUTABLE and WITH_POLLY cmake vars since they are no longer supported by llvm CMake Warning: Manually-specified variables were not used by the project: PYTHON_EXECUTABLE WITH_POLLY --- src/bootstrap/native.rs | 5 ----- 1 file changed, 5 deletions(-) (limited to 'src/bootstrap') diff --git a/src/bootstrap/native.rs b/src/bootstrap/native.rs index a0c79e38f9d..6cd850bc0bf 100644 --- a/src/bootstrap/native.rs +++ b/src/bootstrap/native.rs @@ -169,7 +169,6 @@ impl Step for Llvm { .define("LLVM_INCLUDE_TESTS", "OFF") .define("LLVM_INCLUDE_DOCS", "OFF") .define("LLVM_INCLUDE_BENCHMARKS", "OFF") - .define("WITH_POLLY", "OFF") .define("LLVM_ENABLE_TERMINFO", "OFF") .define("LLVM_ENABLE_LIBEDIT", "OFF") .define("LLVM_ENABLE_BINDINGS", "OFF") @@ -305,10 +304,6 @@ impl Step for Llvm { cfg.define("LLVM_TEMPORARILY_ALLOW_OLD_TOOLCHAIN", "YES"); } - if let Some(ref python) = builder.config.python { - cfg.define("PYTHON_EXECUTABLE", python); - } - configure_cmake(builder, target, &mut cfg, true); // FIXME: we don't actually need to build all LLVM tools and all LLVM -- cgit 1.4.1-3-g733a5 From 10d3f8a484a812db995198f17b17462718f477bc Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sun, 26 Jul 2020 20:11:30 +0300 Subject: Move `rustllvm` into `rustc_llvm` --- .gitignore | 1 - compiler/rustc_codegen_llvm/Cargo.toml | 2 +- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 4 +- compiler/rustc_llvm/Cargo.toml | 16 + compiler/rustc_llvm/build.rs | 322 ++++ compiler/rustc_llvm/llvm-wrapper/.editorconfig | 6 + .../rustc_llvm/llvm-wrapper/ArchiveWrapper.cpp | 226 +++ .../llvm-wrapper/CoverageMappingWrapper.cpp | 70 + compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h | 115 ++ compiler/rustc_llvm/llvm-wrapper/Linker.cpp | 48 + compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp | 1655 +++++++++++++++++++ compiler/rustc_llvm/llvm-wrapper/README | 16 + compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp | 1721 ++++++++++++++++++++ compiler/rustc_llvm/src/lib.rs | 173 ++ config.toml.example | 2 +- src/bootstrap/builder.rs | 6 +- src/bootstrap/compile.rs | 4 +- src/librustc_llvm/Cargo.toml | 19 - src/librustc_llvm/build.rs | 322 ---- src/librustc_llvm/lib.rs | 173 -- src/rustllvm/.editorconfig | 6 - src/rustllvm/ArchiveWrapper.cpp | 226 --- src/rustllvm/CoverageMappingWrapper.cpp | 70 - src/rustllvm/Linker.cpp | 48 - src/rustllvm/PassWrapper.cpp | 1655 ------------------- src/rustllvm/README | 16 - src/rustllvm/RustWrapper.cpp | 1721 -------------------- src/rustllvm/rustllvm.h | 115 -- 28 files changed, 4377 insertions(+), 4381 deletions(-) create mode 100644 compiler/rustc_llvm/Cargo.toml create mode 100644 compiler/rustc_llvm/build.rs create mode 100644 compiler/rustc_llvm/llvm-wrapper/.editorconfig create mode 100644 compiler/rustc_llvm/llvm-wrapper/ArchiveWrapper.cpp create mode 100644 compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp create mode 100644 compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h create mode 100644 compiler/rustc_llvm/llvm-wrapper/Linker.cpp create mode 100644 compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp create mode 100644 compiler/rustc_llvm/llvm-wrapper/README create mode 100644 compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp create mode 100644 compiler/rustc_llvm/src/lib.rs delete mode 100644 src/librustc_llvm/Cargo.toml delete mode 100644 src/librustc_llvm/build.rs delete mode 100644 src/librustc_llvm/lib.rs delete mode 100644 src/rustllvm/.editorconfig delete mode 100644 src/rustllvm/ArchiveWrapper.cpp delete mode 100644 src/rustllvm/CoverageMappingWrapper.cpp delete mode 100644 src/rustllvm/Linker.cpp delete mode 100644 src/rustllvm/PassWrapper.cpp delete mode 100644 src/rustllvm/README delete mode 100644 src/rustllvm/RustWrapper.cpp delete mode 100644 src/rustllvm/rustllvm.h (limited to 'src/bootstrap') diff --git a/.gitignore b/.gitignore index 856ff7dbb0f..1c50d9b054d 100644 --- a/.gitignore +++ b/.gitignore @@ -33,7 +33,6 @@ __pycache__/ /mingw-build/ # Created by default with `src/ci/docker/run.sh`: /obj/ -/rustllvm/ /unicode-downloads /target # Generated by compiletest for incremental: diff --git a/compiler/rustc_codegen_llvm/Cargo.toml b/compiler/rustc_codegen_llvm/Cargo.toml index 38f552558c8..04792b334d5 100644 --- a/compiler/rustc_codegen_llvm/Cargo.toml +++ b/compiler/rustc_codegen_llvm/Cargo.toml @@ -25,7 +25,7 @@ rustc_fs_util = { path = "../rustc_fs_util" } rustc_hir = { path = "../rustc_hir" } rustc_incremental = { path = "../rustc_incremental" } rustc_index = { path = "../rustc_index" } -rustc_llvm = { path = "../../src/librustc_llvm" } +rustc_llvm = { path = "../rustc_llvm" } rustc_session = { path = "../rustc_session" } rustc_serialize = { path = "../rustc_serialize" } rustc_target = { path = "../rustc_target" } diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 32822eba930..4942c997682 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -96,7 +96,7 @@ pub enum DLLStorageClass { DllExport = 2, // Function to be accessible from DLL. } -/// Matches LLVMRustAttribute in rustllvm.h +/// Matches LLVMRustAttribute in LLVMWrapper.h /// Semantically a subset of the C++ enum llvm::Attribute::AttrKind, /// though it is not ABI compatible (since it's a C++ enum) #[repr(C)] @@ -1705,7 +1705,7 @@ extern "C" { PM: &PassManager<'_>, ); - // Stuff that's in rustllvm/ because it's not upstream yet. + // Stuff that's in llvm-wrapper/ because it's not upstream yet. /// Opens an object file. pub fn LLVMCreateObjectFile( diff --git a/compiler/rustc_llvm/Cargo.toml b/compiler/rustc_llvm/Cargo.toml new file mode 100644 index 00000000000..ee83689f0a4 --- /dev/null +++ b/compiler/rustc_llvm/Cargo.toml @@ -0,0 +1,16 @@ +[package] +authors = ["The Rust Project Developers"] +name = "rustc_llvm" +version = "0.0.0" +edition = "2018" + +[features] +static-libstdcpp = [] +emscripten = [] + +[dependencies] +libc = "0.2.73" + +[build-dependencies] +build_helper = { path = "../../src/build_helper" } +cc = "1.0.58" diff --git a/compiler/rustc_llvm/build.rs b/compiler/rustc_llvm/build.rs new file mode 100644 index 00000000000..7f1e5cf336a --- /dev/null +++ b/compiler/rustc_llvm/build.rs @@ -0,0 +1,322 @@ +use std::env; +use std::path::{Path, PathBuf}; +use std::process::Command; + +use build_helper::{output, tracked_env_var_os}; + +fn detect_llvm_link() -> (&'static str, &'static str) { + // Force the link mode we want, preferring static by default, but + // possibly overridden by `configure --enable-llvm-link-shared`. + if tracked_env_var_os("LLVM_LINK_SHARED").is_some() { + ("dylib", "--link-shared") + } else { + ("static", "--link-static") + } +} + +fn main() { + if tracked_env_var_os("RUST_CHECK").is_some() { + // If we're just running `check`, there's no need for LLVM to be built. + return; + } + + build_helper::restore_library_path(); + + let target = env::var("TARGET").expect("TARGET was not set"); + let llvm_config = + tracked_env_var_os("LLVM_CONFIG").map(|x| Some(PathBuf::from(x))).unwrap_or_else(|| { + if let Some(dir) = tracked_env_var_os("CARGO_TARGET_DIR").map(PathBuf::from) { + let to_test = dir + .parent() + .unwrap() + .parent() + .unwrap() + .join(&target) + .join("llvm/bin/llvm-config"); + if Command::new(&to_test).output().is_ok() { + return Some(to_test); + } + } + None + }); + + if let Some(llvm_config) = &llvm_config { + println!("cargo:rerun-if-changed={}", llvm_config.display()); + } + let llvm_config = llvm_config.unwrap_or_else(|| PathBuf::from("llvm-config")); + + // Test whether we're cross-compiling LLVM. This is a pretty rare case + // currently where we're producing an LLVM for a different platform than + // what this build script is currently running on. + // + // In that case, there's no guarantee that we can actually run the target, + // so the build system works around this by giving us the LLVM_CONFIG for + // the host platform. This only really works if the host LLVM and target + // LLVM are compiled the same way, but for us that's typically the case. + // + // We *want* detect this cross compiling situation by asking llvm-config + // what its host-target is. If that's not the TARGET, then we're cross + // compiling. Unfortunately `llvm-config` seems either be buggy, or we're + // misconfiguring it, because the `i686-pc-windows-gnu` build of LLVM will + // report itself with a `--host-target` of `x86_64-pc-windows-gnu`. This + // tricks us into thinking we're doing a cross build when we aren't, so + // havoc ensues. + // + // In any case, if we're cross compiling, this generally just means that we + // can't trust all the output of llvm-config because it might be targeted + // for the host rather than the target. As a result a bunch of blocks below + // are gated on `if !is_crossed` + let target = env::var("TARGET").expect("TARGET was not set"); + let host = env::var("HOST").expect("HOST was not set"); + let is_crossed = target != host; + + let mut optional_components = vec![ + "x86", + "arm", + "aarch64", + "amdgpu", + "avr", + "mips", + "powerpc", + "systemz", + "jsbackend", + "webassembly", + "msp430", + "sparc", + "nvptx", + "hexagon", + ]; + + let mut version_cmd = Command::new(&llvm_config); + version_cmd.arg("--version"); + let version_output = output(&mut version_cmd); + let mut parts = version_output.split('.').take(2).filter_map(|s| s.parse::().ok()); + let (major, _minor) = if let (Some(major), Some(minor)) = (parts.next(), parts.next()) { + (major, minor) + } else { + (6, 0) + }; + + if major > 6 { + optional_components.push("riscv"); + } + + let required_components = &[ + "ipo", + "bitreader", + "bitwriter", + "linker", + "asmparser", + "lto", + "coverage", + "instrumentation", + ]; + + let components = output(Command::new(&llvm_config).arg("--components")); + let mut components = components.split_whitespace().collect::>(); + components.retain(|c| optional_components.contains(c) || required_components.contains(c)); + + for component in required_components { + if !components.contains(component) { + panic!("require llvm component {} but wasn't found", component); + } + } + + for component in components.iter() { + println!("cargo:rustc-cfg=llvm_component=\"{}\"", component); + } + + if major >= 9 { + println!("cargo:rustc-cfg=llvm_has_msp430_asm_parser"); + } + + // Link in our own LLVM shims, compiled with the same flags as LLVM + let mut cmd = Command::new(&llvm_config); + cmd.arg("--cxxflags"); + let cxxflags = output(&mut cmd); + let mut cfg = cc::Build::new(); + cfg.warnings(false); + for flag in cxxflags.split_whitespace() { + // Ignore flags like `-m64` when we're doing a cross build + if is_crossed && flag.starts_with("-m") { + continue; + } + + if flag.starts_with("-flto") { + continue; + } + + // -Wdate-time is not supported by the netbsd cross compiler + if is_crossed && target.contains("netbsd") && flag.contains("date-time") { + continue; + } + + // Include path contains host directory, replace it with target + if is_crossed && flag.starts_with("-I") { + cfg.flag(&flag.replace(&host, &target)); + continue; + } + + cfg.flag(flag); + } + + for component in &components { + let mut flag = String::from("LLVM_COMPONENT_"); + flag.push_str(&component.to_uppercase()); + cfg.define(&flag, None); + } + + if tracked_env_var_os("LLVM_RUSTLLVM").is_some() { + cfg.define("LLVM_RUSTLLVM", None); + } + + if tracked_env_var_os("LLVM_NDEBUG").is_some() { + cfg.define("NDEBUG", None); + cfg.debug(false); + } + + build_helper::rerun_if_changed_anything_in_dir(Path::new("llvm-wrapper")); + cfg.file("llvm-wrapper/PassWrapper.cpp") + .file("llvm-wrapper/RustWrapper.cpp") + .file("llvm-wrapper/ArchiveWrapper.cpp") + .file("llvm-wrapper/CoverageMappingWrapper.cpp") + .file("llvm-wrapper/Linker.cpp") + .cpp(true) + .cpp_link_stdlib(None) // we handle this below + .compile("llvm-wrapper"); + + let (llvm_kind, llvm_link_arg) = detect_llvm_link(); + + // Link in all LLVM libraries, if we're using the "wrong" llvm-config then + // we don't pick up system libs because unfortunately they're for the host + // of llvm-config, not the target that we're attempting to link. + let mut cmd = Command::new(&llvm_config); + cmd.arg(llvm_link_arg).arg("--libs"); + + if !is_crossed { + cmd.arg("--system-libs"); + } else if target.contains("windows-gnu") { + println!("cargo:rustc-link-lib=shell32"); + println!("cargo:rustc-link-lib=uuid"); + } else if target.contains("netbsd") || target.contains("haiku") { + println!("cargo:rustc-link-lib=z"); + } + cmd.args(&components); + + for lib in output(&mut cmd).split_whitespace() { + let name = if lib.starts_with("-l") { + &lib[2..] + } else if lib.starts_with('-') { + &lib[1..] + } else if Path::new(lib).exists() { + // On MSVC llvm-config will print the full name to libraries, but + // we're only interested in the name part + let name = Path::new(lib).file_name().unwrap().to_str().unwrap(); + name.trim_end_matches(".lib") + } else if lib.ends_with(".lib") { + // Some MSVC libraries just come up with `.lib` tacked on, so chop + // that off + lib.trim_end_matches(".lib") + } else { + continue; + }; + + // Don't need or want this library, but LLVM's CMake build system + // doesn't provide a way to disable it, so filter it here even though we + // may or may not have built it. We don't reference anything from this + // library and it otherwise may just pull in extra dependencies on + // libedit which we don't want + if name == "LLVMLineEditor" { + continue; + } + + let kind = if name.starts_with("LLVM") { llvm_kind } else { "dylib" }; + println!("cargo:rustc-link-lib={}={}", kind, name); + } + + // LLVM ldflags + // + // If we're a cross-compile of LLVM then unfortunately we can't trust these + // ldflags (largely where all the LLVM libs are located). Currently just + // hack around this by replacing the host triple with the target and pray + // that those -L directories are the same! + let mut cmd = Command::new(&llvm_config); + cmd.arg(llvm_link_arg).arg("--ldflags"); + for lib in output(&mut cmd).split_whitespace() { + if is_crossed { + if lib.starts_with("-LIBPATH:") { + println!("cargo:rustc-link-search=native={}", lib[9..].replace(&host, &target)); + } else if lib.starts_with("-L") { + println!("cargo:rustc-link-search=native={}", lib[2..].replace(&host, &target)); + } + } else if lib.starts_with("-LIBPATH:") { + println!("cargo:rustc-link-search=native={}", &lib[9..]); + } else if lib.starts_with("-l") { + println!("cargo:rustc-link-lib={}", &lib[2..]); + } else if lib.starts_with("-L") { + println!("cargo:rustc-link-search=native={}", &lib[2..]); + } + } + + // Some LLVM linker flags (-L and -l) may be needed even when linking + // rustc_llvm, for example when using static libc++, we may need to + // manually specify the library search path and -ldl -lpthread as link + // dependencies. + let llvm_linker_flags = tracked_env_var_os("LLVM_LINKER_FLAGS"); + if let Some(s) = llvm_linker_flags { + for lib in s.into_string().unwrap().split_whitespace() { + if lib.starts_with("-l") { + println!("cargo:rustc-link-lib={}", &lib[2..]); + } else if lib.starts_with("-L") { + println!("cargo:rustc-link-search=native={}", &lib[2..]); + } + } + } + + let llvm_static_stdcpp = tracked_env_var_os("LLVM_STATIC_STDCPP"); + let llvm_use_libcxx = tracked_env_var_os("LLVM_USE_LIBCXX"); + + let stdcppname = if target.contains("openbsd") { + if target.contains("sparc64") { "estdc++" } else { "c++" } + } else if target.contains("freebsd") { + "c++" + } else if target.contains("darwin") { + "c++" + } else if target.contains("netbsd") && llvm_static_stdcpp.is_some() { + // NetBSD uses a separate library when relocation is required + "stdc++_pic" + } else if llvm_use_libcxx.is_some() { + "c++" + } else { + "stdc++" + }; + + // RISC-V requires libatomic for sub-word atomic operations + if target.starts_with("riscv") { + println!("cargo:rustc-link-lib=atomic"); + } + + // C++ runtime library + if !target.contains("msvc") { + if let Some(s) = llvm_static_stdcpp { + assert!(!cxxflags.contains("stdlib=libc++")); + let path = PathBuf::from(s); + println!("cargo:rustc-link-search=native={}", path.parent().unwrap().display()); + if target.contains("windows") { + println!("cargo:rustc-link-lib=static-nobundle={}", stdcppname); + } else { + println!("cargo:rustc-link-lib=static={}", stdcppname); + } + } else if cxxflags.contains("stdlib=libc++") { + println!("cargo:rustc-link-lib=c++"); + } else { + println!("cargo:rustc-link-lib={}", stdcppname); + } + } + + // Libstdc++ depends on pthread which Rust doesn't link on MinGW + // since nothing else requires it. + if target.contains("windows-gnu") { + println!("cargo:rustc-link-lib=static-nobundle=pthread"); + } +} diff --git a/compiler/rustc_llvm/llvm-wrapper/.editorconfig b/compiler/rustc_llvm/llvm-wrapper/.editorconfig new file mode 100644 index 00000000000..865cd45f708 --- /dev/null +++ b/compiler/rustc_llvm/llvm-wrapper/.editorconfig @@ -0,0 +1,6 @@ +[*.{h,cpp}] +end_of_line = lf +insert_final_newline = true +charset = utf-8 +indent_style = space +indent_size = 2 diff --git a/compiler/rustc_llvm/llvm-wrapper/ArchiveWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/ArchiveWrapper.cpp new file mode 100644 index 00000000000..2797fe8df4a --- /dev/null +++ b/compiler/rustc_llvm/llvm-wrapper/ArchiveWrapper.cpp @@ -0,0 +1,226 @@ +#include "LLVMWrapper.h" + +#include "llvm/Object/Archive.h" +#include "llvm/Object/ArchiveWriter.h" +#include "llvm/Support/Path.h" + +using namespace llvm; +using namespace llvm::object; + +struct RustArchiveMember { + const char *Filename; + const char *Name; + Archive::Child Child; + + RustArchiveMember() + : Filename(nullptr), Name(nullptr), + Child(nullptr, nullptr, nullptr) + { + } + ~RustArchiveMember() {} +}; + +struct RustArchiveIterator { + bool First; + Archive::child_iterator Cur; + Archive::child_iterator End; + std::unique_ptr Err; + + RustArchiveIterator(Archive::child_iterator Cur, Archive::child_iterator End, + std::unique_ptr Err) + : First(true), + Cur(Cur), + End(End), + Err(std::move(Err)) {} +}; + +enum class LLVMRustArchiveKind { + GNU, + BSD, + DARWIN, + COFF, +}; + +static Archive::Kind fromRust(LLVMRustArchiveKind Kind) { + switch (Kind) { + case LLVMRustArchiveKind::GNU: + return Archive::K_GNU; + case LLVMRustArchiveKind::BSD: + return Archive::K_BSD; + case LLVMRustArchiveKind::DARWIN: + return Archive::K_DARWIN; + case LLVMRustArchiveKind::COFF: + return Archive::K_COFF; + default: + report_fatal_error("Bad ArchiveKind."); + } +} + +typedef OwningBinary *LLVMRustArchiveRef; +typedef RustArchiveMember *LLVMRustArchiveMemberRef; +typedef Archive::Child *LLVMRustArchiveChildRef; +typedef Archive::Child const *LLVMRustArchiveChildConstRef; +typedef RustArchiveIterator *LLVMRustArchiveIteratorRef; + +extern "C" LLVMRustArchiveRef LLVMRustOpenArchive(char *Path) { + ErrorOr> BufOr = + MemoryBuffer::getFile(Path, -1, false); + if (!BufOr) { + LLVMRustSetLastError(BufOr.getError().message().c_str()); + return nullptr; + } + + Expected> ArchiveOr = + Archive::create(BufOr.get()->getMemBufferRef()); + + if (!ArchiveOr) { + LLVMRustSetLastError(toString(ArchiveOr.takeError()).c_str()); + return nullptr; + } + + OwningBinary *Ret = new OwningBinary( + std::move(ArchiveOr.get()), std::move(BufOr.get())); + + return Ret; +} + +extern "C" void LLVMRustDestroyArchive(LLVMRustArchiveRef RustArchive) { + delete RustArchive; +} + +extern "C" LLVMRustArchiveIteratorRef +LLVMRustArchiveIteratorNew(LLVMRustArchiveRef RustArchive) { + Archive *Archive = RustArchive->getBinary(); +#if LLVM_VERSION_GE(10, 0) + std::unique_ptr Err = std::make_unique(Error::success()); +#else + std::unique_ptr Err = llvm::make_unique(Error::success()); +#endif + auto Cur = Archive->child_begin(*Err); + if (*Err) { + LLVMRustSetLastError(toString(std::move(*Err)).c_str()); + return nullptr; + } + auto End = Archive->child_end(); + return new RustArchiveIterator(Cur, End, std::move(Err)); +} + +extern "C" LLVMRustArchiveChildConstRef +LLVMRustArchiveIteratorNext(LLVMRustArchiveIteratorRef RAI) { + if (RAI->Cur == RAI->End) + return nullptr; + + // Advancing the iterator validates the next child, and this can + // uncover an error. LLVM requires that we check all Errors, + // so we only advance the iterator if we actually need to fetch + // the next child. + // This means we must not advance the iterator in the *first* call, + // but instead advance it *before* fetching the child in all later calls. + if (!RAI->First) { + ++RAI->Cur; + if (*RAI->Err) { + LLVMRustSetLastError(toString(std::move(*RAI->Err)).c_str()); + return nullptr; + } + } else { + RAI->First = false; + } + + if (RAI->Cur == RAI->End) + return nullptr; + + const Archive::Child &Child = *RAI->Cur.operator->(); + Archive::Child *Ret = new Archive::Child(Child); + + return Ret; +} + +extern "C" void LLVMRustArchiveChildFree(LLVMRustArchiveChildRef Child) { + delete Child; +} + +extern "C" void LLVMRustArchiveIteratorFree(LLVMRustArchiveIteratorRef RAI) { + delete RAI; +} + +extern "C" const char * +LLVMRustArchiveChildName(LLVMRustArchiveChildConstRef Child, size_t *Size) { + Expected NameOrErr = Child->getName(); + if (!NameOrErr) { + // rustc_codegen_llvm currently doesn't use this error string, but it might be + // useful in the future, and in the mean time this tells LLVM that the + // error was not ignored and that it shouldn't abort the process. + LLVMRustSetLastError(toString(NameOrErr.takeError()).c_str()); + return nullptr; + } + StringRef Name = NameOrErr.get(); + *Size = Name.size(); + return Name.data(); +} + +extern "C" const char *LLVMRustArchiveChildData(LLVMRustArchiveChildRef Child, + size_t *Size) { + StringRef Buf; + Expected BufOrErr = Child->getBuffer(); + if (!BufOrErr) { + LLVMRustSetLastError(toString(BufOrErr.takeError()).c_str()); + return nullptr; + } + Buf = BufOrErr.get(); + *Size = Buf.size(); + return Buf.data(); +} + +extern "C" LLVMRustArchiveMemberRef +LLVMRustArchiveMemberNew(char *Filename, char *Name, + LLVMRustArchiveChildRef Child) { + RustArchiveMember *Member = new RustArchiveMember; + Member->Filename = Filename; + Member->Name = Name; + if (Child) + Member->Child = *Child; + return Member; +} + +extern "C" void LLVMRustArchiveMemberFree(LLVMRustArchiveMemberRef Member) { + delete Member; +} + +extern "C" LLVMRustResult +LLVMRustWriteArchive(char *Dst, size_t NumMembers, + const LLVMRustArchiveMemberRef *NewMembers, + bool WriteSymbtab, LLVMRustArchiveKind RustKind) { + + std::vector Members; + auto Kind = fromRust(RustKind); + + for (size_t I = 0; I < NumMembers; I++) { + auto Member = NewMembers[I]; + assert(Member->Name); + if (Member->Filename) { + Expected MOrErr = + NewArchiveMember::getFile(Member->Filename, true); + if (!MOrErr) { + LLVMRustSetLastError(toString(MOrErr.takeError()).c_str()); + return LLVMRustResult::Failure; + } + MOrErr->MemberName = sys::path::filename(MOrErr->MemberName); + Members.push_back(std::move(*MOrErr)); + } else { + Expected MOrErr = + NewArchiveMember::getOldMember(Member->Child, true); + if (!MOrErr) { + LLVMRustSetLastError(toString(MOrErr.takeError()).c_str()); + return LLVMRustResult::Failure; + } + Members.push_back(std::move(*MOrErr)); + } + } + + auto Result = writeArchive(Dst, Members, WriteSymbtab, Kind, true, false); + if (!Result) + return LLVMRustResult::Success; + LLVMRustSetLastError(toString(std::move(Result)).c_str()); + + return LLVMRustResult::Failure; +} diff --git a/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp new file mode 100644 index 00000000000..2b1143a4ecf --- /dev/null +++ b/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp @@ -0,0 +1,70 @@ +#include "LLVMWrapper.h" +#include "llvm/ProfileData/Coverage/CoverageMapping.h" +#include "llvm/ProfileData/Coverage/CoverageMappingWriter.h" +#include "llvm/ProfileData/InstrProf.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/Support/LEB128.h" + +#include + +using namespace llvm; + +extern "C" void LLVMRustCoverageWriteFilenamesSectionToBuffer( + const char* const Filenames[], + size_t FilenamesLen, + RustStringRef BufferOut) { + // LLVM 11's CoverageFilenamesSectionWriter uses its new `Version4` format, + // so we're manually writing the `Version3` format ourselves. + RawRustStringOstream OS(BufferOut); + encodeULEB128(FilenamesLen, OS); + for (size_t i = 0; i < FilenamesLen; i++) { + StringRef Filename(Filenames[i]); + encodeULEB128(Filename.size(), OS); + OS << Filename; + } +} + +extern "C" void LLVMRustCoverageWriteMappingToBuffer( + const unsigned *VirtualFileMappingIDs, + unsigned NumVirtualFileMappingIDs, + const coverage::CounterExpression *Expressions, + unsigned NumExpressions, + coverage::CounterMappingRegion *MappingRegions, + unsigned NumMappingRegions, + RustStringRef BufferOut) { + auto CoverageMappingWriter = coverage::CoverageMappingWriter( + makeArrayRef(VirtualFileMappingIDs, NumVirtualFileMappingIDs), + makeArrayRef(Expressions, NumExpressions), + makeMutableArrayRef(MappingRegions, NumMappingRegions)); + RawRustStringOstream OS(BufferOut); + CoverageMappingWriter.write(OS); +} + +extern "C" LLVMValueRef LLVMRustCoverageCreatePGOFuncNameVar(LLVMValueRef F, const char *FuncName) { + StringRef FuncNameRef(FuncName); + return wrap(createPGOFuncNameVar(*cast(unwrap(F)), FuncNameRef)); +} + +extern "C" uint64_t LLVMRustCoverageComputeHash(const char *Name) { + StringRef NameRef(Name); + return IndexedInstrProf::ComputeHash(NameRef); +} + +extern "C" void LLVMRustCoverageWriteSectionNameToString(LLVMModuleRef M, + RustStringRef Str) { + Triple TargetTriple(unwrap(M)->getTargetTriple()); + auto name = getInstrProfSectionName(IPSK_covmap, + TargetTriple.getObjectFormat()); + RawRustStringOstream OS(Str); + OS << name; +} + +extern "C" void LLVMRustCoverageWriteMappingVarNameToString(RustStringRef Str) { + auto name = getCoverageMappingVarName(); + RawRustStringOstream OS(Str); + OS << name; +} + +extern "C" uint32_t LLVMRustCoverageMappingVersion() { + return coverage::CovMapVersion::Version3; +} diff --git a/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h b/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h new file mode 100644 index 00000000000..57b8664d3b6 --- /dev/null +++ b/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h @@ -0,0 +1,115 @@ +#include "llvm-c/BitReader.h" +#include "llvm-c/Core.h" +#include "llvm-c/Object.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/Triple.h" +#include "llvm/Analysis/Lint.h" +#include "llvm/Analysis/Passes.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/InlineAsm.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/DynamicLibrary.h" +#include "llvm/Support/FormattedStream.h" +#include "llvm/Support/Host.h" +#include "llvm/Support/Memory.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Support/TargetSelect.h" +#include "llvm/Support/Timer.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Target/TargetMachine.h" +#include "llvm/Target/TargetOptions.h" +#include "llvm/Transforms/IPO.h" +#include "llvm/Transforms/Instrumentation.h" +#include "llvm/Transforms/Scalar.h" +#include "llvm/Transforms/Vectorize.h" + +#define LLVM_VERSION_GE(major, minor) \ + (LLVM_VERSION_MAJOR > (major) || \ + LLVM_VERSION_MAJOR == (major) && LLVM_VERSION_MINOR >= (minor)) + +#define LLVM_VERSION_EQ(major, minor) \ + (LLVM_VERSION_MAJOR == (major) && LLVM_VERSION_MINOR == (minor)) + +#define LLVM_VERSION_LE(major, minor) \ + (LLVM_VERSION_MAJOR < (major) || \ + LLVM_VERSION_MAJOR == (major) && LLVM_VERSION_MINOR <= (minor)) + +#define LLVM_VERSION_LT(major, minor) (!LLVM_VERSION_GE((major), (minor))) + +#include "llvm/IR/LegacyPassManager.h" + +#include "llvm/Bitcode/BitcodeReader.h" +#include "llvm/Bitcode/BitcodeWriter.h" + +#include "llvm/IR/DIBuilder.h" +#include "llvm/IR/DebugInfo.h" +#include "llvm/IR/IRPrintingPasses.h" +#include "llvm/Linker/Linker.h" + +extern "C" void LLVMRustSetLastError(const char *); + +enum class LLVMRustResult { Success, Failure }; + +enum LLVMRustAttribute { + AlwaysInline = 0, + ByVal = 1, + Cold = 2, + InlineHint = 3, + MinSize = 4, + Naked = 5, + NoAlias = 6, + NoCapture = 7, + NoInline = 8, + NonNull = 9, + NoRedZone = 10, + NoReturn = 11, + NoUnwind = 12, + OptimizeForSize = 13, + ReadOnly = 14, + SExt = 15, + StructRet = 16, + UWTable = 17, + ZExt = 18, + InReg = 19, + SanitizeThread = 20, + SanitizeAddress = 21, + SanitizeMemory = 22, + NonLazyBind = 23, + OptimizeNone = 24, + ReturnsTwice = 25, + ReadNone = 26, + InaccessibleMemOnly = 27, +}; + +typedef struct OpaqueRustString *RustStringRef; +typedef struct LLVMOpaqueTwine *LLVMTwineRef; +typedef struct LLVMOpaqueSMDiagnostic *LLVMSMDiagnosticRef; + +extern "C" void LLVMRustStringWriteImpl(RustStringRef Str, const char *Ptr, + size_t Size); + +class RawRustStringOstream : public llvm::raw_ostream { + RustStringRef Str; + uint64_t Pos; + + void write_impl(const char *Ptr, size_t Size) override { + LLVMRustStringWriteImpl(Str, Ptr, Size); + Pos += Size; + } + + uint64_t current_pos() const override { return Pos; } + +public: + explicit RawRustStringOstream(RustStringRef Str) : Str(Str), Pos(0) {} + + ~RawRustStringOstream() { + // LLVM requires this. + flush(); + } +}; diff --git a/compiler/rustc_llvm/llvm-wrapper/Linker.cpp b/compiler/rustc_llvm/llvm-wrapper/Linker.cpp new file mode 100644 index 00000000000..8766e96f086 --- /dev/null +++ b/compiler/rustc_llvm/llvm-wrapper/Linker.cpp @@ -0,0 +1,48 @@ +#include "llvm/Linker/Linker.h" + +#include "LLVMWrapper.h" + +using namespace llvm; + +struct RustLinker { + Linker L; + LLVMContext &Ctx; + + RustLinker(Module &M) : + L(M), + Ctx(M.getContext()) + {} +}; + +extern "C" RustLinker* +LLVMRustLinkerNew(LLVMModuleRef DstRef) { + Module *Dst = unwrap(DstRef); + + return new RustLinker(*Dst); +} + +extern "C" void +LLVMRustLinkerFree(RustLinker *L) { + delete L; +} + +extern "C" bool +LLVMRustLinkerAdd(RustLinker *L, char *BC, size_t Len) { + std::unique_ptr Buf = + MemoryBuffer::getMemBufferCopy(StringRef(BC, Len)); + + Expected> SrcOrError = + llvm::getLazyBitcodeModule(Buf->getMemBufferRef(), L->Ctx); + if (!SrcOrError) { + LLVMRustSetLastError(toString(SrcOrError.takeError()).c_str()); + return false; + } + + auto Src = std::move(*SrcOrError); + + if (L->L.linkInModule(std::move(Src))) { + LLVMRustSetLastError(""); + return false; + } + return true; +} diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp new file mode 100644 index 00000000000..7b1c3f9ba2c --- /dev/null +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -0,0 +1,1655 @@ +#include + +#include +#include + +#include "LLVMWrapper.h" + +#include "llvm/Analysis/TargetLibraryInfo.h" +#include "llvm/Analysis/TargetTransformInfo.h" +#include "llvm/CodeGen/TargetSubtargetInfo.h" +#include "llvm/InitializePasses.h" +#include "llvm/IR/AutoUpgrade.h" +#include "llvm/IR/AssemblyAnnotationWriter.h" +#include "llvm/IR/IntrinsicInst.h" +#include "llvm/IR/Verifier.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/Object/IRObjectFile.h" +#include "llvm/Passes/PassBuilder.h" +#if LLVM_VERSION_GE(9, 0) +#include "llvm/Passes/StandardInstrumentations.h" +#endif +#include "llvm/Support/CBindingWrapping.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Host.h" +#include "llvm/Target/TargetMachine.h" +#include "llvm/Transforms/IPO/PassManagerBuilder.h" +#include "llvm/Transforms/IPO/AlwaysInliner.h" +#include "llvm/Transforms/IPO/FunctionImport.h" +#include "llvm/Transforms/Utils/FunctionImportUtils.h" +#include "llvm/LTO/LTO.h" +#include "llvm-c/Transforms/PassManagerBuilder.h" + +#include "llvm/Transforms/Instrumentation.h" +#if LLVM_VERSION_GE(9, 0) +#include "llvm/Transforms/Instrumentation/AddressSanitizer.h" +#include "llvm/Support/TimeProfiler.h" +#endif +#include "llvm/Transforms/Instrumentation/ThreadSanitizer.h" +#include "llvm/Transforms/Instrumentation/MemorySanitizer.h" +#if LLVM_VERSION_GE(9, 0) +#include "llvm/Transforms/Utils/CanonicalizeAliases.h" +#endif +#include "llvm/Transforms/Utils/NameAnonGlobals.h" + +using namespace llvm; + +typedef struct LLVMOpaquePass *LLVMPassRef; +typedef struct LLVMOpaqueTargetMachine *LLVMTargetMachineRef; + +DEFINE_STDCXX_CONVERSION_FUNCTIONS(Pass, LLVMPassRef) +DEFINE_STDCXX_CONVERSION_FUNCTIONS(TargetMachine, LLVMTargetMachineRef) +#if LLVM_VERSION_LT(11, 0) +DEFINE_STDCXX_CONVERSION_FUNCTIONS(PassManagerBuilder, + LLVMPassManagerBuilderRef) +#endif + +extern "C" void LLVMInitializePasses() { + PassRegistry &Registry = *PassRegistry::getPassRegistry(); + initializeCore(Registry); + initializeCodeGen(Registry); + initializeScalarOpts(Registry); + initializeVectorization(Registry); + initializeIPO(Registry); + initializeAnalysis(Registry); + initializeTransformUtils(Registry); + initializeInstCombine(Registry); + initializeInstrumentation(Registry); + initializeTarget(Registry); +} + +extern "C" void LLVMTimeTraceProfilerInitialize() { +#if LLVM_VERSION_GE(10, 0) + timeTraceProfilerInitialize( + /* TimeTraceGranularity */ 0, + /* ProcName */ "rustc"); +#elif LLVM_VERSION_GE(9, 0) + timeTraceProfilerInitialize(); +#endif +} + +extern "C" void LLVMTimeTraceProfilerFinish(const char* FileName) { +#if LLVM_VERSION_GE(9, 0) + StringRef FN(FileName); + std::error_code EC; + raw_fd_ostream OS(FN, EC, sys::fs::CD_CreateAlways); + + timeTraceProfilerWrite(OS); + timeTraceProfilerCleanup(); +#endif +} + +enum class LLVMRustPassKind { + Other, + Function, + Module, +}; + +static LLVMRustPassKind toRust(PassKind Kind) { + switch (Kind) { + case PT_Function: + return LLVMRustPassKind::Function; + case PT_Module: + return LLVMRustPassKind::Module; + default: + return LLVMRustPassKind::Other; + } +} + +extern "C" LLVMPassRef LLVMRustFindAndCreatePass(const char *PassName) { + StringRef SR(PassName); + PassRegistry *PR = PassRegistry::getPassRegistry(); + + const PassInfo *PI = PR->getPassInfo(SR); + if (PI) { + return wrap(PI->createPass()); + } + return nullptr; +} + +extern "C" LLVMPassRef LLVMRustCreateAddressSanitizerFunctionPass(bool Recover) { + const bool CompileKernel = false; + const bool UseAfterScope = true; + + return wrap(createAddressSanitizerFunctionPass(CompileKernel, Recover, UseAfterScope)); +} + +extern "C" LLVMPassRef LLVMRustCreateModuleAddressSanitizerPass(bool Recover) { + const bool CompileKernel = false; + +#if LLVM_VERSION_GE(9, 0) + return wrap(createModuleAddressSanitizerLegacyPassPass(CompileKernel, Recover)); +#else + return wrap(createAddressSanitizerModulePass(CompileKernel, Recover)); +#endif +} + +extern "C" LLVMPassRef LLVMRustCreateMemorySanitizerPass(int TrackOrigins, bool Recover) { +#if LLVM_VERSION_GE(9, 0) + const bool CompileKernel = false; + + return wrap(createMemorySanitizerLegacyPassPass( + MemorySanitizerOptions{TrackOrigins, Recover, CompileKernel})); +#else + return wrap(createMemorySanitizerLegacyPassPass(TrackOrigins, Recover)); +#endif +} + +extern "C" LLVMPassRef LLVMRustCreateThreadSanitizerPass() { + return wrap(createThreadSanitizerLegacyPassPass()); +} + +extern "C" LLVMRustPassKind LLVMRustPassKind(LLVMPassRef RustPass) { + assert(RustPass); + Pass *Pass = unwrap(RustPass); + return toRust(Pass->getPassKind()); +} + +extern "C" void LLVMRustAddPass(LLVMPassManagerRef PMR, LLVMPassRef RustPass) { + assert(RustPass); + Pass *Pass = unwrap(RustPass); + PassManagerBase *PMB = unwrap(PMR); + PMB->add(Pass); +} + +extern "C" +void LLVMRustPassManagerBuilderPopulateThinLTOPassManager( + LLVMPassManagerBuilderRef PMBR, + LLVMPassManagerRef PMR +) { + unwrap(PMBR)->populateThinLTOPassManager(*unwrap(PMR)); +} + +extern "C" +void LLVMRustAddLastExtensionPasses( + LLVMPassManagerBuilderRef PMBR, LLVMPassRef *Passes, size_t NumPasses) { + auto AddExtensionPasses = [Passes, NumPasses]( + const PassManagerBuilder &Builder, PassManagerBase &PM) { + for (size_t I = 0; I < NumPasses; I++) { + PM.add(unwrap(Passes[I])); + } + }; + // Add the passes to both of the pre-finalization extension points, + // so they are run for optimized and non-optimized builds. + unwrap(PMBR)->addExtension(PassManagerBuilder::EP_OptimizerLast, + AddExtensionPasses); + unwrap(PMBR)->addExtension(PassManagerBuilder::EP_EnabledOnOptLevel0, + AddExtensionPasses); +} + +#ifdef LLVM_COMPONENT_X86 +#define SUBTARGET_X86 SUBTARGET(X86) +#else +#define SUBTARGET_X86 +#endif + +#ifdef LLVM_COMPONENT_ARM +#define SUBTARGET_ARM SUBTARGET(ARM) +#else +#define SUBTARGET_ARM +#endif + +#ifdef LLVM_COMPONENT_AARCH64 +#define SUBTARGET_AARCH64 SUBTARGET(AArch64) +#else +#define SUBTARGET_AARCH64 +#endif + +#ifdef LLVM_COMPONENT_AVR +#define SUBTARGET_AVR SUBTARGET(AVR) +#else +#define SUBTARGET_AVR +#endif + +#ifdef LLVM_COMPONENT_MIPS +#define SUBTARGET_MIPS SUBTARGET(Mips) +#else +#define SUBTARGET_MIPS +#endif + +#ifdef LLVM_COMPONENT_POWERPC +#define SUBTARGET_PPC SUBTARGET(PPC) +#else +#define SUBTARGET_PPC +#endif + +#ifdef LLVM_COMPONENT_SYSTEMZ +#define SUBTARGET_SYSTEMZ SUBTARGET(SystemZ) +#else +#define SUBTARGET_SYSTEMZ +#endif + +#ifdef LLVM_COMPONENT_MSP430 +#define SUBTARGET_MSP430 SUBTARGET(MSP430) +#else +#define SUBTARGET_MSP430 +#endif + +#ifdef LLVM_COMPONENT_RISCV +#define SUBTARGET_RISCV SUBTARGET(RISCV) +#else +#define SUBTARGET_RISCV +#endif + +#ifdef LLVM_COMPONENT_SPARC +#define SUBTARGET_SPARC SUBTARGET(Sparc) +#else +#define SUBTARGET_SPARC +#endif + +#ifdef LLVM_COMPONENT_HEXAGON +#define SUBTARGET_HEXAGON SUBTARGET(Hexagon) +#else +#define SUBTARGET_HEXAGON +#endif + +#define GEN_SUBTARGETS \ + SUBTARGET_X86 \ + SUBTARGET_ARM \ + SUBTARGET_AARCH64 \ + SUBTARGET_AVR \ + SUBTARGET_MIPS \ + SUBTARGET_PPC \ + SUBTARGET_SYSTEMZ \ + SUBTARGET_MSP430 \ + SUBTARGET_SPARC \ + SUBTARGET_HEXAGON \ + SUBTARGET_RISCV \ + +#define SUBTARGET(x) \ + namespace llvm { \ + extern const SubtargetFeatureKV x##FeatureKV[]; \ + extern const SubtargetFeatureKV x##SubTypeKV[]; \ + } + +GEN_SUBTARGETS +#undef SUBTARGET + +extern "C" bool LLVMRustHasFeature(LLVMTargetMachineRef TM, + const char *Feature) { + TargetMachine *Target = unwrap(TM); + const MCSubtargetInfo *MCInfo = Target->getMCSubtargetInfo(); + return MCInfo->checkFeatures(std::string("+") + Feature); +} + +enum class LLVMRustCodeModel { + Tiny, + Small, + Kernel, + Medium, + Large, + None, +}; + +static Optional fromRust(LLVMRustCodeModel Model) { + switch (Model) { + case LLVMRustCodeModel::Tiny: + return CodeModel::Tiny; + case LLVMRustCodeModel::Small: + return CodeModel::Small; + case LLVMRustCodeModel::Kernel: + return CodeModel::Kernel; + case LLVMRustCodeModel::Medium: + return CodeModel::Medium; + case LLVMRustCodeModel::Large: + return CodeModel::Large; + case LLVMRustCodeModel::None: + return None; + default: + report_fatal_error("Bad CodeModel."); + } +} + +enum class LLVMRustCodeGenOptLevel { + None, + Less, + Default, + Aggressive, +}; + +static CodeGenOpt::Level fromRust(LLVMRustCodeGenOptLevel Level) { + switch (Level) { + case LLVMRustCodeGenOptLevel::None: + return CodeGenOpt::None; + case LLVMRustCodeGenOptLevel::Less: + return CodeGenOpt::Less; + case LLVMRustCodeGenOptLevel::Default: + return CodeGenOpt::Default; + case LLVMRustCodeGenOptLevel::Aggressive: + return CodeGenOpt::Aggressive; + default: + report_fatal_error("Bad CodeGenOptLevel."); + } +} + +enum class LLVMRustPassBuilderOptLevel { + O0, + O1, + O2, + O3, + Os, + Oz, +}; + +static PassBuilder::OptimizationLevel fromRust(LLVMRustPassBuilderOptLevel Level) { + switch (Level) { + case LLVMRustPassBuilderOptLevel::O0: + return PassBuilder::OptimizationLevel::O0; + case LLVMRustPassBuilderOptLevel::O1: + return PassBuilder::OptimizationLevel::O1; + case LLVMRustPassBuilderOptLevel::O2: + return PassBuilder::OptimizationLevel::O2; + case LLVMRustPassBuilderOptLevel::O3: + return PassBuilder::OptimizationLevel::O3; + case LLVMRustPassBuilderOptLevel::Os: + return PassBuilder::OptimizationLevel::Os; + case LLVMRustPassBuilderOptLevel::Oz: + return PassBuilder::OptimizationLevel::Oz; + default: + report_fatal_error("Bad PassBuilderOptLevel."); + } +} + +enum class LLVMRustRelocModel { + Static, + PIC, + DynamicNoPic, + ROPI, + RWPI, + ROPIRWPI, +}; + +static Reloc::Model fromRust(LLVMRustRelocModel RustReloc) { + switch (RustReloc) { + case LLVMRustRelocModel::Static: + return Reloc::Static; + case LLVMRustRelocModel::PIC: + return Reloc::PIC_; + case LLVMRustRelocModel::DynamicNoPic: + return Reloc::DynamicNoPIC; + case LLVMRustRelocModel::ROPI: + return Reloc::ROPI; + case LLVMRustRelocModel::RWPI: + return Reloc::RWPI; + case LLVMRustRelocModel::ROPIRWPI: + return Reloc::ROPI_RWPI; + } + report_fatal_error("Bad RelocModel."); +} + +#ifdef LLVM_RUSTLLVM +/// getLongestEntryLength - Return the length of the longest entry in the table. +template +static size_t getLongestEntryLength(ArrayRef Table) { + size_t MaxLen = 0; + for (auto &I : Table) + MaxLen = std::max(MaxLen, std::strlen(I.Key)); + return MaxLen; +} + +extern "C" void LLVMRustPrintTargetCPUs(LLVMTargetMachineRef TM) { + const TargetMachine *Target = unwrap(TM); + const MCSubtargetInfo *MCInfo = Target->getMCSubtargetInfo(); + const Triple::ArchType HostArch = Triple(sys::getProcessTriple()).getArch(); + const Triple::ArchType TargetArch = Target->getTargetTriple().getArch(); + const ArrayRef CPUTable = MCInfo->getCPUTable(); + unsigned MaxCPULen = getLongestEntryLength(CPUTable); + + printf("Available CPUs for this target:\n"); + if (HostArch == TargetArch) { + const StringRef HostCPU = sys::getHostCPUName(); + printf(" %-*s - Select the CPU of the current host (currently %.*s).\n", + MaxCPULen, "native", (int)HostCPU.size(), HostCPU.data()); + } + for (auto &CPU : CPUTable) + printf(" %-*s\n", MaxCPULen, CPU.Key); + printf("\n"); +} + +extern "C" void LLVMRustPrintTargetFeatures(LLVMTargetMachineRef TM) { + const TargetMachine *Target = unwrap(TM); + const MCSubtargetInfo *MCInfo = Target->getMCSubtargetInfo(); + const ArrayRef FeatTable = MCInfo->getFeatureTable(); + unsigned MaxFeatLen = getLongestEntryLength(FeatTable); + + printf("Available features for this target:\n"); + for (auto &Feature : FeatTable) + printf(" %-*s - %s.\n", MaxFeatLen, Feature.Key, Feature.Desc); + printf("\nRust-specific features:\n"); + printf(" %-*s - %s.\n", + MaxFeatLen, + "crt-static", + "Enables libraries with C Run-time Libraries(CRT) to be statically linked" + ); + printf("\n"); + + printf("Use +feature to enable a feature, or -feature to disable it.\n" + "For example, rustc -C -target-cpu=mycpu -C " + "target-feature=+feature1,-feature2\n\n"); +} + +#else + +extern "C" void LLVMRustPrintTargetCPUs(LLVMTargetMachineRef) { + printf("Target CPU help is not supported by this LLVM version.\n\n"); +} + +extern "C" void LLVMRustPrintTargetFeatures(LLVMTargetMachineRef) { + printf("Target features help is not supported by this LLVM version.\n\n"); +} +#endif + +extern "C" const char* LLVMRustGetHostCPUName(size_t *len) { + StringRef Name = sys::getHostCPUName(); + *len = Name.size(); + return Name.data(); +} + +extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine( + const char *TripleStr, const char *CPU, const char *Feature, + const char *ABIStr, LLVMRustCodeModel RustCM, LLVMRustRelocModel RustReloc, + LLVMRustCodeGenOptLevel RustOptLevel, bool UseSoftFloat, + bool FunctionSections, + bool DataSections, + bool TrapUnreachable, + bool Singlethread, + bool AsmComments, + bool EmitStackSizeSection, + bool RelaxELFRelocations, + bool UseInitArray) { + + auto OptLevel = fromRust(RustOptLevel); + auto RM = fromRust(RustReloc); + auto CM = fromRust(RustCM); + + std::string Error; + Triple Trip(Triple::normalize(TripleStr)); + const llvm::Target *TheTarget = + TargetRegistry::lookupTarget(Trip.getTriple(), Error); + if (TheTarget == nullptr) { + LLVMRustSetLastError(Error.c_str()); + return nullptr; + } + + TargetOptions Options; + + Options.FloatABIType = FloatABI::Default; + if (UseSoftFloat) { + Options.FloatABIType = FloatABI::Soft; + } + Options.DataSections = DataSections; + Options.FunctionSections = FunctionSections; + Options.MCOptions.AsmVerbose = AsmComments; + Options.MCOptions.PreserveAsmComments = AsmComments; + Options.MCOptions.ABIName = ABIStr; + Options.RelaxELFRelocations = RelaxELFRelocations; + Options.UseInitArray = UseInitArray; + + if (TrapUnreachable) { + // Tell LLVM to codegen `unreachable` into an explicit trap instruction. + // This limits the extent of possible undefined behavior in some cases, as + // it prevents control flow from "falling through" into whatever code + // happens to be laid out next in memory. + Options.TrapUnreachable = true; + } + + if (Singlethread) { + Options.ThreadModel = ThreadModel::Single; + } + + Options.EmitStackSizeSection = EmitStackSizeSection; + + TargetMachine *TM = TheTarget->createTargetMachine( + Trip.getTriple(), CPU, Feature, Options, RM, CM, OptLevel); + return wrap(TM); +} + +extern "C" void LLVMRustDisposeTargetMachine(LLVMTargetMachineRef TM) { + delete unwrap(TM); +} + +extern "C" void LLVMRustConfigurePassManagerBuilder( + LLVMPassManagerBuilderRef PMBR, LLVMRustCodeGenOptLevel OptLevel, + bool MergeFunctions, bool SLPVectorize, bool LoopVectorize, bool PrepareForThinLTO, + const char* PGOGenPath, const char* PGOUsePath) { + unwrap(PMBR)->MergeFunctions = MergeFunctions; + unwrap(PMBR)->SLPVectorize = SLPVectorize; + unwrap(PMBR)->OptLevel = fromRust(OptLevel); + unwrap(PMBR)->LoopVectorize = LoopVectorize; + unwrap(PMBR)->PrepareForThinLTO = PrepareForThinLTO; + + if (PGOGenPath) { + assert(!PGOUsePath); + unwrap(PMBR)->EnablePGOInstrGen = true; + unwrap(PMBR)->PGOInstrGen = PGOGenPath; + } + if (PGOUsePath) { + assert(!PGOGenPath); + unwrap(PMBR)->PGOInstrUse = PGOUsePath; + } +} + +// Unfortunately, the LLVM C API doesn't provide a way to set the `LibraryInfo` +// field of a PassManagerBuilder, we expose our own method of doing so. +extern "C" void LLVMRustAddBuilderLibraryInfo(LLVMPassManagerBuilderRef PMBR, + LLVMModuleRef M, + bool DisableSimplifyLibCalls) { + Triple TargetTriple(unwrap(M)->getTargetTriple()); + TargetLibraryInfoImpl *TLI = new TargetLibraryInfoImpl(TargetTriple); + if (DisableSimplifyLibCalls) + TLI->disableAllFunctions(); + unwrap(PMBR)->LibraryInfo = TLI; +} + +// Unfortunately, the LLVM C API doesn't provide a way to create the +// TargetLibraryInfo pass, so we use this method to do so. +extern "C" void LLVMRustAddLibraryInfo(LLVMPassManagerRef PMR, LLVMModuleRef M, + bool DisableSimplifyLibCalls) { + Triple TargetTriple(unwrap(M)->getTargetTriple()); + TargetLibraryInfoImpl TLII(TargetTriple); + if (DisableSimplifyLibCalls) + TLII.disableAllFunctions(); + unwrap(PMR)->add(new TargetLibraryInfoWrapperPass(TLII)); +} + +// Unfortunately, the LLVM C API doesn't provide an easy way of iterating over +// all the functions in a module, so we do that manually here. You'll find +// similar code in clang's BackendUtil.cpp file. +extern "C" void LLVMRustRunFunctionPassManager(LLVMPassManagerRef PMR, + LLVMModuleRef M) { + llvm::legacy::FunctionPassManager *P = + unwrap(PMR); + P->doInitialization(); + + // Upgrade all calls to old intrinsics first. + for (Module::iterator I = unwrap(M)->begin(), E = unwrap(M)->end(); I != E;) + UpgradeCallsToIntrinsic(&*I++); // must be post-increment, as we remove + + for (Module::iterator I = unwrap(M)->begin(), E = unwrap(M)->end(); I != E; + ++I) + if (!I->isDeclaration()) + P->run(*I); + + P->doFinalization(); +} + +extern "C" void LLVMRustSetLLVMOptions(int Argc, char **Argv) { + // Initializing the command-line options more than once is not allowed. So, + // check if they've already been initialized. (This could happen if we're + // being called from rustpkg, for example). If the arguments change, then + // that's just kinda unfortunate. + static bool Initialized = false; + if (Initialized) + return; + Initialized = true; + cl::ParseCommandLineOptions(Argc, Argv); +} + +enum class LLVMRustFileType { + AssemblyFile, + ObjectFile, +}; + +#if LLVM_VERSION_GE(10, 0) +static CodeGenFileType fromRust(LLVMRustFileType Type) { + switch (Type) { + case LLVMRustFileType::AssemblyFile: + return CGFT_AssemblyFile; + case LLVMRustFileType::ObjectFile: + return CGFT_ObjectFile; + default: + report_fatal_error("Bad FileType."); + } +} +#else +static TargetMachine::CodeGenFileType fromRust(LLVMRustFileType Type) { + switch (Type) { + case LLVMRustFileType::AssemblyFile: + return TargetMachine::CGFT_AssemblyFile; + case LLVMRustFileType::ObjectFile: + return TargetMachine::CGFT_ObjectFile; + default: + report_fatal_error("Bad FileType."); + } +} +#endif + +extern "C" LLVMRustResult +LLVMRustWriteOutputFile(LLVMTargetMachineRef Target, LLVMPassManagerRef PMR, + LLVMModuleRef M, const char *Path, + LLVMRustFileType RustFileType) { + llvm::legacy::PassManager *PM = unwrap(PMR); + auto FileType = fromRust(RustFileType); + + std::string ErrorInfo; + std::error_code EC; + raw_fd_ostream OS(Path, EC, sys::fs::F_None); + if (EC) + ErrorInfo = EC.message(); + if (ErrorInfo != "") { + LLVMRustSetLastError(ErrorInfo.c_str()); + return LLVMRustResult::Failure; + } + + buffer_ostream BOS(OS); + unwrap(Target)->addPassesToEmitFile(*PM, BOS, nullptr, FileType, false); + PM->run(*unwrap(M)); + + // Apparently `addPassesToEmitFile` adds a pointer to our on-the-stack output + // stream (OS), so the only real safe place to delete this is here? Don't we + // wish this was written in Rust? + LLVMDisposePassManager(PMR); + return LLVMRustResult::Success; +} + +extern "C" typedef void (*LLVMRustSelfProfileBeforePassCallback)(void*, // LlvmSelfProfiler + const char*, // pass name + const char*); // IR name +extern "C" typedef void (*LLVMRustSelfProfileAfterPassCallback)(void*); // LlvmSelfProfiler + +#if LLVM_VERSION_GE(9, 0) + +std::string LLVMRustwrappedIrGetName(const llvm::Any &WrappedIr) { + if (any_isa(WrappedIr)) + return any_cast(WrappedIr)->getName().str(); + if (any_isa(WrappedIr)) + return any_cast(WrappedIr)->getName().str(); + if (any_isa(WrappedIr)) + return any_cast(WrappedIr)->getName().str(); + if (any_isa(WrappedIr)) + return any_cast(WrappedIr)->getName(); + return ""; +} + + +void LLVMSelfProfileInitializeCallbacks( + PassInstrumentationCallbacks& PIC, void* LlvmSelfProfiler, + LLVMRustSelfProfileBeforePassCallback BeforePassCallback, + LLVMRustSelfProfileAfterPassCallback AfterPassCallback) { + PIC.registerBeforePassCallback([LlvmSelfProfiler, BeforePassCallback]( + StringRef Pass, llvm::Any Ir) { + std::string PassName = Pass.str(); + std::string IrName = LLVMRustwrappedIrGetName(Ir); + BeforePassCallback(LlvmSelfProfiler, PassName.c_str(), IrName.c_str()); + return true; + }); + + PIC.registerAfterPassCallback( + [LlvmSelfProfiler, AfterPassCallback](StringRef Pass, llvm::Any Ir) { + AfterPassCallback(LlvmSelfProfiler); + }); + + PIC.registerAfterPassInvalidatedCallback( + [LlvmSelfProfiler, AfterPassCallback](StringRef Pass) { + AfterPassCallback(LlvmSelfProfiler); + }); + + PIC.registerBeforeAnalysisCallback([LlvmSelfProfiler, BeforePassCallback]( + StringRef Pass, llvm::Any Ir) { + std::string PassName = Pass.str(); + std::string IrName = LLVMRustwrappedIrGetName(Ir); + BeforePassCallback(LlvmSelfProfiler, PassName.c_str(), IrName.c_str()); + }); + + PIC.registerAfterAnalysisCallback( + [LlvmSelfProfiler, AfterPassCallback](StringRef Pass, llvm::Any Ir) { + AfterPassCallback(LlvmSelfProfiler); + }); +} +#endif + +enum class LLVMRustOptStage { + PreLinkNoLTO, + PreLinkThinLTO, + PreLinkFatLTO, + ThinLTO, + FatLTO, +}; + +struct LLVMRustSanitizerOptions { + bool SanitizeAddress; + bool SanitizeAddressRecover; + bool SanitizeMemory; + bool SanitizeMemoryRecover; + int SanitizeMemoryTrackOrigins; + bool SanitizeThread; +}; + +extern "C" void +LLVMRustOptimizeWithNewPassManager( + LLVMModuleRef ModuleRef, + LLVMTargetMachineRef TMRef, + LLVMRustPassBuilderOptLevel OptLevelRust, + LLVMRustOptStage OptStage, + bool NoPrepopulatePasses, bool VerifyIR, bool UseThinLTOBuffers, + bool MergeFunctions, bool UnrollLoops, bool SLPVectorize, bool LoopVectorize, + bool DisableSimplifyLibCalls, bool EmitLifetimeMarkers, + LLVMRustSanitizerOptions *SanitizerOptions, + const char *PGOGenPath, const char *PGOUsePath, + void* LlvmSelfProfiler, + LLVMRustSelfProfileBeforePassCallback BeforePassCallback, + LLVMRustSelfProfileAfterPassCallback AfterPassCallback) { +#if LLVM_VERSION_GE(9, 0) + Module *TheModule = unwrap(ModuleRef); + TargetMachine *TM = unwrap(TMRef); + PassBuilder::OptimizationLevel OptLevel = fromRust(OptLevelRust); + + // FIXME: MergeFunctions is not supported by NewPM yet. + (void) MergeFunctions; + + PipelineTuningOptions PTO; + PTO.LoopUnrolling = UnrollLoops; + PTO.LoopInterleaving = UnrollLoops; + PTO.LoopVectorization = LoopVectorize; + PTO.SLPVectorization = SLPVectorize; + + PassInstrumentationCallbacks PIC; + StandardInstrumentations SI; + SI.registerCallbacks(PIC); + + if (LlvmSelfProfiler){ + LLVMSelfProfileInitializeCallbacks(PIC,LlvmSelfProfiler,BeforePassCallback,AfterPassCallback); + } + + Optional PGOOpt; + if (PGOGenPath) { + assert(!PGOUsePath); + PGOOpt = PGOOptions(PGOGenPath, "", "", PGOOptions::IRInstr); + } else if (PGOUsePath) { + assert(!PGOGenPath); + PGOOpt = PGOOptions(PGOUsePath, "", "", PGOOptions::IRUse); + } + + PassBuilder PB(TM, PTO, PGOOpt, &PIC); + + // FIXME: We may want to expose this as an option. + bool DebugPassManager = false; + LoopAnalysisManager LAM(DebugPassManager); + FunctionAnalysisManager FAM(DebugPassManager); + CGSCCAnalysisManager CGAM(DebugPassManager); + ModuleAnalysisManager MAM(DebugPassManager); + + FAM.registerPass([&] { return PB.buildDefaultAAPipeline(); }); + + Triple TargetTriple(TheModule->getTargetTriple()); + std::unique_ptr TLII(new TargetLibraryInfoImpl(TargetTriple)); + if (DisableSimplifyLibCalls) + TLII->disableAllFunctions(); + FAM.registerPass([&] { return TargetLibraryAnalysis(*TLII); }); + + PB.registerModuleAnalyses(MAM); + PB.registerCGSCCAnalyses(CGAM); + PB.registerFunctionAnalyses(FAM); + PB.registerLoopAnalyses(LAM); + PB.crossRegisterProxies(LAM, FAM, CGAM, MAM); + + // We manually collect pipeline callbacks so we can apply them at O0, where the + // PassBuilder does not create a pipeline. + std::vector> PipelineStartEPCallbacks; +#if LLVM_VERSION_GE(11, 0) + std::vector> + OptimizerLastEPCallbacks; +#else + std::vector> + OptimizerLastEPCallbacks; +#endif + + if (VerifyIR) { + PipelineStartEPCallbacks.push_back([VerifyIR](ModulePassManager &MPM) { + MPM.addPass(VerifierPass()); + }); + } + + if (SanitizerOptions) { + if (SanitizerOptions->SanitizeMemory) { + MemorySanitizerOptions Options( + SanitizerOptions->SanitizeMemoryTrackOrigins, + SanitizerOptions->SanitizeMemoryRecover, + /*CompileKernel=*/false); +#if LLVM_VERSION_GE(11, 0) + OptimizerLastEPCallbacks.push_back( + [Options](ModulePassManager &MPM, PassBuilder::OptimizationLevel Level) { + MPM.addPass(MemorySanitizerPass(Options)); + MPM.addPass(createModuleToFunctionPassAdaptor(MemorySanitizerPass(Options))); + } + ); +#else +#if LLVM_VERSION_GE(10, 0) + PipelineStartEPCallbacks.push_back([Options](ModulePassManager &MPM) { + MPM.addPass(MemorySanitizerPass(Options)); + }); +#endif + OptimizerLastEPCallbacks.push_back( + [Options](FunctionPassManager &FPM, PassBuilder::OptimizationLevel Level) { + FPM.addPass(MemorySanitizerPass(Options)); + } + ); +#endif + } + + if (SanitizerOptions->SanitizeThread) { +#if LLVM_VERSION_GE(11, 0) + OptimizerLastEPCallbacks.push_back( + [](ModulePassManager &MPM, PassBuilder::OptimizationLevel Level) { + MPM.addPass(ThreadSanitizerPass()); + MPM.addPass(createModuleToFunctionPassAdaptor(ThreadSanitizerPass())); + } + ); +#else +#if LLVM_VERSION_GE(10, 0) + PipelineStartEPCallbacks.push_back([](ModulePassManager &MPM) { + MPM.addPass(ThreadSanitizerPass()); + }); +#endif + OptimizerLastEPCallbacks.push_back( + [](FunctionPassManager &FPM, PassBuilder::OptimizationLevel Level) { + FPM.addPass(ThreadSanitizerPass()); + } + ); +#endif + } + + if (SanitizerOptions->SanitizeAddress) { +#if LLVM_VERSION_GE(11, 0) + OptimizerLastEPCallbacks.push_back( + [SanitizerOptions](ModulePassManager &MPM, PassBuilder::OptimizationLevel Level) { + MPM.addPass(RequireAnalysisPass()); + MPM.addPass(ModuleAddressSanitizerPass( + /*CompileKernel=*/false, SanitizerOptions->SanitizeAddressRecover)); + MPM.addPass(createModuleToFunctionPassAdaptor(AddressSanitizerPass( + /*CompileKernel=*/false, SanitizerOptions->SanitizeAddressRecover, + /*UseAfterScope=*/true))); + } + ); +#else + PipelineStartEPCallbacks.push_back([&](ModulePassManager &MPM) { + MPM.addPass(RequireAnalysisPass()); + }); + OptimizerLastEPCallbacks.push_back( + [SanitizerOptions](FunctionPassManager &FPM, PassBuilder::OptimizationLevel Level) { + FPM.addPass(AddressSanitizerPass( + /*CompileKernel=*/false, SanitizerOptions->SanitizeAddressRecover, + /*UseAfterScope=*/true)); + } + ); + PipelineStartEPCallbacks.push_back( + [SanitizerOptions](ModulePassManager &MPM) { + MPM.addPass(ModuleAddressSanitizerPass( + /*CompileKernel=*/false, SanitizerOptions->SanitizeAddressRecover)); + } + ); +#endif + } + } + + ModulePassManager MPM(DebugPassManager); + if (!NoPrepopulatePasses) { + if (OptLevel == PassBuilder::OptimizationLevel::O0) { + for (const auto &C : PipelineStartEPCallbacks) + C(MPM); + +#if LLVM_VERSION_GE(11, 0) + for (const auto &C : OptimizerLastEPCallbacks) + C(MPM, OptLevel); +#else + if (!OptimizerLastEPCallbacks.empty()) { + FunctionPassManager FPM(DebugPassManager); + for (const auto &C : OptimizerLastEPCallbacks) + C(FPM, OptLevel); + MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM))); + } +#endif + + MPM.addPass(AlwaysInlinerPass(EmitLifetimeMarkers)); + +#if LLVM_VERSION_GE(10, 0) + if (PGOOpt) { + PB.addPGOInstrPassesForO0( + MPM, DebugPassManager, PGOOpt->Action == PGOOptions::IRInstr, + /*IsCS=*/false, PGOOpt->ProfileFile, PGOOpt->ProfileRemappingFile); + } +#endif + } else { + for (const auto &C : PipelineStartEPCallbacks) + PB.registerPipelineStartEPCallback(C); + if (OptStage != LLVMRustOptStage::PreLinkThinLTO) { + for (const auto &C : OptimizerLastEPCallbacks) + PB.registerOptimizerLastEPCallback(C); + } + + switch (OptStage) { + case LLVMRustOptStage::PreLinkNoLTO: + MPM = PB.buildPerModuleDefaultPipeline(OptLevel, DebugPassManager); + break; + case LLVMRustOptStage::PreLinkThinLTO: + MPM = PB.buildThinLTOPreLinkDefaultPipeline(OptLevel, DebugPassManager); +#if LLVM_VERSION_GE(11, 0) + for (const auto &C : OptimizerLastEPCallbacks) + C(MPM, OptLevel); +#else + if (!OptimizerLastEPCallbacks.empty()) { + FunctionPassManager FPM(DebugPassManager); + for (const auto &C : OptimizerLastEPCallbacks) + C(FPM, OptLevel); + MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM))); + } +#endif + break; + case LLVMRustOptStage::PreLinkFatLTO: + MPM = PB.buildLTOPreLinkDefaultPipeline(OptLevel, DebugPassManager); + break; + case LLVMRustOptStage::ThinLTO: + // FIXME: Does it make sense to pass the ModuleSummaryIndex? + // It only seems to be needed for C++ specific optimizations. + MPM = PB.buildThinLTODefaultPipeline(OptLevel, DebugPassManager, nullptr); + break; + case LLVMRustOptStage::FatLTO: + MPM = PB.buildLTODefaultPipeline(OptLevel, DebugPassManager, nullptr); + break; + } + } + } + + if (UseThinLTOBuffers) { + MPM.addPass(CanonicalizeAliasesPass()); + MPM.addPass(NameAnonGlobalPass()); + } + + // Upgrade all calls to old intrinsics first. + for (Module::iterator I = TheModule->begin(), E = TheModule->end(); I != E;) + UpgradeCallsToIntrinsic(&*I++); // must be post-increment, as we remove + + MPM.run(*TheModule, MAM); +#else + // The new pass manager has been available for a long time, + // but we don't bother supporting it on old LLVM versions. + report_fatal_error("New pass manager only supported since LLVM 9"); +#endif +} + +// Callback to demangle function name +// Parameters: +// * name to be demangled +// * name len +// * output buffer +// * output buffer len +// Returns len of demangled string, or 0 if demangle failed. +typedef size_t (*DemangleFn)(const char*, size_t, char*, size_t); + + +namespace { + +class RustAssemblyAnnotationWriter : public AssemblyAnnotationWriter { + DemangleFn Demangle; + std::vector Buf; + +public: + RustAssemblyAnnotationWriter(DemangleFn Demangle) : Demangle(Demangle) {} + + // Return empty string if demangle failed + // or if name does not need to be demangled + StringRef CallDemangle(StringRef name) { + if (!Demangle) { + return StringRef(); + } + + if (Buf.size() < name.size() * 2) { + // Semangled name usually shorter than mangled, + // but allocate twice as much memory just in case + Buf.resize(name.size() * 2); + } + + auto R = Demangle(name.data(), name.size(), Buf.data(), Buf.size()); + if (!R) { + // Demangle failed. + return StringRef(); + } + + auto Demangled = StringRef(Buf.data(), R); + if (Demangled == name) { + // Do not print anything if demangled name is equal to mangled. + return StringRef(); + } + + return Demangled; + } + + void emitFunctionAnnot(const Function *F, + formatted_raw_ostream &OS) override { + StringRef Demangled = CallDemangle(F->getName()); + if (Demangled.empty()) { + return; + } + + OS << "; " << Demangled << "\n"; + } + + void emitInstructionAnnot(const Instruction *I, + formatted_raw_ostream &OS) override { + const char *Name; + const Value *Value; + if (const CallInst *CI = dyn_cast(I)) { + Name = "call"; + Value = CI->getCalledOperand(); + } else if (const InvokeInst* II = dyn_cast(I)) { + Name = "invoke"; + Value = II->getCalledOperand(); + } else { + // Could demangle more operations, e. g. + // `store %place, @function`. + return; + } + + if (!Value->hasName()) { + return; + } + + StringRef Demangled = CallDemangle(Value->getName()); + if (Demangled.empty()) { + return; + } + + OS << "; " << Name << " " << Demangled << "\n"; + } +}; + +} // namespace + +extern "C" LLVMRustResult +LLVMRustPrintModule(LLVMModuleRef M, const char *Path, DemangleFn Demangle) { + std::string ErrorInfo; + std::error_code EC; + raw_fd_ostream OS(Path, EC, sys::fs::F_None); + if (EC) + ErrorInfo = EC.message(); + if (ErrorInfo != "") { + LLVMRustSetLastError(ErrorInfo.c_str()); + return LLVMRustResult::Failure; + } + + RustAssemblyAnnotationWriter AAW(Demangle); + formatted_raw_ostream FOS(OS); + unwrap(M)->print(FOS, &AAW); + + return LLVMRustResult::Success; +} + +extern "C" void LLVMRustPrintPasses() { + LLVMInitializePasses(); + struct MyListener : PassRegistrationListener { + void passEnumerate(const PassInfo *Info) { + StringRef PassArg = Info->getPassArgument(); + StringRef PassName = Info->getPassName(); + if (!PassArg.empty()) { + // These unsigned->signed casts could theoretically overflow, but + // realistically never will (and even if, the result is implementation + // defined rather plain UB). + printf("%15.*s - %.*s\n", (int)PassArg.size(), PassArg.data(), + (int)PassName.size(), PassName.data()); + } + } + } Listener; + + PassRegistry *PR = PassRegistry::getPassRegistry(); + PR->enumerateWith(&Listener); +} + +extern "C" void LLVMRustAddAlwaysInlinePass(LLVMPassManagerBuilderRef PMBR, + bool AddLifetimes) { + unwrap(PMBR)->Inliner = llvm::createAlwaysInlinerLegacyPass(AddLifetimes); +} + +extern "C" void LLVMRustRunRestrictionPass(LLVMModuleRef M, char **Symbols, + size_t Len) { + llvm::legacy::PassManager passes; + + auto PreserveFunctions = [=](const GlobalValue &GV) { + for (size_t I = 0; I < Len; I++) { + if (GV.getName() == Symbols[I]) { + return true; + } + } + return false; + }; + + passes.add(llvm::createInternalizePass(PreserveFunctions)); + + passes.run(*unwrap(M)); +} + +extern "C" void LLVMRustMarkAllFunctionsNounwind(LLVMModuleRef M) { + for (Module::iterator GV = unwrap(M)->begin(), E = unwrap(M)->end(); GV != E; + ++GV) { + GV->setDoesNotThrow(); + Function *F = dyn_cast(GV); + if (F == nullptr) + continue; + + for (Function::iterator B = F->begin(), BE = F->end(); B != BE; ++B) { + for (BasicBlock::iterator I = B->begin(), IE = B->end(); I != IE; ++I) { + if (isa(I)) { + InvokeInst *CI = cast(I); + CI->setDoesNotThrow(); + } + } + } + } +} + +extern "C" void +LLVMRustSetDataLayoutFromTargetMachine(LLVMModuleRef Module, + LLVMTargetMachineRef TMR) { + TargetMachine *Target = unwrap(TMR); + unwrap(Module)->setDataLayout(Target->createDataLayout()); +} + +extern "C" void LLVMRustSetModulePICLevel(LLVMModuleRef M) { + unwrap(M)->setPICLevel(PICLevel::Level::BigPIC); +} + +extern "C" void LLVMRustSetModulePIELevel(LLVMModuleRef M) { + unwrap(M)->setPIELevel(PIELevel::Level::Large); +} + +// Here you'll find an implementation of ThinLTO as used by the Rust compiler +// right now. This ThinLTO support is only enabled on "recent ish" versions of +// LLVM, and otherwise it's just blanket rejected from other compilers. +// +// Most of this implementation is straight copied from LLVM. At the time of +// this writing it wasn't *quite* suitable to reuse more code from upstream +// for our purposes, but we should strive to upstream this support once it's +// ready to go! I figure we may want a bit of testing locally first before +// sending this upstream to LLVM. I hear though they're quite eager to receive +// feedback like this! +// +// If you're reading this code and wondering "what in the world" or you're +// working "good lord by LLVM upgrade is *still* failing due to these bindings" +// then fear not! (ok maybe fear a little). All code here is mostly based +// on `lib/LTO/ThinLTOCodeGenerator.cpp` in LLVM. +// +// You'll find that the general layout here roughly corresponds to the `run` +// method in that file as well as `ProcessThinLTOModule`. Functions are +// specifically commented below as well, but if you're updating this code +// or otherwise trying to understand it, the LLVM source will be useful in +// interpreting the mysteries within. +// +// Otherwise I'll apologize in advance, it probably requires a relatively +// significant investment on your part to "truly understand" what's going on +// here. Not saying I do myself, but it took me awhile staring at LLVM's source +// and various online resources about ThinLTO to make heads or tails of all +// this. + +// This is a shared data structure which *must* be threadsafe to share +// read-only amongst threads. This also corresponds basically to the arguments +// of the `ProcessThinLTOModule` function in the LLVM source. +struct LLVMRustThinLTOData { + // The combined index that is the global analysis over all modules we're + // performing ThinLTO for. This is mostly managed by LLVM. + ModuleSummaryIndex Index; + + // All modules we may look at, stored as in-memory serialized versions. This + // is later used when inlining to ensure we can extract any module to inline + // from. + StringMap ModuleMap; + + // A set that we manage of everything we *don't* want internalized. Note that + // this includes all transitive references right now as well, but it may not + // always! + DenseSet GUIDPreservedSymbols; + + // Not 100% sure what these are, but they impact what's internalized and + // what's inlined across modules, I believe. + StringMap ImportLists; + StringMap ExportLists; + StringMap ModuleToDefinedGVSummaries; + + LLVMRustThinLTOData() : Index(/* HaveGVs = */ false) {} +}; + +// Just an argument to the `LLVMRustCreateThinLTOData` function below. +struct LLVMRustThinLTOModule { + const char *identifier; + const char *data; + size_t len; +}; + +// This is copied from `lib/LTO/ThinLTOCodeGenerator.cpp`, not sure what it +// does. +static const GlobalValueSummary * +getFirstDefinitionForLinker(const GlobalValueSummaryList &GVSummaryList) { + auto StrongDefForLinker = llvm::find_if( + GVSummaryList, [](const std::unique_ptr &Summary) { + auto Linkage = Summary->linkage(); + return !GlobalValue::isAvailableExternallyLinkage(Linkage) && + !GlobalValue::isWeakForLinker(Linkage); + }); + if (StrongDefForLinker != GVSummaryList.end()) + return StrongDefForLinker->get(); + + auto FirstDefForLinker = llvm::find_if( + GVSummaryList, [](const std::unique_ptr &Summary) { + auto Linkage = Summary->linkage(); + return !GlobalValue::isAvailableExternallyLinkage(Linkage); + }); + if (FirstDefForLinker == GVSummaryList.end()) + return nullptr; + return FirstDefForLinker->get(); +} + +// The main entry point for creating the global ThinLTO analysis. The structure +// here is basically the same as before threads are spawned in the `run` +// function of `lib/LTO/ThinLTOCodeGenerator.cpp`. +extern "C" LLVMRustThinLTOData* +LLVMRustCreateThinLTOData(LLVMRustThinLTOModule *modules, + int num_modules, + const char **preserved_symbols, + int num_symbols) { +#if LLVM_VERSION_GE(10, 0) + auto Ret = std::make_unique(); +#else + auto Ret = llvm::make_unique(); +#endif + + // Load each module's summary and merge it into one combined index + for (int i = 0; i < num_modules; i++) { + auto module = &modules[i]; + StringRef buffer(module->data, module->len); + MemoryBufferRef mem_buffer(buffer, module->identifier); + + Ret->ModuleMap[module->identifier] = mem_buffer; + + if (Error Err = readModuleSummaryIndex(mem_buffer, Ret->Index, i)) { + LLVMRustSetLastError(toString(std::move(Err)).c_str()); + return nullptr; + } + } + + // Collect for each module the list of function it defines (GUID -> Summary) + Ret->Index.collectDefinedGVSummariesPerModule(Ret->ModuleToDefinedGVSummaries); + + // Convert the preserved symbols set from string to GUID, this is then needed + // for internalization. + for (int i = 0; i < num_symbols; i++) { + auto GUID = GlobalValue::getGUID(preserved_symbols[i]); + Ret->GUIDPreservedSymbols.insert(GUID); + } + + // Collect the import/export lists for all modules from the call-graph in the + // combined index + // + // This is copied from `lib/LTO/ThinLTOCodeGenerator.cpp` + auto deadIsPrevailing = [&](GlobalValue::GUID G) { + return PrevailingType::Unknown; + }; + // We don't have a complete picture in our use of ThinLTO, just our immediate + // crate, so we need `ImportEnabled = false` to limit internalization. + // Otherwise, we sometimes lose `static` values -- see #60184. + computeDeadSymbolsWithConstProp(Ret->Index, Ret->GUIDPreservedSymbols, + deadIsPrevailing, /* ImportEnabled = */ false); + ComputeCrossModuleImport( + Ret->Index, + Ret->ModuleToDefinedGVSummaries, + Ret->ImportLists, + Ret->ExportLists + ); + + // Resolve LinkOnce/Weak symbols, this has to be computed early be cause it + // impacts the caching. + // + // This is copied from `lib/LTO/ThinLTOCodeGenerator.cpp` with some of this + // being lifted from `lib/LTO/LTO.cpp` as well + StringMap> ResolvedODR; + DenseMap PrevailingCopy; + for (auto &I : Ret->Index) { + if (I.second.SummaryList.size() > 1) + PrevailingCopy[I.first] = getFirstDefinitionForLinker(I.second.SummaryList); + } + auto isPrevailing = [&](GlobalValue::GUID GUID, const GlobalValueSummary *S) { + const auto &Prevailing = PrevailingCopy.find(GUID); + if (Prevailing == PrevailingCopy.end()) + return true; + return Prevailing->second == S; + }; + auto recordNewLinkage = [&](StringRef ModuleIdentifier, + GlobalValue::GUID GUID, + GlobalValue::LinkageTypes NewLinkage) { + ResolvedODR[ModuleIdentifier][GUID] = NewLinkage; + }; +#if LLVM_VERSION_GE(9, 0) + thinLTOResolvePrevailingInIndex(Ret->Index, isPrevailing, recordNewLinkage, + Ret->GUIDPreservedSymbols); +#else + thinLTOResolvePrevailingInIndex(Ret->Index, isPrevailing, recordNewLinkage); +#endif + + // Here we calculate an `ExportedGUIDs` set for use in the `isExported` + // callback below. This callback below will dictate the linkage for all + // summaries in the index, and we basically just only want to ensure that dead + // symbols are internalized. Otherwise everything that's already external + // linkage will stay as external, and internal will stay as internal. + std::set ExportedGUIDs; + for (auto &List : Ret->Index) { + for (auto &GVS: List.second.SummaryList) { + if (GlobalValue::isLocalLinkage(GVS->linkage())) + continue; + auto GUID = GVS->getOriginalName(); + if (GVS->flags().Live) + ExportedGUIDs.insert(GUID); + } + } +#if LLVM_VERSION_GE(10, 0) + auto isExported = [&](StringRef ModuleIdentifier, ValueInfo VI) { + const auto &ExportList = Ret->ExportLists.find(ModuleIdentifier); + return (ExportList != Ret->ExportLists.end() && + ExportList->second.count(VI)) || + ExportedGUIDs.count(VI.getGUID()); + }; + thinLTOInternalizeAndPromoteInIndex(Ret->Index, isExported, isPrevailing); +#else + auto isExported = [&](StringRef ModuleIdentifier, GlobalValue::GUID GUID) { + const auto &ExportList = Ret->ExportLists.find(ModuleIdentifier); + return (ExportList != Ret->ExportLists.end() && + ExportList->second.count(GUID)) || + ExportedGUIDs.count(GUID); + }; + thinLTOInternalizeAndPromoteInIndex(Ret->Index, isExported); +#endif + + return Ret.release(); +} + +extern "C" void +LLVMRustFreeThinLTOData(LLVMRustThinLTOData *Data) { + delete Data; +} + +// Below are the various passes that happen *per module* when doing ThinLTO. +// +// In other words, these are the functions that are all run concurrently +// with one another, one per module. The passes here correspond to the analysis +// passes in `lib/LTO/ThinLTOCodeGenerator.cpp`, currently found in the +// `ProcessThinLTOModule` function. Here they're split up into separate steps +// so rustc can save off the intermediate bytecode between each step. + +#if LLVM_VERSION_GE(11, 0) +static bool +clearDSOLocalOnDeclarations(Module &Mod, TargetMachine &TM) { + // When linking an ELF shared object, dso_local should be dropped. We + // conservatively do this for -fpic. + bool ClearDSOLocalOnDeclarations = + TM.getTargetTriple().isOSBinFormatELF() && + TM.getRelocationModel() != Reloc::Static && + Mod.getPIELevel() == PIELevel::Default; + return ClearDSOLocalOnDeclarations; +} +#endif + +extern "C" bool +LLVMRustPrepareThinLTORename(const LLVMRustThinLTOData *Data, LLVMModuleRef M, + LLVMTargetMachineRef TM) { + Module &Mod = *unwrap(M); + TargetMachine &Target = *unwrap(TM); + +#if LLVM_VERSION_GE(11, 0) + bool ClearDSOLocal = clearDSOLocalOnDeclarations(Mod, Target); + bool error = renameModuleForThinLTO(Mod, Data->Index, ClearDSOLocal); +#else + bool error = renameModuleForThinLTO(Mod, Data->Index); +#endif + + if (error) { + LLVMRustSetLastError("renameModuleForThinLTO failed"); + return false; + } + return true; +} + +extern "C" bool +LLVMRustPrepareThinLTOResolveWeak(const LLVMRustThinLTOData *Data, LLVMModuleRef M) { + Module &Mod = *unwrap(M); + const auto &DefinedGlobals = Data->ModuleToDefinedGVSummaries.lookup(Mod.getModuleIdentifier()); + thinLTOResolvePrevailingInModule(Mod, DefinedGlobals); + return true; +} + +extern "C" bool +LLVMRustPrepareThinLTOInternalize(const LLVMRustThinLTOData *Data, LLVMModuleRef M) { + Module &Mod = *unwrap(M); + const auto &DefinedGlobals = Data->ModuleToDefinedGVSummaries.lookup(Mod.getModuleIdentifier()); + thinLTOInternalizeModule(Mod, DefinedGlobals); + return true; +} + +extern "C" bool +LLVMRustPrepareThinLTOImport(const LLVMRustThinLTOData *Data, LLVMModuleRef M, + LLVMTargetMachineRef TM) { + Module &Mod = *unwrap(M); + TargetMachine &Target = *unwrap(TM); + + const auto &ImportList = Data->ImportLists.lookup(Mod.getModuleIdentifier()); + auto Loader = [&](StringRef Identifier) { + const auto &Memory = Data->ModuleMap.lookup(Identifier); + auto &Context = Mod.getContext(); + auto MOrErr = getLazyBitcodeModule(Memory, Context, true, true); + + if (!MOrErr) + return MOrErr; + + // The rest of this closure is a workaround for + // https://bugs.llvm.org/show_bug.cgi?id=38184 where during ThinLTO imports + // we accidentally import wasm custom sections into different modules, + // duplicating them by in the final output artifact. + // + // The issue is worked around here by manually removing the + // `wasm.custom_sections` named metadata node from any imported module. This + // we know isn't used by any optimization pass so there's no need for it to + // be imported. + // + // Note that the metadata is currently lazily loaded, so we materialize it + // here before looking up if there's metadata inside. The `FunctionImporter` + // will immediately materialize metadata anyway after an import, so this + // shouldn't be a perf hit. + if (Error Err = (*MOrErr)->materializeMetadata()) { + Expected> Ret(std::move(Err)); + return Ret; + } + + auto *WasmCustomSections = (*MOrErr)->getNamedMetadata("wasm.custom_sections"); + if (WasmCustomSections) + WasmCustomSections->eraseFromParent(); + + return MOrErr; + }; +#if LLVM_VERSION_GE(11, 0) + bool ClearDSOLocal = clearDSOLocalOnDeclarations(Mod, Target); + FunctionImporter Importer(Data->Index, Loader, ClearDSOLocal); +#else + FunctionImporter Importer(Data->Index, Loader); +#endif + Expected Result = Importer.importFunctions(Mod, ImportList); + if (!Result) { + LLVMRustSetLastError(toString(Result.takeError()).c_str()); + return false; + } + return true; +} + +extern "C" typedef void (*LLVMRustModuleNameCallback)(void*, // payload + const char*, // importing module name + const char*); // imported module name + +// Calls `module_name_callback` for each module import done by ThinLTO. +// The callback is provided with regular null-terminated C strings. +extern "C" void +LLVMRustGetThinLTOModuleImports(const LLVMRustThinLTOData *data, + LLVMRustModuleNameCallback module_name_callback, + void* callback_payload) { + for (const auto& importing_module : data->ImportLists) { + const std::string importing_module_id = importing_module.getKey().str(); + const auto& imports = importing_module.getValue(); + for (const auto& imported_module : imports) { + const std::string imported_module_id = imported_module.getKey().str(); + module_name_callback(callback_payload, + importing_module_id.c_str(), + imported_module_id.c_str()); + } + } +} + +// This struct and various functions are sort of a hack right now, but the +// problem is that we've got in-memory LLVM modules after we generate and +// optimize all codegen-units for one compilation in rustc. To be compatible +// with the LTO support above we need to serialize the modules plus their +// ThinLTO summary into memory. +// +// This structure is basically an owned version of a serialize module, with +// a ThinLTO summary attached. +struct LLVMRustThinLTOBuffer { + std::string data; +}; + +extern "C" LLVMRustThinLTOBuffer* +LLVMRustThinLTOBufferCreate(LLVMModuleRef M) { +#if LLVM_VERSION_GE(10, 0) + auto Ret = std::make_unique(); +#else + auto Ret = llvm::make_unique(); +#endif + { + raw_string_ostream OS(Ret->data); + { + legacy::PassManager PM; + PM.add(createWriteThinLTOBitcodePass(OS)); + PM.run(*unwrap(M)); + } + } + return Ret.release(); +} + +extern "C" void +LLVMRustThinLTOBufferFree(LLVMRustThinLTOBuffer *Buffer) { + delete Buffer; +} + +extern "C" const void* +LLVMRustThinLTOBufferPtr(const LLVMRustThinLTOBuffer *Buffer) { + return Buffer->data.data(); +} + +extern "C" size_t +LLVMRustThinLTOBufferLen(const LLVMRustThinLTOBuffer *Buffer) { + return Buffer->data.length(); +} + +// This is what we used to parse upstream bitcode for actual ThinLTO +// processing. We'll call this once per module optimized through ThinLTO, and +// it'll be called concurrently on many threads. +extern "C" LLVMModuleRef +LLVMRustParseBitcodeForLTO(LLVMContextRef Context, + const char *data, + size_t len, + const char *identifier) { + StringRef Data(data, len); + MemoryBufferRef Buffer(Data, identifier); + unwrap(Context)->enableDebugTypeODRUniquing(); + Expected> SrcOrError = + parseBitcodeFile(Buffer, *unwrap(Context)); + if (!SrcOrError) { + LLVMRustSetLastError(toString(SrcOrError.takeError()).c_str()); + return nullptr; + } + return wrap(std::move(*SrcOrError).release()); +} + +// Find the bitcode section in the object file data and return it as a slice. +// Fail if the bitcode section is present but empty. +// +// On success, the return value is the pointer to the start of the slice and +// `out_len` is filled with the (non-zero) length. On failure, the return value +// is `nullptr` and `out_len` is set to zero. +extern "C" const char* +LLVMRustGetBitcodeSliceFromObjectData(const char *data, + size_t len, + size_t *out_len) { + *out_len = 0; + + StringRef Data(data, len); + MemoryBufferRef Buffer(Data, ""); // The id is unused. + + Expected BitcodeOrError = + object::IRObjectFile::findBitcodeInMemBuffer(Buffer); + if (!BitcodeOrError) { + LLVMRustSetLastError(toString(BitcodeOrError.takeError()).c_str()); + return nullptr; + } + + *out_len = BitcodeOrError->getBufferSize(); + return BitcodeOrError->getBufferStart(); +} + +// Rewrite all `DICompileUnit` pointers to the `DICompileUnit` specified. See +// the comment in `back/lto.rs` for why this exists. +extern "C" void +LLVMRustThinLTOGetDICompileUnit(LLVMModuleRef Mod, + DICompileUnit **A, + DICompileUnit **B) { + Module *M = unwrap(Mod); + DICompileUnit **Cur = A; + DICompileUnit **Next = B; + for (DICompileUnit *CU : M->debug_compile_units()) { + *Cur = CU; + Cur = Next; + Next = nullptr; + if (Cur == nullptr) + break; + } +} + +// Rewrite all `DICompileUnit` pointers to the `DICompileUnit` specified. See +// the comment in `back/lto.rs` for why this exists. +extern "C" void +LLVMRustThinLTOPatchDICompileUnit(LLVMModuleRef Mod, DICompileUnit *Unit) { + Module *M = unwrap(Mod); + + // If the original source module didn't have a `DICompileUnit` then try to + // merge all the existing compile units. If there aren't actually any though + // then there's not much for us to do so return. + if (Unit == nullptr) { + for (DICompileUnit *CU : M->debug_compile_units()) { + Unit = CU; + break; + } + if (Unit == nullptr) + return; + } + + // Use LLVM's built-in `DebugInfoFinder` to find a bunch of debuginfo and + // process it recursively. Note that we used to specifically iterate over + // instructions to ensure we feed everything into it, but `processModule` + // started doing this the same way in LLVM 7 (commit d769eb36ab2b8). + DebugInfoFinder Finder; + Finder.processModule(*M); + + // After we've found all our debuginfo, rewrite all subprograms to point to + // the same `DICompileUnit`. + for (auto &F : Finder.subprograms()) { + F->replaceUnit(Unit); + } + + // Erase any other references to other `DICompileUnit` instances, the verifier + // will later ensure that we don't actually have any other stale references to + // worry about. + auto *MD = M->getNamedMetadata("llvm.dbg.cu"); + MD->clearOperands(); + MD->addOperand(Unit); +} diff --git a/compiler/rustc_llvm/llvm-wrapper/README b/compiler/rustc_llvm/llvm-wrapper/README new file mode 100644 index 00000000000..e1c6dd07d2b --- /dev/null +++ b/compiler/rustc_llvm/llvm-wrapper/README @@ -0,0 +1,16 @@ +This directory currently contains some LLVM support code. This will generally +be sent upstream to LLVM in time; for now it lives here. + +NOTE: the LLVM C++ ABI is subject to between-version breakage and must *never* +be exposed to Rust. To allow for easy auditing of that, all Rust-exposed types +must be typedef-ed as "LLVMXyz", or "LLVMRustXyz" if they were defined here. + +Functions that return a failure status and leave the error in +the LLVM last error should return an LLVMRustResult rather than an +int or anything to avoid confusion. + +When translating enums, add a single `Other` variant as the first +one to allow for new variants to be added. It should abort when used +as an input. + +All other types must not be typedef-ed as such. diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp new file mode 100644 index 00000000000..e85a9b76380 --- /dev/null +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -0,0 +1,1721 @@ +#include "LLVMWrapper.h" +#include "llvm/IR/DebugInfoMetadata.h" +#include "llvm/IR/DiagnosticInfo.h" +#include "llvm/IR/DiagnosticPrinter.h" +#include "llvm/IR/GlobalVariable.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Intrinsics.h" +#include "llvm/Object/Archive.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/Bitcode/BitcodeWriterPass.h" +#include "llvm/Support/Signals.h" +#include "llvm/ADT/Optional.h" + +#include + +//===----------------------------------------------------------------------=== +// +// This file defines alternate interfaces to core functions that are more +// readily callable by Rust's FFI. +// +//===----------------------------------------------------------------------=== + +using namespace llvm; +using namespace llvm::sys; +using namespace llvm::object; + +// LLVMAtomicOrdering is already an enum - don't create another +// one. +static AtomicOrdering fromRust(LLVMAtomicOrdering Ordering) { + switch (Ordering) { + case LLVMAtomicOrderingNotAtomic: + return AtomicOrdering::NotAtomic; + case LLVMAtomicOrderingUnordered: + return AtomicOrdering::Unordered; + case LLVMAtomicOrderingMonotonic: + return AtomicOrdering::Monotonic; + case LLVMAtomicOrderingAcquire: + return AtomicOrdering::Acquire; + case LLVMAtomicOrderingRelease: + return AtomicOrdering::Release; + case LLVMAtomicOrderingAcquireRelease: + return AtomicOrdering::AcquireRelease; + case LLVMAtomicOrderingSequentiallyConsistent: + return AtomicOrdering::SequentiallyConsistent; + } + + report_fatal_error("Invalid LLVMAtomicOrdering value!"); +} + +static LLVM_THREAD_LOCAL char *LastError; + +// Custom error handler for fatal LLVM errors. +// +// Notably it exits the process with code 101, unlike LLVM's default of 1. +static void FatalErrorHandler(void *UserData, + const std::string& Reason, + bool GenCrashDiag) { + // Do the same thing that the default error handler does. + std::cerr << "LLVM ERROR: " << Reason << std::endl; + + // Since this error handler exits the process, we have to run any cleanup that + // LLVM would run after handling the error. This might change with an LLVM + // upgrade. + sys::RunInterruptHandlers(); + + exit(101); +} + +extern "C" void LLVMRustInstallFatalErrorHandler() { + install_fatal_error_handler(FatalErrorHandler); +} + +extern "C" LLVMMemoryBufferRef +LLVMRustCreateMemoryBufferWithContentsOfFile(const char *Path) { + ErrorOr> BufOr = + MemoryBuffer::getFile(Path, -1, false); + if (!BufOr) { + LLVMRustSetLastError(BufOr.getError().message().c_str()); + return nullptr; + } + return wrap(BufOr.get().release()); +} + +extern "C" char *LLVMRustGetLastError(void) { + char *Ret = LastError; + LastError = nullptr; + return Ret; +} + +extern "C" unsigned int LLVMRustGetInstructionCount(LLVMModuleRef M) { + return unwrap(M)->getInstructionCount(); +} + +extern "C" void LLVMRustSetLastError(const char *Err) { + free((void *)LastError); + LastError = strdup(Err); +} + +extern "C" LLVMContextRef LLVMRustContextCreate(bool shouldDiscardNames) { + auto ctx = new LLVMContext(); + ctx->setDiscardValueNames(shouldDiscardNames); + return wrap(ctx); +} + +extern "C" void LLVMRustSetNormalizedTarget(LLVMModuleRef M, + const char *Triple) { + unwrap(M)->setTargetTriple(Triple::normalize(Triple)); +} + +extern "C" void LLVMRustPrintPassTimings() { + raw_fd_ostream OS(2, false); // stderr. + TimerGroup::printAll(OS); +} + +extern "C" LLVMValueRef LLVMRustGetNamedValue(LLVMModuleRef M, const char *Name, + size_t NameLen) { + return wrap(unwrap(M)->getNamedValue(StringRef(Name, NameLen))); +} + +extern "C" LLVMValueRef LLVMRustGetOrInsertFunction(LLVMModuleRef M, + const char *Name, + size_t NameLen, + LLVMTypeRef FunctionTy) { + return wrap(unwrap(M) + ->getOrInsertFunction(StringRef(Name, NameLen), + unwrap(FunctionTy)) +#if LLVM_VERSION_GE(9, 0) + .getCallee() +#endif + ); +} + +extern "C" LLVMValueRef +LLVMRustGetOrInsertGlobal(LLVMModuleRef M, const char *Name, size_t NameLen, LLVMTypeRef Ty) { + StringRef NameRef(Name, NameLen); + return wrap(unwrap(M)->getOrInsertGlobal(NameRef, unwrap(Ty))); +} + +extern "C" LLVMValueRef +LLVMRustInsertPrivateGlobal(LLVMModuleRef M, LLVMTypeRef Ty) { + return wrap(new GlobalVariable(*unwrap(M), + unwrap(Ty), + false, + GlobalValue::PrivateLinkage, + nullptr)); +} + +extern "C" LLVMTypeRef LLVMRustMetadataTypeInContext(LLVMContextRef C) { + return wrap(Type::getMetadataTy(*unwrap(C))); +} + +static Attribute::AttrKind fromRust(LLVMRustAttribute Kind) { + switch (Kind) { + case AlwaysInline: + return Attribute::AlwaysInline; + case ByVal: + return Attribute::ByVal; + case Cold: + return Attribute::Cold; + case InlineHint: + return Attribute::InlineHint; + case MinSize: + return Attribute::MinSize; + case Naked: + return Attribute::Naked; + case NoAlias: + return Attribute::NoAlias; + case NoCapture: + return Attribute::NoCapture; + case NoInline: + return Attribute::NoInline; + case NonNull: + return Attribute::NonNull; + case NoRedZone: + return Attribute::NoRedZone; + case NoReturn: + return Attribute::NoReturn; + case NoUnwind: + return Attribute::NoUnwind; + case OptimizeForSize: + return Attribute::OptimizeForSize; + case ReadOnly: + return Attribute::ReadOnly; + case SExt: + return Attribute::SExt; + case StructRet: + return Attribute::StructRet; + case UWTable: + return Attribute::UWTable; + case ZExt: + return Attribute::ZExt; + case InReg: + return Attribute::InReg; + case SanitizeThread: + return Attribute::SanitizeThread; + case SanitizeAddress: + return Attribute::SanitizeAddress; + case SanitizeMemory: + return Attribute::SanitizeMemory; + case NonLazyBind: + return Attribute::NonLazyBind; + case OptimizeNone: + return Attribute::OptimizeNone; + case ReturnsTwice: + return Attribute::ReturnsTwice; + case ReadNone: + return Attribute::ReadNone; + case InaccessibleMemOnly: + return Attribute::InaccessibleMemOnly; + } + report_fatal_error("bad AttributeKind"); +} + +extern "C" void LLVMRustAddCallSiteAttribute(LLVMValueRef Instr, unsigned Index, + LLVMRustAttribute RustAttr) { + CallBase *Call = unwrap(Instr); + Attribute Attr = Attribute::get(Call->getContext(), fromRust(RustAttr)); + Call->addAttribute(Index, Attr); +} + +extern "C" void LLVMRustAddAlignmentCallSiteAttr(LLVMValueRef Instr, + unsigned Index, + uint32_t Bytes) { + CallBase *Call = unwrap(Instr); + AttrBuilder B; + B.addAlignmentAttr(Bytes); + Call->setAttributes(Call->getAttributes().addAttributes( + Call->getContext(), Index, B)); +} + +extern "C" void LLVMRustAddDereferenceableCallSiteAttr(LLVMValueRef Instr, + unsigned Index, + uint64_t Bytes) { + CallBase *Call = unwrap(Instr); + AttrBuilder B; + B.addDereferenceableAttr(Bytes); + Call->setAttributes(Call->getAttributes().addAttributes( + Call->getContext(), Index, B)); +} + +extern "C" void LLVMRustAddDereferenceableOrNullCallSiteAttr(LLVMValueRef Instr, + unsigned Index, + uint64_t Bytes) { + CallBase *Call = unwrap(Instr); + AttrBuilder B; + B.addDereferenceableOrNullAttr(Bytes); + Call->setAttributes(Call->getAttributes().addAttributes( + Call->getContext(), Index, B)); +} + +extern "C" void LLVMRustAddByValCallSiteAttr(LLVMValueRef Instr, unsigned Index, + LLVMTypeRef Ty) { + CallBase *Call = unwrap(Instr); +#if LLVM_VERSION_GE(9, 0) + Attribute Attr = Attribute::getWithByValType(Call->getContext(), unwrap(Ty)); +#else + Attribute Attr = Attribute::get(Call->getContext(), Attribute::ByVal); +#endif + Call->addAttribute(Index, Attr); +} + +extern "C" void LLVMRustAddFunctionAttribute(LLVMValueRef Fn, unsigned Index, + LLVMRustAttribute RustAttr) { + Function *A = unwrap(Fn); + Attribute Attr = Attribute::get(A->getContext(), fromRust(RustAttr)); + AttrBuilder B(Attr); + A->addAttributes(Index, B); +} + +extern "C" void LLVMRustAddAlignmentAttr(LLVMValueRef Fn, + unsigned Index, + uint32_t Bytes) { + Function *A = unwrap(Fn); + AttrBuilder B; + B.addAlignmentAttr(Bytes); + A->addAttributes(Index, B); +} + +extern "C" void LLVMRustAddDereferenceableAttr(LLVMValueRef Fn, unsigned Index, + uint64_t Bytes) { + Function *A = unwrap(Fn); + AttrBuilder B; + B.addDereferenceableAttr(Bytes); + A->addAttributes(Index, B); +} + +extern "C" void LLVMRustAddDereferenceableOrNullAttr(LLVMValueRef Fn, + unsigned Index, + uint64_t Bytes) { + Function *A = unwrap(Fn); + AttrBuilder B; + B.addDereferenceableOrNullAttr(Bytes); + A->addAttributes(Index, B); +} + +extern "C" void LLVMRustAddByValAttr(LLVMValueRef Fn, unsigned Index, + LLVMTypeRef Ty) { + Function *F = unwrap(Fn); +#if LLVM_VERSION_GE(9, 0) + Attribute Attr = Attribute::getWithByValType(F->getContext(), unwrap(Ty)); +#else + Attribute Attr = Attribute::get(F->getContext(), Attribute::ByVal); +#endif + F->addAttribute(Index, Attr); +} + +extern "C" void LLVMRustAddFunctionAttrStringValue(LLVMValueRef Fn, + unsigned Index, + const char *Name, + const char *Value) { + Function *F = unwrap(Fn); + AttrBuilder B; + B.addAttribute(Name, Value); + F->addAttributes(Index, B); +} + +extern "C" void LLVMRustRemoveFunctionAttributes(LLVMValueRef Fn, + unsigned Index, + LLVMRustAttribute RustAttr) { + Function *F = unwrap(Fn); + Attribute Attr = Attribute::get(F->getContext(), fromRust(RustAttr)); + AttrBuilder B(Attr); + auto PAL = F->getAttributes(); + auto PALNew = PAL.removeAttributes(F->getContext(), Index, B); + F->setAttributes(PALNew); +} + +// enable fpmath flag UnsafeAlgebra +extern "C" void LLVMRustSetHasUnsafeAlgebra(LLVMValueRef V) { + if (auto I = dyn_cast(unwrap(V))) { + I->setFast(true); + } +} + +extern "C" LLVMValueRef +LLVMRustBuildAtomicLoad(LLVMBuilderRef B, LLVMValueRef Source, const char *Name, + LLVMAtomicOrdering Order) { + Value *Ptr = unwrap(Source); + Type *Ty = Ptr->getType()->getPointerElementType(); + LoadInst *LI = unwrap(B)->CreateLoad(Ty, Ptr, Name); + LI->setAtomic(fromRust(Order)); + return wrap(LI); +} + +extern "C" LLVMValueRef LLVMRustBuildAtomicStore(LLVMBuilderRef B, + LLVMValueRef V, + LLVMValueRef Target, + LLVMAtomicOrdering Order) { + StoreInst *SI = unwrap(B)->CreateStore(unwrap(V), unwrap(Target)); + SI->setAtomic(fromRust(Order)); + return wrap(SI); +} + +// FIXME: Use the C-API LLVMBuildAtomicCmpXchg and LLVMSetWeak +// once we raise our minimum support to LLVM 10. +extern "C" LLVMValueRef +LLVMRustBuildAtomicCmpXchg(LLVMBuilderRef B, LLVMValueRef Target, + LLVMValueRef Old, LLVMValueRef Source, + LLVMAtomicOrdering Order, + LLVMAtomicOrdering FailureOrder, LLVMBool Weak) { + AtomicCmpXchgInst *ACXI = unwrap(B)->CreateAtomicCmpXchg( + unwrap(Target), unwrap(Old), unwrap(Source), fromRust(Order), + fromRust(FailureOrder)); + ACXI->setWeak(Weak); + return wrap(ACXI); +} + +enum class LLVMRustSynchronizationScope { + SingleThread, + CrossThread, +}; + +static SyncScope::ID fromRust(LLVMRustSynchronizationScope Scope) { + switch (Scope) { + case LLVMRustSynchronizationScope::SingleThread: + return SyncScope::SingleThread; + case LLVMRustSynchronizationScope::CrossThread: + return SyncScope::System; + default: + report_fatal_error("bad SynchronizationScope."); + } +} + +extern "C" LLVMValueRef +LLVMRustBuildAtomicFence(LLVMBuilderRef B, LLVMAtomicOrdering Order, + LLVMRustSynchronizationScope Scope) { + return wrap(unwrap(B)->CreateFence(fromRust(Order), fromRust(Scope))); +} + +enum class LLVMRustAsmDialect { + Att, + Intel, +}; + +static InlineAsm::AsmDialect fromRust(LLVMRustAsmDialect Dialect) { + switch (Dialect) { + case LLVMRustAsmDialect::Att: + return InlineAsm::AD_ATT; + case LLVMRustAsmDialect::Intel: + return InlineAsm::AD_Intel; + default: + report_fatal_error("bad AsmDialect."); + } +} + +extern "C" LLVMValueRef +LLVMRustInlineAsm(LLVMTypeRef Ty, char *AsmString, size_t AsmStringLen, + char *Constraints, size_t ConstraintsLen, + LLVMBool HasSideEffects, LLVMBool IsAlignStack, + LLVMRustAsmDialect Dialect) { + return wrap(InlineAsm::get(unwrap(Ty), + StringRef(AsmString, AsmStringLen), + StringRef(Constraints, ConstraintsLen), + HasSideEffects, IsAlignStack, fromRust(Dialect))); +} + +extern "C" bool LLVMRustInlineAsmVerify(LLVMTypeRef Ty, char *Constraints, + size_t ConstraintsLen) { + return InlineAsm::Verify(unwrap(Ty), + StringRef(Constraints, ConstraintsLen)); +} + +extern "C" void LLVMRustAppendModuleInlineAsm(LLVMModuleRef M, const char *Asm, + size_t AsmLen) { + unwrap(M)->appendModuleInlineAsm(StringRef(Asm, AsmLen)); +} + +typedef DIBuilder *LLVMRustDIBuilderRef; + +template DIT *unwrapDIPtr(LLVMMetadataRef Ref) { + return (DIT *)(Ref ? unwrap(Ref) : nullptr); +} + +#define DIDescriptor DIScope +#define DIArray DINodeArray +#define unwrapDI unwrapDIPtr + +// These values **must** match debuginfo::DIFlags! They also *happen* +// to match LLVM, but that isn't required as we do giant sets of +// matching below. The value shouldn't be directly passed to LLVM. +enum class LLVMRustDIFlags : uint32_t { + FlagZero = 0, + FlagPrivate = 1, + FlagProtected = 2, + FlagPublic = 3, + FlagFwdDecl = (1 << 2), + FlagAppleBlock = (1 << 3), + FlagBlockByrefStruct = (1 << 4), + FlagVirtual = (1 << 5), + FlagArtificial = (1 << 6), + FlagExplicit = (1 << 7), + FlagPrototyped = (1 << 8), + FlagObjcClassComplete = (1 << 9), + FlagObjectPointer = (1 << 10), + FlagVector = (1 << 11), + FlagStaticMember = (1 << 12), + FlagLValueReference = (1 << 13), + FlagRValueReference = (1 << 14), + FlagExternalTypeRef = (1 << 15), + FlagIntroducedVirtual = (1 << 18), + FlagBitField = (1 << 19), + FlagNoReturn = (1 << 20), + // Do not add values that are not supported by the minimum LLVM + // version we support! see llvm/include/llvm/IR/DebugInfoFlags.def +}; + +inline LLVMRustDIFlags operator&(LLVMRustDIFlags A, LLVMRustDIFlags B) { + return static_cast(static_cast(A) & + static_cast(B)); +} + +inline LLVMRustDIFlags operator|(LLVMRustDIFlags A, LLVMRustDIFlags B) { + return static_cast(static_cast(A) | + static_cast(B)); +} + +inline LLVMRustDIFlags &operator|=(LLVMRustDIFlags &A, LLVMRustDIFlags B) { + return A = A | B; +} + +inline bool isSet(LLVMRustDIFlags F) { return F != LLVMRustDIFlags::FlagZero; } + +inline LLVMRustDIFlags visibility(LLVMRustDIFlags F) { + return static_cast(static_cast(F) & 0x3); +} + +static DINode::DIFlags fromRust(LLVMRustDIFlags Flags) { + DINode::DIFlags Result = DINode::DIFlags::FlagZero; + + switch (visibility(Flags)) { + case LLVMRustDIFlags::FlagPrivate: + Result |= DINode::DIFlags::FlagPrivate; + break; + case LLVMRustDIFlags::FlagProtected: + Result |= DINode::DIFlags::FlagProtected; + break; + case LLVMRustDIFlags::FlagPublic: + Result |= DINode::DIFlags::FlagPublic; + break; + default: + // The rest are handled below + break; + } + + if (isSet(Flags & LLVMRustDIFlags::FlagFwdDecl)) { + Result |= DINode::DIFlags::FlagFwdDecl; + } + if (isSet(Flags & LLVMRustDIFlags::FlagAppleBlock)) { + Result |= DINode::DIFlags::FlagAppleBlock; + } +#if LLVM_VERSION_LT(10, 0) + if (isSet(Flags & LLVMRustDIFlags::FlagBlockByrefStruct)) { + Result |= DINode::DIFlags::FlagBlockByrefStruct; + } +#endif + if (isSet(Flags & LLVMRustDIFlags::FlagVirtual)) { + Result |= DINode::DIFlags::FlagVirtual; + } + if (isSet(Flags & LLVMRustDIFlags::FlagArtificial)) { + Result |= DINode::DIFlags::FlagArtificial; + } + if (isSet(Flags & LLVMRustDIFlags::FlagExplicit)) { + Result |= DINode::DIFlags::FlagExplicit; + } + if (isSet(Flags & LLVMRustDIFlags::FlagPrototyped)) { + Result |= DINode::DIFlags::FlagPrototyped; + } + if (isSet(Flags & LLVMRustDIFlags::FlagObjcClassComplete)) { + Result |= DINode::DIFlags::FlagObjcClassComplete; + } + if (isSet(Flags & LLVMRustDIFlags::FlagObjectPointer)) { + Result |= DINode::DIFlags::FlagObjectPointer; + } + if (isSet(Flags & LLVMRustDIFlags::FlagVector)) { + Result |= DINode::DIFlags::FlagVector; + } + if (isSet(Flags & LLVMRustDIFlags::FlagStaticMember)) { + Result |= DINode::DIFlags::FlagStaticMember; + } + if (isSet(Flags & LLVMRustDIFlags::FlagLValueReference)) { + Result |= DINode::DIFlags::FlagLValueReference; + } + if (isSet(Flags & LLVMRustDIFlags::FlagRValueReference)) { + Result |= DINode::DIFlags::FlagRValueReference; + } + if (isSet(Flags & LLVMRustDIFlags::FlagIntroducedVirtual)) { + Result |= DINode::DIFlags::FlagIntroducedVirtual; + } + if (isSet(Flags & LLVMRustDIFlags::FlagBitField)) { + Result |= DINode::DIFlags::FlagBitField; + } + if (isSet(Flags & LLVMRustDIFlags::FlagNoReturn)) { + Result |= DINode::DIFlags::FlagNoReturn; + } + + return Result; +} + +// These values **must** match debuginfo::DISPFlags! They also *happen* +// to match LLVM, but that isn't required as we do giant sets of +// matching below. The value shouldn't be directly passed to LLVM. +enum class LLVMRustDISPFlags : uint32_t { + SPFlagZero = 0, + SPFlagVirtual = 1, + SPFlagPureVirtual = 2, + SPFlagLocalToUnit = (1 << 2), + SPFlagDefinition = (1 << 3), + SPFlagOptimized = (1 << 4), + SPFlagMainSubprogram = (1 << 5), + // Do not add values that are not supported by the minimum LLVM + // version we support! see llvm/include/llvm/IR/DebugInfoFlags.def + // (In LLVM < 8, createFunction supported these as separate bool arguments.) +}; + +inline LLVMRustDISPFlags operator&(LLVMRustDISPFlags A, LLVMRustDISPFlags B) { + return static_cast(static_cast(A) & + static_cast(B)); +} + +inline LLVMRustDISPFlags operator|(LLVMRustDISPFlags A, LLVMRustDISPFlags B) { + return static_cast(static_cast(A) | + static_cast(B)); +} + +inline LLVMRustDISPFlags &operator|=(LLVMRustDISPFlags &A, LLVMRustDISPFlags B) { + return A = A | B; +} + +inline bool isSet(LLVMRustDISPFlags F) { return F != LLVMRustDISPFlags::SPFlagZero; } + +inline LLVMRustDISPFlags virtuality(LLVMRustDISPFlags F) { + return static_cast(static_cast(F) & 0x3); +} + +static DISubprogram::DISPFlags fromRust(LLVMRustDISPFlags SPFlags) { + DISubprogram::DISPFlags Result = DISubprogram::DISPFlags::SPFlagZero; + + switch (virtuality(SPFlags)) { + case LLVMRustDISPFlags::SPFlagVirtual: + Result |= DISubprogram::DISPFlags::SPFlagVirtual; + break; + case LLVMRustDISPFlags::SPFlagPureVirtual: + Result |= DISubprogram::DISPFlags::SPFlagPureVirtual; + break; + default: + // The rest are handled below + break; + } + + if (isSet(SPFlags & LLVMRustDISPFlags::SPFlagLocalToUnit)) { + Result |= DISubprogram::DISPFlags::SPFlagLocalToUnit; + } + if (isSet(SPFlags & LLVMRustDISPFlags::SPFlagDefinition)) { + Result |= DISubprogram::DISPFlags::SPFlagDefinition; + } + if (isSet(SPFlags & LLVMRustDISPFlags::SPFlagOptimized)) { + Result |= DISubprogram::DISPFlags::SPFlagOptimized; + } +#if LLVM_VERSION_GE(9, 0) + if (isSet(SPFlags & LLVMRustDISPFlags::SPFlagMainSubprogram)) { + Result |= DISubprogram::DISPFlags::SPFlagMainSubprogram; + } +#endif + + return Result; +} + +enum class LLVMRustDebugEmissionKind { + NoDebug, + FullDebug, + LineTablesOnly, +}; + +static DICompileUnit::DebugEmissionKind fromRust(LLVMRustDebugEmissionKind Kind) { + switch (Kind) { + case LLVMRustDebugEmissionKind::NoDebug: + return DICompileUnit::DebugEmissionKind::NoDebug; + case LLVMRustDebugEmissionKind::FullDebug: + return DICompileUnit::DebugEmissionKind::FullDebug; + case LLVMRustDebugEmissionKind::LineTablesOnly: + return DICompileUnit::DebugEmissionKind::LineTablesOnly; + default: + report_fatal_error("bad DebugEmissionKind."); + } +} + +enum class LLVMRustChecksumKind { + None, + MD5, + SHA1, +}; + +static Optional fromRust(LLVMRustChecksumKind Kind) { + switch (Kind) { + case LLVMRustChecksumKind::None: + return None; + case LLVMRustChecksumKind::MD5: + return DIFile::ChecksumKind::CSK_MD5; + case LLVMRustChecksumKind::SHA1: + return DIFile::ChecksumKind::CSK_SHA1; + default: + report_fatal_error("bad ChecksumKind."); + } +} + +extern "C" uint32_t LLVMRustDebugMetadataVersion() { + return DEBUG_METADATA_VERSION; +} + +extern "C" uint32_t LLVMRustVersionMinor() { return LLVM_VERSION_MINOR; } + +extern "C" uint32_t LLVMRustVersionMajor() { return LLVM_VERSION_MAJOR; } + +extern "C" void LLVMRustAddModuleFlag(LLVMModuleRef M, const char *Name, + uint32_t Value) { + unwrap(M)->addModuleFlag(Module::Warning, Name, Value); +} + +extern "C" LLVMValueRef LLVMRustMetadataAsValue(LLVMContextRef C, LLVMMetadataRef MD) { + return wrap(MetadataAsValue::get(*unwrap(C), unwrap(MD))); +} + +extern "C" LLVMRustDIBuilderRef LLVMRustDIBuilderCreate(LLVMModuleRef M) { + return new DIBuilder(*unwrap(M)); +} + +extern "C" void LLVMRustDIBuilderDispose(LLVMRustDIBuilderRef Builder) { + delete Builder; +} + +extern "C" void LLVMRustDIBuilderFinalize(LLVMRustDIBuilderRef Builder) { + Builder->finalize(); +} + +extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateCompileUnit( + LLVMRustDIBuilderRef Builder, unsigned Lang, LLVMMetadataRef FileRef, + const char *Producer, size_t ProducerLen, bool isOptimized, + const char *Flags, unsigned RuntimeVer, + const char *SplitName, size_t SplitNameLen, + LLVMRustDebugEmissionKind Kind) { + auto *File = unwrapDI(FileRef); + + return wrap(Builder->createCompileUnit(Lang, File, StringRef(Producer, ProducerLen), + isOptimized, Flags, RuntimeVer, + StringRef(SplitName, SplitNameLen), + fromRust(Kind))); +} + +extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateFile( + LLVMRustDIBuilderRef Builder, + const char *Filename, size_t FilenameLen, + const char *Directory, size_t DirectoryLen, LLVMRustChecksumKind CSKind, + const char *Checksum, size_t ChecksumLen) { + Optional llvmCSKind = fromRust(CSKind); + Optional> CSInfo{}; + if (llvmCSKind) + CSInfo.emplace(*llvmCSKind, StringRef{Checksum, ChecksumLen}); + return wrap(Builder->createFile(StringRef(Filename, FilenameLen), + StringRef(Directory, DirectoryLen), + CSInfo)); +} + +extern "C" LLVMMetadataRef +LLVMRustDIBuilderCreateSubroutineType(LLVMRustDIBuilderRef Builder, + LLVMMetadataRef ParameterTypes) { + return wrap(Builder->createSubroutineType( + DITypeRefArray(unwrap(ParameterTypes)))); +} + +extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateFunction( + LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, + const char *Name, size_t NameLen, + const char *LinkageName, size_t LinkageNameLen, + LLVMMetadataRef File, unsigned LineNo, + LLVMMetadataRef Ty, unsigned ScopeLine, LLVMRustDIFlags Flags, + LLVMRustDISPFlags SPFlags, LLVMValueRef Fn, LLVMMetadataRef TParam, + LLVMMetadataRef Decl) { + DITemplateParameterArray TParams = + DITemplateParameterArray(unwrap(TParam)); + DISubprogram::DISPFlags llvmSPFlags = fromRust(SPFlags); + DINode::DIFlags llvmFlags = fromRust(Flags); +#if LLVM_VERSION_LT(9, 0) + if (isSet(SPFlags & LLVMRustDISPFlags::SPFlagMainSubprogram)) + llvmFlags |= DINode::DIFlags::FlagMainSubprogram; +#endif + DISubprogram *Sub = Builder->createFunction( + unwrapDI(Scope), + StringRef(Name, NameLen), + StringRef(LinkageName, LinkageNameLen), + unwrapDI(File), LineNo, + unwrapDI(Ty), ScopeLine, llvmFlags, + llvmSPFlags, TParams, unwrapDIPtr(Decl)); + unwrap(Fn)->setSubprogram(Sub); + return wrap(Sub); +} + +extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateBasicType( + LLVMRustDIBuilderRef Builder, const char *Name, size_t NameLen, + uint64_t SizeInBits, unsigned Encoding) { + return wrap(Builder->createBasicType(StringRef(Name, NameLen), SizeInBits, Encoding)); +} + +extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateTypedef( + LLVMRustDIBuilderRef Builder, LLVMMetadataRef Type, const char *Name, size_t NameLen, + LLVMMetadataRef File, unsigned LineNo, LLVMMetadataRef Scope) { + return wrap(Builder->createTypedef( + unwrap(Type), StringRef(Name, NameLen), unwrap(File), + LineNo, unwrap(Scope))); +} + +extern "C" LLVMMetadataRef LLVMRustDIBuilderCreatePointerType( + LLVMRustDIBuilderRef Builder, LLVMMetadataRef PointeeTy, + uint64_t SizeInBits, uint32_t AlignInBits, unsigned AddressSpace, + const char *Name, size_t NameLen) { + return wrap(Builder->createPointerType(unwrapDI(PointeeTy), + SizeInBits, AlignInBits, + AddressSpace, + StringRef(Name, NameLen))); +} + +extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateStructType( + LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, + const char *Name, size_t NameLen, + LLVMMetadataRef File, unsigned LineNumber, uint64_t SizeInBits, + uint32_t AlignInBits, LLVMRustDIFlags Flags, + LLVMMetadataRef DerivedFrom, LLVMMetadataRef Elements, + unsigned RunTimeLang, LLVMMetadataRef VTableHolder, + const char *UniqueId, size_t UniqueIdLen) { + return wrap(Builder->createStructType( + unwrapDI(Scope), StringRef(Name, NameLen), + unwrapDI(File), LineNumber, + SizeInBits, AlignInBits, fromRust(Flags), unwrapDI(DerivedFrom), + DINodeArray(unwrapDI(Elements)), RunTimeLang, + unwrapDI(VTableHolder), StringRef(UniqueId, UniqueIdLen))); +} + +extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateVariantPart( + LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, + const char *Name, size_t NameLen, + LLVMMetadataRef File, unsigned LineNumber, uint64_t SizeInBits, + uint32_t AlignInBits, LLVMRustDIFlags Flags, LLVMMetadataRef Discriminator, + LLVMMetadataRef Elements, const char *UniqueId, size_t UniqueIdLen) { + return wrap(Builder->createVariantPart( + unwrapDI(Scope), StringRef(Name, NameLen), + unwrapDI(File), LineNumber, + SizeInBits, AlignInBits, fromRust(Flags), unwrapDI(Discriminator), + DINodeArray(unwrapDI(Elements)), StringRef(UniqueId, UniqueIdLen))); +} + +extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateMemberType( + LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, + const char *Name, size_t NameLen, + LLVMMetadataRef File, unsigned LineNo, uint64_t SizeInBits, + uint32_t AlignInBits, uint64_t OffsetInBits, LLVMRustDIFlags Flags, + LLVMMetadataRef Ty) { + return wrap(Builder->createMemberType(unwrapDI(Scope), + StringRef(Name, NameLen), + unwrapDI(File), LineNo, + SizeInBits, AlignInBits, OffsetInBits, + fromRust(Flags), unwrapDI(Ty))); +} + +extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateVariantMemberType( + LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, + const char *Name, size_t NameLen, LLVMMetadataRef File, unsigned LineNo, + uint64_t SizeInBits, uint32_t AlignInBits, uint64_t OffsetInBits, LLVMValueRef Discriminant, + LLVMRustDIFlags Flags, LLVMMetadataRef Ty) { + llvm::ConstantInt* D = nullptr; + if (Discriminant) { + D = unwrap(Discriminant); + } + return wrap(Builder->createVariantMemberType(unwrapDI(Scope), + StringRef(Name, NameLen), + unwrapDI(File), LineNo, + SizeInBits, AlignInBits, OffsetInBits, D, + fromRust(Flags), unwrapDI(Ty))); +} + +extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateLexicalBlock( + LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, + LLVMMetadataRef File, unsigned Line, unsigned Col) { + return wrap(Builder->createLexicalBlock(unwrapDI(Scope), + unwrapDI(File), Line, Col)); +} + +extern "C" LLVMMetadataRef +LLVMRustDIBuilderCreateLexicalBlockFile(LLVMRustDIBuilderRef Builder, + LLVMMetadataRef Scope, + LLVMMetadataRef File) { + return wrap(Builder->createLexicalBlockFile(unwrapDI(Scope), + unwrapDI(File))); +} + +extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateStaticVariable( + LLVMRustDIBuilderRef Builder, LLVMMetadataRef Context, + const char *Name, size_t NameLen, + const char *LinkageName, size_t LinkageNameLen, + LLVMMetadataRef File, unsigned LineNo, + LLVMMetadataRef Ty, bool IsLocalToUnit, LLVMValueRef V, + LLVMMetadataRef Decl = nullptr, uint32_t AlignInBits = 0) { + llvm::GlobalVariable *InitVal = cast(unwrap(V)); + + llvm::DIExpression *InitExpr = nullptr; + if (llvm::ConstantInt *IntVal = llvm::dyn_cast(InitVal)) { + InitExpr = Builder->createConstantValueExpression( + IntVal->getValue().getSExtValue()); + } else if (llvm::ConstantFP *FPVal = + llvm::dyn_cast(InitVal)) { + InitExpr = Builder->createConstantValueExpression( + FPVal->getValueAPF().bitcastToAPInt().getZExtValue()); + } + + llvm::DIGlobalVariableExpression *VarExpr = Builder->createGlobalVariableExpression( + unwrapDI(Context), StringRef(Name, NameLen), + StringRef(LinkageName, LinkageNameLen), + unwrapDI(File), LineNo, unwrapDI(Ty), IsLocalToUnit, +#if LLVM_VERSION_GE(10, 0) + /* isDefined */ true, +#endif + InitExpr, unwrapDIPtr(Decl), + /* templateParams */ nullptr, + AlignInBits); + + InitVal->setMetadata("dbg", VarExpr); + + return wrap(VarExpr); +} + +extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateVariable( + LLVMRustDIBuilderRef Builder, unsigned Tag, LLVMMetadataRef Scope, + const char *Name, size_t NameLen, + LLVMMetadataRef File, unsigned LineNo, + LLVMMetadataRef Ty, bool AlwaysPreserve, LLVMRustDIFlags Flags, + unsigned ArgNo, uint32_t AlignInBits) { + if (Tag == 0x100) { // DW_TAG_auto_variable + return wrap(Builder->createAutoVariable( + unwrapDI(Scope), StringRef(Name, NameLen), + unwrapDI(File), LineNo, + unwrapDI(Ty), AlwaysPreserve, fromRust(Flags), AlignInBits)); + } else { + return wrap(Builder->createParameterVariable( + unwrapDI(Scope), StringRef(Name, NameLen), ArgNo, + unwrapDI(File), LineNo, + unwrapDI(Ty), AlwaysPreserve, fromRust(Flags))); + } +} + +extern "C" LLVMMetadataRef +LLVMRustDIBuilderCreateArrayType(LLVMRustDIBuilderRef Builder, uint64_t Size, + uint32_t AlignInBits, LLVMMetadataRef Ty, + LLVMMetadataRef Subscripts) { + return wrap( + Builder->createArrayType(Size, AlignInBits, unwrapDI(Ty), + DINodeArray(unwrapDI(Subscripts)))); +} + +extern "C" LLVMMetadataRef +LLVMRustDIBuilderGetOrCreateSubrange(LLVMRustDIBuilderRef Builder, int64_t Lo, + int64_t Count) { + return wrap(Builder->getOrCreateSubrange(Lo, Count)); +} + +extern "C" LLVMMetadataRef +LLVMRustDIBuilderGetOrCreateArray(LLVMRustDIBuilderRef Builder, + LLVMMetadataRef *Ptr, unsigned Count) { + Metadata **DataValue = unwrap(Ptr); + return wrap( + Builder->getOrCreateArray(ArrayRef(DataValue, Count)).get()); +} + +extern "C" LLVMValueRef LLVMRustDIBuilderInsertDeclareAtEnd( + LLVMRustDIBuilderRef Builder, LLVMValueRef V, LLVMMetadataRef VarInfo, + int64_t *AddrOps, unsigned AddrOpsCount, LLVMValueRef DL, + LLVMBasicBlockRef InsertAtEnd) { + return wrap(Builder->insertDeclare( + unwrap(V), unwrap(VarInfo), + Builder->createExpression(llvm::ArrayRef(AddrOps, AddrOpsCount)), + DebugLoc(cast(unwrap(DL)->getMetadata())), + unwrap(InsertAtEnd))); +} + +extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateEnumerator( + LLVMRustDIBuilderRef Builder, const char *Name, size_t NameLen, + int64_t Value, bool IsUnsigned) { + return wrap(Builder->createEnumerator(StringRef(Name, NameLen), Value, IsUnsigned)); +} + +extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateEnumerationType( + LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, + const char *Name, size_t NameLen, + LLVMMetadataRef File, unsigned LineNumber, uint64_t SizeInBits, + uint32_t AlignInBits, LLVMMetadataRef Elements, + LLVMMetadataRef ClassTy, bool IsScoped) { + return wrap(Builder->createEnumerationType( + unwrapDI(Scope), StringRef(Name, NameLen), + unwrapDI(File), LineNumber, + SizeInBits, AlignInBits, DINodeArray(unwrapDI(Elements)), + unwrapDI(ClassTy), "", IsScoped)); +} + +extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateUnionType( + LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, + const char *Name, size_t NameLen, + LLVMMetadataRef File, unsigned LineNumber, uint64_t SizeInBits, + uint32_t AlignInBits, LLVMRustDIFlags Flags, LLVMMetadataRef Elements, + unsigned RunTimeLang, const char *UniqueId, size_t UniqueIdLen) { + return wrap(Builder->createUnionType( + unwrapDI(Scope), StringRef(Name, NameLen), unwrapDI(File), + LineNumber, SizeInBits, AlignInBits, fromRust(Flags), + DINodeArray(unwrapDI(Elements)), RunTimeLang, + StringRef(UniqueId, UniqueIdLen))); +} + +extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateTemplateTypeParameter( + LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, + const char *Name, size_t NameLen, LLVMMetadataRef Ty) { +#if LLVM_VERSION_GE(11, 0) + bool IsDefault = false; // FIXME: should we ever set this true? + return wrap(Builder->createTemplateTypeParameter( + unwrapDI(Scope), StringRef(Name, NameLen), unwrapDI(Ty), IsDefault)); +#else + return wrap(Builder->createTemplateTypeParameter( + unwrapDI(Scope), StringRef(Name, NameLen), unwrapDI(Ty))); +#endif +} + +extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateNameSpace( + LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, + const char *Name, size_t NameLen, bool ExportSymbols) { + return wrap(Builder->createNameSpace( + unwrapDI(Scope), StringRef(Name, NameLen), ExportSymbols + )); +} + +extern "C" void +LLVMRustDICompositeTypeReplaceArrays(LLVMRustDIBuilderRef Builder, + LLVMMetadataRef CompositeTy, + LLVMMetadataRef Elements, + LLVMMetadataRef Params) { + DICompositeType *Tmp = unwrapDI(CompositeTy); + Builder->replaceArrays(Tmp, DINodeArray(unwrap(Elements)), + DINodeArray(unwrap(Params))); +} + +extern "C" LLVMValueRef +LLVMRustDIBuilderCreateDebugLocation(LLVMContextRef ContextRef, unsigned Line, + unsigned Column, LLVMMetadataRef Scope, + LLVMMetadataRef InlinedAt) { + LLVMContext &Context = *unwrap(ContextRef); + + DebugLoc debug_loc = DebugLoc::get(Line, Column, unwrapDIPtr(Scope), + unwrapDIPtr(InlinedAt)); + + return wrap(MetadataAsValue::get(Context, debug_loc.getAsMDNode())); +} + +extern "C" int64_t LLVMRustDIBuilderCreateOpDeref() { + return dwarf::DW_OP_deref; +} + +extern "C" int64_t LLVMRustDIBuilderCreateOpPlusUconst() { + return dwarf::DW_OP_plus_uconst; +} + +extern "C" void LLVMRustWriteTypeToString(LLVMTypeRef Ty, RustStringRef Str) { + RawRustStringOstream OS(Str); + unwrap(Ty)->print(OS); +} + +extern "C" void LLVMRustWriteValueToString(LLVMValueRef V, + RustStringRef Str) { + RawRustStringOstream OS(Str); + if (!V) { + OS << "(null)"; + } else { + OS << "("; + unwrap(V)->getType()->print(OS); + OS << ":"; + unwrap(V)->print(OS); + OS << ")"; + } +} + +// Note that the two following functions look quite similar to the +// LLVMGetSectionName function. Sadly, it appears that this function only +// returns a char* pointer, which isn't guaranteed to be null-terminated. The +// function provided by LLVM doesn't return the length, so we've created our own +// function which returns the length as well as the data pointer. +// +// For an example of this not returning a null terminated string, see +// lib/Object/COFFObjectFile.cpp in the getSectionName function. One of the +// branches explicitly creates a StringRef without a null terminator, and then +// that's returned. + +inline section_iterator *unwrap(LLVMSectionIteratorRef SI) { + return reinterpret_cast(SI); +} + +extern "C" size_t LLVMRustGetSectionName(LLVMSectionIteratorRef SI, + const char **Ptr) { +#if LLVM_VERSION_GE(10, 0) + auto NameOrErr = (*unwrap(SI))->getName(); + if (!NameOrErr) + report_fatal_error(NameOrErr.takeError()); + *Ptr = NameOrErr->data(); + return NameOrErr->size(); +#else + StringRef Ret; + if (std::error_code EC = (*unwrap(SI))->getName(Ret)) + report_fatal_error(EC.message()); + *Ptr = Ret.data(); + return Ret.size(); +#endif +} + +// LLVMArrayType function does not support 64-bit ElementCount +extern "C" LLVMTypeRef LLVMRustArrayType(LLVMTypeRef ElementTy, + uint64_t ElementCount) { + return wrap(ArrayType::get(unwrap(ElementTy), ElementCount)); +} + +DEFINE_SIMPLE_CONVERSION_FUNCTIONS(Twine, LLVMTwineRef) + +extern "C" void LLVMRustWriteTwineToString(LLVMTwineRef T, RustStringRef Str) { + RawRustStringOstream OS(Str); + unwrap(T)->print(OS); +} + +extern "C" void LLVMRustUnpackOptimizationDiagnostic( + LLVMDiagnosticInfoRef DI, RustStringRef PassNameOut, + LLVMValueRef *FunctionOut, unsigned* Line, unsigned* Column, + RustStringRef FilenameOut, RustStringRef MessageOut) { + // Undefined to call this not on an optimization diagnostic! + llvm::DiagnosticInfoOptimizationBase *Opt = + static_cast(unwrap(DI)); + + RawRustStringOstream PassNameOS(PassNameOut); + PassNameOS << Opt->getPassName(); + *FunctionOut = wrap(&Opt->getFunction()); + + RawRustStringOstream FilenameOS(FilenameOut); + DiagnosticLocation loc = Opt->getLocation(); + if (loc.isValid()) { + *Line = loc.getLine(); + *Column = loc.getColumn(); + FilenameOS << loc.getAbsolutePath(); + } + + RawRustStringOstream MessageOS(MessageOut); + MessageOS << Opt->getMsg(); +} + +enum class LLVMRustDiagnosticLevel { + Error, + Warning, + Note, + Remark, +}; + +extern "C" void +LLVMRustUnpackInlineAsmDiagnostic(LLVMDiagnosticInfoRef DI, + LLVMRustDiagnosticLevel *LevelOut, + unsigned *CookieOut, + LLVMTwineRef *MessageOut, + LLVMValueRef *InstructionOut) { + // Undefined to call this not on an inline assembly diagnostic! + llvm::DiagnosticInfoInlineAsm *IA = + static_cast(unwrap(DI)); + + *CookieOut = IA->getLocCookie(); + *MessageOut = wrap(&IA->getMsgStr()); + *InstructionOut = wrap(IA->getInstruction()); + + switch (IA->getSeverity()) { + case DS_Error: + *LevelOut = LLVMRustDiagnosticLevel::Error; + break; + case DS_Warning: + *LevelOut = LLVMRustDiagnosticLevel::Warning; + break; + case DS_Note: + *LevelOut = LLVMRustDiagnosticLevel::Note; + break; + case DS_Remark: + *LevelOut = LLVMRustDiagnosticLevel::Remark; + break; + default: + report_fatal_error("Invalid LLVMRustDiagnosticLevel value!"); + } +} + +extern "C" void LLVMRustWriteDiagnosticInfoToString(LLVMDiagnosticInfoRef DI, + RustStringRef Str) { + RawRustStringOstream OS(Str); + DiagnosticPrinterRawOStream DP(OS); + unwrap(DI)->print(DP); +} + +enum class LLVMRustDiagnosticKind { + Other, + InlineAsm, + StackSize, + DebugMetadataVersion, + SampleProfile, + OptimizationRemark, + OptimizationRemarkMissed, + OptimizationRemarkAnalysis, + OptimizationRemarkAnalysisFPCommute, + OptimizationRemarkAnalysisAliasing, + OptimizationRemarkOther, + OptimizationFailure, + PGOProfile, + Linker, +}; + +static LLVMRustDiagnosticKind toRust(DiagnosticKind Kind) { + switch (Kind) { + case DK_InlineAsm: + return LLVMRustDiagnosticKind::InlineAsm; + case DK_StackSize: + return LLVMRustDiagnosticKind::StackSize; + case DK_DebugMetadataVersion: + return LLVMRustDiagnosticKind::DebugMetadataVersion; + case DK_SampleProfile: + return LLVMRustDiagnosticKind::SampleProfile; + case DK_OptimizationRemark: + return LLVMRustDiagnosticKind::OptimizationRemark; + case DK_OptimizationRemarkMissed: + return LLVMRustDiagnosticKind::OptimizationRemarkMissed; + case DK_OptimizationRemarkAnalysis: + return LLVMRustDiagnosticKind::OptimizationRemarkAnalysis; + case DK_OptimizationRemarkAnalysisFPCommute: + return LLVMRustDiagnosticKind::OptimizationRemarkAnalysisFPCommute; + case DK_OptimizationRemarkAnalysisAliasing: + return LLVMRustDiagnosticKind::OptimizationRemarkAnalysisAliasing; + case DK_PGOProfile: + return LLVMRustDiagnosticKind::PGOProfile; + case DK_Linker: + return LLVMRustDiagnosticKind::Linker; + default: + return (Kind >= DK_FirstRemark && Kind <= DK_LastRemark) + ? LLVMRustDiagnosticKind::OptimizationRemarkOther + : LLVMRustDiagnosticKind::Other; + } +} + +extern "C" LLVMRustDiagnosticKind +LLVMRustGetDiagInfoKind(LLVMDiagnosticInfoRef DI) { + return toRust((DiagnosticKind)unwrap(DI)->getKind()); +} + +// This is kept distinct from LLVMGetTypeKind, because when +// a new type kind is added, the Rust-side enum must be +// updated or UB will result. +extern "C" LLVMTypeKind LLVMRustGetTypeKind(LLVMTypeRef Ty) { + switch (unwrap(Ty)->getTypeID()) { + case Type::VoidTyID: + return LLVMVoidTypeKind; + case Type::HalfTyID: + return LLVMHalfTypeKind; + case Type::FloatTyID: + return LLVMFloatTypeKind; + case Type::DoubleTyID: + return LLVMDoubleTypeKind; + case Type::X86_FP80TyID: + return LLVMX86_FP80TypeKind; + case Type::FP128TyID: + return LLVMFP128TypeKind; + case Type::PPC_FP128TyID: + return LLVMPPC_FP128TypeKind; + case Type::LabelTyID: + return LLVMLabelTypeKind; + case Type::MetadataTyID: + return LLVMMetadataTypeKind; + case Type::IntegerTyID: + return LLVMIntegerTypeKind; + case Type::FunctionTyID: + return LLVMFunctionTypeKind; + case Type::StructTyID: + return LLVMStructTypeKind; + case Type::ArrayTyID: + return LLVMArrayTypeKind; + case Type::PointerTyID: + return LLVMPointerTypeKind; +#if LLVM_VERSION_GE(11, 0) + case Type::FixedVectorTyID: + return LLVMVectorTypeKind; +#else + case Type::VectorTyID: + return LLVMVectorTypeKind; +#endif + case Type::X86_MMXTyID: + return LLVMX86_MMXTypeKind; + case Type::TokenTyID: + return LLVMTokenTypeKind; +#if LLVM_VERSION_GE(11, 0) + case Type::ScalableVectorTyID: + return LLVMScalableVectorTypeKind; + case Type::BFloatTyID: + return LLVMBFloatTypeKind; +#endif + } + report_fatal_error("Unhandled TypeID."); +} + +DEFINE_SIMPLE_CONVERSION_FUNCTIONS(SMDiagnostic, LLVMSMDiagnosticRef) + +extern "C" void LLVMRustSetInlineAsmDiagnosticHandler( + LLVMContextRef C, LLVMContext::InlineAsmDiagHandlerTy H, void *CX) { + unwrap(C)->setInlineAsmDiagnosticHandler(H, CX); +} + +extern "C" bool LLVMRustUnpackSMDiagnostic(LLVMSMDiagnosticRef DRef, + RustStringRef MessageOut, + RustStringRef BufferOut, + LLVMRustDiagnosticLevel* LevelOut, + unsigned* LocOut, + unsigned* RangesOut, + size_t* NumRanges) { + SMDiagnostic& D = *unwrap(DRef); + RawRustStringOstream MessageOS(MessageOut); + MessageOS << D.getMessage(); + + switch (D.getKind()) { + case SourceMgr::DK_Error: + *LevelOut = LLVMRustDiagnosticLevel::Error; + break; + case SourceMgr::DK_Warning: + *LevelOut = LLVMRustDiagnosticLevel::Warning; + break; + case SourceMgr::DK_Note: + *LevelOut = LLVMRustDiagnosticLevel::Note; + break; + case SourceMgr::DK_Remark: + *LevelOut = LLVMRustDiagnosticLevel::Remark; + break; + default: + report_fatal_error("Invalid LLVMRustDiagnosticLevel value!"); + } + + if (D.getLoc() == SMLoc()) + return false; + + const SourceMgr &LSM = *D.getSourceMgr(); + const MemoryBuffer *LBuf = LSM.getMemoryBuffer(LSM.FindBufferContainingLoc(D.getLoc())); + LLVMRustStringWriteImpl(BufferOut, LBuf->getBufferStart(), LBuf->getBufferSize()); + + *LocOut = D.getLoc().getPointer() - LBuf->getBufferStart(); + + *NumRanges = std::min(*NumRanges, D.getRanges().size()); + size_t LineStart = *LocOut - (size_t)D.getColumnNo(); + for (size_t i = 0; i < *NumRanges; i++) { + RangesOut[i * 2] = LineStart + D.getRanges()[i].first; + RangesOut[i * 2 + 1] = LineStart + D.getRanges()[i].second; + } + + return true; +} + +extern "C" LLVMValueRef LLVMRustBuildCleanupPad(LLVMBuilderRef B, + LLVMValueRef ParentPad, + unsigned ArgCount, + LLVMValueRef *LLArgs, + const char *Name) { + Value **Args = unwrap(LLArgs); + if (ParentPad == nullptr) { + Type *Ty = Type::getTokenTy(unwrap(B)->getContext()); + ParentPad = wrap(Constant::getNullValue(Ty)); + } + return wrap(unwrap(B)->CreateCleanupPad( + unwrap(ParentPad), ArrayRef(Args, ArgCount), Name)); +} + +extern "C" LLVMValueRef LLVMRustBuildCleanupRet(LLVMBuilderRef B, + LLVMValueRef CleanupPad, + LLVMBasicBlockRef UnwindBB) { + CleanupPadInst *Inst = cast(unwrap(CleanupPad)); + return wrap(unwrap(B)->CreateCleanupRet(Inst, unwrap(UnwindBB))); +} + +extern "C" LLVMValueRef +LLVMRustBuildCatchPad(LLVMBuilderRef B, LLVMValueRef ParentPad, + unsigned ArgCount, LLVMValueRef *LLArgs, const char *Name) { + Value **Args = unwrap(LLArgs); + return wrap(unwrap(B)->CreateCatchPad( + unwrap(ParentPad), ArrayRef(Args, ArgCount), Name)); +} + +extern "C" LLVMValueRef LLVMRustBuildCatchRet(LLVMBuilderRef B, + LLVMValueRef Pad, + LLVMBasicBlockRef BB) { + return wrap(unwrap(B)->CreateCatchRet(cast(unwrap(Pad)), + unwrap(BB))); +} + +extern "C" LLVMValueRef LLVMRustBuildCatchSwitch(LLVMBuilderRef B, + LLVMValueRef ParentPad, + LLVMBasicBlockRef BB, + unsigned NumHandlers, + const char *Name) { + if (ParentPad == nullptr) { + Type *Ty = Type::getTokenTy(unwrap(B)->getContext()); + ParentPad = wrap(Constant::getNullValue(Ty)); + } + return wrap(unwrap(B)->CreateCatchSwitch(unwrap(ParentPad), unwrap(BB), + NumHandlers, Name)); +} + +extern "C" void LLVMRustAddHandler(LLVMValueRef CatchSwitchRef, + LLVMBasicBlockRef Handler) { + Value *CatchSwitch = unwrap(CatchSwitchRef); + cast(CatchSwitch)->addHandler(unwrap(Handler)); +} + +extern "C" OperandBundleDef *LLVMRustBuildOperandBundleDef(const char *Name, + LLVMValueRef *Inputs, + unsigned NumInputs) { + return new OperandBundleDef(Name, makeArrayRef(unwrap(Inputs), NumInputs)); +} + +extern "C" void LLVMRustFreeOperandBundleDef(OperandBundleDef *Bundle) { + delete Bundle; +} + +extern "C" LLVMValueRef LLVMRustBuildCall(LLVMBuilderRef B, LLVMValueRef Fn, + LLVMValueRef *Args, unsigned NumArgs, + OperandBundleDef *Bundle) { + Value *Callee = unwrap(Fn); + FunctionType *FTy = cast(Callee->getType()->getPointerElementType()); + unsigned Len = Bundle ? 1 : 0; + ArrayRef Bundles = makeArrayRef(Bundle, Len); + return wrap(unwrap(B)->CreateCall( + FTy, Callee, makeArrayRef(unwrap(Args), NumArgs), Bundles)); +} + +extern "C" LLVMValueRef LLVMRustGetInstrProfIncrementIntrinsic(LLVMModuleRef M) { + return wrap(llvm::Intrinsic::getDeclaration(unwrap(M), + (llvm::Intrinsic::ID)llvm::Intrinsic::instrprof_increment)); +} + +extern "C" LLVMValueRef LLVMRustBuildMemCpy(LLVMBuilderRef B, + LLVMValueRef Dst, unsigned DstAlign, + LLVMValueRef Src, unsigned SrcAlign, + LLVMValueRef Size, bool IsVolatile) { +#if LLVM_VERSION_GE(10, 0) + return wrap(unwrap(B)->CreateMemCpy( + unwrap(Dst), MaybeAlign(DstAlign), + unwrap(Src), MaybeAlign(SrcAlign), + unwrap(Size), IsVolatile)); +#else + return wrap(unwrap(B)->CreateMemCpy( + unwrap(Dst), DstAlign, + unwrap(Src), SrcAlign, + unwrap(Size), IsVolatile)); +#endif +} + +extern "C" LLVMValueRef LLVMRustBuildMemMove(LLVMBuilderRef B, + LLVMValueRef Dst, unsigned DstAlign, + LLVMValueRef Src, unsigned SrcAlign, + LLVMValueRef Size, bool IsVolatile) { +#if LLVM_VERSION_GE(10, 0) + return wrap(unwrap(B)->CreateMemMove( + unwrap(Dst), MaybeAlign(DstAlign), + unwrap(Src), MaybeAlign(SrcAlign), + unwrap(Size), IsVolatile)); +#else + return wrap(unwrap(B)->CreateMemMove( + unwrap(Dst), DstAlign, + unwrap(Src), SrcAlign, + unwrap(Size), IsVolatile)); +#endif +} + +extern "C" LLVMValueRef LLVMRustBuildMemSet(LLVMBuilderRef B, + LLVMValueRef Dst, unsigned DstAlign, + LLVMValueRef Val, + LLVMValueRef Size, bool IsVolatile) { +#if LLVM_VERSION_GE(10, 0) + return wrap(unwrap(B)->CreateMemSet( + unwrap(Dst), unwrap(Val), unwrap(Size), MaybeAlign(DstAlign), IsVolatile)); +#else + return wrap(unwrap(B)->CreateMemSet( + unwrap(Dst), unwrap(Val), unwrap(Size), DstAlign, IsVolatile)); +#endif +} + +extern "C" LLVMValueRef +LLVMRustBuildInvoke(LLVMBuilderRef B, LLVMValueRef Fn, LLVMValueRef *Args, + unsigned NumArgs, LLVMBasicBlockRef Then, + LLVMBasicBlockRef Catch, OperandBundleDef *Bundle, + const char *Name) { + Value *Callee = unwrap(Fn); + FunctionType *FTy = cast(Callee->getType()->getPointerElementType()); + unsigned Len = Bundle ? 1 : 0; + ArrayRef Bundles = makeArrayRef(Bundle, Len); + return wrap(unwrap(B)->CreateInvoke(FTy, Callee, unwrap(Then), unwrap(Catch), + makeArrayRef(unwrap(Args), NumArgs), + Bundles, Name)); +} + +extern "C" void LLVMRustPositionBuilderAtStart(LLVMBuilderRef B, + LLVMBasicBlockRef BB) { + auto Point = unwrap(BB)->getFirstInsertionPt(); + unwrap(B)->SetInsertPoint(unwrap(BB), Point); +} + +extern "C" void LLVMRustSetComdat(LLVMModuleRef M, LLVMValueRef V, + const char *Name, size_t NameLen) { + Triple TargetTriple(unwrap(M)->getTargetTriple()); + GlobalObject *GV = unwrap(V); + if (!TargetTriple.isOSBinFormatMachO()) { + StringRef NameRef(Name, NameLen); + GV->setComdat(unwrap(M)->getOrInsertComdat(NameRef)); + } +} + +extern "C" void LLVMRustUnsetComdat(LLVMValueRef V) { + GlobalObject *GV = unwrap(V); + GV->setComdat(nullptr); +} + +enum class LLVMRustLinkage { + ExternalLinkage = 0, + AvailableExternallyLinkage = 1, + LinkOnceAnyLinkage = 2, + LinkOnceODRLinkage = 3, + WeakAnyLinkage = 4, + WeakODRLinkage = 5, + AppendingLinkage = 6, + InternalLinkage = 7, + PrivateLinkage = 8, + ExternalWeakLinkage = 9, + CommonLinkage = 10, +}; + +static LLVMRustLinkage toRust(LLVMLinkage Linkage) { + switch (Linkage) { + case LLVMExternalLinkage: + return LLVMRustLinkage::ExternalLinkage; + case LLVMAvailableExternallyLinkage: + return LLVMRustLinkage::AvailableExternallyLinkage; + case LLVMLinkOnceAnyLinkage: + return LLVMRustLinkage::LinkOnceAnyLinkage; + case LLVMLinkOnceODRLinkage: + return LLVMRustLinkage::LinkOnceODRLinkage; + case LLVMWeakAnyLinkage: + return LLVMRustLinkage::WeakAnyLinkage; + case LLVMWeakODRLinkage: + return LLVMRustLinkage::WeakODRLinkage; + case LLVMAppendingLinkage: + return LLVMRustLinkage::AppendingLinkage; + case LLVMInternalLinkage: + return LLVMRustLinkage::InternalLinkage; + case LLVMPrivateLinkage: + return LLVMRustLinkage::PrivateLinkage; + case LLVMExternalWeakLinkage: + return LLVMRustLinkage::ExternalWeakLinkage; + case LLVMCommonLinkage: + return LLVMRustLinkage::CommonLinkage; + default: + report_fatal_error("Invalid LLVMRustLinkage value!"); + } +} + +static LLVMLinkage fromRust(LLVMRustLinkage Linkage) { + switch (Linkage) { + case LLVMRustLinkage::ExternalLinkage: + return LLVMExternalLinkage; + case LLVMRustLinkage::AvailableExternallyLinkage: + return LLVMAvailableExternallyLinkage; + case LLVMRustLinkage::LinkOnceAnyLinkage: + return LLVMLinkOnceAnyLinkage; + case LLVMRustLinkage::LinkOnceODRLinkage: + return LLVMLinkOnceODRLinkage; + case LLVMRustLinkage::WeakAnyLinkage: + return LLVMWeakAnyLinkage; + case LLVMRustLinkage::WeakODRLinkage: + return LLVMWeakODRLinkage; + case LLVMRustLinkage::AppendingLinkage: + return LLVMAppendingLinkage; + case LLVMRustLinkage::InternalLinkage: + return LLVMInternalLinkage; + case LLVMRustLinkage::PrivateLinkage: + return LLVMPrivateLinkage; + case LLVMRustLinkage::ExternalWeakLinkage: + return LLVMExternalWeakLinkage; + case LLVMRustLinkage::CommonLinkage: + return LLVMCommonLinkage; + } + report_fatal_error("Invalid LLVMRustLinkage value!"); +} + +extern "C" LLVMRustLinkage LLVMRustGetLinkage(LLVMValueRef V) { + return toRust(LLVMGetLinkage(V)); +} + +extern "C" void LLVMRustSetLinkage(LLVMValueRef V, + LLVMRustLinkage RustLinkage) { + LLVMSetLinkage(V, fromRust(RustLinkage)); +} + +// Returns true if both high and low were successfully set. Fails in case constant wasn’t any of +// the common sizes (1, 8, 16, 32, 64, 128 bits) +extern "C" bool LLVMRustConstInt128Get(LLVMValueRef CV, bool sext, uint64_t *high, uint64_t *low) +{ + auto C = unwrap(CV); + if (C->getBitWidth() > 128) { return false; } + APInt AP; + if (sext) { + AP = C->getValue().sextOrSelf(128); + } else { + AP = C->getValue().zextOrSelf(128); + } + *low = AP.getLoBits(64).getZExtValue(); + *high = AP.getHiBits(64).getZExtValue(); + return true; +} + +enum class LLVMRustVisibility { + Default = 0, + Hidden = 1, + Protected = 2, +}; + +static LLVMRustVisibility toRust(LLVMVisibility Vis) { + switch (Vis) { + case LLVMDefaultVisibility: + return LLVMRustVisibility::Default; + case LLVMHiddenVisibility: + return LLVMRustVisibility::Hidden; + case LLVMProtectedVisibility: + return LLVMRustVisibility::Protected; + } + report_fatal_error("Invalid LLVMRustVisibility value!"); +} + +static LLVMVisibility fromRust(LLVMRustVisibility Vis) { + switch (Vis) { + case LLVMRustVisibility::Default: + return LLVMDefaultVisibility; + case LLVMRustVisibility::Hidden: + return LLVMHiddenVisibility; + case LLVMRustVisibility::Protected: + return LLVMProtectedVisibility; + } + report_fatal_error("Invalid LLVMRustVisibility value!"); +} + +extern "C" LLVMRustVisibility LLVMRustGetVisibility(LLVMValueRef V) { + return toRust(LLVMGetVisibility(V)); +} + +// Oh hey, a binding that makes sense for once? (because LLVM’s own do not) +extern "C" LLVMValueRef LLVMRustBuildIntCast(LLVMBuilderRef B, LLVMValueRef Val, + LLVMTypeRef DestTy, bool isSigned) { + return wrap(unwrap(B)->CreateIntCast(unwrap(Val), unwrap(DestTy), isSigned, "")); +} + +extern "C" void LLVMRustSetVisibility(LLVMValueRef V, + LLVMRustVisibility RustVisibility) { + LLVMSetVisibility(V, fromRust(RustVisibility)); +} + +struct LLVMRustModuleBuffer { + std::string data; +}; + +extern "C" LLVMRustModuleBuffer* +LLVMRustModuleBufferCreate(LLVMModuleRef M) { +#if LLVM_VERSION_GE(10, 0) + auto Ret = std::make_unique(); +#else + auto Ret = llvm::make_unique(); +#endif + { + raw_string_ostream OS(Ret->data); + { + legacy::PassManager PM; + PM.add(createBitcodeWriterPass(OS)); + PM.run(*unwrap(M)); + } + } + return Ret.release(); +} + +extern "C" void +LLVMRustModuleBufferFree(LLVMRustModuleBuffer *Buffer) { + delete Buffer; +} + +extern "C" const void* +LLVMRustModuleBufferPtr(const LLVMRustModuleBuffer *Buffer) { + return Buffer->data.data(); +} + +extern "C" size_t +LLVMRustModuleBufferLen(const LLVMRustModuleBuffer *Buffer) { + return Buffer->data.length(); +} + +extern "C" uint64_t +LLVMRustModuleCost(LLVMModuleRef M) { + auto f = unwrap(M)->functions(); + return std::distance(std::begin(f), std::end(f)); +} + +// Vector reductions: +extern "C" LLVMValueRef +LLVMRustBuildVectorReduceFAdd(LLVMBuilderRef B, LLVMValueRef Acc, LLVMValueRef Src) { + return wrap(unwrap(B)->CreateFAddReduce(unwrap(Acc),unwrap(Src))); +} +extern "C" LLVMValueRef +LLVMRustBuildVectorReduceFMul(LLVMBuilderRef B, LLVMValueRef Acc, LLVMValueRef Src) { + return wrap(unwrap(B)->CreateFMulReduce(unwrap(Acc),unwrap(Src))); +} +extern "C" LLVMValueRef +LLVMRustBuildVectorReduceAdd(LLVMBuilderRef B, LLVMValueRef Src) { + return wrap(unwrap(B)->CreateAddReduce(unwrap(Src))); +} +extern "C" LLVMValueRef +LLVMRustBuildVectorReduceMul(LLVMBuilderRef B, LLVMValueRef Src) { + return wrap(unwrap(B)->CreateMulReduce(unwrap(Src))); +} +extern "C" LLVMValueRef +LLVMRustBuildVectorReduceAnd(LLVMBuilderRef B, LLVMValueRef Src) { + return wrap(unwrap(B)->CreateAndReduce(unwrap(Src))); +} +extern "C" LLVMValueRef +LLVMRustBuildVectorReduceOr(LLVMBuilderRef B, LLVMValueRef Src) { + return wrap(unwrap(B)->CreateOrReduce(unwrap(Src))); +} +extern "C" LLVMValueRef +LLVMRustBuildVectorReduceXor(LLVMBuilderRef B, LLVMValueRef Src) { + return wrap(unwrap(B)->CreateXorReduce(unwrap(Src))); +} +extern "C" LLVMValueRef +LLVMRustBuildVectorReduceMin(LLVMBuilderRef B, LLVMValueRef Src, bool IsSigned) { + return wrap(unwrap(B)->CreateIntMinReduce(unwrap(Src), IsSigned)); +} +extern "C" LLVMValueRef +LLVMRustBuildVectorReduceMax(LLVMBuilderRef B, LLVMValueRef Src, bool IsSigned) { + return wrap(unwrap(B)->CreateIntMaxReduce(unwrap(Src), IsSigned)); +} +extern "C" LLVMValueRef +LLVMRustBuildVectorReduceFMin(LLVMBuilderRef B, LLVMValueRef Src, bool NoNaN) { + return wrap(unwrap(B)->CreateFPMinReduce(unwrap(Src), NoNaN)); +} +extern "C" LLVMValueRef +LLVMRustBuildVectorReduceFMax(LLVMBuilderRef B, LLVMValueRef Src, bool NoNaN) { + return wrap(unwrap(B)->CreateFPMaxReduce(unwrap(Src), NoNaN)); +} + +extern "C" LLVMValueRef +LLVMRustBuildMinNum(LLVMBuilderRef B, LLVMValueRef LHS, LLVMValueRef RHS) { + return wrap(unwrap(B)->CreateMinNum(unwrap(LHS),unwrap(RHS))); +} +extern "C" LLVMValueRef +LLVMRustBuildMaxNum(LLVMBuilderRef B, LLVMValueRef LHS, LLVMValueRef RHS) { + return wrap(unwrap(B)->CreateMaxNum(unwrap(LHS),unwrap(RHS))); +} diff --git a/compiler/rustc_llvm/src/lib.rs b/compiler/rustc_llvm/src/lib.rs new file mode 100644 index 00000000000..9d23397ade0 --- /dev/null +++ b/compiler/rustc_llvm/src/lib.rs @@ -0,0 +1,173 @@ +#![feature(nll)] +#![feature(static_nobundle)] +#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] + +// NOTE: This crate only exists to allow linking on mingw targets. + +use libc::{c_char, size_t}; +use std::cell::RefCell; +use std::slice; + +#[repr(C)] +pub struct RustString { + pub bytes: RefCell>, +} + +impl RustString { + pub fn len(&self) -> usize { + self.bytes.borrow().len() + } +} + +/// Appending to a Rust string -- used by RawRustStringOstream. +#[no_mangle] +#[allow(improper_ctypes_definitions)] +pub unsafe extern "C" fn LLVMRustStringWriteImpl( + sr: &RustString, + ptr: *const c_char, + size: size_t, +) { + let slice = slice::from_raw_parts(ptr as *const u8, size as usize); + + sr.bytes.borrow_mut().extend_from_slice(slice); +} + +/// Initialize targets enabled by the build script via `cfg(llvm_component = "...")`. +/// N.B., this function can't be moved to `rustc_codegen_llvm` because of the `cfg`s. +pub fn initialize_available_targets() { + macro_rules! init_target( + ($cfg:meta, $($method:ident),*) => { { + #[cfg($cfg)] + fn init() { + extern { + $(fn $method();)* + } + unsafe { + $($method();)* + } + } + #[cfg(not($cfg))] + fn init() { } + init(); + } } + ); + init_target!( + llvm_component = "x86", + LLVMInitializeX86TargetInfo, + LLVMInitializeX86Target, + LLVMInitializeX86TargetMC, + LLVMInitializeX86AsmPrinter, + LLVMInitializeX86AsmParser + ); + init_target!( + llvm_component = "arm", + LLVMInitializeARMTargetInfo, + LLVMInitializeARMTarget, + LLVMInitializeARMTargetMC, + LLVMInitializeARMAsmPrinter, + LLVMInitializeARMAsmParser + ); + init_target!( + llvm_component = "aarch64", + LLVMInitializeAArch64TargetInfo, + LLVMInitializeAArch64Target, + LLVMInitializeAArch64TargetMC, + LLVMInitializeAArch64AsmPrinter, + LLVMInitializeAArch64AsmParser + ); + init_target!( + llvm_component = "amdgpu", + LLVMInitializeAMDGPUTargetInfo, + LLVMInitializeAMDGPUTarget, + LLVMInitializeAMDGPUTargetMC, + LLVMInitializeAMDGPUAsmPrinter, + LLVMInitializeAMDGPUAsmParser + ); + init_target!( + llvm_component = "avr", + LLVMInitializeAVRTargetInfo, + LLVMInitializeAVRTarget, + LLVMInitializeAVRTargetMC, + LLVMInitializeAVRAsmPrinter, + LLVMInitializeAVRAsmParser + ); + init_target!( + llvm_component = "mips", + LLVMInitializeMipsTargetInfo, + LLVMInitializeMipsTarget, + LLVMInitializeMipsTargetMC, + LLVMInitializeMipsAsmPrinter, + LLVMInitializeMipsAsmParser + ); + init_target!( + llvm_component = "powerpc", + LLVMInitializePowerPCTargetInfo, + LLVMInitializePowerPCTarget, + LLVMInitializePowerPCTargetMC, + LLVMInitializePowerPCAsmPrinter, + LLVMInitializePowerPCAsmParser + ); + init_target!( + llvm_component = "systemz", + LLVMInitializeSystemZTargetInfo, + LLVMInitializeSystemZTarget, + LLVMInitializeSystemZTargetMC, + LLVMInitializeSystemZAsmPrinter, + LLVMInitializeSystemZAsmParser + ); + init_target!( + llvm_component = "jsbackend", + LLVMInitializeJSBackendTargetInfo, + LLVMInitializeJSBackendTarget, + LLVMInitializeJSBackendTargetMC + ); + init_target!( + llvm_component = "msp430", + LLVMInitializeMSP430TargetInfo, + LLVMInitializeMSP430Target, + LLVMInitializeMSP430TargetMC, + LLVMInitializeMSP430AsmPrinter + ); + init_target!( + all(llvm_component = "msp430", llvm_has_msp430_asm_parser), + LLVMInitializeMSP430AsmParser + ); + init_target!( + llvm_component = "riscv", + LLVMInitializeRISCVTargetInfo, + LLVMInitializeRISCVTarget, + LLVMInitializeRISCVTargetMC, + LLVMInitializeRISCVAsmPrinter, + LLVMInitializeRISCVAsmParser + ); + init_target!( + llvm_component = "sparc", + LLVMInitializeSparcTargetInfo, + LLVMInitializeSparcTarget, + LLVMInitializeSparcTargetMC, + LLVMInitializeSparcAsmPrinter, + LLVMInitializeSparcAsmParser + ); + init_target!( + llvm_component = "nvptx", + LLVMInitializeNVPTXTargetInfo, + LLVMInitializeNVPTXTarget, + LLVMInitializeNVPTXTargetMC, + LLVMInitializeNVPTXAsmPrinter + ); + init_target!( + llvm_component = "hexagon", + LLVMInitializeHexagonTargetInfo, + LLVMInitializeHexagonTarget, + LLVMInitializeHexagonTargetMC, + LLVMInitializeHexagonAsmPrinter, + LLVMInitializeHexagonAsmParser + ); + init_target!( + llvm_component = "webassembly", + LLVMInitializeWebAssemblyTargetInfo, + LLVMInitializeWebAssemblyTarget, + LLVMInitializeWebAssemblyTargetMC, + LLVMInitializeWebAssemblyAsmPrinter + ); +} diff --git a/config.toml.example b/config.toml.example index 9abb8add785..0f25d311ec5 100644 --- a/config.toml.example +++ b/config.toml.example @@ -45,7 +45,7 @@ # this flag will indicate that this version check should not be done. #version-check = true -# Link libstdc++ statically into the librustc_llvm instead of relying on a +# Link libstdc++ statically into the rustc_llvm instead of relying on a # dynamic version to be available. #static-libstdcpp = false diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index 01dbb483548..d2baf4a1d1e 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -812,7 +812,7 @@ impl<'a> Builder<'a> { format!("CARGO_PROFILE_{}_{}", profile, name) }; - // See comment in librustc_llvm/build.rs for why this is necessary, largely llvm-config + // See comment in rustc_llvm/build.rs for why this is necessary, largely llvm-config // needs to not accidentally link to libLLVM in stage0/lib. cargo.env("REAL_LIBRARY_PATH_VAR", &util::dylib_path_var()); if let Some(e) = env::var_os(util::dylib_path_var()) { @@ -829,9 +829,9 @@ impl<'a> Builder<'a> { // scripts can do less work (i.e. not building/requiring LLVM). if cmd == "check" || cmd == "clippy" || cmd == "fix" { // If we've not yet built LLVM, or it's stale, then bust - // the librustc_llvm cache. That will always work, even though it + // the rustc_llvm cache. That will always work, even though it // may mean that on the next non-check build we'll need to rebuild - // librustc_llvm. But if LLVM is stale, that'll be a tiny amount + // rustc_llvm. But if LLVM is stale, that'll be a tiny amount // of work comparitively, and we'd likely need to rebuild it anyway, // so that's okay. if crate::native::prebuilt_llvm_config(self, target).is_err() { diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs index 7814ca8e5bb..9d314e8452b 100644 --- a/src/bootstrap/compile.rs +++ b/src/bootstrap/compile.rs @@ -560,7 +560,7 @@ pub fn rustc_cargo_env(builder: &Builder<'_>, cargo: &mut Cargo, target: TargetS } // Pass down configuration from the LLVM build into the build of - // librustc_llvm and librustc_codegen_llvm. + // rustc_llvm and rustc_codegen_llvm. // // Note that this is disabled if LLVM itself is disabled or we're in a check // build. If we are in a check build we still go ahead here presuming we've @@ -579,7 +579,7 @@ pub fn rustc_cargo_env(builder: &Builder<'_>, cargo: &mut Cargo, target: TargetS if let Some(s) = target_config.and_then(|c| c.llvm_config.as_ref()) { cargo.env("CFG_LLVM_ROOT", s); } - // Some LLVM linker flags (-L and -l) may be needed to link librustc_llvm. + // Some LLVM linker flags (-L and -l) may be needed to link rustc_llvm. if let Some(ref s) = builder.config.llvm_ldflags { cargo.env("LLVM_LINKER_FLAGS", s); } diff --git a/src/librustc_llvm/Cargo.toml b/src/librustc_llvm/Cargo.toml deleted file mode 100644 index 7120f2e991a..00000000000 --- a/src/librustc_llvm/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -authors = ["The Rust Project Developers"] -name = "rustc_llvm" -version = "0.0.0" -edition = "2018" - -[lib] -path = "lib.rs" - -[features] -static-libstdcpp = [] -emscripten = [] - -[dependencies] -libc = "0.2.73" - -[build-dependencies] -build_helper = { path = "../build_helper" } -cc = "1.0.58" diff --git a/src/librustc_llvm/build.rs b/src/librustc_llvm/build.rs deleted file mode 100644 index 306ffbf5daa..00000000000 --- a/src/librustc_llvm/build.rs +++ /dev/null @@ -1,322 +0,0 @@ -use std::env; -use std::path::{Path, PathBuf}; -use std::process::Command; - -use build_helper::{output, tracked_env_var_os}; - -fn detect_llvm_link() -> (&'static str, &'static str) { - // Force the link mode we want, preferring static by default, but - // possibly overridden by `configure --enable-llvm-link-shared`. - if tracked_env_var_os("LLVM_LINK_SHARED").is_some() { - ("dylib", "--link-shared") - } else { - ("static", "--link-static") - } -} - -fn main() { - if tracked_env_var_os("RUST_CHECK").is_some() { - // If we're just running `check`, there's no need for LLVM to be built. - return; - } - - build_helper::restore_library_path(); - - let target = env::var("TARGET").expect("TARGET was not set"); - let llvm_config = - tracked_env_var_os("LLVM_CONFIG").map(|x| Some(PathBuf::from(x))).unwrap_or_else(|| { - if let Some(dir) = tracked_env_var_os("CARGO_TARGET_DIR").map(PathBuf::from) { - let to_test = dir - .parent() - .unwrap() - .parent() - .unwrap() - .join(&target) - .join("llvm/bin/llvm-config"); - if Command::new(&to_test).output().is_ok() { - return Some(to_test); - } - } - None - }); - - if let Some(llvm_config) = &llvm_config { - println!("cargo:rerun-if-changed={}", llvm_config.display()); - } - let llvm_config = llvm_config.unwrap_or_else(|| PathBuf::from("llvm-config")); - - // Test whether we're cross-compiling LLVM. This is a pretty rare case - // currently where we're producing an LLVM for a different platform than - // what this build script is currently running on. - // - // In that case, there's no guarantee that we can actually run the target, - // so the build system works around this by giving us the LLVM_CONFIG for - // the host platform. This only really works if the host LLVM and target - // LLVM are compiled the same way, but for us that's typically the case. - // - // We *want* detect this cross compiling situation by asking llvm-config - // what its host-target is. If that's not the TARGET, then we're cross - // compiling. Unfortunately `llvm-config` seems either be buggy, or we're - // misconfiguring it, because the `i686-pc-windows-gnu` build of LLVM will - // report itself with a `--host-target` of `x86_64-pc-windows-gnu`. This - // tricks us into thinking we're doing a cross build when we aren't, so - // havoc ensues. - // - // In any case, if we're cross compiling, this generally just means that we - // can't trust all the output of llvm-config because it might be targeted - // for the host rather than the target. As a result a bunch of blocks below - // are gated on `if !is_crossed` - let target = env::var("TARGET").expect("TARGET was not set"); - let host = env::var("HOST").expect("HOST was not set"); - let is_crossed = target != host; - - let mut optional_components = vec![ - "x86", - "arm", - "aarch64", - "amdgpu", - "avr", - "mips", - "powerpc", - "systemz", - "jsbackend", - "webassembly", - "msp430", - "sparc", - "nvptx", - "hexagon", - ]; - - let mut version_cmd = Command::new(&llvm_config); - version_cmd.arg("--version"); - let version_output = output(&mut version_cmd); - let mut parts = version_output.split('.').take(2).filter_map(|s| s.parse::().ok()); - let (major, _minor) = if let (Some(major), Some(minor)) = (parts.next(), parts.next()) { - (major, minor) - } else { - (6, 0) - }; - - if major > 6 { - optional_components.push("riscv"); - } - - let required_components = &[ - "ipo", - "bitreader", - "bitwriter", - "linker", - "asmparser", - "lto", - "coverage", - "instrumentation", - ]; - - let components = output(Command::new(&llvm_config).arg("--components")); - let mut components = components.split_whitespace().collect::>(); - components.retain(|c| optional_components.contains(c) || required_components.contains(c)); - - for component in required_components { - if !components.contains(component) { - panic!("require llvm component {} but wasn't found", component); - } - } - - for component in components.iter() { - println!("cargo:rustc-cfg=llvm_component=\"{}\"", component); - } - - if major >= 9 { - println!("cargo:rustc-cfg=llvm_has_msp430_asm_parser"); - } - - // Link in our own LLVM shims, compiled with the same flags as LLVM - let mut cmd = Command::new(&llvm_config); - cmd.arg("--cxxflags"); - let cxxflags = output(&mut cmd); - let mut cfg = cc::Build::new(); - cfg.warnings(false); - for flag in cxxflags.split_whitespace() { - // Ignore flags like `-m64` when we're doing a cross build - if is_crossed && flag.starts_with("-m") { - continue; - } - - if flag.starts_with("-flto") { - continue; - } - - // -Wdate-time is not supported by the netbsd cross compiler - if is_crossed && target.contains("netbsd") && flag.contains("date-time") { - continue; - } - - // Include path contains host directory, replace it with target - if is_crossed && flag.starts_with("-I") { - cfg.flag(&flag.replace(&host, &target)); - continue; - } - - cfg.flag(flag); - } - - for component in &components { - let mut flag = String::from("LLVM_COMPONENT_"); - flag.push_str(&component.to_uppercase()); - cfg.define(&flag, None); - } - - if tracked_env_var_os("LLVM_RUSTLLVM").is_some() { - cfg.define("LLVM_RUSTLLVM", None); - } - - if tracked_env_var_os("LLVM_NDEBUG").is_some() { - cfg.define("NDEBUG", None); - cfg.debug(false); - } - - build_helper::rerun_if_changed_anything_in_dir(Path::new("../rustllvm")); - cfg.file("../rustllvm/PassWrapper.cpp") - .file("../rustllvm/RustWrapper.cpp") - .file("../rustllvm/ArchiveWrapper.cpp") - .file("../rustllvm/CoverageMappingWrapper.cpp") - .file("../rustllvm/Linker.cpp") - .cpp(true) - .cpp_link_stdlib(None) // we handle this below - .compile("rustllvm"); - - let (llvm_kind, llvm_link_arg) = detect_llvm_link(); - - // Link in all LLVM libraries, if we're using the "wrong" llvm-config then - // we don't pick up system libs because unfortunately they're for the host - // of llvm-config, not the target that we're attempting to link. - let mut cmd = Command::new(&llvm_config); - cmd.arg(llvm_link_arg).arg("--libs"); - - if !is_crossed { - cmd.arg("--system-libs"); - } else if target.contains("windows-gnu") { - println!("cargo:rustc-link-lib=shell32"); - println!("cargo:rustc-link-lib=uuid"); - } else if target.contains("netbsd") || target.contains("haiku") { - println!("cargo:rustc-link-lib=z"); - } - cmd.args(&components); - - for lib in output(&mut cmd).split_whitespace() { - let name = if lib.starts_with("-l") { - &lib[2..] - } else if lib.starts_with('-') { - &lib[1..] - } else if Path::new(lib).exists() { - // On MSVC llvm-config will print the full name to libraries, but - // we're only interested in the name part - let name = Path::new(lib).file_name().unwrap().to_str().unwrap(); - name.trim_end_matches(".lib") - } else if lib.ends_with(".lib") { - // Some MSVC libraries just come up with `.lib` tacked on, so chop - // that off - lib.trim_end_matches(".lib") - } else { - continue; - }; - - // Don't need or want this library, but LLVM's CMake build system - // doesn't provide a way to disable it, so filter it here even though we - // may or may not have built it. We don't reference anything from this - // library and it otherwise may just pull in extra dependencies on - // libedit which we don't want - if name == "LLVMLineEditor" { - continue; - } - - let kind = if name.starts_with("LLVM") { llvm_kind } else { "dylib" }; - println!("cargo:rustc-link-lib={}={}", kind, name); - } - - // LLVM ldflags - // - // If we're a cross-compile of LLVM then unfortunately we can't trust these - // ldflags (largely where all the LLVM libs are located). Currently just - // hack around this by replacing the host triple with the target and pray - // that those -L directories are the same! - let mut cmd = Command::new(&llvm_config); - cmd.arg(llvm_link_arg).arg("--ldflags"); - for lib in output(&mut cmd).split_whitespace() { - if is_crossed { - if lib.starts_with("-LIBPATH:") { - println!("cargo:rustc-link-search=native={}", lib[9..].replace(&host, &target)); - } else if lib.starts_with("-L") { - println!("cargo:rustc-link-search=native={}", lib[2..].replace(&host, &target)); - } - } else if lib.starts_with("-LIBPATH:") { - println!("cargo:rustc-link-search=native={}", &lib[9..]); - } else if lib.starts_with("-l") { - println!("cargo:rustc-link-lib={}", &lib[2..]); - } else if lib.starts_with("-L") { - println!("cargo:rustc-link-search=native={}", &lib[2..]); - } - } - - // Some LLVM linker flags (-L and -l) may be needed even when linking - // librustc_llvm, for example when using static libc++, we may need to - // manually specify the library search path and -ldl -lpthread as link - // dependencies. - let llvm_linker_flags = tracked_env_var_os("LLVM_LINKER_FLAGS"); - if let Some(s) = llvm_linker_flags { - for lib in s.into_string().unwrap().split_whitespace() { - if lib.starts_with("-l") { - println!("cargo:rustc-link-lib={}", &lib[2..]); - } else if lib.starts_with("-L") { - println!("cargo:rustc-link-search=native={}", &lib[2..]); - } - } - } - - let llvm_static_stdcpp = tracked_env_var_os("LLVM_STATIC_STDCPP"); - let llvm_use_libcxx = tracked_env_var_os("LLVM_USE_LIBCXX"); - - let stdcppname = if target.contains("openbsd") { - if target.contains("sparc64") { "estdc++" } else { "c++" } - } else if target.contains("freebsd") { - "c++" - } else if target.contains("darwin") { - "c++" - } else if target.contains("netbsd") && llvm_static_stdcpp.is_some() { - // NetBSD uses a separate library when relocation is required - "stdc++_pic" - } else if llvm_use_libcxx.is_some() { - "c++" - } else { - "stdc++" - }; - - // RISC-V requires libatomic for sub-word atomic operations - if target.starts_with("riscv") { - println!("cargo:rustc-link-lib=atomic"); - } - - // C++ runtime library - if !target.contains("msvc") { - if let Some(s) = llvm_static_stdcpp { - assert!(!cxxflags.contains("stdlib=libc++")); - let path = PathBuf::from(s); - println!("cargo:rustc-link-search=native={}", path.parent().unwrap().display()); - if target.contains("windows") { - println!("cargo:rustc-link-lib=static-nobundle={}", stdcppname); - } else { - println!("cargo:rustc-link-lib=static={}", stdcppname); - } - } else if cxxflags.contains("stdlib=libc++") { - println!("cargo:rustc-link-lib=c++"); - } else { - println!("cargo:rustc-link-lib={}", stdcppname); - } - } - - // Libstdc++ depends on pthread which Rust doesn't link on MinGW - // since nothing else requires it. - if target.contains("windows-gnu") { - println!("cargo:rustc-link-lib=static-nobundle=pthread"); - } -} diff --git a/src/librustc_llvm/lib.rs b/src/librustc_llvm/lib.rs deleted file mode 100644 index 9d23397ade0..00000000000 --- a/src/librustc_llvm/lib.rs +++ /dev/null @@ -1,173 +0,0 @@ -#![feature(nll)] -#![feature(static_nobundle)] -#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] - -// NOTE: This crate only exists to allow linking on mingw targets. - -use libc::{c_char, size_t}; -use std::cell::RefCell; -use std::slice; - -#[repr(C)] -pub struct RustString { - pub bytes: RefCell>, -} - -impl RustString { - pub fn len(&self) -> usize { - self.bytes.borrow().len() - } -} - -/// Appending to a Rust string -- used by RawRustStringOstream. -#[no_mangle] -#[allow(improper_ctypes_definitions)] -pub unsafe extern "C" fn LLVMRustStringWriteImpl( - sr: &RustString, - ptr: *const c_char, - size: size_t, -) { - let slice = slice::from_raw_parts(ptr as *const u8, size as usize); - - sr.bytes.borrow_mut().extend_from_slice(slice); -} - -/// Initialize targets enabled by the build script via `cfg(llvm_component = "...")`. -/// N.B., this function can't be moved to `rustc_codegen_llvm` because of the `cfg`s. -pub fn initialize_available_targets() { - macro_rules! init_target( - ($cfg:meta, $($method:ident),*) => { { - #[cfg($cfg)] - fn init() { - extern { - $(fn $method();)* - } - unsafe { - $($method();)* - } - } - #[cfg(not($cfg))] - fn init() { } - init(); - } } - ); - init_target!( - llvm_component = "x86", - LLVMInitializeX86TargetInfo, - LLVMInitializeX86Target, - LLVMInitializeX86TargetMC, - LLVMInitializeX86AsmPrinter, - LLVMInitializeX86AsmParser - ); - init_target!( - llvm_component = "arm", - LLVMInitializeARMTargetInfo, - LLVMInitializeARMTarget, - LLVMInitializeARMTargetMC, - LLVMInitializeARMAsmPrinter, - LLVMInitializeARMAsmParser - ); - init_target!( - llvm_component = "aarch64", - LLVMInitializeAArch64TargetInfo, - LLVMInitializeAArch64Target, - LLVMInitializeAArch64TargetMC, - LLVMInitializeAArch64AsmPrinter, - LLVMInitializeAArch64AsmParser - ); - init_target!( - llvm_component = "amdgpu", - LLVMInitializeAMDGPUTargetInfo, - LLVMInitializeAMDGPUTarget, - LLVMInitializeAMDGPUTargetMC, - LLVMInitializeAMDGPUAsmPrinter, - LLVMInitializeAMDGPUAsmParser - ); - init_target!( - llvm_component = "avr", - LLVMInitializeAVRTargetInfo, - LLVMInitializeAVRTarget, - LLVMInitializeAVRTargetMC, - LLVMInitializeAVRAsmPrinter, - LLVMInitializeAVRAsmParser - ); - init_target!( - llvm_component = "mips", - LLVMInitializeMipsTargetInfo, - LLVMInitializeMipsTarget, - LLVMInitializeMipsTargetMC, - LLVMInitializeMipsAsmPrinter, - LLVMInitializeMipsAsmParser - ); - init_target!( - llvm_component = "powerpc", - LLVMInitializePowerPCTargetInfo, - LLVMInitializePowerPCTarget, - LLVMInitializePowerPCTargetMC, - LLVMInitializePowerPCAsmPrinter, - LLVMInitializePowerPCAsmParser - ); - init_target!( - llvm_component = "systemz", - LLVMInitializeSystemZTargetInfo, - LLVMInitializeSystemZTarget, - LLVMInitializeSystemZTargetMC, - LLVMInitializeSystemZAsmPrinter, - LLVMInitializeSystemZAsmParser - ); - init_target!( - llvm_component = "jsbackend", - LLVMInitializeJSBackendTargetInfo, - LLVMInitializeJSBackendTarget, - LLVMInitializeJSBackendTargetMC - ); - init_target!( - llvm_component = "msp430", - LLVMInitializeMSP430TargetInfo, - LLVMInitializeMSP430Target, - LLVMInitializeMSP430TargetMC, - LLVMInitializeMSP430AsmPrinter - ); - init_target!( - all(llvm_component = "msp430", llvm_has_msp430_asm_parser), - LLVMInitializeMSP430AsmParser - ); - init_target!( - llvm_component = "riscv", - LLVMInitializeRISCVTargetInfo, - LLVMInitializeRISCVTarget, - LLVMInitializeRISCVTargetMC, - LLVMInitializeRISCVAsmPrinter, - LLVMInitializeRISCVAsmParser - ); - init_target!( - llvm_component = "sparc", - LLVMInitializeSparcTargetInfo, - LLVMInitializeSparcTarget, - LLVMInitializeSparcTargetMC, - LLVMInitializeSparcAsmPrinter, - LLVMInitializeSparcAsmParser - ); - init_target!( - llvm_component = "nvptx", - LLVMInitializeNVPTXTargetInfo, - LLVMInitializeNVPTXTarget, - LLVMInitializeNVPTXTargetMC, - LLVMInitializeNVPTXAsmPrinter - ); - init_target!( - llvm_component = "hexagon", - LLVMInitializeHexagonTargetInfo, - LLVMInitializeHexagonTarget, - LLVMInitializeHexagonTargetMC, - LLVMInitializeHexagonAsmPrinter, - LLVMInitializeHexagonAsmParser - ); - init_target!( - llvm_component = "webassembly", - LLVMInitializeWebAssemblyTargetInfo, - LLVMInitializeWebAssemblyTarget, - LLVMInitializeWebAssemblyTargetMC, - LLVMInitializeWebAssemblyAsmPrinter - ); -} diff --git a/src/rustllvm/.editorconfig b/src/rustllvm/.editorconfig deleted file mode 100644 index 865cd45f708..00000000000 --- a/src/rustllvm/.editorconfig +++ /dev/null @@ -1,6 +0,0 @@ -[*.{h,cpp}] -end_of_line = lf -insert_final_newline = true -charset = utf-8 -indent_style = space -indent_size = 2 diff --git a/src/rustllvm/ArchiveWrapper.cpp b/src/rustllvm/ArchiveWrapper.cpp deleted file mode 100644 index 9ce614fda57..00000000000 --- a/src/rustllvm/ArchiveWrapper.cpp +++ /dev/null @@ -1,226 +0,0 @@ -#include "rustllvm.h" - -#include "llvm/Object/Archive.h" -#include "llvm/Object/ArchiveWriter.h" -#include "llvm/Support/Path.h" - -using namespace llvm; -using namespace llvm::object; - -struct RustArchiveMember { - const char *Filename; - const char *Name; - Archive::Child Child; - - RustArchiveMember() - : Filename(nullptr), Name(nullptr), - Child(nullptr, nullptr, nullptr) - { - } - ~RustArchiveMember() {} -}; - -struct RustArchiveIterator { - bool First; - Archive::child_iterator Cur; - Archive::child_iterator End; - std::unique_ptr Err; - - RustArchiveIterator(Archive::child_iterator Cur, Archive::child_iterator End, - std::unique_ptr Err) - : First(true), - Cur(Cur), - End(End), - Err(std::move(Err)) {} -}; - -enum class LLVMRustArchiveKind { - GNU, - BSD, - DARWIN, - COFF, -}; - -static Archive::Kind fromRust(LLVMRustArchiveKind Kind) { - switch (Kind) { - case LLVMRustArchiveKind::GNU: - return Archive::K_GNU; - case LLVMRustArchiveKind::BSD: - return Archive::K_BSD; - case LLVMRustArchiveKind::DARWIN: - return Archive::K_DARWIN; - case LLVMRustArchiveKind::COFF: - return Archive::K_COFF; - default: - report_fatal_error("Bad ArchiveKind."); - } -} - -typedef OwningBinary *LLVMRustArchiveRef; -typedef RustArchiveMember *LLVMRustArchiveMemberRef; -typedef Archive::Child *LLVMRustArchiveChildRef; -typedef Archive::Child const *LLVMRustArchiveChildConstRef; -typedef RustArchiveIterator *LLVMRustArchiveIteratorRef; - -extern "C" LLVMRustArchiveRef LLVMRustOpenArchive(char *Path) { - ErrorOr> BufOr = - MemoryBuffer::getFile(Path, -1, false); - if (!BufOr) { - LLVMRustSetLastError(BufOr.getError().message().c_str()); - return nullptr; - } - - Expected> ArchiveOr = - Archive::create(BufOr.get()->getMemBufferRef()); - - if (!ArchiveOr) { - LLVMRustSetLastError(toString(ArchiveOr.takeError()).c_str()); - return nullptr; - } - - OwningBinary *Ret = new OwningBinary( - std::move(ArchiveOr.get()), std::move(BufOr.get())); - - return Ret; -} - -extern "C" void LLVMRustDestroyArchive(LLVMRustArchiveRef RustArchive) { - delete RustArchive; -} - -extern "C" LLVMRustArchiveIteratorRef -LLVMRustArchiveIteratorNew(LLVMRustArchiveRef RustArchive) { - Archive *Archive = RustArchive->getBinary(); -#if LLVM_VERSION_GE(10, 0) - std::unique_ptr Err = std::make_unique(Error::success()); -#else - std::unique_ptr Err = llvm::make_unique(Error::success()); -#endif - auto Cur = Archive->child_begin(*Err); - if (*Err) { - LLVMRustSetLastError(toString(std::move(*Err)).c_str()); - return nullptr; - } - auto End = Archive->child_end(); - return new RustArchiveIterator(Cur, End, std::move(Err)); -} - -extern "C" LLVMRustArchiveChildConstRef -LLVMRustArchiveIteratorNext(LLVMRustArchiveIteratorRef RAI) { - if (RAI->Cur == RAI->End) - return nullptr; - - // Advancing the iterator validates the next child, and this can - // uncover an error. LLVM requires that we check all Errors, - // so we only advance the iterator if we actually need to fetch - // the next child. - // This means we must not advance the iterator in the *first* call, - // but instead advance it *before* fetching the child in all later calls. - if (!RAI->First) { - ++RAI->Cur; - if (*RAI->Err) { - LLVMRustSetLastError(toString(std::move(*RAI->Err)).c_str()); - return nullptr; - } - } else { - RAI->First = false; - } - - if (RAI->Cur == RAI->End) - return nullptr; - - const Archive::Child &Child = *RAI->Cur.operator->(); - Archive::Child *Ret = new Archive::Child(Child); - - return Ret; -} - -extern "C" void LLVMRustArchiveChildFree(LLVMRustArchiveChildRef Child) { - delete Child; -} - -extern "C" void LLVMRustArchiveIteratorFree(LLVMRustArchiveIteratorRef RAI) { - delete RAI; -} - -extern "C" const char * -LLVMRustArchiveChildName(LLVMRustArchiveChildConstRef Child, size_t *Size) { - Expected NameOrErr = Child->getName(); - if (!NameOrErr) { - // rustc_codegen_llvm currently doesn't use this error string, but it might be - // useful in the future, and in the mean time this tells LLVM that the - // error was not ignored and that it shouldn't abort the process. - LLVMRustSetLastError(toString(NameOrErr.takeError()).c_str()); - return nullptr; - } - StringRef Name = NameOrErr.get(); - *Size = Name.size(); - return Name.data(); -} - -extern "C" const char *LLVMRustArchiveChildData(LLVMRustArchiveChildRef Child, - size_t *Size) { - StringRef Buf; - Expected BufOrErr = Child->getBuffer(); - if (!BufOrErr) { - LLVMRustSetLastError(toString(BufOrErr.takeError()).c_str()); - return nullptr; - } - Buf = BufOrErr.get(); - *Size = Buf.size(); - return Buf.data(); -} - -extern "C" LLVMRustArchiveMemberRef -LLVMRustArchiveMemberNew(char *Filename, char *Name, - LLVMRustArchiveChildRef Child) { - RustArchiveMember *Member = new RustArchiveMember; - Member->Filename = Filename; - Member->Name = Name; - if (Child) - Member->Child = *Child; - return Member; -} - -extern "C" void LLVMRustArchiveMemberFree(LLVMRustArchiveMemberRef Member) { - delete Member; -} - -extern "C" LLVMRustResult -LLVMRustWriteArchive(char *Dst, size_t NumMembers, - const LLVMRustArchiveMemberRef *NewMembers, - bool WriteSymbtab, LLVMRustArchiveKind RustKind) { - - std::vector Members; - auto Kind = fromRust(RustKind); - - for (size_t I = 0; I < NumMembers; I++) { - auto Member = NewMembers[I]; - assert(Member->Name); - if (Member->Filename) { - Expected MOrErr = - NewArchiveMember::getFile(Member->Filename, true); - if (!MOrErr) { - LLVMRustSetLastError(toString(MOrErr.takeError()).c_str()); - return LLVMRustResult::Failure; - } - MOrErr->MemberName = sys::path::filename(MOrErr->MemberName); - Members.push_back(std::move(*MOrErr)); - } else { - Expected MOrErr = - NewArchiveMember::getOldMember(Member->Child, true); - if (!MOrErr) { - LLVMRustSetLastError(toString(MOrErr.takeError()).c_str()); - return LLVMRustResult::Failure; - } - Members.push_back(std::move(*MOrErr)); - } - } - - auto Result = writeArchive(Dst, Members, WriteSymbtab, Kind, true, false); - if (!Result) - return LLVMRustResult::Success; - LLVMRustSetLastError(toString(std::move(Result)).c_str()); - - return LLVMRustResult::Failure; -} diff --git a/src/rustllvm/CoverageMappingWrapper.cpp b/src/rustllvm/CoverageMappingWrapper.cpp deleted file mode 100644 index 81aba0cbf7d..00000000000 --- a/src/rustllvm/CoverageMappingWrapper.cpp +++ /dev/null @@ -1,70 +0,0 @@ -#include "rustllvm.h" -#include "llvm/ProfileData/Coverage/CoverageMapping.h" -#include "llvm/ProfileData/Coverage/CoverageMappingWriter.h" -#include "llvm/ProfileData/InstrProf.h" -#include "llvm/ADT/ArrayRef.h" -#include "llvm/Support/LEB128.h" - -#include - -using namespace llvm; - -extern "C" void LLVMRustCoverageWriteFilenamesSectionToBuffer( - const char* const Filenames[], - size_t FilenamesLen, - RustStringRef BufferOut) { - // LLVM 11's CoverageFilenamesSectionWriter uses its new `Version4` format, - // so we're manually writing the `Version3` format ourselves. - RawRustStringOstream OS(BufferOut); - encodeULEB128(FilenamesLen, OS); - for (size_t i = 0; i < FilenamesLen; i++) { - StringRef Filename(Filenames[i]); - encodeULEB128(Filename.size(), OS); - OS << Filename; - } -} - -extern "C" void LLVMRustCoverageWriteMappingToBuffer( - const unsigned *VirtualFileMappingIDs, - unsigned NumVirtualFileMappingIDs, - const coverage::CounterExpression *Expressions, - unsigned NumExpressions, - coverage::CounterMappingRegion *MappingRegions, - unsigned NumMappingRegions, - RustStringRef BufferOut) { - auto CoverageMappingWriter = coverage::CoverageMappingWriter( - makeArrayRef(VirtualFileMappingIDs, NumVirtualFileMappingIDs), - makeArrayRef(Expressions, NumExpressions), - makeMutableArrayRef(MappingRegions, NumMappingRegions)); - RawRustStringOstream OS(BufferOut); - CoverageMappingWriter.write(OS); -} - -extern "C" LLVMValueRef LLVMRustCoverageCreatePGOFuncNameVar(LLVMValueRef F, const char *FuncName) { - StringRef FuncNameRef(FuncName); - return wrap(createPGOFuncNameVar(*cast(unwrap(F)), FuncNameRef)); -} - -extern "C" uint64_t LLVMRustCoverageComputeHash(const char *Name) { - StringRef NameRef(Name); - return IndexedInstrProf::ComputeHash(NameRef); -} - -extern "C" void LLVMRustCoverageWriteSectionNameToString(LLVMModuleRef M, - RustStringRef Str) { - Triple TargetTriple(unwrap(M)->getTargetTriple()); - auto name = getInstrProfSectionName(IPSK_covmap, - TargetTriple.getObjectFormat()); - RawRustStringOstream OS(Str); - OS << name; -} - -extern "C" void LLVMRustCoverageWriteMappingVarNameToString(RustStringRef Str) { - auto name = getCoverageMappingVarName(); - RawRustStringOstream OS(Str); - OS << name; -} - -extern "C" uint32_t LLVMRustCoverageMappingVersion() { - return coverage::CovMapVersion::Version3; -} diff --git a/src/rustllvm/Linker.cpp b/src/rustllvm/Linker.cpp deleted file mode 100644 index 69176f9cb1f..00000000000 --- a/src/rustllvm/Linker.cpp +++ /dev/null @@ -1,48 +0,0 @@ -#include "llvm/Linker/Linker.h" - -#include "rustllvm.h" - -using namespace llvm; - -struct RustLinker { - Linker L; - LLVMContext &Ctx; - - RustLinker(Module &M) : - L(M), - Ctx(M.getContext()) - {} -}; - -extern "C" RustLinker* -LLVMRustLinkerNew(LLVMModuleRef DstRef) { - Module *Dst = unwrap(DstRef); - - return new RustLinker(*Dst); -} - -extern "C" void -LLVMRustLinkerFree(RustLinker *L) { - delete L; -} - -extern "C" bool -LLVMRustLinkerAdd(RustLinker *L, char *BC, size_t Len) { - std::unique_ptr Buf = - MemoryBuffer::getMemBufferCopy(StringRef(BC, Len)); - - Expected> SrcOrError = - llvm::getLazyBitcodeModule(Buf->getMemBufferRef(), L->Ctx); - if (!SrcOrError) { - LLVMRustSetLastError(toString(SrcOrError.takeError()).c_str()); - return false; - } - - auto Src = std::move(*SrcOrError); - - if (L->L.linkInModule(std::move(Src))) { - LLVMRustSetLastError(""); - return false; - } - return true; -} diff --git a/src/rustllvm/PassWrapper.cpp b/src/rustllvm/PassWrapper.cpp deleted file mode 100644 index 76fe5e7f769..00000000000 --- a/src/rustllvm/PassWrapper.cpp +++ /dev/null @@ -1,1655 +0,0 @@ -#include - -#include -#include - -#include "rustllvm.h" - -#include "llvm/Analysis/TargetLibraryInfo.h" -#include "llvm/Analysis/TargetTransformInfo.h" -#include "llvm/CodeGen/TargetSubtargetInfo.h" -#include "llvm/InitializePasses.h" -#include "llvm/IR/AutoUpgrade.h" -#include "llvm/IR/AssemblyAnnotationWriter.h" -#include "llvm/IR/IntrinsicInst.h" -#include "llvm/IR/Verifier.h" -#include "llvm/Object/ObjectFile.h" -#include "llvm/Object/IRObjectFile.h" -#include "llvm/Passes/PassBuilder.h" -#if LLVM_VERSION_GE(9, 0) -#include "llvm/Passes/StandardInstrumentations.h" -#endif -#include "llvm/Support/CBindingWrapping.h" -#include "llvm/Support/FileSystem.h" -#include "llvm/Support/Host.h" -#include "llvm/Target/TargetMachine.h" -#include "llvm/Transforms/IPO/PassManagerBuilder.h" -#include "llvm/Transforms/IPO/AlwaysInliner.h" -#include "llvm/Transforms/IPO/FunctionImport.h" -#include "llvm/Transforms/Utils/FunctionImportUtils.h" -#include "llvm/LTO/LTO.h" -#include "llvm-c/Transforms/PassManagerBuilder.h" - -#include "llvm/Transforms/Instrumentation.h" -#if LLVM_VERSION_GE(9, 0) -#include "llvm/Transforms/Instrumentation/AddressSanitizer.h" -#include "llvm/Support/TimeProfiler.h" -#endif -#include "llvm/Transforms/Instrumentation/ThreadSanitizer.h" -#include "llvm/Transforms/Instrumentation/MemorySanitizer.h" -#if LLVM_VERSION_GE(9, 0) -#include "llvm/Transforms/Utils/CanonicalizeAliases.h" -#endif -#include "llvm/Transforms/Utils/NameAnonGlobals.h" - -using namespace llvm; - -typedef struct LLVMOpaquePass *LLVMPassRef; -typedef struct LLVMOpaqueTargetMachine *LLVMTargetMachineRef; - -DEFINE_STDCXX_CONVERSION_FUNCTIONS(Pass, LLVMPassRef) -DEFINE_STDCXX_CONVERSION_FUNCTIONS(TargetMachine, LLVMTargetMachineRef) -#if LLVM_VERSION_LT(11, 0) -DEFINE_STDCXX_CONVERSION_FUNCTIONS(PassManagerBuilder, - LLVMPassManagerBuilderRef) -#endif - -extern "C" void LLVMInitializePasses() { - PassRegistry &Registry = *PassRegistry::getPassRegistry(); - initializeCore(Registry); - initializeCodeGen(Registry); - initializeScalarOpts(Registry); - initializeVectorization(Registry); - initializeIPO(Registry); - initializeAnalysis(Registry); - initializeTransformUtils(Registry); - initializeInstCombine(Registry); - initializeInstrumentation(Registry); - initializeTarget(Registry); -} - -extern "C" void LLVMTimeTraceProfilerInitialize() { -#if LLVM_VERSION_GE(10, 0) - timeTraceProfilerInitialize( - /* TimeTraceGranularity */ 0, - /* ProcName */ "rustc"); -#elif LLVM_VERSION_GE(9, 0) - timeTraceProfilerInitialize(); -#endif -} - -extern "C" void LLVMTimeTraceProfilerFinish(const char* FileName) { -#if LLVM_VERSION_GE(9, 0) - StringRef FN(FileName); - std::error_code EC; - raw_fd_ostream OS(FN, EC, sys::fs::CD_CreateAlways); - - timeTraceProfilerWrite(OS); - timeTraceProfilerCleanup(); -#endif -} - -enum class LLVMRustPassKind { - Other, - Function, - Module, -}; - -static LLVMRustPassKind toRust(PassKind Kind) { - switch (Kind) { - case PT_Function: - return LLVMRustPassKind::Function; - case PT_Module: - return LLVMRustPassKind::Module; - default: - return LLVMRustPassKind::Other; - } -} - -extern "C" LLVMPassRef LLVMRustFindAndCreatePass(const char *PassName) { - StringRef SR(PassName); - PassRegistry *PR = PassRegistry::getPassRegistry(); - - const PassInfo *PI = PR->getPassInfo(SR); - if (PI) { - return wrap(PI->createPass()); - } - return nullptr; -} - -extern "C" LLVMPassRef LLVMRustCreateAddressSanitizerFunctionPass(bool Recover) { - const bool CompileKernel = false; - const bool UseAfterScope = true; - - return wrap(createAddressSanitizerFunctionPass(CompileKernel, Recover, UseAfterScope)); -} - -extern "C" LLVMPassRef LLVMRustCreateModuleAddressSanitizerPass(bool Recover) { - const bool CompileKernel = false; - -#if LLVM_VERSION_GE(9, 0) - return wrap(createModuleAddressSanitizerLegacyPassPass(CompileKernel, Recover)); -#else - return wrap(createAddressSanitizerModulePass(CompileKernel, Recover)); -#endif -} - -extern "C" LLVMPassRef LLVMRustCreateMemorySanitizerPass(int TrackOrigins, bool Recover) { -#if LLVM_VERSION_GE(9, 0) - const bool CompileKernel = false; - - return wrap(createMemorySanitizerLegacyPassPass( - MemorySanitizerOptions{TrackOrigins, Recover, CompileKernel})); -#else - return wrap(createMemorySanitizerLegacyPassPass(TrackOrigins, Recover)); -#endif -} - -extern "C" LLVMPassRef LLVMRustCreateThreadSanitizerPass() { - return wrap(createThreadSanitizerLegacyPassPass()); -} - -extern "C" LLVMRustPassKind LLVMRustPassKind(LLVMPassRef RustPass) { - assert(RustPass); - Pass *Pass = unwrap(RustPass); - return toRust(Pass->getPassKind()); -} - -extern "C" void LLVMRustAddPass(LLVMPassManagerRef PMR, LLVMPassRef RustPass) { - assert(RustPass); - Pass *Pass = unwrap(RustPass); - PassManagerBase *PMB = unwrap(PMR); - PMB->add(Pass); -} - -extern "C" -void LLVMRustPassManagerBuilderPopulateThinLTOPassManager( - LLVMPassManagerBuilderRef PMBR, - LLVMPassManagerRef PMR -) { - unwrap(PMBR)->populateThinLTOPassManager(*unwrap(PMR)); -} - -extern "C" -void LLVMRustAddLastExtensionPasses( - LLVMPassManagerBuilderRef PMBR, LLVMPassRef *Passes, size_t NumPasses) { - auto AddExtensionPasses = [Passes, NumPasses]( - const PassManagerBuilder &Builder, PassManagerBase &PM) { - for (size_t I = 0; I < NumPasses; I++) { - PM.add(unwrap(Passes[I])); - } - }; - // Add the passes to both of the pre-finalization extension points, - // so they are run for optimized and non-optimized builds. - unwrap(PMBR)->addExtension(PassManagerBuilder::EP_OptimizerLast, - AddExtensionPasses); - unwrap(PMBR)->addExtension(PassManagerBuilder::EP_EnabledOnOptLevel0, - AddExtensionPasses); -} - -#ifdef LLVM_COMPONENT_X86 -#define SUBTARGET_X86 SUBTARGET(X86) -#else -#define SUBTARGET_X86 -#endif - -#ifdef LLVM_COMPONENT_ARM -#define SUBTARGET_ARM SUBTARGET(ARM) -#else -#define SUBTARGET_ARM -#endif - -#ifdef LLVM_COMPONENT_AARCH64 -#define SUBTARGET_AARCH64 SUBTARGET(AArch64) -#else -#define SUBTARGET_AARCH64 -#endif - -#ifdef LLVM_COMPONENT_AVR -#define SUBTARGET_AVR SUBTARGET(AVR) -#else -#define SUBTARGET_AVR -#endif - -#ifdef LLVM_COMPONENT_MIPS -#define SUBTARGET_MIPS SUBTARGET(Mips) -#else -#define SUBTARGET_MIPS -#endif - -#ifdef LLVM_COMPONENT_POWERPC -#define SUBTARGET_PPC SUBTARGET(PPC) -#else -#define SUBTARGET_PPC -#endif - -#ifdef LLVM_COMPONENT_SYSTEMZ -#define SUBTARGET_SYSTEMZ SUBTARGET(SystemZ) -#else -#define SUBTARGET_SYSTEMZ -#endif - -#ifdef LLVM_COMPONENT_MSP430 -#define SUBTARGET_MSP430 SUBTARGET(MSP430) -#else -#define SUBTARGET_MSP430 -#endif - -#ifdef LLVM_COMPONENT_RISCV -#define SUBTARGET_RISCV SUBTARGET(RISCV) -#else -#define SUBTARGET_RISCV -#endif - -#ifdef LLVM_COMPONENT_SPARC -#define SUBTARGET_SPARC SUBTARGET(Sparc) -#else -#define SUBTARGET_SPARC -#endif - -#ifdef LLVM_COMPONENT_HEXAGON -#define SUBTARGET_HEXAGON SUBTARGET(Hexagon) -#else -#define SUBTARGET_HEXAGON -#endif - -#define GEN_SUBTARGETS \ - SUBTARGET_X86 \ - SUBTARGET_ARM \ - SUBTARGET_AARCH64 \ - SUBTARGET_AVR \ - SUBTARGET_MIPS \ - SUBTARGET_PPC \ - SUBTARGET_SYSTEMZ \ - SUBTARGET_MSP430 \ - SUBTARGET_SPARC \ - SUBTARGET_HEXAGON \ - SUBTARGET_RISCV \ - -#define SUBTARGET(x) \ - namespace llvm { \ - extern const SubtargetFeatureKV x##FeatureKV[]; \ - extern const SubtargetFeatureKV x##SubTypeKV[]; \ - } - -GEN_SUBTARGETS -#undef SUBTARGET - -extern "C" bool LLVMRustHasFeature(LLVMTargetMachineRef TM, - const char *Feature) { - TargetMachine *Target = unwrap(TM); - const MCSubtargetInfo *MCInfo = Target->getMCSubtargetInfo(); - return MCInfo->checkFeatures(std::string("+") + Feature); -} - -enum class LLVMRustCodeModel { - Tiny, - Small, - Kernel, - Medium, - Large, - None, -}; - -static Optional fromRust(LLVMRustCodeModel Model) { - switch (Model) { - case LLVMRustCodeModel::Tiny: - return CodeModel::Tiny; - case LLVMRustCodeModel::Small: - return CodeModel::Small; - case LLVMRustCodeModel::Kernel: - return CodeModel::Kernel; - case LLVMRustCodeModel::Medium: - return CodeModel::Medium; - case LLVMRustCodeModel::Large: - return CodeModel::Large; - case LLVMRustCodeModel::None: - return None; - default: - report_fatal_error("Bad CodeModel."); - } -} - -enum class LLVMRustCodeGenOptLevel { - None, - Less, - Default, - Aggressive, -}; - -static CodeGenOpt::Level fromRust(LLVMRustCodeGenOptLevel Level) { - switch (Level) { - case LLVMRustCodeGenOptLevel::None: - return CodeGenOpt::None; - case LLVMRustCodeGenOptLevel::Less: - return CodeGenOpt::Less; - case LLVMRustCodeGenOptLevel::Default: - return CodeGenOpt::Default; - case LLVMRustCodeGenOptLevel::Aggressive: - return CodeGenOpt::Aggressive; - default: - report_fatal_error("Bad CodeGenOptLevel."); - } -} - -enum class LLVMRustPassBuilderOptLevel { - O0, - O1, - O2, - O3, - Os, - Oz, -}; - -static PassBuilder::OptimizationLevel fromRust(LLVMRustPassBuilderOptLevel Level) { - switch (Level) { - case LLVMRustPassBuilderOptLevel::O0: - return PassBuilder::OptimizationLevel::O0; - case LLVMRustPassBuilderOptLevel::O1: - return PassBuilder::OptimizationLevel::O1; - case LLVMRustPassBuilderOptLevel::O2: - return PassBuilder::OptimizationLevel::O2; - case LLVMRustPassBuilderOptLevel::O3: - return PassBuilder::OptimizationLevel::O3; - case LLVMRustPassBuilderOptLevel::Os: - return PassBuilder::OptimizationLevel::Os; - case LLVMRustPassBuilderOptLevel::Oz: - return PassBuilder::OptimizationLevel::Oz; - default: - report_fatal_error("Bad PassBuilderOptLevel."); - } -} - -enum class LLVMRustRelocModel { - Static, - PIC, - DynamicNoPic, - ROPI, - RWPI, - ROPIRWPI, -}; - -static Reloc::Model fromRust(LLVMRustRelocModel RustReloc) { - switch (RustReloc) { - case LLVMRustRelocModel::Static: - return Reloc::Static; - case LLVMRustRelocModel::PIC: - return Reloc::PIC_; - case LLVMRustRelocModel::DynamicNoPic: - return Reloc::DynamicNoPIC; - case LLVMRustRelocModel::ROPI: - return Reloc::ROPI; - case LLVMRustRelocModel::RWPI: - return Reloc::RWPI; - case LLVMRustRelocModel::ROPIRWPI: - return Reloc::ROPI_RWPI; - } - report_fatal_error("Bad RelocModel."); -} - -#ifdef LLVM_RUSTLLVM -/// getLongestEntryLength - Return the length of the longest entry in the table. -template -static size_t getLongestEntryLength(ArrayRef Table) { - size_t MaxLen = 0; - for (auto &I : Table) - MaxLen = std::max(MaxLen, std::strlen(I.Key)); - return MaxLen; -} - -extern "C" void LLVMRustPrintTargetCPUs(LLVMTargetMachineRef TM) { - const TargetMachine *Target = unwrap(TM); - const MCSubtargetInfo *MCInfo = Target->getMCSubtargetInfo(); - const Triple::ArchType HostArch = Triple(sys::getProcessTriple()).getArch(); - const Triple::ArchType TargetArch = Target->getTargetTriple().getArch(); - const ArrayRef CPUTable = MCInfo->getCPUTable(); - unsigned MaxCPULen = getLongestEntryLength(CPUTable); - - printf("Available CPUs for this target:\n"); - if (HostArch == TargetArch) { - const StringRef HostCPU = sys::getHostCPUName(); - printf(" %-*s - Select the CPU of the current host (currently %.*s).\n", - MaxCPULen, "native", (int)HostCPU.size(), HostCPU.data()); - } - for (auto &CPU : CPUTable) - printf(" %-*s\n", MaxCPULen, CPU.Key); - printf("\n"); -} - -extern "C" void LLVMRustPrintTargetFeatures(LLVMTargetMachineRef TM) { - const TargetMachine *Target = unwrap(TM); - const MCSubtargetInfo *MCInfo = Target->getMCSubtargetInfo(); - const ArrayRef FeatTable = MCInfo->getFeatureTable(); - unsigned MaxFeatLen = getLongestEntryLength(FeatTable); - - printf("Available features for this target:\n"); - for (auto &Feature : FeatTable) - printf(" %-*s - %s.\n", MaxFeatLen, Feature.Key, Feature.Desc); - printf("\nRust-specific features:\n"); - printf(" %-*s - %s.\n", - MaxFeatLen, - "crt-static", - "Enables libraries with C Run-time Libraries(CRT) to be statically linked" - ); - printf("\n"); - - printf("Use +feature to enable a feature, or -feature to disable it.\n" - "For example, rustc -C -target-cpu=mycpu -C " - "target-feature=+feature1,-feature2\n\n"); -} - -#else - -extern "C" void LLVMRustPrintTargetCPUs(LLVMTargetMachineRef) { - printf("Target CPU help is not supported by this LLVM version.\n\n"); -} - -extern "C" void LLVMRustPrintTargetFeatures(LLVMTargetMachineRef) { - printf("Target features help is not supported by this LLVM version.\n\n"); -} -#endif - -extern "C" const char* LLVMRustGetHostCPUName(size_t *len) { - StringRef Name = sys::getHostCPUName(); - *len = Name.size(); - return Name.data(); -} - -extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine( - const char *TripleStr, const char *CPU, const char *Feature, - const char *ABIStr, LLVMRustCodeModel RustCM, LLVMRustRelocModel RustReloc, - LLVMRustCodeGenOptLevel RustOptLevel, bool UseSoftFloat, - bool FunctionSections, - bool DataSections, - bool TrapUnreachable, - bool Singlethread, - bool AsmComments, - bool EmitStackSizeSection, - bool RelaxELFRelocations, - bool UseInitArray) { - - auto OptLevel = fromRust(RustOptLevel); - auto RM = fromRust(RustReloc); - auto CM = fromRust(RustCM); - - std::string Error; - Triple Trip(Triple::normalize(TripleStr)); - const llvm::Target *TheTarget = - TargetRegistry::lookupTarget(Trip.getTriple(), Error); - if (TheTarget == nullptr) { - LLVMRustSetLastError(Error.c_str()); - return nullptr; - } - - TargetOptions Options; - - Options.FloatABIType = FloatABI::Default; - if (UseSoftFloat) { - Options.FloatABIType = FloatABI::Soft; - } - Options.DataSections = DataSections; - Options.FunctionSections = FunctionSections; - Options.MCOptions.AsmVerbose = AsmComments; - Options.MCOptions.PreserveAsmComments = AsmComments; - Options.MCOptions.ABIName = ABIStr; - Options.RelaxELFRelocations = RelaxELFRelocations; - Options.UseInitArray = UseInitArray; - - if (TrapUnreachable) { - // Tell LLVM to codegen `unreachable` into an explicit trap instruction. - // This limits the extent of possible undefined behavior in some cases, as - // it prevents control flow from "falling through" into whatever code - // happens to be laid out next in memory. - Options.TrapUnreachable = true; - } - - if (Singlethread) { - Options.ThreadModel = ThreadModel::Single; - } - - Options.EmitStackSizeSection = EmitStackSizeSection; - - TargetMachine *TM = TheTarget->createTargetMachine( - Trip.getTriple(), CPU, Feature, Options, RM, CM, OptLevel); - return wrap(TM); -} - -extern "C" void LLVMRustDisposeTargetMachine(LLVMTargetMachineRef TM) { - delete unwrap(TM); -} - -extern "C" void LLVMRustConfigurePassManagerBuilder( - LLVMPassManagerBuilderRef PMBR, LLVMRustCodeGenOptLevel OptLevel, - bool MergeFunctions, bool SLPVectorize, bool LoopVectorize, bool PrepareForThinLTO, - const char* PGOGenPath, const char* PGOUsePath) { - unwrap(PMBR)->MergeFunctions = MergeFunctions; - unwrap(PMBR)->SLPVectorize = SLPVectorize; - unwrap(PMBR)->OptLevel = fromRust(OptLevel); - unwrap(PMBR)->LoopVectorize = LoopVectorize; - unwrap(PMBR)->PrepareForThinLTO = PrepareForThinLTO; - - if (PGOGenPath) { - assert(!PGOUsePath); - unwrap(PMBR)->EnablePGOInstrGen = true; - unwrap(PMBR)->PGOInstrGen = PGOGenPath; - } - if (PGOUsePath) { - assert(!PGOGenPath); - unwrap(PMBR)->PGOInstrUse = PGOUsePath; - } -} - -// Unfortunately, the LLVM C API doesn't provide a way to set the `LibraryInfo` -// field of a PassManagerBuilder, we expose our own method of doing so. -extern "C" void LLVMRustAddBuilderLibraryInfo(LLVMPassManagerBuilderRef PMBR, - LLVMModuleRef M, - bool DisableSimplifyLibCalls) { - Triple TargetTriple(unwrap(M)->getTargetTriple()); - TargetLibraryInfoImpl *TLI = new TargetLibraryInfoImpl(TargetTriple); - if (DisableSimplifyLibCalls) - TLI->disableAllFunctions(); - unwrap(PMBR)->LibraryInfo = TLI; -} - -// Unfortunately, the LLVM C API doesn't provide a way to create the -// TargetLibraryInfo pass, so we use this method to do so. -extern "C" void LLVMRustAddLibraryInfo(LLVMPassManagerRef PMR, LLVMModuleRef M, - bool DisableSimplifyLibCalls) { - Triple TargetTriple(unwrap(M)->getTargetTriple()); - TargetLibraryInfoImpl TLII(TargetTriple); - if (DisableSimplifyLibCalls) - TLII.disableAllFunctions(); - unwrap(PMR)->add(new TargetLibraryInfoWrapperPass(TLII)); -} - -// Unfortunately, the LLVM C API doesn't provide an easy way of iterating over -// all the functions in a module, so we do that manually here. You'll find -// similar code in clang's BackendUtil.cpp file. -extern "C" void LLVMRustRunFunctionPassManager(LLVMPassManagerRef PMR, - LLVMModuleRef M) { - llvm::legacy::FunctionPassManager *P = - unwrap(PMR); - P->doInitialization(); - - // Upgrade all calls to old intrinsics first. - for (Module::iterator I = unwrap(M)->begin(), E = unwrap(M)->end(); I != E;) - UpgradeCallsToIntrinsic(&*I++); // must be post-increment, as we remove - - for (Module::iterator I = unwrap(M)->begin(), E = unwrap(M)->end(); I != E; - ++I) - if (!I->isDeclaration()) - P->run(*I); - - P->doFinalization(); -} - -extern "C" void LLVMRustSetLLVMOptions(int Argc, char **Argv) { - // Initializing the command-line options more than once is not allowed. So, - // check if they've already been initialized. (This could happen if we're - // being called from rustpkg, for example). If the arguments change, then - // that's just kinda unfortunate. - static bool Initialized = false; - if (Initialized) - return; - Initialized = true; - cl::ParseCommandLineOptions(Argc, Argv); -} - -enum class LLVMRustFileType { - AssemblyFile, - ObjectFile, -}; - -#if LLVM_VERSION_GE(10, 0) -static CodeGenFileType fromRust(LLVMRustFileType Type) { - switch (Type) { - case LLVMRustFileType::AssemblyFile: - return CGFT_AssemblyFile; - case LLVMRustFileType::ObjectFile: - return CGFT_ObjectFile; - default: - report_fatal_error("Bad FileType."); - } -} -#else -static TargetMachine::CodeGenFileType fromRust(LLVMRustFileType Type) { - switch (Type) { - case LLVMRustFileType::AssemblyFile: - return TargetMachine::CGFT_AssemblyFile; - case LLVMRustFileType::ObjectFile: - return TargetMachine::CGFT_ObjectFile; - default: - report_fatal_error("Bad FileType."); - } -} -#endif - -extern "C" LLVMRustResult -LLVMRustWriteOutputFile(LLVMTargetMachineRef Target, LLVMPassManagerRef PMR, - LLVMModuleRef M, const char *Path, - LLVMRustFileType RustFileType) { - llvm::legacy::PassManager *PM = unwrap(PMR); - auto FileType = fromRust(RustFileType); - - std::string ErrorInfo; - std::error_code EC; - raw_fd_ostream OS(Path, EC, sys::fs::F_None); - if (EC) - ErrorInfo = EC.message(); - if (ErrorInfo != "") { - LLVMRustSetLastError(ErrorInfo.c_str()); - return LLVMRustResult::Failure; - } - - buffer_ostream BOS(OS); - unwrap(Target)->addPassesToEmitFile(*PM, BOS, nullptr, FileType, false); - PM->run(*unwrap(M)); - - // Apparently `addPassesToEmitFile` adds a pointer to our on-the-stack output - // stream (OS), so the only real safe place to delete this is here? Don't we - // wish this was written in Rust? - LLVMDisposePassManager(PMR); - return LLVMRustResult::Success; -} - -extern "C" typedef void (*LLVMRustSelfProfileBeforePassCallback)(void*, // LlvmSelfProfiler - const char*, // pass name - const char*); // IR name -extern "C" typedef void (*LLVMRustSelfProfileAfterPassCallback)(void*); // LlvmSelfProfiler - -#if LLVM_VERSION_GE(9, 0) - -std::string LLVMRustwrappedIrGetName(const llvm::Any &WrappedIr) { - if (any_isa(WrappedIr)) - return any_cast(WrappedIr)->getName().str(); - if (any_isa(WrappedIr)) - return any_cast(WrappedIr)->getName().str(); - if (any_isa(WrappedIr)) - return any_cast(WrappedIr)->getName().str(); - if (any_isa(WrappedIr)) - return any_cast(WrappedIr)->getName(); - return ""; -} - - -void LLVMSelfProfileInitializeCallbacks( - PassInstrumentationCallbacks& PIC, void* LlvmSelfProfiler, - LLVMRustSelfProfileBeforePassCallback BeforePassCallback, - LLVMRustSelfProfileAfterPassCallback AfterPassCallback) { - PIC.registerBeforePassCallback([LlvmSelfProfiler, BeforePassCallback]( - StringRef Pass, llvm::Any Ir) { - std::string PassName = Pass.str(); - std::string IrName = LLVMRustwrappedIrGetName(Ir); - BeforePassCallback(LlvmSelfProfiler, PassName.c_str(), IrName.c_str()); - return true; - }); - - PIC.registerAfterPassCallback( - [LlvmSelfProfiler, AfterPassCallback](StringRef Pass, llvm::Any Ir) { - AfterPassCallback(LlvmSelfProfiler); - }); - - PIC.registerAfterPassInvalidatedCallback( - [LlvmSelfProfiler, AfterPassCallback](StringRef Pass) { - AfterPassCallback(LlvmSelfProfiler); - }); - - PIC.registerBeforeAnalysisCallback([LlvmSelfProfiler, BeforePassCallback]( - StringRef Pass, llvm::Any Ir) { - std::string PassName = Pass.str(); - std::string IrName = LLVMRustwrappedIrGetName(Ir); - BeforePassCallback(LlvmSelfProfiler, PassName.c_str(), IrName.c_str()); - }); - - PIC.registerAfterAnalysisCallback( - [LlvmSelfProfiler, AfterPassCallback](StringRef Pass, llvm::Any Ir) { - AfterPassCallback(LlvmSelfProfiler); - }); -} -#endif - -enum class LLVMRustOptStage { - PreLinkNoLTO, - PreLinkThinLTO, - PreLinkFatLTO, - ThinLTO, - FatLTO, -}; - -struct LLVMRustSanitizerOptions { - bool SanitizeAddress; - bool SanitizeAddressRecover; - bool SanitizeMemory; - bool SanitizeMemoryRecover; - int SanitizeMemoryTrackOrigins; - bool SanitizeThread; -}; - -extern "C" void -LLVMRustOptimizeWithNewPassManager( - LLVMModuleRef ModuleRef, - LLVMTargetMachineRef TMRef, - LLVMRustPassBuilderOptLevel OptLevelRust, - LLVMRustOptStage OptStage, - bool NoPrepopulatePasses, bool VerifyIR, bool UseThinLTOBuffers, - bool MergeFunctions, bool UnrollLoops, bool SLPVectorize, bool LoopVectorize, - bool DisableSimplifyLibCalls, bool EmitLifetimeMarkers, - LLVMRustSanitizerOptions *SanitizerOptions, - const char *PGOGenPath, const char *PGOUsePath, - void* LlvmSelfProfiler, - LLVMRustSelfProfileBeforePassCallback BeforePassCallback, - LLVMRustSelfProfileAfterPassCallback AfterPassCallback) { -#if LLVM_VERSION_GE(9, 0) - Module *TheModule = unwrap(ModuleRef); - TargetMachine *TM = unwrap(TMRef); - PassBuilder::OptimizationLevel OptLevel = fromRust(OptLevelRust); - - // FIXME: MergeFunctions is not supported by NewPM yet. - (void) MergeFunctions; - - PipelineTuningOptions PTO; - PTO.LoopUnrolling = UnrollLoops; - PTO.LoopInterleaving = UnrollLoops; - PTO.LoopVectorization = LoopVectorize; - PTO.SLPVectorization = SLPVectorize; - - PassInstrumentationCallbacks PIC; - StandardInstrumentations SI; - SI.registerCallbacks(PIC); - - if (LlvmSelfProfiler){ - LLVMSelfProfileInitializeCallbacks(PIC,LlvmSelfProfiler,BeforePassCallback,AfterPassCallback); - } - - Optional PGOOpt; - if (PGOGenPath) { - assert(!PGOUsePath); - PGOOpt = PGOOptions(PGOGenPath, "", "", PGOOptions::IRInstr); - } else if (PGOUsePath) { - assert(!PGOGenPath); - PGOOpt = PGOOptions(PGOUsePath, "", "", PGOOptions::IRUse); - } - - PassBuilder PB(TM, PTO, PGOOpt, &PIC); - - // FIXME: We may want to expose this as an option. - bool DebugPassManager = false; - LoopAnalysisManager LAM(DebugPassManager); - FunctionAnalysisManager FAM(DebugPassManager); - CGSCCAnalysisManager CGAM(DebugPassManager); - ModuleAnalysisManager MAM(DebugPassManager); - - FAM.registerPass([&] { return PB.buildDefaultAAPipeline(); }); - - Triple TargetTriple(TheModule->getTargetTriple()); - std::unique_ptr TLII(new TargetLibraryInfoImpl(TargetTriple)); - if (DisableSimplifyLibCalls) - TLII->disableAllFunctions(); - FAM.registerPass([&] { return TargetLibraryAnalysis(*TLII); }); - - PB.registerModuleAnalyses(MAM); - PB.registerCGSCCAnalyses(CGAM); - PB.registerFunctionAnalyses(FAM); - PB.registerLoopAnalyses(LAM); - PB.crossRegisterProxies(LAM, FAM, CGAM, MAM); - - // We manually collect pipeline callbacks so we can apply them at O0, where the - // PassBuilder does not create a pipeline. - std::vector> PipelineStartEPCallbacks; -#if LLVM_VERSION_GE(11, 0) - std::vector> - OptimizerLastEPCallbacks; -#else - std::vector> - OptimizerLastEPCallbacks; -#endif - - if (VerifyIR) { - PipelineStartEPCallbacks.push_back([VerifyIR](ModulePassManager &MPM) { - MPM.addPass(VerifierPass()); - }); - } - - if (SanitizerOptions) { - if (SanitizerOptions->SanitizeMemory) { - MemorySanitizerOptions Options( - SanitizerOptions->SanitizeMemoryTrackOrigins, - SanitizerOptions->SanitizeMemoryRecover, - /*CompileKernel=*/false); -#if LLVM_VERSION_GE(11, 0) - OptimizerLastEPCallbacks.push_back( - [Options](ModulePassManager &MPM, PassBuilder::OptimizationLevel Level) { - MPM.addPass(MemorySanitizerPass(Options)); - MPM.addPass(createModuleToFunctionPassAdaptor(MemorySanitizerPass(Options))); - } - ); -#else -#if LLVM_VERSION_GE(10, 0) - PipelineStartEPCallbacks.push_back([Options](ModulePassManager &MPM) { - MPM.addPass(MemorySanitizerPass(Options)); - }); -#endif - OptimizerLastEPCallbacks.push_back( - [Options](FunctionPassManager &FPM, PassBuilder::OptimizationLevel Level) { - FPM.addPass(MemorySanitizerPass(Options)); - } - ); -#endif - } - - if (SanitizerOptions->SanitizeThread) { -#if LLVM_VERSION_GE(11, 0) - OptimizerLastEPCallbacks.push_back( - [](ModulePassManager &MPM, PassBuilder::OptimizationLevel Level) { - MPM.addPass(ThreadSanitizerPass()); - MPM.addPass(createModuleToFunctionPassAdaptor(ThreadSanitizerPass())); - } - ); -#else -#if LLVM_VERSION_GE(10, 0) - PipelineStartEPCallbacks.push_back([](ModulePassManager &MPM) { - MPM.addPass(ThreadSanitizerPass()); - }); -#endif - OptimizerLastEPCallbacks.push_back( - [](FunctionPassManager &FPM, PassBuilder::OptimizationLevel Level) { - FPM.addPass(ThreadSanitizerPass()); - } - ); -#endif - } - - if (SanitizerOptions->SanitizeAddress) { -#if LLVM_VERSION_GE(11, 0) - OptimizerLastEPCallbacks.push_back( - [SanitizerOptions](ModulePassManager &MPM, PassBuilder::OptimizationLevel Level) { - MPM.addPass(RequireAnalysisPass()); - MPM.addPass(ModuleAddressSanitizerPass( - /*CompileKernel=*/false, SanitizerOptions->SanitizeAddressRecover)); - MPM.addPass(createModuleToFunctionPassAdaptor(AddressSanitizerPass( - /*CompileKernel=*/false, SanitizerOptions->SanitizeAddressRecover, - /*UseAfterScope=*/true))); - } - ); -#else - PipelineStartEPCallbacks.push_back([&](ModulePassManager &MPM) { - MPM.addPass(RequireAnalysisPass()); - }); - OptimizerLastEPCallbacks.push_back( - [SanitizerOptions](FunctionPassManager &FPM, PassBuilder::OptimizationLevel Level) { - FPM.addPass(AddressSanitizerPass( - /*CompileKernel=*/false, SanitizerOptions->SanitizeAddressRecover, - /*UseAfterScope=*/true)); - } - ); - PipelineStartEPCallbacks.push_back( - [SanitizerOptions](ModulePassManager &MPM) { - MPM.addPass(ModuleAddressSanitizerPass( - /*CompileKernel=*/false, SanitizerOptions->SanitizeAddressRecover)); - } - ); -#endif - } - } - - ModulePassManager MPM(DebugPassManager); - if (!NoPrepopulatePasses) { - if (OptLevel == PassBuilder::OptimizationLevel::O0) { - for (const auto &C : PipelineStartEPCallbacks) - C(MPM); - -#if LLVM_VERSION_GE(11, 0) - for (const auto &C : OptimizerLastEPCallbacks) - C(MPM, OptLevel); -#else - if (!OptimizerLastEPCallbacks.empty()) { - FunctionPassManager FPM(DebugPassManager); - for (const auto &C : OptimizerLastEPCallbacks) - C(FPM, OptLevel); - MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM))); - } -#endif - - MPM.addPass(AlwaysInlinerPass(EmitLifetimeMarkers)); - -#if LLVM_VERSION_GE(10, 0) - if (PGOOpt) { - PB.addPGOInstrPassesForO0( - MPM, DebugPassManager, PGOOpt->Action == PGOOptions::IRInstr, - /*IsCS=*/false, PGOOpt->ProfileFile, PGOOpt->ProfileRemappingFile); - } -#endif - } else { - for (const auto &C : PipelineStartEPCallbacks) - PB.registerPipelineStartEPCallback(C); - if (OptStage != LLVMRustOptStage::PreLinkThinLTO) { - for (const auto &C : OptimizerLastEPCallbacks) - PB.registerOptimizerLastEPCallback(C); - } - - switch (OptStage) { - case LLVMRustOptStage::PreLinkNoLTO: - MPM = PB.buildPerModuleDefaultPipeline(OptLevel, DebugPassManager); - break; - case LLVMRustOptStage::PreLinkThinLTO: - MPM = PB.buildThinLTOPreLinkDefaultPipeline(OptLevel, DebugPassManager); -#if LLVM_VERSION_GE(11, 0) - for (const auto &C : OptimizerLastEPCallbacks) - C(MPM, OptLevel); -#else - if (!OptimizerLastEPCallbacks.empty()) { - FunctionPassManager FPM(DebugPassManager); - for (const auto &C : OptimizerLastEPCallbacks) - C(FPM, OptLevel); - MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM))); - } -#endif - break; - case LLVMRustOptStage::PreLinkFatLTO: - MPM = PB.buildLTOPreLinkDefaultPipeline(OptLevel, DebugPassManager); - break; - case LLVMRustOptStage::ThinLTO: - // FIXME: Does it make sense to pass the ModuleSummaryIndex? - // It only seems to be needed for C++ specific optimizations. - MPM = PB.buildThinLTODefaultPipeline(OptLevel, DebugPassManager, nullptr); - break; - case LLVMRustOptStage::FatLTO: - MPM = PB.buildLTODefaultPipeline(OptLevel, DebugPassManager, nullptr); - break; - } - } - } - - if (UseThinLTOBuffers) { - MPM.addPass(CanonicalizeAliasesPass()); - MPM.addPass(NameAnonGlobalPass()); - } - - // Upgrade all calls to old intrinsics first. - for (Module::iterator I = TheModule->begin(), E = TheModule->end(); I != E;) - UpgradeCallsToIntrinsic(&*I++); // must be post-increment, as we remove - - MPM.run(*TheModule, MAM); -#else - // The new pass manager has been available for a long time, - // but we don't bother supporting it on old LLVM versions. - report_fatal_error("New pass manager only supported since LLVM 9"); -#endif -} - -// Callback to demangle function name -// Parameters: -// * name to be demangled -// * name len -// * output buffer -// * output buffer len -// Returns len of demangled string, or 0 if demangle failed. -typedef size_t (*DemangleFn)(const char*, size_t, char*, size_t); - - -namespace { - -class RustAssemblyAnnotationWriter : public AssemblyAnnotationWriter { - DemangleFn Demangle; - std::vector Buf; - -public: - RustAssemblyAnnotationWriter(DemangleFn Demangle) : Demangle(Demangle) {} - - // Return empty string if demangle failed - // or if name does not need to be demangled - StringRef CallDemangle(StringRef name) { - if (!Demangle) { - return StringRef(); - } - - if (Buf.size() < name.size() * 2) { - // Semangled name usually shorter than mangled, - // but allocate twice as much memory just in case - Buf.resize(name.size() * 2); - } - - auto R = Demangle(name.data(), name.size(), Buf.data(), Buf.size()); - if (!R) { - // Demangle failed. - return StringRef(); - } - - auto Demangled = StringRef(Buf.data(), R); - if (Demangled == name) { - // Do not print anything if demangled name is equal to mangled. - return StringRef(); - } - - return Demangled; - } - - void emitFunctionAnnot(const Function *F, - formatted_raw_ostream &OS) override { - StringRef Demangled = CallDemangle(F->getName()); - if (Demangled.empty()) { - return; - } - - OS << "; " << Demangled << "\n"; - } - - void emitInstructionAnnot(const Instruction *I, - formatted_raw_ostream &OS) override { - const char *Name; - const Value *Value; - if (const CallInst *CI = dyn_cast(I)) { - Name = "call"; - Value = CI->getCalledOperand(); - } else if (const InvokeInst* II = dyn_cast(I)) { - Name = "invoke"; - Value = II->getCalledOperand(); - } else { - // Could demangle more operations, e. g. - // `store %place, @function`. - return; - } - - if (!Value->hasName()) { - return; - } - - StringRef Demangled = CallDemangle(Value->getName()); - if (Demangled.empty()) { - return; - } - - OS << "; " << Name << " " << Demangled << "\n"; - } -}; - -} // namespace - -extern "C" LLVMRustResult -LLVMRustPrintModule(LLVMModuleRef M, const char *Path, DemangleFn Demangle) { - std::string ErrorInfo; - std::error_code EC; - raw_fd_ostream OS(Path, EC, sys::fs::F_None); - if (EC) - ErrorInfo = EC.message(); - if (ErrorInfo != "") { - LLVMRustSetLastError(ErrorInfo.c_str()); - return LLVMRustResult::Failure; - } - - RustAssemblyAnnotationWriter AAW(Demangle); - formatted_raw_ostream FOS(OS); - unwrap(M)->print(FOS, &AAW); - - return LLVMRustResult::Success; -} - -extern "C" void LLVMRustPrintPasses() { - LLVMInitializePasses(); - struct MyListener : PassRegistrationListener { - void passEnumerate(const PassInfo *Info) { - StringRef PassArg = Info->getPassArgument(); - StringRef PassName = Info->getPassName(); - if (!PassArg.empty()) { - // These unsigned->signed casts could theoretically overflow, but - // realistically never will (and even if, the result is implementation - // defined rather plain UB). - printf("%15.*s - %.*s\n", (int)PassArg.size(), PassArg.data(), - (int)PassName.size(), PassName.data()); - } - } - } Listener; - - PassRegistry *PR = PassRegistry::getPassRegistry(); - PR->enumerateWith(&Listener); -} - -extern "C" void LLVMRustAddAlwaysInlinePass(LLVMPassManagerBuilderRef PMBR, - bool AddLifetimes) { - unwrap(PMBR)->Inliner = llvm::createAlwaysInlinerLegacyPass(AddLifetimes); -} - -extern "C" void LLVMRustRunRestrictionPass(LLVMModuleRef M, char **Symbols, - size_t Len) { - llvm::legacy::PassManager passes; - - auto PreserveFunctions = [=](const GlobalValue &GV) { - for (size_t I = 0; I < Len; I++) { - if (GV.getName() == Symbols[I]) { - return true; - } - } - return false; - }; - - passes.add(llvm::createInternalizePass(PreserveFunctions)); - - passes.run(*unwrap(M)); -} - -extern "C" void LLVMRustMarkAllFunctionsNounwind(LLVMModuleRef M) { - for (Module::iterator GV = unwrap(M)->begin(), E = unwrap(M)->end(); GV != E; - ++GV) { - GV->setDoesNotThrow(); - Function *F = dyn_cast(GV); - if (F == nullptr) - continue; - - for (Function::iterator B = F->begin(), BE = F->end(); B != BE; ++B) { - for (BasicBlock::iterator I = B->begin(), IE = B->end(); I != IE; ++I) { - if (isa(I)) { - InvokeInst *CI = cast(I); - CI->setDoesNotThrow(); - } - } - } - } -} - -extern "C" void -LLVMRustSetDataLayoutFromTargetMachine(LLVMModuleRef Module, - LLVMTargetMachineRef TMR) { - TargetMachine *Target = unwrap(TMR); - unwrap(Module)->setDataLayout(Target->createDataLayout()); -} - -extern "C" void LLVMRustSetModulePICLevel(LLVMModuleRef M) { - unwrap(M)->setPICLevel(PICLevel::Level::BigPIC); -} - -extern "C" void LLVMRustSetModulePIELevel(LLVMModuleRef M) { - unwrap(M)->setPIELevel(PIELevel::Level::Large); -} - -// Here you'll find an implementation of ThinLTO as used by the Rust compiler -// right now. This ThinLTO support is only enabled on "recent ish" versions of -// LLVM, and otherwise it's just blanket rejected from other compilers. -// -// Most of this implementation is straight copied from LLVM. At the time of -// this writing it wasn't *quite* suitable to reuse more code from upstream -// for our purposes, but we should strive to upstream this support once it's -// ready to go! I figure we may want a bit of testing locally first before -// sending this upstream to LLVM. I hear though they're quite eager to receive -// feedback like this! -// -// If you're reading this code and wondering "what in the world" or you're -// working "good lord by LLVM upgrade is *still* failing due to these bindings" -// then fear not! (ok maybe fear a little). All code here is mostly based -// on `lib/LTO/ThinLTOCodeGenerator.cpp` in LLVM. -// -// You'll find that the general layout here roughly corresponds to the `run` -// method in that file as well as `ProcessThinLTOModule`. Functions are -// specifically commented below as well, but if you're updating this code -// or otherwise trying to understand it, the LLVM source will be useful in -// interpreting the mysteries within. -// -// Otherwise I'll apologize in advance, it probably requires a relatively -// significant investment on your part to "truly understand" what's going on -// here. Not saying I do myself, but it took me awhile staring at LLVM's source -// and various online resources about ThinLTO to make heads or tails of all -// this. - -// This is a shared data structure which *must* be threadsafe to share -// read-only amongst threads. This also corresponds basically to the arguments -// of the `ProcessThinLTOModule` function in the LLVM source. -struct LLVMRustThinLTOData { - // The combined index that is the global analysis over all modules we're - // performing ThinLTO for. This is mostly managed by LLVM. - ModuleSummaryIndex Index; - - // All modules we may look at, stored as in-memory serialized versions. This - // is later used when inlining to ensure we can extract any module to inline - // from. - StringMap ModuleMap; - - // A set that we manage of everything we *don't* want internalized. Note that - // this includes all transitive references right now as well, but it may not - // always! - DenseSet GUIDPreservedSymbols; - - // Not 100% sure what these are, but they impact what's internalized and - // what's inlined across modules, I believe. - StringMap ImportLists; - StringMap ExportLists; - StringMap ModuleToDefinedGVSummaries; - - LLVMRustThinLTOData() : Index(/* HaveGVs = */ false) {} -}; - -// Just an argument to the `LLVMRustCreateThinLTOData` function below. -struct LLVMRustThinLTOModule { - const char *identifier; - const char *data; - size_t len; -}; - -// This is copied from `lib/LTO/ThinLTOCodeGenerator.cpp`, not sure what it -// does. -static const GlobalValueSummary * -getFirstDefinitionForLinker(const GlobalValueSummaryList &GVSummaryList) { - auto StrongDefForLinker = llvm::find_if( - GVSummaryList, [](const std::unique_ptr &Summary) { - auto Linkage = Summary->linkage(); - return !GlobalValue::isAvailableExternallyLinkage(Linkage) && - !GlobalValue::isWeakForLinker(Linkage); - }); - if (StrongDefForLinker != GVSummaryList.end()) - return StrongDefForLinker->get(); - - auto FirstDefForLinker = llvm::find_if( - GVSummaryList, [](const std::unique_ptr &Summary) { - auto Linkage = Summary->linkage(); - return !GlobalValue::isAvailableExternallyLinkage(Linkage); - }); - if (FirstDefForLinker == GVSummaryList.end()) - return nullptr; - return FirstDefForLinker->get(); -} - -// The main entry point for creating the global ThinLTO analysis. The structure -// here is basically the same as before threads are spawned in the `run` -// function of `lib/LTO/ThinLTOCodeGenerator.cpp`. -extern "C" LLVMRustThinLTOData* -LLVMRustCreateThinLTOData(LLVMRustThinLTOModule *modules, - int num_modules, - const char **preserved_symbols, - int num_symbols) { -#if LLVM_VERSION_GE(10, 0) - auto Ret = std::make_unique(); -#else - auto Ret = llvm::make_unique(); -#endif - - // Load each module's summary and merge it into one combined index - for (int i = 0; i < num_modules; i++) { - auto module = &modules[i]; - StringRef buffer(module->data, module->len); - MemoryBufferRef mem_buffer(buffer, module->identifier); - - Ret->ModuleMap[module->identifier] = mem_buffer; - - if (Error Err = readModuleSummaryIndex(mem_buffer, Ret->Index, i)) { - LLVMRustSetLastError(toString(std::move(Err)).c_str()); - return nullptr; - } - } - - // Collect for each module the list of function it defines (GUID -> Summary) - Ret->Index.collectDefinedGVSummariesPerModule(Ret->ModuleToDefinedGVSummaries); - - // Convert the preserved symbols set from string to GUID, this is then needed - // for internalization. - for (int i = 0; i < num_symbols; i++) { - auto GUID = GlobalValue::getGUID(preserved_symbols[i]); - Ret->GUIDPreservedSymbols.insert(GUID); - } - - // Collect the import/export lists for all modules from the call-graph in the - // combined index - // - // This is copied from `lib/LTO/ThinLTOCodeGenerator.cpp` - auto deadIsPrevailing = [&](GlobalValue::GUID G) { - return PrevailingType::Unknown; - }; - // We don't have a complete picture in our use of ThinLTO, just our immediate - // crate, so we need `ImportEnabled = false` to limit internalization. - // Otherwise, we sometimes lose `static` values -- see #60184. - computeDeadSymbolsWithConstProp(Ret->Index, Ret->GUIDPreservedSymbols, - deadIsPrevailing, /* ImportEnabled = */ false); - ComputeCrossModuleImport( - Ret->Index, - Ret->ModuleToDefinedGVSummaries, - Ret->ImportLists, - Ret->ExportLists - ); - - // Resolve LinkOnce/Weak symbols, this has to be computed early be cause it - // impacts the caching. - // - // This is copied from `lib/LTO/ThinLTOCodeGenerator.cpp` with some of this - // being lifted from `lib/LTO/LTO.cpp` as well - StringMap> ResolvedODR; - DenseMap PrevailingCopy; - for (auto &I : Ret->Index) { - if (I.second.SummaryList.size() > 1) - PrevailingCopy[I.first] = getFirstDefinitionForLinker(I.second.SummaryList); - } - auto isPrevailing = [&](GlobalValue::GUID GUID, const GlobalValueSummary *S) { - const auto &Prevailing = PrevailingCopy.find(GUID); - if (Prevailing == PrevailingCopy.end()) - return true; - return Prevailing->second == S; - }; - auto recordNewLinkage = [&](StringRef ModuleIdentifier, - GlobalValue::GUID GUID, - GlobalValue::LinkageTypes NewLinkage) { - ResolvedODR[ModuleIdentifier][GUID] = NewLinkage; - }; -#if LLVM_VERSION_GE(9, 0) - thinLTOResolvePrevailingInIndex(Ret->Index, isPrevailing, recordNewLinkage, - Ret->GUIDPreservedSymbols); -#else - thinLTOResolvePrevailingInIndex(Ret->Index, isPrevailing, recordNewLinkage); -#endif - - // Here we calculate an `ExportedGUIDs` set for use in the `isExported` - // callback below. This callback below will dictate the linkage for all - // summaries in the index, and we basically just only want to ensure that dead - // symbols are internalized. Otherwise everything that's already external - // linkage will stay as external, and internal will stay as internal. - std::set ExportedGUIDs; - for (auto &List : Ret->Index) { - for (auto &GVS: List.second.SummaryList) { - if (GlobalValue::isLocalLinkage(GVS->linkage())) - continue; - auto GUID = GVS->getOriginalName(); - if (GVS->flags().Live) - ExportedGUIDs.insert(GUID); - } - } -#if LLVM_VERSION_GE(10, 0) - auto isExported = [&](StringRef ModuleIdentifier, ValueInfo VI) { - const auto &ExportList = Ret->ExportLists.find(ModuleIdentifier); - return (ExportList != Ret->ExportLists.end() && - ExportList->second.count(VI)) || - ExportedGUIDs.count(VI.getGUID()); - }; - thinLTOInternalizeAndPromoteInIndex(Ret->Index, isExported, isPrevailing); -#else - auto isExported = [&](StringRef ModuleIdentifier, GlobalValue::GUID GUID) { - const auto &ExportList = Ret->ExportLists.find(ModuleIdentifier); - return (ExportList != Ret->ExportLists.end() && - ExportList->second.count(GUID)) || - ExportedGUIDs.count(GUID); - }; - thinLTOInternalizeAndPromoteInIndex(Ret->Index, isExported); -#endif - - return Ret.release(); -} - -extern "C" void -LLVMRustFreeThinLTOData(LLVMRustThinLTOData *Data) { - delete Data; -} - -// Below are the various passes that happen *per module* when doing ThinLTO. -// -// In other words, these are the functions that are all run concurrently -// with one another, one per module. The passes here correspond to the analysis -// passes in `lib/LTO/ThinLTOCodeGenerator.cpp`, currently found in the -// `ProcessThinLTOModule` function. Here they're split up into separate steps -// so rustc can save off the intermediate bytecode between each step. - -#if LLVM_VERSION_GE(11, 0) -static bool -clearDSOLocalOnDeclarations(Module &Mod, TargetMachine &TM) { - // When linking an ELF shared object, dso_local should be dropped. We - // conservatively do this for -fpic. - bool ClearDSOLocalOnDeclarations = - TM.getTargetTriple().isOSBinFormatELF() && - TM.getRelocationModel() != Reloc::Static && - Mod.getPIELevel() == PIELevel::Default; - return ClearDSOLocalOnDeclarations; -} -#endif - -extern "C" bool -LLVMRustPrepareThinLTORename(const LLVMRustThinLTOData *Data, LLVMModuleRef M, - LLVMTargetMachineRef TM) { - Module &Mod = *unwrap(M); - TargetMachine &Target = *unwrap(TM); - -#if LLVM_VERSION_GE(11, 0) - bool ClearDSOLocal = clearDSOLocalOnDeclarations(Mod, Target); - bool error = renameModuleForThinLTO(Mod, Data->Index, ClearDSOLocal); -#else - bool error = renameModuleForThinLTO(Mod, Data->Index); -#endif - - if (error) { - LLVMRustSetLastError("renameModuleForThinLTO failed"); - return false; - } - return true; -} - -extern "C" bool -LLVMRustPrepareThinLTOResolveWeak(const LLVMRustThinLTOData *Data, LLVMModuleRef M) { - Module &Mod = *unwrap(M); - const auto &DefinedGlobals = Data->ModuleToDefinedGVSummaries.lookup(Mod.getModuleIdentifier()); - thinLTOResolvePrevailingInModule(Mod, DefinedGlobals); - return true; -} - -extern "C" bool -LLVMRustPrepareThinLTOInternalize(const LLVMRustThinLTOData *Data, LLVMModuleRef M) { - Module &Mod = *unwrap(M); - const auto &DefinedGlobals = Data->ModuleToDefinedGVSummaries.lookup(Mod.getModuleIdentifier()); - thinLTOInternalizeModule(Mod, DefinedGlobals); - return true; -} - -extern "C" bool -LLVMRustPrepareThinLTOImport(const LLVMRustThinLTOData *Data, LLVMModuleRef M, - LLVMTargetMachineRef TM) { - Module &Mod = *unwrap(M); - TargetMachine &Target = *unwrap(TM); - - const auto &ImportList = Data->ImportLists.lookup(Mod.getModuleIdentifier()); - auto Loader = [&](StringRef Identifier) { - const auto &Memory = Data->ModuleMap.lookup(Identifier); - auto &Context = Mod.getContext(); - auto MOrErr = getLazyBitcodeModule(Memory, Context, true, true); - - if (!MOrErr) - return MOrErr; - - // The rest of this closure is a workaround for - // https://bugs.llvm.org/show_bug.cgi?id=38184 where during ThinLTO imports - // we accidentally import wasm custom sections into different modules, - // duplicating them by in the final output artifact. - // - // The issue is worked around here by manually removing the - // `wasm.custom_sections` named metadata node from any imported module. This - // we know isn't used by any optimization pass so there's no need for it to - // be imported. - // - // Note that the metadata is currently lazily loaded, so we materialize it - // here before looking up if there's metadata inside. The `FunctionImporter` - // will immediately materialize metadata anyway after an import, so this - // shouldn't be a perf hit. - if (Error Err = (*MOrErr)->materializeMetadata()) { - Expected> Ret(std::move(Err)); - return Ret; - } - - auto *WasmCustomSections = (*MOrErr)->getNamedMetadata("wasm.custom_sections"); - if (WasmCustomSections) - WasmCustomSections->eraseFromParent(); - - return MOrErr; - }; -#if LLVM_VERSION_GE(11, 0) - bool ClearDSOLocal = clearDSOLocalOnDeclarations(Mod, Target); - FunctionImporter Importer(Data->Index, Loader, ClearDSOLocal); -#else - FunctionImporter Importer(Data->Index, Loader); -#endif - Expected Result = Importer.importFunctions(Mod, ImportList); - if (!Result) { - LLVMRustSetLastError(toString(Result.takeError()).c_str()); - return false; - } - return true; -} - -extern "C" typedef void (*LLVMRustModuleNameCallback)(void*, // payload - const char*, // importing module name - const char*); // imported module name - -// Calls `module_name_callback` for each module import done by ThinLTO. -// The callback is provided with regular null-terminated C strings. -extern "C" void -LLVMRustGetThinLTOModuleImports(const LLVMRustThinLTOData *data, - LLVMRustModuleNameCallback module_name_callback, - void* callback_payload) { - for (const auto& importing_module : data->ImportLists) { - const std::string importing_module_id = importing_module.getKey().str(); - const auto& imports = importing_module.getValue(); - for (const auto& imported_module : imports) { - const std::string imported_module_id = imported_module.getKey().str(); - module_name_callback(callback_payload, - importing_module_id.c_str(), - imported_module_id.c_str()); - } - } -} - -// This struct and various functions are sort of a hack right now, but the -// problem is that we've got in-memory LLVM modules after we generate and -// optimize all codegen-units for one compilation in rustc. To be compatible -// with the LTO support above we need to serialize the modules plus their -// ThinLTO summary into memory. -// -// This structure is basically an owned version of a serialize module, with -// a ThinLTO summary attached. -struct LLVMRustThinLTOBuffer { - std::string data; -}; - -extern "C" LLVMRustThinLTOBuffer* -LLVMRustThinLTOBufferCreate(LLVMModuleRef M) { -#if LLVM_VERSION_GE(10, 0) - auto Ret = std::make_unique(); -#else - auto Ret = llvm::make_unique(); -#endif - { - raw_string_ostream OS(Ret->data); - { - legacy::PassManager PM; - PM.add(createWriteThinLTOBitcodePass(OS)); - PM.run(*unwrap(M)); - } - } - return Ret.release(); -} - -extern "C" void -LLVMRustThinLTOBufferFree(LLVMRustThinLTOBuffer *Buffer) { - delete Buffer; -} - -extern "C" const void* -LLVMRustThinLTOBufferPtr(const LLVMRustThinLTOBuffer *Buffer) { - return Buffer->data.data(); -} - -extern "C" size_t -LLVMRustThinLTOBufferLen(const LLVMRustThinLTOBuffer *Buffer) { - return Buffer->data.length(); -} - -// This is what we used to parse upstream bitcode for actual ThinLTO -// processing. We'll call this once per module optimized through ThinLTO, and -// it'll be called concurrently on many threads. -extern "C" LLVMModuleRef -LLVMRustParseBitcodeForLTO(LLVMContextRef Context, - const char *data, - size_t len, - const char *identifier) { - StringRef Data(data, len); - MemoryBufferRef Buffer(Data, identifier); - unwrap(Context)->enableDebugTypeODRUniquing(); - Expected> SrcOrError = - parseBitcodeFile(Buffer, *unwrap(Context)); - if (!SrcOrError) { - LLVMRustSetLastError(toString(SrcOrError.takeError()).c_str()); - return nullptr; - } - return wrap(std::move(*SrcOrError).release()); -} - -// Find the bitcode section in the object file data and return it as a slice. -// Fail if the bitcode section is present but empty. -// -// On success, the return value is the pointer to the start of the slice and -// `out_len` is filled with the (non-zero) length. On failure, the return value -// is `nullptr` and `out_len` is set to zero. -extern "C" const char* -LLVMRustGetBitcodeSliceFromObjectData(const char *data, - size_t len, - size_t *out_len) { - *out_len = 0; - - StringRef Data(data, len); - MemoryBufferRef Buffer(Data, ""); // The id is unused. - - Expected BitcodeOrError = - object::IRObjectFile::findBitcodeInMemBuffer(Buffer); - if (!BitcodeOrError) { - LLVMRustSetLastError(toString(BitcodeOrError.takeError()).c_str()); - return nullptr; - } - - *out_len = BitcodeOrError->getBufferSize(); - return BitcodeOrError->getBufferStart(); -} - -// Rewrite all `DICompileUnit` pointers to the `DICompileUnit` specified. See -// the comment in `back/lto.rs` for why this exists. -extern "C" void -LLVMRustThinLTOGetDICompileUnit(LLVMModuleRef Mod, - DICompileUnit **A, - DICompileUnit **B) { - Module *M = unwrap(Mod); - DICompileUnit **Cur = A; - DICompileUnit **Next = B; - for (DICompileUnit *CU : M->debug_compile_units()) { - *Cur = CU; - Cur = Next; - Next = nullptr; - if (Cur == nullptr) - break; - } -} - -// Rewrite all `DICompileUnit` pointers to the `DICompileUnit` specified. See -// the comment in `back/lto.rs` for why this exists. -extern "C" void -LLVMRustThinLTOPatchDICompileUnit(LLVMModuleRef Mod, DICompileUnit *Unit) { - Module *M = unwrap(Mod); - - // If the original source module didn't have a `DICompileUnit` then try to - // merge all the existing compile units. If there aren't actually any though - // then there's not much for us to do so return. - if (Unit == nullptr) { - for (DICompileUnit *CU : M->debug_compile_units()) { - Unit = CU; - break; - } - if (Unit == nullptr) - return; - } - - // Use LLVM's built-in `DebugInfoFinder` to find a bunch of debuginfo and - // process it recursively. Note that we used to specifically iterate over - // instructions to ensure we feed everything into it, but `processModule` - // started doing this the same way in LLVM 7 (commit d769eb36ab2b8). - DebugInfoFinder Finder; - Finder.processModule(*M); - - // After we've found all our debuginfo, rewrite all subprograms to point to - // the same `DICompileUnit`. - for (auto &F : Finder.subprograms()) { - F->replaceUnit(Unit); - } - - // Erase any other references to other `DICompileUnit` instances, the verifier - // will later ensure that we don't actually have any other stale references to - // worry about. - auto *MD = M->getNamedMetadata("llvm.dbg.cu"); - MD->clearOperands(); - MD->addOperand(Unit); -} diff --git a/src/rustllvm/README b/src/rustllvm/README deleted file mode 100644 index e1c6dd07d2b..00000000000 --- a/src/rustllvm/README +++ /dev/null @@ -1,16 +0,0 @@ -This directory currently contains some LLVM support code. This will generally -be sent upstream to LLVM in time; for now it lives here. - -NOTE: the LLVM C++ ABI is subject to between-version breakage and must *never* -be exposed to Rust. To allow for easy auditing of that, all Rust-exposed types -must be typedef-ed as "LLVMXyz", or "LLVMRustXyz" if they were defined here. - -Functions that return a failure status and leave the error in -the LLVM last error should return an LLVMRustResult rather than an -int or anything to avoid confusion. - -When translating enums, add a single `Other` variant as the first -one to allow for new variants to be added. It should abort when used -as an input. - -All other types must not be typedef-ed as such. diff --git a/src/rustllvm/RustWrapper.cpp b/src/rustllvm/RustWrapper.cpp deleted file mode 100644 index 9d90b0dfe07..00000000000 --- a/src/rustllvm/RustWrapper.cpp +++ /dev/null @@ -1,1721 +0,0 @@ -#include "rustllvm.h" -#include "llvm/IR/DebugInfoMetadata.h" -#include "llvm/IR/DiagnosticInfo.h" -#include "llvm/IR/DiagnosticPrinter.h" -#include "llvm/IR/GlobalVariable.h" -#include "llvm/IR/Instructions.h" -#include "llvm/IR/Intrinsics.h" -#include "llvm/Object/Archive.h" -#include "llvm/Object/ObjectFile.h" -#include "llvm/Bitcode/BitcodeWriterPass.h" -#include "llvm/Support/Signals.h" -#include "llvm/ADT/Optional.h" - -#include - -//===----------------------------------------------------------------------=== -// -// This file defines alternate interfaces to core functions that are more -// readily callable by Rust's FFI. -// -//===----------------------------------------------------------------------=== - -using namespace llvm; -using namespace llvm::sys; -using namespace llvm::object; - -// LLVMAtomicOrdering is already an enum - don't create another -// one. -static AtomicOrdering fromRust(LLVMAtomicOrdering Ordering) { - switch (Ordering) { - case LLVMAtomicOrderingNotAtomic: - return AtomicOrdering::NotAtomic; - case LLVMAtomicOrderingUnordered: - return AtomicOrdering::Unordered; - case LLVMAtomicOrderingMonotonic: - return AtomicOrdering::Monotonic; - case LLVMAtomicOrderingAcquire: - return AtomicOrdering::Acquire; - case LLVMAtomicOrderingRelease: - return AtomicOrdering::Release; - case LLVMAtomicOrderingAcquireRelease: - return AtomicOrdering::AcquireRelease; - case LLVMAtomicOrderingSequentiallyConsistent: - return AtomicOrdering::SequentiallyConsistent; - } - - report_fatal_error("Invalid LLVMAtomicOrdering value!"); -} - -static LLVM_THREAD_LOCAL char *LastError; - -// Custom error handler for fatal LLVM errors. -// -// Notably it exits the process with code 101, unlike LLVM's default of 1. -static void FatalErrorHandler(void *UserData, - const std::string& Reason, - bool GenCrashDiag) { - // Do the same thing that the default error handler does. - std::cerr << "LLVM ERROR: " << Reason << std::endl; - - // Since this error handler exits the process, we have to run any cleanup that - // LLVM would run after handling the error. This might change with an LLVM - // upgrade. - sys::RunInterruptHandlers(); - - exit(101); -} - -extern "C" void LLVMRustInstallFatalErrorHandler() { - install_fatal_error_handler(FatalErrorHandler); -} - -extern "C" LLVMMemoryBufferRef -LLVMRustCreateMemoryBufferWithContentsOfFile(const char *Path) { - ErrorOr> BufOr = - MemoryBuffer::getFile(Path, -1, false); - if (!BufOr) { - LLVMRustSetLastError(BufOr.getError().message().c_str()); - return nullptr; - } - return wrap(BufOr.get().release()); -} - -extern "C" char *LLVMRustGetLastError(void) { - char *Ret = LastError; - LastError = nullptr; - return Ret; -} - -extern "C" unsigned int LLVMRustGetInstructionCount(LLVMModuleRef M) { - return unwrap(M)->getInstructionCount(); -} - -extern "C" void LLVMRustSetLastError(const char *Err) { - free((void *)LastError); - LastError = strdup(Err); -} - -extern "C" LLVMContextRef LLVMRustContextCreate(bool shouldDiscardNames) { - auto ctx = new LLVMContext(); - ctx->setDiscardValueNames(shouldDiscardNames); - return wrap(ctx); -} - -extern "C" void LLVMRustSetNormalizedTarget(LLVMModuleRef M, - const char *Triple) { - unwrap(M)->setTargetTriple(Triple::normalize(Triple)); -} - -extern "C" void LLVMRustPrintPassTimings() { - raw_fd_ostream OS(2, false); // stderr. - TimerGroup::printAll(OS); -} - -extern "C" LLVMValueRef LLVMRustGetNamedValue(LLVMModuleRef M, const char *Name, - size_t NameLen) { - return wrap(unwrap(M)->getNamedValue(StringRef(Name, NameLen))); -} - -extern "C" LLVMValueRef LLVMRustGetOrInsertFunction(LLVMModuleRef M, - const char *Name, - size_t NameLen, - LLVMTypeRef FunctionTy) { - return wrap(unwrap(M) - ->getOrInsertFunction(StringRef(Name, NameLen), - unwrap(FunctionTy)) -#if LLVM_VERSION_GE(9, 0) - .getCallee() -#endif - ); -} - -extern "C" LLVMValueRef -LLVMRustGetOrInsertGlobal(LLVMModuleRef M, const char *Name, size_t NameLen, LLVMTypeRef Ty) { - StringRef NameRef(Name, NameLen); - return wrap(unwrap(M)->getOrInsertGlobal(NameRef, unwrap(Ty))); -} - -extern "C" LLVMValueRef -LLVMRustInsertPrivateGlobal(LLVMModuleRef M, LLVMTypeRef Ty) { - return wrap(new GlobalVariable(*unwrap(M), - unwrap(Ty), - false, - GlobalValue::PrivateLinkage, - nullptr)); -} - -extern "C" LLVMTypeRef LLVMRustMetadataTypeInContext(LLVMContextRef C) { - return wrap(Type::getMetadataTy(*unwrap(C))); -} - -static Attribute::AttrKind fromRust(LLVMRustAttribute Kind) { - switch (Kind) { - case AlwaysInline: - return Attribute::AlwaysInline; - case ByVal: - return Attribute::ByVal; - case Cold: - return Attribute::Cold; - case InlineHint: - return Attribute::InlineHint; - case MinSize: - return Attribute::MinSize; - case Naked: - return Attribute::Naked; - case NoAlias: - return Attribute::NoAlias; - case NoCapture: - return Attribute::NoCapture; - case NoInline: - return Attribute::NoInline; - case NonNull: - return Attribute::NonNull; - case NoRedZone: - return Attribute::NoRedZone; - case NoReturn: - return Attribute::NoReturn; - case NoUnwind: - return Attribute::NoUnwind; - case OptimizeForSize: - return Attribute::OptimizeForSize; - case ReadOnly: - return Attribute::ReadOnly; - case SExt: - return Attribute::SExt; - case StructRet: - return Attribute::StructRet; - case UWTable: - return Attribute::UWTable; - case ZExt: - return Attribute::ZExt; - case InReg: - return Attribute::InReg; - case SanitizeThread: - return Attribute::SanitizeThread; - case SanitizeAddress: - return Attribute::SanitizeAddress; - case SanitizeMemory: - return Attribute::SanitizeMemory; - case NonLazyBind: - return Attribute::NonLazyBind; - case OptimizeNone: - return Attribute::OptimizeNone; - case ReturnsTwice: - return Attribute::ReturnsTwice; - case ReadNone: - return Attribute::ReadNone; - case InaccessibleMemOnly: - return Attribute::InaccessibleMemOnly; - } - report_fatal_error("bad AttributeKind"); -} - -extern "C" void LLVMRustAddCallSiteAttribute(LLVMValueRef Instr, unsigned Index, - LLVMRustAttribute RustAttr) { - CallBase *Call = unwrap(Instr); - Attribute Attr = Attribute::get(Call->getContext(), fromRust(RustAttr)); - Call->addAttribute(Index, Attr); -} - -extern "C" void LLVMRustAddAlignmentCallSiteAttr(LLVMValueRef Instr, - unsigned Index, - uint32_t Bytes) { - CallBase *Call = unwrap(Instr); - AttrBuilder B; - B.addAlignmentAttr(Bytes); - Call->setAttributes(Call->getAttributes().addAttributes( - Call->getContext(), Index, B)); -} - -extern "C" void LLVMRustAddDereferenceableCallSiteAttr(LLVMValueRef Instr, - unsigned Index, - uint64_t Bytes) { - CallBase *Call = unwrap(Instr); - AttrBuilder B; - B.addDereferenceableAttr(Bytes); - Call->setAttributes(Call->getAttributes().addAttributes( - Call->getContext(), Index, B)); -} - -extern "C" void LLVMRustAddDereferenceableOrNullCallSiteAttr(LLVMValueRef Instr, - unsigned Index, - uint64_t Bytes) { - CallBase *Call = unwrap(Instr); - AttrBuilder B; - B.addDereferenceableOrNullAttr(Bytes); - Call->setAttributes(Call->getAttributes().addAttributes( - Call->getContext(), Index, B)); -} - -extern "C" void LLVMRustAddByValCallSiteAttr(LLVMValueRef Instr, unsigned Index, - LLVMTypeRef Ty) { - CallBase *Call = unwrap(Instr); -#if LLVM_VERSION_GE(9, 0) - Attribute Attr = Attribute::getWithByValType(Call->getContext(), unwrap(Ty)); -#else - Attribute Attr = Attribute::get(Call->getContext(), Attribute::ByVal); -#endif - Call->addAttribute(Index, Attr); -} - -extern "C" void LLVMRustAddFunctionAttribute(LLVMValueRef Fn, unsigned Index, - LLVMRustAttribute RustAttr) { - Function *A = unwrap(Fn); - Attribute Attr = Attribute::get(A->getContext(), fromRust(RustAttr)); - AttrBuilder B(Attr); - A->addAttributes(Index, B); -} - -extern "C" void LLVMRustAddAlignmentAttr(LLVMValueRef Fn, - unsigned Index, - uint32_t Bytes) { - Function *A = unwrap(Fn); - AttrBuilder B; - B.addAlignmentAttr(Bytes); - A->addAttributes(Index, B); -} - -extern "C" void LLVMRustAddDereferenceableAttr(LLVMValueRef Fn, unsigned Index, - uint64_t Bytes) { - Function *A = unwrap(Fn); - AttrBuilder B; - B.addDereferenceableAttr(Bytes); - A->addAttributes(Index, B); -} - -extern "C" void LLVMRustAddDereferenceableOrNullAttr(LLVMValueRef Fn, - unsigned Index, - uint64_t Bytes) { - Function *A = unwrap(Fn); - AttrBuilder B; - B.addDereferenceableOrNullAttr(Bytes); - A->addAttributes(Index, B); -} - -extern "C" void LLVMRustAddByValAttr(LLVMValueRef Fn, unsigned Index, - LLVMTypeRef Ty) { - Function *F = unwrap(Fn); -#if LLVM_VERSION_GE(9, 0) - Attribute Attr = Attribute::getWithByValType(F->getContext(), unwrap(Ty)); -#else - Attribute Attr = Attribute::get(F->getContext(), Attribute::ByVal); -#endif - F->addAttribute(Index, Attr); -} - -extern "C" void LLVMRustAddFunctionAttrStringValue(LLVMValueRef Fn, - unsigned Index, - const char *Name, - const char *Value) { - Function *F = unwrap(Fn); - AttrBuilder B; - B.addAttribute(Name, Value); - F->addAttributes(Index, B); -} - -extern "C" void LLVMRustRemoveFunctionAttributes(LLVMValueRef Fn, - unsigned Index, - LLVMRustAttribute RustAttr) { - Function *F = unwrap(Fn); - Attribute Attr = Attribute::get(F->getContext(), fromRust(RustAttr)); - AttrBuilder B(Attr); - auto PAL = F->getAttributes(); - auto PALNew = PAL.removeAttributes(F->getContext(), Index, B); - F->setAttributes(PALNew); -} - -// enable fpmath flag UnsafeAlgebra -extern "C" void LLVMRustSetHasUnsafeAlgebra(LLVMValueRef V) { - if (auto I = dyn_cast(unwrap(V))) { - I->setFast(true); - } -} - -extern "C" LLVMValueRef -LLVMRustBuildAtomicLoad(LLVMBuilderRef B, LLVMValueRef Source, const char *Name, - LLVMAtomicOrdering Order) { - Value *Ptr = unwrap(Source); - Type *Ty = Ptr->getType()->getPointerElementType(); - LoadInst *LI = unwrap(B)->CreateLoad(Ty, Ptr, Name); - LI->setAtomic(fromRust(Order)); - return wrap(LI); -} - -extern "C" LLVMValueRef LLVMRustBuildAtomicStore(LLVMBuilderRef B, - LLVMValueRef V, - LLVMValueRef Target, - LLVMAtomicOrdering Order) { - StoreInst *SI = unwrap(B)->CreateStore(unwrap(V), unwrap(Target)); - SI->setAtomic(fromRust(Order)); - return wrap(SI); -} - -// FIXME: Use the C-API LLVMBuildAtomicCmpXchg and LLVMSetWeak -// once we raise our minimum support to LLVM 10. -extern "C" LLVMValueRef -LLVMRustBuildAtomicCmpXchg(LLVMBuilderRef B, LLVMValueRef Target, - LLVMValueRef Old, LLVMValueRef Source, - LLVMAtomicOrdering Order, - LLVMAtomicOrdering FailureOrder, LLVMBool Weak) { - AtomicCmpXchgInst *ACXI = unwrap(B)->CreateAtomicCmpXchg( - unwrap(Target), unwrap(Old), unwrap(Source), fromRust(Order), - fromRust(FailureOrder)); - ACXI->setWeak(Weak); - return wrap(ACXI); -} - -enum class LLVMRustSynchronizationScope { - SingleThread, - CrossThread, -}; - -static SyncScope::ID fromRust(LLVMRustSynchronizationScope Scope) { - switch (Scope) { - case LLVMRustSynchronizationScope::SingleThread: - return SyncScope::SingleThread; - case LLVMRustSynchronizationScope::CrossThread: - return SyncScope::System; - default: - report_fatal_error("bad SynchronizationScope."); - } -} - -extern "C" LLVMValueRef -LLVMRustBuildAtomicFence(LLVMBuilderRef B, LLVMAtomicOrdering Order, - LLVMRustSynchronizationScope Scope) { - return wrap(unwrap(B)->CreateFence(fromRust(Order), fromRust(Scope))); -} - -enum class LLVMRustAsmDialect { - Att, - Intel, -}; - -static InlineAsm::AsmDialect fromRust(LLVMRustAsmDialect Dialect) { - switch (Dialect) { - case LLVMRustAsmDialect::Att: - return InlineAsm::AD_ATT; - case LLVMRustAsmDialect::Intel: - return InlineAsm::AD_Intel; - default: - report_fatal_error("bad AsmDialect."); - } -} - -extern "C" LLVMValueRef -LLVMRustInlineAsm(LLVMTypeRef Ty, char *AsmString, size_t AsmStringLen, - char *Constraints, size_t ConstraintsLen, - LLVMBool HasSideEffects, LLVMBool IsAlignStack, - LLVMRustAsmDialect Dialect) { - return wrap(InlineAsm::get(unwrap(Ty), - StringRef(AsmString, AsmStringLen), - StringRef(Constraints, ConstraintsLen), - HasSideEffects, IsAlignStack, fromRust(Dialect))); -} - -extern "C" bool LLVMRustInlineAsmVerify(LLVMTypeRef Ty, char *Constraints, - size_t ConstraintsLen) { - return InlineAsm::Verify(unwrap(Ty), - StringRef(Constraints, ConstraintsLen)); -} - -extern "C" void LLVMRustAppendModuleInlineAsm(LLVMModuleRef M, const char *Asm, - size_t AsmLen) { - unwrap(M)->appendModuleInlineAsm(StringRef(Asm, AsmLen)); -} - -typedef DIBuilder *LLVMRustDIBuilderRef; - -template DIT *unwrapDIPtr(LLVMMetadataRef Ref) { - return (DIT *)(Ref ? unwrap(Ref) : nullptr); -} - -#define DIDescriptor DIScope -#define DIArray DINodeArray -#define unwrapDI unwrapDIPtr - -// These values **must** match debuginfo::DIFlags! They also *happen* -// to match LLVM, but that isn't required as we do giant sets of -// matching below. The value shouldn't be directly passed to LLVM. -enum class LLVMRustDIFlags : uint32_t { - FlagZero = 0, - FlagPrivate = 1, - FlagProtected = 2, - FlagPublic = 3, - FlagFwdDecl = (1 << 2), - FlagAppleBlock = (1 << 3), - FlagBlockByrefStruct = (1 << 4), - FlagVirtual = (1 << 5), - FlagArtificial = (1 << 6), - FlagExplicit = (1 << 7), - FlagPrototyped = (1 << 8), - FlagObjcClassComplete = (1 << 9), - FlagObjectPointer = (1 << 10), - FlagVector = (1 << 11), - FlagStaticMember = (1 << 12), - FlagLValueReference = (1 << 13), - FlagRValueReference = (1 << 14), - FlagExternalTypeRef = (1 << 15), - FlagIntroducedVirtual = (1 << 18), - FlagBitField = (1 << 19), - FlagNoReturn = (1 << 20), - // Do not add values that are not supported by the minimum LLVM - // version we support! see llvm/include/llvm/IR/DebugInfoFlags.def -}; - -inline LLVMRustDIFlags operator&(LLVMRustDIFlags A, LLVMRustDIFlags B) { - return static_cast(static_cast(A) & - static_cast(B)); -} - -inline LLVMRustDIFlags operator|(LLVMRustDIFlags A, LLVMRustDIFlags B) { - return static_cast(static_cast(A) | - static_cast(B)); -} - -inline LLVMRustDIFlags &operator|=(LLVMRustDIFlags &A, LLVMRustDIFlags B) { - return A = A | B; -} - -inline bool isSet(LLVMRustDIFlags F) { return F != LLVMRustDIFlags::FlagZero; } - -inline LLVMRustDIFlags visibility(LLVMRustDIFlags F) { - return static_cast(static_cast(F) & 0x3); -} - -static DINode::DIFlags fromRust(LLVMRustDIFlags Flags) { - DINode::DIFlags Result = DINode::DIFlags::FlagZero; - - switch (visibility(Flags)) { - case LLVMRustDIFlags::FlagPrivate: - Result |= DINode::DIFlags::FlagPrivate; - break; - case LLVMRustDIFlags::FlagProtected: - Result |= DINode::DIFlags::FlagProtected; - break; - case LLVMRustDIFlags::FlagPublic: - Result |= DINode::DIFlags::FlagPublic; - break; - default: - // The rest are handled below - break; - } - - if (isSet(Flags & LLVMRustDIFlags::FlagFwdDecl)) { - Result |= DINode::DIFlags::FlagFwdDecl; - } - if (isSet(Flags & LLVMRustDIFlags::FlagAppleBlock)) { - Result |= DINode::DIFlags::FlagAppleBlock; - } -#if LLVM_VERSION_LT(10, 0) - if (isSet(Flags & LLVMRustDIFlags::FlagBlockByrefStruct)) { - Result |= DINode::DIFlags::FlagBlockByrefStruct; - } -#endif - if (isSet(Flags & LLVMRustDIFlags::FlagVirtual)) { - Result |= DINode::DIFlags::FlagVirtual; - } - if (isSet(Flags & LLVMRustDIFlags::FlagArtificial)) { - Result |= DINode::DIFlags::FlagArtificial; - } - if (isSet(Flags & LLVMRustDIFlags::FlagExplicit)) { - Result |= DINode::DIFlags::FlagExplicit; - } - if (isSet(Flags & LLVMRustDIFlags::FlagPrototyped)) { - Result |= DINode::DIFlags::FlagPrototyped; - } - if (isSet(Flags & LLVMRustDIFlags::FlagObjcClassComplete)) { - Result |= DINode::DIFlags::FlagObjcClassComplete; - } - if (isSet(Flags & LLVMRustDIFlags::FlagObjectPointer)) { - Result |= DINode::DIFlags::FlagObjectPointer; - } - if (isSet(Flags & LLVMRustDIFlags::FlagVector)) { - Result |= DINode::DIFlags::FlagVector; - } - if (isSet(Flags & LLVMRustDIFlags::FlagStaticMember)) { - Result |= DINode::DIFlags::FlagStaticMember; - } - if (isSet(Flags & LLVMRustDIFlags::FlagLValueReference)) { - Result |= DINode::DIFlags::FlagLValueReference; - } - if (isSet(Flags & LLVMRustDIFlags::FlagRValueReference)) { - Result |= DINode::DIFlags::FlagRValueReference; - } - if (isSet(Flags & LLVMRustDIFlags::FlagIntroducedVirtual)) { - Result |= DINode::DIFlags::FlagIntroducedVirtual; - } - if (isSet(Flags & LLVMRustDIFlags::FlagBitField)) { - Result |= DINode::DIFlags::FlagBitField; - } - if (isSet(Flags & LLVMRustDIFlags::FlagNoReturn)) { - Result |= DINode::DIFlags::FlagNoReturn; - } - - return Result; -} - -// These values **must** match debuginfo::DISPFlags! They also *happen* -// to match LLVM, but that isn't required as we do giant sets of -// matching below. The value shouldn't be directly passed to LLVM. -enum class LLVMRustDISPFlags : uint32_t { - SPFlagZero = 0, - SPFlagVirtual = 1, - SPFlagPureVirtual = 2, - SPFlagLocalToUnit = (1 << 2), - SPFlagDefinition = (1 << 3), - SPFlagOptimized = (1 << 4), - SPFlagMainSubprogram = (1 << 5), - // Do not add values that are not supported by the minimum LLVM - // version we support! see llvm/include/llvm/IR/DebugInfoFlags.def - // (In LLVM < 8, createFunction supported these as separate bool arguments.) -}; - -inline LLVMRustDISPFlags operator&(LLVMRustDISPFlags A, LLVMRustDISPFlags B) { - return static_cast(static_cast(A) & - static_cast(B)); -} - -inline LLVMRustDISPFlags operator|(LLVMRustDISPFlags A, LLVMRustDISPFlags B) { - return static_cast(static_cast(A) | - static_cast(B)); -} - -inline LLVMRustDISPFlags &operator|=(LLVMRustDISPFlags &A, LLVMRustDISPFlags B) { - return A = A | B; -} - -inline bool isSet(LLVMRustDISPFlags F) { return F != LLVMRustDISPFlags::SPFlagZero; } - -inline LLVMRustDISPFlags virtuality(LLVMRustDISPFlags F) { - return static_cast(static_cast(F) & 0x3); -} - -static DISubprogram::DISPFlags fromRust(LLVMRustDISPFlags SPFlags) { - DISubprogram::DISPFlags Result = DISubprogram::DISPFlags::SPFlagZero; - - switch (virtuality(SPFlags)) { - case LLVMRustDISPFlags::SPFlagVirtual: - Result |= DISubprogram::DISPFlags::SPFlagVirtual; - break; - case LLVMRustDISPFlags::SPFlagPureVirtual: - Result |= DISubprogram::DISPFlags::SPFlagPureVirtual; - break; - default: - // The rest are handled below - break; - } - - if (isSet(SPFlags & LLVMRustDISPFlags::SPFlagLocalToUnit)) { - Result |= DISubprogram::DISPFlags::SPFlagLocalToUnit; - } - if (isSet(SPFlags & LLVMRustDISPFlags::SPFlagDefinition)) { - Result |= DISubprogram::DISPFlags::SPFlagDefinition; - } - if (isSet(SPFlags & LLVMRustDISPFlags::SPFlagOptimized)) { - Result |= DISubprogram::DISPFlags::SPFlagOptimized; - } -#if LLVM_VERSION_GE(9, 0) - if (isSet(SPFlags & LLVMRustDISPFlags::SPFlagMainSubprogram)) { - Result |= DISubprogram::DISPFlags::SPFlagMainSubprogram; - } -#endif - - return Result; -} - -enum class LLVMRustDebugEmissionKind { - NoDebug, - FullDebug, - LineTablesOnly, -}; - -static DICompileUnit::DebugEmissionKind fromRust(LLVMRustDebugEmissionKind Kind) { - switch (Kind) { - case LLVMRustDebugEmissionKind::NoDebug: - return DICompileUnit::DebugEmissionKind::NoDebug; - case LLVMRustDebugEmissionKind::FullDebug: - return DICompileUnit::DebugEmissionKind::FullDebug; - case LLVMRustDebugEmissionKind::LineTablesOnly: - return DICompileUnit::DebugEmissionKind::LineTablesOnly; - default: - report_fatal_error("bad DebugEmissionKind."); - } -} - -enum class LLVMRustChecksumKind { - None, - MD5, - SHA1, -}; - -static Optional fromRust(LLVMRustChecksumKind Kind) { - switch (Kind) { - case LLVMRustChecksumKind::None: - return None; - case LLVMRustChecksumKind::MD5: - return DIFile::ChecksumKind::CSK_MD5; - case LLVMRustChecksumKind::SHA1: - return DIFile::ChecksumKind::CSK_SHA1; - default: - report_fatal_error("bad ChecksumKind."); - } -} - -extern "C" uint32_t LLVMRustDebugMetadataVersion() { - return DEBUG_METADATA_VERSION; -} - -extern "C" uint32_t LLVMRustVersionMinor() { return LLVM_VERSION_MINOR; } - -extern "C" uint32_t LLVMRustVersionMajor() { return LLVM_VERSION_MAJOR; } - -extern "C" void LLVMRustAddModuleFlag(LLVMModuleRef M, const char *Name, - uint32_t Value) { - unwrap(M)->addModuleFlag(Module::Warning, Name, Value); -} - -extern "C" LLVMValueRef LLVMRustMetadataAsValue(LLVMContextRef C, LLVMMetadataRef MD) { - return wrap(MetadataAsValue::get(*unwrap(C), unwrap(MD))); -} - -extern "C" LLVMRustDIBuilderRef LLVMRustDIBuilderCreate(LLVMModuleRef M) { - return new DIBuilder(*unwrap(M)); -} - -extern "C" void LLVMRustDIBuilderDispose(LLVMRustDIBuilderRef Builder) { - delete Builder; -} - -extern "C" void LLVMRustDIBuilderFinalize(LLVMRustDIBuilderRef Builder) { - Builder->finalize(); -} - -extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateCompileUnit( - LLVMRustDIBuilderRef Builder, unsigned Lang, LLVMMetadataRef FileRef, - const char *Producer, size_t ProducerLen, bool isOptimized, - const char *Flags, unsigned RuntimeVer, - const char *SplitName, size_t SplitNameLen, - LLVMRustDebugEmissionKind Kind) { - auto *File = unwrapDI(FileRef); - - return wrap(Builder->createCompileUnit(Lang, File, StringRef(Producer, ProducerLen), - isOptimized, Flags, RuntimeVer, - StringRef(SplitName, SplitNameLen), - fromRust(Kind))); -} - -extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateFile( - LLVMRustDIBuilderRef Builder, - const char *Filename, size_t FilenameLen, - const char *Directory, size_t DirectoryLen, LLVMRustChecksumKind CSKind, - const char *Checksum, size_t ChecksumLen) { - Optional llvmCSKind = fromRust(CSKind); - Optional> CSInfo{}; - if (llvmCSKind) - CSInfo.emplace(*llvmCSKind, StringRef{Checksum, ChecksumLen}); - return wrap(Builder->createFile(StringRef(Filename, FilenameLen), - StringRef(Directory, DirectoryLen), - CSInfo)); -} - -extern "C" LLVMMetadataRef -LLVMRustDIBuilderCreateSubroutineType(LLVMRustDIBuilderRef Builder, - LLVMMetadataRef ParameterTypes) { - return wrap(Builder->createSubroutineType( - DITypeRefArray(unwrap(ParameterTypes)))); -} - -extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateFunction( - LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, - const char *Name, size_t NameLen, - const char *LinkageName, size_t LinkageNameLen, - LLVMMetadataRef File, unsigned LineNo, - LLVMMetadataRef Ty, unsigned ScopeLine, LLVMRustDIFlags Flags, - LLVMRustDISPFlags SPFlags, LLVMValueRef Fn, LLVMMetadataRef TParam, - LLVMMetadataRef Decl) { - DITemplateParameterArray TParams = - DITemplateParameterArray(unwrap(TParam)); - DISubprogram::DISPFlags llvmSPFlags = fromRust(SPFlags); - DINode::DIFlags llvmFlags = fromRust(Flags); -#if LLVM_VERSION_LT(9, 0) - if (isSet(SPFlags & LLVMRustDISPFlags::SPFlagMainSubprogram)) - llvmFlags |= DINode::DIFlags::FlagMainSubprogram; -#endif - DISubprogram *Sub = Builder->createFunction( - unwrapDI(Scope), - StringRef(Name, NameLen), - StringRef(LinkageName, LinkageNameLen), - unwrapDI(File), LineNo, - unwrapDI(Ty), ScopeLine, llvmFlags, - llvmSPFlags, TParams, unwrapDIPtr(Decl)); - unwrap(Fn)->setSubprogram(Sub); - return wrap(Sub); -} - -extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateBasicType( - LLVMRustDIBuilderRef Builder, const char *Name, size_t NameLen, - uint64_t SizeInBits, unsigned Encoding) { - return wrap(Builder->createBasicType(StringRef(Name, NameLen), SizeInBits, Encoding)); -} - -extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateTypedef( - LLVMRustDIBuilderRef Builder, LLVMMetadataRef Type, const char *Name, size_t NameLen, - LLVMMetadataRef File, unsigned LineNo, LLVMMetadataRef Scope) { - return wrap(Builder->createTypedef( - unwrap(Type), StringRef(Name, NameLen), unwrap(File), - LineNo, unwrap(Scope))); -} - -extern "C" LLVMMetadataRef LLVMRustDIBuilderCreatePointerType( - LLVMRustDIBuilderRef Builder, LLVMMetadataRef PointeeTy, - uint64_t SizeInBits, uint32_t AlignInBits, unsigned AddressSpace, - const char *Name, size_t NameLen) { - return wrap(Builder->createPointerType(unwrapDI(PointeeTy), - SizeInBits, AlignInBits, - AddressSpace, - StringRef(Name, NameLen))); -} - -extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateStructType( - LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, - const char *Name, size_t NameLen, - LLVMMetadataRef File, unsigned LineNumber, uint64_t SizeInBits, - uint32_t AlignInBits, LLVMRustDIFlags Flags, - LLVMMetadataRef DerivedFrom, LLVMMetadataRef Elements, - unsigned RunTimeLang, LLVMMetadataRef VTableHolder, - const char *UniqueId, size_t UniqueIdLen) { - return wrap(Builder->createStructType( - unwrapDI(Scope), StringRef(Name, NameLen), - unwrapDI(File), LineNumber, - SizeInBits, AlignInBits, fromRust(Flags), unwrapDI(DerivedFrom), - DINodeArray(unwrapDI(Elements)), RunTimeLang, - unwrapDI(VTableHolder), StringRef(UniqueId, UniqueIdLen))); -} - -extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateVariantPart( - LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, - const char *Name, size_t NameLen, - LLVMMetadataRef File, unsigned LineNumber, uint64_t SizeInBits, - uint32_t AlignInBits, LLVMRustDIFlags Flags, LLVMMetadataRef Discriminator, - LLVMMetadataRef Elements, const char *UniqueId, size_t UniqueIdLen) { - return wrap(Builder->createVariantPart( - unwrapDI(Scope), StringRef(Name, NameLen), - unwrapDI(File), LineNumber, - SizeInBits, AlignInBits, fromRust(Flags), unwrapDI(Discriminator), - DINodeArray(unwrapDI(Elements)), StringRef(UniqueId, UniqueIdLen))); -} - -extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateMemberType( - LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, - const char *Name, size_t NameLen, - LLVMMetadataRef File, unsigned LineNo, uint64_t SizeInBits, - uint32_t AlignInBits, uint64_t OffsetInBits, LLVMRustDIFlags Flags, - LLVMMetadataRef Ty) { - return wrap(Builder->createMemberType(unwrapDI(Scope), - StringRef(Name, NameLen), - unwrapDI(File), LineNo, - SizeInBits, AlignInBits, OffsetInBits, - fromRust(Flags), unwrapDI(Ty))); -} - -extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateVariantMemberType( - LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, - const char *Name, size_t NameLen, LLVMMetadataRef File, unsigned LineNo, - uint64_t SizeInBits, uint32_t AlignInBits, uint64_t OffsetInBits, LLVMValueRef Discriminant, - LLVMRustDIFlags Flags, LLVMMetadataRef Ty) { - llvm::ConstantInt* D = nullptr; - if (Discriminant) { - D = unwrap(Discriminant); - } - return wrap(Builder->createVariantMemberType(unwrapDI(Scope), - StringRef(Name, NameLen), - unwrapDI(File), LineNo, - SizeInBits, AlignInBits, OffsetInBits, D, - fromRust(Flags), unwrapDI(Ty))); -} - -extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateLexicalBlock( - LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, - LLVMMetadataRef File, unsigned Line, unsigned Col) { - return wrap(Builder->createLexicalBlock(unwrapDI(Scope), - unwrapDI(File), Line, Col)); -} - -extern "C" LLVMMetadataRef -LLVMRustDIBuilderCreateLexicalBlockFile(LLVMRustDIBuilderRef Builder, - LLVMMetadataRef Scope, - LLVMMetadataRef File) { - return wrap(Builder->createLexicalBlockFile(unwrapDI(Scope), - unwrapDI(File))); -} - -extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateStaticVariable( - LLVMRustDIBuilderRef Builder, LLVMMetadataRef Context, - const char *Name, size_t NameLen, - const char *LinkageName, size_t LinkageNameLen, - LLVMMetadataRef File, unsigned LineNo, - LLVMMetadataRef Ty, bool IsLocalToUnit, LLVMValueRef V, - LLVMMetadataRef Decl = nullptr, uint32_t AlignInBits = 0) { - llvm::GlobalVariable *InitVal = cast(unwrap(V)); - - llvm::DIExpression *InitExpr = nullptr; - if (llvm::ConstantInt *IntVal = llvm::dyn_cast(InitVal)) { - InitExpr = Builder->createConstantValueExpression( - IntVal->getValue().getSExtValue()); - } else if (llvm::ConstantFP *FPVal = - llvm::dyn_cast(InitVal)) { - InitExpr = Builder->createConstantValueExpression( - FPVal->getValueAPF().bitcastToAPInt().getZExtValue()); - } - - llvm::DIGlobalVariableExpression *VarExpr = Builder->createGlobalVariableExpression( - unwrapDI(Context), StringRef(Name, NameLen), - StringRef(LinkageName, LinkageNameLen), - unwrapDI(File), LineNo, unwrapDI(Ty), IsLocalToUnit, -#if LLVM_VERSION_GE(10, 0) - /* isDefined */ true, -#endif - InitExpr, unwrapDIPtr(Decl), - /* templateParams */ nullptr, - AlignInBits); - - InitVal->setMetadata("dbg", VarExpr); - - return wrap(VarExpr); -} - -extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateVariable( - LLVMRustDIBuilderRef Builder, unsigned Tag, LLVMMetadataRef Scope, - const char *Name, size_t NameLen, - LLVMMetadataRef File, unsigned LineNo, - LLVMMetadataRef Ty, bool AlwaysPreserve, LLVMRustDIFlags Flags, - unsigned ArgNo, uint32_t AlignInBits) { - if (Tag == 0x100) { // DW_TAG_auto_variable - return wrap(Builder->createAutoVariable( - unwrapDI(Scope), StringRef(Name, NameLen), - unwrapDI(File), LineNo, - unwrapDI(Ty), AlwaysPreserve, fromRust(Flags), AlignInBits)); - } else { - return wrap(Builder->createParameterVariable( - unwrapDI(Scope), StringRef(Name, NameLen), ArgNo, - unwrapDI(File), LineNo, - unwrapDI(Ty), AlwaysPreserve, fromRust(Flags))); - } -} - -extern "C" LLVMMetadataRef -LLVMRustDIBuilderCreateArrayType(LLVMRustDIBuilderRef Builder, uint64_t Size, - uint32_t AlignInBits, LLVMMetadataRef Ty, - LLVMMetadataRef Subscripts) { - return wrap( - Builder->createArrayType(Size, AlignInBits, unwrapDI(Ty), - DINodeArray(unwrapDI(Subscripts)))); -} - -extern "C" LLVMMetadataRef -LLVMRustDIBuilderGetOrCreateSubrange(LLVMRustDIBuilderRef Builder, int64_t Lo, - int64_t Count) { - return wrap(Builder->getOrCreateSubrange(Lo, Count)); -} - -extern "C" LLVMMetadataRef -LLVMRustDIBuilderGetOrCreateArray(LLVMRustDIBuilderRef Builder, - LLVMMetadataRef *Ptr, unsigned Count) { - Metadata **DataValue = unwrap(Ptr); - return wrap( - Builder->getOrCreateArray(ArrayRef(DataValue, Count)).get()); -} - -extern "C" LLVMValueRef LLVMRustDIBuilderInsertDeclareAtEnd( - LLVMRustDIBuilderRef Builder, LLVMValueRef V, LLVMMetadataRef VarInfo, - int64_t *AddrOps, unsigned AddrOpsCount, LLVMValueRef DL, - LLVMBasicBlockRef InsertAtEnd) { - return wrap(Builder->insertDeclare( - unwrap(V), unwrap(VarInfo), - Builder->createExpression(llvm::ArrayRef(AddrOps, AddrOpsCount)), - DebugLoc(cast(unwrap(DL)->getMetadata())), - unwrap(InsertAtEnd))); -} - -extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateEnumerator( - LLVMRustDIBuilderRef Builder, const char *Name, size_t NameLen, - int64_t Value, bool IsUnsigned) { - return wrap(Builder->createEnumerator(StringRef(Name, NameLen), Value, IsUnsigned)); -} - -extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateEnumerationType( - LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, - const char *Name, size_t NameLen, - LLVMMetadataRef File, unsigned LineNumber, uint64_t SizeInBits, - uint32_t AlignInBits, LLVMMetadataRef Elements, - LLVMMetadataRef ClassTy, bool IsScoped) { - return wrap(Builder->createEnumerationType( - unwrapDI(Scope), StringRef(Name, NameLen), - unwrapDI(File), LineNumber, - SizeInBits, AlignInBits, DINodeArray(unwrapDI(Elements)), - unwrapDI(ClassTy), "", IsScoped)); -} - -extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateUnionType( - LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, - const char *Name, size_t NameLen, - LLVMMetadataRef File, unsigned LineNumber, uint64_t SizeInBits, - uint32_t AlignInBits, LLVMRustDIFlags Flags, LLVMMetadataRef Elements, - unsigned RunTimeLang, const char *UniqueId, size_t UniqueIdLen) { - return wrap(Builder->createUnionType( - unwrapDI(Scope), StringRef(Name, NameLen), unwrapDI(File), - LineNumber, SizeInBits, AlignInBits, fromRust(Flags), - DINodeArray(unwrapDI(Elements)), RunTimeLang, - StringRef(UniqueId, UniqueIdLen))); -} - -extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateTemplateTypeParameter( - LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, - const char *Name, size_t NameLen, LLVMMetadataRef Ty) { -#if LLVM_VERSION_GE(11, 0) - bool IsDefault = false; // FIXME: should we ever set this true? - return wrap(Builder->createTemplateTypeParameter( - unwrapDI(Scope), StringRef(Name, NameLen), unwrapDI(Ty), IsDefault)); -#else - return wrap(Builder->createTemplateTypeParameter( - unwrapDI(Scope), StringRef(Name, NameLen), unwrapDI(Ty))); -#endif -} - -extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateNameSpace( - LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, - const char *Name, size_t NameLen, bool ExportSymbols) { - return wrap(Builder->createNameSpace( - unwrapDI(Scope), StringRef(Name, NameLen), ExportSymbols - )); -} - -extern "C" void -LLVMRustDICompositeTypeReplaceArrays(LLVMRustDIBuilderRef Builder, - LLVMMetadataRef CompositeTy, - LLVMMetadataRef Elements, - LLVMMetadataRef Params) { - DICompositeType *Tmp = unwrapDI(CompositeTy); - Builder->replaceArrays(Tmp, DINodeArray(unwrap(Elements)), - DINodeArray(unwrap(Params))); -} - -extern "C" LLVMValueRef -LLVMRustDIBuilderCreateDebugLocation(LLVMContextRef ContextRef, unsigned Line, - unsigned Column, LLVMMetadataRef Scope, - LLVMMetadataRef InlinedAt) { - LLVMContext &Context = *unwrap(ContextRef); - - DebugLoc debug_loc = DebugLoc::get(Line, Column, unwrapDIPtr(Scope), - unwrapDIPtr(InlinedAt)); - - return wrap(MetadataAsValue::get(Context, debug_loc.getAsMDNode())); -} - -extern "C" int64_t LLVMRustDIBuilderCreateOpDeref() { - return dwarf::DW_OP_deref; -} - -extern "C" int64_t LLVMRustDIBuilderCreateOpPlusUconst() { - return dwarf::DW_OP_plus_uconst; -} - -extern "C" void LLVMRustWriteTypeToString(LLVMTypeRef Ty, RustStringRef Str) { - RawRustStringOstream OS(Str); - unwrap(Ty)->print(OS); -} - -extern "C" void LLVMRustWriteValueToString(LLVMValueRef V, - RustStringRef Str) { - RawRustStringOstream OS(Str); - if (!V) { - OS << "(null)"; - } else { - OS << "("; - unwrap(V)->getType()->print(OS); - OS << ":"; - unwrap(V)->print(OS); - OS << ")"; - } -} - -// Note that the two following functions look quite similar to the -// LLVMGetSectionName function. Sadly, it appears that this function only -// returns a char* pointer, which isn't guaranteed to be null-terminated. The -// function provided by LLVM doesn't return the length, so we've created our own -// function which returns the length as well as the data pointer. -// -// For an example of this not returning a null terminated string, see -// lib/Object/COFFObjectFile.cpp in the getSectionName function. One of the -// branches explicitly creates a StringRef without a null terminator, and then -// that's returned. - -inline section_iterator *unwrap(LLVMSectionIteratorRef SI) { - return reinterpret_cast(SI); -} - -extern "C" size_t LLVMRustGetSectionName(LLVMSectionIteratorRef SI, - const char **Ptr) { -#if LLVM_VERSION_GE(10, 0) - auto NameOrErr = (*unwrap(SI))->getName(); - if (!NameOrErr) - report_fatal_error(NameOrErr.takeError()); - *Ptr = NameOrErr->data(); - return NameOrErr->size(); -#else - StringRef Ret; - if (std::error_code EC = (*unwrap(SI))->getName(Ret)) - report_fatal_error(EC.message()); - *Ptr = Ret.data(); - return Ret.size(); -#endif -} - -// LLVMArrayType function does not support 64-bit ElementCount -extern "C" LLVMTypeRef LLVMRustArrayType(LLVMTypeRef ElementTy, - uint64_t ElementCount) { - return wrap(ArrayType::get(unwrap(ElementTy), ElementCount)); -} - -DEFINE_SIMPLE_CONVERSION_FUNCTIONS(Twine, LLVMTwineRef) - -extern "C" void LLVMRustWriteTwineToString(LLVMTwineRef T, RustStringRef Str) { - RawRustStringOstream OS(Str); - unwrap(T)->print(OS); -} - -extern "C" void LLVMRustUnpackOptimizationDiagnostic( - LLVMDiagnosticInfoRef DI, RustStringRef PassNameOut, - LLVMValueRef *FunctionOut, unsigned* Line, unsigned* Column, - RustStringRef FilenameOut, RustStringRef MessageOut) { - // Undefined to call this not on an optimization diagnostic! - llvm::DiagnosticInfoOptimizationBase *Opt = - static_cast(unwrap(DI)); - - RawRustStringOstream PassNameOS(PassNameOut); - PassNameOS << Opt->getPassName(); - *FunctionOut = wrap(&Opt->getFunction()); - - RawRustStringOstream FilenameOS(FilenameOut); - DiagnosticLocation loc = Opt->getLocation(); - if (loc.isValid()) { - *Line = loc.getLine(); - *Column = loc.getColumn(); - FilenameOS << loc.getAbsolutePath(); - } - - RawRustStringOstream MessageOS(MessageOut); - MessageOS << Opt->getMsg(); -} - -enum class LLVMRustDiagnosticLevel { - Error, - Warning, - Note, - Remark, -}; - -extern "C" void -LLVMRustUnpackInlineAsmDiagnostic(LLVMDiagnosticInfoRef DI, - LLVMRustDiagnosticLevel *LevelOut, - unsigned *CookieOut, - LLVMTwineRef *MessageOut, - LLVMValueRef *InstructionOut) { - // Undefined to call this not on an inline assembly diagnostic! - llvm::DiagnosticInfoInlineAsm *IA = - static_cast(unwrap(DI)); - - *CookieOut = IA->getLocCookie(); - *MessageOut = wrap(&IA->getMsgStr()); - *InstructionOut = wrap(IA->getInstruction()); - - switch (IA->getSeverity()) { - case DS_Error: - *LevelOut = LLVMRustDiagnosticLevel::Error; - break; - case DS_Warning: - *LevelOut = LLVMRustDiagnosticLevel::Warning; - break; - case DS_Note: - *LevelOut = LLVMRustDiagnosticLevel::Note; - break; - case DS_Remark: - *LevelOut = LLVMRustDiagnosticLevel::Remark; - break; - default: - report_fatal_error("Invalid LLVMRustDiagnosticLevel value!"); - } -} - -extern "C" void LLVMRustWriteDiagnosticInfoToString(LLVMDiagnosticInfoRef DI, - RustStringRef Str) { - RawRustStringOstream OS(Str); - DiagnosticPrinterRawOStream DP(OS); - unwrap(DI)->print(DP); -} - -enum class LLVMRustDiagnosticKind { - Other, - InlineAsm, - StackSize, - DebugMetadataVersion, - SampleProfile, - OptimizationRemark, - OptimizationRemarkMissed, - OptimizationRemarkAnalysis, - OptimizationRemarkAnalysisFPCommute, - OptimizationRemarkAnalysisAliasing, - OptimizationRemarkOther, - OptimizationFailure, - PGOProfile, - Linker, -}; - -static LLVMRustDiagnosticKind toRust(DiagnosticKind Kind) { - switch (Kind) { - case DK_InlineAsm: - return LLVMRustDiagnosticKind::InlineAsm; - case DK_StackSize: - return LLVMRustDiagnosticKind::StackSize; - case DK_DebugMetadataVersion: - return LLVMRustDiagnosticKind::DebugMetadataVersion; - case DK_SampleProfile: - return LLVMRustDiagnosticKind::SampleProfile; - case DK_OptimizationRemark: - return LLVMRustDiagnosticKind::OptimizationRemark; - case DK_OptimizationRemarkMissed: - return LLVMRustDiagnosticKind::OptimizationRemarkMissed; - case DK_OptimizationRemarkAnalysis: - return LLVMRustDiagnosticKind::OptimizationRemarkAnalysis; - case DK_OptimizationRemarkAnalysisFPCommute: - return LLVMRustDiagnosticKind::OptimizationRemarkAnalysisFPCommute; - case DK_OptimizationRemarkAnalysisAliasing: - return LLVMRustDiagnosticKind::OptimizationRemarkAnalysisAliasing; - case DK_PGOProfile: - return LLVMRustDiagnosticKind::PGOProfile; - case DK_Linker: - return LLVMRustDiagnosticKind::Linker; - default: - return (Kind >= DK_FirstRemark && Kind <= DK_LastRemark) - ? LLVMRustDiagnosticKind::OptimizationRemarkOther - : LLVMRustDiagnosticKind::Other; - } -} - -extern "C" LLVMRustDiagnosticKind -LLVMRustGetDiagInfoKind(LLVMDiagnosticInfoRef DI) { - return toRust((DiagnosticKind)unwrap(DI)->getKind()); -} - -// This is kept distinct from LLVMGetTypeKind, because when -// a new type kind is added, the Rust-side enum must be -// updated or UB will result. -extern "C" LLVMTypeKind LLVMRustGetTypeKind(LLVMTypeRef Ty) { - switch (unwrap(Ty)->getTypeID()) { - case Type::VoidTyID: - return LLVMVoidTypeKind; - case Type::HalfTyID: - return LLVMHalfTypeKind; - case Type::FloatTyID: - return LLVMFloatTypeKind; - case Type::DoubleTyID: - return LLVMDoubleTypeKind; - case Type::X86_FP80TyID: - return LLVMX86_FP80TypeKind; - case Type::FP128TyID: - return LLVMFP128TypeKind; - case Type::PPC_FP128TyID: - return LLVMPPC_FP128TypeKind; - case Type::LabelTyID: - return LLVMLabelTypeKind; - case Type::MetadataTyID: - return LLVMMetadataTypeKind; - case Type::IntegerTyID: - return LLVMIntegerTypeKind; - case Type::FunctionTyID: - return LLVMFunctionTypeKind; - case Type::StructTyID: - return LLVMStructTypeKind; - case Type::ArrayTyID: - return LLVMArrayTypeKind; - case Type::PointerTyID: - return LLVMPointerTypeKind; -#if LLVM_VERSION_GE(11, 0) - case Type::FixedVectorTyID: - return LLVMVectorTypeKind; -#else - case Type::VectorTyID: - return LLVMVectorTypeKind; -#endif - case Type::X86_MMXTyID: - return LLVMX86_MMXTypeKind; - case Type::TokenTyID: - return LLVMTokenTypeKind; -#if LLVM_VERSION_GE(11, 0) - case Type::ScalableVectorTyID: - return LLVMScalableVectorTypeKind; - case Type::BFloatTyID: - return LLVMBFloatTypeKind; -#endif - } - report_fatal_error("Unhandled TypeID."); -} - -DEFINE_SIMPLE_CONVERSION_FUNCTIONS(SMDiagnostic, LLVMSMDiagnosticRef) - -extern "C" void LLVMRustSetInlineAsmDiagnosticHandler( - LLVMContextRef C, LLVMContext::InlineAsmDiagHandlerTy H, void *CX) { - unwrap(C)->setInlineAsmDiagnosticHandler(H, CX); -} - -extern "C" bool LLVMRustUnpackSMDiagnostic(LLVMSMDiagnosticRef DRef, - RustStringRef MessageOut, - RustStringRef BufferOut, - LLVMRustDiagnosticLevel* LevelOut, - unsigned* LocOut, - unsigned* RangesOut, - size_t* NumRanges) { - SMDiagnostic& D = *unwrap(DRef); - RawRustStringOstream MessageOS(MessageOut); - MessageOS << D.getMessage(); - - switch (D.getKind()) { - case SourceMgr::DK_Error: - *LevelOut = LLVMRustDiagnosticLevel::Error; - break; - case SourceMgr::DK_Warning: - *LevelOut = LLVMRustDiagnosticLevel::Warning; - break; - case SourceMgr::DK_Note: - *LevelOut = LLVMRustDiagnosticLevel::Note; - break; - case SourceMgr::DK_Remark: - *LevelOut = LLVMRustDiagnosticLevel::Remark; - break; - default: - report_fatal_error("Invalid LLVMRustDiagnosticLevel value!"); - } - - if (D.getLoc() == SMLoc()) - return false; - - const SourceMgr &LSM = *D.getSourceMgr(); - const MemoryBuffer *LBuf = LSM.getMemoryBuffer(LSM.FindBufferContainingLoc(D.getLoc())); - LLVMRustStringWriteImpl(BufferOut, LBuf->getBufferStart(), LBuf->getBufferSize()); - - *LocOut = D.getLoc().getPointer() - LBuf->getBufferStart(); - - *NumRanges = std::min(*NumRanges, D.getRanges().size()); - size_t LineStart = *LocOut - (size_t)D.getColumnNo(); - for (size_t i = 0; i < *NumRanges; i++) { - RangesOut[i * 2] = LineStart + D.getRanges()[i].first; - RangesOut[i * 2 + 1] = LineStart + D.getRanges()[i].second; - } - - return true; -} - -extern "C" LLVMValueRef LLVMRustBuildCleanupPad(LLVMBuilderRef B, - LLVMValueRef ParentPad, - unsigned ArgCount, - LLVMValueRef *LLArgs, - const char *Name) { - Value **Args = unwrap(LLArgs); - if (ParentPad == nullptr) { - Type *Ty = Type::getTokenTy(unwrap(B)->getContext()); - ParentPad = wrap(Constant::getNullValue(Ty)); - } - return wrap(unwrap(B)->CreateCleanupPad( - unwrap(ParentPad), ArrayRef(Args, ArgCount), Name)); -} - -extern "C" LLVMValueRef LLVMRustBuildCleanupRet(LLVMBuilderRef B, - LLVMValueRef CleanupPad, - LLVMBasicBlockRef UnwindBB) { - CleanupPadInst *Inst = cast(unwrap(CleanupPad)); - return wrap(unwrap(B)->CreateCleanupRet(Inst, unwrap(UnwindBB))); -} - -extern "C" LLVMValueRef -LLVMRustBuildCatchPad(LLVMBuilderRef B, LLVMValueRef ParentPad, - unsigned ArgCount, LLVMValueRef *LLArgs, const char *Name) { - Value **Args = unwrap(LLArgs); - return wrap(unwrap(B)->CreateCatchPad( - unwrap(ParentPad), ArrayRef(Args, ArgCount), Name)); -} - -extern "C" LLVMValueRef LLVMRustBuildCatchRet(LLVMBuilderRef B, - LLVMValueRef Pad, - LLVMBasicBlockRef BB) { - return wrap(unwrap(B)->CreateCatchRet(cast(unwrap(Pad)), - unwrap(BB))); -} - -extern "C" LLVMValueRef LLVMRustBuildCatchSwitch(LLVMBuilderRef B, - LLVMValueRef ParentPad, - LLVMBasicBlockRef BB, - unsigned NumHandlers, - const char *Name) { - if (ParentPad == nullptr) { - Type *Ty = Type::getTokenTy(unwrap(B)->getContext()); - ParentPad = wrap(Constant::getNullValue(Ty)); - } - return wrap(unwrap(B)->CreateCatchSwitch(unwrap(ParentPad), unwrap(BB), - NumHandlers, Name)); -} - -extern "C" void LLVMRustAddHandler(LLVMValueRef CatchSwitchRef, - LLVMBasicBlockRef Handler) { - Value *CatchSwitch = unwrap(CatchSwitchRef); - cast(CatchSwitch)->addHandler(unwrap(Handler)); -} - -extern "C" OperandBundleDef *LLVMRustBuildOperandBundleDef(const char *Name, - LLVMValueRef *Inputs, - unsigned NumInputs) { - return new OperandBundleDef(Name, makeArrayRef(unwrap(Inputs), NumInputs)); -} - -extern "C" void LLVMRustFreeOperandBundleDef(OperandBundleDef *Bundle) { - delete Bundle; -} - -extern "C" LLVMValueRef LLVMRustBuildCall(LLVMBuilderRef B, LLVMValueRef Fn, - LLVMValueRef *Args, unsigned NumArgs, - OperandBundleDef *Bundle) { - Value *Callee = unwrap(Fn); - FunctionType *FTy = cast(Callee->getType()->getPointerElementType()); - unsigned Len = Bundle ? 1 : 0; - ArrayRef Bundles = makeArrayRef(Bundle, Len); - return wrap(unwrap(B)->CreateCall( - FTy, Callee, makeArrayRef(unwrap(Args), NumArgs), Bundles)); -} - -extern "C" LLVMValueRef LLVMRustGetInstrProfIncrementIntrinsic(LLVMModuleRef M) { - return wrap(llvm::Intrinsic::getDeclaration(unwrap(M), - (llvm::Intrinsic::ID)llvm::Intrinsic::instrprof_increment)); -} - -extern "C" LLVMValueRef LLVMRustBuildMemCpy(LLVMBuilderRef B, - LLVMValueRef Dst, unsigned DstAlign, - LLVMValueRef Src, unsigned SrcAlign, - LLVMValueRef Size, bool IsVolatile) { -#if LLVM_VERSION_GE(10, 0) - return wrap(unwrap(B)->CreateMemCpy( - unwrap(Dst), MaybeAlign(DstAlign), - unwrap(Src), MaybeAlign(SrcAlign), - unwrap(Size), IsVolatile)); -#else - return wrap(unwrap(B)->CreateMemCpy( - unwrap(Dst), DstAlign, - unwrap(Src), SrcAlign, - unwrap(Size), IsVolatile)); -#endif -} - -extern "C" LLVMValueRef LLVMRustBuildMemMove(LLVMBuilderRef B, - LLVMValueRef Dst, unsigned DstAlign, - LLVMValueRef Src, unsigned SrcAlign, - LLVMValueRef Size, bool IsVolatile) { -#if LLVM_VERSION_GE(10, 0) - return wrap(unwrap(B)->CreateMemMove( - unwrap(Dst), MaybeAlign(DstAlign), - unwrap(Src), MaybeAlign(SrcAlign), - unwrap(Size), IsVolatile)); -#else - return wrap(unwrap(B)->CreateMemMove( - unwrap(Dst), DstAlign, - unwrap(Src), SrcAlign, - unwrap(Size), IsVolatile)); -#endif -} - -extern "C" LLVMValueRef LLVMRustBuildMemSet(LLVMBuilderRef B, - LLVMValueRef Dst, unsigned DstAlign, - LLVMValueRef Val, - LLVMValueRef Size, bool IsVolatile) { -#if LLVM_VERSION_GE(10, 0) - return wrap(unwrap(B)->CreateMemSet( - unwrap(Dst), unwrap(Val), unwrap(Size), MaybeAlign(DstAlign), IsVolatile)); -#else - return wrap(unwrap(B)->CreateMemSet( - unwrap(Dst), unwrap(Val), unwrap(Size), DstAlign, IsVolatile)); -#endif -} - -extern "C" LLVMValueRef -LLVMRustBuildInvoke(LLVMBuilderRef B, LLVMValueRef Fn, LLVMValueRef *Args, - unsigned NumArgs, LLVMBasicBlockRef Then, - LLVMBasicBlockRef Catch, OperandBundleDef *Bundle, - const char *Name) { - Value *Callee = unwrap(Fn); - FunctionType *FTy = cast(Callee->getType()->getPointerElementType()); - unsigned Len = Bundle ? 1 : 0; - ArrayRef Bundles = makeArrayRef(Bundle, Len); - return wrap(unwrap(B)->CreateInvoke(FTy, Callee, unwrap(Then), unwrap(Catch), - makeArrayRef(unwrap(Args), NumArgs), - Bundles, Name)); -} - -extern "C" void LLVMRustPositionBuilderAtStart(LLVMBuilderRef B, - LLVMBasicBlockRef BB) { - auto Point = unwrap(BB)->getFirstInsertionPt(); - unwrap(B)->SetInsertPoint(unwrap(BB), Point); -} - -extern "C" void LLVMRustSetComdat(LLVMModuleRef M, LLVMValueRef V, - const char *Name, size_t NameLen) { - Triple TargetTriple(unwrap(M)->getTargetTriple()); - GlobalObject *GV = unwrap(V); - if (!TargetTriple.isOSBinFormatMachO()) { - StringRef NameRef(Name, NameLen); - GV->setComdat(unwrap(M)->getOrInsertComdat(NameRef)); - } -} - -extern "C" void LLVMRustUnsetComdat(LLVMValueRef V) { - GlobalObject *GV = unwrap(V); - GV->setComdat(nullptr); -} - -enum class LLVMRustLinkage { - ExternalLinkage = 0, - AvailableExternallyLinkage = 1, - LinkOnceAnyLinkage = 2, - LinkOnceODRLinkage = 3, - WeakAnyLinkage = 4, - WeakODRLinkage = 5, - AppendingLinkage = 6, - InternalLinkage = 7, - PrivateLinkage = 8, - ExternalWeakLinkage = 9, - CommonLinkage = 10, -}; - -static LLVMRustLinkage toRust(LLVMLinkage Linkage) { - switch (Linkage) { - case LLVMExternalLinkage: - return LLVMRustLinkage::ExternalLinkage; - case LLVMAvailableExternallyLinkage: - return LLVMRustLinkage::AvailableExternallyLinkage; - case LLVMLinkOnceAnyLinkage: - return LLVMRustLinkage::LinkOnceAnyLinkage; - case LLVMLinkOnceODRLinkage: - return LLVMRustLinkage::LinkOnceODRLinkage; - case LLVMWeakAnyLinkage: - return LLVMRustLinkage::WeakAnyLinkage; - case LLVMWeakODRLinkage: - return LLVMRustLinkage::WeakODRLinkage; - case LLVMAppendingLinkage: - return LLVMRustLinkage::AppendingLinkage; - case LLVMInternalLinkage: - return LLVMRustLinkage::InternalLinkage; - case LLVMPrivateLinkage: - return LLVMRustLinkage::PrivateLinkage; - case LLVMExternalWeakLinkage: - return LLVMRustLinkage::ExternalWeakLinkage; - case LLVMCommonLinkage: - return LLVMRustLinkage::CommonLinkage; - default: - report_fatal_error("Invalid LLVMRustLinkage value!"); - } -} - -static LLVMLinkage fromRust(LLVMRustLinkage Linkage) { - switch (Linkage) { - case LLVMRustLinkage::ExternalLinkage: - return LLVMExternalLinkage; - case LLVMRustLinkage::AvailableExternallyLinkage: - return LLVMAvailableExternallyLinkage; - case LLVMRustLinkage::LinkOnceAnyLinkage: - return LLVMLinkOnceAnyLinkage; - case LLVMRustLinkage::LinkOnceODRLinkage: - return LLVMLinkOnceODRLinkage; - case LLVMRustLinkage::WeakAnyLinkage: - return LLVMWeakAnyLinkage; - case LLVMRustLinkage::WeakODRLinkage: - return LLVMWeakODRLinkage; - case LLVMRustLinkage::AppendingLinkage: - return LLVMAppendingLinkage; - case LLVMRustLinkage::InternalLinkage: - return LLVMInternalLinkage; - case LLVMRustLinkage::PrivateLinkage: - return LLVMPrivateLinkage; - case LLVMRustLinkage::ExternalWeakLinkage: - return LLVMExternalWeakLinkage; - case LLVMRustLinkage::CommonLinkage: - return LLVMCommonLinkage; - } - report_fatal_error("Invalid LLVMRustLinkage value!"); -} - -extern "C" LLVMRustLinkage LLVMRustGetLinkage(LLVMValueRef V) { - return toRust(LLVMGetLinkage(V)); -} - -extern "C" void LLVMRustSetLinkage(LLVMValueRef V, - LLVMRustLinkage RustLinkage) { - LLVMSetLinkage(V, fromRust(RustLinkage)); -} - -// Returns true if both high and low were successfully set. Fails in case constant wasn’t any of -// the common sizes (1, 8, 16, 32, 64, 128 bits) -extern "C" bool LLVMRustConstInt128Get(LLVMValueRef CV, bool sext, uint64_t *high, uint64_t *low) -{ - auto C = unwrap(CV); - if (C->getBitWidth() > 128) { return false; } - APInt AP; - if (sext) { - AP = C->getValue().sextOrSelf(128); - } else { - AP = C->getValue().zextOrSelf(128); - } - *low = AP.getLoBits(64).getZExtValue(); - *high = AP.getHiBits(64).getZExtValue(); - return true; -} - -enum class LLVMRustVisibility { - Default = 0, - Hidden = 1, - Protected = 2, -}; - -static LLVMRustVisibility toRust(LLVMVisibility Vis) { - switch (Vis) { - case LLVMDefaultVisibility: - return LLVMRustVisibility::Default; - case LLVMHiddenVisibility: - return LLVMRustVisibility::Hidden; - case LLVMProtectedVisibility: - return LLVMRustVisibility::Protected; - } - report_fatal_error("Invalid LLVMRustVisibility value!"); -} - -static LLVMVisibility fromRust(LLVMRustVisibility Vis) { - switch (Vis) { - case LLVMRustVisibility::Default: - return LLVMDefaultVisibility; - case LLVMRustVisibility::Hidden: - return LLVMHiddenVisibility; - case LLVMRustVisibility::Protected: - return LLVMProtectedVisibility; - } - report_fatal_error("Invalid LLVMRustVisibility value!"); -} - -extern "C" LLVMRustVisibility LLVMRustGetVisibility(LLVMValueRef V) { - return toRust(LLVMGetVisibility(V)); -} - -// Oh hey, a binding that makes sense for once? (because LLVM’s own do not) -extern "C" LLVMValueRef LLVMRustBuildIntCast(LLVMBuilderRef B, LLVMValueRef Val, - LLVMTypeRef DestTy, bool isSigned) { - return wrap(unwrap(B)->CreateIntCast(unwrap(Val), unwrap(DestTy), isSigned, "")); -} - -extern "C" void LLVMRustSetVisibility(LLVMValueRef V, - LLVMRustVisibility RustVisibility) { - LLVMSetVisibility(V, fromRust(RustVisibility)); -} - -struct LLVMRustModuleBuffer { - std::string data; -}; - -extern "C" LLVMRustModuleBuffer* -LLVMRustModuleBufferCreate(LLVMModuleRef M) { -#if LLVM_VERSION_GE(10, 0) - auto Ret = std::make_unique(); -#else - auto Ret = llvm::make_unique(); -#endif - { - raw_string_ostream OS(Ret->data); - { - legacy::PassManager PM; - PM.add(createBitcodeWriterPass(OS)); - PM.run(*unwrap(M)); - } - } - return Ret.release(); -} - -extern "C" void -LLVMRustModuleBufferFree(LLVMRustModuleBuffer *Buffer) { - delete Buffer; -} - -extern "C" const void* -LLVMRustModuleBufferPtr(const LLVMRustModuleBuffer *Buffer) { - return Buffer->data.data(); -} - -extern "C" size_t -LLVMRustModuleBufferLen(const LLVMRustModuleBuffer *Buffer) { - return Buffer->data.length(); -} - -extern "C" uint64_t -LLVMRustModuleCost(LLVMModuleRef M) { - auto f = unwrap(M)->functions(); - return std::distance(std::begin(f), std::end(f)); -} - -// Vector reductions: -extern "C" LLVMValueRef -LLVMRustBuildVectorReduceFAdd(LLVMBuilderRef B, LLVMValueRef Acc, LLVMValueRef Src) { - return wrap(unwrap(B)->CreateFAddReduce(unwrap(Acc),unwrap(Src))); -} -extern "C" LLVMValueRef -LLVMRustBuildVectorReduceFMul(LLVMBuilderRef B, LLVMValueRef Acc, LLVMValueRef Src) { - return wrap(unwrap(B)->CreateFMulReduce(unwrap(Acc),unwrap(Src))); -} -extern "C" LLVMValueRef -LLVMRustBuildVectorReduceAdd(LLVMBuilderRef B, LLVMValueRef Src) { - return wrap(unwrap(B)->CreateAddReduce(unwrap(Src))); -} -extern "C" LLVMValueRef -LLVMRustBuildVectorReduceMul(LLVMBuilderRef B, LLVMValueRef Src) { - return wrap(unwrap(B)->CreateMulReduce(unwrap(Src))); -} -extern "C" LLVMValueRef -LLVMRustBuildVectorReduceAnd(LLVMBuilderRef B, LLVMValueRef Src) { - return wrap(unwrap(B)->CreateAndReduce(unwrap(Src))); -} -extern "C" LLVMValueRef -LLVMRustBuildVectorReduceOr(LLVMBuilderRef B, LLVMValueRef Src) { - return wrap(unwrap(B)->CreateOrReduce(unwrap(Src))); -} -extern "C" LLVMValueRef -LLVMRustBuildVectorReduceXor(LLVMBuilderRef B, LLVMValueRef Src) { - return wrap(unwrap(B)->CreateXorReduce(unwrap(Src))); -} -extern "C" LLVMValueRef -LLVMRustBuildVectorReduceMin(LLVMBuilderRef B, LLVMValueRef Src, bool IsSigned) { - return wrap(unwrap(B)->CreateIntMinReduce(unwrap(Src), IsSigned)); -} -extern "C" LLVMValueRef -LLVMRustBuildVectorReduceMax(LLVMBuilderRef B, LLVMValueRef Src, bool IsSigned) { - return wrap(unwrap(B)->CreateIntMaxReduce(unwrap(Src), IsSigned)); -} -extern "C" LLVMValueRef -LLVMRustBuildVectorReduceFMin(LLVMBuilderRef B, LLVMValueRef Src, bool NoNaN) { - return wrap(unwrap(B)->CreateFPMinReduce(unwrap(Src), NoNaN)); -} -extern "C" LLVMValueRef -LLVMRustBuildVectorReduceFMax(LLVMBuilderRef B, LLVMValueRef Src, bool NoNaN) { - return wrap(unwrap(B)->CreateFPMaxReduce(unwrap(Src), NoNaN)); -} - -extern "C" LLVMValueRef -LLVMRustBuildMinNum(LLVMBuilderRef B, LLVMValueRef LHS, LLVMValueRef RHS) { - return wrap(unwrap(B)->CreateMinNum(unwrap(LHS),unwrap(RHS))); -} -extern "C" LLVMValueRef -LLVMRustBuildMaxNum(LLVMBuilderRef B, LLVMValueRef LHS, LLVMValueRef RHS) { - return wrap(unwrap(B)->CreateMaxNum(unwrap(LHS),unwrap(RHS))); -} diff --git a/src/rustllvm/rustllvm.h b/src/rustllvm/rustllvm.h deleted file mode 100644 index 57b8664d3b6..00000000000 --- a/src/rustllvm/rustllvm.h +++ /dev/null @@ -1,115 +0,0 @@ -#include "llvm-c/BitReader.h" -#include "llvm-c/Core.h" -#include "llvm-c/Object.h" -#include "llvm/ADT/ArrayRef.h" -#include "llvm/ADT/DenseSet.h" -#include "llvm/ADT/SmallVector.h" -#include "llvm/ADT/Triple.h" -#include "llvm/Analysis/Lint.h" -#include "llvm/Analysis/Passes.h" -#include "llvm/IR/IRBuilder.h" -#include "llvm/IR/InlineAsm.h" -#include "llvm/IR/LLVMContext.h" -#include "llvm/IR/Module.h" -#include "llvm/Support/CommandLine.h" -#include "llvm/Support/Debug.h" -#include "llvm/Support/DynamicLibrary.h" -#include "llvm/Support/FormattedStream.h" -#include "llvm/Support/Host.h" -#include "llvm/Support/Memory.h" -#include "llvm/Support/SourceMgr.h" -#include "llvm/Support/TargetRegistry.h" -#include "llvm/Support/TargetSelect.h" -#include "llvm/Support/Timer.h" -#include "llvm/Support/raw_ostream.h" -#include "llvm/Target/TargetMachine.h" -#include "llvm/Target/TargetOptions.h" -#include "llvm/Transforms/IPO.h" -#include "llvm/Transforms/Instrumentation.h" -#include "llvm/Transforms/Scalar.h" -#include "llvm/Transforms/Vectorize.h" - -#define LLVM_VERSION_GE(major, minor) \ - (LLVM_VERSION_MAJOR > (major) || \ - LLVM_VERSION_MAJOR == (major) && LLVM_VERSION_MINOR >= (minor)) - -#define LLVM_VERSION_EQ(major, minor) \ - (LLVM_VERSION_MAJOR == (major) && LLVM_VERSION_MINOR == (minor)) - -#define LLVM_VERSION_LE(major, minor) \ - (LLVM_VERSION_MAJOR < (major) || \ - LLVM_VERSION_MAJOR == (major) && LLVM_VERSION_MINOR <= (minor)) - -#define LLVM_VERSION_LT(major, minor) (!LLVM_VERSION_GE((major), (minor))) - -#include "llvm/IR/LegacyPassManager.h" - -#include "llvm/Bitcode/BitcodeReader.h" -#include "llvm/Bitcode/BitcodeWriter.h" - -#include "llvm/IR/DIBuilder.h" -#include "llvm/IR/DebugInfo.h" -#include "llvm/IR/IRPrintingPasses.h" -#include "llvm/Linker/Linker.h" - -extern "C" void LLVMRustSetLastError(const char *); - -enum class LLVMRustResult { Success, Failure }; - -enum LLVMRustAttribute { - AlwaysInline = 0, - ByVal = 1, - Cold = 2, - InlineHint = 3, - MinSize = 4, - Naked = 5, - NoAlias = 6, - NoCapture = 7, - NoInline = 8, - NonNull = 9, - NoRedZone = 10, - NoReturn = 11, - NoUnwind = 12, - OptimizeForSize = 13, - ReadOnly = 14, - SExt = 15, - StructRet = 16, - UWTable = 17, - ZExt = 18, - InReg = 19, - SanitizeThread = 20, - SanitizeAddress = 21, - SanitizeMemory = 22, - NonLazyBind = 23, - OptimizeNone = 24, - ReturnsTwice = 25, - ReadNone = 26, - InaccessibleMemOnly = 27, -}; - -typedef struct OpaqueRustString *RustStringRef; -typedef struct LLVMOpaqueTwine *LLVMTwineRef; -typedef struct LLVMOpaqueSMDiagnostic *LLVMSMDiagnosticRef; - -extern "C" void LLVMRustStringWriteImpl(RustStringRef Str, const char *Ptr, - size_t Size); - -class RawRustStringOstream : public llvm::raw_ostream { - RustStringRef Str; - uint64_t Pos; - - void write_impl(const char *Ptr, size_t Size) override { - LLVMRustStringWriteImpl(Str, Ptr, Size); - Pos += Size; - } - - uint64_t current_pos() const override { return Pos; } - -public: - explicit RawRustStringOstream(RustStringRef Str) : Str(Str), Pos(0) {} - - ~RawRustStringOstream() { - // LLVM requires this. - flush(); - } -}; -- cgit 1.4.1-3-g733a5 From 4f2d94180ddffcb23cb1f3682242df735eeac03f Mon Sep 17 00:00:00 2001 From: Mark Rousskov Date: Thu, 10 Sep 2020 11:44:28 -0400 Subject: Only copy LLVM into rust-dev with internal LLVM This avoids needing to figure out where to locate each of the components with an external LLVM. --- src/bootstrap/dist.rs | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'src/bootstrap') diff --git a/src/bootstrap/dist.rs b/src/bootstrap/dist.rs index 797a1ce20b4..25d0dad5844 100644 --- a/src/bootstrap/dist.rs +++ b/src/bootstrap/dist.rs @@ -2524,6 +2524,14 @@ impl Step for RustDev { fn run(self, builder: &Builder<'_>) -> Option { let target = self.target; + /* run only if llvm-config isn't used */ + if let Some(config) = builder.config.target_config.get(&target) { + if let Some(ref _s) = config.llvm_config { + builder.info(&format!("Skipping RustDev ({}): external LLVM", target)); + return None; + } + } + builder.info(&format!("Dist RustDev ({})", target)); let _time = timeit(builder); let src = builder.src.join("src/llvm-project/llvm"); @@ -2536,6 +2544,7 @@ impl Step for RustDev { // Prepare the image directory let dst_bindir = image.join("bin"); t!(fs::create_dir_all(&dst_bindir)); + let exe = builder.llvm_out(target).join("bin").join(exe("llvm-config", target)); builder.install(&exe, &dst_bindir, 0o755); builder.install(&builder.llvm_filecheck(target), &dst_bindir, 0o755); -- cgit 1.4.1-3-g733a5 From 15aa6f31b974d41ecc0312faf8d50cde19a74cca Mon Sep 17 00:00:00 2001 From: Gus Wynn Date: Thu, 10 Sep 2020 14:58:45 -0700 Subject: add debug-logging to config.toml --- compiler/rustc/Cargo.toml | 1 + compiler/rustc_driver/Cargo.toml | 3 ++- config.toml.example | 6 ++++++ src/bootstrap/config.rs | 6 ++++++ src/bootstrap/lib.rs | 10 ++++++++++ 5 files changed, 25 insertions(+), 1 deletion(-) (limited to 'src/bootstrap') diff --git a/compiler/rustc/Cargo.toml b/compiler/rustc/Cargo.toml index cf011e63e02..b740793a3e5 100644 --- a/compiler/rustc/Cargo.toml +++ b/compiler/rustc/Cargo.toml @@ -19,3 +19,4 @@ features = ['unprefixed_malloc_on_supported_platforms'] [features] jemalloc = ['jemalloc-sys'] llvm = ['rustc_driver/llvm'] +release_max_level_info = ['rustc_driver/release_max_level_info'] diff --git a/compiler/rustc_driver/Cargo.toml b/compiler/rustc_driver/Cargo.toml index 0d9dcb262b2..6f0d7bc4d58 100644 --- a/compiler/rustc_driver/Cargo.toml +++ b/compiler/rustc_driver/Cargo.toml @@ -9,7 +9,7 @@ crate-type = ["dylib"] [dependencies] libc = "0.2" -tracing = { version = "0.1.18", features = ["release_max_level_info"] } +tracing = { version = "0.1.18" } tracing-subscriber = { version = "0.2.10", default-features = false, features = ["fmt", "env-filter", "smallvec", "parking_lot", "ansi"] } rustc_middle = { path = "../rustc_middle" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } @@ -38,3 +38,4 @@ winapi = { version = "0.3", features = ["consoleapi", "debugapi", "processenv"] [features] llvm = ['rustc_interface/llvm'] +release_max_level_info = ['tracing/release_max_level_info'] diff --git a/config.toml.example b/config.toml.example index 9abb8add785..a162c543a50 100644 --- a/config.toml.example +++ b/config.toml.example @@ -329,6 +329,12 @@ # Defaults to rust.debug-assertions value #debug-assertions-std = false +# Whether or not to leave debug! and trace! calls in the rust binary. +# Overrides the `debug-assertions` option, if defined. +# +# Defaults to rust.debug-assertions value +#debug-logging = true + # Debuginfo level for most of Rust code, corresponds to the `-C debuginfo=N` option of `rustc`. # `0` - no debug info # `1` - line tables only - sufficient to generate backtraces that include line diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index ad2f4877867..49a0855b978 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -100,6 +100,7 @@ pub struct Config { pub rust_codegen_units_std: Option, pub rust_debug_assertions: bool, pub rust_debug_assertions_std: bool, + pub rust_debug_logging: bool, pub rust_debuginfo_level_rustc: u32, pub rust_debuginfo_level_std: u32, pub rust_debuginfo_level_tools: u32, @@ -381,6 +382,7 @@ struct Rust { codegen_units_std: Option, debug_assertions: Option, debug_assertions_std: Option, + debug_logging: Option, debuginfo_level: Option, debuginfo_level_rustc: Option, debuginfo_level_std: Option, @@ -591,6 +593,7 @@ impl Config { let mut debug = None; let mut debug_assertions = None; let mut debug_assertions_std = None; + let mut debug_logging = None; let mut debuginfo_level = None; let mut debuginfo_level_rustc = None; let mut debuginfo_level_std = None; @@ -634,6 +637,7 @@ impl Config { debug = rust.debug; debug_assertions = rust.debug_assertions; debug_assertions_std = rust.debug_assertions_std; + debug_logging = rust.debug_logging; debuginfo_level = rust.debuginfo_level; debuginfo_level_rustc = rust.debuginfo_level_rustc; debuginfo_level_std = rust.debuginfo_level_std; @@ -737,6 +741,8 @@ impl Config { config.rust_debug_assertions_std = debug_assertions_std.unwrap_or(config.rust_debug_assertions); + config.rust_debug_logging = debug_logging.unwrap_or(config.rust_debug_assertions); + let with_defaults = |debuginfo_level_specific: Option| { debuginfo_level_specific.or(debuginfo_level).unwrap_or(if debug == Some(true) { 1 diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs index c1dec8ed181..3c2663f6d9f 100644 --- a/src/bootstrap/lib.rs +++ b/src/bootstrap/lib.rs @@ -541,6 +541,16 @@ impl Build { if self.config.llvm_enabled() { features.push_str(" llvm"); } + + // If debug logging is on, then we want the default for tracing: + // https://github.com/tokio-rs/tracing/blob/3dd5c03d907afdf2c39444a29931833335171554/tracing/src/level_filters.rs#L26 + // which is everything (including debug/trace/etc.) + // if its unset, if debug_assertions is on, then debug_logging will also be on + // as well as tracing *ignoring* this feature when debug_assertions is on + if !self.config.rust_debug_logging { + features.push_str(" release_max_level_info"); + } + features } -- cgit 1.4.1-3-g733a5 From 3193d52a21576b8c93f00132c784cce1159f733e Mon Sep 17 00:00:00 2001 From: Mark Rousskov Date: Sun, 6 Sep 2020 12:24:22 -0400 Subject: Remove host parameter from step configurations rustc is a natively cross-compiling compiler, and generally none of our steps should care whether they are using a compiler built of triple A or B, just the --target directive being passed to the running compiler. e.g., when building for some target C, you don't generally want to build two stds: one with a host A compiler and the other with a host B compiler. Just one std is sufficient. --- src/bootstrap/builder.rs | 21 +++++++++------------ src/bootstrap/builder/tests.rs | 10 ---------- src/bootstrap/compile.rs | 6 +++--- src/bootstrap/dist.rs | 4 +++- src/bootstrap/test.rs | 38 ++++++++++++++++++-------------------- src/bootstrap/tool.rs | 5 +++-- 6 files changed, 36 insertions(+), 48 deletions(-) (limited to 'src/bootstrap') diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index 844a29fadae..31d4f1f28a8 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -87,11 +87,16 @@ pub trait Step: 'static + Clone + Debug + PartialEq + Eq + Hash { pub struct RunConfig<'a> { pub builder: &'a Builder<'a>, - pub host: TargetSelection, pub target: TargetSelection, pub path: PathBuf, } +impl RunConfig<'_> { + pub fn build_triple(&self) -> TargetSelection { + self.builder.build.build + } +} + struct StepDescription { default: bool, only_hosts: bool, @@ -165,7 +170,6 @@ impl StepDescription { pathset, self.name, builder.config.exclude ); } - let hosts = &builder.hosts; // Determine the targets participating in this rule. let targets = if self.only_hosts { @@ -178,16 +182,9 @@ impl StepDescription { &builder.targets }; - for host in hosts { - for target in targets { - let run = RunConfig { - builder, - path: pathset.path(builder), - host: *host, - target: *target, - }; - (self.make_run)(run); - } + for target in targets { + let run = RunConfig { builder, path: pathset.path(builder), target: *target }; + (self.make_run)(run); } } diff --git a/src/bootstrap/builder/tests.rs b/src/bootstrap/builder/tests.rs index aeb0d713ef0..144c4dc4c95 100644 --- a/src/bootstrap/builder/tests.rs +++ b/src/bootstrap/builder/tests.rs @@ -384,12 +384,9 @@ mod dist { compile::Std { compiler: Compiler { host: a, stage: 0 }, target: a }, compile::Std { compiler: Compiler { host: a, stage: 1 }, target: a }, compile::Std { compiler: Compiler { host: a, stage: 2 }, target: a }, - compile::Std { compiler: Compiler { host: b, stage: 2 }, target: a }, compile::Std { compiler: Compiler { host: a, stage: 1 }, target: b }, compile::Std { compiler: Compiler { host: a, stage: 2 }, target: b }, - compile::Std { compiler: Compiler { host: b, stage: 2 }, target: b }, compile::Std { compiler: Compiler { host: a, stage: 2 }, target: c }, - compile::Std { compiler: Compiler { host: b, stage: 2 }, target: c }, ] ); assert!(!builder.cache.all::().is_empty()); @@ -399,10 +396,8 @@ mod dist { compile::Rustc { compiler: Compiler { host: a, stage: 0 }, target: a }, compile::Rustc { compiler: Compiler { host: a, stage: 1 }, target: a }, compile::Rustc { compiler: Compiler { host: a, stage: 2 }, target: a }, - compile::Rustc { compiler: Compiler { host: b, stage: 2 }, target: a }, compile::Rustc { compiler: Compiler { host: a, stage: 1 }, target: b }, compile::Rustc { compiler: Compiler { host: a, stage: 2 }, target: b }, - compile::Rustc { compiler: Compiler { host: b, stage: 2 }, target: b }, ] ); } @@ -425,12 +420,9 @@ mod dist { compile::Std { compiler: Compiler { host: a, stage: 0 }, target: a }, compile::Std { compiler: Compiler { host: a, stage: 1 }, target: a }, compile::Std { compiler: Compiler { host: a, stage: 2 }, target: a }, - compile::Std { compiler: Compiler { host: b, stage: 2 }, target: a }, compile::Std { compiler: Compiler { host: a, stage: 1 }, target: b }, compile::Std { compiler: Compiler { host: a, stage: 2 }, target: b }, - compile::Std { compiler: Compiler { host: b, stage: 2 }, target: b }, compile::Std { compiler: Compiler { host: a, stage: 2 }, target: c }, - compile::Std { compiler: Compiler { host: b, stage: 2 }, target: c }, ] ); assert_eq!( @@ -439,7 +431,6 @@ mod dist { compile::Assemble { target_compiler: Compiler { host: a, stage: 0 } }, compile::Assemble { target_compiler: Compiler { host: a, stage: 1 } }, compile::Assemble { target_compiler: Compiler { host: a, stage: 2 } }, - compile::Assemble { target_compiler: Compiler { host: b, stage: 2 } }, ] ); assert_eq!( @@ -447,7 +438,6 @@ mod dist { &[ compile::Rustc { compiler: Compiler { host: a, stage: 0 }, target: a }, compile::Rustc { compiler: Compiler { host: a, stage: 1 }, target: a }, - compile::Rustc { compiler: Compiler { host: a, stage: 1 }, target: b }, ] ); } diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs index 9d314e8452b..e0dddda83b9 100644 --- a/src/bootstrap/compile.rs +++ b/src/bootstrap/compile.rs @@ -45,7 +45,7 @@ impl Step for Std { fn make_run(run: RunConfig<'_>) { run.builder.ensure(Std { - compiler: run.builder.compiler(run.builder.top_stage, run.host), + compiler: run.builder.compiler(run.builder.top_stage, run.build_triple()), target: run.target, }); } @@ -385,7 +385,7 @@ impl Step for StartupObjects { fn make_run(run: RunConfig<'_>) { run.builder.ensure(StartupObjects { - compiler: run.builder.compiler(run.builder.top_stage, run.host), + compiler: run.builder.compiler(run.builder.top_stage, run.build_triple()), target: run.target, }); } @@ -454,7 +454,7 @@ impl Step for Rustc { fn make_run(run: RunConfig<'_>) { run.builder.ensure(Rustc { - compiler: run.builder.compiler(run.builder.top_stage, run.host), + compiler: run.builder.compiler(run.builder.top_stage, run.build_triple()), target: run.target, }); } diff --git a/src/bootstrap/dist.rs b/src/bootstrap/dist.rs index 797a1ce20b4..28a0b411b1d 100644 --- a/src/bootstrap/dist.rs +++ b/src/bootstrap/dist.rs @@ -605,7 +605,9 @@ impl Step for DebuggerScripts { fn make_run(run: RunConfig<'_>) { run.builder.ensure(DebuggerScripts { - sysroot: run.builder.sysroot(run.builder.compiler(run.builder.top_stage, run.host)), + sysroot: run + .builder + .sysroot(run.builder.compiler(run.builder.top_stage, run.build_triple())), host: run.target, }); } diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs index 732028fb6ed..045dda2d4cb 100644 --- a/src/bootstrap/test.rs +++ b/src/bootstrap/test.rs @@ -584,7 +584,7 @@ impl Step for RustdocTheme { } fn make_run(run: RunConfig<'_>) { - let compiler = run.builder.compiler(run.builder.top_stage, run.host); + let compiler = run.builder.compiler(run.builder.top_stage, run.target); run.builder.ensure(RustdocTheme { compiler }); } @@ -651,7 +651,6 @@ impl Step for RustdocJSStd { #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub struct RustdocJSNotStd { - pub host: TargetSelection, pub target: TargetSelection, pub compiler: Compiler, } @@ -666,8 +665,8 @@ impl Step for RustdocJSNotStd { } fn make_run(run: RunConfig<'_>) { - let compiler = run.builder.compiler(run.builder.top_stage, run.host); - run.builder.ensure(RustdocJSNotStd { host: run.host, target: run.target, compiler }); + let compiler = run.builder.compiler(run.builder.top_stage, run.build_triple()); + run.builder.ensure(RustdocJSNotStd { target: run.target, compiler }); } fn run(self, builder: &Builder<'_>) { @@ -688,7 +687,6 @@ impl Step for RustdocJSNotStd { #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub struct RustdocUi { - pub host: TargetSelection, pub target: TargetSelection, pub compiler: Compiler, } @@ -703,8 +701,8 @@ impl Step for RustdocUi { } fn make_run(run: RunConfig<'_>) { - let compiler = run.builder.compiler(run.builder.top_stage, run.host); - run.builder.ensure(RustdocUi { host: run.host, target: run.target, compiler }); + let compiler = run.builder.compiler(run.builder.top_stage, run.build_triple()); + run.builder.ensure(RustdocUi { target: run.target, compiler }); } fn run(self, builder: &Builder<'_>) { @@ -873,7 +871,7 @@ macro_rules! test_definitions { } fn make_run(run: RunConfig<'_>) { - let compiler = run.builder.compiler(run.builder.top_stage, run.host); + let compiler = run.builder.compiler(run.builder.top_stage, run.build_triple()); run.builder.ensure($name { compiler, target: run.target }); } @@ -1422,7 +1420,7 @@ macro_rules! test_book { fn make_run(run: RunConfig<'_>) { run.builder.ensure($name { - compiler: run.builder.compiler(run.builder.top_stage, run.host), + compiler: run.builder.compiler(run.builder.top_stage, run.target), }); } @@ -1469,7 +1467,7 @@ impl Step for ErrorIndex { // error_index_generator depends on librustdoc. Use the compiler that // is normally used to build rustdoc for other tests (like compiletest // tests in src/test/rustdoc) so that it shares the same artifacts. - let compiler = run.builder.compiler_for(run.builder.top_stage, run.host, run.host); + let compiler = run.builder.compiler_for(run.builder.top_stage, run.target, run.target); run.builder.ensure(ErrorIndex { compiler }); } @@ -1573,7 +1571,7 @@ impl Step for CrateLibrustc { fn make_run(run: RunConfig<'_>) { let builder = run.builder; - let compiler = builder.compiler(builder.top_stage, run.host); + let compiler = builder.compiler(builder.top_stage, run.build_triple()); for krate in builder.in_tree_crates("rustc-main") { if krate.path.ends_with(&run.path) { @@ -1620,7 +1618,7 @@ impl Step for CrateNotDefault { fn make_run(run: RunConfig<'_>) { let builder = run.builder; - let compiler = builder.compiler(builder.top_stage, run.host); + let compiler = builder.compiler(builder.top_stage, run.build_triple()); let test_kind = builder.kind.into(); @@ -1668,7 +1666,7 @@ impl Step for Crate { fn make_run(run: RunConfig<'_>) { let builder = run.builder; - let compiler = builder.compiler(builder.top_stage, run.host); + let compiler = builder.compiler(builder.top_stage, run.build_triple()); let make = |mode: Mode, krate: &CargoCrate| { let test_kind = builder.kind.into(); @@ -1808,7 +1806,7 @@ impl Step for CrateRustdoc { let test_kind = builder.kind.into(); - builder.ensure(CrateRustdoc { host: run.host, test_kind }); + builder.ensure(CrateRustdoc { host: run.target, test_kind }); } fn run(self, builder: &Builder<'_>) { @@ -2054,7 +2052,6 @@ impl Step for Bootstrap { #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct TierCheck { pub compiler: Compiler, - target: TargetSelection, } impl Step for TierCheck { @@ -2067,18 +2064,19 @@ impl Step for TierCheck { } fn make_run(run: RunConfig<'_>) { - let compiler = run.builder.compiler_for(run.builder.top_stage, run.host, run.host); - run.builder.ensure(TierCheck { compiler, target: run.host }); + let compiler = + run.builder.compiler_for(run.builder.top_stage, run.builder.build.build, run.target); + run.builder.ensure(TierCheck { compiler }); } /// Tests the Platform Support page in the rustc book. fn run(self, builder: &Builder<'_>) { - builder.ensure(compile::Std { compiler: self.compiler, target: self.target }); + builder.ensure(compile::Std { compiler: self.compiler, target: self.compiler.host }); let mut cargo = tool::prepare_tool_cargo( builder, self.compiler, - Mode::ToolRustc, - self.target, + Mode::ToolStd, + self.compiler.host, "run", "src/tools/tier-check", SourceType::InTree, diff --git a/src/bootstrap/tool.rs b/src/bootstrap/tool.rs index e8d7de7a5dc..a607f0fe258 100644 --- a/src/bootstrap/tool.rs +++ b/src/bootstrap/tool.rs @@ -469,8 +469,9 @@ impl Step for Rustdoc { } fn make_run(run: RunConfig<'_>) { - run.builder - .ensure(Rustdoc { compiler: run.builder.compiler(run.builder.top_stage, run.host) }); + run.builder.ensure(Rustdoc { + compiler: run.builder.compiler(run.builder.top_stage, run.build_triple()), + }); } fn run(self, builder: &Builder<'_>) -> PathBuf { -- cgit 1.4.1-3-g733a5 From b4eb0992614acc242169154d434db658ef6790e0 Mon Sep 17 00:00:00 2001 From: Mark Rousskov Date: Sun, 6 Sep 2020 12:41:28 -0400 Subject: Verify we compile std without involving a b host compiler --- src/bootstrap/builder/tests.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'src/bootstrap') diff --git a/src/bootstrap/builder/tests.rs b/src/bootstrap/builder/tests.rs index 144c4dc4c95..c6eac95c345 100644 --- a/src/bootstrap/builder/tests.rs +++ b/src/bootstrap/builder/tests.rs @@ -217,6 +217,16 @@ mod dist { dist::Std { compiler: Compiler { host: a, stage: 1 }, target: b }, ] ); + assert_eq!( + first(builder.cache.all::()), + &[ + compile::Std { compiler: Compiler { host: a, stage: 0 }, target: a }, + compile::Std { compiler: Compiler { host: a, stage: 1 }, target: a }, + compile::Std { compiler: Compiler { host: a, stage: 2 }, target: a }, + compile::Std { compiler: Compiler { host: a, stage: 1 }, target: b }, + compile::Std { compiler: Compiler { host: a, stage: 2 }, target: b }, + ], + ); assert_eq!(first(builder.cache.all::()), &[dist::Src]); } -- cgit 1.4.1-3-g733a5 From 78125ec6b393441e628f9b04b84d6d125225fe30 Mon Sep 17 00:00:00 2001 From: Mark Rousskov Date: Sun, 6 Sep 2020 12:57:13 -0400 Subject: Stop implicitly appending triples to config.toml hosts and targets Previously, the CLI --target/--host definitions and configured options differed in their effect: when setting these on the CLI, only the passed triples would be compiled for, while in config.toml we would also compile for the build triple and any host triples. This is needlessly confusing; users expect --target and --host to be identical to editing the configuration file. The new behavior is to respect --host and --target when passed as the *only* configured triples (no triples are implicitly added). The default for --host is the build triple, and the default for --target is the host triple(s), either configured or the default build triple. --- config.toml.example | 15 +++++++------- src/bootstrap/config.rs | 52 +++++++++++++++++++++++-------------------------- src/bootstrap/flags.rs | 32 ++++++++++++++++++++---------- 3 files changed, 53 insertions(+), 46 deletions(-) (limited to 'src/bootstrap') diff --git a/config.toml.example b/config.toml.example index c311c933e7e..2d5b3136450 100644 --- a/config.toml.example +++ b/config.toml.example @@ -120,19 +120,18 @@ # Defaults to host platform #build = "x86_64-unknown-linux-gnu" -# In addition to the build triple, other triples to produce full compiler -# toolchains for. Each of these triples will be bootstrapped from the build -# triple and then will continue to bootstrap themselves. This platform must -# currently be able to run all of the triples provided here. +# Which triples to produce a compiler toolchain for. Each of these triples will +# be bootstrapped from the build triple themselves. # # Defaults to just the build triple #host = ["x86_64-unknown-linux-gnu"] -# In addition to all host triples, other triples to produce the standard library -# for. Each host triple will be used to produce a copy of the standard library -# for each target triple. +# Which triples to build libraries (core/alloc/std/test/proc_macro) for. Each of +# these triples will be bootstrapped from the build triple themselves. # -# Defaults to just the build triple +# Defaults to `host`. If you set this explicitly, you likely want to add all +# host triples to this list as well in order for those host toolchains to be +# able to compile programs for their native target. #target = ["x86_64-unknown-linux-gnu"] # Use this directory to store build artifacts. diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index ad2f4877867..5a79d3db5c9 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -273,10 +273,8 @@ struct TomlConfig { #[serde(deny_unknown_fields, rename_all = "kebab-case")] struct Build { build: Option, - #[serde(default)] - host: Vec, - #[serde(default)] - target: Vec, + host: Option>, + target: Option>, // This is ignored, the rust code always gets the build directory from the `BUILD_DIR` env variable build_dir: Option, cargo: Option, @@ -505,11 +503,6 @@ impl Config { config.out = dir; } - // If --target was specified but --host wasn't specified, don't run any host-only tests. - let has_hosts = !flags.host.is_empty(); - let has_targets = !flags.target.is_empty(); - config.skip_only_host_steps = !has_hosts && has_targets; - let toml = file .map(|file| { let contents = t!(fs::read_to_string(&file)); @@ -528,25 +521,28 @@ impl Config { .unwrap_or_else(TomlConfig::default); let build = toml.build.clone().unwrap_or_default(); - // set by bootstrap.py - config.hosts.push(config.build); - for host in build.host.iter().map(|h| TargetSelection::from_user(h)) { - if !config.hosts.contains(&host) { - config.hosts.push(host); - } - } - for target in config - .hosts - .iter() - .copied() - .chain(build.target.iter().map(|h| TargetSelection::from_user(h))) - { - if !config.targets.contains(&target) { - config.targets.push(target); - } - } - config.hosts = if !flags.host.is_empty() { flags.host } else { config.hosts }; - config.targets = if !flags.target.is_empty() { flags.target } else { config.targets }; + + // If --target was specified but --host wasn't specified, don't run any host-only tests. + let has_hosts = build.host.is_some() || flags.host.is_some(); + let has_targets = build.target.is_some() || flags.target.is_some(); + config.skip_only_host_steps = !has_hosts && has_targets; + + config.hosts = if let Some(arg_host) = flags.host.clone() { + arg_host + } else if let Some(file_host) = build.host { + file_host.iter().map(|h| TargetSelection::from_user(h)).collect() + } else { + vec![config.build] + }; + config.targets = if let Some(arg_target) = flags.target.clone() { + arg_target + } else if let Some(file_target) = build.target { + file_target.iter().map(|h| TargetSelection::from_user(h)).collect() + } else { + // If target is *not* configured, then default to the host + // toolchains. + config.hosts.clone() + }; config.nodejs = build.nodejs.map(PathBuf::from); config.gdb = build.gdb.map(PathBuf::from); diff --git a/src/bootstrap/flags.rs b/src/bootstrap/flags.rs index 2db4bb07a9f..ff846857446 100644 --- a/src/bootstrap/flags.rs +++ b/src/bootstrap/flags.rs @@ -20,8 +20,8 @@ pub struct Flags { pub stage: Option, pub keep_stage: Vec, - pub host: Vec, - pub target: Vec, + pub host: Option>, + pub target: Option>, pub config: Option, pub jobs: Option, pub cmd: Subcommand, @@ -526,14 +526,26 @@ Arguments: .into_iter() .map(|j| j.parse().expect("`keep-stage` should be a number")) .collect(), - host: split(&matches.opt_strs("host")) - .into_iter() - .map(|x| TargetSelection::from_user(&x)) - .collect::>(), - target: split(&matches.opt_strs("target")) - .into_iter() - .map(|x| TargetSelection::from_user(&x)) - .collect::>(), + host: if matches.opt_present("host") { + Some( + split(&matches.opt_strs("host")) + .into_iter() + .map(|x| TargetSelection::from_user(&x)) + .collect::>(), + ) + } else { + None + }, + target: if matches.opt_present("target") { + Some( + split(&matches.opt_strs("target")) + .into_iter() + .map(|x| TargetSelection::from_user(&x)) + .collect::>(), + ) + } else { + None + }, config: cfg_file, jobs: matches.opt_str("jobs").map(|j| j.parse().expect("`jobs` should be a number")), cmd, -- cgit 1.4.1-3-g733a5 From 0be66d7f30ba31638e14c00bd2303dd0327964f4 Mon Sep 17 00:00:00 2001 From: Gus Wynn Date: Fri, 11 Sep 2020 09:37:51 -0700 Subject: just max_level_info --- compiler/rustc/Cargo.toml | 2 +- compiler/rustc_driver/Cargo.toml | 2 +- src/bootstrap/lib.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'src/bootstrap') diff --git a/compiler/rustc/Cargo.toml b/compiler/rustc/Cargo.toml index b740793a3e5..6e6c0c71a1f 100644 --- a/compiler/rustc/Cargo.toml +++ b/compiler/rustc/Cargo.toml @@ -19,4 +19,4 @@ features = ['unprefixed_malloc_on_supported_platforms'] [features] jemalloc = ['jemalloc-sys'] llvm = ['rustc_driver/llvm'] -release_max_level_info = ['rustc_driver/release_max_level_info'] +max_level_info = ['rustc_driver/max_level_info'] diff --git a/compiler/rustc_driver/Cargo.toml b/compiler/rustc_driver/Cargo.toml index 1e371f8254d..adfce1008e1 100644 --- a/compiler/rustc_driver/Cargo.toml +++ b/compiler/rustc_driver/Cargo.toml @@ -38,4 +38,4 @@ winapi = { version = "0.3", features = ["consoleapi", "debugapi", "processenv"] [features] llvm = ['rustc_interface/llvm'] -release_max_level_info = ['tracing/release_max_level_info', 'tracing/max_level_info'] +max_level_info = ['tracing/max_level_info'] diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs index 3c2663f6d9f..87a893cd836 100644 --- a/src/bootstrap/lib.rs +++ b/src/bootstrap/lib.rs @@ -548,7 +548,7 @@ impl Build { // if its unset, if debug_assertions is on, then debug_logging will also be on // as well as tracing *ignoring* this feature when debug_assertions is on if !self.config.rust_debug_logging { - features.push_str(" release_max_level_info"); + features.push_str(" max_level_info"); } features -- cgit 1.4.1-3-g733a5 From 85ab152be78ffaf7dd97f60ececceda28f4e4802 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Fri, 11 Sep 2020 20:53:52 -0400 Subject: Update bootstrap readme - Reflect changes in x.py defaults - Remove recommendation to use nightly for incremental; it works fine on beta - Remove note that incremental chooses stage 1 by default; stage 1 is already the default - Update Discord -> Zulip --- src/bootstrap/README.md | 38 ++++++++++---------------------------- 1 file changed, 10 insertions(+), 28 deletions(-) (limited to 'src/bootstrap') diff --git a/src/bootstrap/README.md b/src/bootstrap/README.md index 975b8be02c8..a69bd1cc3bc 100644 --- a/src/bootstrap/README.md +++ b/src/bootstrap/README.md @@ -26,10 +26,10 @@ The script accepts commands, flags, and arguments to determine what to do: ``` # build the whole compiler - ./x.py build + ./x.py build --stage 2 # build the stage1 compiler - ./x.py build --stage 1 + ./x.py build # build stage0 libstd ./x.py build --stage 0 library/std @@ -43,8 +43,8 @@ The script accepts commands, flags, and arguments to determine what to do: that belong to stage n or earlier: ``` - # keep old build products for stage 0 and build stage 1 - ./x.py build --keep-stage 0 --stage 1 + # build stage 1, keeping old build products for stage 0 + ./x.py build --keep-stage 0 ``` * `test` - a command for executing unit tests. Like the `build` command this @@ -123,24 +123,8 @@ that (b) leverage Rust as much as possible! ## Incremental builds -You can configure rustbuild to use incremental compilation. Because -incremental is new and evolving rapidly, if you want to use it, it is -recommended that you replace the snapshot with a locally installed -nightly build of rustc. You will want to keep this up to date. - -To follow this course of action, first thing you will want to do is to -install a nightly, presumably using `rustup`. You will then want to -configure your directory to use this build, like so: - -```sh -# configure to use local rust instead of downloading a beta. -# `--local-rust-root` is optional here. If elided, we will -# use whatever rustc we find on your PATH. -$ ./configure --local-rust-root=~/.cargo/ --enable-local-rebuild -``` - -After that, you can use the `--incremental` flag to actually do -incremental builds: +You can configure rustbuild to use incremental compilation with the +`--incremental` flag: ```sh $ ./x.py build --incremental @@ -150,9 +134,7 @@ The `--incremental` flag will store incremental compilation artifacts in `build//stage0-incremental`. Note that we only use incremental compilation for the stage0 -> stage1 compilation -- this is because the stage1 compiler is changing, and we don't try to cache and reuse -incremental artifacts across different versions of the compiler. For -this reason, `--incremental` defaults to `--stage 1` (though you can -manually select a higher stage, if you prefer). +incremental artifacts across different versions of the compiler. You can always drop the `--incremental` to build as normal (but you will still be using the local nightly as your bootstrap). @@ -331,8 +313,8 @@ are: `Config` struct. * Adding a sanity check? Take a look at `bootstrap/sanity.rs`. -If you have any questions feel free to reach out on `#infra` channel in the -[Rust Discord server][rust-discord] or ask on internals.rust-lang.org. When +If you have any questions feel free to reach out on the `#t-infra` channel in +the [Rust Zulip server][rust-zulip] or ask on internals.rust-lang.org. When you encounter bugs, please file issues on the rust-lang/rust issue tracker. -[rust-discord]: https://discord.gg/rust-lang +[rust-zulip]: https://rust-lang.zulipchat.com/#narrow/stream/242791-t-infra -- cgit 1.4.1-3-g733a5 From e788b1a2fe181124e480b5f841fa71c61b9857f0 Mon Sep 17 00:00:00 2001 From: Mark Rousskov Date: Sat, 12 Sep 2020 10:05:10 -0400 Subject: Print all step timings It is really painful to inspect differences in what was built in CI if things are appearing and disappearing randomly as they hover around the 100ms mark. No matter what we choose there's always going to be quite a bit of variability on CI in timing, so we're going to see things appear and vanish. --- src/bootstrap/builder.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/bootstrap') diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index 844a29fadae..848edd8f37f 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -1424,7 +1424,7 @@ impl<'a> Builder<'a> { (out, dur - deps) }; - if self.config.print_step_timings && dur > Duration::from_millis(100) { + if self.config.print_step_timings { println!("[TIMING] {:?} -- {}.{:03}", step, dur.as_secs(), dur.subsec_millis()); } -- cgit 1.4.1-3-g733a5 From a7b092f4188e927b3456fe6fe5ee2bd72c53cb57 Mon Sep 17 00:00:00 2001 From: Mark Rousskov Date: Fri, 4 Sep 2020 19:00:04 -0400 Subject: Download LLVM from CI to bootstrap --- config.toml.example | 15 +++++++ src/bootstrap/bootstrap.py | 107 +++++++++++++++++++++++++++++++++++---------- src/bootstrap/config.rs | 57 ++++++++++++++++++++++++ src/bootstrap/dist.rs | 34 ++++++++------ src/bootstrap/lib.rs | 4 ++ 5 files changed, 179 insertions(+), 38 deletions(-) (limited to 'src/bootstrap') diff --git a/config.toml.example b/config.toml.example index 2d5b3136450..b50406a9579 100644 --- a/config.toml.example +++ b/config.toml.example @@ -14,6 +14,21 @@ # ============================================================================= [llvm] +# Whether to use Rust CI built LLVM instead of locally building it. +# +# Unless you're developing for a target where Rust CI doesn't build a compiler +# toolchain or changing LLVM locally, you probably want to set this to true. +# +# It's currently false by default due to being newly added; please file bugs if +# enabling this did not work for you on Linux (macOS and Windows support is +# coming soon). +# +# We also currently only support this when building LLVM for the build triple. +# +# Note that many of the LLVM options are not currently supported for +# downloading. Currently only the "assertions" option can be toggled. +#download-ci-llvm = false + # Indicates whether LLVM rebuild should be skipped when running bootstrap. If # this is `false` then the compiler's LLVM will be rebuilt whenever the built # version doesn't have the correct hash. If it is `true` then LLVM will never diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index 955c72856dc..44a17f75451 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -14,8 +14,17 @@ import tempfile from time import time - -def get(url, path, verbose=False): +def support_xz(): + try: + with tempfile.NamedTemporaryFile(delete=False) as temp_file: + temp_path = temp_file.name + with tarfile.open(temp_path, "w:xz"): + pass + return True + except tarfile.CompressionError: + return False + +def get(url, path, verbose=False, do_verify=True): suffix = '.sha256' sha_url = url + suffix with tempfile.NamedTemporaryFile(delete=False) as temp_file: @@ -24,19 +33,20 @@ def get(url, path, verbose=False): sha_path = sha_file.name try: - download(sha_path, sha_url, False, verbose) - if os.path.exists(path): - if verify(path, sha_path, False): - if verbose: - print("using already-download file", path) - return - else: - if verbose: - print("ignoring already-download file", - path, "due to failed verification") - os.unlink(path) + if do_verify: + download(sha_path, sha_url, False, verbose) + if os.path.exists(path): + if verify(path, sha_path, False): + if verbose: + print("using already-download file", path) + return + else: + if verbose: + print("ignoring already-download file", + path, "due to failed verification") + os.unlink(path) download(temp_path, url, True, verbose) - if not verify(temp_path, sha_path, verbose): + if do_verify and not verify(temp_path, sha_path, verbose): raise RuntimeError("failed verification") if verbose: print("moving {} to {}".format(temp_path, path)) @@ -365,16 +375,6 @@ class RustBuild(object): cargo_channel = self.cargo_channel rustfmt_channel = self.rustfmt_channel - def support_xz(): - try: - with tempfile.NamedTemporaryFile(delete=False) as temp_file: - temp_path = temp_file.name - with tarfile.open(temp_path, "w:xz"): - pass - return True - except tarfile.CompressionError: - return False - if self.rustc().startswith(self.bin_root()) and \ (not os.path.exists(self.rustc()) or self.program_out_of_date(self.rustc_stamp())): @@ -423,6 +423,19 @@ class RustBuild(object): with output(self.rustfmt_stamp()) as rustfmt_stamp: rustfmt_stamp.write(self.date + self.rustfmt_channel) + if self.downloading_llvm(): + llvm_sha = subprocess.check_output(["git", "log", "--author=bors", + "--format=%H", "-n1"]).decode(sys.getdefaultencoding()).strip() + llvm_assertions = self.get_toml('assertions', 'llvm') == 'true' + if self.program_out_of_date(self.llvm_stamp(), llvm_sha + str(llvm_assertions)): + self._download_ci_llvm(llvm_sha, llvm_assertions) + with output(self.llvm_stamp()) as llvm_stamp: + llvm_stamp.write(self.date + llvm_sha + str(llvm_assertions)) + + def downloading_llvm(self): + opt = self.get_toml('download-ci-llvm', 'llvm') + return opt == "true" + def _download_stage0_helper(self, filename, pattern, tarball_suffix, date=None): if date is None: date = self.date @@ -437,6 +450,25 @@ class RustBuild(object): get("{}/{}".format(url, filename), tarball, verbose=self.verbose) unpack(tarball, tarball_suffix, self.bin_root(), match=pattern, verbose=self.verbose) + def _download_ci_llvm(self, llvm_sha, llvm_assertions): + cache_prefix = "llvm-{}-{}".format(llvm_sha, llvm_assertions) + cache_dst = os.path.join(self.build_dir, "cache") + rustc_cache = os.path.join(cache_dst, cache_prefix) + if not os.path.exists(rustc_cache): + os.makedirs(rustc_cache) + + url = "https://ci-artifacts.rust-lang.org/rustc-builds/{}".format(llvm_sha) + if llvm_assertions: + url = url.replace('rustc-builds', 'rustc-builds-alt') + tarball_suffix = '.tar.xz' if support_xz() else '.tar.gz' + filename = "rust-dev-nightly-" + self.build + tarball_suffix + tarball = os.path.join(rustc_cache, filename) + if not os.path.exists(tarball): + get("{}/{}".format(url, filename), tarball, verbose=self.verbose, do_verify=False) + unpack(tarball, tarball_suffix, self.llvm_root(), + match="rust-dev", + verbose=self.verbose) + def fix_bin_or_dylib(self, fname): """Modifies the interpreter section of 'fname' to fix the dynamic linker, or the RPATH section, to fix the dynamic library search path @@ -558,6 +590,17 @@ class RustBuild(object): """ return os.path.join(self.bin_root(), '.rustfmt-stamp') + def llvm_stamp(self): + """Return the path for .rustfmt-stamp + + >>> rb = RustBuild() + >>> rb.build_dir = "build" + >>> rb.llvm_stamp() == os.path.join("build", "ci-llvm", ".llvm-stamp") + True + """ + return os.path.join(self.llvm_root(), '.llvm-stamp') + + def program_out_of_date(self, stamp_path, extra=""): """Check if the given program stamp is out of date""" if not os.path.exists(stamp_path) or self.clean: @@ -581,6 +624,22 @@ class RustBuild(object): """ return os.path.join(self.build_dir, self.build, "stage0") + def llvm_root(self): + """Return the CI LLVM root directory + + >>> rb = RustBuild() + >>> rb.build_dir = "build" + >>> rb.llvm_root() == os.path.join("build", "ci-llvm") + True + + When the 'build' property is given should be a nested directory: + + >>> rb.build = "devel" + >>> rb.llvm_root() == os.path.join("build", "devel", "ci-llvm") + True + """ + return os.path.join(self.build_dir, self.build, "ci-llvm") + def get_toml(self, key, section=None): """Returns the value of the given key in config.toml, otherwise returns None diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index 5a79d3db5c9..d889d04fa6f 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -15,9 +15,20 @@ use std::process; use crate::cache::{Interned, INTERNER}; use crate::flags::Flags; pub use crate::flags::Subcommand; +use crate::util::exe; use build_helper::t; use serde::Deserialize; +macro_rules! check_ci_llvm { + ($name:expr) => { + assert!( + $name.is_none(), + "setting {} is incompatible with download-ci-llvm.", + stringify!($name) + ); + }; +} + /// Global configuration for the entire build and/or bootstrap. /// /// This structure is derived from a combination of both `config.toml` and @@ -84,6 +95,7 @@ pub struct Config { pub llvm_version_suffix: Option, pub llvm_use_linker: Option, pub llvm_allow_old_toolchain: Option, + pub llvm_from_ci: bool, pub use_lld: bool, pub lld_enabled: bool, @@ -344,6 +356,7 @@ struct Llvm { use_libcxx: Option, use_linker: Option, allow_old_toolchain: Option, + download_ci_llvm: Option, } #[derive(Deserialize, Default, Clone)] @@ -624,6 +637,36 @@ impl Config { set(&mut config.llvm_use_libcxx, llvm.use_libcxx); config.llvm_use_linker = llvm.use_linker.clone(); config.llvm_allow_old_toolchain = llvm.allow_old_toolchain; + config.llvm_from_ci = llvm.download_ci_llvm.unwrap_or(false); + + if config.llvm_from_ci { + // None of the LLVM options, except assertions, are supported + // when using downloaded LLVM. We could just ignore these but + // that's potentially confusing, so force them to not be + // explicitly set. The defaults and CI defaults don't + // necessarily match but forcing people to match (somewhat + // arbitrary) CI configuration locally seems bad/hard. + check_ci_llvm!(llvm.optimize); + check_ci_llvm!(llvm.thin_lto); + check_ci_llvm!(llvm.release_debuginfo); + check_ci_llvm!(llvm.link_shared); + check_ci_llvm!(llvm.static_libstdcpp); + check_ci_llvm!(llvm.targets); + check_ci_llvm!(llvm.experimental_targets); + check_ci_llvm!(llvm.link_jobs); + check_ci_llvm!(llvm.link_shared); + check_ci_llvm!(llvm.clang_cl); + check_ci_llvm!(llvm.version_suffix); + check_ci_llvm!(llvm.cflags); + check_ci_llvm!(llvm.cxxflags); + check_ci_llvm!(llvm.ldflags); + check_ci_llvm!(llvm.use_libcxx); + check_ci_llvm!(llvm.use_linker); + check_ci_llvm!(llvm.allow_old_toolchain); + + // CI-built LLVM is shared + config.llvm_link_shared = true; + } } if let Some(ref rust) = toml.rust { @@ -706,6 +749,20 @@ impl Config { } } + if config.llvm_from_ci { + let triple = &config.build.triple; + let mut build_target = config + .target_config + .entry(config.build) + .or_insert_with(|| Target::from_triple(&triple)); + + check_ci_llvm!(build_target.llvm_config); + check_ci_llvm!(build_target.llvm_filecheck); + let ci_llvm_bin = config.out.join(&*config.build.triple).join("ci-llvm/bin"); + build_target.llvm_config = Some(ci_llvm_bin.join(exe("llvm-config", config.build))); + build_target.llvm_filecheck = Some(ci_llvm_bin.join(exe("FileCheck", config.build))); + } + if let Some(ref t) = toml.dist { config.dist_sign_folder = t.sign_folder.clone().map(PathBuf::from); config.dist_gpg_password_file = t.gpg_password_file.clone().map(PathBuf::from); diff --git a/src/bootstrap/dist.rs b/src/bootstrap/dist.rs index e22cdb13928..cf73e570fa5 100644 --- a/src/bootstrap/dist.rs +++ b/src/bootstrap/dist.rs @@ -2382,26 +2382,32 @@ impl Step for HashSign { /// Note: This function does not yet support Windows, but we also don't support /// linking LLVM tools dynamically on Windows yet. fn maybe_install_llvm(builder: &Builder<'_>, target: TargetSelection, dst_libdir: &Path) { - let src_libdir = builder.llvm_out(target).join("lib"); + if !builder.config.llvm_link_shared { + // We do not need to copy LLVM files into the sysroot if it is not + // dynamically linked; it is already included into librustc_llvm + // statically. + return; + } + // On macOS for some reason the llvm-config binary behaves differently and + // and fails on missing .a files if run without --link-shared. If run with + // that option, it still fails, but because we only ship a libLLVM.dylib + // rather than libLLVM-11-rust-....dylib file. + // + // For now just don't use llvm-config here on macOS; that will fail to + // support CI-built LLVM, but until we work out the different behavior that + // is fine as it is off by default. if target.contains("apple-darwin") { + let src_libdir = builder.llvm_out(target).join("lib"); let llvm_dylib_path = src_libdir.join("libLLVM.dylib"); if llvm_dylib_path.exists() { builder.install(&llvm_dylib_path, dst_libdir, 0o644); } - return; - } - - // Usually libLLVM.so is a symlink to something like libLLVM-6.0.so. - // Since tools link to the latter rather than the former, we have to - // follow the symlink to find out what to distribute. - let llvm_dylib_path = src_libdir.join("libLLVM.so"); - if llvm_dylib_path.exists() { - let llvm_dylib_path = llvm_dylib_path.canonicalize().unwrap_or_else(|e| { - panic!("dist: Error calling canonicalize path `{}`: {}", llvm_dylib_path.display(), e); - }); - - builder.install(&llvm_dylib_path, dst_libdir, 0o644); + } else if let Ok(llvm_config) = crate::native::prebuilt_llvm_config(builder, target) { + let files = output(Command::new(llvm_config).arg("--libfiles")); + for file in files.lines() { + builder.install(Path::new(file), dst_libdir, 0o644); + } } } diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs index f0224d88226..170c93561ef 100644 --- a/src/bootstrap/lib.rs +++ b/src/bootstrap/lib.rs @@ -611,6 +611,10 @@ impl Build { /// /// If no custom `llvm-config` was specified then Rust's llvm will be used. fn is_rust_llvm(&self, target: TargetSelection) -> bool { + if self.config.llvm_from_ci && target == self.config.build { + return true; + } + match self.config.target_config.get(&target) { Some(ref c) => c.llvm_config.is_none(), None => true, -- cgit 1.4.1-3-g733a5 From 2e87a6e78dacad9c4acc5fe4fc8bf5aa1ca44a51 Mon Sep 17 00:00:00 2001 From: Mark Rousskov Date: Sat, 12 Sep 2020 15:10:13 -0400 Subject: Set link-shared if LLVM ThinLTO is enabled in config.rs This avoids missing a shared build when uplifting LLVM artifacts into the sysroot. We were already producing a shared link anyway, though, so this is not a visible change from the end user's perspective. --- src/bootstrap/compile.rs | 2 +- src/bootstrap/config.rs | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) (limited to 'src/bootstrap') diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs index e0dddda83b9..08907edef1d 100644 --- a/src/bootstrap/compile.rs +++ b/src/bootstrap/compile.rs @@ -593,7 +593,7 @@ pub fn rustc_cargo_env(builder: &Builder<'_>, cargo: &mut Cargo, target: TargetS let file = compiler_file(builder, builder.cxx(target).unwrap(), target, "libstdc++.a"); cargo.env("LLVM_STATIC_STDCPP", file); } - if builder.config.llvm_link_shared || builder.config.llvm_thin_lto { + if builder.config.llvm_link_shared { cargo.env("LLVM_LINK_SHARED", "1"); } if builder.config.llvm_use_libcxx { diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index d889d04fa6f..000f8cef614 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -667,6 +667,13 @@ impl Config { // CI-built LLVM is shared config.llvm_link_shared = true; } + + if config.llvm_thin_lto { + // If we're building with ThinLTO on, we want to link to LLVM + // shared, to avoid re-doing ThinLTO (which happens in the link + // step) with each stage. + config.llvm_link_shared = true; + } } if let Some(ref rust) = toml.rust { -- cgit 1.4.1-3-g733a5 From cf0720146e89a9af8759951aaf774ef19f33f556 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sun, 13 Sep 2020 14:37:35 +0200 Subject: Fix CI LLVM to work on NixOS out of the box --- src/bootstrap/bootstrap.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/bootstrap') diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index 44a17f75451..5f78031e1c7 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -429,6 +429,8 @@ class RustBuild(object): llvm_assertions = self.get_toml('assertions', 'llvm') == 'true' if self.program_out_of_date(self.llvm_stamp(), llvm_sha + str(llvm_assertions)): self._download_ci_llvm(llvm_sha, llvm_assertions) + for binary in ["llvm-config", "FileCheck"]: + self.fix_bin_or_dylib("{}/bin/{}".format(self.llvm_root(), binary)) with output(self.llvm_stamp()) as llvm_stamp: llvm_stamp.write(self.date + llvm_sha + str(llvm_assertions)) -- cgit 1.4.1-3-g733a5 From 45c1e0ae07b9581d8c2d2b39315ac7cc79475d75 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Tue, 8 Sep 2020 15:09:57 -0700 Subject: Auto-generate lint documentation. --- Cargo.lock | 9 + Cargo.toml | 1 + compiler/rustc_lint/src/array_into_iter.rs | 25 + compiler/rustc_lint/src/builtin.rs | 504 +++++ compiler/rustc_lint/src/non_ascii_idents.rs | 129 ++ compiler/rustc_lint/src/nonstandard_style.rs | 49 + compiler/rustc_lint/src/redundant_semicolon.rs | 15 + compiler/rustc_lint/src/types.rs | 106 + compiler/rustc_lint/src/unused.rs | 146 ++ compiler/rustc_session/src/lint.rs | 56 +- compiler/rustc_session/src/lint/builtin.rs | 2118 ++++++++++++++++++++ src/bootstrap/doc.rs | 67 +- src/bootstrap/tool.rs | 1 + src/doc/rustc/src/lints/groups.md | 9 +- src/doc/rustc/src/lints/index.md | 32 + .../rustc/src/lints/listing/allowed-by-default.md | 453 +---- src/doc/rustc/src/lints/listing/deny-by-default.md | 202 +- src/doc/rustc/src/lints/listing/warn-by-default.md | 902 +-------- src/tools/lint-docs/Cargo.toml | 13 + src/tools/lint-docs/src/groups.rs | 114 ++ src/tools/lint-docs/src/lib.rs | 463 +++++ src/tools/lint-docs/src/main.rs | 67 + 22 files changed, 3914 insertions(+), 1567 deletions(-) create mode 100644 src/tools/lint-docs/Cargo.toml create mode 100644 src/tools/lint-docs/src/groups.rs create mode 100644 src/tools/lint-docs/src/lib.rs create mode 100644 src/tools/lint-docs/src/main.rs (limited to 'src/bootstrap') diff --git a/Cargo.lock b/Cargo.lock index b448baf425b..1a67deacbfe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1677,6 +1677,15 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8dd5a6d5999d9907cda8ed67bbd137d3af8085216c2ac62de5be860bd41f304a" +[[package]] +name = "lint-docs" +version = "0.1.0" +dependencies = [ + "serde_json", + "tempfile", + "walkdir", +] + [[package]] name = "lock_api" version = "0.3.4" diff --git a/Cargo.toml b/Cargo.toml index fde1cb5a35c..02794d1028b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ members = [ "src/tools/compiletest", "src/tools/error_index_generator", "src/tools/linkchecker", + "src/tools/lint-docs", "src/tools/rustbook", "src/tools/unstable-book-gen", "src/tools/tidy", diff --git a/compiler/rustc_lint/src/array_into_iter.rs b/compiler/rustc_lint/src/array_into_iter.rs index 1d27bdcb282..e6be082da0e 100644 --- a/compiler/rustc_lint/src/array_into_iter.rs +++ b/compiler/rustc_lint/src/array_into_iter.rs @@ -7,6 +7,31 @@ use rustc_session::lint::FutureIncompatibleInfo; use rustc_span::symbol::sym; declare_lint! { + /// The `array_into_iter` lint detects calling `into_iter` on arrays. + /// + /// ### Example + /// + /// ```rust + /// # #![allow(unused)] + /// [1, 2, 3].into_iter().for_each(|n| { *n; }); + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// In the future, it is planned to add an `IntoIter` implementation for + /// arrays such that it will iterate over *values* of the array instead of + /// references. Due to how method resolution works, this will change + /// existing code that uses `into_iter` on arrays. The solution to avoid + /// this warning is to use `iter()` instead of `into_iter()`. + /// + /// This is a [future-incompatible] lint to transition this to a hard error + /// in the future. See [issue #66145] for more details and a more thorough + /// description of the lint. + /// + /// [issue #66145]: https://github.com/rust-lang/rust/issues/66145 + /// [future-incompatible]: ../index.md#future-incompatible-lints pub ARRAY_INTO_ITER, Warn, "detects calling `into_iter` on arrays", diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index d18d89ed641..e785ba03d22 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -61,6 +61,23 @@ use tracing::{debug, trace}; pub use rustc_session::lint::builtin::*; declare_lint! { + /// The `while_true` lint detects `while true { }`. + /// + /// ### Example + /// + /// ```rust,no_run + /// while true { + /// + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// `while true` should be replaced with `loop`. A `loop` expression is + /// the preferred way to write an infinite loop because it more directly + /// expresses the intent of the loop. WHILE_TRUE, Warn, "suggest using `loop { }` instead of `while true { }`" @@ -102,6 +119,24 @@ impl EarlyLintPass for WhileTrue { } declare_lint! { + /// The `box_pointers` lints use of the Box type. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![deny(box_pointers)] + /// struct Foo { + /// x: Box, + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// This lint is mostly historical, and not particularly useful. `Box` + /// used to be built into the language, and the only way to do heap + /// allocation. Today's Rust can call into other allocators, etc. BOX_POINTERS, Allow, "use of owned (Box type) heap memory" @@ -156,6 +191,36 @@ impl<'tcx> LateLintPass<'tcx> for BoxPointers { } declare_lint! { + /// The `non_shorthand_field_patterns` lint detects using `Struct { x: x }` + /// instead of `Struct { x }` in a pattern. + /// + /// ### Example + /// + /// ```rust + /// struct Point { + /// x: i32, + /// y: i32, + /// } + /// + /// + /// fn main() { + /// let p = Point { + /// x: 5, + /// y: 5, + /// }; + /// + /// match p { + /// Point { x: x, y: y } => (), + /// } + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// The preferred style is to avoid the repetition of specifying both the + /// field name and the binding name if both identifiers are the same. NON_SHORTHAND_FIELD_PATTERNS, Warn, "using `Struct { x: x }` instead of `Struct { x }` in a pattern" @@ -216,6 +281,25 @@ impl<'tcx> LateLintPass<'tcx> for NonShorthandFieldPatterns { } declare_lint! { + /// The `unsafe_code` lint catches usage of `unsafe` code. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![deny(unsafe_code)] + /// fn main() { + /// unsafe { + /// + /// } + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// This lint is intended to restrict the usage of `unsafe`, which can be + /// difficult to use correctly. UNSAFE_CODE, Allow, "usage of `unsafe` code" @@ -303,6 +387,25 @@ impl EarlyLintPass for UnsafeCode { } declare_lint! { + /// The `missing_docs` lint detects missing documentation for public items. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![deny(missing_docs)] + /// pub fn foo() {} + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// This lint is intended to ensure that a library is well-documented. + /// Items without documentation can be difficult for users to understand + /// how to use properly. + /// + /// This lint is "allow" by default because it can be noisy, and not all + /// projects may want to enforce everything to be documented. pub MISSING_DOCS, Allow, "detects missing documentation for public members", @@ -528,6 +631,34 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { } declare_lint! { + /// The `missing_copy_implementations` lint detects potentially-forgotten + /// implementations of [`Copy`]. + /// + /// [`Copy`]: https://doc.rust-lang.org/std/marker/trait.Copy.html + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![deny(missing_copy_implementations)] + /// pub struct Foo { + /// pub field: i32 + /// } + /// # fn main() {} + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Historically (before 1.0), types were automatically marked as `Copy` + /// if possible. This was changed so that it required an explicit opt-in + /// by implementing the `Copy` trait. As part of this change, a lint was + /// added to alert if a copyable type was not marked `Copy`. + /// + /// This lint is "allow" by default because this code isn't bad; it is + /// common to write newtypes like this specifically so that a `Copy` type + /// is no longer `Copy`. `Copy` types can result in unintended copies of + /// large data which can impact performance. pub MISSING_COPY_IMPLEMENTATIONS, Allow, "detects potentially-forgotten implementations of `Copy`" @@ -584,6 +715,32 @@ impl<'tcx> LateLintPass<'tcx> for MissingCopyImplementations { } declare_lint! { + /// The `missing_debug_implementations` lint detects missing + /// implementations of [`fmt::Debug`]. + /// + /// [`fmt::Debug`]: https://doc.rust-lang.org/std/fmt/trait.Debug.html + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![deny(missing_debug_implementations)] + /// pub struct Foo; + /// # fn main() {} + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Having a `Debug` implementation on all types can assist with + /// debugging, as it provides a convenient way to format and display a + /// value. Using the `#[derive(Debug)]` attribute will automatically + /// generate a typical implementation, or a custom implementation can be + /// added by manually implementing the `Debug` trait. + /// + /// This lint is "allow" by default because adding `Debug` to all types can + /// have a negative impact on compile time and code size. It also requires + /// boilerplate to be added to every type, which can be an impediment. MISSING_DEBUG_IMPLEMENTATIONS, Allow, "detects missing implementations of Debug" @@ -640,6 +797,45 @@ impl<'tcx> LateLintPass<'tcx> for MissingDebugImplementations { } declare_lint! { + /// The `anonymous_parameters` lint detects anonymous parameters in trait + /// definitions. + /// + /// ### Example + /// + /// ```rust,edition2015,compile_fail + /// #![deny(anonymous_parameters)] + /// // edition 2015 + /// pub trait Foo { + /// fn foo(usize); + /// } + /// fn main() {} + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// This syntax is mostly a historical accident, and can be worked around + /// quite easily by adding an `_` pattern or a descriptive identifier: + /// + /// ```rust + /// trait Foo { + /// fn foo(_: usize); + /// } + /// ``` + /// + /// This syntax is now a hard error in the 2018 edition. In the 2015 + /// edition, this lint is "allow" by default, because the old code is + /// still valid, and warning for all old code can be noisy. This lint + /// enables the [`cargo fix`] tool with the `--edition` flag to + /// automatically transition old code from the 2015 edition to 2018. The + /// tool will switch this lint to "warn" and will automatically apply the + /// suggested fix from the compiler (which is to add `_` to each + /// parameter). This provides a completely automated way to update old + /// code for a new edition. See [issue #41686] for more details. + /// + /// [issue #41686]: https://github.com/rust-lang/rust/issues/41686 + /// [`cargo fix`]: https://doc.rust-lang.org/cargo/commands/cargo-fix.html pub ANONYMOUS_PARAMETERS, Allow, "detects anonymous parameters", @@ -806,12 +1002,54 @@ impl EarlyLintPass for UnusedDocComment { } declare_lint! { + /// The `no_mangle_const_items` lint detects any `const` items with the + /// [`no_mangle` attribute]. + /// + /// [`no_mangle` attribute]: https://doc.rust-lang.org/reference/abi.html#the-no_mangle-attribute + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #[no_mangle] + /// const FOO: i32 = 5; + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Constants do not have their symbols exported, and therefore, this + /// probably means you meant to use a [`static`], not a [`const`]. + /// + /// [`static`]: https://doc.rust-lang.org/reference/items/static-items.html + /// [`const`]: https://doc.rust-lang.org/reference/items/constant-items.html NO_MANGLE_CONST_ITEMS, Deny, "const items will not have their symbols exported" } declare_lint! { + /// The `no_mangle_generic_items` lint detects generic items that must be + /// mangled. + /// + /// ### Example + /// + /// ```rust + /// #[no_mangle] + /// fn foo(t: T) { + /// + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// An function with generics must have its symbol mangled to accommodate + /// the generic parameter. The [`no_mangle` attribute] has no effect in + /// this situation, and should be removed. + /// + /// [`no_mangle` attribute]: https://doc.rust-lang.org/reference/abi.html#the-no_mangle-attribute NO_MANGLE_GENERIC_ITEMS, Warn, "generic items must be mangled" @@ -882,6 +1120,27 @@ impl<'tcx> LateLintPass<'tcx> for InvalidNoMangleItems { } declare_lint! { + /// The `mutable_transmutes` lint catches transmuting from `&T` to `&mut + /// T` because it is [undefined behavior]. + /// + /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html + /// + /// ### Example + /// + /// ```rust,compile_fail + /// unsafe { + /// let y = std::mem::transmute::<&i32, &mut i32>(&5); + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Certain assumptions are made about aliasing of data, and this transmute + /// violates those assumptions. Consider using [`UnsafeCell`] instead. + /// + /// [`UnsafeCell`]: https://doc.rust-lang.org/std/cell/struct.UnsafeCell.html MUTABLE_TRANSMUTES, Deny, "mutating transmuted &mut T from &T may cause undefined behavior" @@ -931,6 +1190,7 @@ impl<'tcx> LateLintPass<'tcx> for MutableTransmutes { } declare_lint! { + /// The `unstable_features` is deprecated and should no longer be used. UNSTABLE_FEATURES, Allow, "enabling unstable features (deprecated. do not use)" @@ -956,6 +1216,32 @@ impl<'tcx> LateLintPass<'tcx> for UnstableFeatures { } declare_lint! { + /// The `unreachable_pub` lint triggers for `pub` items not reachable from + /// the crate root. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![deny(unreachable_pub)] + /// mod foo { + /// pub mod bar { + /// + /// } + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// A bare `pub` visibility may be misleading if the item is not actually + /// publicly exported from the crate. The `pub(crate)` visibility is + /// recommended to be used instead, which more clearly expresses the intent + /// that the item is only visible within its own crate. + /// + /// This lint is "allow" by default because it will trigger for a large + /// amount existing Rust code, and has some false-positives. Eventually it + /// is desired for this to become warn-by-default. pub UNREACHABLE_PUB, Allow, "`pub` items not reachable from crate root" @@ -1035,6 +1321,21 @@ impl<'tcx> LateLintPass<'tcx> for UnreachablePub { } declare_lint! { + /// The `type_alias_bounds` lint detects bounds in type aliases. + /// + /// ### Example + /// + /// ```rust + /// type SendVec = Vec; + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// The trait bounds in a type alias are currently ignored, and should not + /// be included to avoid confusion. This was previously allowed + /// unintentionally; this may become a hard error in the future. TYPE_ALIAS_BOUNDS, Warn, "bounds in type aliases are not enforced" @@ -1194,6 +1495,35 @@ impl<'tcx> LateLintPass<'tcx> for UnusedBrokenConst { } declare_lint! { + /// The `trivial_bounds` lint detects trait bounds that don't depend on + /// any type parameters. + /// + /// ### Example + /// + /// ```rust + /// #![feature(trivial_bounds)] + /// pub struct A where i32: Copy; + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Usually you would not write a trait bound that you know is always + /// true, or never true. However, when using macros, the macro may not + /// know whether or not the constraint would hold or not at the time when + /// generating the code. Currently, the compiler does not alert you if the + /// constraint is always true, and generates an error if it is never true. + /// The `trivial_bounds` feature changes this to be a warning in both + /// cases, giving macros more freedom and flexibility to generate code, + /// while still providing a signal when writing non-macro code that + /// something is amiss. + /// + /// See [RFC 2056] for more details. This feature is currently only + /// available on the nightly channel, see [tracking issue #48214]. + /// + /// [RFC 2056]: https://github.com/rust-lang/rfcs/blob/master/text/2056-allow-trivial-where-clause-constraints.md + /// [tracking issue #48214]: https://github.com/rust-lang/rust/issues/48214 TRIVIAL_BOUNDS, Warn, "these bounds don't depend on an type parameters" @@ -1269,6 +1599,29 @@ declare_lint_pass!( ); declare_lint! { + /// The `ellipsis_inclusive_range_patterns` lint detects the [`...` range + /// pattern], which is deprecated. + /// + /// [`...` range pattern]: https://doc.rust-lang.org/reference/patterns.html#range-patterns + /// + /// ### Example + /// + /// ```rust + /// let x = 123; + /// match x { + /// 0...100 => {} + /// _ => {} + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// The `...` range pattern syntax was changed to `..=` to avoid potential + /// confusion with the [`..` range expression]. Use the new form instead. + /// + /// [`..` range expression]: https://doc.rust-lang.org/reference/expressions/range-expr.html pub ELLIPSIS_INCLUSIVE_RANGE_PATTERNS, Warn, "`...` range patterns are deprecated" @@ -1355,6 +1708,38 @@ impl EarlyLintPass for EllipsisInclusiveRangePatterns { } declare_lint! { + /// The `unnameable_test_items` lint detects [`#[test]`][test] functions + /// that are not able to be run by the test harness because they are in a + /// position where they are not nameable. + /// + /// [test]: https://doc.rust-lang.org/reference/attributes/testing.html#the-test-attribute + /// + /// ### Example + /// + /// ```rust,test + /// fn main() { + /// #[test] + /// fn foo() { + /// // This test will not fail because it does not run. + /// assert_eq!(1, 2); + /// } + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// In order for the test harness to run a test, the test function must be + /// located in a position where it can be accessed from the crate root. + /// This generally means it must be defined in a module, and not anywhere + /// else such as inside another function. The compiler previously allowed + /// this without an error, so a lint was added as an alert that a test is + /// not being used. Whether or not this should be allowed has not yet been + /// decided, see [RFC 2471] and [issue #36629]. + /// + /// [RFC 2471]: https://github.com/rust-lang/rfcs/pull/2471#issuecomment-397414443 + /// [issue #36629]: https://github.com/rust-lang/rust/issues/36629 UNNAMEABLE_TEST_ITEMS, Warn, "detects an item that cannot be named being marked as `#[test_case]`", @@ -1400,6 +1785,41 @@ impl<'tcx> LateLintPass<'tcx> for UnnameableTestItems { } declare_lint! { + /// The `keyword_idents` lint detects edition keywords being used as an + /// identifier. + /// + /// ### Example + /// + /// ```rust,edition2015,compile_fail + /// #![deny(keyword_idents)] + /// // edition 2015 + /// fn dyn() {} + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Rust [editions] allow the language to evolve without breaking + /// backwards compatibility. This lint catches code that uses new keywords + /// that are added to the language that are used as identifiers (such as a + /// variable name, function name, etc.). If you switch the compiler to a + /// new edition without updating the code, then it will fail to compile if + /// you are using a new keyword as an identifier. + /// + /// You can manually change the identifiers to a non-keyword, or use a + /// [raw identifier], for example `r#dyn`, to transition to a new edition. + /// + /// This lint solves the problem automatically. It is "allow" by default + /// because the code is perfectly valid in older editions. The [`cargo + /// fix`] tool with the `--edition` flag will switch this lint to "warn" + /// and automatically apply the suggested fix from the compiler (which is + /// to use a raw identifier). This provides a completely automated way to + /// update old code for a new edition. + /// + /// [editions]: https://doc.rust-lang.org/edition-guide/ + /// [raw identifier]: https://doc.rust-lang.org/reference/identifiers.html + /// [`cargo fix`]: https://doc.rust-lang.org/cargo/commands/cargo-fix.html pub KEYWORD_IDENTS, Allow, "detects edition keywords being used as an identifier", @@ -1801,6 +2221,26 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements { } declare_lint! { + /// The `incomplete_features` lint detects unstable features enabled with + /// the [`feature` attribute] that may function improperly in some or all + /// cases. + /// + /// [`feature` attribute]: https://doc.rust-lang.org/nightly/unstable-book/ + /// + /// ### Example + /// + /// ```rust + /// #![feature(generic_associated_types)] + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Although it is encouraged for people to experiment with unstable + /// features, some of them are known to be incomplete or faulty. This lint + /// is a signal that the feature has not yet been finished, and you may + /// experience problems with it. pub INCOMPLETE_FEATURES, Warn, "incomplete features that may function improperly in some or all cases" @@ -1841,6 +2281,36 @@ impl EarlyLintPass for IncompleteFeatures { } declare_lint! { + /// The `invalid_value` lint detects creating a value that is not valid, + /// such as a NULL reference. + /// + /// ### Example + /// + /// ```rust,no_run + /// # #![allow(unused)] + /// unsafe { + /// let x: &'static i32 = std::mem::zeroed(); + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// In some situations the compiler can detect that the code is creating + /// an invalid value, which should be avoided. + /// + /// In particular, this lint will check for improper use of + /// [`mem::zeroed`], [`mem::uninitialized`], [`mem::transmute`], and + /// [`MaybeUninit::assume_init`] that can cause [undefined behavior]. The + /// lint should provide extra information to indicate what the problem is + /// and a possible solution. + /// + /// [`mem::zeroed`]: https://doc.rust-lang.org/std/mem/fn.zeroed.html + /// [`mem::uninitialized`]: https://doc.rust-lang.org/std/mem/fn.uninitialized.html + /// [`mem::transmute`]: https://doc.rust-lang.org/std/mem/fn.transmute.html + /// [`MaybeUninit::assume_init`]: https://doc.rust-lang.org/std/mem/union.MaybeUninit.html#method.assume_init + /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html pub INVALID_VALUE, Warn, "an invalid value is being created (such as a NULL reference)" @@ -2072,6 +2542,40 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue { } declare_lint! { + /// The `clashing_extern_declarations` lint detects when an `extern fn` + /// has been declared with the same name but different types. + /// + /// ### Example + /// + /// ```rust + /// mod m { + /// extern "C" { + /// fn foo(); + /// } + /// } + /// + /// extern "C" { + /// fn foo(_: u32); + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Because two symbols of the same name cannot be resolved to two + /// different functions at link time, and one function cannot possibly + /// have two types, a clashing extern declaration is almost certainly a + /// mistake. Check to make sure that the `extern` definitions are correct + /// and equivalent, and possibly consider unifying them in one location. + /// + /// This lint does not run between crates because a project may have + /// dependencies which both rely on the same extern function, but declare + /// it in a different (but valid) way. For example, they may both declare + /// an opaque type for one or more of the arguments (which would end up + /// distinct types), or use types that are valid conversions in the + /// language the `extern fn` is defined in. In these cases, the compiler + /// can't say that the clashing declaration is incorrect. pub CLASHING_EXTERN_DECLARATIONS, Warn, "detects when an extern fn has been declared with the same name but different types" diff --git a/compiler/rustc_lint/src/non_ascii_idents.rs b/compiler/rustc_lint/src/non_ascii_idents.rs index 2f0b2a8d680..ecacdcde49f 100644 --- a/compiler/rustc_lint/src/non_ascii_idents.rs +++ b/compiler/rustc_lint/src/non_ascii_idents.rs @@ -4,6 +4,32 @@ use rustc_data_structures::fx::FxHashMap; use rustc_span::symbol::Symbol; declare_lint! { + /// The `non_ascii_idents` lint detects non-ASCII identifiers. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// # #![allow(unused)] + /// #![feature(non_ascii_idents)] + /// #![deny(non_ascii_idents)] + /// fn main() { + /// let föö = 1; + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Currently on stable Rust, identifiers must contain ASCII characters. + /// The [`non_ascii_idents`] nightly-only feature allows identifiers to + /// contain non-ASCII characters. This lint allows projects that wish to + /// retain the limit of only using ASCII characters to switch this lint to + /// "forbid" (for example to ease collaboration or for security reasons). + /// See [RFC 2457] for more details. + /// + /// [`non_ascii_idents`]: https://doc.rust-lang.org/nightly/unstable-book/language-features/non-ascii-idents.html + /// [RFC 2457]: https://github.com/rust-lang/rfcs/blob/master/text/2457-non-ascii-idents.md pub NON_ASCII_IDENTS, Allow, "detects non-ASCII identifiers", @@ -11,6 +37,37 @@ declare_lint! { } declare_lint! { + /// The `uncommon_codepoints` lint detects uncommon Unicode codepoints in + /// identifiers. + /// + /// ### Example + /// + /// ```rust + /// # #![allow(unused)] + /// #![feature(non_ascii_idents)] + /// const µ: f64 = 0.000001; + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// With the [`non_ascii_idents`] nightly-only feature enabled, + /// identifiers are allowed to use non-ASCII characters. This lint warns + /// about using characters which are not commonly used, and may cause + /// visual confusion. + /// + /// This lint is triggered by identifiers that contain a codepoint that is + /// not part of the set of "Allowed" codepoints as described by [Unicode® + /// Technical Standard #39 Unicode Security Mechanisms Section 3.1 General + /// Security Profile for Identifiers][TR39Allowed]. + /// + /// Note that the set of uncommon codepoints may change over time. Beware + /// that if you "forbid" this lint that existing code may fail in the + /// future. + /// + /// [`non_ascii_idents`]: https://doc.rust-lang.org/nightly/unstable-book/language-features/non-ascii-idents.html + /// [TR39Allowed]: https://www.unicode.org/reports/tr39/#General_Security_Profile pub UNCOMMON_CODEPOINTS, Warn, "detects uncommon Unicode codepoints in identifiers", @@ -18,6 +75,43 @@ declare_lint! { } declare_lint! { + /// The `confusable_idents` lint detects visually confusable pairs between + /// identifiers. + /// + /// ### Example + /// + /// ```rust + /// #![feature(non_ascii_idents)] + /// + /// // Latin Capital Letter E With Caron + /// pub const Ě: i32 = 1; + /// // Latin Capital Letter E With Breve + /// pub const Ĕ: i32 = 2; + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// With the [`non_ascii_idents`] nightly-only feature enabled, + /// identifiers are allowed to use non-ASCII characters. This lint warns + /// when different identifiers may appear visually similar, which can + /// cause confusion. + /// + /// The confusable detection algorithm is based on [Unicode® Technical + /// Standard #39 Unicode Security Mechanisms Section 4 Confusable + /// Detection][TR39Confusable]. For every distinct identifier X execute + /// the function `skeleton(X)`. If there exist two distinct identifiers X + /// and Y in the same crate where `skeleton(X) = skeleton(Y)` report it. + /// The compiler uses the same mechanism to check if an identifier is too + /// similar to a keyword. + /// + /// Note that the set of confusable characters may change over time. + /// Beware that if you "forbid" this lint that existing code may fail in + /// the future. + /// + /// [`non_ascii_idents`]: https://doc.rust-lang.org/nightly/unstable-book/language-features/non-ascii-idents.html + /// [TR39Confusable]: https://www.unicode.org/reports/tr39/#Confusable_Detection pub CONFUSABLE_IDENTS, Warn, "detects visually confusable pairs between identifiers", @@ -25,6 +119,41 @@ declare_lint! { } declare_lint! { + /// The `mixed_script_confusables` lint detects visually confusable + /// characters in identifiers between different [scripts]. + /// + /// [scripts]: https://en.wikipedia.org/wiki/Script_(Unicode) + /// + /// ### Example + /// + /// ```rust + /// #![feature(non_ascii_idents)] + /// + /// // The Japanese katakana character エ can be confused with the Han character 工. + /// const エ: &'static str = "アイウ"; + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// With the [`non_ascii_idents`] nightly-only feature enabled, + /// identifiers are allowed to use non-ASCII characters. This lint warns + /// when characters between different scripts may appear visually similar, + /// which can cause confusion. + /// + /// If the crate contains other identifiers in the same script that have + /// non-confusable characters, then this lint will *not* be issued. For + /// example, if the example given above has another identifier with + /// katakana characters (such as `let カタカナ = 123;`), then this indicates + /// that you are intentionally using katakana, and it will not warn about + /// it. + /// + /// Note that the set of confusable characters may change over time. + /// Beware that if you "forbid" this lint that existing code may fail in + /// the future. + /// + /// [`non_ascii_idents`]: https://doc.rust-lang.org/nightly/unstable-book/language-features/non-ascii-idents.html pub MIXED_SCRIPT_CONFUSABLES, Warn, "detects Unicode scripts whose mixed script confusables codepoints are solely used", diff --git a/compiler/rustc_lint/src/nonstandard_style.rs b/compiler/rustc_lint/src/nonstandard_style.rs index f23e8c5e208..24467f81172 100644 --- a/compiler/rustc_lint/src/nonstandard_style.rs +++ b/compiler/rustc_lint/src/nonstandard_style.rs @@ -31,6 +31,24 @@ pub fn method_context(cx: &LateContext<'_>, id: hir::HirId) -> MethodLateContext } declare_lint! { + /// The `non_camel_case_types` lint detects types, variants, traits and + /// type parameters that don't have camel case names. + /// + /// ### Example + /// + /// ```rust + /// struct my_struct; + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// The preferred style for these identifiers is to use "camel case", such + /// as `MyStruct`, where the first letter should not be lowercase, and + /// should not use underscores between letters. Underscores are allowed at + /// the beginning and end of the identifier, as well as between + /// non-letters (such as `X86_64`). pub NON_CAMEL_CASE_TYPES, Warn, "types, variants, traits and type parameters should have camel case names" @@ -161,6 +179,22 @@ impl EarlyLintPass for NonCamelCaseTypes { } declare_lint! { + /// The `non_snake_case` lint detects variables, methods, functions, + /// lifetime parameters and modules that don't have snake case names. + /// + /// ### Example + /// + /// ```rust + /// let MY_VALUE = 5; + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// The preferred style for these identifiers is to use "snake case", + /// where all the characters are in lowercase, with words separated with a + /// single underscore, such as `my_value`. pub NON_SNAKE_CASE, Warn, "variables, methods, functions, lifetime parameters and modules should have snake case names" @@ -379,6 +413,21 @@ impl<'tcx> LateLintPass<'tcx> for NonSnakeCase { } declare_lint! { + /// The `non_upper_case_globals` lint detects static items that don't have + /// uppercase identifiers. + /// + /// ### Example + /// + /// ```rust + /// static max_points: i32 = 5; + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// The preferred style is for static item names to use all uppercase + /// letters such as `MAX_POINTS`. pub NON_UPPER_CASE_GLOBALS, Warn, "static constants should have uppercase identifiers" diff --git a/compiler/rustc_lint/src/redundant_semicolon.rs b/compiler/rustc_lint/src/redundant_semicolon.rs index d4aa4968f25..a31deb87ff0 100644 --- a/compiler/rustc_lint/src/redundant_semicolon.rs +++ b/compiler/rustc_lint/src/redundant_semicolon.rs @@ -4,6 +4,21 @@ use rustc_errors::Applicability; use rustc_span::Span; declare_lint! { + /// The `redundant_semicolons` lint detects unnecessary trailing + /// semicolons. + /// + /// ### Example + /// + /// ```rust + /// let _ = 123;; + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Extra semicolons are not needed, and may be removed to avoid confusion + /// and visual clutter. pub REDUNDANT_SEMICOLONS, Warn, "detects unnecessary trailing semicolons" diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index af32c16bfe8..a202efa6eda 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -23,18 +23,82 @@ use std::cmp; use tracing::debug; declare_lint! { + /// The `unused_comparisons` lint detects comparisons made useless by + /// limits of the types involved. + /// + /// ### Example + /// + /// ```rust + /// fn foo(x: u8) { + /// x >= 0; + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// A useless comparison may indicate a mistake, and should be fixed or + /// removed. UNUSED_COMPARISONS, Warn, "comparisons made useless by limits of the types involved" } declare_lint! { + /// The `overflowing_literals` lint detects literal out of range for its + /// type. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// let x: u8 = 1000; + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// It is usually a mistake to use a literal that overflows the type where + /// it is used. Either use a literal that is within range, or change the + /// type to be within the range of the literal. OVERFLOWING_LITERALS, Deny, "literal out of range for its type" } declare_lint! { + /// The `variant_size_differences` lint detects enums with widely varying + /// variant sizes. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![deny(variant_size_differences)] + /// enum En { + /// V0(u8), + /// VBig([u8; 1024]), + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// It can be a mistake to add a variant to an enum that is much larger + /// than the other variants, bloating the overall size required for all + /// variants. This can impact performance and memory usage. This is + /// triggered if one variant is more than 3 times larger than the + /// second-largest variant. + /// + /// Consider placing the large variant's contents on the heap (for example + /// via [`Box`]) to keep the overall size of the enum itself down. + /// + /// This lint is "allow" by default because it can be noisy, and may not be + /// an actual problem. Decisions about this should be guided with + /// profiling and benchmarking. + /// + /// [`Box`]: https://doc.rust-lang.org/std/boxed/index.html VARIANT_SIZE_DIFFERENCES, Allow, "detects enums with widely varying variant sizes" @@ -495,6 +559,27 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits { } declare_lint! { + /// The `improper_ctypes` lint detects incorrect use of types in foreign + /// modules. + /// + /// ### Example + /// + /// ```rust + /// extern "C" { + /// static STATIC: String; + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// The compiler has several checks to verify that types used in `extern` + /// blocks are safe and follow certain rules to ensure proper + /// compatibility with the foreign interfaces. This lint is issued when it + /// detects a probable mistake in a definition. The lint usually should + /// provide a description of the issue, along with possibly a hint on how + /// to resolve it. IMPROPER_CTYPES, Warn, "proper use of libc types in foreign modules" @@ -503,6 +588,27 @@ declare_lint! { declare_lint_pass!(ImproperCTypesDeclarations => [IMPROPER_CTYPES]); declare_lint! { + /// The `improper_ctypes_definitions` lint detects incorrect use of + /// [`extern` function] definitions. + /// + /// [`extern` function]: https://doc.rust-lang.org/reference/items/functions.html#extern-function-qualifier + /// + /// ### Example + /// + /// ```rust + /// # #![allow(unused)] + /// pub extern "C" fn str_type(p: &str) { } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// There are many parameter and return types that may be specified in an + /// `extern` function that are not compatible with the given ABI. This + /// lint is an alert that these types should not be used. The lint usually + /// should provide a description of the issue, along with possibly a hint + /// on how to resolve it. IMPROPER_CTYPES_DEFINITIONS, Warn, "proper use of libc types in foreign item definitions" diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 0c06b063e41..1e8c30071e7 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -20,6 +20,29 @@ use rustc_span::{BytePos, Span, DUMMY_SP}; use tracing::debug; declare_lint! { + /// The `unused_must_use` lint detects unused result of a type flagged as + /// `#[must_use]`. + /// + /// ### Example + /// + /// ```rust + /// fn returns_result() -> Result<(), ()> { + /// Ok(()) + /// } + /// + /// fn main() { + /// returns_result(); + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// The `#[must_use]` attribute is an indicator that it is a mistake to + /// ignore the value. See [the reference] for more details. + /// + /// [the reference]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute pub UNUSED_MUST_USE, Warn, "unused result of a type flagged as `#[must_use]`", @@ -27,6 +50,39 @@ declare_lint! { } declare_lint! { + /// The `unused_results` lint checks for the unused result of an + /// expression in a statement. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![deny(unused_results)] + /// fn foo() -> T { panic!() } + /// + /// fn main() { + /// foo::(); + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Ignoring the return value of a function may indicate a mistake. In + /// cases were it is almost certain that the result should be used, it is + /// recommended to annotate the function with the [`must_use` attribute]. + /// Failure to use such a return value will trigger the [`unused_must_use` + /// lint] which is warn-by-default. The `unused_results` lint is + /// essentially the same, but triggers for *all* return values. + /// + /// This lint is "allow" by default because it can be noisy, and may not be + /// an actual problem. For example, calling the `remove` method of a `Vec` + /// or `HashMap` returns the previous value, which you may not care about. + /// Using this lint would require explicitly ignoring or discarding such + /// values. + /// + /// [`must_use` attribute]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute + /// [`unused_must_use` lint]: warn-by-default.html#unused-must-use pub UNUSED_RESULTS, Allow, "unused result of an expression in a statement" @@ -265,6 +321,21 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { } declare_lint! { + /// The `path_statements` lint detects path statements with no effect. + /// + /// ### Example + /// + /// ```rust + /// let x = 42; + /// + /// x; + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// It is usually a mistake to have a statement that has no effect. pub PATH_STATEMENTS, Warn, "path statements with no effect" @@ -635,6 +706,21 @@ trait UnusedDelimLint { } declare_lint! { + /// The `unused_parens` lint detects `if`, `match`, `while` and `return` + /// with parentheses; they do not need them. + /// + /// ### Examples + /// + /// ```rust + /// if(true) {} + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// The parenthesis are not needed, and should be removed. This is the + /// preferred style for writing these expressions. pub(super) UNUSED_PARENS, Warn, "`if`, `match`, `while` and `return` do not need parentheses" @@ -808,6 +894,23 @@ impl EarlyLintPass for UnusedParens { } declare_lint! { + /// The `unused_braces` lint detects unnecessary braces around an + /// expression. + /// + /// ### Example + /// + /// ```rust + /// if { true } { + /// // ... + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// The braces are not needed, and should be removed. This is the + /// preferred style for writing these expressions. pub(super) UNUSED_BRACES, Warn, "unnecessary braces around an expression" @@ -929,6 +1032,30 @@ impl EarlyLintPass for UnusedBraces { } declare_lint! { + /// The `unused_import_braces` lint catches unnecessary braces around an + /// imported item. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![deny(unused_import_braces)] + /// use test::{A}; + /// + /// pub mod test { + /// pub struct A; + /// } + /// # fn main() {} + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// If there is only a single item, then remove the braces (`use test::A;` + /// for example). + /// + /// This lint is "allow" by default because it is only enforcing a + /// stylistic choice. UNUSED_IMPORT_BRACES, Allow, "unnecessary braces around an imported item" @@ -978,6 +1105,25 @@ impl EarlyLintPass for UnusedImportBraces { } declare_lint! { + /// The `unused_allocation` lint detects unnecessary allocations that can + /// be eliminated. + /// + /// ### Example + /// + /// ```rust + /// #![feature(box_syntax)] + /// fn main() { + /// let a = (box [1,2,3]).len(); + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// When a `box` expression is immediately coerced to a reference, then + /// the allocation is unnecessary, and a reference (using `&` or `&mut`) + /// should be used instead to avoid the allocation. pub(super) UNUSED_ALLOCATION, Warn, "detects unnecessary allocations that can be eliminated" diff --git a/compiler/rustc_session/src/lint.rs b/compiler/rustc_session/src/lint.rs index 0dcbee08abe..4a3e59f18e5 100644 --- a/compiler/rustc_session/src/lint.rs +++ b/compiler/rustc_session/src/lint.rs @@ -65,9 +65,15 @@ pub struct Lint { /// /// The name is written with underscores, e.g., "unused_imports". /// On the command line, underscores become dashes. + /// + /// See https://rustc-dev-guide.rust-lang.org/diagnostics.html#lint-naming + /// for naming guidelines. pub name: &'static str, /// Default level for the lint. + /// + /// See https://rustc-dev-guide.rust-lang.org/diagnostics.html#diagnostic-levels + /// for guidelines on choosing a default level. pub default_level: Level, /// Description of the lint or the issue it detects. @@ -275,17 +281,58 @@ impl LintBuffer { } /// Declares a static item of type `&'static Lint`. +/// +/// See https://rustc-dev-guide.rust-lang.org/diagnostics.html for documentation +/// and guidelines on writing lints. +/// +/// The macro call should start with a doc comment explaining the lint +/// which will be embedded in the rustc user documentation book. It should +/// be written in markdown and have a format that looks like this: +/// +/// ```rust,ignore (doc-example) +/// /// The `my_lint_name` lint detects [short explanation here]. +/// /// +/// /// ### Example +/// /// +/// /// ```rust +/// /// [insert a concise example that triggers the lint] +/// /// ``` +/// /// +/// /// {{produces}} +/// /// +/// /// ### Explanation +/// /// +/// /// This should be a detailed explanation of *why* the lint exists, +/// /// and also include suggestions on how the user should fix the problem. +/// /// Try to keep the text simple enough that a beginner can understand, +/// /// and include links to other documentation for terminology that a +/// /// beginner may not be familiar with. If this is "allow" by default, +/// /// it should explain why (are there false positives or other issues?). If +/// /// this is a future-incompatible lint, it should say so, with text that +/// /// looks roughly like this: +/// /// +/// /// This is a [future-incompatible] lint to transition this to a hard +/// /// error in the future. See [issue #xxxxx] for more details. +/// /// +/// /// [issue #xxxxx]: https://github.com/rust-lang/rust/issues/xxxxx +/// ``` +/// +/// The `{{produces}}` tag will be automatically replaced with the output from +/// the example by the build system. You can build and view the rustc book +/// with `x.py doc --stage=1 src/doc/rustc --open` (use --stage=0 if just +/// changing the wording of an existing lint). #[macro_export] macro_rules! declare_lint { - ($vis: vis $NAME: ident, $Level: ident, $desc: expr) => ( + ($(#[$attr:meta])* $vis: vis $NAME: ident, $Level: ident, $desc: expr) => ( $crate::declare_lint!( - $vis $NAME, $Level, $desc, + $(#[$attr])* $vis $NAME, $Level, $desc, ); ); - ($vis: vis $NAME: ident, $Level: ident, $desc: expr, + ($(#[$attr:meta])* $vis: vis $NAME: ident, $Level: ident, $desc: expr, $(@future_incompatible = $fi:expr;)? $(@feature_gate = $gate:expr;)? $($v:ident),*) => ( + $(#[$attr])* $vis static $NAME: &$crate::lint::Lint = &$crate::lint::Lint { name: stringify!($NAME), default_level: $crate::lint::$Level, @@ -298,9 +345,10 @@ macro_rules! declare_lint { ..$crate::lint::Lint::default_fields_for_macro() }; ); - ($vis: vis $NAME: ident, $Level: ident, $desc: expr, + ($(#[$attr:meta])* $vis: vis $NAME: ident, $Level: ident, $desc: expr, $lint_edition: expr => $edition_level: ident ) => ( + $(#[$attr])* $vis static $NAME: &$crate::lint::Lint = &$crate::lint::Lint { name: stringify!($NAME), default_level: $crate::lint::$Level, diff --git a/compiler/rustc_session/src/lint/builtin.rs b/compiler/rustc_session/src/lint/builtin.rs index 0fd6cc10382..4c0c7fae29b 100644 --- a/compiler/rustc_session/src/lint/builtin.rs +++ b/compiler/rustc_session/src/lint/builtin.rs @@ -10,6 +10,32 @@ use rustc_span::edition::Edition; use rustc_span::symbol::sym; declare_lint! { + /// The `ill_formed_attribute_input` lint detects ill-formed attribute + /// inputs that were previously accepted and used in practice. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #[inline = "this is not valid"] + /// fn foo() {} + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Previously, inputs for many built-in attributes weren't validated and + /// nonsensical attribute inputs were accepted. After validation was + /// added, it was determined that some existing projects made use of these + /// invalid forms. This is a [future-incompatible] lint to transition this + /// to a hard error in the future. See [issue #57571] for more details. + /// + /// Check the [attribute reference] for details on the valid inputs for + /// attributes. + /// + /// [issue #57571]: https://github.com/rust-lang/rust/issues/57571 + /// [attribute reference]: https://doc.rust-lang.org/nightly/reference/attributes.html + /// [future-incompatible]: ../index.md#future-incompatible-lints pub ILL_FORMED_ATTRIBUTE_INPUT, Deny, "ill-formed attribute inputs that were previously accepted and used in practice", @@ -21,6 +47,32 @@ declare_lint! { } declare_lint! { + /// The `conflicting_repr_hints` lint detects [`repr` attributes] with + /// conflicting hints. + /// + /// [`repr` attributes]: https://doc.rust-lang.org/reference/type-layout.html#representations + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #[repr(u32, u64)] + /// enum Foo { + /// Variant1, + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// The compiler incorrectly accepted these conflicting representations in + /// the past. This is a [future-incompatible] lint to transition this to a + /// hard error in the future. See [issue #68585] for more details. + /// + /// To correct the issue, remove one of the conflicting hints. + /// + /// [issue #68585]: https://github.com/rust-lang/rust/issues/68585 + /// [future-incompatible]: ../index.md#future-incompatible-lints pub CONFLICTING_REPR_HINTS, Deny, "conflicts between `#[repr(..)]` hints that were previously accepted and used in practice", @@ -31,30 +83,198 @@ declare_lint! { } declare_lint! { + /// The `meta_variable_misuse` lint detects possible meta-variable misuse + /// in macro definitions. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![deny(meta_variable_misuse)] + /// + /// macro_rules! foo { + /// () => {}; + /// ($( $i:ident = $($j:ident),+ );*) => { $( $( $i = $k; )+ )* }; + /// } + /// + /// fn main() { + /// foo!(); + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// There are quite a few different ways a [`macro_rules`] macro can be + /// improperly defined. Many of these errors were previously only detected + /// when the macro was expanded or not at all. This lint is an attempt to + /// catch some of these problems when the macro is *defined*. + /// + /// This lint is "allow" by default because it may have false positives + /// and other issues. See [issue #61053] for more details. + /// + /// [`macro_rules`]: https://doc.rust-lang.org/reference/macros-by-example.html + /// [issue #61053]: https://github.com/rust-lang/rust/issues/61053 pub META_VARIABLE_MISUSE, Allow, "possible meta-variable misuse at macro definition" } declare_lint! { + /// The `incomplete_include` lint detects the use of the [`include!`] + /// macro with a file that contains more than one expression. + /// + /// [`include!`]: https://doc.rust-lang.org/std/macro.include.html + /// + /// ### Example + /// + /// ```rust,compile_fail + /// fn main() { + /// include!("foo.txt"); + /// } + /// ``` + /// + /// where the file `foo.txt` contains: + /// + /// ```text + /// println!("hi!"); + /// ``` + /// + /// produces: + /// + /// ```text + /// error: include macro expected single expression in source + /// --> foo.txt:1:14 + /// | + /// 1 | println!("1"); + /// | ^ + /// | + /// = note: `#[deny(incomplete_include)]` on by default + /// ``` + /// + /// ### Explanation + /// + /// The [`include!`] macro is currently only intended to be used to + /// include a single [expression] or multiple [items]. Historically it + /// would ignore any contents after the first expression, but that can be + /// confusing. In the example above, the `println!` expression ends just + /// before the semicolon, making the semicolon "extra" information that is + /// ignored. Perhaps even more surprising, if the included file had + /// multiple print statements, the subsequent ones would be ignored! + /// + /// One workaround is to place the contents in braces to create a [block + /// expression]. Also consider alternatives, like using functions to + /// encapsulate the expressions, or use [proc-macros]. + /// + /// This is a lint instead of a hard error because existing projects were + /// found to hit this error. To be cautious, it is a lint for now. The + /// future semantics of the `include!` macro are also uncertain, see + /// [issue #35560]. + /// + /// [items]: https://doc.rust-lang.org/reference/items.html + /// [expression]: https://doc.rust-lang.org/reference/expressions.html + /// [block expression]: https://doc.rust-lang.org/reference/expressions/block-expr.html + /// [proc-macros]: https://doc.rust-lang.org/reference/procedural-macros.html + /// [issue #35560]: https://github.com/rust-lang/rust/issues/35560 pub INCOMPLETE_INCLUDE, Deny, "trailing content in included file" } declare_lint! { + /// The `arithmetic_overflow` lint detects that an arithmetic operation + /// will [overflow]. + /// + /// [overflow]: https://doc.rust-lang.org/reference/expressions/operator-expr.html#overflow + /// + /// ### Example + /// + /// ```rust,compile_fail + /// 1_i32 << 32; + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// It is very likely a mistake to perform an arithmetic operation that + /// overflows its value. If the compiler is able to detect these kinds of + /// overflows at compile-time, it will trigger this lint. Consider + /// adjusting the expression to avoid overflow, or use a data type that + /// will not overflow. pub ARITHMETIC_OVERFLOW, Deny, "arithmetic operation overflows" } declare_lint! { + /// The `unconditional_panic` lint detects an operation that will cause a + /// panic at runtime. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// # #![allow(unused)] + /// let x = 1 / 0; + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// This lint detects code that is very likely incorrect. When possible, + /// the compiler will attempt to detect situations where code can be + /// evaluated at compile-time to generate more efficient code. While + /// evaluating such code, if it detects that the code will unconditionally + /// panic, this usually indicates that it is doing something incorrectly. + /// If this lint is allowed, then the code will not be evaluated at + /// compile-time, and instead continue to generate code to evaluate at + /// runtime, which may panic during runtime. pub UNCONDITIONAL_PANIC, Deny, "operation will cause a panic at runtime" } declare_lint! { + /// The `const_err` lint detects an erroneous expression while doing + /// constant evaluation. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![allow(unconditional_panic)] + /// let x: &'static i32 = &(1 / 0); + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// This lint detects code that is very likely incorrect. If this lint is + /// allowed, then the code will not be evaluated at compile-time, and + /// instead continue to generate code to evaluate at runtime, which may + /// panic during runtime. + /// + /// Note that this lint may trigger in either inside or outside of a + /// [const context]. Outside of a [const context], the compiler can + /// sometimes evaluate an expression at compile-time in order to generate + /// more efficient code. As the compiler becomes better at doing this, it + /// needs to decide what to do when it encounters code that it knows for + /// certain will panic or is otherwise incorrect. Making this a hard error + /// would prevent existing code that exhibited this behavior from + /// compiling, breaking backwards-compatibility. However, this is almost + /// certainly incorrect code, so this is a deny-by-default lint. For more + /// details, see [RFC 1229] and [issue #28238]. + /// + /// Note that there are several other more specific lints associated with + /// compile-time evaluation, such as [`arithmetic_overflow`], + /// [`unconditional_panic`]. + /// + /// [const context]: https://doc.rust-lang.org/reference/const_eval.html#const-context + /// [RFC 1229]: https://github.com/rust-lang/rfcs/blob/master/text/1229-compile-time-asserts.md + /// [issue #28238]: https://github.com/rust-lang/rust/issues/28238 + /// [`arithmetic_overflow`]: deny-by-default.html#arithmetic-overflow + /// [`unconditional_panic`]: deny-by-default.html#unconditional-panic pub CONST_ERR, Deny, "constant evaluation detected erroneous expression", @@ -62,18 +282,105 @@ declare_lint! { } declare_lint! { + /// The `unused_imports` lint detects imports that are never used. + /// + /// ### Example + /// + /// ```rust + /// use std::collections::HashMap; + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Unused imports may signal a mistake or unfinished code, and clutter + /// the code, and should be removed. If you intended to re-export the item + /// to make it available outside of the module, add a visibility modifier + /// like `pub`. pub UNUSED_IMPORTS, Warn, "imports that are never used" } declare_lint! { + /// The `unused_extern_crates` lint guards against `extern crate` items + /// that are never used. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![deny(unused_extern_crates)] + /// extern crate proc_macro; + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// `extern crate` items that are unused have no effect and should be + /// removed. Note that there are some cases where specifying an `extern + /// crate` is desired for the side effect of ensuring the given crate is + /// linked, even though it is not otherwise directly referenced. The lint + /// can be silenced by aliasing the crate to an underscore, such as + /// `extern crate foo as _`. Also note that it is no longer idiomatic to + /// use `extern crate` in the [2018 edition], as extern crates are now + /// automatically added in scope. + /// + /// This lint is "allow" by default because it can be noisy, and produce + /// false-positives. If a dependency is being removed from a project, it + /// is recommended to remove it from the build configuration (such as + /// `Cargo.toml`) to ensure stale build entries aren't left behind. + /// + /// [2018 edition]: https://doc.rust-lang.org/edition-guide/rust-2018/module-system/path-clarity.html#no-more-extern-crate pub UNUSED_EXTERN_CRATES, Allow, "extern crates that are never used" } declare_lint! { + /// The `unused_crate_dependencies` lint detects crate dependencies that + /// are never used. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![deny(unused_crate_dependencies)] + /// ``` + /// + /// This will produce: + /// + /// ```text + /// error: external crate `regex` unused in `lint_example`: remove the dependency or add `use regex as _;` + /// | + /// note: the lint level is defined here + /// --> src/lib.rs:1:9 + /// | + /// 1 | #![deny(unused_crate_dependencies)] + /// | ^^^^^^^^^^^^^^^^^^^^^^^^^ + /// ``` + /// + /// ### Explanation + /// + /// After removing the code that uses a dependency, this usually also + /// requires removing the dependency from the build configuration. + /// However, sometimes that step can be missed, which leads to time wasted + /// building dependencies that are no longer used. This lint can be + /// enabled to detect dependencies that are never used (more specifically, + /// any dependency passed with the `--extern` command-line flag that is + /// never referenced via [`use`], [`extern crate`], or in any [path]). + /// + /// This lint is "allow" by default because it can provide false positives + /// depending on how the build system is configured. For example, when + /// using Cargo, a "package" consists of multiple crates (such as a + /// library and a binary), but the dependencies are defined for the + /// package as a whole. If there is a dependency that is only used in the + /// binary, but not the library, then the lint will be incorrectly issued + /// in the library. + /// + /// [path]: https://doc.rust-lang.org/reference/paths.html + /// [`use`]: https://doc.rust-lang.org/reference/items/use-declarations.html + /// [`extern crate`]: https://doc.rust-lang.org/reference/items/extern-crates.html pub UNUSED_CRATE_DEPENDENCIES, Allow, "crate dependencies that are never used", @@ -81,42 +388,174 @@ declare_lint! { } declare_lint! { + /// The `unused_qualifications` lint detects unnecessarily qualified + /// names. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![deny(unused_qualifications)] + /// mod foo { + /// pub fn bar() {} + /// } + /// + /// fn main() { + /// use foo::bar; + /// foo::bar(); + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// If an item from another module is already brought into scope, then + /// there is no need to qualify it in this case. You can call `bar()` + /// directly, without the `foo::`. + /// + /// This lint is "allow" by default because it is somewhat pedantic, and + /// doesn't indicate an actual problem, but rather a stylistic choice, and + /// can be noisy when refactoring or moving around code. pub UNUSED_QUALIFICATIONS, Allow, "detects unnecessarily qualified names" } declare_lint! { + /// The `unknown_lints` lint detects unrecognized lint attribute. + /// + /// ### Example + /// + /// ```rust + /// #![allow(not_a_real_lint)] + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// It is usually a mistake to specify a lint that does not exist. Check + /// the spelling, and check the lint listing for the correct name. Also + /// consider if you are using an old version of the compiler, and the lint + /// is only available in a newer version. pub UNKNOWN_LINTS, Warn, "unrecognized lint attribute" } declare_lint! { + /// The `unused_variables` lint detects variables which are not used in + /// any way. + /// + /// ### Example + /// + /// ```rust + /// let x = 5; + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Unused variables may signal a mistake or unfinished code. To silence + /// the warning for the individual variable, prefix it with an underscore + /// such as `_x`. pub UNUSED_VARIABLES, Warn, "detect variables which are not used in any way" } declare_lint! { + /// The `unused_assignments` lint detects assignments that will never be read. + /// + /// ### Example + /// + /// ```rust + /// let mut x = 5; + /// x = 6; + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Unused assignments may signal a mistake or unfinished code. If the + /// variable is never used after being assigned, then the assignment can + /// be removed. Variables with an underscore prefix such as `_x` will not + /// trigger this lint. pub UNUSED_ASSIGNMENTS, Warn, "detect assignments that will never be read" } declare_lint! { + /// The `dead_code` lint detects unused, unexported items. + /// + /// ### Example + /// + /// ```rust + /// fn foo() {} + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Dead code may signal a mistake or unfinished code. To silence the + /// warning for individual items, prefix the name with an underscore such + /// as `_foo`. If it was intended to expose the item outside of the crate, + /// consider adding a visibility modifier like `pub`. Otherwise consider + /// removing the unused code. pub DEAD_CODE, Warn, "detect unused, unexported items" } declare_lint! { + /// The `unused_attributes` lint detects attributes that were not used by + /// the compiler. + /// + /// ### Example + /// + /// ```rust + /// #![macro_export] + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Unused [attributes] may indicate the attribute is placed in the wrong + /// position. Consider removing it, or placing it in the correct position. + /// Also consider if you intended to use an _inner attribute_ (with a `!` + /// such as `#![allow(unused)]`) which applies to the item the attribute + /// is within, or an _outer attribute_ (without a `!` such as + /// `#[allow(unsued)]`) which applies to the item *following* the + /// attribute. + /// + /// [attributes]: https://doc.rust-lang.org/reference/attributes.html pub UNUSED_ATTRIBUTES, Warn, "detects attributes that were not used by the compiler" } declare_lint! { + /// The `unreachable_code` lint detects unreachable code paths. + /// + /// ### Example + /// + /// ```rust,no_run + /// panic!("we never go past here!"); + /// + /// let x = 5; + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Unreachable code may signal a mistake or unfinished code. If the code + /// is no longer in use, consider removing it. pub UNREACHABLE_CODE, Warn, "detects unreachable code paths", @@ -124,48 +563,217 @@ declare_lint! { } declare_lint! { + /// The `unreachable_patterns` lint detects unreachable patterns. + /// + /// ### Example + /// + /// ```rust + /// let x = 5; + /// match x { + /// y => (), + /// 5 => (), + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// This usually indicates a mistake in how the patterns are specified or + /// ordered. In this example, the `y` pattern will always match, so the + /// five is impossible to reach. Remember, match arms match in order, you + /// probably wanted to put the `5` case above the `y` case. pub UNREACHABLE_PATTERNS, Warn, "detects unreachable patterns" } declare_lint! { + /// The `overlapping_patterns` lint detects `match` arms that have + /// [range patterns] that overlap. + /// + /// [range patterns]: https://doc.rust-lang.org/nightly/reference/patterns.html#range-patterns + /// + /// ### Example + /// + /// ```rust + /// let x = 123u8; + /// match x { + /// 0..=100 => { println!("small"); } + /// 100..=255 => { println!("large"); } + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// It is likely a mistake to have range patterns in a match expression + /// that overlap. Check that the beginning and end values are what you + /// expect, and keep in mind that with `..=` the left and right bounds are + /// inclusive. pub OVERLAPPING_PATTERNS, Warn, "detects overlapping patterns" } declare_lint! { + /// The `bindings_with_variant_name` lint detects pattern bindings with + /// the same name as one of the matched variants. + /// + /// ### Example + /// + /// ```rust + /// pub enum Enum { + /// Foo, + /// Bar, + /// } + /// + /// pub fn foo(x: Enum) { + /// match x { + /// Foo => {} + /// Bar => {} + /// } + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// It is usually a mistake to specify an enum variant name as an + /// [identifier pattern]. In the example above, the `match` arms are + /// specifying a variable name to bind the value of `x` to. The second arm + /// is ignored because the first one matches *all* values. The likely + /// intent is that the arm was intended to match on the enum variant. + /// + /// Two possible solutions are: + /// + /// * Specify the enum variant using a [path pattern], such as + /// `Enum::Foo`. + /// * Bring the enum variants into local scope, such as adding `use + /// Enum::*;` to the beginning of the `foo` function in the example + /// above. + /// + /// [identifier pattern]: https://doc.rust-lang.org/reference/patterns.html#identifier-patterns + /// [path pattern]: https://doc.rust-lang.org/reference/patterns.html#path-patterns pub BINDINGS_WITH_VARIANT_NAME, Warn, "detects pattern bindings with the same name as one of the matched variants" } declare_lint! { + /// The `unused_macros` lint detects macros that were not used. + /// + /// ### Example + /// + /// ```rust + /// macro_rules! unused { + /// () => {}; + /// } + /// + /// fn main() { + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Unused macros may signal a mistake or unfinished code. To silence the + /// warning for the individual macro, prefix the name with an underscore + /// such as `_my_macro`. If you intended to export the macro to make it + /// available outside of the crate, use the [`macro_export` attribute]. + /// + /// [`macro_export` attribute]: https://doc.rust-lang.org/reference/macros-by-example.html#path-based-scope pub UNUSED_MACROS, Warn, "detects macros that were not used" } declare_lint! { + /// The `warnings` lint allows you to change the level of other + /// lints which produce warnings. + /// + /// ### Example + /// + /// ```rust + /// #![deny(warnings)] + /// fn foo() {} + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// The `warnings` lint is a bit special; by changing its level, you + /// change every other warning that would produce a warning to whatever + /// value you'd like. As such, you won't ever trigger this lint in your + /// code directly. pub WARNINGS, Warn, "mass-change the level for lints which produce warnings" } declare_lint! { + /// The `unused_features` lint detects unused or unknown features found in + /// crate-level [`feature` attributes]. + /// + /// [`feature` attributes]: https://doc.rust-lang.org/nightly/unstable-book/ + /// + /// Note: This lint is currently not functional, see [issue #44232] for + /// more details. + /// + /// [issue #44232]: https://github.com/rust-lang/rust/issues/44232 pub UNUSED_FEATURES, Warn, "unused features found in crate-level `#[feature]` directives" } declare_lint! { + /// The `stable_features` lint detects a [`feature` attribute] that + /// has since been made stable. + /// + /// [`feature` attribute]: https://doc.rust-lang.org/nightly/unstable-book/ + /// + /// ### Example + /// + /// ```rust + /// #![feature(test_accepted_feature)] + /// fn main() {} + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// When a feature is stabilized, it is no longer necessary to include a + /// `#![feature]` attribute for it. To fix, simply remove the + /// `#![feature]` attribute. pub STABLE_FEATURES, Warn, "stable features found in `#[feature]` directive" } declare_lint! { + /// The `unknown_crate_types` lint detects an unknown crate type found in + /// a [`crate_type` attribute]. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![crate_type="lol"] + /// fn main() {} + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// An unknown value give to the `crate_type` attribute is almost + /// certainly a mistake. + /// + /// [`crate_type` attribute]: https://doc.rust-lang.org/reference/linkage.html pub UNKNOWN_CRATE_TYPES, Deny, "unknown crate type found in `#[crate_type]` directive", @@ -173,18 +781,104 @@ declare_lint! { } declare_lint! { + /// The `trivial_casts` lint detects trivial casts which could be replaced + /// with coercion, which may require [type ascription] or a temporary + /// variable. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![deny(trivial_casts)] + /// let x: &u32 = &42; + /// let y = x as *const u32; + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// A trivial cast is a cast `e as T` where `e` has type `U` and `U` is a + /// subtype of `T`. This type of cast is usually unnecessary, as it can be + /// usually be inferred. + /// + /// This lint is "allow" by default because there are situations, such as + /// with FFI interfaces or complex type aliases, where it triggers + /// incorrectly, or in situations where it will be more difficult to + /// clearly express the intent. It may be possible that this will become a + /// warning in the future, possibly with [type ascription] providing a + /// convenient way to work around the current issues. See [RFC 401] for + /// historical context. + /// + /// [type ascription]: https://github.com/rust-lang/rust/issues/23416 + /// [RFC 401]: https://github.com/rust-lang/rfcs/blob/master/text/0401-coercions.md pub TRIVIAL_CASTS, Allow, "detects trivial casts which could be removed" } declare_lint! { + /// The `trivial_numeric_casts` lint detects trivial numeric casts of types + /// which could be removed. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![deny(trivial_numeric_casts)] + /// let x = 42_i32 as i32; + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// A trivial numeric cast is a cast of a numeric type to the same numeric + /// type. This type of cast is usually unnecessary. + /// + /// This lint is "allow" by default because there are situations, such as + /// with FFI interfaces or complex type aliases, where it triggers + /// incorrectly, or in situations where it will be more difficult to + /// clearly express the intent. It may be possible that this will become a + /// warning in the future, possibly with [type ascription] providing a + /// convenient way to work around the current issues. See [RFC 401] for + /// historical context. + /// + /// [type ascription]: https://github.com/rust-lang/rust/issues/23416 + /// [RFC 401]: https://github.com/rust-lang/rfcs/blob/master/text/0401-coercions.md pub TRIVIAL_NUMERIC_CASTS, Allow, "detects trivial casts of numeric types which could be removed" } declare_lint! { + /// The `private_in_public` lint detects private items in public + /// interfaces not caught by the old implementation. + /// + /// ### Example + /// + /// ```rust + /// # #![allow(unused)] + /// struct SemiPriv; + /// + /// mod m1 { + /// struct Priv; + /// impl super::SemiPriv { + /// pub fn f(_: Priv) {} + /// } + /// } + /// # fn main() {} + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// The visibility rules are intended to prevent exposing private items in + /// public interfaces. This is a [future-incompatible] lint to transition + /// this to a hard error in the future. See [issue #34537] for more + /// details. + /// + /// [issue #34537]: https://github.com/rust-lang/rust/issues/34537 + /// [future-incompatible]: ../index.md#future-incompatible-lints pub PRIVATE_IN_PUBLIC, Warn, "detect private items in public interfaces not caught by the old implementation", @@ -195,12 +889,76 @@ declare_lint! { } declare_lint! { + /// The `exported_private_dependencies` lint detects private dependencies + /// that are exposed in a public interface. + /// + /// ### Example + /// + /// ```rust,ignore (needs-dependency) + /// pub fn foo() -> Option { + /// None + /// } + /// ``` + /// + /// This will produce: + /// + /// ```text + /// warning: type `bar::Thing` from private dependency 'bar' in public interface + /// --> src/lib.rs:3:1 + /// | + /// 3 | pub fn foo() -> Option { + /// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + /// | + /// = note: `#[warn(exported_private_dependencies)]` on by default + /// ``` + /// + /// ### Explanation + /// + /// Dependencies can be marked as "private" to indicate that they are not + /// exposed in the public interface of a crate. This can be used by Cargo + /// to independently resolve those dependencies because it can assume it + /// does not need to unify them with other packages using that same + /// dependency. This lint is an indication of a violation of that + /// contract. + /// + /// To fix this, avoid exposing the dependency in your public interface. + /// Or, switch the dependency to a public dependency. + /// + /// Note that support for this is only available on the nightly channel. + /// See [RFC 1977] for more details, as well as the [Cargo documentation]. + /// + /// [RFC 1977]: https://github.com/rust-lang/rfcs/blob/master/text/1977-public-private-dependencies.md + /// [Cargo documentation]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#public-dependency pub EXPORTED_PRIVATE_DEPENDENCIES, Warn, "public interface leaks type from a private dependency" } declare_lint! { + /// The `pub_use_of_private_extern_crate` lint detects a specific + /// situation of re-exporting a private `extern crate`. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// extern crate core; + /// pub use core as reexported_core; + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// A public `use` declaration should not be used to publicly re-export a + /// private `extern crate`. `pub extern crate` should be used instead. + /// + /// This was historically allowed, but is not the intended behavior + /// according to the visibility rules. This is a [future-incompatible] + /// lint to transition this to a hard error in the future. See [issue + /// #34537] for more details. + /// + /// [issue #34537]: https://github.com/rust-lang/rust/issues/34537 + /// [future-incompatible]: ../index.md#future-incompatible-lints pub PUB_USE_OF_PRIVATE_EXTERN_CRATE, Deny, "detect public re-exports of private extern crates", @@ -211,6 +969,26 @@ declare_lint! { } declare_lint! { + /// The `invalid_type_param_default` lint detects type parameter defaults + /// erroneously allowed in an invalid location. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// fn foo(t: T) {} + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Default type parameters were only intended to be allowed in certain + /// situations, but historically the compiler allowed them everywhere. + /// This is a [future-incompatible] lint to transition this to a hard + /// error in the future. See [issue #36887] for more details. + /// + /// [issue #36887]: https://github.com/rust-lang/rust/issues/36887 + /// [future-incompatible]: ../index.md#future-incompatible-lints pub INVALID_TYPE_PARAM_DEFAULT, Deny, "type parameter default erroneously allowed in invalid location", @@ -221,24 +999,174 @@ declare_lint! { } declare_lint! { + /// The `renamed_and_removed_lints` lint detects lints that have been + /// renamed or removed. + /// + /// ### Example + /// + /// ```rust + /// #![deny(raw_pointer_derive)] + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// To fix this, either remove the lint or use the new name. This can help + /// avoid confusion about lints that are no longer valid, and help + /// maintain consistency for renamed lints. pub RENAMED_AND_REMOVED_LINTS, Warn, "lints that have been renamed or removed" } declare_lint! { + /// The `unaligned_references` lint detects unaligned references to fields + /// of [packed] structs. + /// + /// [packed]: https://doc.rust-lang.org/reference/type-layout.html#the-alignment-modifiers + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![deny(unaligned_references)] + /// + /// #[repr(packed)] + /// pub struct Foo { + /// field1: u64, + /// field2: u8, + /// } + /// + /// fn main() { + /// unsafe { + /// let foo = Foo { field1: 0, field2: 0 }; + /// let _ = &foo.field1; + /// } + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Creating a reference to an insufficiently aligned packed field is + /// [undefined behavior] and should be disallowed. + /// + /// This lint is "allow" by default because there is no stable + /// alternative, and it is not yet certain how widespread existing code + /// will trigger this lint. + /// + /// See [issue #27060] for more discussion. + /// + /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html + /// [issue #27060]: https://github.com/rust-lang/rust/issues/27060 pub UNALIGNED_REFERENCES, Allow, "detects unaligned references to fields of packed structs", } declare_lint! { + /// The `const_item_mutation` lint detects attempts to mutate a `const` + /// item. + /// + /// ### Example + /// + /// ```rust + /// const FOO: [i32; 1] = [0]; + /// + /// fn main() { + /// FOO[0] = 1; + /// // This will print "[0]". + /// println!("{:?}", FOO); + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Trying to directly mutate a `const` item is almost always a mistake. + /// What is happening in the example above is that a temporary copy of the + /// `const` is mutated, but the original `const` is not. Each time you + /// refer to the `const` by name (such as `FOO` in the example above), a + /// separate copy of the value is inlined at that location. + /// + /// This lint checks for writing directly to a field (`FOO.field = + /// some_value`) or array entry (`FOO[0] = val`), or taking a mutable + /// reference to the const item (`&mut FOO`), including through an + /// autoderef (`FOO.some_mut_self_method()`). + /// + /// There are various alternatives depending on what you are trying to + /// accomplish: + /// + /// * First, always reconsider using mutable globals, as they can be + /// difficult to use correctly, and can make the code more difficult to + /// use or understand. + /// * If you are trying to perform a one-time initialization of a global: + /// * If the value can be computed at compile-time, consider using + /// const-compatible values (see [Constant Evaluation]). + /// * For more complex single-initialization cases, consider using a + /// third-party crate, such as [`lazy_static`] or [`once_cell`]. + /// * If you are using the [nightly channel], consider the new + /// [`lazy`] module in the standard library. + /// * If you truly need a mutable global, consider using a [`static`], + /// which has a variety of options: + /// * Simple data types can be directly defined and mutated with an + /// [`atomic`] type. + /// * More complex types can be placed in a synchronization primitive + /// like a [`Mutex`], which can be initialized with one of the options + /// listed above. + /// * A [mutable `static`] is a low-level primitive, requiring unsafe. + /// Typically This should be avoided in preference of something + /// higher-level like one of the above. + /// + /// [Constant Evaluation]: https://doc.rust-lang.org/reference/const_eval.html + /// [`static`]: https://doc.rust-lang.org/reference/items/static-items.html + /// [mutable `static`]: https://doc.rust-lang.org/reference/items/static-items.html#mutable-statics + /// [`lazy`]: https://doc.rust-lang.org/nightly/std/lazy/index.html + /// [`lazy_static`]: https://crates.io/crates/lazy_static + /// [`once_cell`]: https://crates.io/crates/once_cell + /// [`atomic`]: https://doc.rust-lang.org/std/sync/atomic/index.html + /// [`Mutex`]: https://doc.rust-lang.org/std/sync/struct.Mutex.html pub CONST_ITEM_MUTATION, Warn, "detects attempts to mutate a `const` item", } declare_lint! { + /// The `safe_packed_borrows` lint detects borrowing a field in the + /// interior of a packed structure with alignment other than 1. + /// + /// ### Example + /// + /// ```rust + /// #[repr(packed)] + /// pub struct Unaligned(pub T); + /// + /// pub struct Foo { + /// start: u8, + /// data: Unaligned, + /// } + /// + /// fn main() { + /// let x = Foo { start: 0, data: Unaligned(1) }; + /// let y = &x.data.0; + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// This type of borrow is unsafe and can cause errors on some platforms + /// and violates some assumptions made by the compiler. This was + /// previously allowed unintentionally. This is a [future-incompatible] + /// lint to transition this to a hard error in the future. See [issue + /// #46043] for more details, including guidance on how to solve the + /// problem. + /// + /// [issue #46043]: https://github.com/rust-lang/rust/issues/46043 + /// [future-incompatible]: ../index.md#future-incompatible-lints pub SAFE_PACKED_BORROWS, Warn, "safe borrows of fields of packed structs were erroneously allowed", @@ -249,6 +1177,49 @@ declare_lint! { } declare_lint! { + /// The `patterns_in_fns_without_body` lint detects `mut` identifier + /// patterns as a parameter in functions without a body. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// trait Trait { + /// fn foo(mut arg: u8); + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// To fix this, remove `mut` from the parameter in the trait definition; + /// it can be used in the implementation. That is, the following is OK: + /// + /// ```rust + /// trait Trait { + /// fn foo(arg: u8); // Removed `mut` here + /// } + /// + /// impl Trait for i32 { + /// fn foo(mut arg: u8) { // `mut` here is OK + /// + /// } + /// } + /// ``` + /// + /// Trait definitions can define functions without a body to specify a + /// function that implementors must define. The parameter names in the + /// body-less functions are only allowed to be `_` or an [identifier] for + /// documentation purposes (only the type is relevant). Previous versions + /// of the compiler erroneously allowed [identifier patterns] with the + /// `mut` keyword, but this was not intended to be allowed. This is a + /// [future-incompatible] lint to transition this to a hard error in the + /// future. See [issue #35203] for more details. + /// + /// [identifier]: https://doc.rust-lang.org/reference/identifiers.html + /// [identifier patterns]: https://doc.rust-lang.org/reference/patterns.html#identifier-patterns + /// [issue #35203]: https://github.com/rust-lang/rust/issues/35203 + /// [future-incompatible]: ../index.md#future-incompatible-lints pub PATTERNS_IN_FNS_WITHOUT_BODY, Deny, "patterns in functions without body were erroneously allowed", @@ -259,6 +1230,38 @@ declare_lint! { } declare_lint! { + /// The `late_bound_lifetime_arguments` lint detects generic lifetime + /// arguments in path segments with late bound lifetime parameters. + /// + /// ### Example + /// + /// ```rust + /// struct S; + /// + /// impl S { + /// fn late<'a, 'b>(self, _: &'a u8, _: &'b u8) {} + /// } + /// + /// fn main() { + /// S.late::<'static>(&0, &0); + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// It is not clear how to provide arguments for early-bound lifetime + /// parameters if they are intermixed with late-bound parameters in the + /// same list. For now, providing any explicit arguments will trigger this + /// lint if late-bound parameters are present, so in the future a solution + /// can be adopted without hitting backward compatibility issues. This is + /// a [future-incompatible] lint to transition this to a hard error in the + /// future. See [issue #42868] for more details, along with a description + /// of the difference between early and late-bound parameters. + /// + /// [issue #42868]: https://github.com/rust-lang/rust/issues/42868 + /// [future-incompatible]: ../index.md#future-incompatible-lints pub LATE_BOUND_LIFETIME_ARGUMENTS, Warn, "detects generic lifetime arguments in path segments with late bound lifetime parameters", @@ -269,6 +1272,32 @@ declare_lint! { } declare_lint! { + /// The `order_dependent_trait_objects` lint detects a trait coherency + /// violation that would allow creating two trait impls for the same + /// dynamic trait object involving marker traits. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// pub trait Trait {} + /// + /// impl Trait for dyn Send + Sync { } + /// impl Trait for dyn Sync + Send { } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// A previous bug caused the compiler to interpret traits with different + /// orders (such as `Send + Sync` and `Sync + Send`) as distinct types + /// when they were intended to be treated the same. This allowed code to + /// define separate trait implementations when there should be a coherence + /// error. This is a [future-incompatible] lint to transition this to a + /// hard error in the future. See [issue #56484] for more details. + /// + /// [issue #56484]: https://github.com/rust-lang/rust/issues/56484 + /// [future-incompatible]: ../index.md#future-incompatible-lints pub ORDER_DEPENDENT_TRAIT_OBJECTS, Deny, "trait-object types were treated as different depending on marker-trait order", @@ -279,6 +1308,36 @@ declare_lint! { } declare_lint! { + /// The `coherence_leak_check` lint detects conflicting implementations of + /// a trait that are only distinguished by the old leak-check code. + /// + /// ### Example + /// + /// ```rust + /// trait SomeTrait { } + /// impl SomeTrait for for<'a> fn(&'a u8) { } + /// impl<'a> SomeTrait for fn(&'a u8) { } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// In the past, the compiler would accept trait implementations for + /// identical functions that differed only in where the lifetime binder + /// appeared. Due to a change in the borrow checker implementation to fix + /// several bugs, this is no longer allowed. However, since this affects + /// existing code, this is a [future-incompatible] lint to transition this + /// to a hard error in the future. + /// + /// Code relying on this pattern should introduce "[newtypes]", + /// like `struct Foo(for<'a> fn(&'a u8))`. + /// + /// See [issue #56105] for more details. + /// + /// [issue #56105]: https://github.com/rust-lang/rust/issues/56105 + /// [newtypes]: https://doc.rust-lang.org/book/ch19-04-advanced-types.html#using-the-newtype-pattern-for-type-safety-and-abstraction + /// [future-incompatible]: ../index.md#future-incompatible-lints pub COHERENCE_LEAK_CHECK, Warn, "distinct impls distinguished only by the leak-check code", @@ -289,6 +1348,29 @@ declare_lint! { } declare_lint! { + /// The `deprecated` lint detects use of deprecated items. + /// + /// ### Example + /// + /// ```rust + /// #[deprecated] + /// fn foo() {} + /// + /// fn bar() { + /// foo(); + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Items may be marked "deprecated" with the [`deprecated` attribute] to + /// indicate that they should no longer be used. Usually the attribute + /// should include a note on what to use instead, or check the + /// documentation. + /// + /// [`deprecated` attribute]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-deprecated-attribute pub DEPRECATED, Warn, "detects use of deprecated items", @@ -296,36 +1378,158 @@ declare_lint! { } declare_lint! { + /// The `unused_unsafe` lint detects unnecessary use of an `unsafe` block. + /// + /// ### Example + /// + /// ```rust + /// unsafe {} + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// If nothing within the block requires `unsafe`, then remove the + /// `unsafe` marker because it is not required and may cause confusion. pub UNUSED_UNSAFE, Warn, "unnecessary use of an `unsafe` block" } declare_lint! { + /// The `unused_mut` lint detects mut variables which don't need to be + /// mutable. + /// + /// ### Example + /// + /// ```rust + /// let mut x = 5; + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// The preferred style is to only mark variables as `mut` if it is + /// required. pub UNUSED_MUT, Warn, "detect mut variables which don't need to be mutable" } declare_lint! { + /// The `unconditional_recursion` lint detects functions that cannot + /// return without calling themselves. + /// + /// ### Example + /// + /// ```rust + /// fn foo() { + /// foo(); + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// It is usually a mistake to have a recursive call that does not have + /// some condition to cause it to terminate. If you really intend to have + /// an infinite loop, using a `loop` expression is recommended. pub UNCONDITIONAL_RECURSION, Warn, "functions that cannot return without calling themselves" } declare_lint! { + /// The `single_use_lifetimes` lint detects lifetimes that are only used + /// once. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![deny(single_use_lifetimes)] + /// + /// fn foo<'a>(x: &'a u32) {} + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Specifying an explicit lifetime like `'a` in a function or `impl` + /// should only be used to link together two things. Otherwise, you should + /// just use `'_` to indicate that the lifetime is not linked to anything, + /// or elide the lifetime altogether if possible. + /// + /// This lint is "allow" by default because it was introduced at a time + /// when `'_` and elided lifetimes were first being introduced, and this + /// lint would be too noisy. Also, there are some known false positives + /// that it produces. See [RFC 2115] for historical context, and [issue + /// #44752] for more details. + /// + /// [RFC 2115]: https://github.com/rust-lang/rfcs/blob/master/text/2115-argument-lifetimes.md + /// [issue #44752]: https://github.com/rust-lang/rust/issues/44752 pub SINGLE_USE_LIFETIMES, Allow, "detects lifetime parameters that are only used once" } declare_lint! { + /// The `unused_lifetimes` lint detects lifetime parameters that are never + /// used. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #[deny(unused_lifetimes)] + /// + /// pub fn foo<'a>() {} + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Unused lifetime parameters may signal a mistake or unfinished code. + /// Consider removing the parameter. pub UNUSED_LIFETIMES, Allow, "detects lifetime parameters that are never used" } declare_lint! { + /// The `tyvar_behind_raw_pointer` lint detects raw pointer to an + /// inference variable. + /// + /// ### Example + /// + /// ```rust,edition2015 + /// // edition 2015 + /// let data = std::ptr::null(); + /// let _ = &data as *const *const (); + /// + /// if data.is_null() {} + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// This kind of inference was previously allowed, but with the future + /// arrival of [arbitrary self types], this can introduce ambiguity. To + /// resolve this, use an explicit type instead of relying on type + /// inference. + /// + /// This is a [future-incompatible] lint to transition this to a hard + /// error in the 2018 edition. See [issue #46906] for more details. This + /// is currently a hard-error on the 2018 edition, and is "warn" by + /// default in the 2015 edition. + /// + /// [arbitrary self types]: https://github.com/rust-lang/rust/issues/44874 + /// [issue #46906]: https://github.com/rust-lang/rust/issues/46906 + /// [future-incompatible]: ../index.md#future-incompatible-lints pub TYVAR_BEHIND_RAW_POINTER, Warn, "raw pointer to an inference variable", @@ -336,6 +1540,34 @@ declare_lint! { } declare_lint! { + /// The `elided_lifetimes_in_paths` lint detects the use of hidden + /// lifetime parameters. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![deny(elided_lifetimes_in_paths)] + /// struct Foo<'a> { + /// x: &'a u32 + /// } + /// + /// fn foo(x: &Foo) { + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Elided lifetime parameters can make it difficult to see at a glance + /// that borrowing is occurring. This lint ensures that lifetime + /// parameters are always explicitly stated, even if it is the `'_` + /// [placeholder lifetime]. + /// + /// This lint is "allow" by default because it has some known issues, and + /// may require a significant transition for old code. + /// + /// [placeholder lifetime]: https://doc.rust-lang.org/reference/lifetime-elision.html#lifetime-elision-in-functions pub ELIDED_LIFETIMES_IN_PATHS, Allow, "hidden lifetime parameters in types are deprecated", @@ -343,12 +1575,78 @@ declare_lint! { } declare_lint! { + /// The `bare_trait_objects` lint suggests using `dyn Trait` for trait + /// objects. + /// + /// ### Example + /// + /// ```rust + /// trait Trait { } + /// + /// fn takes_trait_object(_: Box) { + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Without the `dyn` indicator, it can be ambiguous or confusing when + /// reading code as to whether or not you are looking at a trait object. + /// The `dyn` keyword makes it explicit, and adds a symmetry to contrast + /// with [`impl Trait`]. + /// + /// [`impl Trait`]: https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters pub BARE_TRAIT_OBJECTS, Warn, "suggest using `dyn Trait` for trait objects" } declare_lint! { + /// The `absolute_paths_not_starting_with_crate` lint detects fully + /// qualified paths that start with a module name instead of `crate`, + /// `self`, or an extern crate name + /// + /// ### Example + /// + /// ```rust,edition2015,compile_fail + /// #![deny(absolute_paths_not_starting_with_crate)] + /// + /// mod foo { + /// pub fn bar() {} + /// } + /// + /// fn main() { + /// ::foo::bar(); + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Rust [editions] allow the language to evolve without breaking + /// backwards compatibility. This lint catches code that uses absolute + /// paths in the style of the 2015 edition. In the 2015 edition, absolute + /// paths (those starting with `::`) refer to either the crate root or an + /// external crate. In the 2018 edition it was changed so that they only + /// refer to external crates. The path prefix `crate::` should be used + /// instead to reference items from the crate root. + /// + /// If you switch the compiler from the 2015 to 2018 edition without + /// updating the code, then it will fail to compile if the old style paths + /// are used. You can manually change the paths to use the `crate::` + /// prefix to transition to the 2018 edition. + /// + /// This lint solves the problem automatically. It is "allow" by default + /// because the code is perfectly valid in the 2015 edition. The [`cargo + /// fix`] tool with the `--edition` flag will switch this lint to "warn" + /// and automatically apply the suggested fix from the compiler. This + /// provides a completely automated way to update old code to the 2018 + /// edition. + /// + /// [editions]: https://doc.rust-lang.org/edition-guide/ + /// [`cargo fix`]: https://doc.rust-lang.org/cargo/commands/cargo-fix.html pub ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE, Allow, "fully qualified paths that start with a module name \ @@ -360,6 +1658,45 @@ declare_lint! { } declare_lint! { + /// The `illegal_floating_point_literal_pattern` lint detects + /// floating-point literals used in patterns. + /// + /// ### Example + /// + /// ```rust + /// let x = 42.0; + /// + /// match x { + /// 5.0 => {} + /// _ => {} + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Previous versions of the compiler accepted floating-point literals in + /// patterns, but it was later determined this was a mistake. The + /// semantics of comparing floating-point values may not be clear in a + /// pattern when contrasted with "structural equality". Typically you can + /// work around this by using a [match guard], such as: + /// + /// ```rust + /// # let x = 42.0; + /// + /// match x { + /// y if y == 5.0 => {} + /// _ => {} + /// } + /// ``` + /// + /// This is a [future-incompatible] lint to transition this to a hard + /// error in the future. See [issue #41620] for more details. + /// + /// [issue #41620]: https://github.com/rust-lang/rust/issues/41620 + /// [match guard]: https://doc.rust-lang.org/reference/expressions/match-expr.html#match-guards + /// [future-incompatible]: ../index.md#future-incompatible-lints pub ILLEGAL_FLOATING_POINT_LITERAL_PATTERN, Warn, "floating-point literals cannot be used in patterns", @@ -370,6 +1707,40 @@ declare_lint! { } declare_lint! { + /// The `unstable_name_collisions` lint detects that you have used a name + /// that the standard library plans to add in the future. + /// + /// ### Example + /// + /// ```rust + /// trait MyIterator : Iterator { + /// // is_sorted is an unstable method that already exists on the Iterator trait + /// fn is_sorted(self) -> bool where Self: Sized {true} + /// } + /// + /// impl MyIterator for T where T: Iterator { } + /// + /// let x = vec![1,2,3]; + /// let _ = x.iter().is_sorted(); + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// When new methods are added to traits in the standard library, they are + /// usually added in an "unstable" form which is only available on the + /// [nightly channel] with a [`feature` attribute]. If there is any + /// pre-existing code which extends a trait to have a method with the same + /// name, then the names will collide. In the future, when the method is + /// stabilized, this will cause an error due to the ambiguity. This lint + /// is an early-warning to let you know that there may be a collision in + /// the future. This can be avoided by adding type annotations to + /// disambiguate which trait method you intend to call, such as + /// `MyIterator::is_sorted(my_iter)` or renaming or removing the method. + /// + /// [nightly channel]: https://doc.rust-lang.org/book/appendix-07-nightly-rust.html + /// [`feature` attribute]: https://doc.rust-lang.org/nightly/unstable-book/ pub UNSTABLE_NAME_COLLISIONS, Warn, "detects name collision with an existing but unstable method", @@ -382,48 +1753,267 @@ declare_lint! { } declare_lint! { + /// The `irrefutable_let_patterns` lint detects detects [irrefutable + /// patterns] in [if-let] and [while-let] statements. + /// + /// + /// + /// ### Example + /// + /// ```rust + /// if let _ = 123 { + /// println!("always runs!"); + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// There usually isn't a reason to have an irrefutable pattern in an + /// if-let or while-let statement, because the pattern will always match + /// successfully. A [`let`] or [`loop`] statement will suffice. However, + /// when generating code with a macro, forbidding irrefutable patterns + /// would require awkward workarounds in situations where the macro + /// doesn't know if the pattern is refutable or not. This lint allows + /// macros to accept this form, while alerting for a possibly incorrect + /// use in normal code. + /// + /// See [RFC 2086] for more details. + /// + /// [irrefutable patterns]: https://doc.rust-lang.org/reference/patterns.html#refutability + /// [if-let]: https://doc.rust-lang.org/reference/expressions/if-expr.html#if-let-expressions + /// [while-let]: https://doc.rust-lang.org/reference/expressions/loop-expr.html#predicate-pattern-loops + /// [`let`]: https://doc.rust-lang.org/reference/statements.html#let-statements + /// [`loop`]: https://doc.rust-lang.org/reference/expressions/loop-expr.html#infinite-loops + /// [RFC 2086]: https://github.com/rust-lang/rfcs/blob/master/text/2086-allow-if-let-irrefutables.md pub IRREFUTABLE_LET_PATTERNS, Warn, "detects irrefutable patterns in if-let and while-let statements" } declare_lint! { + /// The `unused_labels` lint detects [labels] that are never used. + /// + /// [labels]: https://doc.rust-lang.org/reference/expressions/loop-expr.html#loop-labels + /// + /// ### Example + /// + /// ```rust,no_run + /// 'unused_label: loop {} + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Unused labels may signal a mistake or unfinished code. To silence the + /// warning for the individual label, prefix it with an underscore such as + /// `'_my_label:`. pub UNUSED_LABELS, Warn, "detects labels that are never used" } declare_lint! { + /// The `broken_intra_doc_links` lint detects failures in resolving + /// intra-doc link targets. This is a `rustdoc` only lint, and only works + /// on the [**nightly channel**]. + /// + /// [**nightly channel**]: https://doc.rust-lang.org/book/appendix-07-nightly-rust.html + /// + /// ### Example + /// + /// ```rust,rustdoc + /// /// This is a doc comment. + /// /// + /// /// See also [`bar`]. + /// pub fn foo() { + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// `rustdoc` allows [linking to items by name][intra] which will + /// automatically generate links in the documentation to the item. This + /// lint is issued when `rustdoc` is unable to find the named item. Check + /// that the name is correct, that it is in scope, or if you need to + /// qualify it with a path. If you intended to have square brackets appear + /// literally in the text, surround the brackets with backticks such as `` + /// `[example]` `` to indicate a code span, or prefix it with a backslash + /// such as `\[example]`. + /// + /// [intra]: https://doc.rust-lang.org/nightly/rustdoc/unstable-features.html#linking-to-items-by-name pub BROKEN_INTRA_DOC_LINKS, Warn, "failures in resolving intra-doc link targets" } declare_lint! { + /// The `invalid_codeblock_attributes` lint detects code block attributes + /// in documentation examples that have potentially mis-typed values. This + /// is a `rustdoc` only lint. + /// + /// ### Example + /// + /// ```rust,rustdoc + /// /// Example. + /// /// + /// /// ```should-panic + /// /// assert_eq!(1, 2); + /// /// ``` + /// pub fn foo() {} + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// This lint is issued when `rustdoc` detects an example code block + /// attribute that appears similar to a valid one. In the example above, + /// the correct form is `should_panic`. This helps detect typo mistakes + /// for some common attributes. pub INVALID_CODEBLOCK_ATTRIBUTES, Warn, "codeblock attribute looks a lot like a known one" } declare_lint! { + /// The `missing_crate_level_docs` lint detects if documentation is + /// missing at the crate root. This is a `rustdoc` only lint. This is a + /// `rustdoc` only lint. + /// + /// ### Example + /// + /// ```rust,rustdoc + /// #![deny(missing_crate_level_docs)] + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// This lint causes `rustdoc` to check if the crate root is missing + /// documentation. This is currently "allow" by default, but it is + /// intended to make this a warning in the future. This is intended as a + /// means to introduce new users on *how* to document their crate by + /// pointing them to some instructions on how to get started. pub MISSING_CRATE_LEVEL_DOCS, Allow, "detects crates with no crate-level documentation" } declare_lint! { + /// The `missing_doc_code_examples` lint detects publicly-exported items + /// without code samples in their documentation. This is a `rustdoc` only + /// lint, and only works on the [**nightly channel**]. + /// + /// [**nightly channel**]: https://doc.rust-lang.org/book/appendix-07-nightly-rust.html + /// + /// ### Example + /// + /// ```rust,rustdoc + /// #![warn(missing_doc_code_examples)] + /// + /// /// There is no code example! + /// pub fn no_code_example() {} + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// This lint is to ensure a high level of quality for documentation. Code + /// examples can be very useful to see how to use an API. To add an + /// example, include a markdown code block with an example of how to use + /// the item, such as: + /// + /// ```rust + /// /// Adds one to the number given. + /// /// + /// /// # Examples + /// /// + /// /// ``` + /// /// let arg = 5; + /// /// let answer = my_crate::add_one(arg); + /// /// + /// /// assert_eq!(6, answer); + /// /// ``` + /// pub fn add_one(x: i32) -> i32 { + /// x + 1 + /// } + /// ``` pub MISSING_DOC_CODE_EXAMPLES, Allow, "detects publicly-exported items without code samples in their documentation" } declare_lint! { + /// The `private_doc_tests` lint detects code samples in docs of private + /// items not documented by `rustdoc`. This is a `rustdoc` only lint. + /// + /// ### Example + /// + /// ```rust,rustdoc + /// #![deny(private_doc_tests)] + /// + /// mod foo { + /// /// private doc test + /// /// + /// /// ``` + /// /// assert!(false); + /// /// ``` + /// fn bar() {} + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Because documentation examples link against the public API of the + /// crate, it is not possible for an example to access a private item. + /// This means it was likely a mistake to add a code example to a private + /// item. pub PRIVATE_DOC_TESTS, Allow, "detects code samples in docs of private items not documented by rustdoc" } declare_lint! { + /// The `where_clauses_object_safety` lint detects for [object safety] of + /// [where clauses]. + /// + /// [object safety]: https://doc.rust-lang.org/reference/items/traits.html#object-safety + /// [where clauses]: https://doc.rust-lang.org/reference/items/generics.html#where-clauses + /// + /// ### Example + /// + /// ```rust,no_run + /// trait Trait {} + /// + /// trait X { fn foo(&self) where Self: Trait; } + /// + /// impl X for () { fn foo(&self) {} } + /// + /// impl Trait for dyn X {} + /// + /// // Segfault at opt-level 0, SIGILL otherwise. + /// pub fn main() { ::foo(&()); } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// The compiler previously allowed these object-unsafe bounds, which was + /// incorrect. This is a [future-incompatible] lint to transition this to + /// a hard error in the future. See [issue #51443] for more details. + /// + /// [issue #51443]: https://github.com/rust-lang/rust/issues/51443 + /// [future-incompatible]: ../index.md#future-incompatible-lints pub WHERE_CLAUSES_OBJECT_SAFETY, Warn, "checks the object safety of where clauses", @@ -434,6 +2024,63 @@ declare_lint! { } declare_lint! { + /// The `proc_macro_derive_resolution_fallback` lint detects proc macro + /// derives using inaccessible names from parent modules. + /// + /// ### Example + /// + /// ```rust,ignore (proc-macro) + /// // foo.rs + /// #![crate_type = "proc-macro"] + /// + /// extern crate proc_macro; + /// + /// use proc_macro::*; + /// + /// #[proc_macro_derive(Foo)] + /// pub fn foo1(a: TokenStream) -> TokenStream { + /// drop(a); + /// "mod __bar { static mut BAR: Option = None; }".parse().unwrap() + /// } + /// ``` + /// + /// ```rust,ignore (needs-dependency) + /// // bar.rs + /// #[macro_use] + /// extern crate foo; + /// + /// struct Something; + /// + /// #[derive(Foo)] + /// struct Another; + /// + /// fn main() {} + /// ``` + /// + /// This will produce: + /// + /// ```text + /// warning: cannot find type `Something` in this scope + /// --> src/main.rs:8:10 + /// | + /// 8 | #[derive(Foo)] + /// | ^^^ names from parent modules are not accessible without an explicit import + /// | + /// = note: `#[warn(proc_macro_derive_resolution_fallback)]` on by default + /// = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + /// = note: for more information, see issue #50504 + /// ``` + /// + /// ### Explanation + /// + /// If a proc-macro generates a module, the compiler unintentionally + /// allowed items in that module to refer to items in the crate root + /// without importing them. This is a [future-incompatible] lint to + /// transition this to a hard error in the future. See [issue #50504] for + /// more details. + /// + /// [issue #50504]: https://github.com/rust-lang/rust/issues/50504 + /// [future-incompatible]: ../index.md#future-incompatible-lints pub PROC_MACRO_DERIVE_RESOLUTION_FALLBACK, Warn, "detects proc macro derives using inaccessible names from parent modules", @@ -444,6 +2091,53 @@ declare_lint! { } declare_lint! { + /// The `macro_use_extern_crate` lint detects the use of the + /// [`macro_use` attribute]. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![deny(macro_use_extern_crate)] + /// + /// #[macro_use] + /// extern crate serde_json; + /// + /// fn main() { + /// let _ = json!{{}}; + /// } + /// ``` + /// + /// This will produce: + /// + /// ```text + /// error: deprecated `#[macro_use]` attribute used to import macros should be replaced at use sites with a `use` item to import the macro instead + /// --> src/main.rs:3:1 + /// | + /// 3 | #[macro_use] + /// | ^^^^^^^^^^^^ + /// | + /// note: the lint level is defined here + /// --> src/main.rs:1:9 + /// | + /// 1 | #![deny(macro_use_extern_crate)] + /// | ^^^^^^^^^^^^^^^^^^^^^^ + /// ``` + /// + /// ### Explanation + /// + /// The [`macro_use` attribute] on an [`extern crate`] item causes + /// macros in that external crate to be brought into the prelude of the + /// crate, making the macros in scope everywhere. As part of the efforts + /// to simplify handling of dependencies in the [2018 edition], the use of + /// `extern crate` is being phased out. To bring macros from extern crates + /// into scope, it is recommended to use a [`use` import]. + /// + /// This lint is "allow" by default because this is a stylistic choice + /// that has not been settled, see [issue #52043] for more information. + /// + /// [`macro_use` attribute]: https://doc.rust-lang.org/reference/macros-by-example.html#the-macro_use-attribute + /// [`use` import]: https://doc.rust-lang.org/reference/items/use-declarations.html + /// [issue #52043]: https://github.com/rust-lang/rust/issues/52043 pub MACRO_USE_EXTERN_CRATE, Allow, "the `#[macro_use]` attribute is now deprecated in favor of using macros \ @@ -451,6 +2145,44 @@ declare_lint! { } declare_lint! { + /// The `macro_expanded_macro_exports_accessed_by_absolute_paths` lint + /// detects macro-expanded [`macro_export`] macros from the current crate + /// that cannot be referred to by absolute paths. + /// + /// [`macro_export`]: https://doc.rust-lang.org/reference/macros-by-example.html#path-based-scope + /// + /// ### Example + /// + /// ```rust,compile_fail + /// macro_rules! define_exported { + /// () => { + /// #[macro_export] + /// macro_rules! exported { + /// () => {}; + /// } + /// }; + /// } + /// + /// define_exported!(); + /// + /// fn main() { + /// crate::exported!(); + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// The intent is that all macros marked with the `#[macro_export]` + /// attribute are made available in the root of the crate. However, when a + /// `macro_rules!` definition is generated by another macro, the macro + /// expansion is unable to uphold this rule. This is a + /// [future-incompatible] lint to transition this to a hard error in the + /// future. See [issue #53495] for more details. + /// + /// [issue #53495]: https://github.com/rust-lang/rust/issues/53495 + /// [future-incompatible]: ../index.md#future-incompatible-lints pub MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS, Deny, "macro-expanded `macro_export` macros from the current crate \ @@ -463,12 +2195,92 @@ declare_lint! { } declare_lint! { + /// The `explicit_outlives_requirements` lint detects unnecessary + /// lifetime bounds that can be inferred. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// # #![allow(unused)] + /// #![deny(explicit_outlives_requirements)] + /// + /// struct SharedRef<'a, T> + /// where + /// T: 'a, + /// { + /// data: &'a T, + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// If a `struct` contains a reference, such as `&'a T`, the compiler + /// requires that `T` outlives the lifetime `'a`. This historically + /// required writing an explicit lifetime bound to indicate this + /// requirement. However, this can be overly explicit, causing clutter and + /// unnecessary complexity. The language was changed to automatically + /// infer the bound if it is not specified. Specifically, if the struct + /// contains a reference, directly or indirectly, to `T` with lifetime + /// `'x`, then it will infer that `T: 'x` is a requirement. + /// + /// This lint is "allow" by default because it can be noisy for existing + /// code that already had these requirements. This is a stylistic choice, + /// as it is still valid to explicitly state the bound. It also has some + /// false positives that can cause confusion. + /// + /// See [RFC 2093] for more details. + /// + /// [RFC 2093]: https://github.com/rust-lang/rfcs/blob/master/text/2093-infer-outlives.md pub EXPLICIT_OUTLIVES_REQUIREMENTS, Allow, "outlives requirements can be inferred" } declare_lint! { + /// The `indirect_structural_match` lint detects a `const` in a pattern + /// that manually implements [`PartialEq`] and [`Eq`]. + /// + /// [`PartialEq`]: https://doc.rust-lang.org/std/cmp/trait.PartialEq.html + /// [`Eq`]: https://doc.rust-lang.org/std/cmp/trait.Eq.html + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![deny(indirect_structural_match)] + /// + /// struct Plus(i32, i32); + /// const ONE_PLUS_TWO: &&Plus = &&Plus(1, 2); + /// + /// impl PartialEq for Plus { + /// fn eq(&self, other: &Self) -> bool { + /// self.0 + self.1 == other.0 + other.1 + /// } + /// } + /// + /// impl Eq for Plus {} + /// + /// fn main() { + /// if let ONE_PLUS_TWO = &&Plus(3, 0) { + /// println!("semantic!"); + /// } else { + /// println!("structural!"); + /// } + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// The compiler unintentionally accepted this form in the past. This is a + /// [future-incompatible] lint to transition this to a hard error in the + /// future. See [issue #62411] for a complete description of the problem, + /// and some possible solutions. + /// + /// [issue #62411]: https://github.com/rust-lang/rust/issues/62411 + /// [future-incompatible]: ../index.md#future-incompatible-lints pub INDIRECT_STRUCTURAL_MATCH, // defaulting to allow until rust-lang/rust#62614 is fixed. Allow, @@ -480,6 +2292,17 @@ declare_lint! { } declare_lint! { + /// The `deprecated_in_future` lint is internal to rustc and should not be + /// used by user code. + /// + /// This lint is only enabled in the standard library. It works with the + /// use of `#[rustc_deprecated]` with a `since` field of a version in the + /// future. This allows something to be marked as deprecated in a future + /// version, and then this lint will ensure that the item is no longer + /// used in the standard library. See the [stability documentation] for + /// more details. + /// + /// [stability documentation]: https://rustc-dev-guide.rust-lang.org/stability.html#rustc_deprecated pub DEPRECATED_IN_FUTURE, Allow, "detects use of items that will be deprecated in a future version", @@ -487,6 +2310,54 @@ declare_lint! { } declare_lint! { + /// The `ambiguous_associated_items` lint detects ambiguity between + /// [associated items] and [enum variants]. + /// + /// [associated items]: https://doc.rust-lang.org/reference/items/associated-items.html + /// [enum variants]: https://doc.rust-lang.org/reference/items/enumerations.html + /// + /// ### Example + /// + /// ```rust,compile_fail + /// enum E { + /// V + /// } + /// + /// trait Tr { + /// type V; + /// fn foo() -> Self::V; + /// } + /// + /// impl Tr for E { + /// type V = u8; + /// // `Self::V` is ambiguous because it may refer to the associated type or + /// // the enum variant. + /// fn foo() -> Self::V { 0 } + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Previous versions of Rust did not allow accessing enum variants + /// through [type aliases]. When this ability was added (see [RFC 2338]), this + /// introduced some situations where it can be ambiguous what a type + /// was referring to. + /// + /// To fix this ambiguity, you should use a [qualified path] to explicitly + /// state which type to use. For example, in the above example the + /// function can be written as `fn f() -> ::V { 0 }` to + /// specifically refer to the associated type. + /// + /// This is a [future-incompatible] lint to transition this to a hard + /// error in the future. See [issue #57644] for more details. + /// + /// [issue #57644]: https://github.com/rust-lang/rust/issues/57644 + /// [type aliases]: https://doc.rust-lang.org/reference/items/type-aliases.html#type-aliases + /// [RFC 2338]: https://github.com/rust-lang/rfcs/blob/master/text/2338-type-alias-enum-variants.md + /// [qualified path]: https://doc.rust-lang.org/reference/paths.html#qualified-paths + /// [future-incompatible]: ../index.md#future-incompatible-lints pub AMBIGUOUS_ASSOCIATED_ITEMS, Deny, "ambiguous associated items", @@ -497,6 +2368,27 @@ declare_lint! { } declare_lint! { + /// The `mutable_borrow_reservation_conflict` lint detects the reservation + /// of a two-phased borrow that conflicts with other shared borrows. + /// + /// ### Example + /// + /// ```rust + /// let mut v = vec![0, 1, 2]; + /// let shared = &v; + /// v.push(shared.len()); + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// This is a [future-incompatible] lint to transition this to a hard error + /// in the future. See [issue #59159] for a complete description of the + /// problem, and some possible solutions. + /// + /// [issue #59159]: https://github.com/rust-lang/rust/issues/59159 + /// [future-incompatible]: ../index.md#future-incompatible-lints pub MUTABLE_BORROW_RESERVATION_CONFLICT, Warn, "reservation of a two-phased borrow conflicts with other shared borrows", @@ -507,6 +2399,38 @@ declare_lint! { } declare_lint! { + /// The `soft_unstable` lint detects unstable features that were + /// unintentionally allowed on stable. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #[cfg(test)] + /// extern crate test; + /// + /// #[bench] + /// fn name(b: &mut test::Bencher) { + /// b.iter(|| 123) + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// The [`bench` attribute] was accidentally allowed to be specified on + /// the [stable release channel]. Turning this to a hard error would have + /// broken some projects. This lint allows those projects to continue to + /// build correctly when [`--cap-lints`] is used, but otherwise signal an + /// error that `#[bench]` should not be used on the stable channel. This + /// is a [future-incompatible] lint to transition this to a hard error in + /// the future. See [issue #64266] for more details. + /// + /// [issue #64266]: https://github.com/rust-lang/rust/issues/64266 + /// [`bench` attribute]: https://doc.rust-lang.org/nightly/unstable-book/library-features/test.html + /// [stable release channel]: https://doc.rust-lang.org/book/appendix-07-nightly-rust.html + /// [`--cap-lints`]: https://doc.rust-lang.org/rustc/lints/levels.html#capping-lints + /// [future-incompatible]: ../index.md#future-incompatible-lints pub SOFT_UNSTABLE, Deny, "a feature gate that doesn't break dependent crates", @@ -517,18 +2441,123 @@ declare_lint! { } declare_lint! { + /// The `inline_no_sanitize` lint detects incompatible use of + /// [`#[inline(always)]`][inline] and [`#[no_sanitize(...)]`][no_sanitize]. + /// + /// [inline]: https://doc.rust-lang.org/reference/attributes/codegen.html#the-inline-attribute + /// [no_sanitize]: https://doc.rust-lang.org/nightly/unstable-book/language-features/no-sanitize.html + /// + /// ### Example + /// + /// ```rust + /// #![feature(no_sanitize)] + /// + /// #[inline(always)] + /// #[no_sanitize(address)] + /// fn x() {} + /// + /// fn main() { + /// x() + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// The use of the [`#[inline(always)]`][inline] attribute prevents the + /// the [`#[no_sanitize(...)]`][no_sanitize] attribute from working. + /// Consider temporarily removing `inline` attribute. pub INLINE_NO_SANITIZE, Warn, "detects incompatible use of `#[inline(always)]` and `#[no_sanitize(...)]`", } declare_lint! { + /// The `asm_sub_register` lint detects using only a subset of a register + /// for inline asm inputs. + /// + /// ### Example + /// + /// ```rust,ignore (fails on system llvm) + /// #![feature(asm)] + /// + /// fn main() { + /// #[cfg(target_arch="x86_64")] + /// unsafe { + /// asm!("mov {0}, {0}", in(reg) 0i16); + /// } + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Registers on some architectures can use different names to refer to a + /// subset of the register. By default, the compiler will use the name for + /// the full register size. To explicitly use a subset of the register, + /// you can override the default by using a modifier on the template + /// string operand to specify when subregister to use. This lint is issued + /// if you pass in a value with a smaller data type than the default + /// register size, to alert you of possibly using the incorrect width. To + /// fix this, add the suggested modifier to the template, or cast the + /// value to the correct size. + /// + /// See [register template modifiers] for more details. + /// + /// [register template modifiers]: https://doc.rust-lang.org/nightly/unstable-book/library-features/asm.html#register-template-modifiers pub ASM_SUB_REGISTER, Warn, "using only a subset of a register for inline asm inputs", } declare_lint! { + /// The `unsafe_op_in_unsafe_fn` lint detects unsafe operations in unsafe + /// functions without an explicit unsafe block. This lint only works on + /// the [**nightly channel**] with the + /// `#![feature(unsafe_block_in_unsafe_fn)]` feature. + /// + /// [**nightly channel**]: https://doc.rust-lang.org/book/appendix-07-nightly-rust.html + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![feature(unsafe_block_in_unsafe_fn)] + /// #![deny(unsafe_op_in_unsafe_fn)] + /// + /// unsafe fn foo() {} + /// + /// unsafe fn bar() { + /// foo(); + /// } + /// + /// fn main() {} + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Currently, an [`unsafe fn`] allows any [unsafe] operation within its + /// body. However, this can increase the surface area of code that needs + /// to be scrutinized for proper behavior. The [`unsafe` block] provides a + /// convenient way to make it clear exactly which parts of the code are + /// performing unsafe operations. In the future, it is desired to change + /// it so that unsafe operations cannot be performed in an `unsafe fn` + /// without an `unsafe` block. + /// + /// The fix to this is to wrap the unsafe code in an `unsafe` block. + /// + /// This lint is "allow" by default because it has not yet been + /// stabilized, and is not yet complete. See [RFC #2585] and [issue + /// #71668] for more details + /// + /// [`unsafe fn`]: https://doc.rust-lang.org/reference/unsafe-functions.html + /// [`unsafe` block]: https://doc.rust-lang.org/reference/expressions/block-expr.html#unsafe-blocks + /// [unsafe]: https://doc.rust-lang.org/reference/unsafety.html + /// [RFC #2585]: https://github.com/rust-lang/rfcs/blob/master/text/2585-unsafe-block-in-unsafe-fn.md + /// [issue #71668]: https://github.com/rust-lang/rust/issues/71668 pub UNSAFE_OP_IN_UNSAFE_FN, Allow, "unsafe operations in unsafe functions without an explicit unsafe block are deprecated", @@ -536,6 +2565,48 @@ declare_lint! { } declare_lint! { + /// The `cenum_impl_drop_cast` lint detects an `as` cast of a field-less + /// `enum` that implements [`Drop`]. + /// + /// [`Drop`]: https://doc.rust-lang.org/std/ops/trait.Drop.html + /// + /// ### Example + /// + /// ```rust + /// # #![allow(unused)] + /// enum E { + /// A, + /// } + /// + /// impl Drop for E { + /// fn drop(&mut self) { + /// println!("Drop"); + /// } + /// } + /// + /// fn main() { + /// let e = E::A; + /// let i = e as u32; + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Casting a field-less `enum` that does not implement [`Copy`] to an + /// integer moves the value without calling `drop`. This can result in + /// surprising behavior if it was expected that `drop` should be called. + /// Calling `drop` automatically would be inconsistent with other move + /// operations. Since neither behavior is clear or consistent, it was + /// decided that a cast of this nature will no longer be allowed. + /// + /// This is a [future-incompatible] lint to transition this to a hard error + /// in the future. See [issue #73333] for more details. + /// + /// [future-incompatible]: ../index.md#future-incompatible-lints + /// [issue #73333]: https://github.com/rust-lang/rust/issues/73333 + /// [`Copy`]: https://doc.rust-lang.org/std/marker/trait.Copy.html pub CENUM_IMPL_DROP_CAST, Warn, "a C-like enum implementing Drop is cast", @@ -546,6 +2617,36 @@ declare_lint! { } declare_lint! { + /// The `const_evaluatable_unchecked` lint detects a generic constant used + /// in a type. + /// + /// ### Example + /// + /// ```rust + /// const fn foo() -> usize { + /// if std::mem::size_of::<*mut T>() < 8 { // size of *mut T does not depend on T + /// std::mem::size_of::() + /// } else { + /// 8 + /// } + /// } + /// + /// fn test() { + /// let _ = [0; foo::()]; + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// In the 1.43 release, some uses of generic parameters in array repeat + /// expressions were accidentally allowed. This is a [future-incompatible] + /// lint to transition this to a hard error in the future. See [issue + /// #76200] for a more detailed description and possible fixes. + /// + /// [future-incompatible]: ../index.md#future-incompatible-lints + /// [issue #76200]: https://github.com/rust-lang/rust/issues/76200 pub CONST_EVALUATABLE_UNCHECKED, Warn, "detects a generic constant is used in a type without a emitting a warning", @@ -641,6 +2742,23 @@ declare_lint_pass! { } declare_lint! { + /// The `unused_doc_comments` lint detects doc comments that aren't used + /// by `rustdoc`. + /// + /// ### Example + /// + /// ```rust + /// /// docs for x + /// let x = 12; + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// `rustdoc` does not use doc comments in all positions, and so the doc + /// comment will be ignored. Try changing it to a normal comment with `//` + /// to avoid the warning. pub UNUSED_DOC_COMMENTS, Warn, "detects doc comments that aren't used by rustdoc" diff --git a/src/bootstrap/doc.rs b/src/bootstrap/doc.rs index d7f3a888edd..be5182b939d 100644 --- a/src/bootstrap/doc.rs +++ b/src/bootstrap/doc.rs @@ -66,7 +66,6 @@ book!( Nomicon, "src/doc/nomicon", "nomicon"; Reference, "src/doc/reference", "reference"; RustByExample, "src/doc/rust-by-example", "rust-by-example"; - RustcBook, "src/doc/rustc", "rustc"; RustdocBook, "src/doc/rustdoc", "rustdoc"; ); @@ -718,3 +717,69 @@ fn symlink_dir_force(config: &Config, src: &Path, dst: &Path) -> io::Result<()> symlink_dir(config, src, dst) } + +#[derive(Ord, PartialOrd, Debug, Copy, Clone, Hash, PartialEq, Eq)] +pub struct RustcBook { + pub compiler: Compiler, + pub target: TargetSelection, +} + +impl Step for RustcBook { + type Output = (); + const DEFAULT: bool = true; + const ONLY_HOSTS: bool = true; + + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + let builder = run.builder; + run.path("src/doc/rustc").default_condition(builder.config.docs) + } + + fn make_run(run: RunConfig<'_>) { + run.builder.ensure(RustcBook { + compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.build), + target: run.target, + }); + } + + /// Builds the rustc book. + /// + /// The lints are auto-generated by a tool, and then merged into the book + /// in the "md-doc" directory in the build output directory. Then + /// "rustbook" is used to convert it to HTML. + fn run(self, builder: &Builder<'_>) { + let out_base = builder.md_doc_out(self.target).join("rustc"); + t!(fs::create_dir_all(&out_base)); + let out_listing = out_base.join("src/lints"); + builder.cp_r(&builder.src.join("src/doc/rustc"), &out_base); + builder.info(&format!("Generating lint docs ({})", self.target)); + let rustc = builder.rustc(self.compiler); + // The tool runs `rustc` for extracting output examples, so it needs a + // functional sysroot. + builder.ensure(compile::Std { compiler: self.compiler, target: self.target }); + let rustdoc = builder.rustdoc(self.compiler); + let mut cmd = builder.tool_cmd(Tool::LintDocs); + cmd.arg("--src"); + cmd.arg(builder.src.join("compiler")); + cmd.arg("--out"); + cmd.arg(&out_listing); + cmd.arg("--rustc"); + cmd.arg(rustc); + cmd.arg("--rustdoc"); + cmd.arg(rustdoc); + if builder.config.verbose() { + cmd.arg("--verbose"); + } + builder.run(&mut cmd); + // Run rustbook/mdbook to generate the HTML pages. + builder.ensure(RustbookSrc { + target: self.target, + name: INTERNER.intern_str("rustc"), + src: INTERNER.intern_path(out_base), + }); + if is_explicit_request(builder, "src/doc/rustc") { + let out = builder.doc_out(self.target); + let index = out.join("rustc").join("index.html"); + open(builder, &index); + } + } +} diff --git a/src/bootstrap/tool.rs b/src/bootstrap/tool.rs index a607f0fe258..99e33e3b006 100644 --- a/src/bootstrap/tool.rs +++ b/src/bootstrap/tool.rs @@ -367,6 +367,7 @@ bootstrap_tool!( RustInstaller, "src/tools/rust-installer", "fabricate", is_external_tool = true; RustdocTheme, "src/tools/rustdoc-themes", "rustdoc-themes"; ExpandYamlAnchors, "src/tools/expand-yaml-anchors", "expand-yaml-anchors"; + LintDocs, "src/tools/lint-docs", "lint-docs"; ); #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, Ord, PartialOrd)] diff --git a/src/doc/rustc/src/lints/groups.md b/src/doc/rustc/src/lints/groups.md index 049e59b6517..44cf42ff0d7 100644 --- a/src/doc/rustc/src/lints/groups.md +++ b/src/doc/rustc/src/lints/groups.md @@ -14,14 +14,7 @@ $ rustc -D non-camel-case-types -D non-snake-case -D non-upper-case-globals Here's a list of each lint group, and the lints that they are made up of: -| group | description | lints | -|---------------------|---------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| nonstandard-style | Violation of standard naming conventions | non-camel-case-types, non-snake-case, non-upper-case-globals | -| warnings | all lints that would be issuing warnings | all lints that would be issuing warnings | -| edition-2018 | Lints that will be turned into errors in Rust 2018 | tyvar-behind-raw-pointer | -| rust-2018-idioms | Lints to nudge you toward idiomatic features of Rust 2018 | bare-trait-object, unreachable-pub | -| unused | These lints detect things being declared but not used | unused-imports, unused-variables, unused-assignments, dead-code, unused-mut, unreachable-code, unreachable-patterns, unused-must-use, unused-unsafe, path-statements, unused-attributes, unused-macros, unused-allocation, unused-doc-comment, unused-extern-crates, unused-features, unused-parens | -| future-incompatible | Lints that detect code that has future-compatibility problems | private-in-public, pub-use-of-private-extern-crate, patterns-in-fns-without-body, safe-extern-statics, invalid-type-param-default, legacy-directory-ownership, legacy-imports, legacy-constructor-visibility, missing-fragment-specifier, illegal-floating-point-literal-pattern, anonymous-parameters, parenthesized-params-in-types-and-modules, late-bound-lifetime-arguments, safe-packed-borrows, tyvar-behind-raw-pointer, unstable-name-collision | +{{groups-table}} Additionally, there's a `bad-style` lint group that's a deprecated alias for `nonstandard-style`. diff --git a/src/doc/rustc/src/lints/index.md b/src/doc/rustc/src/lints/index.md index 9010d436eb5..029c9edc1b5 100644 --- a/src/doc/rustc/src/lints/index.md +++ b/src/doc/rustc/src/lints/index.md @@ -26,3 +26,35 @@ warning: unused variable: `x` This is the `unused_variables` lint, and it tells you that you've introduced a variable that you don't use in your code. That's not *wrong*, so it's not an error, but it might be a bug, so you get a warning. + +## Future-incompatible lints + +Sometimes the compiler needs to be changed to fix an issue that can cause +existing code to stop compiling. "Future-incompatible" lints are issued in +these cases to give users of Rust a smooth transition to the new behavior. +Initially, the compiler will continue to accept the problematic code and issue +a warning. The warning has a description of the problem, a notice that this +will become an error in the future, and a link to a tracking issue that +provides detailed information and an opportunity for feedback. This gives +users some time to fix the code to accommodate the change. After some time, +the warning may become an error. + +The following is an example of what a future-incompatible looks like: + +```text +warning: borrow of packed field is unsafe and requires unsafe function or block (error E0133) + --> lint_example.rs:11:13 + | +11 | let y = &x.data.0; + | ^^^^^^^^^ + | + = note: `#[warn(safe_packed_borrows)]` on by default + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #46043 + = note: fields of packed structs might be misaligned: dereferencing a misaligned pointer or even just creating a misaligned reference is undefined behavior +``` + +For more information about the process and policy of future-incompatible +changes, see [RFC 1589]. + +[RFC 1589]: https://github.com/rust-lang/rfcs/blob/master/text/1589-rustc-bug-fix-procedure.md diff --git a/src/doc/rustc/src/lints/listing/allowed-by-default.md b/src/doc/rustc/src/lints/listing/allowed-by-default.md index d2d8c471efc..95dd60bebfb 100644 --- a/src/doc/rustc/src/lints/listing/allowed-by-default.md +++ b/src/doc/rustc/src/lints/listing/allowed-by-default.md @@ -1,454 +1,3 @@ # Allowed-by-default lints -These lints are all set to the 'allow' level by default. As such, they won't show up -unless you set them to a higher lint level with a flag or attribute. - -## anonymous-parameters - -This lint detects anonymous parameters. Some example code that triggers this lint: - -```rust -trait Foo { - fn foo(usize); -} -``` - -When set to 'deny', this will produce: - -```text -error: use of deprecated anonymous parameter - --> src/lib.rs:5:11 - | -5 | fn foo(usize); - | ^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #41686 -``` - -This syntax is mostly a historical accident, and can be worked around quite -easily: - -```rust -trait Foo { - fn foo(_: usize); -} -``` - -## bare-trait-object - -This lint suggests using `dyn Trait` for trait objects. Some example code -that triggers this lint: - -```rust -#![feature(dyn_trait)] - -trait Trait { } - -fn takes_trait_object(_: Box) { -} -``` - -When set to 'deny', this will produce: - -```text -error: trait objects without an explicit `dyn` are deprecated - --> src/lib.rs:7:30 - | -7 | fn takes_trait_object(_: Box) { - | ^^^^^ help: use `dyn`: `dyn Trait` - | -``` - -To fix it, do as the help message suggests: - -```rust -#![feature(dyn_trait)] -#![deny(bare_trait_objects)] - -trait Trait { } - -fn takes_trait_object(_: Box) { -} -``` - -## box-pointers - -This lints use of the Box type. Some example code that triggers this lint: - -```rust -struct Foo { - x: Box, -} -``` - -When set to 'deny', this will produce: - -```text -error: type uses owned (Box type) pointers: std::boxed::Box - --> src/lib.rs:6:5 - | -6 | x: Box //~ ERROR type uses owned - | ^^^^^^^^^^^^^ - | -``` - -This lint is mostly historical, and not particularly useful. `Box` used to -be built into the language, and the only way to do heap allocation. Today's -Rust can call into other allocators, etc. - -## elided-lifetime-in-path - -This lint detects the use of hidden lifetime parameters. Some example code -that triggers this lint: - -```rust -struct Foo<'a> { - x: &'a u32 -} - -fn foo(x: &Foo) { -} -``` - -When set to 'deny', this will produce: - -```text -error: hidden lifetime parameters are deprecated, try `Foo<'_>` - --> src/lib.rs:5:12 - | -5 | fn foo(x: &Foo) { - | ^^^ - | -``` - -Lifetime elision elides this lifetime, but that is being deprecated. - -## missing-copy-implementations - -This lint detects potentially-forgotten implementations of `Copy`. Some -example code that triggers this lint: - -```rust -pub struct Foo { - pub field: i32 -} -``` - -When set to 'deny', this will produce: - -```text -error: type could implement `Copy`; consider adding `impl Copy` - --> src/main.rs:3:1 - | -3 | / pub struct Foo { //~ ERROR type could implement `Copy`; consider adding `impl Copy` -4 | | pub field: i32 -5 | | } - | |_^ - | -``` - -You can fix the lint by deriving `Copy`. - -This lint is set to 'allow' because this code isn't bad; it's common to write -newtypes like this specifically so that a `Copy` type is no longer `Copy`. - -## missing-debug-implementations - -This lint detects missing implementations of `fmt::Debug`. Some example code -that triggers this lint: - -```rust -pub struct Foo; -``` - -When set to 'deny', this will produce: - -```text -error: type does not implement `fmt::Debug`; consider adding `#[derive(Debug)]` or a manual implementation - --> src/main.rs:3:1 - | -3 | pub struct Foo; - | ^^^^^^^^^^^^^^^ - | -``` - -You can fix the lint by deriving `Debug`. - -## missing-docs - -This lint detects missing documentation for public items. Some example code -that triggers this lint: - -```rust -pub fn foo() {} -``` - -When set to 'deny', this will produce: - -```text -error: missing documentation for crate - --> src/main.rs:1:1 - | -1 | / #![deny(missing_docs)] -2 | | -3 | | pub fn foo() {} -4 | | -5 | | fn main() {} - | |____________^ - | - -error: missing documentation for a function - --> src/main.rs:3:1 - | -3 | pub fn foo() {} - | ^^^^^^^^^^^^ - -``` - -To fix the lint, add documentation to all items. - -## single-use-lifetimes - -This lint detects lifetimes that are only used once. Some example code that -triggers this lint: - -```rust -struct Foo<'x> { - x: &'x u32 -} -``` - -When set to 'deny', this will produce: - -```text -error: lifetime name `'x` only used once - --> src/main.rs:3:12 - | -3 | struct Foo<'x> { - | ^^ - | -``` - -## trivial-casts - -This lint detects trivial casts which could be replaced with coercion, which may require -type ascription or a temporary variable. Some example code -that triggers this lint: - -```rust -let x: &u32 = &42; -let _ = x as *const u32; -``` - -When set to 'deny', this will produce: - -```text -error: trivial cast: `&u32` as `*const u32`. Cast can be replaced by coercion, this might require type ascription or a temporary variable - --> src/main.rs:5:13 - | -5 | let _ = x as *const u32; - | ^^^^^^^^^^^^^^^ - | -note: lint level defined here - --> src/main.rs:1:9 - | -1 | #![deny(trivial_casts)] - | ^^^^^^^^^^^^^ -``` - -## trivial-numeric-casts - -This lint detects trivial casts of numeric types which could be removed. Some -example code that triggers this lint: - -```rust -let x = 42i32 as i32; -``` - -When set to 'deny', this will produce: - -```text -error: trivial numeric cast: `i32` as `i32`. Cast can be replaced by coercion, this might require type ascription or a temporary variable - --> src/main.rs:4:13 - | -4 | let x = 42i32 as i32; - | ^^^^^^^^^^^^ - | -``` - -## unreachable-pub - -This lint triggers for `pub` items not reachable from the crate root. Some -example code that triggers this lint: - -```rust -mod foo { - pub mod bar { - - } -} -``` - -When set to 'deny', this will produce: - -```text -error: unreachable `pub` item - --> src/main.rs:4:5 - | -4 | pub mod bar { - | ---^^^^^^^^ - | | - | help: consider restricting its visibility: `pub(crate)` - | -``` - -## unsafe-code - -This lint catches usage of `unsafe` code. Some example code that triggers this lint: - -```rust -fn main() { - unsafe { - - } -} -``` - -When set to 'deny', this will produce: - -```text -error: usage of an `unsafe` block - --> src/main.rs:4:5 - | -4 | / unsafe { -5 | | -6 | | } - | |_____^ - | -``` - -## unstable-features - -This lint is deprecated and no longer used. - -## unused-extern-crates - -This lint guards against `extern crate` items that are never used. Some -example code that triggers this lint: - -```rust,ignore -extern crate semver; -``` - -When set to 'deny', this will produce: - -```text -error: unused extern crate - --> src/main.rs:3:1 - | -3 | extern crate semver; - | ^^^^^^^^^^^^^^^^^^^^ - | -``` - -## unused-import-braces - -This lint catches unnecessary braces around an imported item. Some example -code that triggers this lint: - -```rust -use test::{A}; - -pub mod test { - pub struct A; -} -# fn main() {} -``` - -When set to 'deny', this will produce: - -```text -error: braces around A is unnecessary - --> src/main.rs:3:1 - | -3 | use test::{A}; - | ^^^^^^^^^^^^^^ - | -``` - -To fix it, `use test::A;` - -## unused-qualifications - -This lint detects unnecessarily qualified names. Some example code that triggers this lint: - -```rust -mod foo { - pub fn bar() {} -} - -fn main() { - use foo::bar; - foo::bar(); -} -``` - -When set to 'deny', this will produce: - -```text -error: unnecessary qualification - --> src/main.rs:9:5 - | -9 | foo::bar(); - | ^^^^^^^^ - | -``` - -You can call `bar()` directly, without the `foo::`. - -## unused-results - -This lint checks for the unused result of an expression in a statement. Some -example code that triggers this lint: - -```rust,no_run -fn foo() -> T { panic!() } - -fn main() { - foo::(); -} -``` - -When set to 'deny', this will produce: - -```text -error: unused result - --> src/main.rs:6:5 - | -6 | foo::(); - | ^^^^^^^^^^^^^^^ - | -``` - -## variant-size-differences - -This lint detects enums with widely varying variant sizes. Some example code that triggers this lint: - -```rust -enum En { - V0(u8), - VBig([u8; 1024]), -} -``` - -When set to 'deny', this will produce: - -```text -error: enum variant is more than three times larger (1024 bytes) than the next largest - --> src/main.rs:5:5 - | -5 | VBig([u8; 1024]), //~ ERROR variant is more than three times larger - | ^^^^^^^^^^^^^^^^ - | -``` +This file is auto-generated by the lint-docs script. diff --git a/src/doc/rustc/src/lints/listing/deny-by-default.md b/src/doc/rustc/src/lints/listing/deny-by-default.md index 55714f8f454..3c1452d6467 100644 --- a/src/doc/rustc/src/lints/listing/deny-by-default.md +++ b/src/doc/rustc/src/lints/listing/deny-by-default.md @@ -1,203 +1,3 @@ # Deny-by-default lints -These lints are all set to the 'deny' level by default. - -## exceeding-bitshifts - -This lint detects that a shift exceeds the type's number of bits. Some -example code that triggers this lint: - -```rust,ignore -1_i32 << 32; -``` - -This will produce: - -```text -error: bitshift exceeds the type's number of bits - --> src/main.rs:2:5 - | -2 | 1_i32 << 32; - | ^^^^^^^^^^^ - | -``` - -## invalid-type-param-default - -This lint detects type parameter default erroneously allowed in invalid location. Some -example code that triggers this lint: - -```rust,ignore -fn foo(t: T) {} -``` - -This will produce: - -```text -error: defaults for type parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions. - --> src/main.rs:4:8 - | -4 | fn foo(t: T) {} - | ^ - | - = note: `#[deny(invalid_type_param_default)]` on by default - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #36887 -``` - -## mutable-transmutes - -This lint catches transmuting from `&T` to `&mut T` because it is undefined -behavior. Some example code that triggers this lint: - -```rust,ignore -unsafe { - let y = std::mem::transmute::<&i32, &mut i32>(&5); -} -``` - -This will produce: - -```text -error: mutating transmuted &mut T from &T may cause undefined behavior, consider instead using an UnsafeCell - --> src/main.rs:3:17 - | -3 | let y = std::mem::transmute::<&i32, &mut i32>(&5); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -``` - - -## no-mangle-const-items - -This lint detects any `const` items with the `#[no_mangle]` attribute. -Constants do not have their symbols exported, and therefore, this probably -means you meant to use a `static`, not a `const`. Some example code that -triggers this lint: - -```rust,ignore -#[no_mangle] -const FOO: i32 = 5; -``` - -This will produce: - -```text -error: const items should never be `#[no_mangle]` - --> src/main.rs:3:1 - | -3 | const FOO: i32 = 5; - | -----^^^^^^^^^^^^^^ - | | - | help: try a static value: `pub static` - | -``` - -## overflowing-literals - -This lint detects literal out of range for its type. Some -example code that triggers this lint: - -```rust,compile_fail -let x: u8 = 1000; -``` - -This will produce: - -```text -error: literal out of range for u8 - --> src/main.rs:2:17 - | -2 | let x: u8 = 1000; - | ^^^^ - | -``` - -## patterns-in-fns-without-body - -This lint detects patterns in functions without body were that were -previously erroneously allowed. Some example code that triggers this lint: - -```rust,compile_fail -trait Trait { - fn foo(mut arg: u8); -} -``` - -This will produce: - -```text -warning: patterns aren't allowed in methods without bodies - --> src/main.rs:2:12 - | -2 | fn foo(mut arg: u8); - | ^^^^^^^ - | - = note: `#[warn(patterns_in_fns_without_body)]` on by default - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #35203 -``` - -To fix this, remove the pattern; it can be used in the implementation without -being used in the definition. That is: - -```rust -trait Trait { - fn foo(arg: u8); -} - -impl Trait for i32 { - fn foo(mut arg: u8) { - - } -} -``` - -## pub-use-of-private-extern-crate - -This lint detects a specific situation of re-exporting a private `extern crate`; - -## unknown-crate-types - -This lint detects an unknown crate type found in a `#[crate_type]` directive. Some -example code that triggers this lint: - -```rust,ignore -#![crate_type="lol"] -``` - -This will produce: - -```text -error: invalid `crate_type` value - --> src/lib.rs:1:1 - | -1 | #![crate_type="lol"] - | ^^^^^^^^^^^^^^^^^^^^ - | -``` - -## const-err - -This lint detects expressions that will always panic at runtime and would be an -error in a `const` context. - -```rust,ignore -let _ = [0; 4][4]; -``` - -This will produce: - -```text -error: index out of bounds: the len is 4 but the index is 4 - --> src/lib.rs:1:9 - | -1 | let _ = [0; 4][4]; - | ^^^^^^^^^ - | -``` - -## order-dependent-trait-objects - -This lint detects a trait coherency violation that would allow creating two -trait impls for the same dynamic trait object involving marker traits. +This file is auto-generated by the lint-docs script. diff --git a/src/doc/rustc/src/lints/listing/warn-by-default.md b/src/doc/rustc/src/lints/listing/warn-by-default.md index 386f6008d06..eebc022a82b 100644 --- a/src/doc/rustc/src/lints/listing/warn-by-default.md +++ b/src/doc/rustc/src/lints/listing/warn-by-default.md @@ -1,903 +1,3 @@ # Warn-by-default lints -These lints are all set to the 'warn' level by default. - -## const-err - -This lint detects an erroneous expression while doing constant evaluation. Some -example code that triggers this lint: - -```rust,ignore -let b = 200u8 + 200u8; -``` - -This will produce: - -```text -warning: attempt to add with overflow - --> src/main.rs:2:9 - | -2 | let b = 200u8 + 200u8; - | ^^^^^^^^^^^^^ - | -``` - -## dead-code - -This lint detects unused, unexported items. Some -example code that triggers this lint: - -```rust -fn foo() {} -``` - -This will produce: - -```text -warning: function is never used: `foo` - --> src/lib.rs:2:1 - | -2 | fn foo() {} - | ^^^^^^^^ - | -``` - -## deprecated - -This lint detects use of deprecated items. Some -example code that triggers this lint: - -```rust -#[deprecated] -fn foo() {} - -fn bar() { - foo(); -} -``` - -This will produce: - -```text -warning: use of deprecated item 'foo' - --> src/lib.rs:7:5 - | -7 | foo(); - | ^^^ - | -``` - -## illegal-floating-point-literal-pattern - -This lint detects floating-point literals used in patterns. Some example code -that triggers this lint: - -```rust -let x = 42.0; - -match x { - 5.0 => {}, - _ => {}, -} -``` - -This will produce: - -```text -warning: floating-point literals cannot be used in patterns - --> src/main.rs:4:9 - | -4 | 5.0 => {}, - | ^^^ - | - = note: `#[warn(illegal_floating_point_literal_pattern)]` on by default - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #41620 -``` - -## improper-ctypes - -This lint detects proper use of libc types in foreign modules. Some -example code that triggers this lint: - -```rust -extern "C" { - static STATIC: String; -} -``` - -This will produce: - -```text -warning: found struct without foreign-function-safe representation annotation in foreign module, consider adding a `#[repr(C)]` attribute to the type - --> src/main.rs:2:20 - | -2 | static STATIC: String; - | ^^^^^^ - | -``` - -## late-bound-lifetime-arguments - -This lint detects generic lifetime arguments in path segments with -late bound lifetime parameters. Some example code that triggers this lint: - -```rust -struct S; - -impl S { - fn late<'a, 'b>(self, _: &'a u8, _: &'b u8) {} -} - -fn main() { - S.late::<'static>(&0, &0); -} -``` - -This will produce: - -```text -warning: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present - --> src/main.rs:8:14 - | -4 | fn late<'a, 'b>(self, _: &'a u8, _: &'b u8) {} - | -- the late bound lifetime parameter is introduced here -... -8 | S.late::<'static>(&0, &0); - | ^^^^^^^ - | - = note: `#[warn(late_bound_lifetime_arguments)]` on by default - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #42868 -``` - -## non-camel-case-types - -This lint detects types, variants, traits and type parameters that don't have -camel case names. Some example code that triggers this lint: - -```rust -struct s; -``` - -This will produce: - -```text -warning: type `s` should have a camel case name such as `S` - --> src/main.rs:1:1 - | -1 | struct s; - | ^^^^^^^^^ - | -``` - -## non-shorthand-field-patterns - -This lint detects using `Struct { x: x }` instead of `Struct { x }` in a pattern. Some -example code that triggers this lint: - -```rust -struct Point { - x: i32, - y: i32, -} - - -fn main() { - let p = Point { - x: 5, - y: 5, - }; - - match p { - Point { x: x, y: y } => (), - } -} -``` - -This will produce: - -```text -warning: the `x:` in this pattern is redundant - --> src/main.rs:14:17 - | -14 | Point { x: x, y: y } => (), - | --^^ - | | - | help: remove this - | - -warning: the `y:` in this pattern is redundant - --> src/main.rs:14:23 - | -14 | Point { x: x, y: y } => (), - | --^^ - | | - | help: remove this - -``` - -## non-snake-case - -This lint detects variables, methods, functions, lifetime parameters and -modules that don't have snake case names. Some example code that triggers -this lint: - -```rust -let X = 5; -``` - -This will produce: - -```text -warning: variable `X` should have a snake case name such as `x` - --> src/main.rs:2:9 - | -2 | let X = 5; - | ^ - | -``` - -## non-upper-case-globals - -This lint detects static constants that don't have uppercase identifiers. -Some example code that triggers this lint: - -```rust -static x: i32 = 5; -``` - -This will produce: - -```text -warning: static variable `x` should have an upper case name such as `X` - --> src/main.rs:1:1 - | -1 | static x: i32 = 5; - | ^^^^^^^^^^^^^^^^^^ - | -``` - -## no-mangle-generic-items - -This lint detects generic items must be mangled. Some -example code that triggers this lint: - -```rust -#[no_mangle] -fn foo(t: T) { - -} -``` - -This will produce: - -```text -warning: functions generic over types must be mangled - --> src/main.rs:2:1 - | -1 | #[no_mangle] - | ------------ help: remove this attribute -2 | / fn foo(t: T) { -3 | | -4 | | } - | |_^ - | -``` - -## path-statements - -This lint detects path statements with no effect. Some example code that -triggers this lint: - -```rust -let x = 42; - -x; -``` - -This will produce: - -```text -warning: path statement with no effect - --> src/main.rs:3:5 - | -3 | x; - | ^^ - | -``` - -## private-in-public - -This lint detects private items in public interfaces not caught by the old implementation. Some -example code that triggers this lint: - -```rust,ignore -pub trait Trait { - type A; -} - -pub struct S; - -mod foo { - struct Z; - - impl ::Trait for ::S { - type A = Z; - } -} -# fn main() {} -``` - -This will produce: - -```text -error[E0446]: private type `foo::Z` in public interface - --> src/main.rs:11:9 - | -11 | type A = Z; - | ^^^^^^^^^^^ can't leak private type -``` - -## private-no-mangle-fns - -This lint detects functions marked `#[no_mangle]` that are also private. -Given that private functions aren't exposed publicly, and `#[no_mangle]` -controls the public symbol, this combination is erroneous. Some example code -that triggers this lint: - -```rust -#[no_mangle] -fn foo() {} -``` - -This will produce: - -```text -warning: function is marked `#[no_mangle]`, but not exported - --> src/main.rs:2:1 - | -2 | fn foo() {} - | -^^^^^^^^^^ - | | - | help: try making it public: `pub` - | -``` - -To fix this, either make it public or remove the `#[no_mangle]`. - -## private-no-mangle-statics - -This lint detects any statics marked `#[no_mangle]` that are private. -Given that private statics aren't exposed publicly, and `#[no_mangle]` -controls the public symbol, this combination is erroneous. Some example code -that triggers this lint: - -```rust -#[no_mangle] -static X: i32 = 4; -``` - -This will produce: - -```text -warning: static is marked `#[no_mangle]`, but not exported - --> src/main.rs:2:1 - | -2 | static X: i32 = 4; - | -^^^^^^^^^^^^^^^^^ - | | - | help: try making it public: `pub` - | -``` - -To fix this, either make it public or remove the `#[no_mangle]`. - -## renamed-and-removed-lints - -This lint detects lints that have been renamed or removed. Some -example code that triggers this lint: - -```rust -#![deny(raw_pointer_derive)] -``` - -This will produce: - -```text -warning: lint raw_pointer_derive has been removed: using derive with raw pointers is ok - --> src/main.rs:1:9 - | -1 | #![deny(raw_pointer_derive)] - | ^^^^^^^^^^^^^^^^^^ - | -``` - -To fix this, either remove the lint or use the new name. - -## safe-packed-borrows - -This lint detects borrowing a field in the interior of a packed structure -with alignment other than 1. Some example code that triggers this lint: - -```rust -#[repr(packed)] -pub struct Unaligned(pub T); - -pub struct Foo { - start: u8, - data: Unaligned, -} - -fn main() { - let x = Foo { start: 0, data: Unaligned(1) }; - let y = &x.data.0; -} -``` - -This will produce: - -```text -warning: borrow of packed field requires unsafe function or block (error E0133) - --> src/main.rs:11:13 - | -11 | let y = &x.data.0; - | ^^^^^^^^^ - | - = note: `#[warn(safe_packed_borrows)]` on by default - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #46043 -``` - -## stable-features - -This lint detects a `#[feature]` attribute that's since been made stable. Some -example code that triggers this lint: - -```rust -#![feature(test_accepted_feature)] -``` - -This will produce: - -```text -warning: this feature has been stable since 1.0.0. Attribute no longer needed - --> src/main.rs:1:12 - | -1 | #![feature(test_accepted_feature)] - | ^^^^^^^^^^^^^^^^^^^^^ - | -``` - -To fix, simply remove the `#![feature]` attribute, as it's no longer needed. - -## type-alias-bounds - -This lint detects bounds in type aliases. These are not currently enforced. -Some example code that triggers this lint: - -```rust -#[allow(dead_code)] -type SendVec = Vec; -``` - -This will produce: - -```text -warning: bounds on generic parameters are not enforced in type aliases - --> src/lib.rs:2:17 - | -2 | type SendVec = Vec; - | ^^^^ - | - = note: `#[warn(type_alias_bounds)]` on by default - = help: the bound will not be checked when the type alias is used, and should be removed -``` - -## tyvar-behind-raw-pointer - -This lint detects raw pointer to an inference variable. Some -example code that triggers this lint: - -```rust -let data = std::ptr::null(); -let _ = &data as *const *const (); - -if data.is_null() {} -``` - -This will produce: - -```text -warning: type annotations needed - --> src/main.rs:4:13 - | -4 | if data.is_null() {} - | ^^^^^^^ - | - = note: `#[warn(tyvar_behind_raw_pointer)]` on by default - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2018 edition! - = note: for more information, see issue #46906 -``` - -## unconditional-recursion - -This lint detects functions that cannot return without calling themselves. -Some example code that triggers this lint: - -```rust -fn foo() { - foo(); -} -``` - -This will produce: - -```text -warning: function cannot return without recursing - --> src/main.rs:1:1 - | -1 | fn foo() { - | ^^^^^^^^ cannot return without recursing -2 | foo(); - | ----- recursive call site - | -``` - -## unknown-lints - -This lint detects unrecognized lint attribute. Some -example code that triggers this lint: - -```rust,ignore -#[allow(not_a_real_lint)] -``` - -This will produce: - -```text -warning: unknown lint: `not_a_real_lint` - --> src/main.rs:1:10 - | -1 | #![allow(not_a_real_lint)] - | ^^^^^^^^^^^^^^^ - | -``` - -## unreachable-code - -This lint detects unreachable code paths. Some example code that -triggers this lint: - -```rust,no_run -panic!("we never go past here!"); - -let x = 5; -``` - -This will produce: - -```text -warning: unreachable statement - --> src/main.rs:4:5 - | -4 | let x = 5; - | ^^^^^^^^^^ - | -``` - -## unreachable-patterns - -This lint detects unreachable patterns. Some -example code that triggers this lint: - -```rust -let x = 5; -match x { - y => (), - 5 => (), -} -``` - -This will produce: - -```text -warning: unreachable pattern - --> src/main.rs:5:5 - | -5 | 5 => (), - | ^ - | -``` - -The `y` pattern will always match, so the five is impossible to reach. -Remember, match arms match in order, you probably wanted to put the `5` case -above the `y` case. - -## unstable-name-collision - -This lint detects that you've used a name that the standard library plans to -add in the future, which means that your code may fail to compile without -additional type annotations in the future. Either rename, or add those -annotations now. - -## unused-allocation - -This lint detects unnecessary allocations that can be eliminated. - -## unused-assignments - -This lint detects assignments that will never be read. Some -example code that triggers this lint: - -```rust -let mut x = 5; -x = 6; -``` - -This will produce: - -```text -warning: value assigned to `x` is never read - --> src/main.rs:4:5 - | -4 | x = 6; - | ^ - | -``` - -## unused-attributes - -This lint detects attributes that were not used by the compiler. Some -example code that triggers this lint: - -```rust -#![macro_export] -``` - -This will produce: - -```text -warning: unused attribute - --> src/main.rs:1:1 - | -1 | #![macro_export] - | ^^^^^^^^^^^^^^^^ - | -``` - -## unused-comparisons - -This lint detects comparisons made useless by limits of the types involved. Some -example code that triggers this lint: - -```rust -fn foo(x: u8) { - x >= 0; -} -``` - -This will produce: - -```text -warning: comparison is useless due to type limits - --> src/main.rs:6:5 - | -6 | x >= 0; - | ^^^^^^ - | -``` - -## unused-doc-comment - -This lint detects doc comments that aren't used by rustdoc. Some -example code that triggers this lint: - -```rust -/// docs for x -let x = 12; -``` - -This will produce: - -```text -warning: doc comment not used by rustdoc - --> src/main.rs:2:5 - | -2 | /// docs for x - | ^^^^^^^^^^^^^^ - | -``` - -## unused-features - -This lint detects unused or unknown features found in crate-level `#[feature]` directives. -To fix this, simply remove the feature flag. - -## unused-imports - -This lint detects imports that are never used. Some -example code that triggers this lint: - -```rust -use std::collections::HashMap; -``` - -This will produce: - -```text -warning: unused import: `std::collections::HashMap` - --> src/main.rs:1:5 - | -1 | use std::collections::HashMap; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - | -``` - -## unused-macros - -This lint detects macros that were not used. Some example code that -triggers this lint: - -```rust -macro_rules! unused { - () => {}; -} - -fn main() { -} -``` - -This will produce: - -```text -warning: unused macro definition - --> src/main.rs:1:1 - | -1 | / macro_rules! unused { -2 | | () => {}; -3 | | } - | |_^ - | -``` - -## unused-must-use - -This lint detects unused result of a type flagged as `#[must_use]`. Some -example code that triggers this lint: - -```rust -fn returns_result() -> Result<(), ()> { - Ok(()) -} - -fn main() { - returns_result(); -} -``` - -This will produce: - -```text -warning: unused `std::result::Result` that must be used - --> src/main.rs:6:5 - | -6 | returns_result(); - | ^^^^^^^^^^^^^^^^^ - | -``` - -## unused-mut - -This lint detects mut variables which don't need to be mutable. Some -example code that triggers this lint: - -```rust -let mut x = 5; -``` - -This will produce: - -```text -warning: variable does not need to be mutable - --> src/main.rs:2:9 - | -2 | let mut x = 5; - | ----^ - | | - | help: remove this `mut` - | -``` - -## unused-parens - -This lint detects `if`, `match`, `while` and `return` with parentheses; they -do not need them. Some example code that triggers this lint: - -```rust -if(true) {} -``` - -This will produce: - -```text -warning: unnecessary parentheses around `if` condition - --> src/main.rs:2:7 - | -2 | if(true) {} - | ^^^^^^ help: remove these parentheses - | -``` - -## unused-unsafe - -This lint detects unnecessary use of an `unsafe` block. Some -example code that triggers this lint: - -```rust -unsafe {} -``` - -This will produce: - -```text -warning: unnecessary `unsafe` block - --> src/main.rs:2:5 - | -2 | unsafe {} - | ^^^^^^ unnecessary `unsafe` block - | -``` - -## unused-variables - -This lint detects variables which are not used in any way. Some -example code that triggers this lint: - -```rust -let x = 5; -``` - -This will produce: - -```text -warning: unused variable: `x` - --> src/main.rs:2:9 - | -2 | let x = 5; - | ^ help: consider using `_x` instead - | -``` - -## warnings - -This lint is a bit special; by changing its level, you change every other warning -that would produce a warning to whatever value you'd like: - -```rust -#![deny(warnings)] -``` - -As such, you won't ever trigger this lint in your code directly. - -## while-true - -This lint detects `while true { }`. Some example code that triggers this -lint: - -```rust,no_run -while true { - -} -``` - -This will produce: - -```text -warning: denote infinite loops with `loop { ... }` - --> src/main.rs:2:5 - | -2 | while true { - | ^^^^^^^^^^ help: use `loop` - | -``` +This file is auto-generated by the lint-docs script. diff --git a/src/tools/lint-docs/Cargo.toml b/src/tools/lint-docs/Cargo.toml new file mode 100644 index 00000000000..657b115671e --- /dev/null +++ b/src/tools/lint-docs/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "lint-docs" +version = "0.1.0" +authors = ["The Rust Project Developers"] +edition = "2018" +description = "A script to extract the lint documentation for the rustc book." + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +serde_json = "1.0.57" +tempfile = "3.1.0" +walkdir = "2.3.1" diff --git a/src/tools/lint-docs/src/groups.rs b/src/tools/lint-docs/src/groups.rs new file mode 100644 index 00000000000..a212459bb4d --- /dev/null +++ b/src/tools/lint-docs/src/groups.rs @@ -0,0 +1,114 @@ +use crate::Lint; +use std::collections::{BTreeMap, BTreeSet}; +use std::error::Error; +use std::fmt::Write; +use std::fs; +use std::path::Path; +use std::process::Command; + +static GROUP_DESCRIPTIONS: &[(&str, &str)] = &[ + ("unused", "Lints that detect things being declared but not used, or excess syntax"), + ("rustdoc", "Rustdoc-specific lints"), + ("rust-2018-idioms", "Lints to nudge you toward idiomatic features of Rust 2018"), + ("nonstandard-style", "Violation of standard naming conventions"), + ("future-incompatible", "Lints that detect code that has future-compatibility problems"), + ("rust-2018-compatibility", "Lints used to transition code from the 2015 edition to 2018"), +]; + +/// Updates the documentation of lint groups. +pub(crate) fn generate_group_docs( + lints: &[Lint], + rustc_path: &Path, + out_path: &Path, +) -> Result<(), Box> { + let groups = collect_groups(rustc_path)?; + let groups_path = out_path.join("groups.md"); + let contents = fs::read_to_string(&groups_path) + .map_err(|e| format!("could not read {}: {}", groups_path.display(), e))?; + let new_contents = contents.replace("{{groups-table}}", &make_groups_table(lints, &groups)?); + // Delete the output because rustbuild uses hard links in its copies. + let _ = fs::remove_file(&groups_path); + fs::write(&groups_path, new_contents) + .map_err(|e| format!("could not write to {}: {}", groups_path.display(), e))?; + Ok(()) +} + +type LintGroups = BTreeMap>; + +/// Collects the group names from rustc. +fn collect_groups(rustc: &Path) -> Result> { + let mut result = BTreeMap::new(); + let mut cmd = Command::new(rustc); + cmd.arg("-Whelp"); + let output = cmd.output().map_err(|e| format!("failed to run command {:?}\n{}", cmd, e))?; + if !output.status.success() { + return Err(format!( + "failed to collect lint info: {:?}\n--- stderr\n{}--- stdout\n{}\n", + output.status, + std::str::from_utf8(&output.stderr).unwrap(), + std::str::from_utf8(&output.stdout).unwrap(), + ) + .into()); + } + let stdout = std::str::from_utf8(&output.stdout).unwrap(); + let lines = stdout.lines(); + let group_start = lines.skip_while(|line| !line.contains("groups provided")).skip(1); + let table_start = group_start.skip_while(|line| !line.contains("----")).skip(1); + for line in table_start { + if line.is_empty() { + break; + } + let mut parts = line.trim().splitn(2, ' '); + let name = parts.next().expect("name in group"); + if name == "warnings" { + // This is special. + continue; + } + let lints = + parts.next().ok_or_else(|| format!("expected lints following name, got `{}`", line))?; + let lints = lints.split(',').map(|l| l.trim().to_string()).collect(); + assert!(result.insert(name.to_string(), lints).is_none()); + } + if result.is_empty() { + return Err( + format!("expected at least one group in -Whelp output, got:\n{}", stdout).into() + ); + } + Ok(result) +} + +fn make_groups_table(lints: &[Lint], groups: &LintGroups) -> Result> { + let mut result = String::new(); + let mut to_link = Vec::new(); + result.push_str("| Group | Description | Lints |\n"); + result.push_str("|-------|-------------|-------|\n"); + result.push_str("| warnings | All lints that are set to issue warnings | See [warn-by-default] for the default set of warnings |\n"); + for (group_name, group_lints) in groups { + let description = GROUP_DESCRIPTIONS.iter().find(|(n, _)| n == group_name) + .ok_or_else(|| format!("lint group `{}` does not have a description, please update the GROUP_DESCRIPTIONS list", group_name))? + .1; + to_link.extend(group_lints); + let brackets: Vec<_> = group_lints.iter().map(|l| format!("[{}]", l)).collect(); + write!(result, "| {} | {} | {} |\n", group_name, description, brackets.join(", ")).unwrap(); + } + result.push('\n'); + result.push_str("[warn-by-default]: listing/warn-by-default.md\n"); + for lint_name in to_link { + let lint_def = + lints.iter().find(|l| l.name == lint_name.replace("-", "_")).ok_or_else(|| { + format!( + "`rustc -W help` defined lint `{}` but that lint does not appear to exist", + lint_name + ) + })?; + write!( + result, + "[{}]: listing/{}#{}\n", + lint_name, + lint_def.level.doc_filename(), + lint_name + ) + .unwrap(); + } + Ok(result) +} diff --git a/src/tools/lint-docs/src/lib.rs b/src/tools/lint-docs/src/lib.rs new file mode 100644 index 00000000000..b1e14ea6c46 --- /dev/null +++ b/src/tools/lint-docs/src/lib.rs @@ -0,0 +1,463 @@ +use std::error::Error; +use std::fmt::Write; +use std::fs; +use std::path::{Path, PathBuf}; +use std::process::Command; +use walkdir::WalkDir; + +mod groups; + +struct Lint { + name: String, + doc: Vec, + level: Level, + path: PathBuf, + lineno: usize, +} + +#[derive(Clone, Copy, PartialEq)] +enum Level { + Allow, + Warn, + Deny, +} + +impl Level { + fn doc_filename(&self) -> &str { + match self { + Level::Allow => "allowed-by-default.md", + Level::Warn => "warn-by-default.md", + Level::Deny => "deny-by-default.md", + } + } +} + +/// Collects all lints, and writes the markdown documentation at the given directory. +pub fn extract_lint_docs( + src_path: &Path, + out_path: &Path, + rustc_path: &Path, + rustdoc_path: &Path, + verbose: bool, +) -> Result<(), Box> { + let mut lints = gather_lints(src_path)?; + for lint in &mut lints { + generate_output_example(lint, rustc_path, rustdoc_path, verbose).map_err(|e| { + format!( + "failed to test example in lint docs for `{}` in {}:{}: {}", + lint.name, + lint.path.display(), + lint.lineno, + e + ) + })?; + } + save_lints_markdown(&lints, &out_path.join("listing"))?; + groups::generate_group_docs(&lints, rustc_path, out_path)?; + Ok(()) +} + +/// Collects all lints from all files in the given directory. +fn gather_lints(src_path: &Path) -> Result, Box> { + let mut lints = Vec::new(); + for entry in WalkDir::new(src_path).into_iter().filter_map(|e| e.ok()) { + if !entry.path().extension().map_or(false, |ext| ext == "rs") { + continue; + } + lints.extend(lints_from_file(entry.path())?); + } + if lints.is_empty() { + return Err("no lints were found!".into()); + } + Ok(lints) +} + +/// Collects all lints from the given file. +fn lints_from_file(path: &Path) -> Result, Box> { + let mut lints = Vec::new(); + let contents = fs::read_to_string(path) + .map_err(|e| format!("could not read {}: {}", path.display(), e))?; + let mut lines = contents.lines().enumerate(); + loop { + // Find a lint declaration. + let lint_start = loop { + match lines.next() { + Some((lineno, line)) => { + if line.trim().starts_with("declare_lint!") { + break lineno + 1; + } + } + None => return Ok(lints), + } + }; + // Read the lint. + let mut doc_lines = Vec::new(); + let (doc, name) = loop { + match lines.next() { + Some((lineno, line)) => { + let line = line.trim(); + if line.starts_with("/// ") { + doc_lines.push(line.trim()[4..].to_string()); + } else if line.starts_with("///") { + doc_lines.push("".to_string()); + } else if line.starts_with("// ") { + // Ignore comments. + continue; + } else { + let name = lint_name(line).map_err(|e| { + format!( + "could not determine lint name in {}:{}: {}, line was `{}`", + path.display(), + lineno, + e, + line + ) + })?; + if doc_lines.is_empty() { + return Err(format!( + "did not find doc lines for lint `{}` in {}", + name, + path.display() + ) + .into()); + } + break (doc_lines, name); + } + } + None => { + return Err(format!( + "unexpected EOF for lint definition at {}:{}", + path.display(), + lint_start + ) + .into()); + } + } + }; + // These lints are specifically undocumented. This should be reserved + // for internal rustc-lints only. + if name == "deprecated_in_future" { + continue; + } + // Read the level. + let level = loop { + match lines.next() { + // Ignore comments. + Some((_, line)) if line.trim().starts_with("// ") => {} + Some((lineno, line)) => match line.trim() { + "Allow," => break Level::Allow, + "Warn," => break Level::Warn, + "Deny," => break Level::Deny, + _ => { + return Err(format!( + "unexpected lint level `{}` in {}:{}", + line, + path.display(), + lineno + ) + .into()); + } + }, + None => { + return Err(format!( + "expected lint level in {}:{}, got EOF", + path.display(), + lint_start + ) + .into()); + } + } + }; + // The rest of the lint definition is ignored. + assert!(!doc.is_empty()); + lints.push(Lint { name, doc, level, path: PathBuf::from(path), lineno: lint_start }); + } +} + +/// Extracts the lint name (removing the visibility modifier, and checking validity). +fn lint_name(line: &str) -> Result { + // Skip over any potential `pub` visibility. + match line.trim().split(' ').next_back() { + Some(name) => { + if !name.ends_with(',') { + return Err("lint name should end with comma"); + } + let name = &name[..name.len() - 1]; + if !name.chars().all(|ch| ch.is_uppercase() || ch == '_') || name.is_empty() { + return Err("lint name did not have expected format"); + } + Ok(name.to_lowercase().to_string()) + } + None => Err("could not find lint name"), + } +} + +/// Mutates the lint definition to replace the `{{produces}}` marker with the +/// actual output from the compiler. +fn generate_output_example( + lint: &mut Lint, + rustc_path: &Path, + rustdoc_path: &Path, + verbose: bool, +) -> Result<(), Box> { + // Explicit list of lints that are allowed to not have an example. Please + // try to avoid adding to this list. + if matches!( + lint.name.as_str(), + "unused_features" + | "unstable_features" + | "incomplete_include" + | "unused_crate_dependencies" + | "exported_private_dependencies" + | "proc_macro_derive_resolution_fallback" + | "macro_use_extern_crate" + ) { + return Ok(()); + } + check_style(lint)?; + replace_produces(lint, rustc_path, rustdoc_path, verbose)?; + Ok(()) +} + +/// Checks the doc style of the lint. +fn check_style(lint: &Lint) -> Result<(), Box> { + for expected in &["### Example", "### Explanation", "{{produces}}"] { + if !lint.doc.iter().any(|line| line.contains(expected)) { + return Err(format!("lint docs should contain the line `{}`", expected).into()); + } + } + if let Some(first) = lint.doc.first() { + if !first.starts_with(&format!("The `{}` lint", lint.name)) { + return Err(format!( + "lint docs should start with the text \"The `{}` lint\" to introduce the lint", + lint.name + ) + .into()); + } + } + Ok(()) +} + +/// Mutates the lint docs to replace the `{{produces}}` marker with the actual +/// output from the compiler. +fn replace_produces( + lint: &mut Lint, + rustc_path: &Path, + rustdoc_path: &Path, + verbose: bool, +) -> Result<(), Box> { + let mut lines = lint.doc.iter_mut(); + loop { + // Find start of example. + let options = loop { + match lines.next() { + Some(line) if line.starts_with("```rust") => { + break line[7..].split(',').collect::>(); + } + Some(line) if line.contains("{{produces}}") => { + return Err("lint marker {{{{produces}}}} found, \ + but expected to immediately follow a rust code block" + .into()); + } + Some(_) => {} + None => return Ok(()), + } + }; + // Find the end of example. + let mut example = Vec::new(); + loop { + match lines.next() { + Some(line) if line == "```" => break, + Some(line) => example.push(line), + None => { + return Err(format!( + "did not find end of example triple ticks ```, docs were:\n{:?}", + lint.doc + ) + .into()); + } + } + } + // Find the {{produces}} line. + loop { + match lines.next() { + Some(line) if line.is_empty() => {} + Some(line) if line == "{{produces}}" => { + let output = generate_lint_output( + &lint.name, + &example, + &options, + rustc_path, + rustdoc_path, + verbose, + )?; + line.replace_range( + .., + &format!( + "This will produce:\n\ + \n\ + ```text\n\ + {}\ + ```", + output + ), + ); + break; + } + // No {{produces}} after example, find next example. + Some(_line) => break, + None => return Ok(()), + } + } + } +} + +/// Runs the compiler against the example, and extracts the output. +fn generate_lint_output( + name: &str, + example: &[&mut String], + options: &[&str], + rustc_path: &Path, + rustdoc_path: &Path, + verbose: bool, +) -> Result> { + if verbose { + eprintln!("compiling lint {}", name); + } + let tempdir = tempfile::TempDir::new()?; + let tempfile = tempdir.path().join("lint_example.rs"); + let mut source = String::new(); + let is_rustdoc = options.contains(&"rustdoc"); + let needs_main = !example.iter().any(|line| line.contains("fn main")) && !is_rustdoc; + // Remove `# ` prefix for hidden lines. + let unhidden = + example.iter().map(|line| if line.starts_with("# ") { &line[2..] } else { line }); + let mut lines = unhidden.peekable(); + while let Some(line) = lines.peek() { + if line.starts_with("#!") { + source.push_str(line); + source.push('\n'); + lines.next(); + } else { + break; + } + } + if needs_main { + source.push_str("fn main() {\n"); + } + for line in lines { + source.push_str(line); + source.push('\n') + } + if needs_main { + source.push_str("}\n"); + } + fs::write(&tempfile, source) + .map_err(|e| format!("failed to write {}: {}", tempfile.display(), e))?; + let program = if is_rustdoc { rustdoc_path } else { rustc_path }; + let mut cmd = Command::new(program); + if options.contains(&"edition2015") { + cmd.arg("--edition=2015"); + } else { + cmd.arg("--edition=2018"); + } + cmd.arg("--error-format=json"); + if options.contains(&"test") { + cmd.arg("--test"); + } + cmd.arg("lint_example.rs"); + cmd.current_dir(tempdir.path()); + let output = cmd.output().map_err(|e| format!("failed to run command {:?}\n{}", cmd, e))?; + let stderr = std::str::from_utf8(&output.stderr).unwrap(); + let msgs = stderr + .lines() + .filter(|line| line.starts_with('{')) + .map(serde_json::from_str) + .collect::, _>>()?; + match msgs + .iter() + .find(|msg| matches!(&msg["code"]["code"], serde_json::Value::String(s) if s==name)) + { + Some(msg) => { + let rendered = msg["rendered"].as_str().expect("rendered field should exist"); + Ok(rendered.to_string()) + } + None => { + match msgs.iter().find( + |msg| matches!(&msg["rendered"], serde_json::Value::String(s) if s.contains(name)), + ) { + Some(msg) => { + let rendered = msg["rendered"].as_str().expect("rendered field should exist"); + Ok(rendered.to_string()) + } + None => { + let rendered: Vec<&str> = + msgs.iter().filter_map(|msg| msg["rendered"].as_str()).collect(); + Err(format!( + "did not find lint `{}` in output of example, got:\n{}", + name, + rendered.join("\n") + ) + .into()) + } + } + } + } +} + +static ALLOWED_MD: &str = r#"# Allowed-by-default lints + +These lints are all set to the 'allow' level by default. As such, they won't show up +unless you set them to a higher lint level with a flag or attribute. + +"#; + +static WARN_MD: &str = r#"# Warn-by-default lints + +These lints are all set to the 'warn' level by default. + +"#; + +static DENY_MD: &str = r#"# Deny-by-default lints + +These lints are all set to the 'deny' level by default. + +"#; + +/// Saves the mdbook lint chapters at the given path. +fn save_lints_markdown(lints: &[Lint], out_dir: &Path) -> Result<(), Box> { + save_level(lints, Level::Allow, out_dir, ALLOWED_MD)?; + save_level(lints, Level::Warn, out_dir, WARN_MD)?; + save_level(lints, Level::Deny, out_dir, DENY_MD)?; + Ok(()) +} + +fn save_level( + lints: &[Lint], + level: Level, + out_dir: &Path, + header: &str, +) -> Result<(), Box> { + let mut result = String::new(); + result.push_str(header); + let mut these_lints: Vec<_> = lints.iter().filter(|lint| lint.level == level).collect(); + these_lints.sort_unstable_by_key(|lint| &lint.name); + for lint in &these_lints { + write!(result, "* [`{}`](#{})\n", lint.name, lint.name.replace("_", "-")).unwrap(); + } + result.push('\n'); + for lint in &these_lints { + write!(result, "## {}\n\n", lint.name.replace("_", "-")).unwrap(); + for line in &lint.doc { + result.push_str(line); + result.push('\n'); + } + result.push('\n'); + } + let out_path = out_dir.join(level.doc_filename()); + // Delete the output because rustbuild uses hard links in its copies. + let _ = fs::remove_file(&out_path); + fs::write(&out_path, result) + .map_err(|e| format!("could not write to {}: {}", out_path.display(), e))?; + Ok(()) +} diff --git a/src/tools/lint-docs/src/main.rs b/src/tools/lint-docs/src/main.rs new file mode 100644 index 00000000000..4b824596b46 --- /dev/null +++ b/src/tools/lint-docs/src/main.rs @@ -0,0 +1,67 @@ +use std::error::Error; +use std::path::PathBuf; + +fn main() { + if let Err(e) = doit() { + println!("error: {}", e); + std::process::exit(1); + } +} + +fn doit() -> Result<(), Box> { + let mut args = std::env::args().skip(1); + let mut src_path = None; + let mut out_path = None; + let mut rustc_path = None; + let mut rustdoc_path = None; + let mut verbose = false; + while let Some(arg) = args.next() { + match arg.as_str() { + "--src" => { + src_path = match args.next() { + Some(s) => Some(PathBuf::from(s)), + None => return Err("--src requires a value".into()), + }; + } + "--out" => { + out_path = match args.next() { + Some(s) => Some(PathBuf::from(s)), + None => return Err("--out requires a value".into()), + }; + } + "--rustc" => { + rustc_path = match args.next() { + Some(s) => Some(PathBuf::from(s)), + None => return Err("--rustc requires a value".into()), + }; + } + "--rustdoc" => { + rustdoc_path = match args.next() { + Some(s) => Some(PathBuf::from(s)), + None => return Err("--rustdoc requires a value".into()), + }; + } + "-v" | "--verbose" => verbose = true, + s => return Err(format!("unexpected argument `{}`", s).into()), + } + } + if src_path.is_none() { + return Err("--src must be specified to the directory with the compiler source".into()); + } + if out_path.is_none() { + return Err("--out must be specified to the directory with the lint listing docs".into()); + } + if rustc_path.is_none() { + return Err("--rustc must be specified to the path of rustc".into()); + } + if rustdoc_path.is_none() { + return Err("--rustdoc must be specified to the path of rustdoc".into()); + } + lint_docs::extract_lint_docs( + &src_path.unwrap(), + &out_path.unwrap(), + &rustc_path.unwrap(), + &rustdoc_path.unwrap(), + verbose, + ) +} -- cgit 1.4.1-3-g733a5 From ce014be0b9d0325ad9e6ecb8c93b3c8186b18089 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Fri, 11 Sep 2020 08:55:59 -0700 Subject: Link rustdoc lint docs to the rustdoc book. --- compiler/rustc_session/src/lint/builtin.rs | 138 +++-------------------------- src/bootstrap/doc.rs | 3 - src/doc/rustdoc/src/lints.md | 58 ++++++++++++ src/tools/lint-docs/src/lib.rs | 36 ++++---- src/tools/lint-docs/src/main.rs | 11 --- 5 files changed, 88 insertions(+), 158 deletions(-) (limited to 'src/bootstrap') diff --git a/compiler/rustc_session/src/lint/builtin.rs b/compiler/rustc_session/src/lint/builtin.rs index 4c0c7fae29b..22030a842db 100644 --- a/compiler/rustc_session/src/lint/builtin.rs +++ b/compiler/rustc_session/src/lint/builtin.rs @@ -1817,35 +1817,10 @@ declare_lint! { declare_lint! { /// The `broken_intra_doc_links` lint detects failures in resolving - /// intra-doc link targets. This is a `rustdoc` only lint, and only works - /// on the [**nightly channel**]. + /// intra-doc link targets. This is a `rustdoc` only lint, see the + /// documentation in the [rustdoc book]. /// - /// [**nightly channel**]: https://doc.rust-lang.org/book/appendix-07-nightly-rust.html - /// - /// ### Example - /// - /// ```rust,rustdoc - /// /// This is a doc comment. - /// /// - /// /// See also [`bar`]. - /// pub fn foo() { - /// } - /// ``` - /// - /// {{produces}} - /// - /// ### Explanation - /// - /// `rustdoc` allows [linking to items by name][intra] which will - /// automatically generate links in the documentation to the item. This - /// lint is issued when `rustdoc` is unable to find the named item. Check - /// that the name is correct, that it is in scope, or if you need to - /// qualify it with a path. If you intended to have square brackets appear - /// literally in the text, surround the brackets with backticks such as `` - /// `[example]` `` to indicate a code span, or prefix it with a backslash - /// such as `\[example]`. - /// - /// [intra]: https://doc.rust-lang.org/nightly/rustdoc/unstable-features.html#linking-to-items-by-name + /// [rustdoc book]: ../../../rustdoc/lints.html#broken_intra_doc_links pub BROKEN_INTRA_DOC_LINKS, Warn, "failures in resolving intra-doc link targets" @@ -1854,27 +1829,9 @@ declare_lint! { declare_lint! { /// The `invalid_codeblock_attributes` lint detects code block attributes /// in documentation examples that have potentially mis-typed values. This - /// is a `rustdoc` only lint. - /// - /// ### Example + /// is a `rustdoc` only lint, see the documentation in the [rustdoc book]. /// - /// ```rust,rustdoc - /// /// Example. - /// /// - /// /// ```should-panic - /// /// assert_eq!(1, 2); - /// /// ``` - /// pub fn foo() {} - /// ``` - /// - /// {{produces}} - /// - /// ### Explanation - /// - /// This lint is issued when `rustdoc` detects an example code block - /// attribute that appears similar to a valid one. In the example above, - /// the correct form is `should_panic`. This helps detect typo mistakes - /// for some common attributes. + /// [rustdoc book]: ../../../rustdoc/lints.html#invalid_codeblock_attributes pub INVALID_CODEBLOCK_ATTRIBUTES, Warn, "codeblock attribute looks a lot like a known one" @@ -1882,24 +1839,10 @@ declare_lint! { declare_lint! { /// The `missing_crate_level_docs` lint detects if documentation is - /// missing at the crate root. This is a `rustdoc` only lint. This is a - /// `rustdoc` only lint. - /// - /// ### Example - /// - /// ```rust,rustdoc - /// #![deny(missing_crate_level_docs)] - /// ``` - /// - /// {{produces}} - /// - /// ### Explanation + /// missing at the crate root. This is a `rustdoc` only lint, see the + /// documentation in the [rustdoc book]. /// - /// This lint causes `rustdoc` to check if the crate root is missing - /// documentation. This is currently "allow" by default, but it is - /// intended to make this a warning in the future. This is intended as a - /// means to introduce new users on *how* to document their crate by - /// pointing them to some instructions on how to get started. + /// [rustdoc book]: ../../../rustdoc/lints.html#missing_crate_level_docs pub MISSING_CRATE_LEVEL_DOCS, Allow, "detects crates with no crate-level documentation" @@ -1908,43 +1851,9 @@ declare_lint! { declare_lint! { /// The `missing_doc_code_examples` lint detects publicly-exported items /// without code samples in their documentation. This is a `rustdoc` only - /// lint, and only works on the [**nightly channel**]. + /// lint, see the documentation in the [rustdoc book]. /// - /// [**nightly channel**]: https://doc.rust-lang.org/book/appendix-07-nightly-rust.html - /// - /// ### Example - /// - /// ```rust,rustdoc - /// #![warn(missing_doc_code_examples)] - /// - /// /// There is no code example! - /// pub fn no_code_example() {} - /// ``` - /// - /// {{produces}} - /// - /// ### Explanation - /// - /// This lint is to ensure a high level of quality for documentation. Code - /// examples can be very useful to see how to use an API. To add an - /// example, include a markdown code block with an example of how to use - /// the item, such as: - /// - /// ```rust - /// /// Adds one to the number given. - /// /// - /// /// # Examples - /// /// - /// /// ``` - /// /// let arg = 5; - /// /// let answer = my_crate::add_one(arg); - /// /// - /// /// assert_eq!(6, answer); - /// /// ``` - /// pub fn add_one(x: i32) -> i32 { - /// x + 1 - /// } - /// ``` + /// [rustdoc book]: ../../../rustdoc/lints.html#missing_doc_code_examples pub MISSING_DOC_CODE_EXAMPLES, Allow, "detects publicly-exported items without code samples in their documentation" @@ -1952,31 +1861,10 @@ declare_lint! { declare_lint! { /// The `private_doc_tests` lint detects code samples in docs of private - /// items not documented by `rustdoc`. This is a `rustdoc` only lint. - /// - /// ### Example - /// - /// ```rust,rustdoc - /// #![deny(private_doc_tests)] + /// items not documented by `rustdoc`. This is a `rustdoc` only lint, see + /// the documentation in the [rustdoc book]. /// - /// mod foo { - /// /// private doc test - /// /// - /// /// ``` - /// /// assert!(false); - /// /// ``` - /// fn bar() {} - /// } - /// ``` - /// - /// {{produces}} - /// - /// ### Explanation - /// - /// Because documentation examples link against the public API of the - /// crate, it is not possible for an example to access a private item. - /// This means it was likely a mistake to add a code example to a private - /// item. + /// [rustdoc book]: ../../../rustdoc/lints.html#private_doc_tests pub PRIVATE_DOC_TESTS, Allow, "detects code samples in docs of private items not documented by rustdoc" diff --git a/src/bootstrap/doc.rs b/src/bootstrap/doc.rs index be5182b939d..98a0119e4df 100644 --- a/src/bootstrap/doc.rs +++ b/src/bootstrap/doc.rs @@ -756,7 +756,6 @@ impl Step for RustcBook { // The tool runs `rustc` for extracting output examples, so it needs a // functional sysroot. builder.ensure(compile::Std { compiler: self.compiler, target: self.target }); - let rustdoc = builder.rustdoc(self.compiler); let mut cmd = builder.tool_cmd(Tool::LintDocs); cmd.arg("--src"); cmd.arg(builder.src.join("compiler")); @@ -764,8 +763,6 @@ impl Step for RustcBook { cmd.arg(&out_listing); cmd.arg("--rustc"); cmd.arg(rustc); - cmd.arg("--rustdoc"); - cmd.arg(rustdoc); if builder.config.verbose() { cmd.arg("--verbose"); } diff --git a/src/doc/rustdoc/src/lints.md b/src/doc/rustdoc/src/lints.md index 8e2869fef55..ce292c60460 100644 --- a/src/doc/rustdoc/src/lints.md +++ b/src/doc/rustdoc/src/lints.md @@ -49,6 +49,30 @@ warning: missing documentation for a function | ^^^^^^^^^^^^^^^^^^^^^ ``` +## missing_crate_level_docs + +This lint is **allowed by default**. It detects if there is no documentation +at the crate root. For example: + +```rust +#![warn(missing_crate_level_docs)] +``` + +This will generate the following warning: + +```text +warning: no documentation found for this crate's top-level module + | + = help: The following guide may be of use: + https://doc.rust-lang.org/nightly/rustdoc/how-to-write-documentation.html +``` + +This is currently "allow" by default, but it is intended to make this a +warning in the future. This is intended as a means to introduce new users on +*how* to document their crate by pointing them to some instructions on how to +get started, without providing overwhelming warnings like `missing_docs` +might. + ## missing_doc_code_examples This lint is **allowed by default** and is **nightly-only**. It detects when a documentation block @@ -117,3 +141,37 @@ warning: Documentation test in private item 8 | | /// ``` | |___________^ ``` + +## invalid_codeblock_attributes + +This lint **warns by default**. It detects code block attributes in +documentation examples that have potentially mis-typed values. For example: + +```rust +/// Example. +/// +/// ```should-panic +/// assert_eq!(1, 2); +/// ``` +pub fn foo() {} +``` + +Which will give: + +```text +warning: unknown attribute `should-panic`. Did you mean `should_panic`? + --> src/lib.rs:1:1 + | +1 | / /// Example. +2 | | /// +3 | | /// ```should-panic +4 | | /// assert_eq!(1, 2); +5 | | /// ``` + | |_______^ + | + = note: `#[warn(invalid_codeblock_attributes)]` on by default + = help: the code block will either not be tested if not marked as a rust one or won't fail if it doesn't panic when running +``` + +In the example above, the correct form is `should_panic`. This helps detect +typo mistakes for some common attributes. diff --git a/src/tools/lint-docs/src/lib.rs b/src/tools/lint-docs/src/lib.rs index b1e14ea6c46..a8e3278fc66 100644 --- a/src/tools/lint-docs/src/lib.rs +++ b/src/tools/lint-docs/src/lib.rs @@ -15,6 +15,12 @@ struct Lint { lineno: usize, } +impl Lint { + fn doc_contains(&self, text: &str) -> bool { + self.doc.iter().any(|line| line.contains(text)) + } +} + #[derive(Clone, Copy, PartialEq)] enum Level { Allow, @@ -37,12 +43,11 @@ pub fn extract_lint_docs( src_path: &Path, out_path: &Path, rustc_path: &Path, - rustdoc_path: &Path, verbose: bool, ) -> Result<(), Box> { let mut lints = gather_lints(src_path)?; for lint in &mut lints { - generate_output_example(lint, rustc_path, rustdoc_path, verbose).map_err(|e| { + generate_output_example(lint, rustc_path, verbose).map_err(|e| { format!( "failed to test example in lint docs for `{}` in {}:{}: {}", lint.name, @@ -197,7 +202,6 @@ fn lint_name(line: &str) -> Result { fn generate_output_example( lint: &mut Lint, rustc_path: &Path, - rustdoc_path: &Path, verbose: bool, ) -> Result<(), Box> { // Explicit list of lints that are allowed to not have an example. Please @@ -214,15 +218,19 @@ fn generate_output_example( ) { return Ok(()); } + if lint.doc_contains("[rustdoc book]") && !lint.doc_contains("{{produces}}") { + // Rustdoc lints are documented in the rustdoc book, don't check these. + return Ok(()); + } check_style(lint)?; - replace_produces(lint, rustc_path, rustdoc_path, verbose)?; + replace_produces(lint, rustc_path, verbose)?; Ok(()) } /// Checks the doc style of the lint. fn check_style(lint: &Lint) -> Result<(), Box> { for expected in &["### Example", "### Explanation", "{{produces}}"] { - if !lint.doc.iter().any(|line| line.contains(expected)) { + if !lint.doc_contains(expected) { return Err(format!("lint docs should contain the line `{}`", expected).into()); } } @@ -243,7 +251,6 @@ fn check_style(lint: &Lint) -> Result<(), Box> { fn replace_produces( lint: &mut Lint, rustc_path: &Path, - rustdoc_path: &Path, verbose: bool, ) -> Result<(), Box> { let mut lines = lint.doc.iter_mut(); @@ -283,14 +290,8 @@ fn replace_produces( match lines.next() { Some(line) if line.is_empty() => {} Some(line) if line == "{{produces}}" => { - let output = generate_lint_output( - &lint.name, - &example, - &options, - rustc_path, - rustdoc_path, - verbose, - )?; + let output = + generate_lint_output(&lint.name, &example, &options, rustc_path, verbose)?; line.replace_range( .., &format!( @@ -318,7 +319,6 @@ fn generate_lint_output( example: &[&mut String], options: &[&str], rustc_path: &Path, - rustdoc_path: &Path, verbose: bool, ) -> Result> { if verbose { @@ -327,8 +327,7 @@ fn generate_lint_output( let tempdir = tempfile::TempDir::new()?; let tempfile = tempdir.path().join("lint_example.rs"); let mut source = String::new(); - let is_rustdoc = options.contains(&"rustdoc"); - let needs_main = !example.iter().any(|line| line.contains("fn main")) && !is_rustdoc; + let needs_main = !example.iter().any(|line| line.contains("fn main")); // Remove `# ` prefix for hidden lines. let unhidden = example.iter().map(|line| if line.starts_with("# ") { &line[2..] } else { line }); @@ -354,8 +353,7 @@ fn generate_lint_output( } fs::write(&tempfile, source) .map_err(|e| format!("failed to write {}: {}", tempfile.display(), e))?; - let program = if is_rustdoc { rustdoc_path } else { rustc_path }; - let mut cmd = Command::new(program); + let mut cmd = Command::new(rustc_path); if options.contains(&"edition2015") { cmd.arg("--edition=2015"); } else { diff --git a/src/tools/lint-docs/src/main.rs b/src/tools/lint-docs/src/main.rs index 4b824596b46..45d97bd4317 100644 --- a/src/tools/lint-docs/src/main.rs +++ b/src/tools/lint-docs/src/main.rs @@ -13,7 +13,6 @@ fn doit() -> Result<(), Box> { let mut src_path = None; let mut out_path = None; let mut rustc_path = None; - let mut rustdoc_path = None; let mut verbose = false; while let Some(arg) = args.next() { match arg.as_str() { @@ -35,12 +34,6 @@ fn doit() -> Result<(), Box> { None => return Err("--rustc requires a value".into()), }; } - "--rustdoc" => { - rustdoc_path = match args.next() { - Some(s) => Some(PathBuf::from(s)), - None => return Err("--rustdoc requires a value".into()), - }; - } "-v" | "--verbose" => verbose = true, s => return Err(format!("unexpected argument `{}`", s).into()), } @@ -54,14 +47,10 @@ fn doit() -> Result<(), Box> { if rustc_path.is_none() { return Err("--rustc must be specified to the path of rustc".into()); } - if rustdoc_path.is_none() { - return Err("--rustdoc must be specified to the path of rustdoc".into()); - } lint_docs::extract_lint_docs( &src_path.unwrap(), &out_path.unwrap(), &rustc_path.unwrap(), - &rustdoc_path.unwrap(), verbose, ) } -- cgit 1.4.1-3-g733a5 From 9dad90814543bdd10b1989020edad70939b0e869 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Mon, 14 Sep 2020 14:42:56 -0700 Subject: Fix generating rustc docs with non-default lib directory. --- src/bootstrap/builder.rs | 8 ++++++-- src/bootstrap/doc.rs | 4 ++++ src/bootstrap/test.rs | 8 ++++---- 3 files changed, 14 insertions(+), 6 deletions(-) (limited to 'src/bootstrap') diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index 0f18660c0e1..53560c9deb6 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -710,7 +710,7 @@ impl<'a> Builder<'a> { /// Adds the compiler's directory of dynamic libraries to `cmd`'s dynamic /// library lookup path. - pub fn add_rustc_lib_path(&self, compiler: Compiler, cmd: &mut Cargo) { + pub fn add_rustc_lib_path(&self, compiler: Compiler, cmd: &mut Command) { // Windows doesn't need dylib path munging because the dlls for the // compiler live next to the compiler and the system will find them // automatically. @@ -718,7 +718,7 @@ impl<'a> Builder<'a> { return; } - add_dylib_path(vec![self.rustc_libdir(compiler)], &mut cmd.command); + add_dylib_path(vec![self.rustc_libdir(compiler)], cmd); } /// Gets a path to the compiler specified. @@ -1515,6 +1515,10 @@ impl Cargo { self.command.env(key.as_ref(), value.as_ref()); self } + + pub fn add_rustc_lib_path(&mut self, builder: &Builder<'_>, compiler: Compiler) { + builder.add_rustc_lib_path(compiler, &mut self.command); + } } impl From for Command { diff --git a/src/bootstrap/doc.rs b/src/bootstrap/doc.rs index 98a0119e4df..f90e76a4f4e 100644 --- a/src/bootstrap/doc.rs +++ b/src/bootstrap/doc.rs @@ -766,6 +766,10 @@ impl Step for RustcBook { if builder.config.verbose() { cmd.arg("--verbose"); } + // If the lib directories are in an unusual location (changed in + // config.toml), then this needs to explicitly update the dylib search + // path. + builder.add_rustc_lib_path(self.compiler, &mut cmd); builder.run(&mut cmd); // Run rustbook/mdbook to generate the HTML pages. builder.ensure(RustbookSrc { diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs index 045dda2d4cb..ba5f75c49ac 100644 --- a/src/bootstrap/test.rs +++ b/src/bootstrap/test.rs @@ -270,7 +270,7 @@ impl Step for Rls { &[], ); - builder.add_rustc_lib_path(compiler, &mut cargo); + cargo.add_rustc_lib_path(builder, compiler); cargo.arg("--").args(builder.config.cmd.test_args()); if try_run(builder, &mut cargo.into()) { @@ -328,7 +328,7 @@ impl Step for Rustfmt { t!(fs::create_dir_all(&dir)); cargo.env("RUSTFMT_TEST_DIR", dir); - builder.add_rustc_lib_path(compiler, &mut cargo); + cargo.add_rustc_lib_path(builder, compiler); if try_run(builder, &mut cargo.into()) { builder.save_toolstate("rustfmt", ToolState::TestPass); @@ -449,7 +449,7 @@ impl Step for Miri { cargo.arg("--").args(builder.config.cmd.test_args()); - builder.add_rustc_lib_path(compiler, &mut cargo); + cargo.add_rustc_lib_path(builder, compiler); if !try_run(builder, &mut cargo.into()) { return; @@ -554,7 +554,7 @@ impl Step for Clippy { cargo.arg("--").args(builder.config.cmd.test_args()); - builder.add_rustc_lib_path(compiler, &mut cargo); + cargo.add_rustc_lib_path(builder, compiler); builder.run(&mut cargo.into()); } -- cgit 1.4.1-3-g733a5 From 5f3145febd4e0e8ff28bfeec0418ece7f832428a Mon Sep 17 00:00:00 2001 From: Mark Rousskov Date: Tue, 15 Sep 2020 08:59:34 -0400 Subject: Avoid printing dry run timings --- src/bootstrap/builder.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/bootstrap') diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index 0f18660c0e1..dc027661c83 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -1421,7 +1421,7 @@ impl<'a> Builder<'a> { (out, dur - deps) }; - if self.config.print_step_timings { + if self.config.print_step_timings && !self.config.dry_run { println!("[TIMING] {:?} -- {}.{:03}", step, dur.as_secs(), dur.subsec_millis()); } -- cgit 1.4.1-3-g733a5 From f001a0c8dd074ae46276ce84c3de6a6072e77015 Mon Sep 17 00:00:00 2001 From: Mark Rousskov Date: Mon, 14 Sep 2020 16:22:56 -0400 Subject: Enable shared linking to LLVM on non-Windows Windows doesn't quite support dynamic linking to LLVM yet, but on other platforms we do. In #76708, it was discovered that we dynamically link to LLVM from the LLVM tools (e.g., rust-lld), so we need the shared LLVM library to link against. That means that if we do not have a shared link to LLVM, and want LLVM tools to work, we'd be shipping two copies of LLVM on all of these platforms: one in librustc_driver and one in libLLVM. Also introduce an error into rustbuild if we do end up configured for shared linking on Windows. --- src/bootstrap/native.rs | 4 ++++ src/ci/run.sh | 7 +++++++ 2 files changed, 11 insertions(+) (limited to 'src/bootstrap') diff --git a/src/bootstrap/native.rs b/src/bootstrap/native.rs index 6cd850bc0bf..3829d47da33 100644 --- a/src/bootstrap/native.rs +++ b/src/bootstrap/native.rs @@ -129,6 +129,10 @@ impl Step for Llvm { Err(m) => m, }; + if builder.config.llvm_link_shared && target.contains("windows") { + panic!("shared linking to LLVM is not currently supported on Windows"); + } + builder.info(&format!("Building LLVM for {}", target)); t!(stamp.remove()); let _time = util::timeit(&builder); diff --git a/src/ci/run.sh b/src/ci/run.sh index 5231aa2e766..c8faf1ec831 100755 --- a/src/ci/run.sh +++ b/src/ci/run.sh @@ -75,6 +75,13 @@ if [ "$DEPLOY$DEPLOY_ALT" = "1" ]; then RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set rust.remap-debuginfo" RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --debuginfo-level-std=1" + # If we're distributing binaries, we want a shared LLVM link. We're already + # going to link LLVM to the LLVM tools dynamically, so we need to ship a + # libLLVM library anyway. + if !isWindows; then + RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set llvm.link-shared=true" + fi + if [ "$NO_LLVM_ASSERTIONS" = "1" ]; then RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --disable-llvm-assertions" elif [ "$DEPLOY_ALT" != "" ]; then -- cgit 1.4.1-3-g733a5 From 328c61c15711ae8a741b2aa56af1c450f54d1796 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Fri, 11 Sep 2020 22:17:16 -0400 Subject: Make the default stage for x.py configurable This allows configuring the default stage for each sub-command individually. - Normalize the stage as early as possible, so there's no confusion about which to use. - Don't add an explicit `stage` option in config.toml This offers no more flexibility than `*_stage` and makes it confusing which takes precedence. - Always give `--stage N` precedence over config.toml - Fix bootstrap tests This changes the tests to go through `Config::parse` so that they test the actual defaults, not the dummy ones provided by `default_opts`. To make this workable (and independent of the environment), it does not read `config.toml` for tests. --- config.toml.example | 17 ++++++++++++ src/bootstrap/builder.rs | 31 ++-------------------- src/bootstrap/builder/tests.rs | 14 +++++----- src/bootstrap/config.rs | 59 +++++++++++++++++++++++++++++++++++++----- 4 files changed, 79 insertions(+), 42 deletions(-) (limited to 'src/bootstrap') diff --git a/config.toml.example b/config.toml.example index 2d5b3136450..f10d1a7b44e 100644 --- a/config.toml.example +++ b/config.toml.example @@ -111,6 +111,23 @@ # General build configuration options # ============================================================================= [build] +# The default stage to use for the `doc` subcommand +#doc-stage = 0 + +# The default stage to use for the `build` subcommand +#build-stage = 1 + +# The default stage to use for the `test` subcommand +#test-stage = 1 + +# The default stage to use for the `dist` subcommand +#dist-stage = 2 + +# The default stage to use for the `install` subcommand +#install-stage = 2 + +# The default stage to use for the `bench` subcommand +#bench-stage = 2 # Build triple for the original snapshot compiler. This must be a compiler that # nightlies are already produced for. The current platform must be able to run diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index 31d4f1f28a8..034b01a502c 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -526,23 +526,9 @@ impl<'a> Builder<'a> { } fn new_internal(build: &Build, kind: Kind, paths: Vec) -> Builder<'_> { - let top_stage = if let Some(explicit_stage) = build.config.stage { - explicit_stage - } else { - // See https://github.com/rust-lang/compiler-team/issues/326 - match kind { - Kind::Doc => 0, - Kind::Build | Kind::Test => 1, - Kind::Bench | Kind::Dist | Kind::Install => 2, - // These are all bootstrap tools, which don't depend on the compiler. - // The stage we pass shouldn't matter, but use 0 just in case. - Kind::Check | Kind::Clippy | Kind::Fix | Kind::Run | Kind::Format => 0, - } - }; - Builder { build, - top_stage, + top_stage: build.config.stage, kind, cache: Cache::new(), stack: RefCell::new(Vec::new()), @@ -566,20 +552,7 @@ impl<'a> Builder<'a> { Subcommand::Format { .. } | Subcommand::Clean { .. } => panic!(), }; - let this = Self::new_internal(build, kind, paths.to_owned()); - - // CI should always run stage 2 builds, unless it specifically states otherwise - #[cfg(not(test))] - if build.config.stage.is_none() && build.ci_env != crate::CiEnv::None { - match kind { - Kind::Test | Kind::Doc | Kind::Build | Kind::Bench | Kind::Dist | Kind::Install => { - assert_eq!(this.top_stage, 2) - } - Kind::Check | Kind::Clippy | Kind::Fix | Kind::Run | Kind::Format => {} - } - } - - this + Self::new_internal(build, kind, paths.to_owned()) } pub fn execute_cli(&self) { diff --git a/src/bootstrap/builder/tests.rs b/src/bootstrap/builder/tests.rs index c6eac95c345..f96925f9270 100644 --- a/src/bootstrap/builder/tests.rs +++ b/src/bootstrap/builder/tests.rs @@ -2,8 +2,8 @@ use super::*; use crate::config::{Config, TargetSelection}; use std::thread; -fn configure(host: &[&str], target: &[&str]) -> Config { - let mut config = Config::default_opts(); +fn configure(cmd: &str, host: &[&str], target: &[&str]) -> Config { + let mut config = Config::parse(&[cmd.to_owned()]); // don't save toolstates config.save_toolstates = None; config.skip_only_host_steps = false; @@ -42,7 +42,7 @@ mod defaults { #[test] fn build_default() { - let build = Build::new(configure(&[], &[])); + let build = Build::new(configure("build", &[], &[])); let mut builder = Builder::new(&build); builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Build), &[]); @@ -70,7 +70,7 @@ mod defaults { #[test] fn build_stage_0() { - let config = Config { stage: Some(0), ..configure(&[], &[]) }; + let config = Config { stage: 0, ..configure("build", &[], &[]) }; let build = Build::new(config); let mut builder = Builder::new(&build); builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Build), &[]); @@ -92,7 +92,7 @@ mod defaults { #[test] fn doc_default() { - let mut config = configure(&[], &[]); + let mut config = configure("doc", &[], &[]); config.compiler_docs = true; config.cmd = Subcommand::Doc { paths: Vec::new(), open: false }; let build = Build::new(config); @@ -126,7 +126,7 @@ mod dist { use pretty_assertions::assert_eq; fn configure(host: &[&str], target: &[&str]) -> Config { - Config { stage: Some(2), ..super::configure(host, target) } + Config { stage: 2, ..super::configure("dist", host, target) } } #[test] @@ -455,7 +455,7 @@ mod dist { #[test] fn test_with_no_doc_stage0() { let mut config = configure(&[], &[]); - config.stage = Some(0); + config.stage = 0; config.cmd = Subcommand::Test { paths: vec!["library/std".into()], test_args: vec![], diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index 5a79d3db5c9..cc0a534314a 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -10,7 +10,6 @@ use std::ffi::OsString; use std::fmt; use std::fs; use std::path::{Path, PathBuf}; -use std::process; use crate::cache::{Interned, INTERNER}; use crate::flags::Flags; @@ -57,7 +56,7 @@ pub struct Config { pub skip_only_host_steps: bool, pub on_fail: Option, - pub stage: Option, + pub stage: u32, pub keep_stage: Vec, pub src: PathBuf, pub jobs: Option, @@ -300,6 +299,12 @@ struct Build { configure_args: Option>, local_rebuild: Option, print_step_timings: Option, + doc_stage: Option, + build_stage: Option, + test_stage: Option, + install_stage: Option, + dist_stage: Option, + bench_stage: Option, } /// TOML representation of various global install decisions. @@ -480,13 +485,11 @@ impl Config { pub fn parse(args: &[String]) -> Config { let flags = Flags::parse(&args); - let file = flags.config.clone(); let mut config = Config::default_opts(); config.exclude = flags.exclude; config.rustc_error_format = flags.rustc_error_format; config.json_output = flags.json_output; config.on_fail = flags.on_fail; - config.stage = flags.stage; config.jobs = flags.jobs.map(threads_from_config); config.cmd = flags.cmd; config.incremental = flags.incremental; @@ -503,8 +506,14 @@ impl Config { config.out = dir; } - let toml = file + #[cfg(test)] + let toml = TomlConfig::default(); + #[cfg(not(test))] + let toml = flags + .config .map(|file| { + use std::process; + let contents = t!(fs::read_to_string(&file)); match toml::from_str(&contents) { Ok(table) => table, @@ -520,7 +529,7 @@ impl Config { }) .unwrap_or_else(TomlConfig::default); - let build = toml.build.clone().unwrap_or_default(); + let build = toml.build.unwrap_or_default(); // If --target was specified but --host wasn't specified, don't run any host-only tests. let has_hosts = build.host.is_some() || flags.host.is_some(); @@ -564,6 +573,44 @@ impl Config { set(&mut config.configure_args, build.configure_args); set(&mut config.local_rebuild, build.local_rebuild); set(&mut config.print_step_timings, build.print_step_timings); + + // See https://github.com/rust-lang/compiler-team/issues/326 + config.stage = match config.cmd { + Subcommand::Doc { .. } => flags.stage.or(build.doc_stage).unwrap_or(0), + Subcommand::Build { .. } => flags.stage.or(build.build_stage).unwrap_or(1), + Subcommand::Test { .. } => flags.stage.or(build.test_stage).unwrap_or(1), + Subcommand::Bench { .. } => flags.stage.or(build.bench_stage).unwrap_or(2), + Subcommand::Dist { .. } => flags.stage.or(build.dist_stage).unwrap_or(2), + Subcommand::Install { .. } => flags.stage.or(build.install_stage).unwrap_or(2), + // These are all bootstrap tools, which don't depend on the compiler. + // The stage we pass shouldn't matter, but use 0 just in case. + Subcommand::Clean { .. } + | Subcommand::Check { .. } + | Subcommand::Clippy { .. } + | Subcommand::Fix { .. } + | Subcommand::Run { .. } + | Subcommand::Format { .. } => flags.stage.unwrap_or(0), + }; + + // CI should always run stage 2 builds, unless it specifically states otherwise + #[cfg(not(test))] + if flags.stage.is_none() && crate::CiEnv::current() != crate::CiEnv::None { + match config.cmd { + Subcommand::Test { .. } + | Subcommand::Doc { .. } + | Subcommand::Build { .. } + | Subcommand::Bench { .. } + | Subcommand::Dist { .. } + | Subcommand::Install { .. } => assert_eq!(config.stage, 2), + Subcommand::Clean { .. } + | Subcommand::Check { .. } + | Subcommand::Clippy { .. } + | Subcommand::Fix { .. } + | Subcommand::Run { .. } + | Subcommand::Format { .. } => {} + } + } + config.verbose = cmp::max(config.verbose, flags.verbose); if let Some(ref install) = toml.install { -- cgit 1.4.1-3-g733a5 From d04ca008e1c8ade73ec22976ff6a2a6a13ceabec Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Tue, 15 Sep 2020 00:08:37 -0400 Subject: Remove unnecessary `clone()`s in bootstrap The performance difference is negligible, but it makes me feel better. Note that this does not remove some clones in `config`, because it would require changing the logic around (and performance doesn't matter for bootstrap). --- src/bootstrap/builder.rs | 2 +- src/bootstrap/config.rs | 65 ++++++++++++++++++++++++------------------------ src/bootstrap/format.rs | 8 +++--- src/bootstrap/sanity.rs | 4 +-- 4 files changed, 39 insertions(+), 40 deletions(-) (limited to 'src/bootstrap') diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index 34756af21e4..319f81c4ebb 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -1209,7 +1209,7 @@ impl<'a> Builder<'a> { cargo.env(format!("CC_{}", target.triple), &cc); let cflags = self.cflags(target, GitRepo::Rustc).join(" "); - cargo.env(format!("CFLAGS_{}", target.triple), cflags.clone()); + cargo.env(format!("CFLAGS_{}", target.triple), &cflags); if let Some(ar) = self.ar(target) { let ranlib = format!("{} s", ar.display()); diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index 30c9e012cd6..7e2cb772186 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -500,6 +500,7 @@ impl Config { pub fn parse(args: &[String]) -> Config { let flags = Flags::parse(&args); + let mut config = Config::default_opts(); config.exclude = flags.exclude; config.rustc_error_format = flags.rustc_error_format; @@ -551,14 +552,14 @@ impl Config { let has_targets = build.target.is_some() || flags.target.is_some(); config.skip_only_host_steps = !has_hosts && has_targets; - config.hosts = if let Some(arg_host) = flags.host.clone() { + config.hosts = if let Some(arg_host) = flags.host { arg_host } else if let Some(file_host) = build.host { file_host.iter().map(|h| TargetSelection::from_user(h)).collect() } else { vec![config.build] }; - config.targets = if let Some(arg_target) = flags.target.clone() { + config.targets = if let Some(arg_target) = flags.target { arg_target } else if let Some(file_target) = build.target { file_target.iter().map(|h| TargetSelection::from_user(h)).collect() @@ -628,14 +629,14 @@ impl Config { config.verbose = cmp::max(config.verbose, flags.verbose); - if let Some(ref install) = toml.install { - config.prefix = install.prefix.clone().map(PathBuf::from); - config.sysconfdir = install.sysconfdir.clone().map(PathBuf::from); - config.datadir = install.datadir.clone().map(PathBuf::from); - config.docdir = install.docdir.clone().map(PathBuf::from); - set(&mut config.bindir, install.bindir.clone().map(PathBuf::from)); - config.libdir = install.libdir.clone().map(PathBuf::from); - config.mandir = install.mandir.clone().map(PathBuf::from); + if let Some(install) = toml.install { + config.prefix = install.prefix.map(PathBuf::from); + config.sysconfdir = install.sysconfdir.map(PathBuf::from); + config.datadir = install.datadir.map(PathBuf::from); + config.docdir = install.docdir.map(PathBuf::from); + set(&mut config.bindir, install.bindir.map(PathBuf::from)); + config.libdir = install.libdir.map(PathBuf::from); + config.mandir = install.mandir.map(PathBuf::from); } // We want the llvm-skip-rebuild flag to take precedence over the @@ -658,7 +659,7 @@ impl Config { let mut optimize = None; let mut ignore_git = None; - if let Some(ref llvm) = toml.llvm { + if let Some(llvm) = toml.llvm { match llvm.ccache { Some(StringOrBool::String(ref s)) => config.ccache = Some(s.to_string()), Some(StringOrBool::Bool(true)) => { @@ -726,7 +727,7 @@ impl Config { } } - if let Some(ref rust) = toml.rust { + if let Some(rust) = toml.rust { debug = rust.debug; debug_assertions = rust.debug_assertions; debug_assertions_std = rust.debug_assertions_std; @@ -746,7 +747,7 @@ impl Config { set(&mut config.test_compare_mode, rust.test_compare_mode); set(&mut config.llvm_libunwind, rust.llvm_libunwind); set(&mut config.backtrace, rust.backtrace); - set(&mut config.channel, rust.channel.clone()); + set(&mut config.channel, rust.channel); set(&mut config.rust_dist_src, rust.dist_src); set(&mut config.verbose_tests, rust.verbose_tests); // in the case "false" is set explicitly, do not overwrite the command line args @@ -757,9 +758,9 @@ impl Config { set(&mut config.lld_enabled, rust.lld); set(&mut config.llvm_tools_enabled, rust.llvm_tools); config.rustc_parallel = rust.parallel_compiler.unwrap_or(false); - config.rustc_default_linker = rust.default_linker.clone(); - config.musl_root = rust.musl_root.clone().map(PathBuf::from); - config.save_toolstates = rust.save_toolstates.clone().map(PathBuf::from); + config.rustc_default_linker = rust.default_linker; + config.musl_root = rust.musl_root.map(PathBuf::from); + config.save_toolstates = rust.save_toolstates.map(PathBuf::from); set(&mut config.deny_warnings, flags.deny_warnings.or(rust.deny_warnings)); set(&mut config.backtrace_on_ice, rust.backtrace_on_ice); set(&mut config.rust_verify_llvm_ir, rust.verify_llvm_ir); @@ -776,9 +777,9 @@ impl Config { config.rust_codegen_units_std = rust.codegen_units_std.map(threads_from_config); } - if let Some(ref t) = toml.target { + if let Some(t) = toml.target { for (triple, cfg) in t { - let mut target = Target::from_triple(triple); + let mut target = Target::from_triple(&triple); if let Some(ref s) = cfg.llvm_config { target.llvm_config = Some(config.src.join(s)); @@ -792,18 +793,18 @@ impl Config { if let Some(s) = cfg.no_std { target.no_std = s; } - target.cc = cfg.cc.clone().map(PathBuf::from); - target.cxx = cfg.cxx.clone().map(PathBuf::from); - target.ar = cfg.ar.clone().map(PathBuf::from); - target.ranlib = cfg.ranlib.clone().map(PathBuf::from); - target.linker = cfg.linker.clone().map(PathBuf::from); + target.cc = cfg.cc.map(PathBuf::from); + target.cxx = cfg.cxx.map(PathBuf::from); + target.ar = cfg.ar.map(PathBuf::from); + target.ranlib = cfg.ranlib.map(PathBuf::from); + target.linker = cfg.linker.map(PathBuf::from); target.crt_static = cfg.crt_static; - target.musl_root = cfg.musl_root.clone().map(PathBuf::from); - target.musl_libdir = cfg.musl_libdir.clone().map(PathBuf::from); - target.wasi_root = cfg.wasi_root.clone().map(PathBuf::from); - target.qemu_rootfs = cfg.qemu_rootfs.clone().map(PathBuf::from); + target.musl_root = cfg.musl_root.map(PathBuf::from); + target.musl_libdir = cfg.musl_libdir.map(PathBuf::from); + target.wasi_root = cfg.wasi_root.map(PathBuf::from); + target.qemu_rootfs = cfg.qemu_rootfs.map(PathBuf::from); - config.target_config.insert(TargetSelection::from_user(triple), target); + config.target_config.insert(TargetSelection::from_user(&triple), target); } } @@ -821,10 +822,10 @@ impl Config { build_target.llvm_filecheck = Some(ci_llvm_bin.join(exe("FileCheck", config.build))); } - if let Some(ref t) = toml.dist { - config.dist_sign_folder = t.sign_folder.clone().map(PathBuf::from); - config.dist_gpg_password_file = t.gpg_password_file.clone().map(PathBuf::from); - config.dist_upload_addr = t.upload_addr.clone(); + if let Some(t) = toml.dist { + config.dist_sign_folder = t.sign_folder.map(PathBuf::from); + config.dist_gpg_password_file = t.gpg_password_file.map(PathBuf::from); + config.dist_upload_addr = t.upload_addr; set(&mut config.rust_dist_src, t.src_tarball); set(&mut config.missing_tools, t.missing_tools); } diff --git a/src/bootstrap/format.rs b/src/bootstrap/format.rs index 6f93082e675..0ae9f9712d5 100644 --- a/src/bootstrap/format.rs +++ b/src/bootstrap/format.rs @@ -105,15 +105,13 @@ pub fn format(build: &Build, check: bool) { eprintln!("./x.py fmt is not supported on this channel"); std::process::exit(1); }); - let src = build.src.clone(); - let walker = WalkBuilder::new(&build.src).types(matcher).overrides(ignore_fmt).build_parallel(); + let src = &build.src; + let walker = WalkBuilder::new(src).types(matcher).overrides(ignore_fmt).build_parallel(); walker.run(|| { - let src = src.clone(); - let rustfmt_path = rustfmt_path.clone(); Box::new(move |entry| { let entry = t!(entry); if entry.file_type().map_or(false, |t| t.is_file()) { - rustfmt(&src, &rustfmt_path, &entry.path(), check); + rustfmt(src, &rustfmt_path, &entry.path(), check); } ignore::WalkState::Continue }) diff --git a/src/bootstrap/sanity.rs b/src/bootstrap/sanity.rs index 4d6612a376a..6826d177a4a 100644 --- a/src/bootstrap/sanity.rs +++ b/src/bootstrap/sanity.rs @@ -30,8 +30,8 @@ impl Finder { Self { cache: HashMap::new(), path: env::var_os("PATH").unwrap_or_default() } } - pub fn maybe_have>(&mut self, cmd: S) -> Option { - let cmd: OsString = cmd.as_ref().into(); + pub fn maybe_have>(&mut self, cmd: S) -> Option { + let cmd: OsString = cmd.into(); let path = &self.path; self.cache .entry(cmd.clone()) -- cgit 1.4.1-3-g733a5 From be372657089da3b7e88c2baaa44b0b833f3a31d3 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Wed, 16 Sep 2020 12:00:56 -0400 Subject: Give a better error message when x.py uses the wrong stage for CI --- src/bootstrap/config.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'src/bootstrap') diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index 30c9e012cd6..913104ff9e3 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -616,7 +616,13 @@ impl Config { | Subcommand::Build { .. } | Subcommand::Bench { .. } | Subcommand::Dist { .. } - | Subcommand::Install { .. } => assert_eq!(config.stage, 2), + | Subcommand::Install { .. } => { + assert_eq!( + config.stage, 2, + "x.py should be run with `--stage 2` on CI, but was run with `--stage {}`", + config.stage, + ); + } Subcommand::Clean { .. } | Subcommand::Check { .. } | Subcommand::Clippy { .. } -- cgit 1.4.1-3-g733a5 From c35ce3ff170333d11ccf89e75dc87c49f44a570a Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Wed, 16 Sep 2020 13:31:20 -0400 Subject: Don't generate bootstrap usage unless it's needed Previously, `x.py` would unconditionally run `x.py build` to get the help message. After https://github.com/rust-lang/rust/issues/76165, when checking the CI stage was moved into `Config`, that would cause an assertion failure (but only only in CI!): ``` thread 'main' panicked at 'assertion failed: `(left == right)` left: `1`, right: `2`', src/bootstrap/config.rs:619:49 ``` This changes bootstrap to only generate a help message when it needs to (when someone passes `--help`). --- src/bootstrap/flags.rs | 62 ++++++++++++++++++++++++++------------------------ 1 file changed, 32 insertions(+), 30 deletions(-) (limited to 'src/bootstrap') diff --git a/src/bootstrap/flags.rs b/src/bootstrap/flags.rs index ff846857446..842c84a3e5c 100644 --- a/src/bootstrap/flags.rs +++ b/src/bootstrap/flags.rs @@ -98,7 +98,6 @@ impl Default for Subcommand { impl Flags { pub fn parse(args: &[String]) -> Flags { - let mut extra_help = String::new(); let mut subcommand_help = String::from( "\ Usage: x.py [options] [...] @@ -170,16 +169,6 @@ To learn more about a subcommand, run `./x.py -h`", "VALUE", ); - // fn usage() - let usage = - |exit_code: i32, opts: &Options, subcommand_help: &str, extra_help: &str| -> ! { - println!("{}", opts.usage(subcommand_help)); - if !extra_help.is_empty() { - println!("{}", extra_help); - } - process::exit(exit_code); - }; - // We can't use getopt to parse the options until we have completed specifying which // options are valid, but under the current implementation, some options are conditional on // the subcommand. Therefore we must manually identify the subcommand first, so that we can @@ -263,12 +252,38 @@ To learn more about a subcommand, run `./x.py -h`", _ => {} }; + // fn usage() + let usage = |exit_code: i32, opts: &Options, verbose: bool, subcommand_help: &str| -> ! { + let mut extra_help = String::new(); + + // All subcommands except `clean` can have an optional "Available paths" section + if verbose { + let config = Config::parse(&["build".to_string()]); + let build = Build::new(config); + + let maybe_rules_help = Builder::get_help(&build, subcommand.as_str()); + extra_help.push_str(maybe_rules_help.unwrap_or_default().as_str()); + } else if !(subcommand.as_str() == "clean" || subcommand.as_str() == "fmt") { + extra_help.push_str( + format!("Run `./x.py {} -h -v` to see a list of available paths.", subcommand) + .as_str(), + ); + } + + println!("{}", opts.usage(subcommand_help)); + if !extra_help.is_empty() { + println!("{}", extra_help); + } + process::exit(exit_code); + }; + // Done specifying what options are possible, so do the getopts parsing let matches = opts.parse(&args[..]).unwrap_or_else(|e| { // Invalid argument/option format println!("\n{}\n", e); - usage(1, &opts, &subcommand_help, &extra_help); + usage(1, &opts, false, &subcommand_help); }); + // Extra sanity check to make sure we didn't hit this crazy corner case: // // ./x.py --frobulate clean build @@ -436,24 +451,11 @@ Arguments: let paths = matches.free[1..].iter().map(|p| p.into()).collect::>(); let cfg_file = env::var_os("BOOTSTRAP_CONFIG").map(PathBuf::from); - - // All subcommands except `clean` can have an optional "Available paths" section - if matches.opt_present("verbose") { - let config = Config::parse(&["build".to_string()]); - let build = Build::new(config); - - let maybe_rules_help = Builder::get_help(&build, subcommand.as_str()); - extra_help.push_str(maybe_rules_help.unwrap_or_default().as_str()); - } else if !(subcommand.as_str() == "clean" || subcommand.as_str() == "fmt") { - extra_help.push_str( - format!("Run `./x.py {} -h -v` to see a list of available paths.", subcommand) - .as_str(), - ); - } + let verbose = matches.opt_present("verbose"); // User passed in -h/--help? if matches.opt_present("help") { - usage(0, &opts, &subcommand_help, &extra_help); + usage(0, &opts, verbose, &subcommand_help); } let cmd = match subcommand.as_str() { @@ -483,7 +485,7 @@ Arguments: "clean" => { if !paths.is_empty() { println!("\nclean does not take a path argument\n"); - usage(1, &opts, &subcommand_help, &extra_help); + usage(1, &opts, verbose, &subcommand_help); } Subcommand::Clean { all: matches.opt_present("all") } @@ -494,12 +496,12 @@ Arguments: "run" | "r" => { if paths.is_empty() { println!("\nrun requires at least a path!\n"); - usage(1, &opts, &subcommand_help, &extra_help); + usage(1, &opts, verbose, &subcommand_help); } Subcommand::Run { paths } } _ => { - usage(1, &opts, &subcommand_help, &extra_help); + usage(1, &opts, verbose, &subcommand_help); } }; -- cgit 1.4.1-3-g733a5 From 7e24136996fd412ba2890952d5f0ddffb3cb7370 Mon Sep 17 00:00:00 2001 From: Mark Rousskov Date: Wed, 16 Sep 2020 10:29:31 -0400 Subject: Pass --target to lint docs Otherwise, we may not have a standard library built for the native "host" target of the rustc being run. --- src/bootstrap/doc.rs | 4 +++- src/tools/lint-docs/src/groups.rs | 8 ++++---- src/tools/lint-docs/src/lib.rs | 25 ++++++++++++++++--------- src/tools/lint-docs/src/main.rs | 15 ++++++++++++++- 4 files changed, 37 insertions(+), 15 deletions(-) (limited to 'src/bootstrap') diff --git a/src/bootstrap/doc.rs b/src/bootstrap/doc.rs index f90e76a4f4e..cf9211bc7ea 100644 --- a/src/bootstrap/doc.rs +++ b/src/bootstrap/doc.rs @@ -752,6 +752,7 @@ impl Step for RustcBook { let out_listing = out_base.join("src/lints"); builder.cp_r(&builder.src.join("src/doc/rustc"), &out_base); builder.info(&format!("Generating lint docs ({})", self.target)); + let rustc = builder.rustc(self.compiler); // The tool runs `rustc` for extracting output examples, so it needs a // functional sysroot. @@ -762,7 +763,8 @@ impl Step for RustcBook { cmd.arg("--out"); cmd.arg(&out_listing); cmd.arg("--rustc"); - cmd.arg(rustc); + cmd.arg(&rustc); + cmd.arg("--rustc-target").arg(&self.target.rustc_target_arg()); if builder.config.verbose() { cmd.arg("--verbose"); } diff --git a/src/tools/lint-docs/src/groups.rs b/src/tools/lint-docs/src/groups.rs index a212459bb4d..6b32ebdc284 100644 --- a/src/tools/lint-docs/src/groups.rs +++ b/src/tools/lint-docs/src/groups.rs @@ -18,10 +18,10 @@ static GROUP_DESCRIPTIONS: &[(&str, &str)] = &[ /// Updates the documentation of lint groups. pub(crate) fn generate_group_docs( lints: &[Lint], - rustc_path: &Path, + rustc: crate::Rustc<'_>, out_path: &Path, ) -> Result<(), Box> { - let groups = collect_groups(rustc_path)?; + let groups = collect_groups(rustc)?; let groups_path = out_path.join("groups.md"); let contents = fs::read_to_string(&groups_path) .map_err(|e| format!("could not read {}: {}", groups_path.display(), e))?; @@ -36,9 +36,9 @@ pub(crate) fn generate_group_docs( type LintGroups = BTreeMap>; /// Collects the group names from rustc. -fn collect_groups(rustc: &Path) -> Result> { +fn collect_groups(rustc: crate::Rustc<'_>) -> Result> { let mut result = BTreeMap::new(); - let mut cmd = Command::new(rustc); + let mut cmd = Command::new(rustc.path); cmd.arg("-Whelp"); let output = cmd.output().map_err(|e| format!("failed to run command {:?}\n{}", cmd, e))?; if !output.status.success() { diff --git a/src/tools/lint-docs/src/lib.rs b/src/tools/lint-docs/src/lib.rs index 92b3d186fa1..6ca71dcaf3c 100644 --- a/src/tools/lint-docs/src/lib.rs +++ b/src/tools/lint-docs/src/lib.rs @@ -45,16 +45,22 @@ impl Level { } } +#[derive(Copy, Clone)] +pub struct Rustc<'a> { + pub path: &'a Path, + pub target: &'a str, +} + /// Collects all lints, and writes the markdown documentation at the given directory. pub fn extract_lint_docs( src_path: &Path, out_path: &Path, - rustc_path: &Path, + rustc: Rustc<'_>, verbose: bool, ) -> Result<(), Box> { let mut lints = gather_lints(src_path)?; for lint in &mut lints { - generate_output_example(lint, rustc_path, verbose).map_err(|e| { + generate_output_example(lint, rustc, verbose).map_err(|e| { format!( "failed to test example in lint docs for `{}` in {}:{}: {}", lint.name, @@ -65,7 +71,7 @@ pub fn extract_lint_docs( })?; } save_lints_markdown(&lints, &out_path.join("listing"))?; - groups::generate_group_docs(&lints, rustc_path, out_path)?; + groups::generate_group_docs(&lints, rustc, out_path)?; Ok(()) } @@ -208,7 +214,7 @@ fn lint_name(line: &str) -> Result { /// actual output from the compiler. fn generate_output_example( lint: &mut Lint, - rustc_path: &Path, + rustc: Rustc<'_>, verbose: bool, ) -> Result<(), Box> { // Explicit list of lints that are allowed to not have an example. Please @@ -230,7 +236,7 @@ fn generate_output_example( // separate test suite, and use an include mechanism such as mdbook's // `{{#rustdoc_include}}`. if !lint.is_ignored() { - replace_produces(lint, rustc_path, verbose)?; + replace_produces(lint, rustc, verbose)?; } Ok(()) } @@ -261,7 +267,7 @@ fn check_style(lint: &Lint) -> Result<(), Box> { /// output from the compiler. fn replace_produces( lint: &mut Lint, - rustc_path: &Path, + rustc: Rustc<'_>, verbose: bool, ) -> Result<(), Box> { let mut lines = lint.doc.iter_mut(); @@ -302,7 +308,7 @@ fn replace_produces( Some(line) if line.is_empty() => {} Some(line) if line == "{{produces}}" => { let output = - generate_lint_output(&lint.name, &example, &options, rustc_path, verbose)?; + generate_lint_output(&lint.name, &example, &options, rustc, verbose)?; line.replace_range( .., &format!( @@ -329,7 +335,7 @@ fn generate_lint_output( name: &str, example: &[&mut String], options: &[&str], - rustc_path: &Path, + rustc: Rustc<'_>, verbose: bool, ) -> Result> { if verbose { @@ -364,13 +370,14 @@ fn generate_lint_output( } fs::write(&tempfile, source) .map_err(|e| format!("failed to write {}: {}", tempfile.display(), e))?; - let mut cmd = Command::new(rustc_path); + let mut cmd = Command::new(rustc.path); if options.contains(&"edition2015") { cmd.arg("--edition=2015"); } else { cmd.arg("--edition=2018"); } cmd.arg("--error-format=json"); + cmd.arg("--target").arg(rustc.target); if options.contains(&"test") { cmd.arg("--test"); } diff --git a/src/tools/lint-docs/src/main.rs b/src/tools/lint-docs/src/main.rs index 45d97bd4317..5db49007d37 100644 --- a/src/tools/lint-docs/src/main.rs +++ b/src/tools/lint-docs/src/main.rs @@ -13,6 +13,7 @@ fn doit() -> Result<(), Box> { let mut src_path = None; let mut out_path = None; let mut rustc_path = None; + let mut rustc_target = None; let mut verbose = false; while let Some(arg) = args.next() { match arg.as_str() { @@ -34,6 +35,12 @@ fn doit() -> Result<(), Box> { None => return Err("--rustc requires a value".into()), }; } + "--rustc-target" => { + rustc_target = match args.next() { + Some(s) => Some(s), + None => return Err("--rustc-target requires a value".into()), + }; + } "-v" | "--verbose" => verbose = true, s => return Err(format!("unexpected argument `{}`", s).into()), } @@ -47,10 +54,16 @@ fn doit() -> Result<(), Box> { if rustc_path.is_none() { return Err("--rustc must be specified to the path of rustc".into()); } + if rustc_target.is_none() { + return Err("--rustc-target must be specified to the rustc target".into()); + } lint_docs::extract_lint_docs( &src_path.unwrap(), &out_path.unwrap(), - &rustc_path.unwrap(), + lint_docs::Rustc { + path: rustc_path.as_deref().unwrap(), + target: rustc_target.as_deref().unwrap(), + }, verbose, ) } -- cgit 1.4.1-3-g733a5 From bd4e0af0b54afc91903c282740e25ee6135224c8 Mon Sep 17 00:00:00 2001 From: Mark Rousskov Date: Wed, 16 Sep 2020 12:47:26 -0400 Subject: Build rustdoc for cross-compiled targets This isn't an issue for most folks who use x.py dist, which will directly depend on this. But for x.py build, if we don't properly set target here rustdoc will not be built. Currently, there is not a default-on step for generating a rustc for a given target either, so we will fail to build a rustc as well. --- src/bootstrap/tool.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'src/bootstrap') diff --git a/src/bootstrap/tool.rs b/src/bootstrap/tool.rs index 99e33e3b006..d2346e40e51 100644 --- a/src/bootstrap/tool.rs +++ b/src/bootstrap/tool.rs @@ -471,7 +471,11 @@ impl Step for Rustdoc { fn make_run(run: RunConfig<'_>) { run.builder.ensure(Rustdoc { - compiler: run.builder.compiler(run.builder.top_stage, run.build_triple()), + // Note: this is somewhat unique in that we actually want a *target* + // compiler here, because rustdoc *is* a compiler. We won't be using + // this as the compiler to build with, but rather this is "what + // compiler are we producing"? + compiler: run.builder.compiler(run.builder.top_stage, run.target), }); } -- cgit 1.4.1-3-g733a5 From 363aff0a9d0b85285b7501cb04dd8263d29d273a Mon Sep 17 00:00:00 2001 From: Mark Rousskov Date: Thu, 17 Sep 2020 16:03:42 -0400 Subject: Add test for x.py build cross-compilation --- src/bootstrap/builder/tests.rs | 48 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) (limited to 'src/bootstrap') diff --git a/src/bootstrap/builder/tests.rs b/src/bootstrap/builder/tests.rs index f96925f9270..77b39cbb87e 100644 --- a/src/bootstrap/builder/tests.rs +++ b/src/bootstrap/builder/tests.rs @@ -90,6 +90,54 @@ mod defaults { assert!(builder.cache.all::().is_empty()); } + #[test] + fn build_cross_compile() { + let config = Config { stage: 1, ..configure("build", &["B"], &["B"]) }; + let build = Build::new(config); + let mut builder = Builder::new(&build); + builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Build), &[]); + + let a = TargetSelection::from_user("A"); + let b = TargetSelection::from_user("B"); + + // Ideally, this build wouldn't actually have `target: a` + // rustdoc/rustcc/std here (the user only requested a host=B build, so + // there's not really a need for us to build for target A in this case + // (since we're producing stage 1 libraries/binaries). But currently + // rustbuild is just a bit buggy here; this should be fixed though. + assert_eq!( + first(builder.cache.all::()), + &[ + compile::Std { compiler: Compiler { host: a, stage: 0 }, target: a }, + compile::Std { compiler: Compiler { host: a, stage: 1 }, target: a }, + compile::Std { compiler: Compiler { host: a, stage: 0 }, target: b }, + compile::Std { compiler: Compiler { host: a, stage: 1 }, target: b }, + ] + ); + assert_eq!( + first(builder.cache.all::()), + &[ + compile::Assemble { target_compiler: Compiler { host: a, stage: 0 } }, + compile::Assemble { target_compiler: Compiler { host: a, stage: 1 } }, + compile::Assemble { target_compiler: Compiler { host: b, stage: 1 } }, + ] + ); + assert_eq!( + first(builder.cache.all::()), + &[ + tool::Rustdoc { compiler: Compiler { host: a, stage: 1 } }, + tool::Rustdoc { compiler: Compiler { host: b, stage: 1 } }, + ], + ); + assert_eq!( + first(builder.cache.all::()), + &[ + compile::Rustc { compiler: Compiler { host: a, stage: 0 }, target: a }, + compile::Rustc { compiler: Compiler { host: a, stage: 0 }, target: b }, + ] + ); + } + #[test] fn doc_default() { let mut config = configure("doc", &[], &[]); -- cgit 1.4.1-3-g733a5 From ee1e9343b35c169a100654a511e21c47eb2cb0bc Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Fri, 18 Sep 2020 00:18:19 +0200 Subject: Distribute rustc sources as part of `rustc-dev` --- src/bootstrap/dist.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'src/bootstrap') diff --git a/src/bootstrap/dist.rs b/src/bootstrap/dist.rs index cf73e570fa5..debe6e36a9b 100644 --- a/src/bootstrap/dist.rs +++ b/src/bootstrap/dist.rs @@ -793,6 +793,18 @@ impl Step for RustcDev { let stamp = compile::librustc_stamp(builder, compiler_to_use, target); copy_target_libs(builder, target, &image, &stamp); + // Copy compiler sources. + let dst_src = image.join("lib/rustlib/rustc-src/rust"); + t!(fs::create_dir_all(&dst_src)); + + let src_files = ["Cargo.lock"]; + // This is the reduced set of paths which will become the rustc-dev component + // (essentially the compiler crates and all of their path dependencies). + copy_src_dirs(builder, &builder.src, &["compiler"], &[], &dst_src); + for file in src_files.iter() { + builder.copy(&builder.src.join(file), &dst_src.join(file)); + } + let mut cmd = rust_installer(builder); cmd.arg("generate") .arg("--product-name=Rust") -- cgit 1.4.1-3-g733a5 From f05b47ccdfa63f8b4b9fb47a9aa92381801d3ff1 Mon Sep 17 00:00:00 2001 From: est31 Date: Fri, 18 Sep 2020 06:21:06 +0200 Subject: Don't download/sync llvm-project submodule if download-ci-llvm is set llvm-project takes > 1GB storage space and a long time to download. It's better to not download it unless needed. --- src/bootstrap/bootstrap.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src/bootstrap') diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index 5f78031e1c7..613f364b753 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -881,8 +881,9 @@ class RustBuild(object): submodules_names = [] for module in submodules: if module.endswith("llvm-project"): - if self.get_toml('llvm-config') and self.get_toml('lld') != 'true': - continue + if self.get_toml('llvm-config') or self.get_toml('download-ci-llvm') == 'true': + if self.get_toml('lld') != 'true': + continue check = self.check_submodule(module, slow_submodules) filtered_submodules.append((module, check)) submodules_names.append(module) -- cgit 1.4.1-3-g733a5 From b9af3e30a9e34e4fe353e7e5c42103851f9c8f79 Mon Sep 17 00:00:00 2001 From: Pietro Albini Date: Fri, 18 Sep 2020 14:58:22 +0200 Subject: bootstrap: move the version number to a plaintext file The Rust version number is currently embedded in bootstrap's source code, which makes it hard to update it automatically or access it outside of ./x.py (as you'd have to parse the source code). This commit moves the version number to a standalone plaintext file, which makes accessing or updating it trivial. --- src/bootstrap/channel.rs | 3 --- src/bootstrap/dist.rs | 7 +++---- src/bootstrap/doc.rs | 4 ++-- src/bootstrap/lib.rs | 21 ++++++++++++++------- src/bootstrap/native.rs | 3 +-- src/bootstrap/test.rs | 2 +- src/bootstrap/tool.rs | 3 +-- src/version | 1 + 8 files changed, 23 insertions(+), 21 deletions(-) create mode 100644 src/version (limited to 'src/bootstrap') diff --git a/src/bootstrap/channel.rs b/src/bootstrap/channel.rs index 2a461170b5c..2b82f6c30b2 100644 --- a/src/bootstrap/channel.rs +++ b/src/bootstrap/channel.rs @@ -12,9 +12,6 @@ use build_helper::output; use crate::Build; -// The version number -pub const CFG_RELEASE_NUM: &str = "1.48.0"; - pub struct GitInfo { inner: Option, } diff --git a/src/bootstrap/dist.rs b/src/bootstrap/dist.rs index cf73e570fa5..f25ad50c9b7 100644 --- a/src/bootstrap/dist.rs +++ b/src/bootstrap/dist.rs @@ -18,7 +18,6 @@ use build_helper::{output, t}; use crate::builder::{Builder, RunConfig, ShouldRun, Step}; use crate::cache::{Interned, INTERNER}; -use crate::channel; use crate::compile; use crate::config::TargetSelection; use crate::tool::{self, Tool}; @@ -569,7 +568,7 @@ impl Step for Rustc { &page_dst, &[ ("", &month_year), - ("", channel::CFG_RELEASE_NUM), + ("", &builder.version), ], ); } @@ -2289,9 +2288,9 @@ impl Step for Extended { } fn add_env(builder: &Builder<'_>, cmd: &mut Command, target: TargetSelection) { - let mut parts = channel::CFG_RELEASE_NUM.split('.'); + let mut parts = builder.version.split('.'); cmd.env("CFG_RELEASE_INFO", builder.rust_version()) - .env("CFG_RELEASE_NUM", channel::CFG_RELEASE_NUM) + .env("CFG_RELEASE_NUM", &builder.version) .env("CFG_RELEASE", builder.rust_release()) .env("CFG_VER_MAJOR", parts.next().unwrap()) .env("CFG_VER_MINOR", parts.next().unwrap()) diff --git a/src/bootstrap/doc.rs b/src/bootstrap/doc.rs index f90e76a4f4e..97f32b61fb9 100644 --- a/src/bootstrap/doc.rs +++ b/src/bootstrap/doc.rs @@ -433,7 +433,7 @@ impl Step for Std { .arg("-Z") .arg("unstable-options") .arg("--resource-suffix") - .arg(crate::channel::CFG_RELEASE_NUM) + .arg(&builder.version) .arg("--index-page") .arg(&builder.src.join("src/doc/index.md")); @@ -659,7 +659,7 @@ impl Step for ErrorIndex { let mut index = tool::ErrorIndex::command(builder, self.compiler); index.arg("html"); index.arg(out.join("error-index.html")); - index.arg(crate::channel::CFG_RELEASE_NUM); + index.arg(&builder.version); builder.run(&mut index); } diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs index 91b85f5af1d..3f7aeae0ed4 100644 --- a/src/bootstrap/lib.rs +++ b/src/bootstrap/lib.rs @@ -218,6 +218,9 @@ pub struct Build { /// User-specified configuration from `config.toml`. config: Config, + // Version information + version: String, + // Properties derived from the above configuration src: PathBuf, out: PathBuf, @@ -380,6 +383,10 @@ impl Build { .unwrap() .to_path_buf(); + let version = std::fs::read_to_string(src.join("src").join("version")) + .expect("failed to read src/version"); + let version = version.trim(); + let mut build = Build { initial_rustc: config.initial_rustc.clone(), initial_cargo: config.initial_cargo.clone(), @@ -395,6 +402,7 @@ impl Build { targets: config.targets.clone(), config, + version: version.to_string(), src, out, @@ -433,8 +441,7 @@ impl Build { .next() .unwrap() .trim(); - let my_version = channel::CFG_RELEASE_NUM; - if local_release.split('.').take(2).eq(my_version.split('.').take(2)) { + if local_release.split('.').take(2).eq(version.split('.').take(2)) { build.verbose(&format!("auto-detected local-rebuild {}", local_release)); build.local_rebuild = true; } @@ -785,7 +792,7 @@ impl Build { match which { GitRepo::Rustc => { - let sha = self.rust_sha().unwrap_or(channel::CFG_RELEASE_NUM); + let sha = self.rust_sha().unwrap_or(&self.version); Some(format!("/rustc/{}", sha)) } GitRepo::Llvm => Some(String::from("/rustc/llvm")), @@ -1016,7 +1023,7 @@ impl Build { /// Returns the value of `release` above for Rust itself. fn rust_release(&self) -> String { - self.release(channel::CFG_RELEASE_NUM) + self.release(&self.version) } /// Returns the "package version" for a component given the `num` release @@ -1036,7 +1043,7 @@ impl Build { /// Returns the value of `package_vers` above for Rust itself. fn rust_package_vers(&self) -> String { - self.package_vers(channel::CFG_RELEASE_NUM) + self.package_vers(&self.version) } /// Returns the value of `package_vers` above for Cargo @@ -1070,7 +1077,7 @@ impl Build { } fn llvm_tools_package_vers(&self) -> String { - self.package_vers(channel::CFG_RELEASE_NUM) + self.package_vers(&self.version) } fn llvm_tools_vers(&self) -> String { @@ -1087,7 +1094,7 @@ impl Build { /// Note that this is a descriptive string which includes the commit date, /// sha, version, etc. fn rust_version(&self) -> String { - self.rust_info.version(self, channel::CFG_RELEASE_NUM) + self.rust_info.version(self, &self.version) } /// Returns the full commit hash. diff --git a/src/bootstrap/native.rs b/src/bootstrap/native.rs index 3829d47da33..9e4d6d0023d 100644 --- a/src/bootstrap/native.rs +++ b/src/bootstrap/native.rs @@ -19,7 +19,6 @@ use std::process::Command; use build_helper::{output, t}; use crate::builder::{Builder, RunConfig, ShouldRun, Step}; -use crate::channel; use crate::config::TargetSelection; use crate::util::{self, exe}; use crate::GitRepo; @@ -296,7 +295,7 @@ impl Step for Llvm { // release number on the dev channel. cfg.define("LLVM_VERSION_SUFFIX", "-rust-dev"); } else { - let suffix = format!("-rust-{}-{}", channel::CFG_RELEASE_NUM, builder.config.channel); + let suffix = format!("-rust-{}-{}", builder.version, builder.config.channel); cfg.define("LLVM_VERSION_SUFFIX", suffix); } diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs index ba5f75c49ac..dc28b8ece24 100644 --- a/src/bootstrap/test.rs +++ b/src/bootstrap/test.rs @@ -636,7 +636,7 @@ impl Step for RustdocJSStd { .arg("--crate-name") .arg("std") .arg("--resource-suffix") - .arg(crate::channel::CFG_RELEASE_NUM) + .arg(&builder.version) .arg("--doc-folder") .arg(builder.doc_out(self.target)) .arg("--test-folder") diff --git a/src/bootstrap/tool.rs b/src/bootstrap/tool.rs index 99e33e3b006..5d66632d92c 100644 --- a/src/bootstrap/tool.rs +++ b/src/bootstrap/tool.rs @@ -7,7 +7,6 @@ use std::process::{exit, Command}; use build_helper::t; use crate::builder::{Builder, Cargo as CargoCommand, RunConfig, ShouldRun, Step}; -use crate::channel; use crate::channel::GitInfo; use crate::compile; use crate::config::TargetSelection; @@ -255,7 +254,7 @@ pub fn prepare_tool_cargo( cargo.env("CFG_RELEASE", builder.rust_release()); cargo.env("CFG_RELEASE_CHANNEL", &builder.config.channel); cargo.env("CFG_VERSION", builder.rust_version()); - cargo.env("CFG_RELEASE_NUM", channel::CFG_RELEASE_NUM); + cargo.env("CFG_RELEASE_NUM", &builder.version); let info = GitInfo::new(builder.config.ignore_git, &dir); if let Some(sha) = info.sha() { diff --git a/src/version b/src/version new file mode 100644 index 00000000000..9db5ea12f52 --- /dev/null +++ b/src/version @@ -0,0 +1 @@ +1.48.0 -- cgit 1.4.1-3-g733a5 From 3bddfea7e23341f788526f8b303b9a27038511e3 Mon Sep 17 00:00:00 2001 From: Pietro Albini Date: Fri, 18 Sep 2020 14:40:01 +0200 Subject: build-manifest: stop receiving release numbers from bootstrap --- Cargo.lock | 1 + src/bootstrap/dist.rs | 10 +-- src/tools/build-manifest/Cargo.toml | 1 + src/tools/build-manifest/README.md | 9 ++- src/tools/build-manifest/src/main.rs | 77 +++++++-------------- src/tools/build-manifest/src/versions.rs | 114 +++++++++++++++++++++++++++++++ 6 files changed, 146 insertions(+), 66 deletions(-) (limited to 'src/bootstrap') diff --git a/Cargo.lock b/Cargo.lock index d3f777bc663..5a2dd23a7ae 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -230,6 +230,7 @@ dependencies = [ name = "build-manifest" version = "0.1.0" dependencies = [ + "anyhow", "serde", "serde_json", "toml", diff --git a/src/bootstrap/dist.rs b/src/bootstrap/dist.rs index f25ad50c9b7..991f8d4eea3 100644 --- a/src/bootstrap/dist.rs +++ b/src/bootstrap/dist.rs @@ -2356,15 +2356,9 @@ impl Step for HashSign { cmd.arg(sign); cmd.arg(distdir(builder)); cmd.arg(today.trim()); - cmd.arg(builder.rust_package_vers()); cmd.arg(addr); - cmd.arg(builder.package_vers(&builder.release_num("cargo"))); - cmd.arg(builder.package_vers(&builder.release_num("rls"))); - cmd.arg(builder.package_vers(&builder.release_num("rust-analyzer/crates/rust-analyzer"))); - cmd.arg(builder.package_vers(&builder.release_num("clippy"))); - cmd.arg(builder.package_vers(&builder.release_num("miri"))); - cmd.arg(builder.package_vers(&builder.release_num("rustfmt"))); - cmd.arg(builder.llvm_tools_package_vers()); + cmd.arg(&builder.config.channel); + cmd.arg(&builder.src); builder.create_dir(&distdir(builder)); diff --git a/src/tools/build-manifest/Cargo.toml b/src/tools/build-manifest/Cargo.toml index 0bbbabd2998..2da224a54dc 100644 --- a/src/tools/build-manifest/Cargo.toml +++ b/src/tools/build-manifest/Cargo.toml @@ -8,3 +8,4 @@ edition = "2018" toml = "0.5" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" +anyhow = "1.0.32" diff --git a/src/tools/build-manifest/README.md b/src/tools/build-manifest/README.md index a80f36d4969..4d7d9f7da18 100644 --- a/src/tools/build-manifest/README.md +++ b/src/tools/build-manifest/README.md @@ -21,10 +21,9 @@ Then, you can generate the manifest and all the packages from `path/to/dist` to ``` $ BUILD_MANIFEST_DISABLE_SIGNING=1 cargo +nightly run \ - path/to/dist path/to/output 1970-01-01 \ - nightly nightly nightly nightly nightly nightly nightly nightly \ - http://example.com + path/to/dist path/to/output 1970-01-01 http://example.com \ + CHANNEL path/to/rust/repo ``` -In the future, if the tool complains about missing arguments just add more -`nightly`s in the middle. +Remember to replace `CHANNEL` with the channel you produced dist artifacts of +and `path/to/rust/repo` with the path to your checkout of the Rust repository. diff --git a/src/tools/build-manifest/src/main.rs b/src/tools/build-manifest/src/main.rs index 7a1878f749e..c694948bac0 100644 --- a/src/tools/build-manifest/src/main.rs +++ b/src/tools/build-manifest/src/main.rs @@ -6,7 +6,7 @@ mod versions; -use crate::versions::PkgType; +use crate::versions::{PkgType, Versions}; use serde::Serialize; use std::collections::BTreeMap; use std::collections::HashMap; @@ -227,14 +227,7 @@ macro_rules! t { } struct Builder { - rust_release: String, - cargo_release: String, - rls_release: String, - rust_analyzer_release: String, - clippy_release: String, - rustfmt_release: String, - llvm_tools_release: String, - miri_release: String, + versions: Versions, input: PathBuf, output: PathBuf, @@ -281,15 +274,9 @@ fn main() { let input = PathBuf::from(args.next().unwrap()); let output = PathBuf::from(args.next().unwrap()); let date = args.next().unwrap(); - let rust_release = args.next().unwrap(); let s3_address = args.next().unwrap(); - let cargo_release = args.next().unwrap(); - let rls_release = args.next().unwrap(); - let rust_analyzer_release = args.next().unwrap(); - let clippy_release = args.next().unwrap(); - let miri_release = args.next().unwrap(); - let rustfmt_release = args.next().unwrap(); - let llvm_tools_release = args.next().unwrap(); + let channel = args.next().unwrap(); + let monorepo_path = args.next().unwrap(); // Do not ask for a passphrase while manually testing let mut passphrase = String::new(); @@ -299,14 +286,7 @@ fn main() { } Builder { - rust_release, - cargo_release, - rls_release, - rust_analyzer_release, - clippy_release, - rustfmt_release, - llvm_tools_release, - miri_release, + versions: Versions::new(&channel, Path::new(&monorepo_path)).unwrap(), input, output, @@ -363,10 +343,11 @@ impl Builder { self.check_toolstate(); self.digest_and_sign(); let manifest = self.build_manifest(); - self.write_channel_files(&self.rust_release, &manifest); - if self.rust_release != "beta" && self.rust_release != "nightly" { - self.write_channel_files("stable", &manifest); + let rust_version = self.versions.package_version(&PkgType::Rust).unwrap(); + self.write_channel_files(self.versions.channel(), &manifest); + if self.versions.channel() != rust_version { + self.write_channel_files(&rust_version, &manifest); } } @@ -473,7 +454,7 @@ impl Builder { // The compiler libraries are not stable for end users, and they're also huge, so we only // `rustc-dev` for nightly users, and only in the "complete" profile. It's still possible // for users to install the additional component manually, if needed. - if self.rust_release == "nightly" { + if self.versions.channel() == "nightly" { self.extend_profile("complete", &mut manifest.profiles, &["rustc-dev"]); self.extend_profile("complete", &mut manifest.profiles, &["rustc-docs"]); } @@ -511,7 +492,7 @@ impl Builder { } fn target_host_combination(&mut self, host: &str, manifest: &Manifest) -> Option { - let filename = self.filename("rust", host); + let filename = self.versions.tarball_name(&PkgType::Rust, host).unwrap(); let digest = self.digests.remove(&filename)?; let xz_filename = filename.replace(".tar.gz", ".tar.xz"); let xz_digest = self.digests.remove(&xz_filename); @@ -610,7 +591,7 @@ impl Builder { .unwrap_or_default(); // `is_present` defaults to `false` here. // Never ship nightly-only components for other trains. - if self.rust_release != "nightly" && NIGHTLY_ONLY_COMPONENTS.contains(&pkgname) { + if self.versions.channel() != "nightly" && NIGHTLY_ONLY_COMPONENTS.contains(&pkgname) { is_present = false; // Pretend the component is entirely missing. } @@ -619,7 +600,10 @@ impl Builder { .map(|name| { if is_present { // The component generally exists, but it might still be missing for this target. - let filename = self.filename(pkgname, name); + let filename = self + .versions + .tarball_name(&PkgType::from_component(pkgname), name) + .unwrap(); let digest = match self.digests.remove(&filename) { Some(digest) => digest, // This component does not exist for this target -- skip it. @@ -662,23 +646,6 @@ impl Builder { format!("{}/{}/{}", self.s3_address, self.date, filename) } - fn filename(&self, component: &str, target: &str) -> String { - use PkgType::*; - match PkgType::from_component(component) { - RustSrc => format!("rust-src-{}.tar.gz", self.rust_release), - Cargo => format!("cargo-{}-{}.tar.gz", self.cargo_release, target), - Rls => format!("rls-{}-{}.tar.gz", self.rls_release, target), - RustAnalyzer => { - format!("rust-analyzer-{}-{}.tar.gz", self.rust_analyzer_release, target) - } - Clippy => format!("clippy-{}-{}.tar.gz", self.clippy_release, target), - Rustfmt => format!("rustfmt-{}-{}.tar.gz", self.rustfmt_release, target), - LlvmTools => format!("llvm-tools-{}-{}.tar.gz", self.llvm_tools_release, target), - Miri => format!("miri-{}-{}.tar.gz", self.miri_release, target), - Other(_) => format!("{}-{}-{}.tar.gz", component, self.rust_release, target), - } - } - fn cached_version(&self, component: &str) -> &Option { use PkgType::*; match PkgType::from_component(component) { @@ -707,20 +674,24 @@ impl Builder { } } - fn version(&self, component: &str, target: &str) -> Option { + fn version(&mut self, component: &str, target: &str) -> Option { self.untar(component, target, |filename| format!("{}/version", filename)) } - fn git_commit_hash(&self, component: &str, target: &str) -> Option { + fn git_commit_hash(&mut self, component: &str, target: &str) -> Option { self.untar(component, target, |filename| format!("{}/git-commit-hash", filename)) } - fn untar(&self, component: &str, target: &str, dir: F) -> Option + fn untar(&mut self, component: &str, target: &str, dir: F) -> Option where F: FnOnce(String) -> String, { + let filename = self + .versions + .tarball_name(&PkgType::from_component(component), target) + .expect("failed to retrieve the tarball path"); + let mut cmd = Command::new("tar"); - let filename = self.filename(component, target); cmd.arg("xf") .arg(self.input.join(&filename)) .arg(dir(filename.replace(".tar.gz", ""))) diff --git a/src/tools/build-manifest/src/versions.rs b/src/tools/build-manifest/src/versions.rs index 044c04914b1..151cfa0d9c6 100644 --- a/src/tools/build-manifest/src/versions.rs +++ b/src/tools/build-manifest/src/versions.rs @@ -1,4 +1,10 @@ +use anyhow::{Context, Error}; +use std::collections::HashMap; +use std::path::{Path, PathBuf}; + +#[derive(Debug, Hash, Eq, PartialEq, Clone)] pub(crate) enum PkgType { + Rust, RustSrc, Cargo, Rls, @@ -13,6 +19,7 @@ pub(crate) enum PkgType { impl PkgType { pub(crate) fn from_component(component: &str) -> Self { match component { + "rust" => PkgType::Rust, "rust-src" => PkgType::RustSrc, "cargo" => PkgType::Cargo, "rls" | "rls-preview" => PkgType::Rls, @@ -24,4 +31,111 @@ impl PkgType { other => PkgType::Other(other.into()), } } + + fn rust_monorepo_path(&self) -> Option<&'static str> { + match self { + PkgType::Cargo => Some("src/tools/cargo"), + PkgType::Rls => Some("src/tools/rls"), + PkgType::RustAnalyzer => Some("src/tools/rust-analyzer/crates/rust-analyzer"), + PkgType::Clippy => Some("src/tools/clippy"), + PkgType::Rustfmt => Some("src/tools/rustfmt"), + PkgType::Miri => Some("src/tools/miri"), + PkgType::Rust => None, + PkgType::RustSrc => None, + PkgType::LlvmTools => None, + PkgType::Other(_) => None, + } + } + + fn tarball_component_name(&self) -> &str { + match self { + PkgType::Rust => "rust", + PkgType::RustSrc => "rust-src", + PkgType::Cargo => "cargo", + PkgType::Rls => "rls", + PkgType::RustAnalyzer => "rust-analyzer", + PkgType::Clippy => "clippy", + PkgType::Rustfmt => "rustfmt", + PkgType::LlvmTools => "llvm-tools", + PkgType::Miri => "miri", + PkgType::Other(component) => component, + } + } +} + +pub(crate) struct Versions { + channel: String, + rustc_version: String, + monorepo_root: PathBuf, + package_versions: HashMap, +} + +impl Versions { + pub(crate) fn new(channel: &str, monorepo_root: &Path) -> Result { + Ok(Self { + channel: channel.into(), + rustc_version: std::fs::read_to_string(monorepo_root.join("src").join("version")) + .context("failed to read the rustc version from src/version")? + .trim() + .to_string(), + monorepo_root: monorepo_root.into(), + package_versions: HashMap::new(), + }) + } + + pub(crate) fn channel(&self) -> &str { + &self.channel + } + + pub(crate) fn tarball_name( + &mut self, + package: &PkgType, + target: &str, + ) -> Result { + Ok(format!( + "{}-{}-{}.tar.gz", + package.tarball_component_name(), + self.package_version(package).with_context(|| format!( + "failed to get the package version for component {:?}", + package, + ))?, + target + )) + } + + pub(crate) fn package_version(&mut self, package: &PkgType) -> Result { + match self.package_versions.get(package) { + Some(release) => Ok(release.clone()), + None => { + let version = match package.rust_monorepo_path() { + Some(path) => { + let path = self.monorepo_root.join(path).join("Cargo.toml"); + let cargo_toml: CargoToml = toml::from_slice(&std::fs::read(path)?)?; + cargo_toml.package.version + } + None => self.rustc_version.clone(), + }; + + let release = match self.channel.as_str() { + "stable" => version, + "beta" => "beta".into(), + "nightly" => "nightly".into(), + _ => format!("{}-dev", version), + }; + + self.package_versions.insert(package.clone(), release.clone()); + Ok(release) + } + } + } +} + +#[derive(serde::Deserialize)] +struct CargoToml { + package: CargoTomlPackage, +} + +#[derive(serde::Deserialize)] +struct CargoTomlPackage { + version: String, } -- cgit 1.4.1-3-g733a5 From 389b7ff19074988a3855bbc5ba7b19eb903a6fb4 Mon Sep 17 00:00:00 2001 From: Mark Rousskov Date: Wed, 16 Sep 2020 17:57:20 -0400 Subject: Do not link LLVM tools to LLVM dylib unless rustc is Previously we would have some platforms where LLVM was linked to rustc statically, but to the LLVM tools dynamically. That meant we were distributing two copies of LLVM: one as a separate dylib and one statically linked in to librustc_driver. --- src/bootstrap/dist.rs | 13 +++++-------- src/bootstrap/native.rs | 11 ++++++++--- src/ci/run.sh | 7 ------- 3 files changed, 13 insertions(+), 18 deletions(-) (limited to 'src/bootstrap') diff --git a/src/bootstrap/dist.rs b/src/bootstrap/dist.rs index debe6e36a9b..044507c398f 100644 --- a/src/bootstrap/dist.rs +++ b/src/bootstrap/dist.rs @@ -2401,14 +2401,11 @@ fn maybe_install_llvm(builder: &Builder<'_>, target: TargetSelection, dst_libdir return; } - // On macOS for some reason the llvm-config binary behaves differently and - // and fails on missing .a files if run without --link-shared. If run with - // that option, it still fails, but because we only ship a libLLVM.dylib - // rather than libLLVM-11-rust-....dylib file. - // - // For now just don't use llvm-config here on macOS; that will fail to - // support CI-built LLVM, but until we work out the different behavior that - // is fine as it is off by default. + // On macOS, rustc (and LLVM tools) link to an unversioned libLLVM.dylib + // instead of libLLVM-11-rust-....dylib, as on linux. It's not entirely + // clear why this is the case, though. llvm-config will emit the versioned + // paths and we don't want those in the sysroot (as we're expecting + // unversioned paths). if target.contains("apple-darwin") { let src_libdir = builder.llvm_out(target).join("lib"); let llvm_dylib_path = src_libdir.join("libLLVM.dylib"); diff --git a/src/bootstrap/native.rs b/src/bootstrap/native.rs index 3829d47da33..71b35c0433a 100644 --- a/src/bootstrap/native.rs +++ b/src/bootstrap/native.rs @@ -129,8 +129,10 @@ impl Step for Llvm { Err(m) => m, }; - if builder.config.llvm_link_shared && target.contains("windows") { - panic!("shared linking to LLVM is not currently supported on Windows"); + if builder.config.llvm_link_shared + && (target.contains("windows") || target.contains("apple-darwin")) + { + panic!("shared linking to LLVM is not currently supported on {}", target.triple); } builder.info(&format!("Building LLVM for {}", target)); @@ -209,7 +211,10 @@ impl Step for Llvm { // which saves both memory during parallel links and overall disk space // for the tools. We don't do this on every platform as it doesn't work // equally well everywhere. - if builder.llvm_link_tools_dynamically(target) { + // + // If we're not linking rustc to a dynamic LLVM, though, then don't link + // tools to it. + if builder.llvm_link_tools_dynamically(target) && builder.config.llvm_link_shared { cfg.define("LLVM_LINK_LLVM_DYLIB", "ON"); } diff --git a/src/ci/run.sh b/src/ci/run.sh index c8faf1ec831..5231aa2e766 100755 --- a/src/ci/run.sh +++ b/src/ci/run.sh @@ -75,13 +75,6 @@ if [ "$DEPLOY$DEPLOY_ALT" = "1" ]; then RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set rust.remap-debuginfo" RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --debuginfo-level-std=1" - # If we're distributing binaries, we want a shared LLVM link. We're already - # going to link LLVM to the LLVM tools dynamically, so we need to ship a - # libLLVM library anyway. - if !isWindows; then - RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set llvm.link-shared=true" - fi - if [ "$NO_LLVM_ASSERTIONS" = "1" ]; then RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --disable-llvm-assertions" elif [ "$DEPLOY_ALT" != "" ]; then -- cgit 1.4.1-3-g733a5 From 6d195615872440798ac47ad7a4d8fd090fcbc24a Mon Sep 17 00:00:00 2001 From: Mark Rousskov Date: Sun, 6 Sep 2020 19:57:07 -0400 Subject: Set BUILD_TRIPLE via build script This moves build triple discovery for rustbuild from bootstrap.py into a build script, meaning it will "just work" if building rustbuild via Cargo rather than Python. --- src/bootstrap/Cargo.toml | 1 + src/bootstrap/bootstrap.py | 1 - src/bootstrap/build.rs | 3 +++ src/bootstrap/config.rs | 2 +- 4 files changed, 5 insertions(+), 2 deletions(-) create mode 100644 src/bootstrap/build.rs (limited to 'src/bootstrap') diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml index faec2c53742..890315a744e 100644 --- a/src/bootstrap/Cargo.toml +++ b/src/bootstrap/Cargo.toml @@ -3,6 +3,7 @@ authors = ["The Rust Project Developers"] name = "bootstrap" version = "0.0.0" edition = "2018" +build = "build.rs" [lib] path = "lib.rs" diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index 5f78031e1c7..c9a9a484ee3 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -1032,7 +1032,6 @@ def bootstrap(help_triggered): args = [build.bootstrap_binary()] args.extend(sys.argv[1:]) env = os.environ.copy() - env["BUILD"] = build.build env["SRC"] = build.rust_root env["BOOTSTRAP_PARENT_ID"] = str(os.getpid()) env["BOOTSTRAP_PYTHON"] = sys.executable diff --git a/src/bootstrap/build.rs b/src/bootstrap/build.rs new file mode 100644 index 00000000000..5e5c31de5b6 --- /dev/null +++ b/src/bootstrap/build.rs @@ -0,0 +1,3 @@ +fn main() { + println!("cargo:rustc-env=BUILD_TRIPLE={}", std::env::var("HOST").unwrap()); +} diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index 6de4388495b..1ec99ef30ff 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -487,7 +487,7 @@ impl Config { config.missing_tools = false; // set by bootstrap.py - config.build = TargetSelection::from_user(&env::var("BUILD").expect("'BUILD' to be set")); + config.build = TargetSelection::from_user(&env!("BUILD_TRIPLE")); config.src = Config::path_from_python("SRC"); config.out = Config::path_from_python("BUILD_DIR"); -- cgit 1.4.1-3-g733a5 From e21eb613e09a0ecb4eaa141712a389d05c40ffad Mon Sep 17 00:00:00 2001 From: Mark Rousskov Date: Sun, 6 Sep 2020 21:32:55 -0400 Subject: Remove support for different src directory This requires that bootstrap is run from the same worktree as the sources it'll build, but this is basically required for the build to work anyway. You can still run it from a different directory, just that the files it builds must be beside it. --- src/bootstrap/bootstrap.py | 4 +--- src/bootstrap/config.rs | 4 +++- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'src/bootstrap') diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index c9a9a484ee3..c8cb2595d32 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -966,7 +966,6 @@ def bootstrap(help_triggered): parser = argparse.ArgumentParser(description='Build rust') parser.add_argument('--config') parser.add_argument('--build') - parser.add_argument('--src') parser.add_argument('--clean', action='store_true') parser.add_argument('-v', '--verbose', action='count', default=0) @@ -975,7 +974,7 @@ def bootstrap(help_triggered): # Configure initial bootstrap build = RustBuild() - build.rust_root = args.src or os.path.abspath(os.path.join(__file__, '../../..')) + build.rust_root = os.path.abspath(os.path.join(__file__, '../../..')) build.verbose = args.verbose build.clean = args.clean @@ -1032,7 +1031,6 @@ def bootstrap(help_triggered): args = [build.bootstrap_binary()] args.extend(sys.argv[1:]) env = os.environ.copy() - env["SRC"] = build.rust_root env["BOOTSTRAP_PARENT_ID"] = str(os.getpid()) env["BOOTSTRAP_PYTHON"] = sys.executable env["BUILD_DIR"] = build.build_dir diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index 1ec99ef30ff..3ecfac83564 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -488,7 +488,9 @@ impl Config { // set by bootstrap.py config.build = TargetSelection::from_user(&env!("BUILD_TRIPLE")); - config.src = Config::path_from_python("SRC"); + let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + // Undo `src/bootstrap` + config.src = manifest_dir.parent().unwrap().parent().unwrap().to_owned(); config.out = Config::path_from_python("BUILD_DIR"); config.initial_rustc = Config::path_from_python("RUSTC"); -- cgit 1.4.1-3-g733a5 From a625ab77e87b31ac6689e97f2f10cb09773bed38 Mon Sep 17 00:00:00 2001 From: Mark Rousskov Date: Mon, 7 Sep 2020 12:02:50 -0400 Subject: Discover Rust toolchain without Python --- src/bootstrap/bootstrap.py | 4 ---- src/bootstrap/build.rs | 25 ++++++++++++++++++++++++- src/bootstrap/config.rs | 19 ++++++++++++------- 3 files changed, 36 insertions(+), 12 deletions(-) (limited to 'src/bootstrap') diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index c8cb2595d32..e58cf0d1641 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -1035,12 +1035,8 @@ def bootstrap(help_triggered): env["BOOTSTRAP_PYTHON"] = sys.executable env["BUILD_DIR"] = build.build_dir env["RUSTC_BOOTSTRAP"] = '1' - env["CARGO"] = build.cargo() - env["RUSTC"] = build.rustc() if toml_path: env["BOOTSTRAP_CONFIG"] = toml_path - if build.rustfmt(): - env["RUSTFMT"] = build.rustfmt() run(args, env=env, verbose=build.verbose) diff --git a/src/bootstrap/build.rs b/src/bootstrap/build.rs index 5e5c31de5b6..d40b924e0ff 100644 --- a/src/bootstrap/build.rs +++ b/src/bootstrap/build.rs @@ -1,3 +1,26 @@ +use std::env; +use std::path::PathBuf; + fn main() { - println!("cargo:rustc-env=BUILD_TRIPLE={}", std::env::var("HOST").unwrap()); + println!("cargo:rerun-if-changed=build.rs"); + println!("cargo:rustc-env=BUILD_TRIPLE={}", env::var("HOST").unwrap()); + + // This may not be a canonicalized path. + let mut rustc = PathBuf::from(env::var_os("RUSTC").unwrap()); + + if rustc.is_relative() { + for dir in env::split_paths(&env::var_os("PATH").unwrap_or_default()) { + let absolute = dir.join(&rustc); + if absolute.exists() { + rustc = absolute; + break; + } + } + } + assert!(rustc.is_absolute()); + + // FIXME: if the path is not utf-8, this is going to break. Unfortunately + // Cargo doesn't have a way for us to specify non-utf-8 paths easily, so + // we'll need to invent some encoding scheme if this becomes a problem. + println!("cargo:rustc-env=RUSTC={}", rustc.to_str().unwrap()); } diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index 3ecfac83564..d4f6ce64dec 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -291,7 +291,7 @@ struct Build { build_dir: Option, cargo: Option, rustc: Option, - rustfmt: Option, /* allow bootstrap.py to use rustfmt key */ + rustfmt: Option, docs: Option, compiler_docs: Option, submodules: Option, @@ -493,9 +493,8 @@ impl Config { config.src = manifest_dir.parent().unwrap().parent().unwrap().to_owned(); config.out = Config::path_from_python("BUILD_DIR"); - config.initial_rustc = Config::path_from_python("RUSTC"); - config.initial_cargo = Config::path_from_python("CARGO"); - config.initial_rustfmt = env::var_os("RUSTFMT").map(Config::normalize_python_path); + config.initial_cargo = PathBuf::from(env!("CARGO")); + config.initial_rustc = PathBuf::from(env!("RUSTC")); config } @@ -584,6 +583,9 @@ impl Config { set(&mut config.full_bootstrap, build.full_bootstrap); set(&mut config.extended, build.extended); config.tools = build.tools; + if build.rustfmt.is_some() { + config.initial_rustfmt = build.rustfmt; + } set(&mut config.verbose, build.verbose); set(&mut config.sanitizers, build.sanitizers); set(&mut config.profiler, build.profiler); @@ -838,12 +840,15 @@ impl Config { set(&mut config.missing_tools, t.missing_tools); } + // Cargo does not provide a RUSTFMT environment variable, so we + // synthesize it manually. Note that we also later check the config.toml + // and set this to that path if necessary. + let rustfmt = config.initial_rustc.with_file_name(exe("rustfmt", config.build)); + config.initial_rustfmt = if rustfmt.exists() { Some(rustfmt) } else { None }; + // Now that we've reached the end of our configuration, infer the // default values for all options that we haven't otherwise stored yet. - set(&mut config.initial_rustc, build.rustc.map(PathBuf::from)); - set(&mut config.initial_cargo, build.cargo.map(PathBuf::from)); - config.llvm_skip_rebuild = llvm_skip_rebuild.unwrap_or(false); let default = false; -- cgit 1.4.1-3-g733a5 From c9c8fb88cf1be7e0a6bd6fd049d8d28fb5d86135 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Sat, 12 Sep 2020 00:42:52 -0400 Subject: Add sample defaults for config.toml - Allow including defaults in `src/bootstrap/defaults` using `profile = "..."` - Add default config files - Combine config files using the merge dependency. - Add comments to default config files - Add a README asking to open an issue if the defaults are bad - Give a loud error if trying to merge `.target`, since it's not currently supported - Use an exhaustive match - Use `` in config.toml.example to avoid confusion - Fix bugs in `Merge` derives Previously, it would completely ignore the profile defaults if there were any settings in `config.toml`. I sent an email to the `merge` maintainer asking them to make the behavior in this commit the default. This introduces a new dependency on `merge` that hasn't yet been vetted. I want to improve the output when `include = "x"` isn't found: ``` thread 'main' panicked at 'fs::read_to_string(&file) failed with No such file or directory (os error 2) ("configuration file did not exist")', src/bootstrap/config.rs:522:28 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace failed to run: /home/joshua/rustc/build/bootstrap/debug/bootstrap test tidy Build completed unsuccessfully in 0:00:00 ``` However that seems like it could be fixed in a follow-up. --- Cargo.lock | 23 +++++++++ config.toml.example | 10 ++++ src/bootstrap/Cargo.toml | 1 + src/bootstrap/config.rs | 76 +++++++++++++++++++---------- src/bootstrap/defaults/README.md | 11 +++++ src/bootstrap/defaults/config.toml.codegen | 13 +++++ src/bootstrap/defaults/config.toml.compiler | 8 +++ src/bootstrap/defaults/config.toml.library | 10 ++++ src/bootstrap/defaults/config.toml.user | 9 ++++ 9 files changed, 136 insertions(+), 25 deletions(-) create mode 100644 src/bootstrap/defaults/README.md create mode 100644 src/bootstrap/defaults/config.toml.codegen create mode 100644 src/bootstrap/defaults/config.toml.compiler create mode 100644 src/bootstrap/defaults/config.toml.library create mode 100644 src/bootstrap/defaults/config.toml.user (limited to 'src/bootstrap') diff --git a/Cargo.lock b/Cargo.lock index d3f777bc663..4c55fea30e0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -207,6 +207,7 @@ dependencies = [ "ignore", "lazy_static", "libc", + "merge", "num_cpus", "opener", "pretty_assertions", @@ -1900,6 +1901,28 @@ dependencies = [ "autocfg", ] +[[package]] +name = "merge" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10bbef93abb1da61525bbc45eeaff6473a41907d19f8f9aa5168d214e10693e9" +dependencies = [ + "merge_derive", + "num-traits", +] + +[[package]] +name = "merge_derive" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "209d075476da2e63b4b29e72a2ef627b840589588e71400a25e3565c4f849d07" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "minifier" version = "0.0.33" diff --git a/config.toml.example b/config.toml.example index 99e6f9dceb4..8bf1b48ce83 100644 --- a/config.toml.example +++ b/config.toml.example @@ -9,6 +9,16 @@ # a custom configuration file can also be specified with `--config` to the build # system. +# ============================================================================= +# Global Settings +# ============================================================================= + +# Use different pre-set defaults than the global defaults. +# +# See `src/bootstrap/defaults` for more information. +# Note that this has no default value (x.py uses the defaults in `config.toml.example`). +#profile = + # ============================================================================= # Tweaking how LLVM is compiled # ============================================================================= diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml index faec2c53742..0177a9dab97 100644 --- a/src/bootstrap/Cargo.toml +++ b/src/bootstrap/Cargo.toml @@ -48,6 +48,7 @@ lazy_static = "1.3.0" time = "0.1" ignore = "0.4.10" opener = "0.4" +merge = "0.1.0" [target.'cfg(windows)'.dependencies.winapi] version = "0.3" diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index 7e2cb772186..d925af19a84 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -16,6 +16,7 @@ use crate::flags::Flags; pub use crate::flags::Subcommand; use crate::util::exe; use build_helper::t; +use merge::Merge; use serde::Deserialize; macro_rules! check_ci_llvm { @@ -278,10 +279,31 @@ struct TomlConfig { rust: Option, target: Option>, dist: Option, + profile: Option, +} + +impl Merge for TomlConfig { + fn merge(&mut self, TomlConfig { build, install, llvm, rust, dist, target, profile: _ }: Self) { + fn do_merge(x: &mut Option, y: Option) { + if let Some(new) = y { + if let Some(original) = x { + original.merge(new); + } else { + *x = Some(new); + } + } + }; + do_merge(&mut self.build, build); + do_merge(&mut self.install, install); + do_merge(&mut self.llvm, llvm); + do_merge(&mut self.rust, rust); + do_merge(&mut self.dist, dist); + assert!(target.is_none(), "merging target-specific config is not currently supported"); + } } /// TOML representation of various global build decisions. -#[derive(Deserialize, Default, Clone)] +#[derive(Deserialize, Default, Clone, Merge)] #[serde(deny_unknown_fields, rename_all = "kebab-case")] struct Build { build: Option, @@ -321,7 +343,7 @@ struct Build { } /// TOML representation of various global install decisions. -#[derive(Deserialize, Default, Clone)] +#[derive(Deserialize, Default, Clone, Merge)] #[serde(deny_unknown_fields, rename_all = "kebab-case")] struct Install { prefix: Option, @@ -338,7 +360,7 @@ struct Install { } /// TOML representation of how the LLVM build is configured. -#[derive(Deserialize, Default)] +#[derive(Deserialize, Default, Merge)] #[serde(deny_unknown_fields, rename_all = "kebab-case")] struct Llvm { skip_rebuild: Option, @@ -365,7 +387,7 @@ struct Llvm { download_ci_llvm: Option, } -#[derive(Deserialize, Default, Clone)] +#[derive(Deserialize, Default, Clone, Merge)] #[serde(deny_unknown_fields, rename_all = "kebab-case")] struct Dist { sign_folder: Option, @@ -389,7 +411,7 @@ impl Default for StringOrBool { } /// TOML representation of how the Rust build is configured. -#[derive(Deserialize, Default)] +#[derive(Deserialize, Default, Merge)] #[serde(deny_unknown_fields, rename_all = "kebab-case")] struct Rust { optimize: Option, @@ -434,7 +456,7 @@ struct Rust { } /// TOML representation of how each build target is configured. -#[derive(Deserialize, Default)] +#[derive(Deserialize, Default, Merge)] #[serde(deny_unknown_fields, rename_all = "kebab-case")] struct TomlTarget { cc: Option, @@ -523,27 +545,31 @@ impl Config { } #[cfg(test)] - let toml = TomlConfig::default(); + let get_toml = |_| TomlConfig::default(); #[cfg(not(test))] - let toml = flags - .config - .map(|file| { - use std::process; - - let contents = t!(fs::read_to_string(&file)); - match toml::from_str(&contents) { - Ok(table) => table, - Err(err) => { - println!( - "failed to parse TOML configuration '{}': {}", - file.display(), - err - ); - process::exit(2); - } + let get_toml = |file: PathBuf| { + use std::process; + + let contents = t!(fs::read_to_string(&file), "configuration file did not exist"); + match toml::from_str(&contents) { + Ok(table) => table, + Err(err) => { + println!("failed to parse TOML configuration '{}': {}", file.display(), err); + process::exit(2); } - }) - .unwrap_or_else(TomlConfig::default); + } + }; + + let mut toml = flags.config.map(get_toml).unwrap_or_else(TomlConfig::default); + if let Some(include) = &toml.profile { + let mut include_path = config.src.clone(); + include_path.push("src"); + include_path.push("bootstrap"); + include_path.push("defaults"); + include_path.push(format!("config.toml.{}", include)); + let included_toml = get_toml(include_path); + toml.merge(included_toml); + } let build = toml.build.unwrap_or_default(); diff --git a/src/bootstrap/defaults/README.md b/src/bootstrap/defaults/README.md new file mode 100644 index 00000000000..a91fc3538eb --- /dev/null +++ b/src/bootstrap/defaults/README.md @@ -0,0 +1,11 @@ +# About bootstrap defaults + +These defaults are intended to be a good starting point for working with x.py, +with the understanding that no one set of defaults make sense for everyone. + +They are still experimental, and we'd appreciate your help improving them! +If you use a setting that's not in these defaults that you think others would benefit from, please [file an issue] or make a PR with the changes. +Similarly, if one of these defaults doesn't match what you use personally, +please open an issue to get it changed. + +[file an issue]: https://github.com/rust-lang/rust/issues/new/choose diff --git a/src/bootstrap/defaults/config.toml.codegen b/src/bootstrap/defaults/config.toml.codegen new file mode 100644 index 00000000000..a9505922ca7 --- /dev/null +++ b/src/bootstrap/defaults/config.toml.codegen @@ -0,0 +1,13 @@ +# These defaults are meant for contributors to the compiler who modify codegen or LLVM +[llvm] +# This enables debug-assertions in LLVM, +# catching logic errors in codegen much earlier in the process. +assertions = true + +[rust] +# This enables `RUSTC_LOG=debug`, avoiding confusing situations +# where adding `debug!()` appears to do nothing. +# However, it makes running the compiler slightly slower. +debug-logging = true +# This greatly increases the speed of rebuilds, especially when there are only minor changes. However, it makes the initial build slightly slower. +incremental = true diff --git a/src/bootstrap/defaults/config.toml.compiler b/src/bootstrap/defaults/config.toml.compiler new file mode 100644 index 00000000000..4772de8a2cb --- /dev/null +++ b/src/bootstrap/defaults/config.toml.compiler @@ -0,0 +1,8 @@ +# These defaults are meant for contributors to the compiler who do not modify codegen or LLVM +[rust] +# This enables `RUSTC_LOG=debug`, avoiding confusing situations +# where adding `debug!()` appears to do nothing. +# However, it makes running the compiler slightly slower. +debug-logging = true +# This greatly increases the speed of rebuilds, especially when there are only minor changes. However, it makes the initial build slightly slower. +incremental = true diff --git a/src/bootstrap/defaults/config.toml.library b/src/bootstrap/defaults/config.toml.library new file mode 100644 index 00000000000..e4316f4d864 --- /dev/null +++ b/src/bootstrap/defaults/config.toml.library @@ -0,0 +1,10 @@ +# These defaults are meant for contributors to the standard library and documentation. +[build] +# When building the standard library, you almost never want to build the compiler itself. +build-stage = 0 +test-stage = 0 +bench-stage = 0 + +[rust] +# This greatly increases the speed of rebuilds, especially when there are only minor changes. However, it makes the initial build slightly slower. +incremental = true diff --git a/src/bootstrap/defaults/config.toml.user b/src/bootstrap/defaults/config.toml.user new file mode 100644 index 00000000000..6647061d88f --- /dev/null +++ b/src/bootstrap/defaults/config.toml.user @@ -0,0 +1,9 @@ +# These defaults are meant for users and distro maintainers building from source, without intending to make multiple changes. +[build] +# When compiling from source, you almost always want a full stage 2 build, +# which has all the latest optimizations from nightly. +build-stage = 2 +test-stage = 2 +doc-stage = 2 +# When compiling from source, you usually want all tools. +extended = true -- cgit 1.4.1-3-g733a5 From 1aac99de25d3e28e4b2a16e8fbe6737d5202f27a Mon Sep 17 00:00:00 2001 From: Mark Rousskov Date: Thu, 10 Sep 2020 10:17:32 -0400 Subject: Provide bootstrap tools with RUSTC in environment --- src/bootstrap/test.rs | 2 ++ src/bootstrap/tool.rs | 4 ++++ src/test/run-make/thumb-none-cortex-m/Makefile | 2 +- src/test/run-make/thumb-none-qemu/script.sh | 4 ++-- 4 files changed, 9 insertions(+), 3 deletions(-) (limited to 'src/bootstrap') diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs index dc28b8ece24..a9a8d3783fd 100644 --- a/src/bootstrap/test.rs +++ b/src/bootstrap/test.rs @@ -1281,6 +1281,8 @@ impl Step for Compiletest { cmd.arg("--rustfix-coverage"); } + cmd.env("BOOTSTRAP_CARGO", &builder.initial_cargo); + builder.ci_env.force_coloring_in_ci(&mut cmd); builder.info(&format!( diff --git a/src/bootstrap/tool.rs b/src/bootstrap/tool.rs index 5d66632d92c..460dffb5c8a 100644 --- a/src/bootstrap/tool.rs +++ b/src/bootstrap/tool.rs @@ -700,6 +700,10 @@ impl<'a> Builder<'a> { } add_dylib_path(lib_paths, &mut cmd); + + // Provide a RUSTC for this command to use. + cmd.env("RUSTC", &self.initial_rustc); + cmd } } diff --git a/src/test/run-make/thumb-none-cortex-m/Makefile b/src/test/run-make/thumb-none-cortex-m/Makefile index 36e51bcab6d..13385369e44 100644 --- a/src/test/run-make/thumb-none-cortex-m/Makefile +++ b/src/test/run-make/thumb-none-cortex-m/Makefile @@ -35,4 +35,4 @@ all: # HACK(eddyb) sets `RUSTC_BOOTSTRAP=1` so Cargo can accept nightly features. # These come from the top-level Rust workspace, that this crate is not a # member of, but Cargo tries to load the workspace `Cargo.toml` anyway. - cd $(WORK_DIR) && cd $(CRATE) && env RUSTC_BOOTSTRAP=1 $(CARGO) build --target $(TARGET) -v + cd $(WORK_DIR) && cd $(CRATE) && env RUSTC_BOOTSTRAP=1 $(BOOTSTRAP_CARGO) build --target $(TARGET) -v diff --git a/src/test/run-make/thumb-none-qemu/script.sh b/src/test/run-make/thumb-none-qemu/script.sh index c5cbff5c3c3..045d02a8ed2 100644 --- a/src/test/run-make/thumb-none-qemu/script.sh +++ b/src/test/run-make/thumb-none-qemu/script.sh @@ -12,8 +12,8 @@ pushd $WORK_DIR # These come from the top-level Rust workspace, that this crate is not a # member of, but Cargo tries to load the workspace `Cargo.toml` anyway. env RUSTC_BOOTSTRAP=1 RUSTFLAGS="-C linker=arm-none-eabi-ld -C link-arg=-Tlink.x" \ - $CARGO run --target $TARGET | grep "x = 42" + $BOOTSTRAP_CARGO run --target $TARGET | grep "x = 42" env RUSTC_BOOTSTRAP=1 RUSTFLAGS="-C linker=arm-none-eabi-ld -C link-arg=-Tlink.x" \ - $CARGO run --target $TARGET --release | grep "x = 42" + $BOOTSTRAP_CARGO run --target $TARGET --release | grep "x = 42" popd popd -- cgit 1.4.1-3-g733a5 From cf33aad8fdd3885fbc26518aaccff0788d1a21a7 Mon Sep 17 00:00:00 2001 From: Mark Rousskov Date: Thu, 10 Sep 2020 10:52:27 -0400 Subject: Specify output directory for bootstrap tests --- src/bootstrap/builder/tests.rs | 3 +++ src/bootstrap/test.rs | 2 ++ 2 files changed, 5 insertions(+) (limited to 'src/bootstrap') diff --git a/src/bootstrap/builder/tests.rs b/src/bootstrap/builder/tests.rs index f96925f9270..cd90021507e 100644 --- a/src/bootstrap/builder/tests.rs +++ b/src/bootstrap/builder/tests.rs @@ -10,6 +10,9 @@ fn configure(cmd: &str, host: &[&str], target: &[&str]) -> Config { config.dry_run = true; config.ninja_in_file = false; // try to avoid spurious failures in dist where we create/delete each others file + config.out = PathBuf::from(env::var_os("BOOTSTRAP_OUTPUT_DIRECTORY").unwrap()); + config.initial_rustc = PathBuf::from(env::var_os("RUSTC").unwrap()); + config.initial_cargo = PathBuf::from(env::var_os("BOOTSTRAP_INITIAL_CARGO").unwrap()); let dir = config .out .join("tmp-rustbuild-tests") diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs index a9a8d3783fd..00522ee6b67 100644 --- a/src/bootstrap/test.rs +++ b/src/bootstrap/test.rs @@ -2024,6 +2024,8 @@ impl Step for Bootstrap { .current_dir(builder.src.join("src/bootstrap")) .env("RUSTFLAGS", "-Cdebuginfo=2") .env("CARGO_TARGET_DIR", builder.out.join("bootstrap")) + .env("BOOTSTRAP_OUTPUT_DIRECTORY", &builder.config.out) + .env("BOOTSTRAP_INITIAL_CARGO", &builder.config.initial_cargo) .env("RUSTC_BOOTSTRAP", "1") .env("RUSTC", &builder.initial_rustc); if let Some(flags) = option_env!("RUSTFLAGS") { -- cgit 1.4.1-3-g733a5 From fe6fc555acd51bd7ba8755d9fbc7060feb67be25 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Fri, 11 Sep 2020 22:55:16 -0400 Subject: Add a changelog for x.py - Add a changelog and instructions for updating it - Use `changelog-seen` in `config.toml` and `VERSION` in bootstrap to determine whether the changelog has been read - Nag people if they haven't read the x.py changelog + Print message twice to make sure it's seen - Give different error messages depending on whether the version needs to be updated or added --- config.toml.example | 6 ++++++ src/bootstrap/CHANGELOG.md | 33 +++++++++++++++++++++++++++++++++ src/bootstrap/README.md | 14 ++++++++++++++ src/bootstrap/bin/main.rs | 35 +++++++++++++++++++++++++++++++++++ src/bootstrap/config.rs | 9 ++++++++- 5 files changed, 96 insertions(+), 1 deletion(-) create mode 100644 src/bootstrap/CHANGELOG.md (limited to 'src/bootstrap') diff --git a/config.toml.example b/config.toml.example index 8bf1b48ce83..fb62e1b4726 100644 --- a/config.toml.example +++ b/config.toml.example @@ -9,6 +9,12 @@ # a custom configuration file can also be specified with `--config` to the build # system. +# Keeps track of the last version of `x.py` used. +# If it does not match the version that is currently running, +# `x.py` will prompt you to update it and read the changelog. +# See `src/bootstrap/CHANGELOG.md` for more information. +changelog-seen = 1 + # ============================================================================= # Global Settings # ============================================================================= diff --git a/src/bootstrap/CHANGELOG.md b/src/bootstrap/CHANGELOG.md new file mode 100644 index 00000000000..5fcaafab959 --- /dev/null +++ b/src/bootstrap/CHANGELOG.md @@ -0,0 +1,33 @@ +# Changelog + +All notable changes to bootstrap will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). + +## [Non-breaking changes since the last major version] + +- Add a changelog for x.py [#76626](https://github.com/rust-lang/rust/pull/76626) +- Optionally, download LLVM from CI on Linux and NixOS + + [#76439](https://github.com/rust-lang/rust/pull/76349) + + [#76667](https://github.com/rust-lang/rust/pull/76667) + + [#76708](https://github.com/rust-lang/rust/pull/76708) +- Distribute rustc sources as part of `rustc-dev` [#76856](https://github.com/rust-lang/rust/pull/76856) +- Make the default stage for x.py configurable [#76625](https://github.com/rust-lang/rust/pull/76625) +- Add a dedicated debug-logging option [#76588](https://github.com/rust-lang/rust/pull/76588) +- Add sample defaults for x.py [#76628](https://github.com/rust-lang/rust/pull/76628) + +## [Version 0] - 2020-09-11 + +This is the first changelog entry, and it does not attempt to be an exhaustive list of features in x.py. +Instead, this documents the changes to bootstrap in the past 2 months. + +- Improve defaults in `x.py` [#73964](https://github.com/rust-lang/rust/pull/73964) + (see [blog post] for details) +- Set `ninja = true` by default [#74922](https://github.com/rust-lang/rust/pull/74922) +- Avoid trying to inversely cross-compile for build triple from host triples [#76415](https://github.com/rust-lang/rust/pull/76415) +- Allow blessing expect-tests in tools [#75975](https://github.com/rust-lang/rust/pull/75975) +- `x.py check` checks tests/examples/benches [#76258](https://github.com/rust-lang/rust/pull/76258) +- Fix `rust.use-lld` when linker is not set [#76326](https://github.com/rust-lang/rust/pull/76326) +- Build tests with LLD if `use-lld = true` was passed [#76378](https://github.com/rust-lang/rust/pull/76378) + +[blog post]: https://blog.rust-lang.org/inside-rust/2020/08/30/changes-to-x-py-defaults.html diff --git a/src/bootstrap/README.md b/src/bootstrap/README.md index a69bd1cc3bc..bc8bae14b21 100644 --- a/src/bootstrap/README.md +++ b/src/bootstrap/README.md @@ -313,6 +313,20 @@ are: `Config` struct. * Adding a sanity check? Take a look at `bootstrap/sanity.rs`. +If you make a major change, please remember to: + ++ Update `VERSION` in `src/bootstrap/main.rs`. +* Update `changelog-seen = N` in `config.toml.example`. +* Add an entry in `src/bootstrap/CHANGELOG.md`. + +A 'major change' includes + +* A new option or +* A change in the default options. + +Changes that do not affect contributors to the compiler or users +building rustc from source don't need an update to `VERSION`. + If you have any questions feel free to reach out on the `#t-infra` channel in the [Rust Zulip server][rust-zulip] or ask on internals.rust-lang.org. When you encounter bugs, please file issues on the rust-lang/rust issue tracker. diff --git a/src/bootstrap/bin/main.rs b/src/bootstrap/bin/main.rs index b67486c9628..f7512aa9fce 100644 --- a/src/bootstrap/bin/main.rs +++ b/src/bootstrap/bin/main.rs @@ -12,5 +12,40 @@ use bootstrap::{Build, Config}; fn main() { let args = env::args().skip(1).collect::>(); let config = Config::parse(&args); + + let changelog_suggestion = check_version(&config); + if let Some(suggestion) = &changelog_suggestion { + println!("{}", suggestion); + } + Build::new(config).build(); + + if let Some(suggestion) = changelog_suggestion { + println!("{}", suggestion); + println!("note: this message was printed twice to make it more likely to be seen"); + } +} + +fn check_version(config: &Config) -> Option { + const VERSION: usize = 1; + + let mut msg = String::new(); + + let suggestion = if let Some(seen) = config.changelog_seen { + if seen != VERSION { + msg.push_str("warning: there have been changes to x.py since you last updated.\n"); + format!("update `config.toml` to use `changelog-seen = {}` instead", VERSION) + } else { + return None; + } + } else { + msg.push_str("warning: x.py has made several changes recently you may want to look at\n"); + format!("add `changelog-seen = {}` to `config.toml`", VERSION) + }; + + msg.push_str("help: consider looking at the changes in `src/bootstrap/CHANGELOG.md`\n"); + msg.push_str("note: to silence this warning, "); + msg.push_str(&suggestion); + + Some(msg) } diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index 801ed5ac6ee..c74501979f0 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -42,6 +42,7 @@ macro_rules! check_ci_llvm { /// `config.toml.example`. #[derive(Default)] pub struct Config { + pub changelog_seen: Option, pub ccache: Option, /// Call Build::ninja() instead of this. pub ninja_in_file: bool, @@ -273,6 +274,7 @@ impl Target { #[derive(Deserialize, Default)] #[serde(deny_unknown_fields, rename_all = "kebab-case")] struct TomlConfig { + changelog_seen: Option, build: Option, install: Option, llvm: Option, @@ -283,7 +285,10 @@ struct TomlConfig { } impl Merge for TomlConfig { - fn merge(&mut self, TomlConfig { build, install, llvm, rust, dist, target, profile: _ }: Self) { + fn merge( + &mut self, + TomlConfig { build, install, llvm, rust, dist, target, profile: _, changelog_seen: _ }: Self, + ) { fn do_merge(x: &mut Option, y: Option) { if let Some(new) = y { if let Some(original) = x { @@ -572,6 +577,8 @@ impl Config { toml.merge(included_toml); } + config.changelog_seen = toml.changelog_seen; + let build = toml.build.unwrap_or_default(); // If --target was specified but --host wasn't specified, don't run any host-only tests. -- cgit 1.4.1-3-g733a5 From 9f27f3796d3487411ab035803a0757d69040649c Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Tue, 22 Sep 2020 17:20:15 -0700 Subject: Include libunwind in the rust-src component. --- src/bootstrap/dist.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/bootstrap') diff --git a/src/bootstrap/dist.rs b/src/bootstrap/dist.rs index cf73e570fa5..55ca25ffe06 100644 --- a/src/bootstrap/dist.rs +++ b/src/bootstrap/dist.rs @@ -1022,7 +1022,7 @@ impl Step for Src { copy_src_dirs( builder, &builder.src, - &["library"], + &["library", "src/llvm-project/libunwind"], &[ // not needed and contains symlinks which rustup currently // chokes on when unpacking. -- cgit 1.4.1-3-g733a5 From b5d47bfa3aafb8fcb8c60d93fea45207c7ea5c7b Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Wed, 23 Sep 2020 15:45:35 +0200 Subject: clarify that `changelog-seen = 1` goes to the beginning of config.toml Fixes #77105 --- src/bootstrap/bin/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/bootstrap') diff --git a/src/bootstrap/bin/main.rs b/src/bootstrap/bin/main.rs index f7512aa9fce..6af13fc83d0 100644 --- a/src/bootstrap/bin/main.rs +++ b/src/bootstrap/bin/main.rs @@ -40,7 +40,7 @@ fn check_version(config: &Config) -> Option { } } else { msg.push_str("warning: x.py has made several changes recently you may want to look at\n"); - format!("add `changelog-seen = {}` to `config.toml`", VERSION) + format!("add `changelog-seen = {}` at the top of `config.toml`", VERSION) }; msg.push_str("help: consider looking at the changes in `src/bootstrap/CHANGELOG.md`\n"); -- cgit 1.4.1-3-g733a5 From bcbd2ccc8d4591705fd5b63a5d5f5fc320cd89be Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Wed, 23 Sep 2020 14:25:23 -0700 Subject: Add `keep-stage-std` to `x.py` This keeps only the `std` artifacts compiled by the given stage, not the compiler. This is useful when working on the latter stages of the compiler in tandem with the standard library, since you don't have to rebuild the *entire* compiler when the standard library changes. --- src/bootstrap/compile.rs | 9 ++++++++- src/bootstrap/config.rs | 2 ++ src/bootstrap/flags.rs | 17 ++++++++++++++++- 3 files changed, 26 insertions(+), 2 deletions(-) (limited to 'src/bootstrap') diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs index 08907edef1d..02095c27335 100644 --- a/src/bootstrap/compile.rs +++ b/src/bootstrap/compile.rs @@ -59,7 +59,9 @@ impl Step for Std { let target = self.target; let compiler = self.compiler; - if builder.config.keep_stage.contains(&compiler.stage) { + if builder.config.keep_stage.contains(&compiler.stage) + || builder.config.keep_stage_std.contains(&compiler.stage) + { builder.info("Warning: Using a potentially old libstd. This may not behave well."); builder.ensure(StdLink { compiler, target_compiler: compiler, target }); return; @@ -472,6 +474,11 @@ impl Step for Rustc { if builder.config.keep_stage.contains(&compiler.stage) { builder.info("Warning: Using a potentially old librustc. This may not behave well."); + builder.info("Warning: Use `--keep-stage-std` if you want to rebuild the compiler when it changes"); + builder.info( + "Warning: Please file a GitHub issue if `--keep-stage-std` doesn't work for you.", + ); + builder.info("Warning: It may replace `--keep-stage` in the future"); builder.ensure(RustcLink { compiler, target_compiler: compiler, target }); return; } diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index c74501979f0..53fef7a838d 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -71,6 +71,7 @@ pub struct Config { pub on_fail: Option, pub stage: u32, pub keep_stage: Vec, + pub keep_stage_std: Vec, pub src: PathBuf, pub jobs: Option, pub cmd: Subcommand, @@ -539,6 +540,7 @@ impl Config { config.incremental = flags.incremental; config.dry_run = flags.dry_run; config.keep_stage = flags.keep_stage; + config.keep_stage_std = flags.keep_stage_std; config.bindir = "bin".into(); // default if let Some(value) = flags.deny_warnings { config.deny_warnings = value; diff --git a/src/bootstrap/flags.rs b/src/bootstrap/flags.rs index 842c84a3e5c..dad31fc77be 100644 --- a/src/bootstrap/flags.rs +++ b/src/bootstrap/flags.rs @@ -19,6 +19,7 @@ pub struct Flags { pub on_fail: Option, pub stage: Option, pub keep_stage: Vec, + pub keep_stage_std: Vec, pub host: Option>, pub target: Option>, @@ -144,6 +145,13 @@ To learn more about a subcommand, run `./x.py -h`", (pass multiple times to keep e.g., both stages 0 and 1)", "N", ); + opts.optmulti( + "", + "keep-stage-std", + "stage(s) of the standard library to keep without recompiling \ + (pass multiple times to keep e.g., both stages 0 and 1)", + "N", + ); opts.optopt("", "src", "path to the root of the rust checkout", "DIR"); let j_msg = format!( "number of jobs to run in parallel; \ @@ -510,7 +518,9 @@ Arguments: println!("--stage not supported for x.py check, always treated as stage 0"); process::exit(1); } - if matches.opt_str("keep-stage").is_some() { + if matches.opt_str("keep-stage").is_some() + || matches.opt_str("keep-stage-std").is_some() + { println!("--keep-stage not supported for x.py check, only one stage available"); process::exit(1); } @@ -528,6 +538,11 @@ Arguments: .into_iter() .map(|j| j.parse().expect("`keep-stage` should be a number")) .collect(), + keep_stage_std: matches + .opt_strs("keep-stage-std") + .into_iter() + .map(|j| j.parse().expect("`keep-stage-std` should be a number")) + .collect(), host: if matches.opt_present("host") { Some( split(&matches.opt_strs("host")) -- cgit 1.4.1-3-g733a5 From df004df3a79b70b4af2b8c267457a5be76bb0d85 Mon Sep 17 00:00:00 2001 From: Mark Rousskov Date: Wed, 23 Sep 2020 19:03:52 -0400 Subject: Re-download LLVM on submodule updates only --- src/bootstrap/bootstrap.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'src/bootstrap') diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index 1f0b55a7869..5c9184f4506 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -424,8 +424,19 @@ class RustBuild(object): rustfmt_stamp.write(self.date + self.rustfmt_channel) if self.downloading_llvm(): - llvm_sha = subprocess.check_output(["git", "log", "--author=bors", - "--format=%H", "-n1"]).decode(sys.getdefaultencoding()).strip() + # We want the most recent LLVM submodule update to avoid downloading + # LLVM more often than necessary. + # + # This git command finds that commit SHA, looking for bors-authored + # merges that modified src/llvm-project. + # + # This works even in a repository that has not yet initialized + # submodules. + llvm_sha = subprocess.check_output([ + "git", "log", "--author=bors", "--format=%H", "-n1", + "-m", "--first-parent", + "--", "src/llvm-project" + ]).decode(sys.getdefaultencoding()).strip() llvm_assertions = self.get_toml('assertions', 'llvm') == 'true' if self.program_out_of_date(self.llvm_stamp(), llvm_sha + str(llvm_assertions)): self._download_ci_llvm(llvm_sha, llvm_assertions) -- cgit 1.4.1-3-g733a5 From c0ddaed2bfa95d488db7576055ee7472968babfd Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Wed, 23 Sep 2020 16:17:11 -0700 Subject: Remove warning about possible future deprecation --- src/bootstrap/compile.rs | 4 ---- 1 file changed, 4 deletions(-) (limited to 'src/bootstrap') diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs index 02095c27335..40bf6c48296 100644 --- a/src/bootstrap/compile.rs +++ b/src/bootstrap/compile.rs @@ -475,10 +475,6 @@ impl Step for Rustc { if builder.config.keep_stage.contains(&compiler.stage) { builder.info("Warning: Using a potentially old librustc. This may not behave well."); builder.info("Warning: Use `--keep-stage-std` if you want to rebuild the compiler when it changes"); - builder.info( - "Warning: Please file a GitHub issue if `--keep-stage-std` doesn't work for you.", - ); - builder.info("Warning: It may replace `--keep-stage` in the future"); builder.ensure(RustcLink { compiler, target_compiler: compiler, target }); return; } -- cgit 1.4.1-3-g733a5 From 16769eb19e8051a32a93c1b3fd7847aff5c9eded Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Wed, 23 Sep 2020 16:22:53 -0700 Subject: Add entry to CHANGELOG for `--keep-stage-std` --- src/bootstrap/CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/bootstrap') diff --git a/src/bootstrap/CHANGELOG.md b/src/bootstrap/CHANGELOG.md index 5fcaafab959..1510f4d59fa 100644 --- a/src/bootstrap/CHANGELOG.md +++ b/src/bootstrap/CHANGELOG.md @@ -15,6 +15,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Make the default stage for x.py configurable [#76625](https://github.com/rust-lang/rust/pull/76625) - Add a dedicated debug-logging option [#76588](https://github.com/rust-lang/rust/pull/76588) - Add sample defaults for x.py [#76628](https://github.com/rust-lang/rust/pull/76628) +- Add `--keep-stage-std`, which behaves like `keep-stage` but allows the stage + 0 compiler artifacts (i.e., stage1/bin/rustc) to be rebuilt if changed + [#77120](https://github.com/rust-lang/rust/pull/77120). + ## [Version 0] - 2020-09-11 -- cgit 1.4.1-3-g733a5 From 4de836e2141c418d37e556bc80c4ad0127d74b71 Mon Sep 17 00:00:00 2001 From: Mark Rousskov Date: Thu, 24 Sep 2020 09:37:23 -0400 Subject: Install std for non-host targets --- src/bootstrap/install.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/bootstrap') diff --git a/src/bootstrap/install.rs b/src/bootstrap/install.rs index d9ee3bc90fb..074f5cd73f3 100644 --- a/src/bootstrap/install.rs +++ b/src/bootstrap/install.rs @@ -192,7 +192,7 @@ install!((self, builder, _config), builder.ensure(dist::Docs { host: self.target }); install_docs(builder, self.compiler.stage, self.target); }; - Std, "library/std", true, only_hosts: true, { + Std, "library/std", true, only_hosts: false, { for target in &builder.targets { builder.ensure(dist::Std { compiler: self.compiler, -- cgit 1.4.1-3-g733a5 From 9baa601afd50f57655be9eb7e5e2892c2ea9f005 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Sat, 12 Sep 2020 02:32:43 -0400 Subject: Add `x.py setup` - Suggest `x.py setup` if config.toml doesn't exist yet (twice, once before and once after the build) - Prompt for a profile if not given on the command line - Print the configuration file that will be used - Print helpful starting commands after setup - Link to the dev-guide after finishing - Note that distro maintainers will see the changelog warning --- src/bootstrap/CHANGELOG.md | 1 + src/bootstrap/bin/main.rs | 19 ++++++++-- src/bootstrap/builder.rs | 4 ++- src/bootstrap/config.rs | 7 +++- src/bootstrap/flags.rs | 32 ++++++++++++++++- src/bootstrap/lib.rs | 7 +++- src/bootstrap/run.rs | 2 +- src/bootstrap/setup.rs | 88 ++++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 152 insertions(+), 8 deletions(-) create mode 100644 src/bootstrap/setup.rs (limited to 'src/bootstrap') diff --git a/src/bootstrap/CHANGELOG.md b/src/bootstrap/CHANGELOG.md index 5fcaafab959..aa398f25020 100644 --- a/src/bootstrap/CHANGELOG.md +++ b/src/bootstrap/CHANGELOG.md @@ -6,6 +6,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## [Non-breaking changes since the last major version] +- Add `x.py setup` [#76631](https://github.com/rust-lang/rust/pull/76631) - Add a changelog for x.py [#76626](https://github.com/rust-lang/rust/pull/76626) - Optionally, download LLVM from CI on Linux and NixOS + [#76439](https://github.com/rust-lang/rust/pull/76349) diff --git a/src/bootstrap/bin/main.rs b/src/bootstrap/bin/main.rs index f7512aa9fce..669dd7a33de 100644 --- a/src/bootstrap/bin/main.rs +++ b/src/bootstrap/bin/main.rs @@ -7,21 +7,34 @@ use std::env; -use bootstrap::{Build, Config}; +use bootstrap::{Build, Config, Subcommand}; fn main() { let args = env::args().skip(1).collect::>(); let config = Config::parse(&args); let changelog_suggestion = check_version(&config); - if let Some(suggestion) = &changelog_suggestion { + + // NOTE: Since `./configure` generates a `config.toml`, distro maintainers will see the + // changelog warning, not the `x.py setup` message. + let suggest_setup = !config.config.exists() && !matches!(config.cmd, Subcommand::Setup { .. }); + if suggest_setup { + println!("warning: you have not made a `config.toml`"); + println!("help: consider running `x.py setup` or copying `config.toml.example`"); + } else if let Some(suggestion) = &changelog_suggestion { println!("{}", suggestion); } Build::new(config).build(); - if let Some(suggestion) = changelog_suggestion { + if suggest_setup { + println!("warning: you have not made a `config.toml`"); + println!("help: consider running `x.py setup` or copying `config.toml.example`"); + } else if let Some(suggestion) = &changelog_suggestion { println!("{}", suggestion); + } + + if suggest_setup || changelog_suggestion.is_some() { println!("note: this message was printed twice to make it more likely to be seen"); } } diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index d2537d65e67..4aaaeb8a93b 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -549,7 +549,9 @@ impl<'a> Builder<'a> { Subcommand::Dist { ref paths } => (Kind::Dist, &paths[..]), Subcommand::Install { ref paths } => (Kind::Install, &paths[..]), Subcommand::Run { ref paths } => (Kind::Run, &paths[..]), - Subcommand::Format { .. } | Subcommand::Clean { .. } => panic!(), + Subcommand::Format { .. } | Subcommand::Clean { .. } | Subcommand::Setup { .. } => { + panic!() + } }; Self::new_internal(build, kind, paths.to_owned()) diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index c74501979f0..46a9d989652 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -72,6 +72,8 @@ pub struct Config { pub stage: u32, pub keep_stage: Vec, pub src: PathBuf, + // defaults to `config.toml` + pub config: PathBuf, pub jobs: Option, pub cmd: Subcommand, pub incremental: bool, @@ -512,6 +514,7 @@ impl Config { config.rust_codegen_backends = vec![INTERNER.intern_str("llvm")]; config.deny_warnings = true; config.missing_tools = false; + config.config = PathBuf::from("config.toml"); // set by bootstrap.py config.build = TargetSelection::from_user(&env!("BUILD_TRIPLE")); @@ -556,7 +559,7 @@ impl Config { let get_toml = |file: PathBuf| { use std::process; - let contents = t!(fs::read_to_string(&file), "configuration file did not exist"); + let contents = t!(fs::read_to_string(&file), "`include` config not found"); match toml::from_str(&contents) { Ok(table) => table, Err(err) => { @@ -642,6 +645,7 @@ impl Config { | Subcommand::Clippy { .. } | Subcommand::Fix { .. } | Subcommand::Run { .. } + | Subcommand::Setup { .. } | Subcommand::Format { .. } => flags.stage.unwrap_or(0), }; @@ -666,6 +670,7 @@ impl Config { | Subcommand::Clippy { .. } | Subcommand::Fix { .. } | Subcommand::Run { .. } + | Subcommand::Setup { .. } | Subcommand::Format { .. } => {} } } diff --git a/src/bootstrap/flags.rs b/src/bootstrap/flags.rs index 842c84a3e5c..643edeb8321 100644 --- a/src/bootstrap/flags.rs +++ b/src/bootstrap/flags.rs @@ -7,6 +7,7 @@ use std::env; use std::path::PathBuf; use std::process; +use build_helper::t; use getopts::Options; use crate::builder::Builder; @@ -88,6 +89,9 @@ pub enum Subcommand { Run { paths: Vec, }, + Setup { + path: String, + }, } impl Default for Subcommand { @@ -191,6 +195,7 @@ To learn more about a subcommand, run `./x.py -h`", || (s == "install") || (s == "run") || (s == "r") + || (s == "setup") }); let subcommand = match subcommand { Some(s) => s, @@ -445,10 +450,21 @@ Arguments: At least a tool needs to be called.", ); } + "setup" => { + subcommand_help.push_str( + "\n +Arguments: + This subcommand accepts a 'profile' to use for builds. For example: + + ./x.py setup library + + The profile is optional and you will be prompted interactively if it is not given.", + ); + } _ => {} }; // Get any optional paths which occur after the subcommand - let paths = matches.free[1..].iter().map(|p| p.into()).collect::>(); + let mut paths = matches.free[1..].iter().map(|p| p.into()).collect::>(); let cfg_file = env::var_os("BOOTSTRAP_CONFIG").map(PathBuf::from); let verbose = matches.opt_present("verbose"); @@ -500,6 +516,20 @@ Arguments: } Subcommand::Run { paths } } + "setup" => { + let path = if paths.len() > 1 { + println!("\nat most one profile can be passed to setup\n"); + usage(1, &opts, verbose, &subcommand_help) + } else if let Some(path) = paths.pop() { + t!(path.into_os_string().into_string().map_err(|path| format!( + "{} is not a valid UTF8 string", + path.to_string_lossy() + ))) + } else { + t!(crate::setup::interactive_path()) + }; + Subcommand::Setup { path } + } _ => { usage(1, &opts, verbose, &subcommand_help); } diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs index 3f7aeae0ed4..4cc72f5f39c 100644 --- a/src/bootstrap/lib.rs +++ b/src/bootstrap/lib.rs @@ -141,6 +141,7 @@ mod metadata; mod native; mod run; mod sanity; +mod setup; mod test; mod tool; mod toolstate; @@ -165,7 +166,7 @@ mod job { use crate::cache::{Interned, INTERNER}; pub use crate::config::Config; -use crate::flags::Subcommand; +pub use crate::flags::Subcommand; const LLVM_TOOLS: &[&str] = &[ "llvm-nm", // used to inspect binaries; it shows symbol names, their sizes and visibility @@ -470,6 +471,10 @@ impl Build { return clean::clean(self, all); } + if let Subcommand::Setup { path: include_name } = &self.config.cmd { + return setup::setup(&self.config.src, include_name); + } + { let builder = builder::Builder::new(&self); if let Some(path) = builder.paths.get(0) { diff --git a/src/bootstrap/run.rs b/src/bootstrap/run.rs index 90053471427..ba593cadbad 100644 --- a/src/bootstrap/run.rs +++ b/src/bootstrap/run.rs @@ -10,7 +10,7 @@ impl Step for ExpandYamlAnchors { /// Runs the `expand-yaml_anchors` tool. /// - /// This tool in `src/tools` read the CI configuration files written in YAML and expands the + /// This tool in `src/tools` reads the CI configuration files written in YAML and expands the /// anchors in them, since GitHub Actions doesn't support them. fn run(self, builder: &Builder<'_>) { builder.info("Expanding YAML anchors in the GitHub Actions configuration"); diff --git a/src/bootstrap/setup.rs b/src/bootstrap/setup.rs new file mode 100644 index 00000000000..9d3a889aa00 --- /dev/null +++ b/src/bootstrap/setup.rs @@ -0,0 +1,88 @@ +use crate::t; +use std::path::{Path, PathBuf}; +use std::{ + env, fs, + io::{self, Write}, +}; + +pub fn setup(src_path: &Path, include_name: &str) { + let cfg_file = env::var_os("BOOTSTRAP_CONFIG").map(PathBuf::from); + + if cfg_file.as_ref().map_or(false, |f| f.exists()) { + let file = cfg_file.unwrap(); + println!( + "error: you asked `x.py` to setup a new config file, but one already exists at `{}`", + file.display() + ); + println!( + "help: try adding `profile = \"{}\"` at the top of {}", + include_name, + file.display() + ); + println!( + "note: this will use the configuration in {}/src/bootstrap/defaults/config.toml.{}", + src_path.display(), + include_name + ); + std::process::exit(1); + } + + let path = cfg_file.unwrap_or_else(|| src_path.join("config.toml")); + let settings = format!( + "# Includes one of the default files in src/bootstrap/defaults\n\ + profile = \"{}\"\n", + include_name + ); + t!(fs::write(path, settings)); + + let include_path = + format!("{}/src/bootstrap/defaults/config.toml.{}", src_path.display(), include_name); + println!("`x.py` will now use the configuration at {}", include_path); + + let suggestions = match include_name { + "codegen" | "compiler" => &["check", "build", "test"][..], + "library" => &["check", "build", "test library/std", "doc"], + "user" => &["dist", "build"], + _ => return, + }; + + println!("To get started, try one of the following commands:"); + for cmd in suggestions { + println!("- `x.py {}`", cmd); + } + + if include_name != "user" { + println!( + "For more suggestions, see https://rustc-dev-guide.rust-lang.org/building/suggested.html" + ); + } +} + +// Used to get the path for `Subcommand::Setup` +pub fn interactive_path() -> io::Result { + let mut input = String::new(); + println!( + "Welcome to the Rust project! What do you want to do with x.py? +a) Contribute to the standard library +b) Contribute to the compiler +c) Contribute to the compiler, and also modify LLVM or codegen +d) Install Rust from source" + ); + let template = loop { + print!("Please choose one (a/b/c/d): "); + io::stdout().flush()?; + io::stdin().read_line(&mut input)?; + break match input.trim().to_lowercase().as_str() { + "a" | "lib" | "library" => "library", + "b" | "compiler" => "compiler", + "c" | "llvm" => "llvm", + "d" | "user" | "maintainer" => "maintainer", + _ => { + println!("error: unrecognized option '{}'", input.trim()); + println!("note: press Ctrl+C to exit"); + continue; + } + }; + }; + Ok(template.to_owned()) +} -- cgit 1.4.1-3-g733a5 From 159d11fb069fca88056bc1b8194d520489e3e921 Mon Sep 17 00:00:00 2001 From: Raoul Strackx Date: Tue, 31 Mar 2020 11:28:01 +0200 Subject: Patch compilation test helpers for sgx platform --- src/bootstrap/native.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'src/bootstrap') diff --git a/src/bootstrap/native.rs b/src/bootstrap/native.rs index 9e4d6d0023d..1e233cb3891 100644 --- a/src/bootstrap/native.rs +++ b/src/bootstrap/native.rs @@ -625,7 +625,14 @@ impl Step for TestHelpers { if builder.config.dry_run { return; } - let target = self.target; + // The x86_64-fortanix-unknown-sgx target doesn't have a working C + // toolchain. However, some x86_64 ELF objects can be linked + // without issues. Use this hack to compile the test helpers. + let target = if self.target == "x86_64-fortanix-unknown-sgx" { + TargetSelection::from_user("x86_64-unknown-linux-gnu") + } else { + self.target + }; let dst = builder.test_helpers_out(target); let src = builder.src.join("src/test/auxiliary/rust_test_helpers.c"); if up_to_date(&src, &dst.join("librust_test_helpers.a")) { @@ -649,7 +656,6 @@ impl Step for TestHelpers { } cfg.compiler(builder.cc(target)); } - cfg.cargo_metadata(false) .out_dir(&dst) .target(&target.triple) -- cgit 1.4.1-3-g733a5 From 58c232af6a86bfcde605ec4f2302af52f1cac139 Mon Sep 17 00:00:00 2001 From: Tshepang Lekhonkhobe Date: Sun, 27 Sep 2020 04:22:55 +0200 Subject: reduce overlong line --- src/bootstrap/defaults/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/bootstrap') diff --git a/src/bootstrap/defaults/README.md b/src/bootstrap/defaults/README.md index a91fc3538eb..f5b96db1b0f 100644 --- a/src/bootstrap/defaults/README.md +++ b/src/bootstrap/defaults/README.md @@ -4,7 +4,8 @@ These defaults are intended to be a good starting point for working with x.py, with the understanding that no one set of defaults make sense for everyone. They are still experimental, and we'd appreciate your help improving them! -If you use a setting that's not in these defaults that you think others would benefit from, please [file an issue] or make a PR with the changes. +If you use a setting that's not in these defaults that you think +others would benefit from, please [file an issue] or make a PR with the changes. Similarly, if one of these defaults doesn't match what you use personally, please open an issue to get it changed. -- cgit 1.4.1-3-g733a5 From d537067c97531af7f8343f75fafb4d6b9e79eaaf Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Mon, 28 Sep 2020 10:28:15 -0400 Subject: Don't warn if the config file is somewhere other than `config.toml` Previously, `config.config` was always hardcoded as `"config.toml"`. I thought that it was being overridden with the actual value later, but it turns out `flags.config` was being completely discarded. This keeps `config.config` in sync with `flags.config`. --- src/bootstrap/config.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'src/bootstrap') diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index b14746dabb9..93bde400f06 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -515,7 +515,6 @@ impl Config { config.rust_codegen_backends = vec![INTERNER.intern_str("llvm")]; config.deny_warnings = true; config.missing_tools = false; - config.config = PathBuf::from("config.toml"); // set by bootstrap.py config.build = TargetSelection::from_user(&env!("BUILD_TRIPLE")); @@ -558,10 +557,10 @@ impl Config { #[cfg(test)] let get_toml = |_| TomlConfig::default(); #[cfg(not(test))] - let get_toml = |file: PathBuf| { + let get_toml = |file: &Path| { use std::process; - let contents = t!(fs::read_to_string(&file), "`include` config not found"); + let contents = t!(fs::read_to_string(file), "`include` config not found"); match toml::from_str(&contents) { Ok(table) => table, Err(err) => { @@ -571,18 +570,21 @@ impl Config { } }; - let mut toml = flags.config.map(get_toml).unwrap_or_else(TomlConfig::default); + let mut toml = flags.config.as_deref().map(get_toml).unwrap_or_else(TomlConfig::default); if let Some(include) = &toml.profile { let mut include_path = config.src.clone(); include_path.push("src"); include_path.push("bootstrap"); include_path.push("defaults"); include_path.push(format!("config.toml.{}", include)); - let included_toml = get_toml(include_path); + let included_toml = get_toml(&include_path); toml.merge(included_toml); } config.changelog_seen = toml.changelog_seen; + if let Some(cfg) = flags.config { + config.config = cfg; + } let build = toml.build.unwrap_or_default(); -- cgit 1.4.1-3-g733a5 From e715c7f234ba25c25b98894c822de9e7cf87558c Mon Sep 17 00:00:00 2001 From: Tyler Mandry Date: Thu, 24 Sep 2020 01:13:25 +0000 Subject: bootstrap: Always build for host, even when target is given This changes the behavior from *not* building for host whenever an explicit target is specified. I find this much less confusing. You can still disable host steps by passing an explicit empty list for host. Fixes #76990. --- src/bootstrap/builder/tests.rs | 19 +++++++------------ src/bootstrap/config.rs | 7 ++----- 2 files changed, 9 insertions(+), 17 deletions(-) (limited to 'src/bootstrap') diff --git a/src/bootstrap/builder/tests.rs b/src/bootstrap/builder/tests.rs index 4a9082d3e85..3ca41be0a9f 100644 --- a/src/bootstrap/builder/tests.rs +++ b/src/bootstrap/builder/tests.rs @@ -342,31 +342,29 @@ mod dist { } #[test] - fn dist_with_target_flag() { - let mut config = configure(&["B"], &["C"]); - config.skip_only_host_steps = true; // as-if --target=C was passed + fn dist_with_empty_host() { + let mut config = configure(&[], &["C"]); + config.skip_only_host_steps = true; let build = Build::new(config); let mut builder = Builder::new(&build); builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Dist), &[]); let a = TargetSelection::from_user("A"); - let b = TargetSelection::from_user("B"); let c = TargetSelection::from_user("C"); assert_eq!( first(builder.cache.all::()), - &[dist::Docs { host: a }, dist::Docs { host: b }, dist::Docs { host: c },] + &[dist::Docs { host: a }, dist::Docs { host: c },] ); assert_eq!( first(builder.cache.all::()), - &[dist::Mingw { host: a }, dist::Mingw { host: b }, dist::Mingw { host: c },] + &[dist::Mingw { host: a }, dist::Mingw { host: c },] ); assert_eq!(first(builder.cache.all::()), &[]); assert_eq!( first(builder.cache.all::()), &[ dist::Std { compiler: Compiler { host: a, stage: 1 }, target: a }, - dist::Std { compiler: Compiler { host: a, stage: 1 }, target: b }, dist::Std { compiler: Compiler { host: a, stage: 2 }, target: c }, ] ); @@ -464,15 +462,14 @@ mod dist { } #[test] - fn build_with_target_flag() { - let mut config = configure(&["B"], &["C"]); + fn build_with_empty_host() { + let mut config = configure(&[], &["C"]); config.skip_only_host_steps = true; let build = Build::new(config); let mut builder = Builder::new(&build); builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Build), &[]); let a = TargetSelection::from_user("A"); - let b = TargetSelection::from_user("B"); let c = TargetSelection::from_user("C"); assert_eq!( @@ -481,8 +478,6 @@ mod dist { compile::Std { compiler: Compiler { host: a, stage: 0 }, target: a }, compile::Std { compiler: Compiler { host: a, stage: 1 }, target: a }, compile::Std { compiler: Compiler { host: a, stage: 2 }, target: a }, - compile::Std { compiler: Compiler { host: a, stage: 1 }, target: b }, - compile::Std { compiler: Compiler { host: a, stage: 2 }, target: b }, compile::Std { compiler: Compiler { host: a, stage: 2 }, target: c }, ] ); diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index b14746dabb9..942d1178a64 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -586,11 +586,6 @@ impl Config { let build = toml.build.unwrap_or_default(); - // If --target was specified but --host wasn't specified, don't run any host-only tests. - let has_hosts = build.host.is_some() || flags.host.is_some(); - let has_targets = build.target.is_some() || flags.target.is_some(); - config.skip_only_host_steps = !has_hosts && has_targets; - config.hosts = if let Some(arg_host) = flags.host { arg_host } else if let Some(file_host) = build.host { @@ -598,6 +593,8 @@ impl Config { } else { vec![config.build] }; + // If host was explicitly given an empty list, don't run any host-only steps. + config.skip_only_host_steps = config.hosts.is_empty(); config.targets = if let Some(arg_target) = flags.target { arg_target } else if let Some(file_target) = build.target { -- cgit 1.4.1-3-g733a5 From 52ca5ca7b71a041bfb9a08fa143847581fce4843 Mon Sep 17 00:00:00 2001 From: Tyler Mandry Date: Fri, 25 Sep 2020 00:42:20 +0000 Subject: Remove skip_only_host_steps And make tests explicitly list their hosts and targets. --- src/bootstrap/builder.rs | 10 +------ src/bootstrap/builder/tests.rs | 67 ++++++++++++++---------------------------- src/bootstrap/config.rs | 4 --- 3 files changed, 23 insertions(+), 58 deletions(-) (limited to 'src/bootstrap') diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index 4aaaeb8a93b..4beeb9c87c4 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -172,15 +172,7 @@ impl StepDescription { } // Determine the targets participating in this rule. - let targets = if self.only_hosts { - if builder.config.skip_only_host_steps { - return; // don't run anything - } else { - &builder.hosts - } - } else { - &builder.targets - }; + let targets = if self.only_hosts { &builder.hosts } else { &builder.targets }; for target in targets { let run = RunConfig { builder, path: pathset.path(builder), target: *target }; diff --git a/src/bootstrap/builder/tests.rs b/src/bootstrap/builder/tests.rs index 3ca41be0a9f..a367aa53496 100644 --- a/src/bootstrap/builder/tests.rs +++ b/src/bootstrap/builder/tests.rs @@ -6,7 +6,6 @@ fn configure(cmd: &str, host: &[&str], target: &[&str]) -> Config { let mut config = Config::parse(&[cmd.to_owned()]); // don't save toolstates config.save_toolstates = None; - config.skip_only_host_steps = false; config.dry_run = true; config.ninja_in_file = false; // try to avoid spurious failures in dist where we create/delete each others file @@ -20,16 +19,8 @@ fn configure(cmd: &str, host: &[&str], target: &[&str]) -> Config { t!(fs::create_dir_all(&dir)); config.out = dir; config.build = TargetSelection::from_user("A"); - config.hosts = vec![config.build] - .into_iter() - .chain(host.iter().map(|s| TargetSelection::from_user(s))) - .collect::>(); - config.targets = config - .hosts - .clone() - .into_iter() - .chain(target.iter().map(|s| TargetSelection::from_user(s))) - .collect::>(); + config.hosts = host.iter().map(|s| TargetSelection::from_user(s)).collect(); + config.targets = target.iter().map(|s| TargetSelection::from_user(s)).collect(); config } @@ -45,7 +36,7 @@ mod defaults { #[test] fn build_default() { - let build = Build::new(configure("build", &[], &[])); + let build = Build::new(configure("build", &["A"], &["A"])); let mut builder = Builder::new(&build); builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Build), &[]); @@ -73,7 +64,7 @@ mod defaults { #[test] fn build_stage_0() { - let config = Config { stage: 0, ..configure("build", &[], &[]) }; + let config = Config { stage: 0, ..configure("build", &["A"], &["A"]) }; let build = Build::new(config); let mut builder = Builder::new(&build); builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Build), &[]); @@ -95,7 +86,7 @@ mod defaults { #[test] fn build_cross_compile() { - let config = Config { stage: 1, ..configure("build", &["B"], &["B"]) }; + let config = Config { stage: 1, ..configure("build", &["A", "B"], &["A", "B"]) }; let build = Build::new(config); let mut builder = Builder::new(&build); builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Build), &[]); @@ -143,7 +134,7 @@ mod defaults { #[test] fn doc_default() { - let mut config = configure("doc", &[], &[]); + let mut config = configure("doc", &["A"], &["A"]); config.compiler_docs = true; config.cmd = Subcommand::Doc { paths: Vec::new(), open: false }; let build = Build::new(config); @@ -182,7 +173,7 @@ mod dist { #[test] fn dist_baseline() { - let build = Build::new(configure(&[], &[])); + let build = Build::new(configure(&["A"], &["A"])); let mut builder = Builder::new(&build); builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Dist), &[]); @@ -208,7 +199,7 @@ mod dist { #[test] fn dist_with_targets() { - let build = Build::new(configure(&[], &["B"])); + let build = Build::new(configure(&["A"], &["A", "B"])); let mut builder = Builder::new(&build); builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Dist), &[]); @@ -239,7 +230,7 @@ mod dist { #[test] fn dist_with_hosts() { - let build = Build::new(configure(&["B"], &[])); + let build = Build::new(configure(&["A", "B"], &["A", "B"])); let mut builder = Builder::new(&build); builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Dist), &[]); @@ -285,7 +276,7 @@ mod dist { fn dist_only_cross_host() { let a = TargetSelection::from_user("A"); let b = TargetSelection::from_user("B"); - let mut build = Build::new(configure(&["B"], &[])); + let mut build = Build::new(configure(&["A", "B"], &["A", "B"])); build.config.docs = false; build.config.extended = true; build.hosts = vec![b]; @@ -307,7 +298,7 @@ mod dist { #[test] fn dist_with_targets_and_hosts() { - let build = Build::new(configure(&["B"], &["C"])); + let build = Build::new(configure(&["A", "B"], &["A", "B", "C"])); let mut builder = Builder::new(&build); builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Dist), &[]); @@ -343,8 +334,7 @@ mod dist { #[test] fn dist_with_empty_host() { - let mut config = configure(&[], &["C"]); - config.skip_only_host_steps = true; + let config = configure(&[], &["C"]); let build = Build::new(config); let mut builder = Builder::new(&build); builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Dist), &[]); @@ -352,28 +342,17 @@ mod dist { let a = TargetSelection::from_user("A"); let c = TargetSelection::from_user("C"); - assert_eq!( - first(builder.cache.all::()), - &[dist::Docs { host: a }, dist::Docs { host: c },] - ); - assert_eq!( - first(builder.cache.all::()), - &[dist::Mingw { host: a }, dist::Mingw { host: c },] - ); - assert_eq!(first(builder.cache.all::()), &[]); + assert_eq!(first(builder.cache.all::()), &[dist::Docs { host: c },]); + assert_eq!(first(builder.cache.all::()), &[dist::Mingw { host: c },]); assert_eq!( first(builder.cache.all::()), - &[ - dist::Std { compiler: Compiler { host: a, stage: 1 }, target: a }, - dist::Std { compiler: Compiler { host: a, stage: 2 }, target: c }, - ] + &[dist::Std { compiler: Compiler { host: a, stage: 2 }, target: c },] ); - assert_eq!(first(builder.cache.all::()), &[]); } #[test] fn dist_with_same_targets_and_hosts() { - let build = Build::new(configure(&["B"], &["B"])); + let build = Build::new(configure(&["A", "B"], &["A", "B"])); let mut builder = Builder::new(&build); builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Dist), &[]); @@ -426,7 +405,7 @@ mod dist { #[test] fn build_all() { - let build = Build::new(configure(&["B"], &["C"])); + let build = Build::new(configure(&["A", "B"], &["A", "B", "C"])); let mut builder = Builder::new(&build); builder.run_step_descriptions( &Builder::get_step_descriptions(Kind::Build), @@ -463,8 +442,7 @@ mod dist { #[test] fn build_with_empty_host() { - let mut config = configure(&[], &["C"]); - config.skip_only_host_steps = true; + let config = configure(&[], &["C"]); let build = Build::new(config); let mut builder = Builder::new(&build); builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Build), &[]); @@ -477,7 +455,6 @@ mod dist { &[ compile::Std { compiler: Compiler { host: a, stage: 0 }, target: a }, compile::Std { compiler: Compiler { host: a, stage: 1 }, target: a }, - compile::Std { compiler: Compiler { host: a, stage: 2 }, target: a }, compile::Std { compiler: Compiler { host: a, stage: 2 }, target: c }, ] ); @@ -500,7 +477,7 @@ mod dist { #[test] fn test_with_no_doc_stage0() { - let mut config = configure(&[], &[]); + let mut config = configure(&["A"], &["A"]); config.stage = 0; config.cmd = Subcommand::Test { paths: vec!["library/std".into()], @@ -540,7 +517,7 @@ mod dist { #[test] fn test_exclude() { - let mut config = configure(&[], &[]); + let mut config = configure(&["A"], &["A"]); config.exclude = vec!["src/tools/tidy".into()]; config.cmd = Subcommand::Test { paths: Vec::new(), @@ -567,7 +544,7 @@ mod dist { #[test] fn doc_ci() { - let mut config = configure(&[], &[]); + let mut config = configure(&["A"], &["A"]); config.compiler_docs = true; config.cmd = Subcommand::Doc { paths: Vec::new(), open: false }; let build = Build::new(config); @@ -596,7 +573,7 @@ mod dist { #[test] fn test_docs() { // Behavior of `x.py test` doing various documentation tests. - let mut config = configure(&[], &[]); + let mut config = configure(&["A"], &["A"]); config.cmd = Subcommand::Test { paths: vec![], test_args: vec![], diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index 942d1178a64..9c6b88243e2 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -66,8 +66,6 @@ pub struct Config { pub test_compare_mode: bool, pub llvm_libunwind: bool, - pub skip_only_host_steps: bool, - pub on_fail: Option, pub stage: u32, pub keep_stage: Vec, @@ -593,8 +591,6 @@ impl Config { } else { vec![config.build] }; - // If host was explicitly given an empty list, don't run any host-only steps. - config.skip_only_host_steps = config.hosts.is_empty(); config.targets = if let Some(arg_target) = flags.target { arg_target } else if let Some(file_target) = build.target { -- cgit 1.4.1-3-g733a5 From 6bd57e3531573ea4727b247d1db37046537b4e22 Mon Sep 17 00:00:00 2001 From: Tyler Mandry Date: Sat, 26 Sep 2020 00:04:23 +0000 Subject: Bump bootstrap version and update changelog --- src/bootstrap/CHANGELOG.md | 11 ++++++++++- src/bootstrap/bin/main.rs | 2 +- 2 files changed, 11 insertions(+), 2 deletions(-) (limited to 'src/bootstrap') diff --git a/src/bootstrap/CHANGELOG.md b/src/bootstrap/CHANGELOG.md index dfb39c54c17..fe426c4cec7 100644 --- a/src/bootstrap/CHANGELOG.md +++ b/src/bootstrap/CHANGELOG.md @@ -6,6 +6,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## [Non-breaking changes since the last major version] +None. + +## [Version 2] - 2020-09-25 + +- `host` now defaults to the value of `build` in all cases + + Previously `host` defaulted to an empty list when `target` was overridden, and to `build` otherwise + +### Non-breaking changes + - Add `x.py setup` [#76631](https://github.com/rust-lang/rust/pull/76631) - Add a changelog for x.py [#76626](https://github.com/rust-lang/rust/pull/76626) - Optionally, download LLVM from CI on Linux and NixOS @@ -21,7 +30,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). [#77120](https://github.com/rust-lang/rust/pull/77120). -## [Version 0] - 2020-09-11 +## [Version 1] - 2020-09-11 This is the first changelog entry, and it does not attempt to be an exhaustive list of features in x.py. Instead, this documents the changes to bootstrap in the past 2 months. diff --git a/src/bootstrap/bin/main.rs b/src/bootstrap/bin/main.rs index 637083e08d5..d31f95ee5e9 100644 --- a/src/bootstrap/bin/main.rs +++ b/src/bootstrap/bin/main.rs @@ -40,7 +40,7 @@ fn main() { } fn check_version(config: &Config) -> Option { - const VERSION: usize = 1; + const VERSION: usize = 2; let mut msg = String::new(); -- cgit 1.4.1-3-g733a5 From 6533c29bea5416c62c098f369553ed2b6c2c9eae Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Tue, 29 Sep 2020 18:00:19 -0400 Subject: Remove --cfg dox from rustdoc.rs This was added in https://github.com/rust-lang/rust/pull/53076 because several dependencies were using `cfg(dox)` instead of `cfg(rustdoc)`. I ran `rg 'cfg\(dox\)'` on the source tree with no matches, so I think this is now safe to remove. --- src/bootstrap/bin/rustdoc.rs | 4 ---- 1 file changed, 4 deletions(-) (limited to 'src/bootstrap') diff --git a/src/bootstrap/bin/rustdoc.rs b/src/bootstrap/bin/rustdoc.rs index cb58eb89ad8..95c55ffe75a 100644 --- a/src/bootstrap/bin/rustdoc.rs +++ b/src/bootstrap/bin/rustdoc.rs @@ -24,14 +24,10 @@ fn main() { let mut dylib_path = bootstrap::util::dylib_path(); dylib_path.insert(0, PathBuf::from(libdir.clone())); - //FIXME(misdreavus): once stdsimd uses cfg(doc) instead of cfg(dox), remove the `--cfg dox` - //arguments here let mut cmd = Command::new(rustdoc); cmd.args(&args) .arg("--cfg") .arg(format!("stage{}", stage)) - .arg("--cfg") - .arg("dox") .arg("--sysroot") .arg(&sysroot) .env(bootstrap::util::dylib_path_var(), env::join_paths(&dylib_path).unwrap()); -- cgit 1.4.1-3-g733a5 From 351f850d33e0a000c8fd8b43e71e03d91f470383 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Tue, 29 Sep 2020 18:20:21 -0400 Subject: Remove unused --cfg stageN --- src/bootstrap/bin/rustdoc.rs | 3 --- 1 file changed, 3 deletions(-) (limited to 'src/bootstrap') diff --git a/src/bootstrap/bin/rustdoc.rs b/src/bootstrap/bin/rustdoc.rs index 95c55ffe75a..cba17c8e608 100644 --- a/src/bootstrap/bin/rustdoc.rs +++ b/src/bootstrap/bin/rustdoc.rs @@ -11,7 +11,6 @@ fn main() { let args = env::args_os().skip(1).collect::>(); let rustdoc = env::var_os("RUSTDOC_REAL").expect("RUSTDOC_REAL was not set"); let libdir = env::var_os("RUSTDOC_LIBDIR").expect("RUSTDOC_LIBDIR was not set"); - let stage = env::var("RUSTC_STAGE").expect("RUSTC_STAGE was not set"); let sysroot = env::var_os("RUSTC_SYSROOT").expect("RUSTC_SYSROOT was not set"); use std::str::FromStr; @@ -26,8 +25,6 @@ fn main() { let mut cmd = Command::new(rustdoc); cmd.args(&args) - .arg("--cfg") - .arg(format!("stage{}", stage)) .arg("--sysroot") .arg(&sysroot) .env(bootstrap::util::dylib_path_var(), env::join_paths(&dylib_path).unwrap()); -- cgit 1.4.1-3-g733a5 From bf7aeaa617e309c31e6d28a469ea6092e28393df Mon Sep 17 00:00:00 2001 From: Tyler Mandry Date: Tue, 29 Sep 2020 22:50:57 +0000 Subject: Filter out empty items in bootstrap::flags::split --- src/bootstrap/flags.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/bootstrap') diff --git a/src/bootstrap/flags.rs b/src/bootstrap/flags.rs index a12fc50afad..795244e7cd4 100644 --- a/src/bootstrap/flags.rs +++ b/src/bootstrap/flags.rs @@ -679,7 +679,7 @@ impl Subcommand { } fn split(s: &[String]) -> Vec { - s.iter().flat_map(|s| s.split(',')).map(|s| s.to_string()).collect() + s.iter().flat_map(|s| s.split(',')).filter(|s| !s.is_empty()).map(|s| s.to_string()).collect() } fn parse_deny_warnings(matches: &getopts::Matches) -> Option { -- cgit 1.4.1-3-g733a5 From e05e2f9a94f4fa43f9a77bb59184eeb46fd49577 Mon Sep 17 00:00:00 2001 From: Pietro Albini Date: Wed, 30 Sep 2020 13:54:12 +0200 Subject: bootstrap: add ./x.py run src/tools/build-manifest --- src/bootstrap/builder.rs | 2 +- src/bootstrap/dist.rs | 2 +- src/bootstrap/run.rs | 42 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 2 deletions(-) (limited to 'src/bootstrap') diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index 4beeb9c87c4..3de5797180c 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -477,7 +477,7 @@ impl<'a> Builder<'a> { install::Src, install::Rustc ), - Kind::Run => describe!(run::ExpandYamlAnchors,), + Kind::Run => describe!(run::ExpandYamlAnchors, run::BuildManifest,), } } diff --git a/src/bootstrap/dist.rs b/src/bootstrap/dist.rs index c846662fd51..8cdd293239e 100644 --- a/src/bootstrap/dist.rs +++ b/src/bootstrap/dist.rs @@ -46,7 +46,7 @@ pub fn pkgname(builder: &Builder<'_>, component: &str) -> String { } } -fn distdir(builder: &Builder<'_>) -> PathBuf { +pub(crate) fn distdir(builder: &Builder<'_>) -> PathBuf { builder.out.join("dist") } diff --git a/src/bootstrap/run.rs b/src/bootstrap/run.rs index ba593cadbad..80c093e713e 100644 --- a/src/bootstrap/run.rs +++ b/src/bootstrap/run.rs @@ -1,5 +1,7 @@ use crate::builder::{Builder, RunConfig, ShouldRun, Step}; +use crate::dist::distdir; use crate::tool::Tool; +use build_helper::output; use std::process::Command; #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] @@ -41,3 +43,43 @@ fn try_run(builder: &Builder<'_>, cmd: &mut Command) -> bool { } true } + +#[derive(Debug, PartialOrd, Ord, Copy, Clone, Hash, PartialEq, Eq)] +pub struct BuildManifest; + +impl Step for BuildManifest { + type Output = (); + const ONLY_HOSTS: bool = true; + + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.path("src/tools/build-manifest") + } + + fn make_run(run: RunConfig<'_>) { + run.builder.ensure(BuildManifest); + } + + fn run(self, builder: &Builder<'_>) { + // This gets called by `promote-release` + // (https://github.com/rust-lang/promote-release). + let mut cmd = builder.tool_cmd(Tool::BuildManifest); + let sign = builder.config.dist_sign_folder.as_ref().unwrap_or_else(|| { + panic!("\n\nfailed to specify `dist.sign-folder` in `config.toml`\n\n") + }); + let addr = builder.config.dist_upload_addr.as_ref().unwrap_or_else(|| { + panic!("\n\nfailed to specify `dist.upload-addr` in `config.toml`\n\n") + }); + + let today = output(Command::new("date").arg("+%Y-%m-%d")); + + cmd.arg(sign); + cmd.arg(distdir(builder)); + cmd.arg(today.trim()); + cmd.arg(addr); + cmd.arg(&builder.config.channel); + cmd.arg(&builder.src); + + builder.create_dir(&distdir(builder)); + builder.run(&mut cmd); + } +} -- cgit 1.4.1-3-g733a5 From d4928ad7fd6636f1a1f8d51209a70f0ee7a5f43e Mon Sep 17 00:00:00 2001 From: Pietro Albini Date: Wed, 30 Sep 2020 14:26:10 +0200 Subject: build-manifest: keep legacy behavior when invoking through ./x.py dist --- src/bootstrap/dist.rs | 1 + src/tools/build-manifest/README.md | 3 +-- src/tools/build-manifest/src/main.rs | 37 ++++++++++++++++++------------------ 3 files changed, 21 insertions(+), 20 deletions(-) (limited to 'src/bootstrap') diff --git a/src/bootstrap/dist.rs b/src/bootstrap/dist.rs index 8cdd293239e..857e06d846d 100644 --- a/src/bootstrap/dist.rs +++ b/src/bootstrap/dist.rs @@ -2371,6 +2371,7 @@ impl Step for HashSign { cmd.arg(addr); cmd.arg(&builder.config.channel); cmd.arg(&builder.src); + cmd.env("BUILD_MANIFEST_LEGACY", "1"); builder.create_dir(&distdir(builder)); diff --git a/src/tools/build-manifest/README.md b/src/tools/build-manifest/README.md index 4d7d9f7da18..26e96c9fd8f 100644 --- a/src/tools/build-manifest/README.md +++ b/src/tools/build-manifest/README.md @@ -20,8 +20,7 @@ Then, you can generate the manifest and all the packages from `path/to/dist` to `path/to/output` with: ``` -$ BUILD_MANIFEST_DISABLE_SIGNING=1 cargo +nightly run \ - path/to/dist path/to/output 1970-01-01 http://example.com \ +$ cargo +nightly run path/to/dist path/to/output 1970-01-01 http://example.com \ CHANNEL path/to/rust/repo ``` diff --git a/src/tools/build-manifest/src/main.rs b/src/tools/build-manifest/src/main.rs index be3e862e7ae..7821994e117 100644 --- a/src/tools/build-manifest/src/main.rs +++ b/src/tools/build-manifest/src/main.rs @@ -232,26 +232,27 @@ struct Builder { input: PathBuf, output: PathBuf, - gpg_passphrase: String, digests: BTreeMap, s3_address: String, date: String, - should_sign: bool, + legacy: bool, + legacy_gpg_passphrase: String, } fn main() { - // Avoid signing packages while manually testing - // Do NOT set this envvar in CI - let should_sign = env::var("BUILD_MANIFEST_DISABLE_SIGNING").is_err(); - - // Safety check to ensure signing is always enabled on CI - // The CI environment variable is set by both Travis and AppVeyor - if !should_sign && env::var("CI").is_ok() { - println!("The 'BUILD_MANIFEST_DISABLE_SIGNING' env var can't be enabled on CI."); - println!("If you're not running this on CI, unset the 'CI' env var."); - panic!(); - } + // Up until Rust 1.48 the release process relied on build-manifest to create the SHA256 + // checksums of released files and to sign the tarballs. That was moved over to promote-release + // in time for the branching of Rust 1.48, but the old release process still had to work the + // old way. + // + // When running build-manifest through the old ./x.py dist hash-and-sign the environment + // variable will be set, enabling the legacy behavior of generating the .sha256 files and + // signing the tarballs. + // + // Once the old release process is fully decommissioned, the environment variable, all the + // related code in this tool and ./x.py dist hash-and-sign can be removed. + let legacy = env::var("BUILD_MANIFEST_LEGACY").is_ok(); let mut args = env::args().skip(1); let input = PathBuf::from(args.next().unwrap()); @@ -263,7 +264,7 @@ fn main() { // Do not ask for a passphrase while manually testing let mut passphrase = String::new(); - if should_sign { + if legacy { // `x.py` passes the passphrase via stdin. t!(io::stdin().read_to_string(&mut passphrase)); } @@ -273,12 +274,12 @@ fn main() { input, output, - gpg_passphrase: passphrase, digests: BTreeMap::new(), s3_address, date, - should_sign, + legacy, + legacy_gpg_passphrase: passphrase, } .build(); } @@ -604,7 +605,7 @@ impl Builder { } fn sign(&self, path: &Path) { - if !self.should_sign { + if !self.legacy { return; } @@ -627,7 +628,7 @@ impl Builder { .arg(path) .stdin(Stdio::piped()); let mut child = t!(cmd.spawn()); - t!(child.stdin.take().unwrap().write_all(self.gpg_passphrase.as_bytes())); + t!(child.stdin.take().unwrap().write_all(self.legacy_gpg_passphrase.as_bytes())); assert!(t!(child.wait()).success()); } -- cgit 1.4.1-3-g733a5 From f2961638c8b9c4494b962236aabfa9daa531f029 Mon Sep 17 00:00:00 2001 From: Mark Rousskov Date: Fri, 2 Oct 2020 19:51:36 -0400 Subject: Place all-targets checking behind a flag This matches Cargo behavior and avoids the (somewhat expensive) double checking, as well as the unfortunate duplicate error messages (#76822, rust-lang/cargo#5128). --- src/bootstrap/CHANGELOG.md | 2 +- src/bootstrap/builder.rs | 2 +- src/bootstrap/check.rs | 69 ++++++++++++++++++++++++++-------------------- src/bootstrap/flags.rs | 10 ++++++- 4 files changed, 50 insertions(+), 33 deletions(-) (limited to 'src/bootstrap') diff --git a/src/bootstrap/CHANGELOG.md b/src/bootstrap/CHANGELOG.md index fe426c4cec7..c9c1f90277a 100644 --- a/src/bootstrap/CHANGELOG.md +++ b/src/bootstrap/CHANGELOG.md @@ -6,7 +6,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## [Non-breaking changes since the last major version] -None. +- x.py check needs opt-in to check tests (--all-targets) [#77473](https://github.com/rust-lang/rust/pull/77473) ## [Version 2] - 2020-09-25 diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index 4beeb9c87c4..6856cd338cf 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -532,7 +532,7 @@ impl<'a> Builder<'a> { pub fn new(build: &Build) -> Builder<'_> { let (kind, paths) = match build.config.cmd { Subcommand::Build { ref paths } => (Kind::Build, &paths[..]), - Subcommand::Check { ref paths } => (Kind::Check, &paths[..]), + Subcommand::Check { ref paths, all_targets: _ } => (Kind::Check, &paths[..]), Subcommand::Clippy { ref paths } => (Kind::Clippy, &paths[..]), Subcommand::Fix { ref paths } => (Kind::Fix, &paths[..]), Subcommand::Doc { ref paths, .. } => (Kind::Doc, &paths[..]), diff --git a/src/bootstrap/check.rs b/src/bootstrap/check.rs index ead0bd0413b..371631154f7 100644 --- a/src/bootstrap/check.rs +++ b/src/bootstrap/check.rs @@ -1,9 +1,12 @@ //! Implementation of compiling the compiler and standard library, in "check"-based modes. -use crate::builder::{Builder, Kind, RunConfig, ShouldRun, Step}; use crate::compile::{add_to_sysroot, run_cargo, rustc_cargo, std_cargo}; use crate::config::TargetSelection; use crate::tool::{prepare_tool_cargo, SourceType}; +use crate::{ + builder::{Builder, Kind, RunConfig, ShouldRun, Step}, + Subcommand, +}; use crate::{Compiler, Mode}; use std::path::PathBuf; @@ -74,35 +77,37 @@ impl Step for Std { // // Currently only the "libtest" tree of crates does this. - let mut cargo = builder.cargo( - compiler, - Mode::Std, - SourceType::InTree, - target, - cargo_subcommand(builder.kind), - ); - std_cargo(builder, target, compiler.stage, &mut cargo); - cargo.arg("--all-targets"); + if let Subcommand::Check { all_targets: true, .. } = builder.config.cmd { + let mut cargo = builder.cargo( + compiler, + Mode::Std, + SourceType::InTree, + target, + cargo_subcommand(builder.kind), + ); + std_cargo(builder, target, compiler.stage, &mut cargo); + cargo.arg("--all-targets"); + + // Explicitly pass -p for all dependencies krates -- this will force cargo + // to also check the tests/benches/examples for these crates, rather + // than just the leaf crate. + for krate in builder.in_tree_crates("test") { + cargo.arg("-p").arg(krate.name); + } - // Explicitly pass -p for all dependencies krates -- this will force cargo - // to also check the tests/benches/examples for these crates, rather - // than just the leaf crate. - for krate in builder.in_tree_crates("test") { - cargo.arg("-p").arg(krate.name); + builder.info(&format!( + "Checking std test/bench/example targets ({} -> {})", + &compiler.host, target + )); + run_cargo( + builder, + cargo, + args(builder.kind), + &libstd_test_stamp(builder, compiler, target), + vec![], + true, + ); } - - builder.info(&format!( - "Checking std test/bench/example targets ({} -> {})", - &compiler.host, target - )); - run_cargo( - builder, - cargo, - args(builder.kind), - &libstd_test_stamp(builder, compiler, target), - vec![], - true, - ); } } @@ -143,7 +148,9 @@ impl Step for Rustc { cargo_subcommand(builder.kind), ); rustc_cargo(builder, &mut cargo, target); - cargo.arg("--all-targets"); + if let Subcommand::Check { all_targets: true, .. } = builder.config.cmd { + cargo.arg("--all-targets"); + } // Explicitly pass -p for all compiler krates -- this will force cargo // to also check the tests/benches/examples for these crates, rather @@ -205,7 +212,9 @@ macro_rules! tool_check_step { &[], ); - cargo.arg("--all-targets"); + if let Subcommand::Check { all_targets: true, .. } = builder.config.cmd { + cargo.arg("--all-targets"); + } builder.info(&format!( "Checking {} artifacts ({} -> {})", diff --git a/src/bootstrap/flags.rs b/src/bootstrap/flags.rs index 795244e7cd4..c1a9d4fcd23 100644 --- a/src/bootstrap/flags.rs +++ b/src/bootstrap/flags.rs @@ -47,6 +47,9 @@ pub enum Subcommand { paths: Vec, }, Check { + // Whether to run checking over all targets (e.g., unit / integration + // tests). + all_targets: bool, paths: Vec, }, Clippy { @@ -250,6 +253,9 @@ To learn more about a subcommand, run `./x.py -h`", `//rustfix_missing_coverage.txt`", ); } + "check" => { + opts.optflag("", "all-targets", "Check all targets"); + } "bench" => { opts.optmulti("", "test-args", "extra arguments", "ARGS"); } @@ -484,7 +490,9 @@ Arguments: let cmd = match subcommand.as_str() { "build" | "b" => Subcommand::Build { paths }, - "check" | "c" => Subcommand::Check { paths }, + "check" | "c" => { + Subcommand::Check { paths, all_targets: matches.opt_present("all-targets") } + } "clippy" => Subcommand::Clippy { paths }, "fix" => Subcommand::Fix { paths }, "test" | "t" => Subcommand::Test { -- cgit 1.4.1-3-g733a5 From eaa0186662b0a494b2536f75f9e2811b6aa8366b Mon Sep 17 00:00:00 2001 From: ecstatic-morse Date: Sat, 3 Oct 2020 09:29:50 -0700 Subject: Add quotes around command in CHANGELOG --- src/bootstrap/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/bootstrap') diff --git a/src/bootstrap/CHANGELOG.md b/src/bootstrap/CHANGELOG.md index c9c1f90277a..d8c704f451b 100644 --- a/src/bootstrap/CHANGELOG.md +++ b/src/bootstrap/CHANGELOG.md @@ -6,7 +6,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## [Non-breaking changes since the last major version] -- x.py check needs opt-in to check tests (--all-targets) [#77473](https://github.com/rust-lang/rust/pull/77473) +- `x.py check` needs opt-in to check tests (--all-targets) [#77473](https://github.com/rust-lang/rust/pull/77473) ## [Version 2] - 2020-09-25 -- cgit 1.4.1-3-g733a5