{
let repo_name = get_repo_name(to_clone);
- git_clone_inner(
- to_clone,
- &dest_parent_dir.join(&repo_name),
- shallow_clone,
- repo_name,
- )
+ git_clone_inner(to_clone, &dest_parent_dir.join(&repo_name), shallow_clone, repo_name)
}
-pub fn walk_dir(dir: P, mut dir_cb: D, mut file_cb: F) -> Result<(), String>
+pub fn walk_dir
(
+ dir: P,
+ dir_cb: &mut D,
+ file_cb: &mut F,
+ recursive: bool,
+) -> Result<(), String>
where
P: AsRef,
D: FnMut(&Path) -> Result<(), String>,
@@ -341,6 +363,9 @@ where
let entry_path = entry.path();
if entry_path.is_dir() {
dir_cb(&entry_path)?;
+ if recursive {
+ walk_dir(entry_path, dir_cb, file_cb, recursive)?; // Recursive call
+ }
} else {
file_cb(&entry_path)?;
}
@@ -383,11 +408,7 @@ pub fn split_args(args: &str) -> Result, String> {
}
}
if !found_end {
- return Err(format!(
- "Didn't find `{}` at the end of `{}`",
- end,
- &args[start..]
- ));
+ return Err(format!("Didn't find `{}` at the end of `{}`", end, &args[start..]));
}
} else if c == '\\' {
// We skip the escaped character.
@@ -403,11 +424,7 @@ pub fn split_args(args: &str) -> Result, String> {
pub fn remove_file + ?Sized>(file_path: &P) -> Result<(), String> {
std::fs::remove_file(file_path).map_err(|error| {
- format!(
- "Failed to remove `{}`: {:?}",
- file_path.as_ref().display(),
- error
- )
+ format!("Failed to remove `{}`: {:?}", file_path.as_ref().display(), error)
})
}
@@ -427,6 +444,10 @@ pub fn create_symlink, Q: AsRef>(original: P, link: Q) -> R
})
}
+pub fn get_sysroot_dir() -> PathBuf {
+ Path::new(crate::BUILD_DIR).join("build_sysroot")
+}
+
#[cfg(test)]
mod tests {
use super::*;
diff --git a/compiler/rustc_codegen_gcc/config.example.toml b/compiler/rustc_codegen_gcc/config.example.toml
index dcc414b73100..890387dc2426 100644
--- a/compiler/rustc_codegen_gcc/config.example.toml
+++ b/compiler/rustc_codegen_gcc/config.example.toml
@@ -1,2 +1,2 @@
-gcc-path = "gcc-build/gcc"
-# download-gccjit = true
+#gcc-path = "gcc-build/gcc"
+download-gccjit = true
diff --git a/compiler/rustc_codegen_gcc/deps/libLLVM-18-rust-1.78.0-nightly.so b/compiler/rustc_codegen_gcc/deps/libLLVM-18-rust-1.78.0-nightly.so
deleted file mode 100644
index c44ca790b4f8..000000000000
--- a/compiler/rustc_codegen_gcc/deps/libLLVM-18-rust-1.78.0-nightly.so
+++ /dev/null
@@ -1 +0,0 @@
-INPUT(libLLVM.so.18.1-rust-1.78.0-nightly)
diff --git a/compiler/rustc_codegen_gcc/doc/tips.md b/compiler/rustc_codegen_gcc/doc/tips.md
index 1379f5130be0..86c22db186e0 100644
--- a/compiler/rustc_codegen_gcc/doc/tips.md
+++ b/compiler/rustc_codegen_gcc/doc/tips.md
@@ -35,6 +35,14 @@ COLLECT_NO_DEMANGLE=1
* Build the stage2 compiler (`rustup toolchain link debug-current build/x86_64-unknown-linux-gnu/stage2`).
* Clean and rebuild the codegen with `debug-current` in the file `rust-toolchain`.
+### How to use a custom sysroot source path
+
+If you wish to build a custom sysroot, pass the path of your sysroot source to `--sysroot-source` during the `prepare` step, like so:
+
+```
+./y.sh prepare --sysroot-source /path/to/custom/source
+```
+
### How to use [mem-trace](https://github.com/antoyo/mem-trace)
`rustc` needs to be built without `jemalloc` so that `mem-trace` can overload `malloc` since `jemalloc` is linked statically, so a `LD_PRELOAD`-ed library won't a chance to intercept the calls to `malloc`.
@@ -54,13 +62,13 @@ generate it in [gimple.md](./doc/gimple.md).
* Run `./y.sh prepare --cross` so that the sysroot is patched for the cross-compiling case.
* Set the path to the cross-compiling libgccjit in `gcc-path` (in `config.toml`).
- * Make sure you have the linker for your target (for instance `m68k-unknown-linux-gnu-gcc`) in your `$PATH`. Currently, the linker name is hardcoded as being `$TARGET-gcc`. Specify the target when building the sysroot: `./y.sh build --target-triple m68k-unknown-linux-gnu`.
+ * Make sure you have the linker for your target (for instance `m68k-unknown-linux-gnu-gcc`) in your `$PATH`. Currently, the linker name is hardcoded as being `$TARGET-gcc`. Specify the target when building the sysroot: `./y.sh build --sysroot --target-triple m68k-unknown-linux-gnu`.
* Build your project by specifying the target: `OVERWRITE_TARGET_TRIPLE=m68k-unknown-linux-gnu ../y.sh cargo build --target m68k-unknown-linux-gnu`.
If the target is not yet supported by the Rust compiler, create a [target specification file](https://docs.rust-embedded.org/embedonomicon/custom-target.html) (note that the `arch` specified in this file must be supported by the rust compiler).
Then, you can use it the following way:
- * Add the target specification file using `--target` as an **absolute** path to build the sysroot: `./y.sh build --target-triple m68k-unknown-linux-gnu --target $(pwd)/m68k-unknown-linux-gnu.json`
+ * Add the target specification file using `--target` as an **absolute** path to build the sysroot: `./y.sh build --sysroot --target-triple m68k-unknown-linux-gnu --target $(pwd)/m68k-unknown-linux-gnu.json`
* Build your project by specifying the target specification file: `OVERWRITE_TARGET_TRIPLE=m68k-unknown-linux-gnu ../y.sh cargo build --target path/to/m68k-unknown-linux-gnu.json`.
If you get the following error:
diff --git a/compiler/rustc_codegen_gcc/example/example.rs b/compiler/rustc_codegen_gcc/example/example.rs
index 7c21b73b630e..03470b74d0a1 100644
--- a/compiler/rustc_codegen_gcc/example/example.rs
+++ b/compiler/rustc_codegen_gcc/example/example.rs
@@ -153,10 +153,9 @@ fn array_as_slice(arr: &[u8; 3]) -> &[u8] {
arr
}
-// FIXME: fix the intrinsic implementation to work with the new ->u32 signature
-// unsafe fn use_ctlz_nonzero(a: u16) -> u32 {
-// intrinsics::ctlz_nonzero(a)
-// }
+unsafe fn use_ctlz_nonzero(a: u16) -> u32 {
+ intrinsics::ctlz_nonzero(a)
+}
fn ptr_as_usize(ptr: *const u8) -> usize {
ptr as usize
diff --git a/compiler/rustc_codegen_gcc/example/mini_core.rs b/compiler/rustc_codegen_gcc/example/mini_core.rs
index a48c0a4450c2..f47bfdad1312 100644
--- a/compiler/rustc_codegen_gcc/example/mini_core.rs
+++ b/compiler/rustc_codegen_gcc/example/mini_core.rs
@@ -1,5 +1,5 @@
#![feature(
- no_core, lang_items, intrinsics, unboxed_closures, type_ascription, extern_types,
+ no_core, lang_items, intrinsics, unboxed_closures, extern_types,
decl_macro, rustc_attrs, transparent_unions, auto_traits, freeze_impls,
thread_local
)]
diff --git a/compiler/rustc_codegen_gcc/libgccjit.version b/compiler/rustc_codegen_gcc/libgccjit.version
index 41bec6df5d95..23ca7f022155 100644
--- a/compiler/rustc_codegen_gcc/libgccjit.version
+++ b/compiler/rustc_codegen_gcc/libgccjit.version
@@ -1 +1 @@
-b6f163f52
+341be3b7d7ac6976cfed8ed59da3573c040d0776
diff --git a/compiler/rustc_codegen_gcc/patches/0001-Add-stdarch-Cargo.toml-for-testing.patch b/compiler/rustc_codegen_gcc/patches/0001-Add-stdarch-Cargo.toml-for-testing.patch
index 2a55f2cb796f..9cc377850b9b 100644
--- a/compiler/rustc_codegen_gcc/patches/0001-Add-stdarch-Cargo.toml-for-testing.patch
+++ b/compiler/rustc_codegen_gcc/patches/0001-Add-stdarch-Cargo.toml-for-testing.patch
@@ -19,7 +19,7 @@ index 0000000..4c63700
+members = [
+ "crates/core_arch",
+ "crates/std_detect",
-+ "crates/stdarch-gen",
++ "crates/stdarch-gen-arm",
+ #"examples/"
+]
+exclude = [
diff --git a/compiler/rustc_codegen_gcc/patches/0022-core-Disable-not-compiling-tests.patch b/compiler/rustc_codegen_gcc/patches/0022-core-Disable-not-compiling-tests.patch
index a7d523f94082..ea1a5a8d3553 100644
--- a/compiler/rustc_codegen_gcc/patches/0022-core-Disable-not-compiling-tests.patch
+++ b/compiler/rustc_codegen_gcc/patches/0022-core-Disable-not-compiling-tests.patch
@@ -39,4 +39,4 @@ index 42a26ae..5ac1042 100644
+#![cfg(test)]
#![feature(alloc_layout_extra)]
#![feature(array_chunks)]
- #![feature(array_windows)]
+ #![feature(array_ptr_get)]
diff --git a/compiler/rustc_codegen_gcc/patches/libgccjit12/0001-core-Disable-portable-simd-test.patch b/compiler/rustc_codegen_gcc/patches/libgccjit12/0001-core-Disable-portable-simd-test.patch
index 36d0789d2a23..42ae534e464d 100644
--- a/compiler/rustc_codegen_gcc/patches/libgccjit12/0001-core-Disable-portable-simd-test.patch
+++ b/compiler/rustc_codegen_gcc/patches/libgccjit12/0001-core-Disable-portable-simd-test.patch
@@ -1,4 +1,4 @@
-From a5663265f797a43c502915c356fe7899c16cee92 Mon Sep 17 00:00:00 2001
+From 124a11ce086952a5794d5cfbaa45175809497b81 Mon Sep 17 00:00:00 2001
From: None
Date: Sat, 18 Nov 2023 10:50:36 -0500
Subject: [PATCH] [core] Disable portable-simd test
@@ -8,18 +8,18 @@ Subject: [PATCH] [core] Disable portable-simd test
1 file changed, 2 deletions(-)
diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs
-index d0a119c..76fdece 100644
+index b71786c..cf484d5 100644
--- a/library/core/tests/lib.rs
+++ b/library/core/tests/lib.rs
-@@ -89,7 +89,6 @@
+@@ -95,7 +95,6 @@
#![feature(never_type)]
#![feature(unwrap_infallible)]
#![feature(pointer_is_aligned_to)]
-#![feature(portable_simd)]
#![feature(ptr_metadata)]
- #![feature(lazy_cell)]
#![feature(unsized_tuple_coercion)]
-@@ -155,7 +154,6 @@ mod pin;
+ #![feature(const_option)]
+@@ -157,7 +156,6 @@ mod pin;
mod pin_macro;
mod ptr;
mod result;
@@ -27,6 +27,5 @@ index d0a119c..76fdece 100644
mod slice;
mod str;
mod str_lossy;
---
-2.42.1
-
+--
+2.45.2
diff --git a/compiler/rustc_codegen_gcc/rust-toolchain b/compiler/rustc_codegen_gcc/rust-toolchain
index a0ac82866609..3c83f4b4608d 100644
--- a/compiler/rustc_codegen_gcc/rust-toolchain
+++ b/compiler/rustc_codegen_gcc/rust-toolchain
@@ -1,3 +1,3 @@
[toolchain]
-channel = "nightly-2024-03-05"
+channel = "nightly-2024-07-02"
components = ["rust-src", "rustc-dev", "llvm-tools-preview"]
diff --git a/compiler/rustc_codegen_gcc/src/abi.rs b/compiler/rustc_codegen_gcc/src/abi.rs
index b098594dbcc3..166dd080cf20 100644
--- a/compiler/rustc_codegen_gcc/src/abi.rs
+++ b/compiler/rustc_codegen_gcc/src/abi.rs
@@ -4,6 +4,7 @@ use gccjit::{ToLValue, ToRValue, Type};
use rustc_codegen_ssa::traits::{AbiBuilderMethods, BaseTypeMethods};
use rustc_data_structures::fx::FxHashSet;
use rustc_middle::bug;
+use rustc_middle::ty::layout::LayoutOf;
use rustc_middle::ty::Ty;
#[cfg(feature = "master")]
use rustc_session::config;
@@ -184,9 +185,17 @@ impl<'gcc, 'tcx> FnAbiGccExt<'gcc, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
}
PassMode::Indirect { attrs, meta_attrs: Some(meta_attrs), on_stack } => {
assert!(!on_stack);
- let ty =
- apply_attrs(cx.type_ptr_to(arg.memory_ty(cx)), &attrs, argument_tys.len());
- apply_attrs(ty, &meta_attrs, argument_tys.len())
+ // Construct the type of a (wide) pointer to `ty`, and pass its two fields.
+ // Any two ABI-compatible unsized types have the same metadata type and
+ // moreover the same metadata value leads to the same dynamic size and
+ // alignment, so this respects ABI compatibility.
+ let ptr_ty = Ty::new_mut_ptr(cx.tcx, arg.layout.ty);
+ let ptr_layout = cx.layout_of(ptr_ty);
+ let typ1 = ptr_layout.scalar_pair_element_gcc_type(cx, 0);
+ let typ2 = ptr_layout.scalar_pair_element_gcc_type(cx, 1);
+ argument_tys.push(apply_attrs(typ1, &attrs, argument_tys.len()));
+ argument_tys.push(apply_attrs(typ2, &meta_attrs, argument_tys.len()));
+ continue;
}
};
argument_tys.push(arg_ty);
diff --git a/compiler/rustc_codegen_gcc/src/asm.rs b/compiler/rustc_codegen_gcc/src/asm.rs
index 06b14a1f118a..aa485846cd42 100644
--- a/compiler/rustc_codegen_gcc/src/asm.rs
+++ b/compiler/rustc_codegen_gcc/src/asm.rs
@@ -115,7 +115,7 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
span: &[Span],
instance: Instance<'_>,
dest: Option,
- _catch_funclet: Option<(Self::BasicBlock, Option<&Self::Funclet>)>,
+ _dest_catch_funclet: Option<(Self::BasicBlock, Option<&Self::Funclet>)>,
) {
if options.contains(InlineAsmOptions::MAY_UNWIND) {
self.sess().dcx().create_err(UnwindingInlineAsm { span: span[0] }).emit();
@@ -485,9 +485,8 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
}
InlineAsmOperandRef::Label { label } => {
- let label_gcc_index = labels.iter()
- .position(|&l| l == label)
- .expect("wrong rust index");
+ let label_gcc_index =
+ labels.iter().position(|&l| l == label).expect("wrong rust index");
let gcc_index = label_gcc_index + outputs.len() + inputs.len();
push_to_template(Some('l'), gcc_index);
}
@@ -538,9 +537,8 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
}
if dest.is_none() && options.contains(InlineAsmOptions::NORETURN) {
let builtin_unreachable = self.context.get_builtin_function("__builtin_unreachable");
- let builtin_unreachable: RValue<'gcc> = unsafe {
- std::mem::transmute(builtin_unreachable)
- };
+ let builtin_unreachable: RValue<'gcc> =
+ unsafe { std::mem::transmute(builtin_unreachable) };
self.call(self.type_void(), None, None, builtin_unreachable, &[], None, None);
}
@@ -696,10 +694,12 @@ fn reg_to_gcc(reg: InlineAsmRegOrRegClass) -> ConstraintOrRegister {
fn dummy_output_type<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, reg: InlineAsmRegClass) -> Type<'gcc> {
match reg {
InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::reg) => cx.type_i32(),
- InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::preg) => unimplemented!(),
InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg)
| InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg_low16) => {
- unimplemented!()
+ cx.type_vector(cx.type_i64(), 2)
+ }
+ InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::preg) => {
+ unreachable!("clobber-only")
}
InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg) => cx.type_i32(),
InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg)
@@ -710,21 +710,13 @@ fn dummy_output_type<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, reg: InlineAsmRegCl
InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg)
| InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg_low8)
| InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg_low4) => {
- unimplemented!()
+ cx.type_vector(cx.type_i64(), 2)
}
- InlineAsmRegClass::Avr(_) => unimplemented!(),
- InlineAsmRegClass::Bpf(_) => unimplemented!(),
InlineAsmRegClass::Hexagon(HexagonInlineAsmRegClass::reg) => cx.type_i32(),
InlineAsmRegClass::LoongArch(LoongArchInlineAsmRegClass::reg) => cx.type_i32(),
InlineAsmRegClass::LoongArch(LoongArchInlineAsmRegClass::freg) => cx.type_f32(),
- InlineAsmRegClass::M68k(M68kInlineAsmRegClass::reg) => cx.type_i32(),
- InlineAsmRegClass::M68k(M68kInlineAsmRegClass::reg_addr) => cx.type_i32(),
- InlineAsmRegClass::M68k(M68kInlineAsmRegClass::reg_data) => cx.type_i32(),
- InlineAsmRegClass::CSKY(CSKYInlineAsmRegClass::reg) => cx.type_i32(),
- InlineAsmRegClass::CSKY(CSKYInlineAsmRegClass::freg) => cx.type_f32(),
InlineAsmRegClass::Mips(MipsInlineAsmRegClass::reg) => cx.type_i32(),
InlineAsmRegClass::Mips(MipsInlineAsmRegClass::freg) => cx.type_f32(),
- InlineAsmRegClass::Msp430(_) => unimplemented!(),
InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg16) => cx.type_i16(),
InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg32) => cx.type_i32(),
InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg64) => cx.type_i64(),
@@ -737,26 +729,43 @@ fn dummy_output_type<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, reg: InlineAsmRegCl
}
InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::reg) => cx.type_i32(),
InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::freg) => cx.type_f32(),
- InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::vreg) => cx.type_f32(),
+ InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::vreg) => {
+ unreachable!("clobber-only")
+ }
InlineAsmRegClass::X86(X86InlineAsmRegClass::reg)
| InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_abcd) => cx.type_i32(),
InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_byte) => cx.type_i8(),
- InlineAsmRegClass::X86(X86InlineAsmRegClass::mmx_reg) => unimplemented!(),
InlineAsmRegClass::X86(X86InlineAsmRegClass::xmm_reg)
| InlineAsmRegClass::X86(X86InlineAsmRegClass::ymm_reg)
| InlineAsmRegClass::X86(X86InlineAsmRegClass::zmm_reg) => cx.type_f32(),
- InlineAsmRegClass::X86(X86InlineAsmRegClass::x87_reg) => unimplemented!(),
InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg) => cx.type_i16(),
- InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg0) => cx.type_i16(),
- InlineAsmRegClass::X86(X86InlineAsmRegClass::tmm_reg) => unimplemented!(),
- InlineAsmRegClass::Wasm(WasmInlineAsmRegClass::local) => cx.type_i32(),
- InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => {
- bug!("LLVM backend does not support SPIR-V")
+ InlineAsmRegClass::X86(X86InlineAsmRegClass::x87_reg)
+ | InlineAsmRegClass::X86(X86InlineAsmRegClass::mmx_reg)
+ | InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg0)
+ | InlineAsmRegClass::X86(X86InlineAsmRegClass::tmm_reg) => {
+ unreachable!("clobber-only")
}
+ InlineAsmRegClass::Wasm(WasmInlineAsmRegClass::local) => cx.type_i32(),
+ InlineAsmRegClass::Bpf(BpfInlineAsmRegClass::reg) => cx.type_i64(),
+ InlineAsmRegClass::Bpf(BpfInlineAsmRegClass::wreg) => cx.type_i32(),
+ InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg) => cx.type_i8(),
+ InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg_upper) => cx.type_i8(),
+ InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg_pair) => cx.type_i16(),
+ InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg_iw) => cx.type_i16(),
+ InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg_ptr) => cx.type_i16(),
InlineAsmRegClass::S390x(
S390xInlineAsmRegClass::reg | S390xInlineAsmRegClass::reg_addr,
) => cx.type_i32(),
InlineAsmRegClass::S390x(S390xInlineAsmRegClass::freg) => cx.type_f64(),
+ InlineAsmRegClass::Msp430(Msp430InlineAsmRegClass::reg) => cx.type_i16(),
+ InlineAsmRegClass::M68k(M68kInlineAsmRegClass::reg) => cx.type_i32(),
+ InlineAsmRegClass::M68k(M68kInlineAsmRegClass::reg_addr) => cx.type_i32(),
+ InlineAsmRegClass::M68k(M68kInlineAsmRegClass::reg_data) => cx.type_i32(),
+ InlineAsmRegClass::CSKY(CSKYInlineAsmRegClass::reg) => cx.type_i32(),
+ InlineAsmRegClass::CSKY(CSKYInlineAsmRegClass::freg) => cx.type_f32(),
+ InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => {
+ bug!("GCC backend does not support SPIR-V")
+ }
InlineAsmRegClass::Err => unreachable!(),
}
}
diff --git a/compiler/rustc_codegen_gcc/src/attributes.rs b/compiler/rustc_codegen_gcc/src/attributes.rs
index 8602566ab8fa..27f21107eda7 100644
--- a/compiler/rustc_codegen_gcc/src/attributes.rs
+++ b/compiler/rustc_codegen_gcc/src/attributes.rs
@@ -92,7 +92,7 @@ pub fn from_fn_attrs<'gcc, 'tcx>(
let mut function_features = function_features
.iter()
.flat_map(|feat| to_gcc_features(cx.tcx.sess, feat).into_iter())
- .chain(codegen_fn_attrs.instruction_set.iter().map(|x| match x {
+ .chain(codegen_fn_attrs.instruction_set.iter().map(|x| match *x {
InstructionSetAttr::ArmA32 => "-thumb-mode", // TODO(antoyo): support removing feature.
InstructionSetAttr::ArmT32 => "thumb-mode",
}))
@@ -118,8 +118,8 @@ pub fn from_fn_attrs<'gcc, 'tcx>(
if feature.starts_with('-') {
Some(format!("no{}", feature))
- } else if feature.starts_with('+') {
- Some(feature[1..].to_string())
+ } else if let Some(stripped) = feature.strip_prefix('+') {
+ Some(stripped.to_string())
} else {
Some(feature.to_string())
}
@@ -128,6 +128,12 @@ pub fn from_fn_attrs<'gcc, 'tcx>(
.join(",");
if !target_features.is_empty() {
#[cfg(feature = "master")]
- func.add_attribute(FnAttribute::Target(&target_features));
+ match cx.sess().target.arch.as_ref() {
+ "x86" | "x86_64" | "powerpc" => {
+ func.add_attribute(FnAttribute::Target(&target_features))
+ }
+ // The target attribute is not supported on other targets in GCC.
+ _ => (),
+ }
}
}
diff --git a/compiler/rustc_codegen_gcc/src/back/lto.rs b/compiler/rustc_codegen_gcc/src/back/lto.rs
index ec70fbdddb0f..6b2dbbbed677 100644
--- a/compiler/rustc_codegen_gcc/src/back/lto.rs
+++ b/compiler/rustc_codegen_gcc/src/back/lto.rs
@@ -16,13 +16,14 @@
// /usr/bin/ld: warning: type of symbol `_RNvNvNvNtCs5JWOrf9uCus_5rayon11thread_pool19WORKER_THREAD_STATE7___getit5___KEY' changed from 1 to 6 in /tmp/ccKeUSiR.ltrans0.ltrans.o
// /usr/bin/ld: warning: type of symbol `_RNvNvNvNvNtNtNtCsAj5i4SGTR7_3std4sync4mpmc5waker17current_thread_id5DUMMY7___getit5___KEY' changed from 1 to 6 in /tmp/ccKeUSiR.ltrans0.ltrans.o
// /usr/bin/ld: warning: incremental linking of LTO and non-LTO objects; using -flinker-output=nolto-rel which will bypass whole program optimization
-use std::ffi::CString;
+use std::ffi::{CStr, CString};
use std::fs::{self, File};
use std::path::{Path, PathBuf};
+use std::sync::Arc;
-use gccjit::OutputKind;
+use gccjit::{Context, OutputKind};
use object::read::archive::ArchiveFile;
-use rustc_codegen_ssa::back::lto::{LtoModuleCodegen, SerializedModule};
+use rustc_codegen_ssa::back::lto::{LtoModuleCodegen, SerializedModule, ThinModule, ThinShared};
use rustc_codegen_ssa::back::symbol_export;
use rustc_codegen_ssa::back::write::{CodegenContext, FatLtoInput};
use rustc_codegen_ssa::traits::*;
@@ -30,6 +31,7 @@ use rustc_codegen_ssa::{looks_like_rust_object_file, ModuleCodegen, ModuleKind};
use rustc_data_structures::memmap::Mmap;
use rustc_errors::{DiagCtxtHandle, FatalError};
use rustc_hir::def_id::LOCAL_CRATE;
+use rustc_middle::bug;
use rustc_middle::dep_graph::WorkProduct;
use rustc_middle::middle::exported_symbols::{SymbolExportInfo, SymbolExportLevel};
use rustc_session::config::{CrateType, Lto};
@@ -37,7 +39,7 @@ use tempfile::{tempdir, TempDir};
use crate::back::write::save_temp_bitcode;
use crate::errors::{DynamicLinkingWithLTO, LtoBitcodeFromRlib, LtoDisallowed, LtoDylib};
-use crate::{to_gcc_opt_level, GccCodegenBackend, GccContext};
+use crate::{to_gcc_opt_level, GccCodegenBackend, GccContext, SyncContext};
/// We keep track of the computed LTO cache keys from the previous
/// session to determine which CGUs we can reuse.
@@ -128,8 +130,7 @@ fn prepare_lto(
}
let archive_data = unsafe {
- Mmap::map(File::open(&path).expect("couldn't open rlib"))
- .expect("couldn't map rlib")
+ Mmap::map(File::open(path).expect("couldn't open rlib")).expect("couldn't map rlib")
};
let archive = ArchiveFile::parse(&*archive_data).expect("wanted an rlib");
let obj_files = archive
@@ -349,6 +350,395 @@ impl ModuleBuffer {
impl ModuleBufferMethods for ModuleBuffer {
fn data(&self) -> &[u8] {
- unimplemented!("data not needed for GCC codegen");
+ &[]
}
}
+
+/// Performs thin LTO by performing necessary global analysis and returning two
+/// lists, one of the modules that need optimization and another for modules that
+/// can simply be copied over from the incr. comp. cache.
+pub(crate) fn run_thin(
+ cgcx: &CodegenContext,
+ modules: Vec<(String, ThinBuffer)>,
+ cached_modules: Vec<(SerializedModule, WorkProduct)>,
+) -> Result<(Vec>, Vec), FatalError> {
+ let dcx = cgcx.create_dcx();
+ let dcx = dcx.handle();
+ let lto_data = prepare_lto(cgcx, dcx)?;
+ /*let symbols_below_threshold =
+ symbols_below_threshold.iter().map(|c| c.as_ptr()).collect::>();*/
+ if cgcx.opts.cg.linker_plugin_lto.enabled() {
+ unreachable!(
+ "We should never reach this case if the LTO step \
+ is deferred to the linker"
+ );
+ }
+ thin_lto(
+ cgcx,
+ dcx,
+ modules,
+ lto_data.upstream_modules,
+ lto_data.tmp_path,
+ cached_modules, /*, &symbols_below_threshold*/
+ )
+}
+
+pub(crate) fn prepare_thin(
+ module: ModuleCodegen,
+ _emit_summary: bool,
+) -> (String, ThinBuffer) {
+ let name = module.name;
+ //let buffer = ThinBuffer::new(module.module_llvm.context, true, emit_summary);
+ let buffer = ThinBuffer::new(&module.module_llvm.context);
+ (name, buffer)
+}
+
+/// Prepare "thin" LTO to get run on these modules.
+///
+/// The general structure of ThinLTO is quite different from the structure of
+/// "fat" LTO above. With "fat" LTO all LLVM modules in question are merged into
+/// one giant LLVM module, and then we run more optimization passes over this
+/// big module after internalizing most symbols. Thin LTO, on the other hand,
+/// avoid this large bottleneck through more targeted optimization.
+///
+/// At a high level Thin LTO looks like:
+///
+/// 1. Prepare a "summary" of each LLVM module in question which describes
+/// the values inside, cost of the values, etc.
+/// 2. Merge the summaries of all modules in question into one "index"
+/// 3. Perform some global analysis on this index
+/// 4. For each module, use the index and analysis calculated previously to
+/// perform local transformations on the module, for example inlining
+/// small functions from other modules.
+/// 5. Run thin-specific optimization passes over each module, and then code
+/// generate everything at the end.
+///
+/// The summary for each module is intended to be quite cheap, and the global
+/// index is relatively quite cheap to create as well. As a result, the goal of
+/// ThinLTO is to reduce the bottleneck on LTO and enable LTO to be used in more
+/// situations. For example one cheap optimization is that we can parallelize
+/// all codegen modules, easily making use of all the cores on a machine.
+///
+/// With all that in mind, the function here is designed at specifically just
+/// calculating the *index* for ThinLTO. This index will then be shared amongst
+/// all of the `LtoModuleCodegen` units returned below and destroyed once
+/// they all go out of scope.
+fn thin_lto(
+ cgcx: &CodegenContext,
+ _dcx: DiagCtxtHandle<'_>,
+ modules: Vec<(String, ThinBuffer)>,
+ serialized_modules: Vec<(SerializedModule, CString)>,
+ tmp_path: TempDir,
+ cached_modules: Vec<(SerializedModule, WorkProduct)>,
+ //symbols_below_threshold: &[*const libc::c_char],
+) -> Result<(Vec>, Vec), FatalError> {
+ let _timer = cgcx.prof.generic_activity("LLVM_thin_lto_global_analysis");
+ info!("going for that thin, thin LTO");
+
+ /*let green_modules: FxHashMap<_, _> =
+ cached_modules.iter().map(|(_, wp)| (wp.cgu_name.clone(), wp.clone())).collect();*/
+
+ let full_scope_len = modules.len() + serialized_modules.len() + cached_modules.len();
+ let mut thin_buffers = Vec::with_capacity(modules.len());
+ let mut module_names = Vec::with_capacity(full_scope_len);
+ //let mut thin_modules = Vec::with_capacity(full_scope_len);
+
+ for (i, (name, buffer)) in modules.into_iter().enumerate() {
+ info!("local module: {} - {}", i, name);
+ let cname = CString::new(name.as_bytes()).unwrap();
+ /*thin_modules.push(llvm::ThinLTOModule {
+ identifier: cname.as_ptr(),
+ data: buffer.data().as_ptr(),
+ len: buffer.data().len(),
+ });*/
+ thin_buffers.push(buffer);
+ module_names.push(cname);
+ }
+
+ // FIXME: All upstream crates are deserialized internally in the
+ // function below to extract their summary and modules. Note that
+ // unlike the loop above we *must* decode and/or read something
+ // here as these are all just serialized files on disk. An
+ // improvement, however, to make here would be to store the
+ // module summary separately from the actual module itself. Right
+ // now this is store in one large bitcode file, and the entire
+ // file is deflate-compressed. We could try to bypass some of the
+ // decompression by storing the index uncompressed and only
+ // lazily decompressing the bytecode if necessary.
+ //
+ // Note that truly taking advantage of this optimization will
+ // likely be further down the road. We'd have to implement
+ // incremental ThinLTO first where we could actually avoid
+ // looking at upstream modules entirely sometimes (the contents,
+ // we must always unconditionally look at the index).
+ let mut serialized = Vec::with_capacity(serialized_modules.len() + cached_modules.len());
+
+ let cached_modules =
+ cached_modules.into_iter().map(|(sm, wp)| (sm, CString::new(wp.cgu_name).unwrap()));
+
+ for (module, name) in serialized_modules.into_iter().chain(cached_modules) {
+ info!("upstream or cached module {:?}", name);
+ /*thin_modules.push(llvm::ThinLTOModule {
+ identifier: name.as_ptr(),
+ data: module.data().as_ptr(),
+ len: module.data().len(),
+ });*/
+
+ match module {
+ SerializedModule::Local(_) => {
+ //let path = module_buffer.0.to_str().expect("path");
+ //let my_path = PathBuf::from(path);
+ //let exists = my_path.exists();
+ /*module.module_llvm.should_combine_object_files = true;
+ module
+ .module_llvm
+ .context
+ .add_driver_option(module_buffer.0.to_str().expect("path"));*/
+ }
+ SerializedModule::FromRlib(_) => unimplemented!("from rlib"),
+ SerializedModule::FromUncompressedFile(_) => {
+ unimplemented!("from uncompressed file")
+ }
+ }
+
+ serialized.push(module);
+ module_names.push(name);
+ }
+
+ // Sanity check
+ //assert_eq!(thin_modules.len(), module_names.len());
+
+ // Delegate to the C++ bindings to create some data here. Once this is a
+ // tried-and-true interface we may wish to try to upstream some of this
+ // to LLVM itself, right now we reimplement a lot of what they do
+ // upstream...
+ /*let data = llvm::LLVMRustCreateThinLTOData(
+ thin_modules.as_ptr(),
+ thin_modules.len() as u32,
+ symbols_below_threshold.as_ptr(),
+ symbols_below_threshold.len() as u32,
+ )
+ .ok_or_else(|| write::llvm_err(dcx, LlvmError::PrepareThinLtoContext))?;
+ */
+
+ let data = ThinData; //(Arc::new(tmp_path))/*(data)*/;
+
+ info!("thin LTO data created");
+
+ /*let (key_map_path, prev_key_map, curr_key_map) =
+ if let Some(ref incr_comp_session_dir) = cgcx.incr_comp_session_dir {
+ let path = incr_comp_session_dir.join(THIN_LTO_KEYS_INCR_COMP_FILE_NAME);
+ // If the previous file was deleted, or we get an IO error
+ // reading the file, then we'll just use `None` as the
+ // prev_key_map, which will force the code to be recompiled.
+ let prev =
+ if path.exists() { ThinLTOKeysMap::load_from_file(&path).ok() } else { None };
+ let curr = ThinLTOKeysMap::from_thin_lto_modules(&data, &thin_modules, &module_names);
+ (Some(path), prev, curr)
+ }
+ else {
+ // If we don't compile incrementally, we don't need to load the
+ // import data from LLVM.
+ assert!(green_modules.is_empty());
+ let curr = ThinLTOKeysMap::default();
+ (None, None, curr)
+ };
+ info!("thin LTO cache key map loaded");
+ info!("prev_key_map: {:#?}", prev_key_map);
+ info!("curr_key_map: {:#?}", curr_key_map);*/
+
+ // Throw our data in an `Arc` as we'll be sharing it across threads. We
+ // also put all memory referenced by the C++ data (buffers, ids, etc)
+ // into the arc as well. After this we'll create a thin module
+ // codegen per module in this data.
+ let shared =
+ Arc::new(ThinShared { data, thin_buffers, serialized_modules: serialized, module_names });
+
+ let copy_jobs = vec![];
+ let mut opt_jobs = vec![];
+
+ info!("checking which modules can be-reused and which have to be re-optimized.");
+ for (module_index, module_name) in shared.module_names.iter().enumerate() {
+ let module_name = module_name_to_str(module_name);
+ /*if let (Some(prev_key_map), true) =
+ (prev_key_map.as_ref(), green_modules.contains_key(module_name))
+ {
+ assert!(cgcx.incr_comp_session_dir.is_some());
+
+ // If a module exists in both the current and the previous session,
+ // and has the same LTO cache key in both sessions, then we can re-use it
+ if prev_key_map.keys.get(module_name) == curr_key_map.keys.get(module_name) {
+ let work_product = green_modules[module_name].clone();
+ copy_jobs.push(work_product);
+ info!(" - {}: re-used", module_name);
+ assert!(cgcx.incr_comp_session_dir.is_some());
+ continue;
+ }
+ }*/
+
+ info!(" - {}: re-compiled", module_name);
+ opt_jobs
+ .push(LtoModuleCodegen::Thin(ThinModule { shared: shared.clone(), idx: module_index }));
+ }
+
+ // Save the current ThinLTO import information for the next compilation
+ // session, overwriting the previous serialized data (if any).
+ /*if let Some(path) = key_map_path {
+ if let Err(err) = curr_key_map.save_to_file(&path) {
+ return Err(write::llvm_err(dcx, LlvmError::WriteThinLtoKey { err }));
+ }
+ }*/
+
+ // NOTE: save the temporary directory used by LTO so that it gets deleted after linking instead
+ // of now.
+ //module.module_llvm.temp_dir = Some(tmp_path);
+ // TODO: save the directory so that it gets deleted later.
+ std::mem::forget(tmp_path);
+
+ Ok((opt_jobs, copy_jobs))
+}
+
+pub unsafe fn optimize_thin_module(
+ thin_module: ThinModule,
+ _cgcx: &CodegenContext,
+) -> Result, FatalError> {
+ //let dcx = cgcx.create_dcx();
+
+ //let module_name = &thin_module.shared.module_names[thin_module.idx];
+ /*let tm_factory_config = TargetMachineFactoryConfig::new(cgcx, module_name.to_str().unwrap());
+ let tm = (cgcx.tm_factory)(tm_factory_config).map_err(|e| write::llvm_err(&dcx, e))?;*/
+
+ // Right now the implementation we've got only works over serialized
+ // modules, so we create a fresh new LLVM context and parse the module
+ // into that context. One day, however, we may do this for upstream
+ // crates but for locally codegened modules we may be able to reuse
+ // that LLVM Context and Module.
+ //let llcx = llvm::LLVMRustContextCreate(cgcx.fewer_names);
+ //let llmod_raw = parse_module(llcx, module_name, thin_module.data(), &dcx)? as *const _;
+ let mut should_combine_object_files = false;
+ let context = match thin_module.shared.thin_buffers.get(thin_module.idx) {
+ Some(thin_buffer) => Arc::clone(&thin_buffer.context),
+ None => {
+ let context = Context::default();
+ let len = thin_module.shared.thin_buffers.len();
+ let module = &thin_module.shared.serialized_modules[thin_module.idx - len];
+ match *module {
+ SerializedModule::Local(ref module_buffer) => {
+ let path = module_buffer.0.to_str().expect("path");
+ context.add_driver_option(path);
+ should_combine_object_files = true;
+ /*module.module_llvm.should_combine_object_files = true;
+ module
+ .module_llvm
+ .context
+ .add_driver_option(module_buffer.0.to_str().expect("path"));*/
+ }
+ SerializedModule::FromRlib(_) => unimplemented!("from rlib"),
+ SerializedModule::FromUncompressedFile(_) => {
+ unimplemented!("from uncompressed file")
+ }
+ }
+ Arc::new(SyncContext::new(context))
+ }
+ };
+ let module = ModuleCodegen {
+ module_llvm: GccContext { context, should_combine_object_files, temp_dir: None },
+ name: thin_module.name().to_string(),
+ kind: ModuleKind::Regular,
+ };
+ /*{
+ let target = &*module.module_llvm.tm;
+ let llmod = module.module_llvm.llmod();
+ save_temp_bitcode(cgcx, &module, "thin-lto-input");
+
+ // Up next comes the per-module local analyses that we do for Thin LTO.
+ // Each of these functions is basically copied from the LLVM
+ // implementation and then tailored to suit this implementation. Ideally
+ // each of these would be supported by upstream LLVM but that's perhaps
+ // a patch for another day!
+ //
+ // You can find some more comments about these functions in the LLVM
+ // bindings we've got (currently `PassWrapper.cpp`)
+ {
+ let _timer =
+ cgcx.prof.generic_activity_with_arg("LLVM_thin_lto_rename", thin_module.name());
+ if !llvm::LLVMRustPrepareThinLTORename(thin_module.shared.data.0, llmod, target) {
+ return Err(write::llvm_err(&dcx, LlvmError::PrepareThinLtoModule));
+ }
+ save_temp_bitcode(cgcx, &module, "thin-lto-after-rename");
+ }
+
+ {
+ let _timer = cgcx
+ .prof
+ .generic_activity_with_arg("LLVM_thin_lto_resolve_weak", thin_module.name());
+ if !llvm::LLVMRustPrepareThinLTOResolveWeak(thin_module.shared.data.0, llmod) {
+ return Err(write::llvm_err(&dcx, LlvmError::PrepareThinLtoModule));
+ }
+ save_temp_bitcode(cgcx, &module, "thin-lto-after-resolve");
+ }
+
+ {
+ let _timer = cgcx
+ .prof
+ .generic_activity_with_arg("LLVM_thin_lto_internalize", thin_module.name());
+ if !llvm::LLVMRustPrepareThinLTOInternalize(thin_module.shared.data.0, llmod) {
+ return Err(write::llvm_err(&dcx, LlvmError::PrepareThinLtoModule));
+ }
+ save_temp_bitcode(cgcx, &module, "thin-lto-after-internalize");
+ }
+
+ {
+ let _timer =
+ cgcx.prof.generic_activity_with_arg("LLVM_thin_lto_import", thin_module.name());
+ if !llvm::LLVMRustPrepareThinLTOImport(thin_module.shared.data.0, llmod, target) {
+ return Err(write::llvm_err(&dcx, LlvmError::PrepareThinLtoModule));
+ }
+ save_temp_bitcode(cgcx, &module, "thin-lto-after-import");
+ }
+
+ // Alright now that we've done everything related to the ThinLTO
+ // analysis it's time to run some optimizations! Here we use the same
+ // `run_pass_manager` as the "fat" LTO above except that we tell it to
+ // populate a thin-specific pass manager, which presumably LLVM treats a
+ // little differently.
+ {
+ info!("running thin lto passes over {}", module.name);
+ run_pass_manager(cgcx, &dcx, &mut module, true)?;
+ save_temp_bitcode(cgcx, &module, "thin-lto-after-pm");
+ }
+ }*/
+ Ok(module)
+}
+
+pub struct ThinBuffer {
+ context: Arc,
+}
+
+// TODO: check if this makes sense to make ThinBuffer Send and Sync.
+unsafe impl Send for ThinBuffer {}
+unsafe impl Sync for ThinBuffer {}
+
+impl ThinBuffer {
+ pub(crate) fn new(context: &Arc) -> Self {
+ Self { context: Arc::clone(context) }
+ }
+}
+
+impl ThinBufferMethods for ThinBuffer {
+ fn data(&self) -> &[u8] {
+ &[]
+ }
+
+ fn thin_link_data(&self) -> &[u8] {
+ unimplemented!();
+ }
+}
+
+pub struct ThinData; //(Arc);
+
+fn module_name_to_str(c_str: &CStr) -> &str {
+ c_str.to_str().unwrap_or_else(|e| {
+ bug!("Encountered non-utf8 GCC module name `{}`: {}", c_str.to_string_lossy(), e)
+ })
+}
diff --git a/compiler/rustc_codegen_gcc/src/back/write.rs b/compiler/rustc_codegen_gcc/src/back/write.rs
index b9c7f72d0b72..802968979c72 100644
--- a/compiler/rustc_codegen_gcc/src/back/write.rs
+++ b/compiler/rustc_codegen_gcc/src/back/write.rs
@@ -31,6 +31,7 @@ pub(crate) unsafe fn codegen(
// NOTE: Only generate object files with GIMPLE when this environment variable is set for
// now because this requires a particular setup (same gcc/lto1/lto-wrapper commit as libgccjit).
+ // TODO: remove this environment variable.
let fat_lto = env::var("EMBED_LTO_BITCODE").as_deref() == Ok("1");
let bc_out = cgcx.output_filenames.temp_path(OutputType::Bitcode, module_name);
@@ -56,6 +57,8 @@ pub(crate) unsafe fn codegen(
.generic_activity_with_arg("GCC_module_codegen_emit_bitcode", &*module.name);
context.add_command_line_option("-flto=auto");
context.add_command_line_option("-flto-partition=one");
+ // TODO: remove since we don't want fat objects when it is for Bitcode only.
+ context.add_command_line_option("-ffat-lto-objects");
context
.compile_to_file(OutputKind::ObjectFile, bc_out.to_str().expect("path to str"));
}
@@ -104,7 +107,7 @@ pub(crate) unsafe fn codegen(
// FIXME(antoyo): segfault in dump_reproducer_to_file() might be caused by
// transmuting an rvalue to an lvalue.
// Segfault is actually in gcc::jit::reproducer::get_identifier_as_lvalue
- context.dump_reproducer_to_file(&format!("/tmp/reproducers/{}.c", module.name));
+ context.dump_reproducer_to_file(format!("/tmp/reproducers/{}.c", module.name));
println!("Dumped reproducer {}", module.name);
}
if env::var("CG_GCCJIT_DUMP_TO_FILE").as_deref() == Ok("1") {
@@ -113,17 +116,20 @@ pub(crate) unsafe fn codegen(
context.set_debug_info(true);
context.dump_to_file(path, true);
}
- if should_combine_object_files && fat_lto {
- context.add_command_line_option("-flto=auto");
- context.add_command_line_option("-flto-partition=one");
+ if should_combine_object_files {
+ if fat_lto {
+ context.add_command_line_option("-flto=auto");
+ context.add_command_line_option("-flto-partition=one");
+
+ // NOTE: without -fuse-linker-plugin, we get the following error:
+ // lto1: internal compiler error: decompressed stream: Destination buffer is too small
+ context.add_driver_option("-fuse-linker-plugin");
+ }
context.add_driver_option("-Wl,-r");
// NOTE: we need -nostdlib, otherwise, we get the following error:
// /usr/bin/ld: cannot find -lgcc_s: No such file or directory
context.add_driver_option("-nostdlib");
- // NOTE: without -fuse-linker-plugin, we get the following error:
- // lto1: internal compiler error: decompressed stream: Destination buffer is too small
- context.add_driver_option("-fuse-linker-plugin");
// NOTE: this doesn't actually generate an executable. With the above flags, it combines the .o files together in another .o.
context.compile_to_file(
diff --git a/compiler/rustc_codegen_gcc/src/base.rs b/compiler/rustc_codegen_gcc/src/base.rs
index 2a2d5741d131..be149ffe5a16 100644
--- a/compiler/rustc_codegen_gcc/src/base.rs
+++ b/compiler/rustc_codegen_gcc/src/base.rs
@@ -1,8 +1,9 @@
use std::collections::HashSet;
use std::env;
+use std::sync::Arc;
use std::time::Instant;
-use gccjit::{FunctionType, GlobalKind};
+use gccjit::{CType, FunctionType, GlobalKind};
use rustc_codegen_ssa::base::maybe_create_entry_wrapper;
use rustc_codegen_ssa::mono_item::MonoItemExt;
use rustc_codegen_ssa::traits::DebugInfoMethods;
@@ -18,8 +19,8 @@ use rustc_target::spec::PanicStrategy;
use crate::builder::Builder;
use crate::context::CodegenCx;
-use crate::GccContext;
use crate::{gcc_util, new_context, LockedTargetInfo};
+use crate::{GccContext, SyncContext};
#[cfg(feature = "master")]
pub fn visibility_to_gcc(linkage: Visibility) -> gccjit::Visibility {
@@ -135,7 +136,7 @@ pub fn compile_codegen_unit(
let target_cpu = gcc_util::target_cpu(tcx.sess);
if target_cpu != "generic" {
- context.add_command_line_option(&format!("-march={}", target_cpu));
+ context.add_command_line_option(format!("-march={}", target_cpu));
}
if tcx
@@ -181,7 +182,24 @@ pub fn compile_codegen_unit(
context.set_allow_unreachable_blocks(true);
{
- let cx = CodegenCx::new(&context, cgu, tcx, target_info.supports_128bit_int());
+ // TODO: to make it less error-prone (calling get_target_info() will add the flag
+ // -fsyntax-only), forbid the compilation when get_target_info() is called on a
+ // context.
+ let f16_type_supported = target_info.supports_target_dependent_type(CType::Float16);
+ let f32_type_supported = target_info.supports_target_dependent_type(CType::Float32);
+ let f64_type_supported = target_info.supports_target_dependent_type(CType::Float64);
+ let f128_type_supported = target_info.supports_target_dependent_type(CType::Float128);
+ // TODO: improve this to avoid passing that many arguments.
+ let cx = CodegenCx::new(
+ &context,
+ cgu,
+ tcx,
+ target_info.supports_128bit_int(),
+ f16_type_supported,
+ f32_type_supported,
+ f64_type_supported,
+ f128_type_supported,
+ );
let mono_items = cgu.items_in_deterministic_order(tcx);
for &(mono_item, data) in &mono_items {
@@ -205,7 +223,11 @@ pub fn compile_codegen_unit(
ModuleCodegen {
name: cgu_name.to_string(),
- module_llvm: GccContext { context, should_combine_object_files: false, temp_dir: None },
+ module_llvm: GccContext {
+ context: Arc::new(SyncContext::new(context)),
+ should_combine_object_files: false,
+ temp_dir: None,
+ },
kind: ModuleKind::Regular,
}
}
diff --git a/compiler/rustc_codegen_gcc/src/builder.rs b/compiler/rustc_codegen_gcc/src/builder.rs
index 4a3b6f678c44..307348f595dc 100644
--- a/compiler/rustc_codegen_gcc/src/builder.rs
+++ b/compiler/rustc_codegen_gcc/src/builder.rs
@@ -25,7 +25,7 @@ use rustc_middle::ty::layout::{
FnAbiError, FnAbiOfHelpers, FnAbiRequest, HasParamEnv, HasTyCtxt, LayoutError, LayoutOfHelpers,
TyAndLayout,
};
-use rustc_middle::ty::{ParamEnv, Ty, TyCtxt, Instance};
+use rustc_middle::ty::{Instance, ParamEnv, Ty, TyCtxt};
use rustc_span::def_id::DefId;
use rustc_span::Span;
use rustc_target::abi::{
@@ -68,7 +68,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
src: RValue<'gcc>,
order: AtomicOrdering,
) -> RValue<'gcc> {
- let size = src.get_type().get_size();
+ let size = get_maybe_pointer_size(src);
let func = self.current_func();
@@ -138,7 +138,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
failure_order: AtomicOrdering,
weak: bool,
) -> RValue<'gcc> {
- let size = src.get_type().get_size();
+ let size = get_maybe_pointer_size(src);
let compare_exchange =
self.context.get_builtin_function(&format!("__atomic_compare_exchange_{}", size));
let order = self.context.new_rvalue_from_int(self.i32_type, order.to_gcc());
@@ -153,7 +153,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
// NOTE: not sure why, but we have the wrong type here.
let int_type = compare_exchange.get_param(2).to_rvalue().get_type();
- let src = self.context.new_cast(self.location, src, int_type);
+ let src = self.context.new_bitcast(self.location, src, int_type);
self.context.new_call(
self.location,
compare_exchange,
@@ -190,8 +190,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
let casted_args: Vec<_> = param_types
.into_iter()
.zip(args.iter())
- .enumerate()
- .map(|(_i, (expected_ty, &actual_val))| {
+ .map(|(expected_ty, &actual_val)| {
let actual_ty = actual_val.get_type();
if expected_ty != actual_ty {
self.bitcast(actual_val, expected_ty)
@@ -225,7 +224,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
let mut on_stack_param_indices = FxHashSet::default();
if let Some(indices) = self.on_stack_params.borrow().get(&gcc_func) {
- on_stack_param_indices = indices.clone();
+ on_stack_param_indices.clone_from(indices);
}
if all_args_match {
@@ -253,11 +252,26 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
{
self.context.new_cast(self.location, actual_val, expected_ty)
} else if on_stack_param_indices.contains(&index) {
- actual_val.dereference(self.location).to_rvalue()
+ let ty = actual_val.get_type();
+ // It's possible that the value behind the pointer is actually not exactly
+ // the expected type, so to go around that, we add a cast before
+ // dereferencing the value.
+ if let Some(pointee_val) = ty.get_pointee()
+ && pointee_val != expected_ty
+ {
+ let new_val = self.context.new_cast(
+ self.location,
+ actual_val,
+ expected_ty.make_pointer(),
+ );
+ new_val.dereference(self.location).to_rvalue()
+ } else {
+ actual_val.dereference(self.location).to_rvalue()
+ }
} else {
assert!(
- !((actual_ty.is_vector() && !expected_ty.is_vector())
- || (!actual_ty.is_vector() && expected_ty.is_vector())),
+ (!expected_ty.is_vector() || actual_ty.is_vector())
+ && (expected_ty.is_vector() || !actual_ty.is_vector()),
"{:?} ({}) -> {:?} ({}), index: {:?}[{}]",
actual_ty,
actual_ty.is_vector(),
@@ -277,8 +291,8 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
.collect();
// NOTE: to take into account variadic functions.
- for i in casted_args.len()..args.len() {
- casted_args.push(args[i]);
+ for arg in args.iter().skip(casted_args.len()) {
+ casted_args.push(*arg);
}
Cow::Owned(casted_args)
@@ -353,7 +367,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
let function_address_names = self.function_address_names.borrow();
let original_function_name = function_address_names.get(&func_ptr);
llvm::adjust_intrinsic_arguments(
- &self,
+ self,
gcc_func,
args.into(),
&func_name,
@@ -361,7 +375,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
)
};
let args_adjusted = args.len() != previous_arg_count;
- let args = self.check_ptr_call("call", func_ptr, &*args);
+ let args = self.check_ptr_call("call", func_ptr, &args);
// gccjit requires to use the result of functions, even when it's not used.
// That's why we assign the result to a local or call add_eval().
@@ -373,7 +387,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
unsafe { RETURN_VALUE_COUNT += 1 };
let return_value = self.cx.context.new_call_through_ptr(self.location, func_ptr, &args);
let return_value = llvm::adjust_intrinsic_return_value(
- &self,
+ self,
return_value,
&func_name,
&args,
@@ -441,7 +455,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
self.block.add_assignment(
self.location,
result,
- self.cx.context.new_call(self.location, func, &args),
+ self.cx.context.new_call(self.location, func, args),
);
result.to_rvalue()
}
@@ -596,7 +610,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
) -> RValue<'gcc> {
let try_block = self.current_func().new_block("try");
- let current_block = self.block.clone();
+ let current_block = self.block;
self.block = try_block;
let call = self.call(typ, fn_attrs, None, func, args, None, instance); // TODO(antoyo): use funclet here?
self.block = current_block;
@@ -630,8 +644,9 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
then: Block<'gcc>,
catch: Block<'gcc>,
_funclet: Option<&Funclet>,
+ instance: Option>,
) -> RValue<'gcc> {
- let call_site = self.call(typ, fn_attrs, None, func, args, None);
+ let call_site = self.call(typ, fn_attrs, None, func, args, None, instance);
let condition = self.context.new_rvalue_from_int(self.bool_type, 1);
self.llbb().end_with_conditional(self.location, condition, then, catch);
if let Some(_fn_abi) = fn_abi {
@@ -749,6 +764,24 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
// FIXME(antoyo): this seems to produce the wrong result.
return self.context.new_call(self.location, fmodf, &[a, b]);
}
+
+ #[cfg(feature = "master")]
+ match self.cx.type_kind(a_type) {
+ TypeKind::Half | TypeKind::Float => {
+ let fmodf = self.context.get_builtin_function("fmodf");
+ return self.context.new_call(self.location, fmodf, &[a, b]);
+ }
+ TypeKind::Double => {
+ let fmod = self.context.get_builtin_function("fmod");
+ return self.context.new_call(self.location, fmod, &[a, b]);
+ }
+ TypeKind::FP128 => {
+ let fmodl = self.context.get_builtin_function("fmodl");
+ return self.context.new_call(self.location, fmodl, &[a, b]);
+ }
+ _ => (),
+ }
+
if let Some(vector_type) = a_type_unqualified.dyncast_vector() {
assert_eq!(a_type_unqualified, b.get_type().unqualified());
@@ -903,11 +936,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
// TODO(antoyo): It might be better to return a LValue, but fixing the rustc API is non-trivial.
self.stack_var_count.set(self.stack_var_count.get() + 1);
self.current_func()
- .new_local(
- self.location,
- ty,
- &format!("stack_var_{}", self.stack_var_count.get()),
- )
+ .new_local(self.location, ty, &format!("stack_var_{}", self.stack_var_count.get()))
.get_address(self.location)
}
@@ -993,7 +1022,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
}
}
- let val = if let Some(_) = place.val.llextra {
+ let val = if place.val.llextra.is_some() {
// FIXME: Merge with the `else` below?
OperandValue::Ref(place.val)
} else if place.layout.is_gcc_immediate() {
@@ -1125,7 +1154,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
// the following cast is required to avoid this error:
// gcc_jit_context_new_call: mismatching types for argument 2 of function "__atomic_store_4": assignment to param arg1 (type: int) from loadedValue3577 (type: unsigned int __attribute__((aligned(4))))
let int_type = atomic_store.get_param(1).to_rvalue().get_type();
- let value = self.context.new_cast(self.location, value, int_type);
+ let value = self.context.new_bitcast(self.location, value, int_type);
self.llbb().add_eval(
self.location,
self.context.new_call(self.location, atomic_store, &[ptr, value, ordering]),
@@ -1172,7 +1201,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
// NOTE: due to opaque pointers now being used, we need to cast here.
let ptr = self.context.new_cast(self.location, ptr, typ.make_pointer());
// NOTE: array indexing is always considered in bounds in GCC (TODO(antoyo): to be verified).
- let mut indices = indices.into_iter();
+ let mut indices = indices.iter();
let index = indices.next().expect("first index in inbounds_gep");
let mut result = self.context.new_array_access(self.location, ptr, *index);
for index in indices {
@@ -1589,7 +1618,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
src: RValue<'gcc>,
order: AtomicOrdering,
) -> RValue<'gcc> {
- let size = src.get_type().get_size();
+ let size = get_maybe_pointer_size(src);
let name = match op {
AtomicRmwBinOp::AtomicXchg => format!("__atomic_exchange_{}", size),
AtomicRmwBinOp::AtomicAdd => format!("__atomic_fetch_add_{}", size),
@@ -1620,7 +1649,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
let dst = self.context.new_cast(self.location, dst, volatile_void_ptr_type);
// FIXME(antoyo): not sure why, but we have the wrong type here.
let new_src_type = atomic_function.get_param(1).to_rvalue().get_type();
- let src = self.context.new_cast(self.location, src, new_src_type);
+ let src = self.context.new_bitcast(self.location, src, new_src_type);
let res = self.context.new_call(self.location, atomic_function, &[dst, src, order]);
self.context.new_cast(self.location, res, src.get_type())
}
@@ -1661,7 +1690,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
_instance: Option>,
) -> RValue<'gcc> {
// FIXME(antoyo): remove when having a proper API.
- let gcc_func = unsafe { std::mem::transmute(func) };
+ let gcc_func = unsafe { std::mem::transmute::, Function<'gcc>>(func) };
let call = if self.functions.borrow().values().any(|value| *value == gcc_func) {
self.function_call(func, args, funclet)
} else {
@@ -1676,11 +1705,6 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
fn zext(&mut self, value: RValue<'gcc>, dest_typ: Type<'gcc>) -> RValue<'gcc> {
// FIXME(antoyo): this does not zero-extend.
- if value.get_type().is_bool() && dest_typ.is_i8(&self.cx) {
- // FIXME(antoyo): hack because base::from_immediate converts i1 to i8.
- // Fix the code in codegen_ssa::base::from_immediate.
- return value;
- }
self.gcc_int_cast(value, dest_typ)
}
@@ -2049,7 +2073,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
self.context.new_rvalue_from_vector(self.location, mask_type, &vector_elements);
let shifted = self.context.new_rvalue_vector_perm(self.location, res, res, mask);
shift *= 2;
- res = op(res, shifted, &self.context);
+ res = op(res, shifted, self.context);
}
self.context
.new_vector_access(self.location, res, self.context.new_rvalue_zero(self.int_type))
@@ -2065,7 +2089,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
}
pub fn vector_reduce_op(&mut self, src: RValue<'gcc>, op: BinaryOp) -> RValue<'gcc> {
- let loc = self.location.clone();
+ let loc = self.location;
self.vector_reduce(src, |a, b, context| context.new_binary_op(loc, op, a.get_type(), a, b))
}
@@ -2082,7 +2106,6 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
let vector_type = src.get_type().unqualified().dyncast_vector().expect("vector type");
let element_count = vector_type.get_num_units();
(0..element_count)
- .into_iter()
.map(|i| {
self.context
.new_vector_access(
@@ -2113,7 +2136,6 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
let vector_type = src.get_type().unqualified().dyncast_vector().expect("vector type");
let element_count = vector_type.get_num_units();
(0..element_count)
- .into_iter()
.map(|i| {
self.context
.new_vector_access(
@@ -2133,7 +2155,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
// Inspired by Hacker's Delight min implementation.
pub fn vector_reduce_min(&mut self, src: RValue<'gcc>) -> RValue<'gcc> {
- let loc = self.location.clone();
+ let loc = self.location;
self.vector_reduce(src, |a, b, context| {
let differences_or_zeros = difference_or_zero(loc, a, b, context);
context.new_binary_op(loc, BinaryOp::Plus, b.get_type(), b, differences_or_zeros)
@@ -2142,7 +2164,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
// Inspired by Hacker's Delight max implementation.
pub fn vector_reduce_max(&mut self, src: RValue<'gcc>) -> RValue<'gcc> {
- let loc = self.location.clone();
+ let loc = self.location;
self.vector_reduce(src, |a, b, context| {
let differences_or_zeros = difference_or_zero(loc, a, b, context);
context.new_binary_op(loc, BinaryOp::Minus, a.get_type(), a, differences_or_zeros)
@@ -2337,7 +2359,7 @@ impl<'tcx> HasParamEnv<'tcx> for Builder<'_, '_, 'tcx> {
impl<'tcx> HasTargetSpec for Builder<'_, '_, 'tcx> {
fn target_spec(&self) -> &Target {
- &self.cx.target_spec()
+ self.cx.target_spec()
}
}
@@ -2422,3 +2444,19 @@ impl ToGccOrdering for AtomicOrdering {
ordering as i32
}
}
+
+// Needed because gcc 12 `get_size()` doesn't work on pointers.
+#[cfg(feature = "master")]
+fn get_maybe_pointer_size(value: RValue<'_>) -> u32 {
+ value.get_type().get_size()
+}
+
+#[cfg(not(feature = "master"))]
+fn get_maybe_pointer_size(value: RValue<'_>) -> u32 {
+ let type_ = value.get_type();
+ if type_.get_pointee().is_some() {
+ std::mem::size_of::<*const ()>() as _
+ } else {
+ type_.get_size()
+ }
+}
diff --git a/compiler/rustc_codegen_gcc/src/callee.rs b/compiler/rustc_codegen_gcc/src/callee.rs
index 84f49b6856d4..9ad2e90122f5 100644
--- a/compiler/rustc_codegen_gcc/src/callee.rs
+++ b/compiler/rustc_codegen_gcc/src/callee.rs
@@ -28,7 +28,7 @@ pub fn get_fn<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, instance: Instance<'tcx>)
let fn_abi = cx.fn_abi_of_instance(instance, ty::List::empty());
- let func = if let Some(_func) = cx.get_declared_value(&sym) {
+ let func = if let Some(_func) = cx.get_declared_value(sym) {
// FIXME(antoyo): we never reach this because get_declared_value only returns global variables
// and here we try to get a function.
unreachable!();
@@ -68,7 +68,7 @@ pub fn get_fn<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, instance: Instance<'tcx>)
}*/
} else {
cx.linkage.set(FunctionType::Extern);
- let func = cx.declare_fn(&sym, &fn_abi);
+ let func = cx.declare_fn(sym, fn_abi);
attributes::from_fn_attrs(cx, func, instance);
diff --git a/compiler/rustc_codegen_gcc/src/common.rs b/compiler/rustc_codegen_gcc/src/common.rs
index fa8a1ec037c5..19333689aaa9 100644
--- a/compiler/rustc_codegen_gcc/src/common.rs
+++ b/compiler/rustc_codegen_gcc/src/common.rs
@@ -21,7 +21,7 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
fn global_string(&self, string: &str) -> LValue<'gcc> {
// TODO(antoyo): handle non-null-terminated strings.
- let string = self.context.new_string_literal(&*string);
+ let string = self.context.new_string_literal(string);
let sym = self.generate_local_symbol_name("str");
let global = self.declare_private_global(&sym, self.val_ty(string));
global.global_set_initializer_rvalue(string);
@@ -187,7 +187,8 @@ impl<'gcc, 'tcx> ConstMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
return self
.context
.new_rvalue_from_double(ty, f32::from_bits(data as u32) as f64);
- } else if ty == self.double_type {
+ }
+ if ty == self.double_type {
return self.context.new_rvalue_from_double(ty, f64::from_bits(data as u64));
}
@@ -297,7 +298,7 @@ impl<'gcc, 'tcx> SignType<'gcc, 'tcx> for Type<'gcc> {
} else if self.is_ulonglong(cx) {
cx.longlong_type
} else {
- self.clone()
+ *self
}
}
@@ -323,7 +324,7 @@ impl<'gcc, 'tcx> SignType<'gcc, 'tcx> for Type<'gcc> {
} else if self.is_longlong(cx) {
cx.ulonglong_type
} else {
- self.clone()
+ *self
}
}
}
@@ -436,7 +437,7 @@ impl<'gcc, 'tcx> TypeReflection<'gcc, 'tcx> for Type<'gcc> {
}
fn is_vector(&self) -> bool {
- let mut typ = self.clone();
+ let mut typ = *self;
loop {
if typ.dyncast_vector().is_some() {
return true;
diff --git a/compiler/rustc_codegen_gcc/src/consts.rs b/compiler/rustc_codegen_gcc/src/consts.rs
index 3d73a60b2550..ba7e08e33efa 100644
--- a/compiler/rustc_codegen_gcc/src/consts.rs
+++ b/compiler/rustc_codegen_gcc/src/consts.rs
@@ -1,15 +1,16 @@
#[cfg(feature = "master")]
use gccjit::{FnAttribute, VarAttribute, Visibility};
-use gccjit::{Function, GlobalKind, LValue, RValue, ToRValue};
-use rustc_codegen_ssa::traits::{BaseTypeMethods, ConstMethods, DerivedTypeMethods, StaticMethods};
+use gccjit::{Function, GlobalKind, LValue, RValue, ToRValue, Type};
+use rustc_codegen_ssa::traits::{BaseTypeMethods, ConstMethods, StaticMethods};
+use rustc_hir::def::DefKind;
+use rustc_middle::bug;
use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs};
use rustc_middle::mir::interpret::{
self, read_target_uint, ConstAllocation, ErrorHandled, Scalar as InterpScalar,
};
-use rustc_middle::mir::mono::MonoItem;
use rustc_middle::span_bug;
use rustc_middle::ty::layout::LayoutOf;
-use rustc_middle::ty::{self, Instance, Ty};
+use rustc_middle::ty::{self, Instance};
use rustc_span::def_id::DefId;
use rustc_target::abi::{self, Align, HasDataLayout, Primitive, Size, WrappingRange};
@@ -63,16 +64,15 @@ impl<'gcc, 'tcx> StaticMethods for CodegenCx<'gcc, 'tcx> {
global_value
}
+ #[cfg_attr(not(feature = "master"), allow(unused_mut))]
fn codegen_static(&self, def_id: DefId) {
let attrs = self.tcx.codegen_fn_attrs(def_id);
- let value = match codegen_static_initializer(&self, def_id) {
- Ok((value, _)) => value,
+ let Ok((value, alloc)) = codegen_static_initializer(self, def_id) else {
// Error has already been reported
- Err(_) => return,
+ return;
};
-
- let global = self.get_static(def_id);
+ let alloc = alloc.inner();
// boolean SSA values are i1, but they have to be stored in i8 slots,
// otherwise some LLVM optimization passes don't work as expected
@@ -81,23 +81,25 @@ impl<'gcc, 'tcx> StaticMethods for CodegenCx<'gcc, 'tcx> {
unimplemented!();
};
- let instance = Instance::mono(self.tcx, def_id);
- let ty = instance.ty(self.tcx, ty::ParamEnv::reveal_all());
- let gcc_type = self.layout_of(ty).gcc_type(self);
+ let is_thread_local = attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL);
+ let global = self.get_static_inner(def_id, val_llty);
- set_global_alignment(self, global, self.align_of(ty));
+ #[cfg(feature = "master")]
+ if global.to_rvalue().get_type() != val_llty {
+ global.to_rvalue().set_type(val_llty);
+ }
+ set_global_alignment(self, global, alloc.align);
- let value = self.bitcast_if_needed(value, gcc_type);
global.global_set_initializer_rvalue(value);
// As an optimization, all shared statics which do not have interior
// mutability are placed into read-only memory.
- if !self.tcx.static_mutability(def_id).unwrap().is_mut() && self.type_is_freeze(ty) {
+ if alloc.mutability.is_not() {
#[cfg(feature = "master")]
global.global_set_readonly();
}
- if attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL) {
+ if is_thread_local {
// Do not allow LLVM to change the alignment of a TLS on macOS.
//
// By default a global's alignment can be freely increased.
@@ -205,35 +207,49 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
pub fn get_static(&self, def_id: DefId) -> LValue<'gcc> {
let instance = Instance::mono(self.tcx, def_id);
- let fn_attrs = self.tcx.codegen_fn_attrs(def_id);
+ let DefKind::Static { nested, .. } = self.tcx.def_kind(def_id) else { bug!() };
+ // Nested statics do not have a type, so pick a random type and let `define_static` figure out
+ // the gcc type from the actual evaluated initializer.
+ let gcc_type = if nested {
+ self.type_i8()
+ } else {
+ let ty = instance.ty(self.tcx, ty::ParamEnv::reveal_all());
+ self.layout_of(ty).gcc_type(self)
+ };
+
+ self.get_static_inner(def_id, gcc_type)
+ }
+
+ pub(crate) fn get_static_inner(&self, def_id: DefId, gcc_type: Type<'gcc>) -> LValue<'gcc> {
+ let instance = Instance::mono(self.tcx, def_id);
if let Some(&global) = self.instances.borrow().get(&instance) {
+ trace!("used cached value");
return global;
}
- let defined_in_current_codegen_unit =
- self.codegen_unit.items().contains_key(&MonoItem::Static(def_id));
- assert!(
- !defined_in_current_codegen_unit,
- "consts::get_static() should always hit the cache for \
- statics defined in the same CGU, but did not for `{:?}`",
- def_id
- );
-
- let ty = instance.ty(self.tcx, ty::ParamEnv::reveal_all());
+ // FIXME: Once we stop removing globals in `codegen_static`, we can uncomment this code.
+ // let defined_in_current_codegen_unit =
+ // self.codegen_unit.items().contains_key(&MonoItem::Static(def_id));
+ // assert!(
+ // !defined_in_current_codegen_unit,
+ // "consts::get_static() should always hit the cache for \
+ // statics defined in the same CGU, but did not for `{:?}`",
+ // def_id
+ // );
let sym = self.tcx.symbol_name(instance).name;
+ let fn_attrs = self.tcx.codegen_fn_attrs(def_id);
let global = if def_id.is_local() && !self.tcx.is_foreign_item(def_id) {
- let llty = self.layout_of(ty).gcc_type(self);
if let Some(global) = self.get_declared_value(sym) {
- if self.val_ty(global) != self.type_ptr_to(llty) {
+ if self.val_ty(global) != self.type_ptr_to(gcc_type) {
span_bug!(self.tcx.def_span(def_id), "Conflicting types for static");
}
}
let is_tls = fn_attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL);
let global = self.declare_global(
- &sym,
- llty,
+ sym,
+ gcc_type,
GlobalKind::Exported,
is_tls,
fn_attrs.link_section,
@@ -246,7 +262,7 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
global
} else {
- check_and_apply_linkage(&self, &fn_attrs, ty, sym)
+ check_and_apply_linkage(self, fn_attrs, gcc_type, sym)
};
if !def_id.is_local() {
@@ -360,18 +376,14 @@ fn codegen_static_initializer<'gcc, 'tcx>(
fn check_and_apply_linkage<'gcc, 'tcx>(
cx: &CodegenCx<'gcc, 'tcx>,
attrs: &CodegenFnAttrs,
- ty: Ty<'tcx>,
+ gcc_type: Type<'gcc>,
sym: &str,
) -> LValue<'gcc> {
let is_tls = attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL);
- let gcc_type = cx.layout_of(ty).gcc_type(cx);
if let Some(linkage) = attrs.import_linkage {
// Declare a symbol `foo` with the desired linkage.
- let global1 = cx.declare_global_with_linkage(
- &sym,
- cx.type_i8(),
- base::global_linkage_to_gcc(linkage),
- );
+ let global1 =
+ cx.declare_global_with_linkage(sym, cx.type_i8(), base::global_linkage_to_gcc(linkage));
// Declare an internal global `extern_with_linkage_foo` which
// is initialized with the address of `foo`. If `foo` is
@@ -380,7 +392,7 @@ fn check_and_apply_linkage<'gcc, 'tcx>(
// `extern_with_linkage_foo` will instead be initialized to
// zero.
let mut real_name = "_rust_extern_with_linkage_".to_string();
- real_name.push_str(&sym);
+ real_name.push_str(sym);
let global2 = cx.define_global(&real_name, gcc_type, is_tls, attrs.link_section);
// TODO(antoyo): set linkage.
let value = cx.const_ptrcast(global1.get_address(None), gcc_type);
@@ -397,6 +409,6 @@ fn check_and_apply_linkage<'gcc, 'tcx>(
// don't do this then linker errors can be generated where the linker
// complains that one object files has a thread local version of the
// symbol and another one doesn't.
- cx.declare_global(&sym, gcc_type, GlobalKind::Imported, is_tls, attrs.link_section)
+ cx.declare_global(sym, gcc_type, GlobalKind::Imported, is_tls, attrs.link_section)
}
}
diff --git a/compiler/rustc_codegen_gcc/src/context.rs b/compiler/rustc_codegen_gcc/src/context.rs
index 1d689c9ac0ef..86a5000a7232 100644
--- a/compiler/rustc_codegen_gcc/src/context.rs
+++ b/compiler/rustc_codegen_gcc/src/context.rs
@@ -68,6 +68,10 @@ pub struct CodegenCx<'gcc, 'tcx> {
pub sizet_type: Type<'gcc>,
pub supports_128bit_integers: bool,
+ pub supports_f16_type: bool,
+ pub supports_f32_type: bool,
+ pub supports_f64_type: bool,
+ pub supports_f128_type: bool,
pub float_type: Type<'gcc>,
pub double_type: Type<'gcc>,
@@ -110,7 +114,7 @@ pub struct CodegenCx<'gcc, 'tcx> {
local_gen_sym_counter: Cell,
eh_personality: Cell>>,
- #[cfg(feature="master")]
+ #[cfg(feature = "master")]
pub rust_try_fn: Cell , Function<'gcc>)>>,
pub pointee_infos: RefCell, Size), Option>>,
@@ -122,16 +126,21 @@ pub struct CodegenCx<'gcc, 'tcx> {
/// FIXME(antoyo): fix the rustc API to avoid having this hack.
pub structs_as_pointer: RefCell>>,
- #[cfg(feature="master")]
+ #[cfg(feature = "master")]
pub cleanup_blocks: RefCell>>,
}
impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
+ #[allow(clippy::too_many_arguments)]
pub fn new(
context: &'gcc Context<'gcc>,
codegen_unit: &'tcx CodegenUnit<'tcx>,
tcx: TyCtxt<'tcx>,
supports_128bit_integers: bool,
+ supports_f16_type: bool,
+ supports_f32_type: bool,
+ supports_f64_type: bool,
+ supports_f128_type: bool,
) -> Self {
let create_type = |ctype, rust_type| {
let layout = tcx.layout_of(ParamEnv::reveal_all().and(rust_type)).unwrap();
@@ -304,6 +313,10 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
sizet_type,
supports_128bit_integers,
+ supports_f16_type,
+ supports_f32_type,
+ supports_f64_type,
+ supports_f128_type,
float_type,
double_type,
@@ -324,11 +337,11 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
struct_types: Default::default(),
local_gen_sym_counter: Cell::new(0),
eh_personality: Cell::new(None),
- #[cfg(feature="master")]
+ #[cfg(feature = "master")]
rust_try_fn: Cell::new(None),
pointee_infos: Default::default(),
structs_as_pointer: Default::default(),
- #[cfg(feature="master")]
+ #[cfg(feature = "master")]
cleanup_blocks: Default::default(),
};
// TODO(antoyo): instead of doing this, add SsizeT to libgccjit.
@@ -385,7 +398,7 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
}
pub fn sess(&self) -> &'tcx Session {
- &self.tcx.sess
+ self.tcx.sess
}
pub fn bitcast_if_needed(
@@ -432,7 +445,9 @@ impl<'gcc, 'tcx> MiscMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
let func_name = self.tcx.symbol_name(instance).name;
let func = if self.intrinsics.borrow().contains_key(func_name) {
- self.intrinsics.borrow()[func_name].clone()
+ self.intrinsics.borrow()[func_name]
+ } else if let Some(variable) = self.get_declared_value(func_name) {
+ return variable;
} else {
get_fn(self, instance)
};
@@ -485,7 +500,7 @@ impl<'gcc, 'tcx> MiscMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
let symbol_name = tcx.symbol_name(instance).name;
let fn_abi = self.fn_abi_of_instance(instance, ty::List::empty());
self.linkage.set(FunctionType::Extern);
- let func = self.declare_fn(symbol_name, &fn_abi);
+ let func = self.declare_fn(symbol_name, fn_abi);
let func: RValue<'gcc> = unsafe { std::mem::transmute(func) };
func
}
@@ -496,7 +511,7 @@ impl<'gcc, 'tcx> MiscMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
"rust_eh_personality"
};
let func = self.declare_func(name, self.type_i32(), &[], true);
- unsafe { std::mem::transmute(func) }
+ unsafe { std::mem::transmute::, RValue<'gcc>>(func) }
}
};
// TODO(antoyo): apply target cpu attributes.
@@ -505,7 +520,7 @@ impl<'gcc, 'tcx> MiscMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
}
fn sess(&self) -> &Session {
- &self.tcx.sess
+ self.tcx.sess
}
fn codegen_unit(&self) -> &'tcx CodegenUnit<'tcx> {
@@ -522,7 +537,7 @@ impl<'gcc, 'tcx> MiscMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
fn declare_c_main(&self, fn_type: Self::Type) -> Option {
let entry_name = self.sess().target.entry_name.as_ref();
- if self.get_declared_value(entry_name).is_none() {
+ if !self.functions.borrow().contains_key(entry_name) {
Some(self.declare_entry_fn(entry_name, fn_type, ()))
} else {
// If the symbol already exists, it is an error: for example, the user wrote
@@ -614,7 +629,7 @@ impl<'b, 'tcx> CodegenCx<'b, 'tcx> {
// user defined names
let mut name = String::with_capacity(prefix.len() + 6);
name.push_str(prefix);
- name.push_str(".");
+ name.push('.');
name.push_str(&(idx as u64).to_base(ALPHANUMERIC_ONLY));
name
}
diff --git a/compiler/rustc_codegen_gcc/src/debuginfo.rs b/compiler/rustc_codegen_gcc/src/debuginfo.rs
index 1d0a6d9f09bb..3d9ea278a639 100644
--- a/compiler/rustc_codegen_gcc/src/debuginfo.rs
+++ b/compiler/rustc_codegen_gcc/src/debuginfo.rs
@@ -90,7 +90,7 @@ fn compute_mir_scopes<'gcc, 'tcx>(
/// FIXME(tempdragon/?): Add Scope Support Here.
fn make_mir_scope<'gcc, 'tcx>(
cx: &CodegenCx<'gcc, 'tcx>,
- instance: Instance<'tcx>,
+ _instance: Instance<'tcx>,
mir: &Body<'tcx>,
variables: &Option>,
debug_context: &mut FunctionDebugContext<'tcx, (), Location<'gcc>>,
@@ -103,7 +103,7 @@ fn make_mir_scope<'gcc, 'tcx>(
let scope_data = &mir.source_scopes[scope];
let parent_scope = if let Some(parent) = scope_data.parent_scope {
- make_mir_scope(cx, instance, mir, variables, debug_context, instantiated, parent);
+ make_mir_scope(cx, _instance, mir, variables, debug_context, instantiated, parent);
debug_context.scopes[parent]
} else {
// The root is the function itself.
@@ -117,7 +117,7 @@ fn make_mir_scope<'gcc, 'tcx>(
return;
};
- if let Some(vars) = variables {
+ if let Some(ref vars) = *variables {
if !vars.contains(scope) && scope_data.inlined.is_none() {
// Do not create a DIScope if there are no variables defined in this
// MIR `SourceScope`, and it's not `inlined`, to avoid debuginfo bloat.
@@ -135,8 +135,14 @@ fn make_mir_scope<'gcc, 'tcx>(
let inlined_at = scope_data.inlined.map(|(_, callsite_span)| {
// FIXME(eddyb) this doesn't account for the macro-related
// `Span` fixups that `rustc_codegen_ssa::mir::debuginfo` does.
- let callsite_scope = parent_scope.adjust_dbg_scope_for_span(cx, callsite_span);
- cx.dbg_loc(callsite_scope, parent_scope.inlined_at, callsite_span)
+
+ // TODO(tempdragon): Add scope support and then revert to cg_llvm version of this closure
+ // NOTE: These variables passed () here.
+ // Changed to comply to clippy.
+
+ /* let callsite_scope = */
+ parent_scope.adjust_dbg_scope_for_span(cx, callsite_span);
+ cx.dbg_loc(/* callsite_scope */ (), parent_scope.inlined_at, callsite_span)
});
let p_inlined_at = parent_scope.inlined_at;
// TODO(tempdragon): dbg_scope: Add support for scope extension here.
@@ -224,7 +230,7 @@ impl<'gcc, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
file_end_pos: BytePos(0),
};
let mut fn_debug_context = FunctionDebugContext {
- scopes: IndexVec::from_elem(empty_scope, &mir.source_scopes.as_slice()),
+ scopes: IndexVec::from_elem(empty_scope, mir.source_scopes.as_slice()),
inlined_function_scopes: Default::default(),
};
@@ -273,16 +279,19 @@ impl<'gcc, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
) -> Self::DILocation {
let pos = span.lo();
let DebugLoc { file, line, col } = self.lookup_debug_loc(pos);
- let loc = match &file.name {
- rustc_span::FileName::Real(name) => match name {
- rustc_span::RealFileName::LocalPath(name) => {
+ let loc = match file.name {
+ rustc_span::FileName::Real(ref name) => match *name {
+ rustc_span::RealFileName::LocalPath(ref name) => {
if let Some(name) = name.to_str() {
self.context.new_location(name, line as i32, col as i32)
} else {
Location::null()
}
}
- rustc_span::RealFileName::Remapped { local_path, virtual_name: _ } => {
+ rustc_span::RealFileName::Remapped {
+ ref local_path,
+ virtual_name: ref _unused,
+ } => {
if let Some(name) = local_path.as_ref() {
if let Some(name) = name.to_str() {
self.context.new_location(name, line as i32, col as i32)
diff --git a/compiler/rustc_codegen_gcc/src/declare.rs b/compiler/rustc_codegen_gcc/src/declare.rs
index db6edbab12d4..a2b158ee0a7e 100644
--- a/compiler/rustc_codegen_gcc/src/declare.rs
+++ b/compiler/rustc_codegen_gcc/src/declare.rs
@@ -35,7 +35,7 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
pub fn declare_unnamed_global(&self, ty: Type<'gcc>) -> LValue<'gcc> {
let name = self.generate_local_symbol_name("global");
- self.context.new_global(None, GlobalKind::Internal, ty, &name)
+ self.context.new_global(None, GlobalKind::Internal, ty, name)
}
pub fn declare_global_with_linkage(
@@ -176,16 +176,14 @@ fn declare_raw_fn<'gcc>(
cx.functions.borrow()[name]
} else {
let params: Vec<_> = param_types
- .into_iter()
+ .iter()
.enumerate()
- .map(|(index, param)| {
- cx.context.new_parameter(None, *param, &format!("param{}", index))
- }) // TODO(antoyo): set name.
+ .map(|(index, param)| cx.context.new_parameter(None, *param, format!("param{}", index))) // TODO(antoyo): set name.
.collect();
#[cfg(not(feature = "master"))]
- let name = mangle_name(name);
+ let name = &mangle_name(name);
let func =
- cx.context.new_function(None, cx.linkage.get(), return_type, ¶ms, &name, variadic);
+ cx.context.new_function(None, cx.linkage.get(), return_type, ¶ms, name, variadic);
cx.functions.borrow_mut().insert(name.to_string(), func);
#[cfg(feature = "master")]
@@ -200,10 +198,10 @@ fn declare_raw_fn<'gcc>(
// create a wrapper function that calls rust_eh_personality.
let params: Vec<_> = param_types
- .into_iter()
+ .iter()
.enumerate()
.map(|(index, param)| {
- cx.context.new_parameter(None, *param, &format!("param{}", index))
+ cx.context.new_parameter(None, *param, format!("param{}", index))
}) // TODO(antoyo): set name.
.collect();
let gcc_func = cx.context.new_function(
diff --git a/compiler/rustc_codegen_gcc/src/int.rs b/compiler/rustc_codegen_gcc/src/int.rs
index 841bcf592e48..e4c5eb913731 100644
--- a/compiler/rustc_codegen_gcc/src/int.rs
+++ b/compiler/rustc_codegen_gcc/src/int.rs
@@ -2,8 +2,6 @@
//! This module exists because some integer types are not supported on some gcc platforms, e.g.
//! 128-bit integers on 32-bit platforms and thus require to be handled manually.
-use std::convert::TryFrom;
-
use gccjit::{BinaryOp, ComparisonOp, FunctionType, Location, RValue, ToRValue, Type, UnaryOp};
use rustc_codegen_ssa::common::{IntPredicate, TypeKind};
use rustc_codegen_ssa::traits::{BackendTypes, BaseTypeMethods, BuilderMethods, OverflowOp};
@@ -40,7 +38,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
self.cx.context.new_unary_op(self.location, operation, typ, a)
} else {
let element_type = typ.dyncast_array().expect("element type");
- self.from_low_high_rvalues(
+ self.concat_low_high_rvalues(
typ,
self.cx.context.new_unary_op(
self.location,
@@ -83,7 +81,19 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
let b = self.context.new_cast(self.location, b, a_type);
a >> b
} else {
- a >> b
+ let a_size = a_type.get_size();
+ let b_size = b_type.get_size();
+ match a_size.cmp(&b_size) {
+ std::cmp::Ordering::Less => {
+ let a = self.context.new_cast(self.location, a, b_type);
+ a >> b
+ }
+ std::cmp::Ordering::Equal => a >> b,
+ std::cmp::Ordering::Greater => {
+ let b = self.context.new_cast(self.location, b, a_type);
+ a >> b
+ }
+ }
}
} else if a_type.is_vector() && a_type.is_vector() {
a >> b
@@ -114,7 +124,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
let shift_value = self.gcc_sub(b, sixty_four);
let high = self.high(a);
let sign = if a_type.is_signed(self) { high >> sixty_three } else { zero };
- let array_value = self.from_low_high_rvalues(a_type, high >> shift_value, sign);
+ let array_value = self.concat_low_high_rvalues(a_type, high >> shift_value, sign);
then_block.add_assignment(self.location, result, array_value);
then_block.end_with_jump(self.location, after_block);
@@ -126,12 +136,15 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
let shift_value = self.gcc_sub(sixty_four, b);
// NOTE: cast low to its unsigned type in order to perform a logical right shift.
- let unsigned_type = native_int_type.to_unsigned(&self.cx);
+ let unsigned_type = native_int_type.to_unsigned(self.cx);
let casted_low = self.context.new_cast(self.location, self.low(a), unsigned_type);
let shifted_low = casted_low >> self.context.new_cast(self.location, b, unsigned_type);
let shifted_low = self.context.new_cast(self.location, shifted_low, native_int_type);
- let array_value =
- self.from_low_high_rvalues(a_type, (high << shift_value) | shifted_low, high >> b);
+ let array_value = self.concat_low_high_rvalues(
+ a_type,
+ (high << shift_value) | shifted_low,
+ high >> b,
+ );
actual_else_block.add_assignment(self.location, result, array_value);
actual_else_block.end_with_jump(self.location, after_block);
@@ -255,10 +268,10 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
) -> (::Value, ::Value) {
use rustc_middle::ty::{Int, IntTy::*, Uint, UintTy::*};
- let new_kind = match typ.kind() {
+ let new_kind = match *typ.kind() {
Int(t @ Isize) => Int(t.normalize(self.tcx.sess.target.pointer_width)),
Uint(t @ Usize) => Uint(t.normalize(self.tcx.sess.target.pointer_width)),
- t @ (Uint(_) | Int(_)) => t.clone(),
+ t @ (Uint(_) | Int(_)) => t,
_ => panic!("tried to get overflow intrinsic for op applied to non-int type"),
};
@@ -344,7 +357,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
}
};
- let intrinsic = self.context.get_builtin_function(&name);
+ let intrinsic = self.context.get_builtin_function(name);
let res = self
.current_func()
// TODO(antoyo): is it correct to use rhs type instead of the parameter typ?
@@ -454,7 +467,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
let native_int_type = a_type.dyncast_array().expect("get element type");
// NOTE: cast low to its unsigned type in order to perform a comparison correctly (e.g.
// the sign is only on high).
- let unsigned_type = native_int_type.to_unsigned(&self.cx);
+ let unsigned_type = native_int_type.to_unsigned(self.cx);
let lhs_low = self.context.new_cast(self.location, self.low(lhs), unsigned_type);
let rhs_low = self.context.new_cast(self.location, self.low(rhs), unsigned_type);
@@ -589,7 +602,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
| IntPredicate::IntULT
| IntPredicate::IntULE => {
if !a_type.is_vector() {
- let unsigned_type = a_type.to_unsigned(&self.cx);
+ let unsigned_type = a_type.to_unsigned(self.cx);
lhs = self.context.new_cast(self.location, lhs, unsigned_type);
rhs = self.context.new_cast(self.location, rhs, unsigned_type);
}
@@ -612,7 +625,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
{
a ^ b
} else {
- self.from_low_high_rvalues(
+ self.concat_low_high_rvalues(
a_type,
self.low(a) ^ self.low(b),
self.high(a) ^ self.high(b),
@@ -635,7 +648,19 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
let b = self.context.new_cast(self.location, b, a_type);
a << b
} else {
- a << b
+ let a_size = a_type.get_size();
+ let b_size = b_type.get_size();
+ match a_size.cmp(&b_size) {
+ std::cmp::Ordering::Less => {
+ let a = self.context.new_cast(self.location, a, b_type);
+ a << b
+ }
+ std::cmp::Ordering::Equal => a << b,
+ std::cmp::Ordering::Greater => {
+ let b = self.context.new_cast(self.location, b, a_type);
+ a << b
+ }
+ }
}
} else if a_type.is_vector() && a_type.is_vector() {
a << b
@@ -661,7 +686,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
self.llbb().end_with_conditional(self.location, condition, then_block, else_block);
let array_value =
- self.from_low_high_rvalues(a_type, zero, self.low(a) << (b - sixty_four));
+ self.concat_low_high_rvalues(a_type, zero, self.low(a) << (b - sixty_four));
then_block.add_assignment(self.location, result, array_value);
then_block.end_with_jump(self.location, after_block);
@@ -673,13 +698,13 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
// NOTE: cast low to its unsigned type in order to perform a logical right shift.
// TODO(antoyo): adjust this ^ comment.
- let unsigned_type = native_int_type.to_unsigned(&self.cx);
+ let unsigned_type = native_int_type.to_unsigned(self.cx);
let casted_low = self.context.new_cast(self.location, self.low(a), unsigned_type);
let shift_value = self.context.new_cast(self.location, sixty_four - b, unsigned_type);
let high_low =
self.context.new_cast(self.location, casted_low >> shift_value, native_int_type);
- let array_value = self.from_low_high_rvalues(
+ let array_value = self.concat_low_high_rvalues(
a_type,
self.low(a) << b,
(self.high(a) << b) | high_low,
@@ -708,7 +733,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
// NOTE: we also need to swap the two elements here, in addition to swapping inside
// the elements themselves like done above.
- return self.from_low_high_rvalues(arg_type, swapped_msb, swapped_lsb);
+ return self.concat_low_high_rvalues(arg_type, swapped_msb, swapped_lsb);
}
// TODO(antoyo): check if it's faster to use string literals and a
@@ -727,10 +752,10 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
pub fn gcc_int(&self, typ: Type<'gcc>, int: i64) -> RValue<'gcc> {
if self.is_native_int_type_or_bool(typ) {
- self.context.new_rvalue_from_long(typ, i64::try_from(int).expect("i64::try_from"))
+ self.context.new_rvalue_from_long(typ, int)
} else {
// NOTE: set the sign in high.
- self.from_low_high(typ, int, -(int.is_negative() as i64))
+ self.concat_low_high(typ, int, -(int.is_negative() as i64))
}
}
@@ -740,10 +765,9 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
let num = self.context.new_rvalue_from_long(self.u64_type, int as i64);
self.gcc_int_cast(num, typ)
} else if self.is_native_int_type_or_bool(typ) {
- self.context
- .new_rvalue_from_long(typ, u64::try_from(int).expect("u64::try_from") as i64)
+ self.context.new_rvalue_from_long(typ, int as i64)
} else {
- self.from_low_high(typ, int as i64, 0)
+ self.concat_low_high(typ, int as i64, 0)
}
}
@@ -760,7 +784,7 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
let shift = high << sixty_four;
shift | self.context.new_cast(None, low, typ)
} else {
- self.from_low_high(typ, low as i64, high as i64)
+ self.concat_low_high(typ, low as i64, high as i64)
}
} else if typ.is_i128(self) {
// FIXME(antoyo): libgccjit cannot create 128-bit values yet.
@@ -775,7 +799,7 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
if self.is_native_int_type_or_bool(typ) {
self.context.new_rvalue_zero(typ)
} else {
- self.from_low_high(typ, 0, 0)
+ self.concat_low_high(typ, 0, 0)
}
}
@@ -813,7 +837,7 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
"both types should either be native or non-native for or operation"
);
let native_int_type = a_type.dyncast_array().expect("get element type");
- self.from_low_high_rvalues(
+ self.concat_low_high_rvalues(
a_type,
self.context.new_binary_op(
loc,
@@ -858,7 +882,7 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
let is_negative =
self.context.new_comparison(None, ComparisonOp::LessThan, value, zero);
let is_negative = self.gcc_int_cast(is_negative, dest_element_type);
- self.from_low_high_rvalues(
+ self.concat_low_high_rvalues(
dest_typ,
self.context.new_cast(None, value, dest_element_type),
self.context.new_unary_op(None, UnaryOp::Minus, dest_element_type, is_negative),
@@ -926,7 +950,7 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
return self.context.new_cast(None, value, dest_typ);
}
- debug_assert!(value_type.dyncast_array().is_some());
+ debug_assert!(dest_typ.dyncast_array().is_some());
let name_suffix = match self.type_kind(value_type) {
TypeKind::Float => "sfti",
TypeKind::Double => "dfti",
@@ -978,7 +1002,7 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
.to_rvalue()
}
- fn from_low_high_rvalues(
+ fn concat_low_high_rvalues(
&self,
typ: Type<'gcc>,
low: RValue<'gcc>,
@@ -993,7 +1017,7 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
self.context.new_array_constructor(None, typ, &values)
}
- fn from_low_high(&self, typ: Type<'gcc>, low: i64, high: i64) -> RValue<'gcc> {
+ fn concat_low_high(&self, typ: Type<'gcc>, low: i64, high: i64) -> RValue<'gcc> {
let (first, last) = match self.sess().target.options.endian {
Endian::Little => (low, high),
Endian::Big => (high, low),
diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/archs.rs b/compiler/rustc_codegen_gcc/src/intrinsic/archs.rs
index c4ae1751fa05..f75009337898 100644
--- a/compiler/rustc_codegen_gcc/src/intrinsic/archs.rs
+++ b/compiler/rustc_codegen_gcc/src/intrinsic/archs.rs
@@ -74,6 +74,10 @@ match name {
"llvm.amdgcn.cvt.sr.bf8.f32" => "__builtin_amdgcn_cvt_sr_bf8_f32",
"llvm.amdgcn.cvt.sr.fp8.f32" => "__builtin_amdgcn_cvt_sr_fp8_f32",
"llvm.amdgcn.dispatch.id" => "__builtin_amdgcn_dispatch_id",
+ "llvm.amdgcn.dot4.f32.bf8.bf8" => "__builtin_amdgcn_dot4_f32_bf8_bf8",
+ "llvm.amdgcn.dot4.f32.bf8.fp8" => "__builtin_amdgcn_dot4_f32_bf8_fp8",
+ "llvm.amdgcn.dot4.f32.fp8.bf8" => "__builtin_amdgcn_dot4_f32_fp8_bf8",
+ "llvm.amdgcn.dot4.f32.fp8.fp8" => "__builtin_amdgcn_dot4_f32_fp8_fp8",
"llvm.amdgcn.ds.add.gs.reg.rtn" => "__builtin_amdgcn_ds_add_gs_reg_rtn",
"llvm.amdgcn.ds.bpermute" => "__builtin_amdgcn_ds_bpermute",
"llvm.amdgcn.ds.fadd.v2bf16" => "__builtin_amdgcn_ds_atomic_fadd_v2bf16",
@@ -2291,6 +2295,10 @@ match name {
"llvm.loongarch.csrxchg.d" => "__builtin_loongarch_csrxchg_d",
"llvm.loongarch.csrxchg.w" => "__builtin_loongarch_csrxchg_w",
"llvm.loongarch.dbar" => "__builtin_loongarch_dbar",
+ "llvm.loongarch.frecipe.d" => "__builtin_loongarch_frecipe_d",
+ "llvm.loongarch.frecipe.s" => "__builtin_loongarch_frecipe_s",
+ "llvm.loongarch.frsqrte.d" => "__builtin_loongarch_frsqrte_d",
+ "llvm.loongarch.frsqrte.s" => "__builtin_loongarch_frsqrte_s",
"llvm.loongarch.ibar" => "__builtin_loongarch_ibar",
"llvm.loongarch.iocsrrd.b" => "__builtin_loongarch_iocsrrd_b",
"llvm.loongarch.iocsrrd.d" => "__builtin_loongarch_iocsrrd_d",
@@ -2529,6 +2537,8 @@ match name {
"llvm.loongarch.lasx.xvfnmsub.s" => "__builtin_lasx_xvfnmsub_s",
"llvm.loongarch.lasx.xvfrecip.d" => "__builtin_lasx_xvfrecip_d",
"llvm.loongarch.lasx.xvfrecip.s" => "__builtin_lasx_xvfrecip_s",
+ "llvm.loongarch.lasx.xvfrecipe.d" => "__builtin_lasx_xvfrecipe_d",
+ "llvm.loongarch.lasx.xvfrecipe.s" => "__builtin_lasx_xvfrecipe_s",
"llvm.loongarch.lasx.xvfrint.d" => "__builtin_lasx_xvfrint_d",
"llvm.loongarch.lasx.xvfrint.s" => "__builtin_lasx_xvfrint_s",
"llvm.loongarch.lasx.xvfrintrm.d" => "__builtin_lasx_xvfrintrm_d",
@@ -2541,6 +2551,8 @@ match name {
"llvm.loongarch.lasx.xvfrintrz.s" => "__builtin_lasx_xvfrintrz_s",
"llvm.loongarch.lasx.xvfrsqrt.d" => "__builtin_lasx_xvfrsqrt_d",
"llvm.loongarch.lasx.xvfrsqrt.s" => "__builtin_lasx_xvfrsqrt_s",
+ "llvm.loongarch.lasx.xvfrsqrte.d" => "__builtin_lasx_xvfrsqrte_d",
+ "llvm.loongarch.lasx.xvfrsqrte.s" => "__builtin_lasx_xvfrsqrte_s",
"llvm.loongarch.lasx.xvfrstp.b" => "__builtin_lasx_xvfrstp_b",
"llvm.loongarch.lasx.xvfrstp.h" => "__builtin_lasx_xvfrstp_h",
"llvm.loongarch.lasx.xvfrstpi.b" => "__builtin_lasx_xvfrstpi_b",
@@ -3255,6 +3267,8 @@ match name {
"llvm.loongarch.lsx.vfnmsub.s" => "__builtin_lsx_vfnmsub_s",
"llvm.loongarch.lsx.vfrecip.d" => "__builtin_lsx_vfrecip_d",
"llvm.loongarch.lsx.vfrecip.s" => "__builtin_lsx_vfrecip_s",
+ "llvm.loongarch.lsx.vfrecipe.d" => "__builtin_lsx_vfrecipe_d",
+ "llvm.loongarch.lsx.vfrecipe.s" => "__builtin_lsx_vfrecipe_s",
"llvm.loongarch.lsx.vfrint.d" => "__builtin_lsx_vfrint_d",
"llvm.loongarch.lsx.vfrint.s" => "__builtin_lsx_vfrint_s",
"llvm.loongarch.lsx.vfrintrm.d" => "__builtin_lsx_vfrintrm_d",
@@ -3267,6 +3281,8 @@ match name {
"llvm.loongarch.lsx.vfrintrz.s" => "__builtin_lsx_vfrintrz_s",
"llvm.loongarch.lsx.vfrsqrt.d" => "__builtin_lsx_vfrsqrt_d",
"llvm.loongarch.lsx.vfrsqrt.s" => "__builtin_lsx_vfrsqrt_s",
+ "llvm.loongarch.lsx.vfrsqrte.d" => "__builtin_lsx_vfrsqrte_d",
+ "llvm.loongarch.lsx.vfrsqrte.s" => "__builtin_lsx_vfrsqrte_s",
"llvm.loongarch.lsx.vfrstp.b" => "__builtin_lsx_vfrstp_b",
"llvm.loongarch.lsx.vfrstp.h" => "__builtin_lsx_vfrstp_h",
"llvm.loongarch.lsx.vfrstpi.b" => "__builtin_lsx_vfrstpi_b",
@@ -4434,6 +4450,7 @@ match name {
"llvm.nvvm.abs.bf16x2" => "__nvvm_abs_bf16x2",
"llvm.nvvm.abs.i" => "__nvvm_abs_i",
"llvm.nvvm.abs.ll" => "__nvvm_abs_ll",
+ "llvm.nvvm.activemask" => "__nvvm_activemask",
"llvm.nvvm.add.rm.d" => "__nvvm_add_rm_d",
"llvm.nvvm.add.rm.f" => "__nvvm_add_rm_f",
"llvm.nvvm.add.rm.ftz.f" => "__nvvm_add_rm_ftz_f",
@@ -4522,6 +4539,7 @@ match name {
"llvm.nvvm.ex2.approx.d" => "__nvvm_ex2_approx_d",
"llvm.nvvm.ex2.approx.f" => "__nvvm_ex2_approx_f",
"llvm.nvvm.ex2.approx.ftz.f" => "__nvvm_ex2_approx_ftz_f",
+ "llvm.nvvm.exit" => "__nvvm_exit",
"llvm.nvvm.f2bf16.rn" => "__nvvm_f2bf16_rn",
"llvm.nvvm.f2bf16.rn.relu" => "__nvvm_f2bf16_rn_relu",
"llvm.nvvm.f2bf16.rz" => "__nvvm_f2bf16_rz",
@@ -4722,8 +4740,11 @@ match name {
"llvm.nvvm.mul24.ui" => "__nvvm_mul24_ui",
"llvm.nvvm.mulhi.i" => "__nvvm_mulhi_i",
"llvm.nvvm.mulhi.ll" => "__nvvm_mulhi_ll",
+ "llvm.nvvm.mulhi.s" => "__nvvm_mulhi_s",
"llvm.nvvm.mulhi.ui" => "__nvvm_mulhi_ui",
"llvm.nvvm.mulhi.ull" => "__nvvm_mulhi_ull",
+ "llvm.nvvm.mulhi.us" => "__nvvm_mulhi_us",
+ "llvm.nvvm.nanosleep" => "__nvvm_nanosleep",
"llvm.nvvm.neg.bf16" => "__nvvm_neg_bf16",
"llvm.nvvm.neg.bf16x2" => "__nvvm_neg_bf16x2",
"llvm.nvvm.popc.i" => "__nvvm_popc_i",
@@ -4783,6 +4804,7 @@ match name {
"llvm.nvvm.read.ptx.sreg.envreg7" => "__nvvm_read_ptx_sreg_envreg7",
"llvm.nvvm.read.ptx.sreg.envreg8" => "__nvvm_read_ptx_sreg_envreg8",
"llvm.nvvm.read.ptx.sreg.envreg9" => "__nvvm_read_ptx_sreg_envreg9",
+ "llvm.nvvm.read.ptx.sreg.globaltimer" => "__nvvm_read_ptx_sreg_globaltimer",
"llvm.nvvm.read.ptx.sreg.gridid" => "__nvvm_read_ptx_sreg_gridid",
// [DUPLICATE]: "llvm.nvvm.read.ptx.sreg.gridid" => "__nvvm_read_ptx_sreg_",
"llvm.nvvm.read.ptx.sreg.laneid" => "__nvvm_read_ptx_sreg_laneid",
@@ -4835,6 +4857,7 @@ match name {
"llvm.nvvm.redux.sync.umax" => "__nvvm_redux_sync_umax",
"llvm.nvvm.redux.sync.umin" => "__nvvm_redux_sync_umin",
"llvm.nvvm.redux.sync.xor" => "__nvvm_redux_sync_xor",
+ "llvm.nvvm.reflect" => "__nvvm_reflect",
"llvm.nvvm.rotate.b32" => "__nvvm_rotate_b32",
"llvm.nvvm.rotate.b64" => "__nvvm_rotate_b64",
"llvm.nvvm.rotate.right.b64" => "__nvvm_rotate_right_b64",
@@ -4845,7 +4868,11 @@ match name {
"llvm.nvvm.rsqrt.approx.f" => "__nvvm_rsqrt_approx_f",
"llvm.nvvm.rsqrt.approx.ftz.f" => "__nvvm_rsqrt_approx_ftz_f",
"llvm.nvvm.sad.i" => "__nvvm_sad_i",
+ "llvm.nvvm.sad.ll" => "__nvvm_sad_ll",
+ "llvm.nvvm.sad.s" => "__nvvm_sad_s",
"llvm.nvvm.sad.ui" => "__nvvm_sad_ui",
+ "llvm.nvvm.sad.ull" => "__nvvm_sad_ull",
+ "llvm.nvvm.sad.us" => "__nvvm_sad_us",
"llvm.nvvm.saturate.d" => "__nvvm_saturate_d",
"llvm.nvvm.saturate.f" => "__nvvm_saturate_f",
"llvm.nvvm.saturate.ftz.f" => "__nvvm_saturate_ftz_f",
@@ -5471,6 +5498,7 @@ match name {
"llvm.ppc.fctiwz" => "__builtin_ppc_fctiwz",
"llvm.ppc.fctudz" => "__builtin_ppc_fctudz",
"llvm.ppc.fctuwz" => "__builtin_ppc_fctuwz",
+ "llvm.ppc.fence" => "__builtin_ppc_fence",
"llvm.ppc.fmaf128.round.to.odd" => "__builtin_fmaf128_round_to_odd",
"llvm.ppc.fmsub" => "__builtin_ppc_fmsub",
"llvm.ppc.fmsubs" => "__builtin_ppc_fmsubs",
@@ -5599,6 +5627,9 @@ match name {
"llvm.ppc.qpx.qvstfs" => "__builtin_qpx_qvstfs",
"llvm.ppc.qpx.qvstfsa" => "__builtin_qpx_qvstfsa",
"llvm.ppc.readflm" => "__builtin_readflm",
+ "llvm.ppc.rldimi" => "__builtin_ppc_rldimi",
+ "llvm.ppc.rlwimi" => "__builtin_ppc_rlwimi",
+ "llvm.ppc.rlwnm" => "__builtin_ppc_rlwnm",
"llvm.ppc.scalar.extract.expq" => "__builtin_vsx_scalar_extract_expq",
"llvm.ppc.scalar.insert.exp.qp" => "__builtin_vsx_scalar_insert_exp_qp",
"llvm.ppc.set.texasr" => "__builtin_set_texasr",
@@ -5912,6 +5943,8 @@ match name {
"llvm.s390.vupllb" => "__builtin_s390_vupllb",
"llvm.s390.vupllf" => "__builtin_s390_vupllf",
"llvm.s390.vupllh" => "__builtin_s390_vupllh",
+ // spv
+ "llvm.spv.create.handle" => "__builtin_hlsl_create_handle",
// ve
"llvm.ve.vl.andm.MMM" => "__builtin_ve_vl_andm_MMM",
"llvm.ve.vl.andm.mmm" => "__builtin_ve_vl_andm_mmm",
diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/llvm.rs b/compiler/rustc_codegen_gcc/src/intrinsic/llvm.rs
index ce8dee69a988..a12704822190 100644
--- a/compiler/rustc_codegen_gcc/src/intrinsic/llvm.rs
+++ b/compiler/rustc_codegen_gcc/src/intrinsic/llvm.rs
@@ -15,7 +15,7 @@ pub fn adjust_intrinsic_arguments<'a, 'b, 'gcc, 'tcx>(
// Some LLVM intrinsics do not map 1-to-1 to GCC intrinsics, so we add the missing
// arguments here.
if gcc_func.get_param_count() != args.len() {
- match &*func_name {
+ match func_name {
// NOTE: the following intrinsics have a different number of parameters in LLVM and GCC.
"__builtin_ia32_prold512_mask"
| "__builtin_ia32_pmuldq512_mask"
@@ -380,7 +380,7 @@ pub fn adjust_intrinsic_arguments<'a, 'b, 'gcc, 'tcx>(
_ => (),
}
} else {
- match &*func_name {
+ match func_name {
"__builtin_ia32_rndscaless_mask_round" | "__builtin_ia32_rndscalesd_mask_round" => {
let new_args = args.to_vec();
let arg3_type = gcc_func.get_param_type(2);
@@ -629,17 +629,22 @@ pub fn intrinsic<'gcc, 'tcx>(name: &str, cx: &CodegenCx<'gcc, 'tcx>) -> Function
#[cfg(feature = "master")]
pub fn intrinsic<'gcc, 'tcx>(name: &str, cx: &CodegenCx<'gcc, 'tcx>) -> Function<'gcc> {
- match name {
+ let gcc_name = match name {
"llvm.prefetch" => {
let gcc_name = "__builtin_prefetch";
let func = cx.context.get_builtin_function(gcc_name);
cx.functions.borrow_mut().insert(gcc_name.to_string(), func);
return func;
}
- _ => (),
- }
- let gcc_name = match name {
+ "llvm.aarch64.isb" => {
+ // FIXME: GCC doesn't support __builtin_arm_isb yet, check if this builtin is OK.
+ let gcc_name = "__atomic_thread_fence";
+ let func = cx.context.get_builtin_function(gcc_name);
+ cx.functions.borrow_mut().insert(gcc_name.to_string(), func);
+ return func;
+ }
+
"llvm.x86.xgetbv" => "__builtin_ia32_xgetbv",
// NOTE: this doc specifies the equivalent GCC builtins: http://huonw.github.io/llvmint/llvmint/x86/index.html
"llvm.sqrt.v2f64" => "__builtin_ia32_sqrtpd",
diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs
index 43f12b514aff..9352a67e362f 100644
--- a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs
+++ b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs
@@ -91,7 +91,7 @@ fn get_simple_intrinsic<'gcc, 'tcx>(
sym::abort => "abort",
_ => return None,
};
- Some(cx.context.get_builtin_function(&gcc_name))
+ Some(cx.context.get_builtin_function(gcc_name))
}
impl<'a, 'gcc, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
@@ -122,10 +122,17 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
let result = PlaceRef::new_sized(llresult, fn_abi.ret.layout);
let simple = get_simple_intrinsic(self, name);
+
+ // FIXME(tempdragon): Re-enable `clippy::suspicious_else_formatting` if the following issue is solved:
+ // https://github.com/rust-lang/rust-clippy/issues/12497
+ // and leave `else if use_integer_compare` to be placed "as is".
+ #[allow(clippy::suspicious_else_formatting)]
let llval = match name {
_ if simple.is_some() => {
// FIXME(antoyo): remove this cast when the API supports function.
- let func = unsafe { std::mem::transmute(simple.expect("simple")) };
+ let func = unsafe {
+ std::mem::transmute::, RValue<'gcc>>(simple.expect("simple"))
+ };
self.call(
self.type_void(),
None,
@@ -167,7 +174,7 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
sym::volatile_load | sym::unaligned_volatile_load => {
let tp_ty = fn_args.type_at(0);
let ptr = args[0].immediate();
- let load = if let PassMode::Cast { cast: ty, pad_i32: _ } = &fn_abi.ret.mode {
+ let load = if let PassMode::Cast { cast: ref ty, pad_i32: _ } = fn_abi.ret.mode {
let gcc_ty = ty.gcc_type(self);
self.volatile_load(gcc_ty, ptr)
} else {
@@ -213,12 +220,12 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
let after_block = func.new_block("after");
let arg = args[0].immediate();
- let result = func.new_local(None, arg.get_type(), "zeros");
+ let result = func.new_local(None, self.u32_type, "zeros");
let zero = self.cx.gcc_zero(arg.get_type());
let cond = self.gcc_icmp(IntPredicate::IntEQ, arg, zero);
self.llbb().end_with_conditional(None, cond, then_block, else_block);
- let zero_result = self.cx.gcc_uint(arg.get_type(), width);
+ let zero_result = self.cx.gcc_uint(self.u32_type, width);
then_block.add_assignment(None, result, zero_result);
then_block.end_with_jump(None, after_block);
@@ -386,7 +393,7 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
};
if !fn_abi.ret.is_ignore() {
- if let PassMode::Cast { cast: ty, .. } = &fn_abi.ret.mode {
+ if let PassMode::Cast { cast: ref ty, .. } = fn_abi.ret.mode {
let ptr_llty = self.type_ptr_to(ty.gcc_type(self));
let ptr = self.pointercast(result.val.llval, ptr_llty);
self.store(llval, ptr, result.val.align);
@@ -592,7 +599,7 @@ fn int_type_width_signed<'gcc, 'tcx>(
ty: Ty<'tcx>,
cx: &CodegenCx<'gcc, 'tcx>,
) -> Option<(u64, bool)> {
- match ty.kind() {
+ match *ty.kind() {
ty::Int(t) => Some((
match t {
rustc_middle::ty::IntTy::Isize => u64::from(cx.tcx.sess.target.pointer_width),
@@ -698,16 +705,17 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
fn count_leading_zeroes(&mut self, width: u64, arg: RValue<'gcc>) -> RValue<'gcc> {
// TODO(antoyo): use width?
let arg_type = arg.get_type();
+ let result_type = self.u32_type;
let count_leading_zeroes =
// TODO(antoyo): write a new function Type::is_compatible_with(&Type) and use it here
// instead of using is_uint().
- if arg_type.is_uint(&self.cx) {
+ if arg_type.is_uint(self.cx) {
"__builtin_clz"
}
- else if arg_type.is_ulong(&self.cx) {
+ else if arg_type.is_ulong(self.cx) {
"__builtin_clzl"
}
- else if arg_type.is_ulonglong(&self.cx) {
+ else if arg_type.is_ulonglong(self.cx) {
"__builtin_clzll"
}
else if width == 128 {
@@ -755,7 +763,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
let res = self.context.new_array_access(self.location, result, index);
- return self.gcc_int_cast(res.to_rvalue(), arg_type);
+ return self.gcc_int_cast(res.to_rvalue(), result_type);
}
else {
let count_leading_zeroes = self.context.get_builtin_function("__builtin_clzll");
@@ -763,17 +771,18 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
let diff = self.ulonglong_type.get_size() as i64 - arg_type.get_size() as i64;
let diff = self.context.new_rvalue_from_long(self.int_type, diff * 8);
let res = self.context.new_call(self.location, count_leading_zeroes, &[arg]) - diff;
- return self.context.new_cast(self.location, res, arg_type);
+ return self.context.new_cast(self.location, res, result_type);
};
let count_leading_zeroes = self.context.get_builtin_function(count_leading_zeroes);
let res = self.context.new_call(self.location, count_leading_zeroes, &[arg]);
- self.context.new_cast(self.location, res, arg_type)
+ self.context.new_cast(self.location, res, result_type)
}
fn count_trailing_zeroes(&mut self, _width: u64, arg: RValue<'gcc>) -> RValue<'gcc> {
- let result_type = arg.get_type();
- let arg = if result_type.is_signed(self.cx) {
- let new_type = result_type.to_unsigned(self.cx);
+ let arg_type = arg.get_type();
+ let result_type = self.u32_type;
+ let arg = if arg_type.is_signed(self.cx) {
+ let new_type = arg_type.to_unsigned(self.cx);
self.gcc_int_cast(arg, new_type)
} else {
arg
@@ -782,17 +791,17 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
let (count_trailing_zeroes, expected_type) =
// TODO(antoyo): write a new function Type::is_compatible_with(&Type) and use it here
// instead of using is_uint().
- if arg_type.is_uchar(&self.cx) || arg_type.is_ushort(&self.cx) || arg_type.is_uint(&self.cx) {
+ if arg_type.is_uchar(self.cx) || arg_type.is_ushort(self.cx) || arg_type.is_uint(self.cx) {
// NOTE: we don't need to & 0xFF for uchar because the result is undefined on zero.
("__builtin_ctz", self.cx.uint_type)
}
- else if arg_type.is_ulong(&self.cx) {
+ else if arg_type.is_ulong(self.cx) {
("__builtin_ctzl", self.cx.ulong_type)
}
- else if arg_type.is_ulonglong(&self.cx) {
+ else if arg_type.is_ulonglong(self.cx) {
("__builtin_ctzll", self.cx.ulonglong_type)
}
- else if arg_type.is_u128(&self.cx) {
+ else if arg_type.is_u128(self.cx) {
// Adapted from the algorithm to count leading zeroes from: https://stackoverflow.com/a/28433850/389119
let array_type = self.context.new_array_type(None, arg_type, 3);
let result = self.current_func()
@@ -863,18 +872,16 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
fn pop_count(&mut self, value: RValue<'gcc>) -> RValue<'gcc> {
// TODO(antoyo): use the optimized version with fewer operations.
- let result_type = value.get_type();
- let value_type = result_type.to_unsigned(self.cx);
+ let result_type = self.u32_type;
+ let arg_type = value.get_type();
+ let value_type = arg_type.to_unsigned(self.cx);
- let value = if result_type.is_signed(self.cx) {
- self.gcc_int_cast(value, value_type)
- } else {
- value
- };
+ let value =
+ if arg_type.is_signed(self.cx) { self.gcc_int_cast(value, value_type) } else { value };
// only break apart 128-bit ints if they're not natively supported
// TODO(antoyo): remove this if/when native 128-bit integers land in libgccjit
- if value_type.is_u128(&self.cx) && !self.cx.supports_128bit_integers {
+ if value_type.is_u128(self.cx) && !self.cx.supports_128bit_integers {
let sixty_four = self.gcc_int(value_type, 64);
let right_shift = self.gcc_lshr(value, sixty_four);
let high = self.gcc_int_cast(right_shift, self.cx.ulonglong_type);
@@ -997,7 +1004,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
// Return `result_type`'s maximum or minimum value on overflow
// NOTE: convert the type to unsigned to have an unsigned shift.
- let unsigned_type = result_type.to_unsigned(&self.cx);
+ let unsigned_type = result_type.to_unsigned(self.cx);
let shifted = self.gcc_lshr(
self.gcc_int_cast(lhs, unsigned_type),
self.gcc_int(unsigned_type, width as i64 - 1),
@@ -1189,7 +1196,7 @@ fn codegen_gnu_try<'gcc>(
bx.invoke(try_func_ty, None, None, try_func, &[data], then, catch, None, None);
});
- let func = unsafe { std::mem::transmute(func) };
+ let func = unsafe { std::mem::transmute::, RValue<'gcc>>(func) };
// Note that no invoke is used here because by definition this function
// can't panic (that's what it's catching).
@@ -1263,7 +1270,7 @@ fn gen_fn<'a, 'gcc, 'tcx>(
// FIXME(eddyb) find a nicer way to do this.
cx.linkage.set(FunctionType::Internal);
let func = cx.declare_fn(name, fn_abi);
- let func_val = unsafe { std::mem::transmute(func) };
+ let func_val = unsafe { std::mem::transmute::, RValue<'gcc>>(func) };
cx.set_frame_pointer_type(func_val);
cx.apply_target_cpu_attr(func_val);
let block = Builder::append_block(cx, func_val, "entry-block");
diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs b/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs
index 1625e6ecdb70..ba214a9c24cf 100644
--- a/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs
+++ b/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs
@@ -13,6 +13,7 @@ use rustc_codegen_ssa::errors::InvalidMonomorphization;
use rustc_codegen_ssa::mir::operand::OperandRef;
use rustc_codegen_ssa::mir::place::PlaceRef;
use rustc_codegen_ssa::traits::{BaseTypeMethods, BuilderMethods};
+#[cfg(feature = "master")]
use rustc_hir as hir;
use rustc_middle::mir::BinOp;
use rustc_middle::span_bug;
@@ -72,11 +73,11 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
let expected_bytes = len / 8 + ((len % 8 > 0) as u64);
let mask_ty = arg_tys[0];
- let mut mask = match mask_ty.kind() {
+ let mut mask = match *mask_ty.kind() {
ty::Int(i) if i.bit_width() == Some(expected_int_bits) => args[0].immediate(),
ty::Uint(i) if i.bit_width() == Some(expected_int_bits) => args[0].immediate(),
ty::Array(elem, len)
- if matches!(elem.kind(), ty::Uint(ty::UintTy::U8))
+ if matches!(*elem.kind(), ty::Uint(ty::UintTy::U8))
&& len.try_eval_target_usize(bx.tcx, ty::ParamEnv::reveal_all())
== Some(expected_bytes) =>
{
@@ -309,10 +310,9 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
})
.collect();
return Ok(bx.context.new_rvalue_from_vector(None, v_type, &elems));
- } else {
- // avoid the unnecessary truncation as an optimization.
- return Ok(bx.context.new_bitcast(None, result, v_type));
}
+ // avoid the unnecessary truncation as an optimization.
+ return Ok(bx.context.new_bitcast(None, result, v_type));
}
// since gcc doesn't have vector shuffle methods available in non-patched builds, fallback to
// component-wise bitreverses if they're not available.
@@ -342,11 +342,13 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
.map(|i| {
let index = bx.context.new_rvalue_from_long(bx.i32_type, i as i64);
let value = bx.extract_element(vector, index).to_rvalue();
- if name == sym::simd_ctlz {
- bx.count_leading_zeroes(value.get_type().get_size() as u64 * 8, value)
+ let value_type = value.get_type();
+ let element = if name == sym::simd_ctlz {
+ bx.count_leading_zeroes(value_type.get_size() as u64 * 8, value)
} else {
- bx.count_trailing_zeroes(value.get_type().get_size() as u64 * 8, value)
- }
+ bx.count_trailing_zeroes(value_type.get_size() as u64 * 8, value)
+ };
+ bx.context.new_cast(None, element, value_type)
})
.collect();
return Ok(bx.context.new_rvalue_from_vector(None, vector.get_type(), &elements));
@@ -355,8 +357,8 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
if name == sym::simd_shuffle {
// Make sure this is actually an array, since typeck only checks the length-suffixed
// version of this intrinsic.
- let n: u64 = match args[2].layout.ty.kind() {
- ty::Array(ty, len) if matches!(ty.kind(), ty::Uint(ty::UintTy::U32)) => {
+ let n: u64 = match *args[2].layout.ty.kind() {
+ ty::Array(ty, len) if matches!(*ty.kind(), ty::Uint(ty::UintTy::U32)) => {
len.try_eval_target_usize(bx.cx.tcx, ty::ParamEnv::reveal_all()).unwrap_or_else(
|| span_bug!(span, "could not evaluate shuffle index array length"),
)
@@ -429,13 +431,148 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
m_len == v_len,
InvalidMonomorphization::MismatchedLengths { span, name, m_len, v_len }
);
- match m_elem_ty.kind() {
+ match *m_elem_ty.kind() {
ty::Int(_) => {}
_ => return_error!(InvalidMonomorphization::MaskType { span, name, ty: m_elem_ty }),
}
return Ok(bx.vector_select(args[0].immediate(), args[1].immediate(), args[2].immediate()));
}
+ if name == sym::simd_cast_ptr {
+ require_simd!(ret_ty, InvalidMonomorphization::SimdReturn { span, name, ty: ret_ty });
+ let (out_len, out_elem) = ret_ty.simd_size_and_type(bx.tcx());
+
+ require!(
+ in_len == out_len,
+ InvalidMonomorphization::ReturnLengthInputType {
+ span,
+ name,
+ in_len,
+ in_ty,
+ ret_ty,
+ out_len
+ }
+ );
+
+ match *in_elem.kind() {
+ ty::RawPtr(p_ty, _) => {
+ let metadata = p_ty.ptr_metadata_ty(bx.tcx, |ty| {
+ bx.tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), ty)
+ });
+ require!(
+ metadata.is_unit(),
+ InvalidMonomorphization::CastFatPointer { span, name, ty: in_elem }
+ );
+ }
+ _ => {
+ return_error!(InvalidMonomorphization::ExpectedPointer { span, name, ty: in_elem })
+ }
+ }
+ match *out_elem.kind() {
+ ty::RawPtr(p_ty, _) => {
+ let metadata = p_ty.ptr_metadata_ty(bx.tcx, |ty| {
+ bx.tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), ty)
+ });
+ require!(
+ metadata.is_unit(),
+ InvalidMonomorphization::CastFatPointer { span, name, ty: out_elem }
+ );
+ }
+ _ => {
+ return_error!(InvalidMonomorphization::ExpectedPointer { span, name, ty: out_elem })
+ }
+ }
+
+ let arg = args[0].immediate();
+ let elem_type = llret_ty.dyncast_vector().expect("vector return type").get_element_type();
+ let values: Vec<_> = (0..in_len)
+ .map(|i| {
+ let idx = bx.gcc_int(bx.usize_type, i as _);
+ let value = bx.extract_element(arg, idx);
+ bx.pointercast(value, elem_type)
+ })
+ .collect();
+ return Ok(bx.context.new_rvalue_from_vector(bx.location, llret_ty, &values));
+ }
+
+ if name == sym::simd_expose_provenance {
+ require_simd!(ret_ty, InvalidMonomorphization::SimdReturn { span, name, ty: ret_ty });
+ let (out_len, out_elem) = ret_ty.simd_size_and_type(bx.tcx());
+
+ require!(
+ in_len == out_len,
+ InvalidMonomorphization::ReturnLengthInputType {
+ span,
+ name,
+ in_len,
+ in_ty,
+ ret_ty,
+ out_len
+ }
+ );
+
+ match *in_elem.kind() {
+ ty::RawPtr(_, _) => {}
+ _ => {
+ return_error!(InvalidMonomorphization::ExpectedPointer { span, name, ty: in_elem })
+ }
+ }
+ match *out_elem.kind() {
+ ty::Uint(ty::UintTy::Usize) => {}
+ _ => return_error!(InvalidMonomorphization::ExpectedUsize { span, name, ty: out_elem }),
+ }
+
+ let arg = args[0].immediate();
+ let elem_type = llret_ty.dyncast_vector().expect("vector return type").get_element_type();
+ let values: Vec<_> = (0..in_len)
+ .map(|i| {
+ let idx = bx.gcc_int(bx.usize_type, i as _);
+ let value = bx.extract_element(arg, idx);
+ bx.ptrtoint(value, elem_type)
+ })
+ .collect();
+ return Ok(bx.context.new_rvalue_from_vector(bx.location, llret_ty, &values));
+ }
+
+ if name == sym::simd_with_exposed_provenance {
+ require_simd!(ret_ty, InvalidMonomorphization::SimdReturn { span, name, ty: ret_ty });
+ let (out_len, out_elem) = ret_ty.simd_size_and_type(bx.tcx());
+
+ require!(
+ in_len == out_len,
+ InvalidMonomorphization::ReturnLengthInputType {
+ span,
+ name,
+ in_len,
+ in_ty,
+ ret_ty,
+ out_len
+ }
+ );
+
+ match *in_elem.kind() {
+ ty::Uint(ty::UintTy::Usize) => {}
+ _ => return_error!(InvalidMonomorphization::ExpectedUsize { span, name, ty: in_elem }),
+ }
+ match *out_elem.kind() {
+ ty::RawPtr(_, _) => {}
+ _ => {
+ return_error!(InvalidMonomorphization::ExpectedPointer { span, name, ty: out_elem })
+ }
+ }
+
+ let arg = args[0].immediate();
+ let elem_type = llret_ty.dyncast_vector().expect("vector return type").get_element_type();
+ let values: Vec<_> = (0..in_len)
+ .map(|i| {
+ let idx = bx.gcc_int(bx.usize_type, i as _);
+ let value = bx.extract_element(arg, idx);
+ bx.inttoptr(value, elem_type)
+ })
+ .collect();
+ return Ok(bx.context.new_rvalue_from_vector(bx.location, llret_ty, &values));
+ }
+
#[cfg(feature = "master")]
if name == sym::simd_cast || name == sym::simd_as {
require_simd!(ret_ty, InvalidMonomorphization::SimdReturn { span, name, ty: ret_ty });
@@ -462,13 +599,13 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
Unsupported,
}
- let in_style = match in_elem.kind() {
+ let in_style = match *in_elem.kind() {
ty::Int(_) | ty::Uint(_) => Style::Int,
ty::Float(_) => Style::Float,
_ => Style::Unsupported,
};
- let out_style = match out_elem.kind() {
+ let out_style = match *out_elem.kind() {
ty::Int(_) | ty::Uint(_) => Style::Int,
ty::Float(_) => Style::Float,
_ => Style::Unsupported,
@@ -495,7 +632,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
macro_rules! arith_binary {
($($name: ident: $($($p: ident),* => $call: ident),*;)*) => {
$(if name == sym::$name {
- match in_elem.kind() {
+ match *in_elem.kind() {
$($(ty::$p(_))|* => {
return Ok(bx.$call(args[0].immediate(), args[1].immediate()))
})*
@@ -533,7 +670,6 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
let sign_shift = bx.context.new_rvalue_from_int(elem_type, elem_size as i32 - 1);
let one = bx.context.new_rvalue_one(elem_type);
- let mut shift = 0;
for i in 0..in_len {
let elem =
bx.extract_element(vector, bx.context.new_rvalue_from_int(bx.int_type, i as i32));
@@ -541,17 +677,16 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
let masked = shifted & one;
result = result
| (bx.context.new_cast(None, masked, result_type)
- << bx.context.new_rvalue_from_int(result_type, shift));
- shift += 1;
+ << bx.context.new_rvalue_from_int(result_type, i as i32));
}
- match ret_ty.kind() {
+ match *ret_ty.kind() {
ty::Uint(i) if i.bit_width() == Some(expected_int_bits) => {
// Zero-extend iN to the bitmask type:
return Ok(result);
}
ty::Array(elem, len)
- if matches!(elem.kind(), ty::Uint(ty::UintTy::U8))
+ if matches!(*elem.kind(), ty::Uint(ty::UintTy::U8))
&& len.try_eval_target_usize(bx.tcx, ty::ParamEnv::reveal_all())
== Some(expected_bytes) =>
{
@@ -590,7 +725,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
return Err(());
}};
}
- let (elem_ty_str, elem_ty) = if let ty::Float(f) = in_elem.kind() {
+ let (elem_ty_str, elem_ty) = if let ty::Float(ref f) = *in_elem.kind() {
let elem_ty = bx.cx.type_float_from_ty(*f);
match f.bit_width() {
32 => ("f", elem_ty),
@@ -816,7 +951,9 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
let (_, element_ty0) = arg_tys[0].simd_size_and_type(bx.tcx());
let (_, element_ty1) = arg_tys[1].simd_size_and_type(bx.tcx());
let (pointer_count, underlying_ty) = match *element_ty1.kind() {
- ty::RawPtr(p_ty, _) if p_ty == in_elem => (ptr_count(element_ty1), non_ptr(element_ty1)),
+ ty::RawPtr(p_ty, _) if p_ty == in_elem => {
+ (ptr_count(element_ty1), non_ptr(element_ty1))
+ }
_ => {
require!(
false,
@@ -839,7 +976,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
// The element type of the third argument must be a signed integer type of any width:
let (_, element_ty2) = arg_tys[2].simd_size_and_type(bx.tcx());
- match element_ty2.kind() {
+ match *element_ty2.kind() {
ty::Int(_) => (),
_ => {
require!(
@@ -955,7 +1092,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
assert_eq!(underlying_ty, non_ptr(element_ty0));
// The element type of the third argument must be a signed integer type of any width:
- match element_ty2.kind() {
+ match *element_ty2.kind() {
ty::Int(_) => (),
_ => {
require!(
@@ -1013,7 +1150,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
macro_rules! arith_unary {
($($name: ident: $($($p: ident),* => $call: ident),*;)*) => {
$(if name == sym::$name {
- match in_elem.kind() {
+ match *in_elem.kind() {
$($(ty::$p(_))|* => {
return Ok(bx.$call(args[0].immediate()))
})*
@@ -1137,7 +1274,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
ret_ty == in_elem,
InvalidMonomorphization::ReturnType { span, name, in_elem, in_ty, ret_ty }
);
- return match in_elem.kind() {
+ return match *in_elem.kind() {
ty::Int(_) | ty::Uint(_) => {
let r = bx.vector_reduce_op(args[0].immediate(), $vec_op);
if $ordered {
@@ -1206,7 +1343,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
ret_ty == in_elem,
InvalidMonomorphization::ReturnType { span, name, in_elem, in_ty, ret_ty }
);
- return match in_elem.kind() {
+ return match *in_elem.kind() {
ty::Int(_) | ty::Uint(_) => Ok(bx.$int_red(args[0].immediate())),
ty::Float(_) => Ok(bx.$float_red(args[0].immediate())),
_ => return_error!(InvalidMonomorphization::UnsupportedSymbol {
@@ -1235,7 +1372,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
);
args[0].immediate()
} else {
- match in_elem.kind() {
+ match *in_elem.kind() {
ty::Int(_) | ty::Uint(_) => {}
_ => return_error!(InvalidMonomorphization::UnsupportedSymbol {
span,
@@ -1249,7 +1386,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
args[0].immediate()
};
- return match in_elem.kind() {
+ return match *in_elem.kind() {
ty::Int(_) | ty::Uint(_) => {
let r = bx.vector_reduce_op(input, $op);
Ok(if !$boolean {
diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs
index 24856506c464..1132b0cd2f5a 100644
--- a/compiler/rustc_codegen_gcc/src/lib.rs
+++ b/compiler/rustc_codegen_gcc/src/lib.rs
@@ -4,7 +4,7 @@
* TODO(antoyo): support LTO (gcc's equivalent to Full LTO is -flto -flto-partition=one — https://documentation.suse.com/sbp/all/html/SBP-GCC-10/index.html).
* For Thin LTO, this might be helpful:
* In gcc 4.6 -fwhopr was removed and became default with -flto. The non-whopr path can still be executed via -flto-partition=none.
- * Or the new incremental LTO?
+ * Or the new incremental LTO (https://www.phoronix.com/news/GCC-Incremental-LTO-Patches)?
*
* Maybe some missing optizations enabled by rustc's LTO is in there: https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html
* Like -fipa-icf (should be already enabled) and maybe -fdevirtualize-at-ltrans.
@@ -16,12 +16,13 @@
#![allow(internal_features)]
#![doc(rust_logo)]
#![feature(rustdoc_internals)]
-#![feature(rustc_private, decl_macro, never_type, trusted_len, hash_raw_entry)]
+#![feature(rustc_private, decl_macro, never_type, trusted_len, hash_raw_entry, let_chains)]
#![allow(broken_intra_doc_links)]
#![recursion_limit = "256"]
#![warn(rust_2018_idioms)]
#![warn(unused_lifetimes)]
#![deny(clippy::pattern_type_mismatch)]
+#![allow(clippy::needless_lifetimes)]
extern crate rustc_apfloat;
extern crate rustc_ast;
@@ -73,6 +74,7 @@ mod type_of;
use std::any::Any;
use std::fmt::Debug;
+use std::ops::Deref;
#[cfg(not(feature = "master"))]
use std::sync::atomic::AtomicBool;
#[cfg(not(feature = "master"))]
@@ -80,8 +82,9 @@ use std::sync::atomic::Ordering;
use std::sync::Arc;
use std::sync::Mutex;
+use back::lto::ThinBuffer;
+use back::lto::ThinData;
use errors::LTONotSupported;
-#[cfg(not(feature = "master"))]
use gccjit::CType;
use gccjit::{Context, OptimizationLevel};
#[cfg(feature = "master")]
@@ -92,9 +95,7 @@ use rustc_codegen_ssa::back::write::{
CodegenContext, FatLtoInput, ModuleConfig, TargetMachineFactoryFn,
};
use rustc_codegen_ssa::base::codegen_crate;
-use rustc_codegen_ssa::traits::{
- CodegenBackend, ExtraBackendMethods, ThinBufferMethods, WriteBackendMethods,
-};
+use rustc_codegen_ssa::traits::{CodegenBackend, ExtraBackendMethods, WriteBackendMethods};
use rustc_codegen_ssa::{CodegenResults, CompiledModule, ModuleCodegen};
use rustc_data_structures::fx::FxIndexMap;
use rustc_data_structures::sync::IntoDynSyncSend;
@@ -139,6 +140,10 @@ impl TargetInfo {
fn supports_128bit_int(&self) -> bool {
self.supports_128bit_integers.load(Ordering::SeqCst)
}
+
+ fn supports_target_dependent_type(&self, _typ: CType) -> bool {
+ false
+ }
}
#[derive(Clone)]
@@ -160,6 +165,10 @@ impl LockedTargetInfo {
fn supports_128bit_int(&self) -> bool {
self.info.lock().expect("lock").supports_128bit_int()
}
+
+ fn supports_target_dependent_type(&self, typ: CType) -> bool {
+ self.info.lock().expect("lock").supports_target_dependent_type(typ)
+ }
}
#[derive(Clone)]
@@ -188,6 +197,7 @@ impl CodegenBackend for GccCodegenBackend {
#[cfg(feature = "master")]
gccjit::set_global_personality_function_name(b"rust_eh_personality\0");
+
if sess.lto() == Lto::Thin {
sess.dcx().emit_warn(LTONotSupported {});
}
@@ -293,7 +303,7 @@ impl ExtraBackendMethods for GccCodegenBackend {
alloc_error_handler_kind: AllocatorKind,
) -> Self::Module {
let mut mods = GccContext {
- context: new_context(tcx),
+ context: Arc::new(SyncContext::new(new_context(tcx))),
should_combine_object_files: false,
temp_dir: None,
};
@@ -323,35 +333,42 @@ impl ExtraBackendMethods for GccCodegenBackend {
}
}
-pub struct ThinBuffer;
-
-impl ThinBufferMethods for ThinBuffer {
- fn data(&self) -> &[u8] {
- unimplemented!();
- }
-
- fn thin_link_data(&self) -> &[u8] {
- unimplemented!();
- }
-}
-
pub struct GccContext {
- context: Context<'static>,
+ context: Arc,
should_combine_object_files: bool,
// Temporary directory used by LTO. We keep it here so that it's not removed before linking.
temp_dir: Option,
}
-unsafe impl Send for GccContext {}
-// FIXME(antoyo): that shouldn't be Sync. Parallel compilation is currently disabled with "-Zno-parallel-llvm". Try to disable it here.
-unsafe impl Sync for GccContext {}
+struct SyncContext {
+ context: Context<'static>,
+}
+
+impl SyncContext {
+ fn new(context: Context<'static>) -> Self {
+ Self { context }
+ }
+}
+
+impl Deref for SyncContext {
+ type Target = Context<'static>;
+
+ fn deref(&self) -> &Self::Target {
+ &self.context
+ }
+}
+
+unsafe impl Send for SyncContext {}
+// FIXME(antoyo): that shouldn't be Sync. Parallel compilation is currently disabled with "-Zno-parallel-llvm".
+// TODO: disable it here by returing false in CodegenBackend::supports_parallel().
+unsafe impl Sync for SyncContext {}
impl WriteBackendMethods for GccCodegenBackend {
type Module = GccContext;
type TargetMachine = ();
type TargetMachineError = ();
type ModuleBuffer = ModuleBuffer;
- type ThinData = ();
+ type ThinData = ThinData;
type ThinBuffer = ThinBuffer;
fn run_fat_lto(
@@ -363,11 +380,11 @@ impl WriteBackendMethods for GccCodegenBackend {
}
fn run_thin_lto(
- _cgcx: &CodegenContext,
- _modules: Vec<(String, Self::ThinBuffer)>,
- _cached_modules: Vec<(SerializedModule, WorkProduct)>,
+ cgcx: &CodegenContext,
+ modules: Vec<(String, Self::ThinBuffer)>,
+ cached_modules: Vec<(SerializedModule, WorkProduct)>,
) -> Result<(Vec>, Vec), FatalError> {
- unimplemented!();
+ back::lto::run_thin(cgcx, modules, cached_modules)
}
fn print_pass_timings(&self) {
@@ -397,10 +414,10 @@ impl WriteBackendMethods for GccCodegenBackend {
}
unsafe fn optimize_thin(
- _cgcx: &CodegenContext,
- _thin: ThinModule,
+ cgcx: &CodegenContext,
+ thin: ThinModule,
) -> Result, FatalError> {
- unimplemented!();
+ back::lto::optimize_thin_module(thin, cgcx)
}
unsafe fn codegen(
@@ -413,10 +430,10 @@ impl WriteBackendMethods for GccCodegenBackend {
}
fn prepare_thin(
- _module: ModuleCodegen,
- _emit_summary: bool,
+ module: ModuleCodegen,
+ emit_summary: bool,
) -> (String, Self::ThinBuffer) {
- unimplemented!();
+ back::lto::prepare_thin(module, emit_summary)
}
fn serialize_module(_module: ModuleCodegen) -> (String, Self::ModuleBuffer) {
@@ -437,7 +454,8 @@ impl WriteBackendMethods for GccCodegenBackend {
pub fn __rustc_codegen_backend() -> Box {
#[cfg(feature = "master")]
let info = {
- // Check whether the target supports 128-bit integers.
+ // Check whether the target supports 128-bit integers, and sized floating point types (like
+ // Float16).
let context = Context::default();
Arc::new(Mutex::new(IntoDynSyncSend(context.get_target_info())))
};
@@ -467,6 +485,7 @@ pub fn target_features(
allow_unstable: bool,
target_info: &LockedTargetInfo,
) -> Vec {
+ // TODO(antoyo): use global_gcc_features.
sess.target
.supported_target_features()
.iter()
@@ -477,8 +496,12 @@ pub fn target_features(
None
}
})
- .filter(|_feature| {
- target_info.cpu_supports(_feature)
+ .filter(|feature| {
+ // TODO: we disable Neon for now since we don't support the LLVM intrinsics for it.
+ if *feature == "neon" {
+ return false;
+ }
+ target_info.cpu_supports(feature)
/*
adx, aes, avx, avx2, avx512bf16, avx512bitalg, avx512bw, avx512cd, avx512dq, avx512er, avx512f, avx512fp16, avx512ifma,
avx512pf, avx512vbmi, avx512vbmi2, avx512vl, avx512vnni, avx512vp2intersect, avx512vpopcntdq,
diff --git a/compiler/rustc_codegen_gcc/src/mono_item.rs b/compiler/rustc_codegen_gcc/src/mono_item.rs
index 359d3c70b4ca..44657ad4f6e2 100644
--- a/compiler/rustc_codegen_gcc/src/mono_item.rs
+++ b/compiler/rustc_codegen_gcc/src/mono_item.rs
@@ -81,6 +81,6 @@ impl<'gcc, 'tcx> PreDefineMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
// TODO(antoyo): use inline attribute from there in linkage.set() above.
self.functions.borrow_mut().insert(symbol_name.to_string(), decl);
- self.function_instances.borrow_mut().insert(instance, unsafe { std::mem::transmute(decl) });
+ self.function_instances.borrow_mut().insert(instance, decl);
}
}
diff --git a/compiler/rustc_codegen_gcc/src/type_.rs b/compiler/rustc_codegen_gcc/src/type_.rs
index 4caff2e63106..e20234c5ce79 100644
--- a/compiler/rustc_codegen_gcc/src/type_.rs
+++ b/compiler/rustc_codegen_gcc/src/type_.rs
@@ -1,3 +1,8 @@
+#[cfg(feature = "master")]
+use std::convert::TryInto;
+
+#[cfg(feature = "master")]
+use gccjit::CType;
use gccjit::{RValue, Struct, Type};
use rustc_codegen_ssa::common::TypeKind;
use rustc_codegen_ssa::traits::{BaseTypeMethods, DerivedTypeMethods, TypeMembershipMethods};
@@ -142,25 +147,76 @@ impl<'gcc, 'tcx> BaseTypeMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
}
fn type_f16(&self) -> Type<'gcc> {
- unimplemented!("f16_f128")
+ #[cfg(feature = "master")]
+ if self.supports_f16_type {
+ return self.context.new_c_type(CType::Float16);
+ }
+ bug!("unsupported float width 16")
}
fn type_f32(&self) -> Type<'gcc> {
+ #[cfg(feature = "master")]
+ if self.supports_f32_type {
+ return self.context.new_c_type(CType::Float32);
+ }
self.float_type
}
fn type_f64(&self) -> Type<'gcc> {
+ #[cfg(feature = "master")]
+ if self.supports_f64_type {
+ return self.context.new_c_type(CType::Float64);
+ }
self.double_type
}
fn type_f128(&self) -> Type<'gcc> {
- unimplemented!("f16_f128")
+ #[cfg(feature = "master")]
+ if self.supports_f128_type {
+ return self.context.new_c_type(CType::Float128);
+ }
+ bug!("unsupported float width 128")
}
fn type_func(&self, params: &[Type<'gcc>], return_type: Type<'gcc>) -> Type<'gcc> {
self.context.new_function_pointer_type(None, return_type, params, false)
}
+ #[cfg(feature = "master")]
+ fn type_kind(&self, typ: Type<'gcc>) -> TypeKind {
+ if self.is_int_type_or_bool(typ) {
+ TypeKind::Integer
+ } else if typ.get_pointee().is_some() {
+ TypeKind::Pointer
+ } else if typ.is_vector() {
+ TypeKind::Vector
+ } else if typ.dyncast_array().is_some() {
+ TypeKind::Array
+ } else if typ.is_struct().is_some() {
+ TypeKind::Struct
+ } else if typ.dyncast_function_ptr_type().is_some() {
+ TypeKind::Function
+ } else if typ.is_compatible_with(self.float_type) {
+ TypeKind::Float
+ } else if typ.is_compatible_with(self.double_type) {
+ TypeKind::Double
+ } else if typ.is_floating_point() {
+ match typ.get_size() {
+ 2 => TypeKind::Half,
+ 4 => TypeKind::Float,
+ 8 => TypeKind::Double,
+ 16 => TypeKind::FP128,
+ size => unreachable!("Floating-point type of size {}", size),
+ }
+ } else if typ == self.type_void() {
+ TypeKind::Void
+ } else {
+ // TODO(antoyo): support other types.
+ unimplemented!();
+ }
+ }
+
+ #[cfg(not(feature = "master"))]
fn type_kind(&self, typ: Type<'gcc>) -> TypeKind {
if self.is_int_type_or_bool(typ) {
TypeKind::Integer
@@ -170,9 +226,19 @@ impl<'gcc, 'tcx> BaseTypeMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
TypeKind::Double
} else if typ.is_vector() {
TypeKind::Vector
+ } else if typ.get_pointee().is_some() {
+ TypeKind::Pointer
+ } else if typ.dyncast_array().is_some() {
+ TypeKind::Array
+ } else if typ.is_struct().is_some() {
+ TypeKind::Struct
+ } else if typ.dyncast_function_ptr_type().is_some() {
+ TypeKind::Function
+ } else if typ == self.type_void() {
+ TypeKind::Void
} else {
// TODO(antoyo): support other types.
- TypeKind::Void
+ unimplemented!();
}
}
@@ -200,6 +266,16 @@ impl<'gcc, 'tcx> BaseTypeMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
unimplemented!();
}
+ #[cfg(feature = "master")]
+ fn float_width(&self, typ: Type<'gcc>) -> usize {
+ if typ.is_floating_point() {
+ (typ.get_size() * u8::BITS).try_into().unwrap()
+ } else {
+ panic!("Cannot get width of float type {:?}", typ);
+ }
+ }
+
+ #[cfg(not(feature = "master"))]
fn float_width(&self, typ: Type<'gcc>) -> usize {
let f32 = self.context.new_type::();
let f64 = self.context.new_type::();
diff --git a/compiler/rustc_codegen_gcc/src/type_of.rs b/compiler/rustc_codegen_gcc/src/type_of.rs
index a88d50cb4340..d85ed4f12ffa 100644
--- a/compiler/rustc_codegen_gcc/src/type_of.rs
+++ b/compiler/rustc_codegen_gcc/src/type_of.rs
@@ -8,7 +8,7 @@ use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::{self, CoroutineArgsExt, Ty, TypeVisitableExt};
use rustc_target::abi::call::{CastTarget, FnAbi, Reg};
use rustc_target::abi::{
- self, Abi, Align, FieldsShape, Float, Int, Integer, PointeeInfo, Pointer, Size, TyAbiInterface,
+ self, Abi, FieldsShape, Float, Int, Integer, PointeeInfo, Pointer, Size, TyAbiInterface,
Variants,
};
@@ -53,12 +53,6 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
}
}
-impl<'a, 'tcx> CodegenCx<'a, 'tcx> {
- pub fn align_of(&self, ty: Ty<'tcx>) -> Align {
- self.layout_of(ty).align.abi
- }
-}
-
fn uncached_gcc_type<'gcc, 'tcx>(
cx: &CodegenCx<'gcc, 'tcx>,
layout: TyAndLayout<'tcx>,
@@ -90,7 +84,7 @@ fn uncached_gcc_type<'gcc, 'tcx>(
Abi::Uninhabited | Abi::Aggregate { .. } => {}
}
- let name = match layout.ty.kind() {
+ let name = match *layout.ty.kind() {
// FIXME(eddyb) producing readable type names for trait objects can result
// in problematically distinct types due to HRTB and subtyping (see #47638).
// ty::Dynamic(..) |
@@ -220,7 +214,7 @@ impl<'tcx> LayoutGccExt<'tcx> for TyAndLayout<'tcx> {
// to fn_ptr_backend_type handle the on-stack attribute.
// TODO(antoyo): find a less hackish way to hande the on-stack attribute.
ty::FnPtr(sig) => {
- cx.fn_ptr_backend_type(&cx.fn_abi_of_fn_ptr(sig, ty::List::empty()))
+ cx.fn_ptr_backend_type(cx.fn_abi_of_fn_ptr(sig, ty::List::empty()))
}
_ => self.scalar_gcc_type_at(cx, scalar, Size::ZERO),
};
diff --git a/compiler/rustc_codegen_gcc/target_specs/m68k-unknown-linux-gnu.json b/compiler/rustc_codegen_gcc/target_specs/m68k-unknown-linux-gnu.json
new file mode 100644
index 000000000000..95ea06106fb1
--- /dev/null
+++ b/compiler/rustc_codegen_gcc/target_specs/m68k-unknown-linux-gnu.json
@@ -0,0 +1,26 @@
+{
+ "arch": "m68k",
+ "cpu": "M68020",
+ "crt-static-respected": true,
+ "data-layout": "E-m:e-p:32:16:32-i8:8:8-i16:16:16-i32:16:32-n8:16:32-a:0:16-S16",
+ "dynamic-linking": true,
+ "env": "gnu",
+ "has-rpath": true,
+ "has-thread-local": true,
+ "llvm-target": "m68k-unknown-linux-gnu",
+ "max-atomic-width": 32,
+ "os": "linux",
+ "position-independent-executables": true,
+ "relro-level": "full",
+ "supported-split-debuginfo": [
+ "packed",
+ "unpacked",
+ "off"
+ ],
+ "target-endian": "big",
+ "target-family": [
+ "unix"
+ ],
+ "target-mcount": "_mcount",
+ "target-pointer-width": "32"
+}
diff --git a/compiler/rustc_codegen_gcc/tests/failing-ice-tests.txt b/compiler/rustc_codegen_gcc/tests/failing-ice-tests.txt
new file mode 100644
index 000000000000..2084f86b62e1
--- /dev/null
+++ b/compiler/rustc_codegen_gcc/tests/failing-ice-tests.txt
@@ -0,0 +1,36 @@
+tests/ui/treat-err-as-bug/span_delayed_bug.rs
+tests/ui/treat-err-as-bug/err.rs
+tests/ui/simd/not-out-of-bounds.rs
+tests/ui/simd/monomorphize-shuffle-index.rs
+tests/ui/simd/masked-load-store-build-fail.rs
+tests/ui/simd/intrinsic/generic-shuffle.rs
+tests/ui/simd/intrinsic/generic-elements.rs
+tests/ui/simd/intrinsic/generic-cast.rs
+tests/ui/simd/intrinsic/generic-arithmetic-saturating-2.rs
+tests/ui/simd/intrinsic/generic-arithmetic-2.rs
+tests/ui/panics/default-backtrace-ice.rs
+tests/ui/mir/lint/storage-live.rs
+tests/ui/layout/valid_range_oob.rs
+tests/ui/higher-ranked/trait-bounds/future.rs
+tests/ui/consts/const-eval/const-eval-query-stack.rs
+tests/ui/simd/masked-load-store.rs
+tests/ui/simd/issue-39720.rs
+tests/ui/simd/intrinsic/ptr-cast.rs
+tests/ui/sepcomp/sepcomp-statics.rs
+tests/ui/sepcomp/sepcomp-fns.rs
+tests/ui/sepcomp/sepcomp-fns-backwards.rs
+tests/ui/sepcomp/sepcomp-extern.rs
+tests/ui/sepcomp/sepcomp-cci.rs
+tests/ui/lto/thin-lto-inlines2.rs
+tests/ui/lto/weak-works.rs
+tests/ui/lto/thin-lto-inlines.rs
+tests/ui/lto/thin-lto-global-allocator.rs
+tests/ui/lto/msvc-imp-present.rs
+tests/ui/lto/dylib-works.rs
+tests/ui/lto/all-crates.rs
+tests/ui/issues/issue-47364.rs
+tests/ui/functions-closures/parallel-codegen-closures.rs
+tests/ui/sepcomp/sepcomp-unwind.rs
+tests/ui/extern/issue-64655-extern-rust-must-allow-unwind.rs
+tests/ui/extern/issue-64655-allow-unwind-when-calling-panic-directly.rs
+tests/ui/unwind-no-uwtable.rs
diff --git a/compiler/rustc_codegen_gcc/tests/failing-lto-tests.txt b/compiler/rustc_codegen_gcc/tests/failing-lto-tests.txt
index 6e1ed99c6f7a..b9126fb73a77 100644
--- a/compiler/rustc_codegen_gcc/tests/failing-lto-tests.txt
+++ b/compiler/rustc_codegen_gcc/tests/failing-lto-tests.txt
@@ -30,3 +30,4 @@ tests/ui/macros/rfc-2011-nicer-assert-messages/feature-gate-generic_assert.rs
tests/ui/macros/stringify.rs
tests/ui/reexport-test-harness-main.rs
tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-in-test.rs
+tests/ui/binding/fn-arg-incomplete-pattern-drop-order.rs
diff --git a/compiler/rustc_codegen_gcc/tests/failing-run-make-tests.txt b/compiler/rustc_codegen_gcc/tests/failing-run-make-tests.txt
new file mode 100644
index 000000000000..842533cd3c62
--- /dev/null
+++ b/compiler/rustc_codegen_gcc/tests/failing-run-make-tests.txt
@@ -0,0 +1,42 @@
+tests/run-make/a-b-a-linker-guard/
+tests/run-make/CURRENT_RUSTC_VERSION/
+tests/run-make/cross-lang-lto/
+tests/run-make/cross-lang-lto-upstream-rlibs/
+tests/run-make/doctests-keep-binaries/
+tests/run-make/doctests-runtool/
+tests/run-make/emit-shared-files/
+tests/run-make/exit-code/
+tests/run-make/issue-22131/
+tests/run-make/issue-64153/
+tests/run-make/llvm-ident/
+tests/run-make/native-link-modifier-bundle/
+tests/run-make/remap-path-prefix-dwarf/
+tests/run-make/repr128-dwarf/
+tests/run-make/rlib-format-packed-bundled-libs/
+tests/run-make/rlib-format-packed-bundled-libs-2/
+tests/run-make/rustdoc-determinism/
+tests/run-make/rustdoc-error-lines/
+tests/run-make/rustdoc-map-file/
+tests/run-make/rustdoc-output-path/
+tests/run-make/rustdoc-scrape-examples-invalid-expr/
+tests/run-make/rustdoc-scrape-examples-multiple/
+tests/run-make/rustdoc-scrape-examples-ordering/
+tests/run-make/rustdoc-scrape-examples-remap/
+tests/run-make/rustdoc-scrape-examples-test/
+tests/run-make/rustdoc-scrape-examples-whitespace/
+tests/run-make/rustdoc-scrape-examples-macros/
+tests/run-make/rustdoc-with-out-dir-option/
+tests/run-make/rustdoc-verify-output-files/
+tests/run-make/rustdoc-themes/
+tests/run-make/rustdoc-with-short-out-dir-option/
+tests/run-make/rustdoc-with-output-option/
+tests/run-make/arguments-non-c-like-enum/
+tests/run-make/c-link-to-rust-staticlib/
+tests/run-make/foreign-double-unwind/
+tests/run-make/foreign-exceptions/
+tests/run-make/glibc-staticlib-args/
+tests/run-make/issue-36710/
+tests/run-make/issue-68794-textrel-on-minimal-lib/
+tests/run-make/lto-smoke-c/
+tests/run-make/return-non-c-like-enum/
+
diff --git a/compiler/rustc_codegen_gcc/tests/failing-ui-tests.txt b/compiler/rustc_codegen_gcc/tests/failing-ui-tests.txt
index d13562f8bb01..5a55bdb156e3 100644
--- a/compiler/rustc_codegen_gcc/tests/failing-ui-tests.txt
+++ b/compiler/rustc_codegen_gcc/tests/failing-ui-tests.txt
@@ -2,7 +2,6 @@ tests/ui/allocator/no_std-alloc-error-handler-custom.rs
tests/ui/allocator/no_std-alloc-error-handler-default.rs
tests/ui/asm/may_unwind.rs
tests/ui/asm/x86_64/multiple-clobber-abi.rs
-tests/ui/debuginfo/debuginfo-emit-llvm-ir-and-split-debuginfo.rs
tests/ui/functions-closures/parallel-codegen-closures.rs
tests/ui/linkage-attr/linkage1.rs
tests/ui/lto/dylib-works.rs
@@ -14,13 +13,12 @@ tests/ui/sepcomp/sepcomp-fns-backwards.rs
tests/ui/sepcomp/sepcomp-fns.rs
tests/ui/sepcomp/sepcomp-statics.rs
tests/ui/asm/x86_64/may_unwind.rs
-tests/ui/backtrace.rs
tests/ui/catch-unwind-bang.rs
-tests/ui/cfg/cfg-panic-abort.rs
tests/ui/drop/dynamic-drop-async.rs
+tests/ui/cfg/cfg-panic-abort.rs
tests/ui/drop/repeat-drop.rs
-tests/ui/fmt/format-args-capture.rs
tests/ui/coroutine/panic-drops-resume.rs
+tests/ui/fmt/format-args-capture.rs
tests/ui/coroutine/panic-drops.rs
tests/ui/intrinsics/panic-uninitialized-zeroed.rs
tests/ui/iterators/iter-sum-overflow-debug.rs
@@ -34,12 +32,8 @@ tests/ui/panic-runtime/abort.rs
tests/ui/panic-runtime/link-to-abort.rs
tests/ui/unwind-no-uwtable.rs
tests/ui/parser/unclosed-delimiter-in-dep.rs
-tests/ui/runtime/rt-explody-panic-payloads.rs
-tests/ui/simd/intrinsic/ptr-cast.rs
-tests/ui/binding/fn-arg-incomplete-pattern-drop-order.rs
tests/ui/consts/missing_span_in_backtrace.rs
tests/ui/drop/dynamic-drop.rs
-tests/ui/dyn-star/box.rs
tests/ui/issues/issue-40883.rs
tests/ui/issues/issue-43853.rs
tests/ui/issues/issue-47364.rs
@@ -48,29 +42,56 @@ tests/ui/rfcs/rfc-1857-stabilize-drop-order/drop-order.rs
tests/ui/rfcs/rfc-2091-track-caller/std-panic-locations.rs
tests/ui/simd/issue-17170.rs
tests/ui/simd/issue-39720.rs
-tests/ui/statics/issue-91050-1.rs
-tests/ui/statics/issue-91050-2.rs
tests/ui/alloc-error/default-alloc-error-hook.rs
tests/ui/coroutine/panic-safe.rs
tests/ui/issues/issue-14875.rs
tests/ui/issues/issue-29948.rs
tests/ui/panics/nested_panic_caught.rs
-tests/ui/const_prop/ice-issue-111353.rs
tests/ui/process/println-with-broken-pipe.rs
-tests/ui/panic-runtime/lto-abort.rs
tests/ui/lto/thin-lto-inlines2.rs
tests/ui/lto/weak-works.rs
+tests/ui/panic-runtime/lto-abort.rs
tests/ui/lto/thin-lto-inlines.rs
tests/ui/lto/thin-lto-global-allocator.rs
tests/ui/lto/msvc-imp-present.rs
tests/ui/lto/lto-thin-rustc-loads-linker-plugin.rs
tests/ui/lto/all-crates.rs
tests/ui/async-await/deep-futures-are-freeze.rs
-tests/ui/closures/capture-unsized-by-ref.rs
tests/ui/coroutine/resume-after-return.rs
-tests/ui/macros/rfc-2011-nicer-assert-messages/all-expr-kinds.rs
tests/ui/simd/masked-load-store.rs
tests/ui/simd/repr_packed.rs
tests/ui/async-await/in-trait/dont-project-to-specializable-projection.rs
tests/ui/consts/try-operator.rs
tests/ui/coroutine/unwind-abort-mix.rs
+tests/ui/type-alias-impl-trait/rpit_tait_equality_in_canonical_query.rs
+tests/ui/impl-trait/equality-in-canonical-query.rs
+tests/ui/consts/issue-miri-1910.rs
+tests/ui/mir/mir_heavy_promoted.rs
+tests/ui/consts/const_cmp_type_id.rs
+tests/ui/consts/issue-73976-monomorphic.rs
+tests/ui/consts/issue-94675.rs
+tests/ui/rfcs/rfc-2632-const-trait-impl/const-drop-fail.rs
+tests/ui/rfcs/rfc-2632-const-trait-impl/const-drop.rs
+tests/ui/runtime/on-broken-pipe/child-processes.rs
+tests/ui/sanitizer/cfi-assoc-ty-lifetime-issue-123053.rs
+tests/ui/sanitizer/cfi-async-closures.rs
+tests/ui/sanitizer/cfi-closures.rs
+tests/ui/sanitizer/cfi-complex-receiver.rs
+tests/ui/sanitizer/cfi-coroutine.rs
+tests/ui/sanitizer/cfi-drop-in-place.rs
+tests/ui/sanitizer/cfi-drop-no-principal.rs
+tests/ui/sanitizer/cfi-fn-ptr.rs
+tests/ui/sanitizer/cfi-self-ref.rs
+tests/ui/sanitizer/cfi-supertraits.rs
+tests/ui/sanitizer/cfi-virtual-auto.rs
+tests/ui/sanitizer/kcfi-mangling.rs
+tests/ui/statics/const_generics.rs
+tests/ui/backtrace/dylib-dep.rs
+tests/ui/errors/pic-linker.rs
+tests/ui/delegation/fn-header.rs
+tests/ui/consts/zst_no_llvm_alloc.rs
+tests/ui/consts/const-eval/parse_ints.rs
+tests/ui/simd/intrinsic/generic-arithmetic-pass.rs
+tests/ui/backtrace/backtrace.rs
+tests/ui/lifetimes/tail-expr-lock-poisoning.rs
+tests/ui/runtime/rt-explody-panic-payloads.rs
diff --git a/compiler/rustc_codegen_gcc/tests/hello-world/Cargo.toml b/compiler/rustc_codegen_gcc/tests/hello-world/Cargo.toml
new file mode 100644
index 000000000000..0b8cdc63fbe8
--- /dev/null
+++ b/compiler/rustc_codegen_gcc/tests/hello-world/Cargo.toml
@@ -0,0 +1,4 @@
+[package]
+name = "hello_world"
+
+[dependencies]
diff --git a/compiler/rustc_codegen_gcc/tests/hello-world/src/main.rs b/compiler/rustc_codegen_gcc/tests/hello-world/src/main.rs
new file mode 100644
index 000000000000..e7a11a969c03
--- /dev/null
+++ b/compiler/rustc_codegen_gcc/tests/hello-world/src/main.rs
@@ -0,0 +1,3 @@
+fn main() {
+ println!("Hello, world!");
+}
diff --git a/compiler/rustc_codegen_gcc/tests/lang_tests_common.rs b/compiler/rustc_codegen_gcc/tests/lang_tests_common.rs
index 09307836fd42..aecea37ab5a9 100644
--- a/compiler/rustc_codegen_gcc/tests/lang_tests_common.rs
+++ b/compiler/rustc_codegen_gcc/tests/lang_tests_common.rs
@@ -80,8 +80,7 @@ pub fn main_inner(profile: Profile) {
compiler.args([
&format!("-Zcodegen-backend={}/target/debug/librustc_codegen_gcc.so", current_dir),
"--sysroot",
- &format!("{}/build_sysroot/sysroot/", current_dir),
- "-Zno-parallel-llvm",
+ &format!("{}/build/build_sysroot/sysroot/", current_dir),
"-C",
"link-arg=-lc",
"-o",
diff --git a/compiler/rustc_codegen_gcc/tests/run/array.rs b/compiler/rustc_codegen_gcc/tests/run/array.rs
index afd0eed82004..3fe8917c9a3c 100644
--- a/compiler/rustc_codegen_gcc/tests/run/array.rs
+++ b/compiler/rustc_codegen_gcc/tests/run/array.rs
@@ -205,6 +205,17 @@ impl Sub for i16 {
}
}
+#[track_caller]
+#[lang = "panic_const_add_overflow"]
+pub fn panic_const_add_overflow() -> ! {
+ panic("attempt to add with overflow");
+}
+
+#[track_caller]
+#[lang = "panic_const_sub_overflow"]
+pub fn panic_const_sub_overflow() -> ! {
+ panic("attempt to subtract with overflow");
+}
/*
* Code
diff --git a/compiler/rustc_codegen_gcc/tests/run/assign.rs b/compiler/rustc_codegen_gcc/tests/run/assign.rs
index 5b0db2da294d..e105d64a8ad8 100644
--- a/compiler/rustc_codegen_gcc/tests/run/assign.rs
+++ b/compiler/rustc_codegen_gcc/tests/run/assign.rs
@@ -120,6 +120,12 @@ impl Add for isize {
}
}
+#[track_caller]
+#[lang = "panic_const_add_overflow"]
+pub fn panic_const_add_overflow() -> ! {
+ panic("attempt to add with overflow");
+}
+
/*
* Code
*/
diff --git a/compiler/rustc_codegen_gcc/tests/run/closure.rs b/compiler/rustc_codegen_gcc/tests/run/closure.rs
index 4ce528f86800..355f0acee747 100644
--- a/compiler/rustc_codegen_gcc/tests/run/closure.rs
+++ b/compiler/rustc_codegen_gcc/tests/run/closure.rs
@@ -189,6 +189,12 @@ pub fn panic(_msg: &'static str) -> ! {
}
}
+#[track_caller]
+#[lang = "panic_const_add_overflow"]
+pub fn panic_const_add_overflow() -> ! {
+ panic("attempt to add with overflow");
+}
+
/*
* Code
*/
diff --git a/compiler/rustc_codegen_gcc/tests/run/mut_ref.rs b/compiler/rustc_codegen_gcc/tests/run/mut_ref.rs
index 194e55a3deae..5a3f72b69047 100644
--- a/compiler/rustc_codegen_gcc/tests/run/mut_ref.rs
+++ b/compiler/rustc_codegen_gcc/tests/run/mut_ref.rs
@@ -122,6 +122,12 @@ impl Add for isize {
}
}
+#[track_caller]
+#[lang = "panic_const_add_overflow"]
+pub fn panic_const_add_overflow() -> ! {
+ panic("attempt to add with overflow");
+}
+
/*
* Code
*/
diff --git a/compiler/rustc_codegen_gcc/tests/run/operations.rs b/compiler/rustc_codegen_gcc/tests/run/operations.rs
index 2d781670873f..d697bd921cd1 100644
--- a/compiler/rustc_codegen_gcc/tests/run/operations.rs
+++ b/compiler/rustc_codegen_gcc/tests/run/operations.rs
@@ -207,6 +207,24 @@ impl Mul for isize {
}
}
+#[track_caller]
+#[lang = "panic_const_add_overflow"]
+pub fn panic_const_add_overflow() -> ! {
+ panic("attempt to add with overflow");
+}
+
+#[track_caller]
+#[lang = "panic_const_sub_overflow"]
+pub fn panic_const_sub_overflow() -> ! {
+ panic("attempt to subtract with overflow");
+}
+
+#[track_caller]
+#[lang = "panic_const_mul_overflow"]
+pub fn panic_const_mul_overflow() -> ! {
+ panic("attempt to multiply with overflow");
+}
+
/*
* Code
*/
diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs
index cd82894af18e..e76694700263 100644
--- a/compiler/rustc_codegen_llvm/src/attributes.rs
+++ b/compiler/rustc_codegen_llvm/src/attributes.rs
@@ -6,7 +6,6 @@ use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, PatchableFuncti
use rustc_middle::ty::{self, TyCtxt};
use rustc_session::config::{FunctionReturn, OptLevel};
use rustc_span::symbol::sym;
-use rustc_target::spec::abi::Abi;
use rustc_target::spec::{FramePointer, SanitizerSet, StackProbeType, StackProtector};
use smallvec::SmallVec;
@@ -482,7 +481,7 @@ pub fn from_fn_attrs<'ll, 'tcx>(
return;
}
- let mut function_features = function_features
+ let function_features = function_features
.iter()
.flat_map(|feat| {
llvm_util::to_llvm_features(cx.tcx.sess, feat).into_iter().map(|f| format!("+{f}"))
@@ -504,17 +503,6 @@ pub fn from_fn_attrs<'ll, 'tcx>(
let name = name.as_str();
to_add.push(llvm::CreateAttrStringValue(cx.llcx, "wasm-import-name", name));
}
-
- // The `"wasm"` abi on wasm targets automatically enables the
- // `+multivalue` feature because the purpose of the wasm abi is to match
- // the WebAssembly specification, which has this feature. This won't be
- // needed when LLVM enables this `multivalue` feature by default.
- if !cx.tcx.is_closure_like(instance.def_id()) {
- let abi = cx.tcx.fn_sig(instance.def_id()).skip_binder().abi();
- if abi == Abi::Wasm {
- function_features.push("+multivalue".to_string());
- }
- }
}
let global_features = cx.tcx.global_backend_features(()).iter().map(|s| s.as_str());
diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/map_data.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/map_data.rs
index b969fe27a99b..14a944685870 100644
--- a/compiler/rustc_codegen_llvm/src/coverageinfo/map_data.rs
+++ b/compiler/rustc_codegen_llvm/src/coverageinfo/map_data.rs
@@ -66,8 +66,15 @@ impl<'tcx> FunctionCoverageCollector<'tcx> {
// For each expression ID that is directly used by one or more mappings,
// mark it as not-yet-seen. This indicates that we expect to see a
// corresponding `ExpressionUsed` statement during MIR traversal.
- for term in function_coverage_info.mappings.iter().flat_map(|m| m.kind.terms()) {
- if let CovTerm::Expression(id) = term {
+ for mapping in function_coverage_info.mappings.iter() {
+ // Currently we only worry about ordinary code mappings.
+ // For branch and MC/DC mappings, expressions might not correspond
+ // to any particular point in the control-flow graph.
+ // (Keep this in sync with the injection of `ExpressionUsed`
+ // statements in the `InstrumentCoverage` MIR pass.)
+ if let MappingKind::Code(term) = mapping.kind
+ && let CovTerm::Expression(id) = term
+ {
expressions_seen.remove(id);
}
}
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
index 742bfd76590a..851a4c42e99b 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
@@ -705,10 +705,12 @@ impl MsvcBasicName for ty::UintTy {
impl MsvcBasicName for ty::FloatTy {
fn msvc_basic_name(self) -> &'static str {
- // FIXME: f16 and f128 have no MSVC representation. We could improve the debuginfo.
- // See:
+ // FIXME(f16_f128): `f16` and `f128` have no MSVC representation. We could improve the
+ // debuginfo. See:
match self {
- ty::FloatTy::F16 => "half",
+ ty::FloatTy::F16 => {
+ bug!("`f16` should have been handled in `build_basic_type_di_node`")
+ }
ty::FloatTy::F32 => "float",
ty::FloatTy::F64 => "double",
ty::FloatTy::F128 => "fp128",
@@ -716,6 +718,38 @@ impl MsvcBasicName for ty::FloatTy {
}
}
+fn build_cpp_f16_di_node<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>) -> DINodeCreationResult<'ll> {
+ // MSVC has no native support for `f16`. Instead, emit `struct f16 { bits: u16 }` to allow the
+ // `f16`'s value to be displayed using a Natvis visualiser in `intrinsic.natvis`.
+ let float_ty = cx.tcx.types.f16;
+ let bits_ty = cx.tcx.types.u16;
+ type_map::build_type_with_children(
+ cx,
+ type_map::stub(
+ cx,
+ Stub::Struct,
+ UniqueTypeId::for_ty(cx.tcx, float_ty),
+ "f16",
+ cx.size_and_align_of(float_ty),
+ NO_SCOPE_METADATA,
+ DIFlags::FlagZero,
+ ),
+ // Fields:
+ |cx, float_di_node| {
+ smallvec![build_field_di_node(
+ cx,
+ float_di_node,
+ "bits",
+ cx.size_and_align_of(bits_ty),
+ Size::ZERO,
+ DIFlags::FlagZero,
+ type_di_node(cx, bits_ty),
+ )]
+ },
+ NO_GENERICS,
+ )
+}
+
fn build_basic_type_di_node<'ll, 'tcx>(
cx: &CodegenCx<'ll, 'tcx>,
t: Ty<'tcx>,
@@ -739,6 +773,9 @@ fn build_basic_type_di_node<'ll, 'tcx>(
ty::Char => ("char", DW_ATE_UTF),
ty::Int(int_ty) if cpp_like_debuginfo => (int_ty.msvc_basic_name(), DW_ATE_signed),
ty::Uint(uint_ty) if cpp_like_debuginfo => (uint_ty.msvc_basic_name(), DW_ATE_unsigned),
+ ty::Float(ty::FloatTy::F16) if cpp_like_debuginfo => {
+ return build_cpp_f16_di_node(cx);
+ }
ty::Float(float_ty) if cpp_like_debuginfo => (float_ty.msvc_basic_name(), DW_ATE_float),
ty::Int(int_ty) => (int_ty.name_str(), DW_ATE_signed),
ty::Uint(uint_ty) => (uint_ty.name_str(), DW_ATE_unsigned),
diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs
index e02c61cd2965..68c3d47e826b 100644
--- a/compiler/rustc_codegen_llvm/src/intrinsic.rs
+++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs
@@ -1469,8 +1469,10 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
let (elem_ty_str, elem_ty) = if let ty::Float(f) = in_elem.kind() {
let elem_ty = bx.cx.type_float_from_ty(*f);
match f.bit_width() {
+ 16 => ("f16", elem_ty),
32 => ("f32", elem_ty),
64 => ("f64", elem_ty),
+ 128 => ("f128", elem_ty),
_ => return_error!(InvalidMonomorphization::FloatingPointVector {
span,
name,
diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
index 08e9e312827c..e0bf6110cdf0 100644
--- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
+++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
@@ -2057,7 +2057,7 @@ extern "C" {
AddrOpsCount: c_uint,
DL: &'a DILocation,
InsertAtEnd: &'a BasicBlock,
- ) -> &'a Value;
+ );
pub fn LLVMRustDIBuilderCreateEnumerator<'a>(
Builder: &DIBuilder<'a>,
diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs
index 2bd5dfdce83e..dd134ebbe6b1 100644
--- a/compiler/rustc_codegen_ssa/src/back/linker.rs
+++ b/compiler/rustc_codegen_ssa/src/back/linker.rs
@@ -398,7 +398,7 @@ impl<'a> GccLinker<'a> {
self.link_arg("-dylib");
// Note that the `osx_rpath_install_name` option here is a hack
- // purely to support rustbuild right now, we should get a more
+ // purely to support bootstrap right now, we should get a more
// principled solution at some point to force the compiler to pass
// the right `-Wl,-install_name` with an `@rpath` in it.
if self.sess.opts.cg.rpath || self.sess.opts.unstable_opts.osx_rpath_install_name {
diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs
index bcddfe9fb9cb..cea164df6173 100644
--- a/compiler/rustc_codegen_ssa/src/target_features.rs
+++ b/compiler/rustc_codegen_ssa/src/target_features.rs
@@ -80,6 +80,8 @@ pub fn from_target_feature(
Some(sym::loongarch_target_feature) => rust_features.loongarch_target_feature,
Some(sym::lahfsahf_target_feature) => rust_features.lahfsahf_target_feature,
Some(sym::prfchw_target_feature) => rust_features.prfchw_target_feature,
+ Some(sym::x86_amx_intrinsics) => rust_features.x86_amx_intrinsics,
+ Some(sym::xop_target_feature) => rust_features.xop_target_feature,
Some(name) => bug!("unknown target feature gate {}", name),
None => true,
};
diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs
index 412effba32dc..523d55fe2d03 100644
--- a/compiler/rustc_const_eval/src/check_consts/check.rs
+++ b/compiler/rustc_const_eval/src/check_consts/check.rs
@@ -13,7 +13,7 @@ use rustc_middle::ty::{self, adjustment::PointerCoercion, Ty, TyCtxt};
use rustc_middle::ty::{Instance, InstanceKind, TypeVisitableExt};
use rustc_mir_dataflow::Analysis;
use rustc_span::{sym, Span, Symbol, DUMMY_SP};
-use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _;
+use rustc_trait_selection::error_reporting::traits::TypeErrCtxtExt as _;
use rustc_trait_selection::traits::{self, ObligationCauseCode, ObligationCtxt};
use rustc_type_ir::visit::{TypeSuperVisitable, TypeVisitor};
diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
index d86f1a7a34f2..b227565f8f91 100644
--- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs
+++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
@@ -20,7 +20,7 @@ use super::{
err_inval, err_ub_custom, err_unsup_format, memory::MemoryKind, throw_inval, throw_ub_custom,
throw_ub_format, util::ensure_monomorphic_enough, Allocation, CheckInAllocMsg, ConstAllocation,
GlobalId, ImmTy, InterpCx, InterpResult, MPlaceTy, Machine, OpTy, Pointer, PointerArithmetic,
- Scalar,
+ Provenance, Scalar,
};
use crate::fluent_generated as fluent;
@@ -259,25 +259,28 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
// This will always return 0.
(a, b)
}
- (Err(_), _) | (_, Err(_)) => {
- // We managed to find a valid allocation for one pointer, but not the other.
- // That means they are definitely not pointing to the same allocation.
+ _ if M::Provenance::OFFSET_IS_ADDR && a.addr() == b.addr() => {
+ // At least one of the pointers has provenance, but they also point to
+ // the same address so it doesn't matter; this is fine. `(0, 0)` means
+ // we pass all the checks below and return 0.
+ (0, 0)
+ }
+ // From here onwards, the pointers are definitely for different addresses
+ // (or we can't determine their absolute address).
+ (Ok((a_alloc_id, a_offset, _)), Ok((b_alloc_id, b_offset, _)))
+ if a_alloc_id == b_alloc_id =>
+ {
+ // Found allocation for both, and it's the same.
+ // Use these offsets for distance calculation.
+ (a_offset.bytes(), b_offset.bytes())
+ }
+ _ => {
+ // Not into the same allocation -- this is UB.
throw_ub_custom!(
fluent::const_eval_offset_from_different_allocations,
name = intrinsic_name,
);
}
- (Ok((a_alloc_id, a_offset, _)), Ok((b_alloc_id, b_offset, _))) => {
- // Found allocation for both. They must be into the same allocation.
- if a_alloc_id != b_alloc_id {
- throw_ub_custom!(
- fluent::const_eval_offset_from_different_allocations,
- name = intrinsic_name,
- );
- }
- // Use these offsets for distance calculation.
- (a_offset.bytes(), b_offset.bytes())
- }
};
// Compute distance.
diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs
index baaee67e7871..33c25b746ccc 100644
--- a/compiler/rustc_const_eval/src/interpret/place.rs
+++ b/compiler/rustc_const_eval/src/interpret/place.rs
@@ -995,13 +995,25 @@ where
}
/// Returns a wide MPlace of type `str` to a new 1-aligned allocation.
+ /// Immutable strings are deduplicated and stored in global memory.
pub fn allocate_str(
&mut self,
str: &str,
kind: MemoryKind,
mutbl: Mutability,
) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> {
- let ptr = self.allocate_bytes_ptr(str.as_bytes(), Align::ONE, kind, mutbl)?;
+ let tcx = self.tcx.tcx;
+
+ // Use cache for immutable strings.
+ let ptr = if mutbl.is_not() {
+ // Use dedup'd allocation function.
+ let id = tcx.allocate_bytes_dedup(str.as_bytes());
+
+ // Turn untagged "global" pointers (obtained via `tcx`) into the machine pointer to the allocation.
+ M::adjust_alloc_root_pointer(&self, Pointer::from(id), Some(kind))?
+ } else {
+ self.allocate_bytes_ptr(str.as_bytes(), Align::ONE, kind, mutbl)?
+ };
let meta = Scalar::from_target_usize(u64::try_from(str.len()).unwrap(), self);
let layout = self.layout_of(self.tcx.types.str_).unwrap();
Ok(self.ptr_with_meta_to_mplace(ptr.into(), MemPlaceMeta::Meta(meta), layout))
diff --git a/compiler/rustc_data_structures/Cargo.toml b/compiler/rustc_data_structures/Cargo.toml
index c4b2e067bbeb..e5e733439ea0 100644
--- a/compiler/rustc_data_structures/Cargo.toml
+++ b/compiler/rustc_data_structures/Cargo.toml
@@ -15,6 +15,7 @@ jobserver_crate = { version = "0.1.28", package = "jobserver" }
measureme = "11"
rustc-hash = "1.1.0"
rustc-rayon = { version = "0.5.0", optional = true }
+rustc-stable-hash = { version = "0.1.0", features = ["nightly"] }
rustc_arena = { path = "../rustc_arena" }
rustc_graphviz = { path = "../rustc_graphviz" }
rustc_index = { path = "../rustc_index", package = "rustc_index" }
diff --git a/compiler/rustc_data_structures/src/fingerprint.rs b/compiler/rustc_data_structures/src/fingerprint.rs
index 1bee159489dc..30e3d6aa86ce 100644
--- a/compiler/rustc_data_structures/src/fingerprint.rs
+++ b/compiler/rustc_data_structures/src/fingerprint.rs
@@ -1,5 +1,5 @@
use crate::stable_hasher::impl_stable_traits_for_trivial_type;
-use crate::stable_hasher::{Hash64, StableHasher, StableHasherResult};
+use crate::stable_hasher::{FromStableHash, Hash64, StableHasherHash};
use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
use std::hash::{Hash, Hasher};
@@ -154,10 +154,11 @@ impl FingerprintHasher for crate::unhash::Unhasher {
}
}
-impl StableHasherResult for Fingerprint {
+impl FromStableHash for Fingerprint {
+ type Hash = StableHasherHash;
+
#[inline]
- fn finish(hasher: StableHasher) -> Self {
- let (_0, _1) = hasher.finalize();
+ fn from(StableHasherHash([_0, _1]): Self::Hash) -> Self {
Fingerprint(_0, _1)
}
}
diff --git a/compiler/rustc_data_structures/src/hashes.rs b/compiler/rustc_data_structures/src/hashes.rs
index 1564eeb4baee..ef5d2e845ef0 100644
--- a/compiler/rustc_data_structures/src/hashes.rs
+++ b/compiler/rustc_data_structures/src/hashes.rs
@@ -11,7 +11,7 @@
//! connect the fact that they can only be produced by a `StableHasher` to their
//! `Encode`/`Decode` impls.
-use crate::stable_hasher::{StableHasher, StableHasherResult};
+use crate::stable_hasher::{FromStableHash, StableHasherHash};
use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
use std::fmt;
use std::ops::BitXorAssign;
@@ -56,10 +56,12 @@ impl Decodable for Hash64 {
}
}
-impl StableHasherResult for Hash64 {
+impl FromStableHash for Hash64 {
+ type Hash = StableHasherHash;
+
#[inline]
- fn finish(hasher: StableHasher) -> Self {
- Self { inner: hasher.finalize().0 }
+ fn from(StableHasherHash([_0, __1]): Self::Hash) -> Self {
+ Self { inner: _0 }
}
}
@@ -121,10 +123,11 @@ impl Decodable for Hash128 {
}
}
-impl StableHasherResult for Hash128 {
+impl FromStableHash for Hash128 {
+ type Hash = StableHasherHash;
+
#[inline]
- fn finish(hasher: StableHasher) -> Self {
- let (_0, _1) = hasher.finalize();
+ fn from(StableHasherHash([_0, _1]): Self::Hash) -> Self {
Self { inner: u128::from(_0) | (u128::from(_1) << 64) }
}
}
diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs
index 356ddf014bee..3f18b036940b 100644
--- a/compiler/rustc_data_structures/src/lib.rs
+++ b/compiler/rustc_data_structures/src/lib.rs
@@ -24,7 +24,6 @@
#![feature(core_intrinsics)]
#![feature(extend_one)]
#![feature(hash_raw_entry)]
-#![feature(hasher_prefixfree_extras)]
#![feature(macro_metavar_expr)]
#![feature(map_try_insert)]
#![feature(min_specialization)]
@@ -67,7 +66,6 @@ pub mod owned_slice;
pub mod packed;
pub mod profiling;
pub mod sharded;
-pub mod sip128;
pub mod small_c_str;
pub mod snapshot_map;
pub mod sorted_map;
diff --git a/compiler/rustc_data_structures/src/sip128.rs b/compiler/rustc_data_structures/src/sip128.rs
deleted file mode 100644
index 812ed410a94b..000000000000
--- a/compiler/rustc_data_structures/src/sip128.rs
+++ /dev/null
@@ -1,505 +0,0 @@
-//! This is a copy of `core::hash::sip` adapted to providing 128 bit hashes.
-
-// This code is very hot and uses lots of arithmetic, avoid overflow checks for performance.
-// See https://github.com/rust-lang/rust/pull/119440#issuecomment-1874255727
-use rustc_serialize::int_overflow::{DebugStrictAdd, DebugStrictSub};
-use std::hash::Hasher;
-use std::mem::{self, MaybeUninit};
-use std::ptr;
-
-#[cfg(test)]
-mod tests;
-
-// The SipHash algorithm operates on 8-byte chunks.
-const ELEM_SIZE: usize = mem::size_of::();
-
-// Size of the buffer in number of elements, not including the spill.
-//
-// The selection of this size was guided by rustc-perf benchmark comparisons of
-// different buffer sizes. It should be periodically reevaluated as the compiler
-// implementation and input characteristics change.
-//
-// Using the same-sized buffer for everything we hash is a performance versus
-// complexity tradeoff. The ideal buffer size, and whether buffering should even
-// be used, depends on what is being hashed. It may be worth it to size the
-// buffer appropriately (perhaps by making SipHasher128 generic over the buffer
-// size) or disable buffering depending on what is being hashed. But at this
-// time, we use the same buffer size for everything.
-const BUFFER_CAPACITY: usize = 8;
-
-// Size of the buffer in bytes, not including the spill.
-const BUFFER_SIZE: usize = BUFFER_CAPACITY * ELEM_SIZE;
-
-// Size of the buffer in number of elements, including the spill.
-const BUFFER_WITH_SPILL_CAPACITY: usize = BUFFER_CAPACITY + 1;
-
-// Size of the buffer in bytes, including the spill.
-const BUFFER_WITH_SPILL_SIZE: usize = BUFFER_WITH_SPILL_CAPACITY * ELEM_SIZE;
-
-// Index of the spill element in the buffer.
-const BUFFER_SPILL_INDEX: usize = BUFFER_WITH_SPILL_CAPACITY - 1;
-
-#[derive(Debug, Clone)]
-#[repr(C)]
-pub struct SipHasher128 {
- // The access pattern during hashing consists of accesses to `nbuf` and
- // `buf` until the buffer is full, followed by accesses to `state` and
- // `processed`, and then repetition of that pattern until hashing is done.
- // This is the basis for the ordering of fields below. However, in practice
- // the cache miss-rate for data access is extremely low regardless of order.
- nbuf: usize, // how many bytes in buf are valid
- buf: [MaybeUninit; BUFFER_WITH_SPILL_CAPACITY], // unprocessed bytes le
- state: State, // hash State
- processed: usize, // how many bytes we've processed
-}
-
-#[derive(Debug, Clone, Copy)]
-#[repr(C)]
-struct State {
- // v0, v2 and v1, v3 show up in pairs in the algorithm,
- // and simd implementations of SipHash will use vectors
- // of v02 and v13. By placing them in this order in the struct,
- // the compiler can pick up on just a few simd optimizations by itself.
- v0: u64,
- v2: u64,
- v1: u64,
- v3: u64,
-}
-
-macro_rules! compress {
- ($state:expr) => {{ compress!($state.v0, $state.v1, $state.v2, $state.v3) }};
- ($v0:expr, $v1:expr, $v2:expr, $v3:expr) => {{
- $v0 = $v0.wrapping_add($v1);
- $v2 = $v2.wrapping_add($v3);
- $v1 = $v1.rotate_left(13);
- $v1 ^= $v0;
- $v3 = $v3.rotate_left(16);
- $v3 ^= $v2;
- $v0 = $v0.rotate_left(32);
-
- $v2 = $v2.wrapping_add($v1);
- $v0 = $v0.wrapping_add($v3);
- $v1 = $v1.rotate_left(17);
- $v1 ^= $v2;
- $v3 = $v3.rotate_left(21);
- $v3 ^= $v0;
- $v2 = $v2.rotate_left(32);
- }};
-}
-
-// Copies up to 8 bytes from source to destination. This performs better than
-// `ptr::copy_nonoverlapping` on microbenchmarks and may perform better on real
-// workloads since all of the copies have fixed sizes and avoid calling memcpy.
-//
-// This is specifically designed for copies of up to 8 bytes, because that's the
-// maximum of number bytes needed to fill an 8-byte-sized element on which
-// SipHash operates. Note that for variable-sized copies which are known to be
-// less than 8 bytes, this function will perform more work than necessary unless
-// the compiler is able to optimize the extra work away.
-#[inline]
-unsafe fn copy_nonoverlapping_small(src: *const u8, dst: *mut u8, count: usize) {
- debug_assert!(count <= 8);
-
- unsafe {
- if count == 8 {
- ptr::copy_nonoverlapping(src, dst, 8);
- return;
- }
-
- let mut i = 0;
- if i.debug_strict_add(3) < count {
- ptr::copy_nonoverlapping(src.add(i), dst.add(i), 4);
- i = i.debug_strict_add(4);
- }
-
- if i.debug_strict_add(1) < count {
- ptr::copy_nonoverlapping(src.add(i), dst.add(i), 2);
- i = i.debug_strict_add(2)
- }
-
- if i < count {
- *dst.add(i) = *src.add(i);
- i = i.debug_strict_add(1);
- }
-
- debug_assert_eq!(i, count);
- }
-}
-
-// # Implementation
-//
-// This implementation uses buffering to reduce the hashing cost for inputs
-// consisting of many small integers. Buffering simplifies the integration of
-// integer input--the integer write function typically just appends to the
-// buffer with a statically sized write, updates metadata, and returns.
-//
-// Buffering also prevents alternating between writes that do and do not trigger
-// the hashing process. Only when the entire buffer is full do we transition
-// into hashing. This allows us to keep the hash state in registers for longer,
-// instead of loading and storing it before and after processing each element.
-//
-// When a write fills the buffer, a buffer processing function is invoked to
-// hash all of the buffered input. The buffer processing functions are marked
-// `#[inline(never)]` so that they aren't inlined into the append functions,
-// which ensures the more frequently called append functions remain inlineable
-// and don't include register pushing/popping that would only be made necessary
-// by inclusion of the complex buffer processing path which uses those
-// registers.
-//
-// The buffer includes a "spill"--an extra element at the end--which simplifies
-// the integer write buffer processing path. The value that fills the buffer can
-// be written with a statically sized write that may spill over into the spill.
-// After the buffer is processed, the part of the value that spilled over can be
-// written from the spill to the beginning of the buffer with another statically
-// sized write. This write may copy more bytes than actually spilled over, but
-// we maintain the metadata such that any extra copied bytes will be ignored by
-// subsequent processing. Due to the static sizes, this scheme performs better
-// than copying the exact number of bytes needed into the end and beginning of
-// the buffer.
-//
-// The buffer is uninitialized, which improves performance, but may preclude
-// efficient implementation of alternative approaches. The improvement is not so
-// large that an alternative approach should be disregarded because it cannot be
-// efficiently implemented with an uninitialized buffer. On the other hand, an
-// uninitialized buffer may become more important should a larger one be used.
-//
-// # Platform Dependence
-//
-// The SipHash algorithm operates on byte sequences. It parses the input stream
-// as 8-byte little-endian integers. Therefore, given the same byte sequence, it
-// produces the same result on big- and little-endian hardware.
-//
-// However, the Hasher trait has methods which operate on multi-byte integers.
-// How they are converted into byte sequences can be endian-dependent (by using
-// native byte order) or independent (by consistently using either LE or BE byte
-// order). It can also be `isize` and `usize` size dependent (by using the
-// native size), or independent (by converting to a common size), supposing the
-// values can be represented in 32 bits.
-//
-// In order to make `SipHasher128` consistent with `SipHasher` in libstd, we
-// choose to do the integer to byte sequence conversion in the platform-
-// dependent way. Clients can achieve platform-independent hashing by widening
-// `isize` and `usize` integers to 64 bits on 32-bit systems and byte-swapping
-// integers on big-endian systems before passing them to the writing functions.
-// This causes the input byte sequence to look identical on big- and little-
-// endian systems (supposing `isize` and `usize` values can be represented in 32
-// bits), which ensures platform-independent results.
-impl SipHasher128 {
- #[inline]
- pub fn new_with_keys(key0: u64, key1: u64) -> SipHasher128 {
- let mut hasher = SipHasher128 {
- nbuf: 0,
- buf: [MaybeUninit::uninit(); BUFFER_WITH_SPILL_CAPACITY],
- state: State {
- v0: key0 ^ 0x736f6d6570736575,
- // The XOR with 0xee is only done on 128-bit algorithm version.
- v1: key1 ^ (0x646f72616e646f6d ^ 0xee),
- v2: key0 ^ 0x6c7967656e657261,
- v3: key1 ^ 0x7465646279746573,
- },
- processed: 0,
- };
-
- unsafe {
- // Initialize spill because we read from it in `short_write_process_buffer`.
- *hasher.buf.get_unchecked_mut(BUFFER_SPILL_INDEX) = MaybeUninit::zeroed();
- }
-
- hasher
- }
-
- #[inline]
- pub fn short_write(&mut self, bytes: [u8; LEN]) {
- let nbuf = self.nbuf;
- debug_assert!(LEN <= 8);
- debug_assert!(nbuf < BUFFER_SIZE);
- debug_assert!(nbuf + LEN < BUFFER_WITH_SPILL_SIZE);
-
- if nbuf.debug_strict_add(LEN) < BUFFER_SIZE {
- unsafe {
- // The memcpy call is optimized away because the size is known.
- let dst = (self.buf.as_mut_ptr() as *mut u8).add(nbuf);
- ptr::copy_nonoverlapping(bytes.as_ptr(), dst, LEN);
- }
-
- self.nbuf = nbuf.debug_strict_add(LEN);
-
- return;
- }
-
- unsafe { self.short_write_process_buffer(bytes) }
- }
-
- // A specialized write function for values with size <= 8 that should only
- // be called when the write would cause the buffer to fill.
- //
- // SAFETY: the write of `x` into `self.buf` starting at byte offset
- // `self.nbuf` must cause `self.buf` to become fully initialized (and not
- // overflow) if it wasn't already.
- #[inline(never)]
- unsafe fn short_write_process_buffer(&mut self, bytes: [u8; LEN]) {
- unsafe {
- let nbuf = self.nbuf;
- debug_assert!(LEN <= 8);
- debug_assert!(nbuf < BUFFER_SIZE);
- debug_assert!(nbuf + LEN >= BUFFER_SIZE);
- debug_assert!(nbuf + LEN < BUFFER_WITH_SPILL_SIZE);
-
- // Copy first part of input into end of buffer, possibly into spill
- // element. The memcpy call is optimized away because the size is known.
- let dst = (self.buf.as_mut_ptr() as *mut u8).add(nbuf);
- ptr::copy_nonoverlapping(bytes.as_ptr(), dst, LEN);
-
- // Process buffer.
- for i in 0..BUFFER_CAPACITY {
- let elem = self.buf.get_unchecked(i).assume_init().to_le();
- self.state.v3 ^= elem;
- Sip13Rounds::c_rounds(&mut self.state);
- self.state.v0 ^= elem;
- }
-
- // Copy remaining input into start of buffer by copying LEN - 1
- // elements from spill (at most LEN - 1 bytes could have overflowed
- // into the spill). The memcpy call is optimized away because the size
- // is known. And the whole copy is optimized away for LEN == 1.
- let dst = self.buf.as_mut_ptr() as *mut u8;
- let src = self.buf.get_unchecked(BUFFER_SPILL_INDEX) as *const _ as *const u8;
- ptr::copy_nonoverlapping(src, dst, LEN - 1);
-
- // This function should only be called when the write fills the buffer.
- // Therefore, when LEN == 1, the new `self.nbuf` must be zero.
- // LEN is statically known, so the branch is optimized away.
- self.nbuf =
- if LEN == 1 { 0 } else { nbuf.debug_strict_add(LEN).debug_strict_sub(BUFFER_SIZE) };
- self.processed = self.processed.debug_strict_add(BUFFER_SIZE);
- }
- }
-
- // A write function for byte slices.
- #[inline]
- fn slice_write(&mut self, msg: &[u8]) {
- let length = msg.len();
- let nbuf = self.nbuf;
- debug_assert!(nbuf < BUFFER_SIZE);
-
- if nbuf.debug_strict_add(length) < BUFFER_SIZE {
- unsafe {
- let dst = (self.buf.as_mut_ptr() as *mut u8).add(nbuf);
-
- if length <= 8 {
- copy_nonoverlapping_small(msg.as_ptr(), dst, length);
- } else {
- // This memcpy is *not* optimized away.
- ptr::copy_nonoverlapping(msg.as_ptr(), dst, length);
- }
- }
-
- self.nbuf = nbuf.debug_strict_add(length);
-
- return;
- }
-
- unsafe { self.slice_write_process_buffer(msg) }
- }
-
- // A write function for byte slices that should only be called when the
- // write would cause the buffer to fill.
- //
- // SAFETY: `self.buf` must be initialized up to the byte offset `self.nbuf`,
- // and `msg` must contain enough bytes to initialize the rest of the element
- // containing the byte offset `self.nbuf`.
- #[inline(never)]
- unsafe fn slice_write_process_buffer(&mut self, msg: &[u8]) {
- unsafe {
- let length = msg.len();
- let nbuf = self.nbuf;
- debug_assert!(nbuf < BUFFER_SIZE);
- debug_assert!(nbuf + length >= BUFFER_SIZE);
-
- // Always copy first part of input into current element of buffer.
- // This function should only be called when the write fills the buffer,
- // so we know that there is enough input to fill the current element.
- let valid_in_elem = nbuf % ELEM_SIZE;
- let needed_in_elem = ELEM_SIZE.debug_strict_sub(valid_in_elem);
-
- let src = msg.as_ptr();
- let dst = (self.buf.as_mut_ptr() as *mut u8).add(nbuf);
- copy_nonoverlapping_small(src, dst, needed_in_elem);
-
- // Process buffer.
-
- // Using `nbuf / ELEM_SIZE + 1` rather than `(nbuf + needed_in_elem) /
- // ELEM_SIZE` to show the compiler that this loop's upper bound is > 0.
- // We know that is true, because last step ensured we have a full
- // element in the buffer.
- let last = (nbuf / ELEM_SIZE).debug_strict_add(1);
-
- for i in 0..last {
- let elem = self.buf.get_unchecked(i).assume_init().to_le();
- self.state.v3 ^= elem;
- Sip13Rounds::c_rounds(&mut self.state);
- self.state.v0 ^= elem;
- }
-
- // Process the remaining element-sized chunks of input.
- let mut processed = needed_in_elem;
- let input_left = length.debug_strict_sub(processed);
- let elems_left = input_left / ELEM_SIZE;
- let extra_bytes_left = input_left % ELEM_SIZE;
-
- for _ in 0..elems_left {
- let elem = (msg.as_ptr().add(processed) as *const u64).read_unaligned().to_le();
- self.state.v3 ^= elem;
- Sip13Rounds::c_rounds(&mut self.state);
- self.state.v0 ^= elem;
- processed = processed.debug_strict_add(ELEM_SIZE);
- }
-
- // Copy remaining input into start of buffer.
- let src = msg.as_ptr().add(processed);
- let dst = self.buf.as_mut_ptr() as *mut u8;
- copy_nonoverlapping_small(src, dst, extra_bytes_left);
-
- self.nbuf = extra_bytes_left;
- self.processed = self.processed.debug_strict_add(nbuf.debug_strict_add(processed));
- }
- }
-
- #[inline]
- pub fn finish128(mut self) -> (u64, u64) {
- debug_assert!(self.nbuf < BUFFER_SIZE);
-
- // Process full elements in buffer.
- let last = self.nbuf / ELEM_SIZE;
-
- // Since we're consuming self, avoid updating members for a potential
- // performance gain.
- let mut state = self.state;
-
- for i in 0..last {
- let elem = unsafe { self.buf.get_unchecked(i).assume_init().to_le() };
- state.v3 ^= elem;
- Sip13Rounds::c_rounds(&mut state);
- state.v0 ^= elem;
- }
-
- // Get remaining partial element.
- let elem = if self.nbuf % ELEM_SIZE != 0 {
- unsafe {
- // Ensure element is initialized by writing zero bytes. At most
- // `ELEM_SIZE - 1` are required given the above check. It's safe
- // to write this many because we have the spill and we maintain
- // `self.nbuf` such that this write will start before the spill.
- let dst = (self.buf.as_mut_ptr() as *mut u8).add(self.nbuf);
- ptr::write_bytes(dst, 0, ELEM_SIZE - 1);
- self.buf.get_unchecked(last).assume_init().to_le()
- }
- } else {
- 0
- };
-
- // Finalize the hash.
- let length = self.processed.debug_strict_add(self.nbuf);
- let b: u64 = ((length as u64 & 0xff) << 56) | elem;
-
- state.v3 ^= b;
- Sip13Rounds::c_rounds(&mut state);
- state.v0 ^= b;
-
- state.v2 ^= 0xee;
- Sip13Rounds::d_rounds(&mut state);
- let _0 = state.v0 ^ state.v1 ^ state.v2 ^ state.v3;
-
- state.v1 ^= 0xdd;
- Sip13Rounds::d_rounds(&mut state);
- let _1 = state.v0 ^ state.v1 ^ state.v2 ^ state.v3;
-
- (_0, _1)
- }
-}
-
-impl Hasher for SipHasher128 {
- #[inline]
- fn write_u8(&mut self, i: u8) {
- self.short_write(i.to_ne_bytes());
- }
-
- #[inline]
- fn write_u16(&mut self, i: u16) {
- self.short_write(i.to_ne_bytes());
- }
-
- #[inline]
- fn write_u32(&mut self, i: u32) {
- self.short_write(i.to_ne_bytes());
- }
-
- #[inline]
- fn write_u64(&mut self, i: u64) {
- self.short_write(i.to_ne_bytes());
- }
-
- #[inline]
- fn write_usize(&mut self, i: usize) {
- self.short_write(i.to_ne_bytes());
- }
-
- #[inline]
- fn write_i8(&mut self, i: i8) {
- self.short_write((i as u8).to_ne_bytes());
- }
-
- #[inline]
- fn write_i16(&mut self, i: i16) {
- self.short_write((i as u16).to_ne_bytes());
- }
-
- #[inline]
- fn write_i32(&mut self, i: i32) {
- self.short_write((i as u32).to_ne_bytes());
- }
-
- #[inline]
- fn write_i64(&mut self, i: i64) {
- self.short_write((i as u64).to_ne_bytes());
- }
-
- #[inline]
- fn write_isize(&mut self, i: isize) {
- self.short_write((i as usize).to_ne_bytes());
- }
-
- #[inline]
- fn write(&mut self, msg: &[u8]) {
- self.slice_write(msg);
- }
-
- #[inline]
- fn write_str(&mut self, s: &str) {
- // This hasher works byte-wise, and `0xFF` cannot show up in a `str`,
- // so just hashing the one extra byte is enough to be prefix-free.
- self.write(s.as_bytes());
- self.write_u8(0xFF);
- }
-
- fn finish(&self) -> u64 {
- panic!("SipHasher128 cannot provide valid 64 bit hashes")
- }
-}
-
-#[derive(Debug, Clone, Default)]
-struct Sip13Rounds;
-
-impl Sip13Rounds {
- #[inline]
- fn c_rounds(state: &mut State) {
- compress!(state);
- }
-
- #[inline]
- fn d_rounds(state: &mut State) {
- compress!(state);
- compress!(state);
- compress!(state);
- }
-}
diff --git a/compiler/rustc_data_structures/src/sip128/tests.rs b/compiler/rustc_data_structures/src/sip128/tests.rs
deleted file mode 100644
index e9dd0f1176b9..000000000000
--- a/compiler/rustc_data_structures/src/sip128/tests.rs
+++ /dev/null
@@ -1,304 +0,0 @@
-use super::*;
-
-use std::hash::Hash;
-
-// Hash just the bytes of the slice, without length prefix
-struct Bytes<'a>(&'a [u8]);
-
-impl<'a> Hash for Bytes<'a> {
- #[allow(unused_must_use)]
- fn hash(&self, state: &mut H) {
- for byte in self.0 {
- state.write_u8(*byte);
- }
- }
-}
-
-fn hash_with(mut st: SipHasher128, x: &T) -> (u64, u64) {
- x.hash(&mut st);
- st.finish128()
-}
-
-fn hash(x: &T) -> (u64, u64) {
- hash_with(SipHasher128::new_with_keys(0, 0), x)
-}
-#[rustfmt::skip]
-const TEST_VECTOR: [[u8; 16]; 64] = [
- [0xe7, 0x7e, 0xbc, 0xb2, 0x27, 0x88, 0xa5, 0xbe, 0xfd, 0x62, 0xdb, 0x6a, 0xdd, 0x30, 0x30, 0x01],
- [0xfc, 0x6f, 0x37, 0x04, 0x60, 0xd3, 0xed, 0xa8, 0x5e, 0x05, 0x73, 0xcc, 0x2b, 0x2f, 0xf0, 0x63],
- [0x75, 0x78, 0x7f, 0x09, 0x05, 0x69, 0x83, 0x9b, 0x85, 0x5b, 0xc9, 0x54, 0x8c, 0x6a, 0xea, 0x95],
- [0x6b, 0xc5, 0xcc, 0xfa, 0x1e, 0xdc, 0xf7, 0x9f, 0x48, 0x23, 0x18, 0x77, 0x12, 0xeb, 0xd7, 0x43],
- [0x0c, 0x78, 0x4e, 0x71, 0xac, 0x2b, 0x28, 0x5a, 0x9f, 0x8e, 0x92, 0xe7, 0x8f, 0xbf, 0x2c, 0x25],
- [0xf3, 0x28, 0xdb, 0x89, 0x34, 0x5b, 0x62, 0x0c, 0x79, 0x52, 0x29, 0xa4, 0x26, 0x95, 0x84, 0x3e],
- [0xdc, 0xd0, 0x3d, 0x29, 0xf7, 0x43, 0xe7, 0x10, 0x09, 0x51, 0xb0, 0xe8, 0x39, 0x85, 0xa6, 0xf8],
- [0x10, 0x84, 0xb9, 0x23, 0xf2, 0xaa, 0xe0, 0xc3, 0xa6, 0x2f, 0x2e, 0xc8, 0x08, 0x48, 0xab, 0x77],
- [0xaa, 0x12, 0xfe, 0xe1, 0xd5, 0xe3, 0xda, 0xb4, 0x72, 0x4f, 0x16, 0xab, 0x35, 0xf9, 0xc7, 0x99],
- [0x81, 0xdd, 0xb8, 0x04, 0x2c, 0xf3, 0x39, 0x94, 0xf4, 0x72, 0x0e, 0x00, 0x94, 0x13, 0x7c, 0x42],
- [0x4f, 0xaa, 0x54, 0x1d, 0x5d, 0x49, 0x8e, 0x89, 0xba, 0x0e, 0xa4, 0xc3, 0x87, 0xb2, 0x2f, 0xb4],
- [0x72, 0x3b, 0x9a, 0xf3, 0x55, 0x44, 0x91, 0xdb, 0xb1, 0xd6, 0x63, 0x3d, 0xfc, 0x6e, 0x0c, 0x4e],
- [0xe5, 0x3f, 0x92, 0x85, 0x9e, 0x48, 0x19, 0xa8, 0xdc, 0x06, 0x95, 0x73, 0x9f, 0xea, 0x8c, 0x65],
- [0xb2, 0xf8, 0x58, 0xc7, 0xc9, 0xea, 0x80, 0x1d, 0x53, 0xd6, 0x03, 0x59, 0x6d, 0x65, 0x78, 0x44],
- [0x87, 0xe7, 0x62, 0x68, 0xdb, 0xc9, 0x22, 0x72, 0x26, 0xb0, 0xca, 0x66, 0x5f, 0x64, 0xe3, 0x78],
- [0xc1, 0x7e, 0x55, 0x05, 0xb2, 0xbd, 0x52, 0x6c, 0x29, 0x21, 0xcd, 0xec, 0x1e, 0x7e, 0x01, 0x09],
- [0xd0, 0xa8, 0xd9, 0x57, 0x15, 0x51, 0x8e, 0xeb, 0xb5, 0x13, 0xb0, 0xf8, 0x3d, 0x9e, 0x17, 0x93],
- [0x23, 0x41, 0x26, 0xf9, 0x3f, 0xbb, 0x66, 0x8d, 0x97, 0x51, 0x12, 0xe8, 0xfe, 0xbd, 0xf7, 0xec],
- [0xef, 0x42, 0xf0, 0x3d, 0xb7, 0x8f, 0x70, 0x4d, 0x02, 0x3c, 0x44, 0x9f, 0x16, 0xb7, 0x09, 0x2b],
- [0xab, 0xf7, 0x62, 0x38, 0xc2, 0x0a, 0xf1, 0x61, 0xb2, 0x31, 0x4b, 0x4d, 0x55, 0x26, 0xbc, 0xe9],
- [0x3c, 0x2c, 0x2f, 0x11, 0xbb, 0x90, 0xcf, 0x0b, 0xe3, 0x35, 0xca, 0x9b, 0x2e, 0x91, 0xe9, 0xb7],
- [0x2a, 0x7a, 0x68, 0x0f, 0x22, 0xa0, 0x2a, 0x92, 0xf4, 0x51, 0x49, 0xd2, 0x0f, 0xec, 0xe0, 0xef],
- [0xc9, 0xa8, 0xd1, 0x30, 0x23, 0x1d, 0xd4, 0x3e, 0x42, 0xe6, 0x45, 0x69, 0x57, 0xf8, 0x37, 0x79],
- [0x1d, 0x12, 0x7b, 0x84, 0x40, 0x5c, 0xea, 0xb9, 0x9f, 0xd8, 0x77, 0x5a, 0x9b, 0xe6, 0xc5, 0x59],
- [0x9e, 0x4b, 0xf8, 0x37, 0xbc, 0xfd, 0x92, 0xca, 0xce, 0x09, 0xd2, 0x06, 0x1a, 0x84, 0xd0, 0x4a],
- [0x39, 0x03, 0x1a, 0x96, 0x5d, 0x73, 0xb4, 0xaf, 0x5a, 0x27, 0x4d, 0x18, 0xf9, 0x73, 0xb1, 0xd2],
- [0x7f, 0x4d, 0x0a, 0x12, 0x09, 0xd6, 0x7e, 0x4e, 0xd0, 0x6f, 0x75, 0x38, 0xe1, 0xcf, 0xad, 0x64],
- [0xe6, 0x1e, 0xe2, 0x40, 0xfb, 0xdc, 0xce, 0x38, 0x96, 0x9f, 0x4c, 0xd2, 0x49, 0x27, 0xdd, 0x93],
- [0x4c, 0x3b, 0xa2, 0xb3, 0x7b, 0x0f, 0xdd, 0x8c, 0xfa, 0x5e, 0x95, 0xc1, 0x89, 0xb2, 0x94, 0x14],
- [0xe0, 0x6f, 0xd4, 0xca, 0x06, 0x6f, 0xec, 0xdd, 0x54, 0x06, 0x8a, 0x5a, 0xd8, 0x89, 0x6f, 0x86],
- [0x5c, 0xa8, 0x4c, 0x34, 0x13, 0x9c, 0x65, 0x80, 0xa8, 0x8a, 0xf2, 0x49, 0x90, 0x72, 0x07, 0x06],
- [0x42, 0xea, 0x96, 0x1c, 0x5b, 0x3c, 0x85, 0x8b, 0x17, 0xc3, 0xe5, 0x50, 0xdf, 0xa7, 0x90, 0x10],
- [0x40, 0x6c, 0x44, 0xde, 0xe6, 0x78, 0x57, 0xb2, 0x94, 0x31, 0x60, 0xf3, 0x0c, 0x74, 0x17, 0xd3],
- [0xc5, 0xf5, 0x7b, 0xae, 0x13, 0x20, 0xfc, 0xf4, 0xb4, 0xe8, 0x68, 0xe7, 0x1d, 0x56, 0xc6, 0x6b],
- [0x04, 0xbf, 0x73, 0x7a, 0x5b, 0x67, 0x6b, 0xe7, 0xc3, 0xde, 0x05, 0x01, 0x7d, 0xf4, 0xbf, 0xf9],
- [0x51, 0x63, 0xc9, 0xc0, 0x3f, 0x19, 0x07, 0xea, 0x10, 0x44, 0xed, 0x5c, 0x30, 0x72, 0x7b, 0x4f],
- [0x37, 0xa1, 0x10, 0xf0, 0x02, 0x71, 0x8e, 0xda, 0xd2, 0x4b, 0x3f, 0x9e, 0xe4, 0x53, 0xf1, 0x40],
- [0xb9, 0x87, 0x7e, 0x38, 0x1a, 0xed, 0xd3, 0xda, 0x08, 0xc3, 0x3e, 0x75, 0xff, 0x23, 0xac, 0x10],
- [0x7c, 0x50, 0x04, 0x00, 0x5e, 0xc5, 0xda, 0x4c, 0x5a, 0xc9, 0x44, 0x0e, 0x5c, 0x72, 0x31, 0x93],
- [0x81, 0xb8, 0x24, 0x37, 0x83, 0xdb, 0xc6, 0x46, 0xca, 0x9d, 0x0c, 0xd8, 0x2a, 0xbd, 0xb4, 0x6c],
- [0x50, 0x57, 0x20, 0x54, 0x3e, 0xb9, 0xb4, 0x13, 0xd5, 0x0b, 0x3c, 0xfa, 0xd9, 0xee, 0xf9, 0x38],
- [0x94, 0x5f, 0x59, 0x4d, 0xe7, 0x24, 0x11, 0xe4, 0xd3, 0x35, 0xbe, 0x87, 0x44, 0x56, 0xd8, 0xf3],
- [0x37, 0x92, 0x3b, 0x3e, 0x37, 0x17, 0x77, 0xb2, 0x11, 0x70, 0xbf, 0x9d, 0x7e, 0x62, 0xf6, 0x02],
- [0x3a, 0xd4, 0xe7, 0xc8, 0x57, 0x64, 0x96, 0x46, 0x11, 0xeb, 0x0a, 0x6c, 0x4d, 0x62, 0xde, 0x56],
- [0xcd, 0x91, 0x39, 0x6c, 0x44, 0xaf, 0x4f, 0x51, 0x85, 0x57, 0x8d, 0x9d, 0xd9, 0x80, 0x3f, 0x0a],
- [0xfe, 0x28, 0x15, 0x8e, 0x72, 0x7b, 0x86, 0x8f, 0x39, 0x03, 0xc9, 0xac, 0xda, 0x64, 0xa2, 0x58],
- [0x40, 0xcc, 0x10, 0xb8, 0x28, 0x8c, 0xe5, 0xf0, 0xbc, 0x3a, 0xc0, 0xb6, 0x8a, 0x0e, 0xeb, 0xc8],
- [0x6f, 0x14, 0x90, 0xf5, 0x40, 0x69, 0x9a, 0x3c, 0xd4, 0x97, 0x44, 0x20, 0xec, 0xc9, 0x27, 0x37],
- [0xd5, 0x05, 0xf1, 0xb7, 0x5e, 0x1a, 0x84, 0xa6, 0x03, 0xc4, 0x35, 0x83, 0xb2, 0xed, 0x03, 0x08],
- [0x49, 0x15, 0x73, 0xcf, 0xd7, 0x2b, 0xb4, 0x68, 0x2b, 0x7c, 0xa5, 0x88, 0x0e, 0x1c, 0x8d, 0x6f],
- [0x3e, 0xd6, 0x9c, 0xfe, 0x45, 0xab, 0x40, 0x3f, 0x2f, 0xd2, 0xad, 0x95, 0x9b, 0xa2, 0x76, 0x66],
- [0x8b, 0xe8, 0x39, 0xef, 0x1b, 0x20, 0xb5, 0x7c, 0x83, 0xba, 0x7e, 0xb6, 0xa8, 0xc2, 0x2b, 0x6a],
- [0x14, 0x09, 0x18, 0x6a, 0xb4, 0x22, 0x31, 0xfe, 0xde, 0xe1, 0x81, 0x62, 0xcf, 0x1c, 0xb4, 0xca],
- [0x2b, 0xf3, 0xcc, 0xc2, 0x4a, 0xb6, 0x72, 0xcf, 0x15, 0x1f, 0xb8, 0xd2, 0xf3, 0xf3, 0x06, 0x9b],
- [0xb9, 0xb9, 0x3a, 0x28, 0x82, 0xd6, 0x02, 0x5c, 0xdb, 0x8c, 0x56, 0xfa, 0x13, 0xf7, 0x53, 0x7b],
- [0xd9, 0x7c, 0xca, 0x36, 0x94, 0xfb, 0x20, 0x6d, 0xb8, 0xbd, 0x1f, 0x36, 0x50, 0xc3, 0x33, 0x22],
- [0x94, 0xec, 0x2e, 0x19, 0xa4, 0x0b, 0xe4, 0x1a, 0xf3, 0x94, 0x0d, 0x6b, 0x30, 0xc4, 0x93, 0x84],
- [0x4b, 0x41, 0x60, 0x3f, 0x20, 0x9a, 0x04, 0x5b, 0xe1, 0x40, 0xa3, 0x41, 0xa3, 0xdf, 0xfe, 0x10],
- [0x23, 0xfb, 0xcb, 0x30, 0x9f, 0x1c, 0xf0, 0x94, 0x89, 0x07, 0x55, 0xab, 0x1b, 0x42, 0x65, 0x69],
- [0xe7, 0xd9, 0xb6, 0x56, 0x90, 0x91, 0x8a, 0x2b, 0x23, 0x2f, 0x2f, 0x5c, 0x12, 0xc8, 0x30, 0x0e],
- [0xad, 0xe8, 0x3c, 0xf7, 0xe7, 0xf3, 0x84, 0x7b, 0x36, 0xfa, 0x4b, 0x54, 0xb0, 0x0d, 0xce, 0x61],
- [0x06, 0x10, 0xc5, 0xf2, 0xee, 0x57, 0x1c, 0x8a, 0xc8, 0x0c, 0xbf, 0xe5, 0x38, 0xbd, 0xf1, 0xc7],
- [0x27, 0x1d, 0x5d, 0x00, 0xfb, 0xdb, 0x5d, 0x15, 0x5d, 0x9d, 0xce, 0xa9, 0x7c, 0xb4, 0x02, 0x18],
- [0x4c, 0x58, 0x00, 0xe3, 0x4e, 0xfe, 0x42, 0x6f, 0x07, 0x9f, 0x6b, 0x0a, 0xa7, 0x52, 0x60, 0xad],
-];
-
-#[test]
-fn test_siphash_1_3_test_vector() {
- let k0 = 0x_07_06_05_04_03_02_01_00;
- let k1 = 0x_0f_0e_0d_0c_0b_0a_09_08;
-
- let mut input: Vec = Vec::new();
-
- for i in 0..64 {
- let out = hash_with(SipHasher128::new_with_keys(k0, k1), &Bytes(&input[..]));
- let expected = (
- ((TEST_VECTOR[i][0] as u64) << 0)
- | ((TEST_VECTOR[i][1] as u64) << 8)
- | ((TEST_VECTOR[i][2] as u64) << 16)
- | ((TEST_VECTOR[i][3] as u64) << 24)
- | ((TEST_VECTOR[i][4] as u64) << 32)
- | ((TEST_VECTOR[i][5] as u64) << 40)
- | ((TEST_VECTOR[i][6] as u64) << 48)
- | ((TEST_VECTOR[i][7] as u64) << 56),
- ((TEST_VECTOR[i][8] as u64) << 0)
- | ((TEST_VECTOR[i][9] as u64) << 8)
- | ((TEST_VECTOR[i][10] as u64) << 16)
- | ((TEST_VECTOR[i][11] as u64) << 24)
- | ((TEST_VECTOR[i][12] as u64) << 32)
- | ((TEST_VECTOR[i][13] as u64) << 40)
- | ((TEST_VECTOR[i][14] as u64) << 48)
- | ((TEST_VECTOR[i][15] as u64) << 56),
- );
-
- assert_eq!(out, expected);
- input.push(i as u8);
- }
-}
-
-#[test]
-#[cfg(target_arch = "arm")]
-fn test_hash_usize() {
- let val = 0xdeadbeef_deadbeef_u64;
- assert!(hash(&(val as u64)) != hash(&(val as usize)));
- assert_eq!(hash(&(val as u32)), hash(&(val as usize)));
-}
-#[test]
-#[cfg(target_arch = "x86_64")]
-fn test_hash_usize() {
- let val = 0xdeadbeef_deadbeef_u64;
- assert_eq!(hash(&(val as u64)), hash(&(val as usize)));
- assert!(hash(&(val as u32)) != hash(&(val as usize)));
-}
-#[test]
-#[cfg(target_arch = "x86")]
-fn test_hash_usize() {
- let val = 0xdeadbeef_deadbeef_u64;
- assert!(hash(&(val as u64)) != hash(&(val as usize)));
- assert_eq!(hash(&(val as u32)), hash(&(val as usize)));
-}
-
-#[test]
-fn test_hash_idempotent() {
- let val64 = 0xdeadbeef_deadbeef_u64;
- assert_eq!(hash(&val64), hash(&val64));
- let val32 = 0xdeadbeef_u32;
- assert_eq!(hash(&val32), hash(&val32));
-}
-
-#[test]
-fn test_hash_no_bytes_dropped_64() {
- let val = 0xdeadbeef_deadbeef_u64;
-
- assert!(hash(&val) != hash(&zero_byte(val, 0)));
- assert!(hash(&val) != hash(&zero_byte(val, 1)));
- assert!(hash(&val) != hash(&zero_byte(val, 2)));
- assert!(hash(&val) != hash(&zero_byte(val, 3)));
- assert!(hash(&val) != hash(&zero_byte(val, 4)));
- assert!(hash(&val) != hash(&zero_byte(val, 5)));
- assert!(hash(&val) != hash(&zero_byte(val, 6)));
- assert!(hash(&val) != hash(&zero_byte(val, 7)));
-
- fn zero_byte(val: u64, byte: usize) -> u64 {
- assert!(byte < 8);
- val & !(0xff << (byte * 8))
- }
-}
-
-#[test]
-fn test_hash_no_bytes_dropped_32() {
- let val = 0xdeadbeef_u32;
-
- assert!(hash(&val) != hash(&zero_byte(val, 0)));
- assert!(hash(&val) != hash(&zero_byte(val, 1)));
- assert!(hash(&val) != hash(&zero_byte(val, 2)));
- assert!(hash(&val) != hash(&zero_byte(val, 3)));
-
- fn zero_byte(val: u32, byte: usize) -> u32 {
- assert!(byte < 4);
- val & !(0xff << (byte * 8))
- }
-}
-
-#[test]
-fn test_hash_no_concat_alias() {
- let s = ("aa", "bb");
- let t = ("aabb", "");
- let u = ("a", "abb");
-
- assert!(s != t && t != u);
- assert!(hash(&s) != hash(&t) && hash(&s) != hash(&u));
-
- let u = [1, 0, 0, 0];
- let v = (&u[..1], &u[1..3], &u[3..]);
- let w = (&u[..], &u[4..4], &u[4..4]);
-
- assert!(v != w);
- assert!(hash(&v) != hash(&w));
-}
-
-#[test]
-fn test_short_write_works() {
- let test_u8 = 0xFF_u8;
- let test_u16 = 0x1122_u16;
- let test_u32 = 0x22334455_u32;
- let test_u64 = 0x33445566_778899AA_u64;
- let test_u128 = 0x11223344_55667788_99AABBCC_DDEEFF77_u128;
- let test_usize = 0xD0C0B0A0_usize;
-
- let test_i8 = -1_i8;
- let test_i16 = -2_i16;
- let test_i32 = -3_i32;
- let test_i64 = -4_i64;
- let test_i128 = -5_i128;
- let test_isize = -6_isize;
-
- let mut h1 = SipHasher128::new_with_keys(0, 0);
- h1.write(b"bytes");
- h1.write(b"string");
- h1.write_u8(test_u8);
- h1.write_u16(test_u16);
- h1.write_u32(test_u32);
- h1.write_u64(test_u64);
- h1.write_u128(test_u128);
- h1.write_usize(test_usize);
- h1.write_i8(test_i8);
- h1.write_i16(test_i16);
- h1.write_i32(test_i32);
- h1.write_i64(test_i64);
- h1.write_i128(test_i128);
- h1.write_isize(test_isize);
-
- let mut h2 = SipHasher128::new_with_keys(0, 0);
- h2.write(b"bytes");
- h2.write(b"string");
- h2.write(&test_u8.to_ne_bytes());
- h2.write(&test_u16.to_ne_bytes());
- h2.write(&test_u32.to_ne_bytes());
- h2.write(&test_u64.to_ne_bytes());
- h2.write(&test_u128.to_ne_bytes());
- h2.write(&test_usize.to_ne_bytes());
- h2.write(&test_i8.to_ne_bytes());
- h2.write(&test_i16.to_ne_bytes());
- h2.write(&test_i32.to_ne_bytes());
- h2.write(&test_i64.to_ne_bytes());
- h2.write(&test_i128.to_ne_bytes());
- h2.write(&test_isize.to_ne_bytes());
-
- let h1_hash = h1.finish128();
- let h2_hash = h2.finish128();
-
- assert_eq!(h1_hash, h2_hash);
-}
-
-macro_rules! test_fill_buffer {
- ($type:ty, $write_method:ident) => {{
- // Test filling and overfilling the buffer from all possible offsets
- // for a given integer type and its corresponding write method.
- const SIZE: usize = std::mem::size_of::<$type>();
- let input = [42; BUFFER_SIZE];
- let x = 0x01234567_89ABCDEF_76543210_FEDCBA98_u128 as $type;
- let x_bytes = &x.to_ne_bytes();
-
- for i in 1..=SIZE {
- let s = &input[..BUFFER_SIZE - i];
-
- let mut h1 = SipHasher128::new_with_keys(7, 13);
- h1.write(s);
- h1.$write_method(x);
-
- let mut h2 = SipHasher128::new_with_keys(7, 13);
- h2.write(s);
- h2.write(x_bytes);
-
- let h1_hash = h1.finish128();
- let h2_hash = h2.finish128();
-
- assert_eq!(h1_hash, h2_hash);
- }
- }};
-}
-
-#[test]
-fn test_fill_buffer() {
- test_fill_buffer!(u8, write_u8);
- test_fill_buffer!(u16, write_u16);
- test_fill_buffer!(u32, write_u32);
- test_fill_buffer!(u64, write_u64);
- test_fill_buffer!(u128, write_u128);
- test_fill_buffer!(usize, write_usize);
-
- test_fill_buffer!(i8, write_i8);
- test_fill_buffer!(i16, write_i16);
- test_fill_buffer!(i32, write_i32);
- test_fill_buffer!(i64, write_i64);
- test_fill_buffer!(i128, write_i128);
- test_fill_buffer!(isize, write_isize);
-}
diff --git a/compiler/rustc_data_structures/src/stable_hasher.rs b/compiler/rustc_data_structures/src/stable_hasher.rs
index a57f5067dd8d..83883eeba9ca 100644
--- a/compiler/rustc_data_structures/src/stable_hasher.rs
+++ b/compiler/rustc_data_structures/src/stable_hasher.rs
@@ -1,8 +1,6 @@
-use crate::sip128::SipHasher128;
use rustc_index::bit_set::{self, BitSet};
use rustc_index::{Idx, IndexSlice, IndexVec};
use smallvec::SmallVec;
-use std::fmt;
use std::hash::{BuildHasher, Hash, Hasher};
use std::marker::PhantomData;
use std::mem;
@@ -13,163 +11,9 @@ mod tests;
pub use crate::hashes::{Hash128, Hash64};
-/// When hashing something that ends up affecting properties like symbol names,
-/// we want these symbol names to be calculated independently of other factors
-/// like what architecture you're compiling *from*.
-///
-/// To that end we always convert integers to little-endian format before
-/// hashing and the architecture dependent `isize` and `usize` types are
-/// extended to 64 bits if needed.
-pub struct StableHasher {
- state: SipHasher128,
-}
-
-impl fmt::Debug for StableHasher {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(f, "{:?}", self.state)
- }
-}
-
-pub trait StableHasherResult: Sized {
- fn finish(hasher: StableHasher) -> Self;
-}
-
-impl StableHasher {
- #[inline]
- pub fn new() -> Self {
- StableHasher { state: SipHasher128::new_with_keys(0, 0) }
- }
-
- #[inline]
- pub fn finish(self) -> W {
- W::finish(self)
- }
-}
-
-impl StableHasher {
- #[inline]
- pub fn finalize(self) -> (u64, u64) {
- self.state.finish128()
- }
-}
-
-impl Hasher for StableHasher {
- fn finish(&self) -> u64 {
- panic!("use StableHasher::finalize instead");
- }
-
- #[inline]
- fn write(&mut self, bytes: &[u8]) {
- self.state.write(bytes);
- }
-
- #[inline]
- fn write_str(&mut self, s: &str) {
- self.state.write_str(s);
- }
-
- #[inline]
- fn write_length_prefix(&mut self, len: usize) {
- // Our impl for `usize` will extend it if needed.
- self.write_usize(len);
- }
-
- #[inline]
- fn write_u8(&mut self, i: u8) {
- self.state.write_u8(i);
- }
-
- #[inline]
- fn write_u16(&mut self, i: u16) {
- self.state.short_write(i.to_le_bytes());
- }
-
- #[inline]
- fn write_u32(&mut self, i: u32) {
- self.state.short_write(i.to_le_bytes());
- }
-
- #[inline]
- fn write_u64(&mut self, i: u64) {
- self.state.short_write(i.to_le_bytes());
- }
-
- #[inline]
- fn write_u128(&mut self, i: u128) {
- self.write_u64(i as u64);
- self.write_u64((i >> 64) as u64);
- }
-
- #[inline]
- fn write_usize(&mut self, i: usize) {
- // Always treat usize as u64 so we get the same results on 32 and 64 bit
- // platforms. This is important for symbol hashes when cross compiling,
- // for example.
- self.state.short_write((i as u64).to_le_bytes());
- }
-
- #[inline]
- fn write_i8(&mut self, i: i8) {
- self.state.write_i8(i);
- }
-
- #[inline]
- fn write_i16(&mut self, i: i16) {
- self.state.short_write((i as u16).to_le_bytes());
- }
-
- #[inline]
- fn write_i32(&mut self, i: i32) {
- self.state.short_write((i as u32).to_le_bytes());
- }
-
- #[inline]
- fn write_i64(&mut self, i: i64) {
- self.state.short_write((i as u64).to_le_bytes());
- }
-
- #[inline]
- fn write_i128(&mut self, i: i128) {
- self.state.write(&(i as u128).to_le_bytes());
- }
-
- #[inline]
- fn write_isize(&mut self, i: isize) {
- // Always treat isize as a 64-bit number so we get the same results on 32 and 64 bit
- // platforms. This is important for symbol hashes when cross compiling,
- // for example. Sign extending here is preferable as it means that the
- // same negative number hashes the same on both 32 and 64 bit platforms.
- let value = i as u64;
-
- // Cold path
- #[cold]
- #[inline(never)]
- fn hash_value(state: &mut SipHasher128, value: u64) {
- state.write_u8(0xFF);
- state.short_write(value.to_le_bytes());
- }
-
- // `isize` values often seem to have a small (positive) numeric value in practice.
- // To exploit this, if the value is small, we will hash a smaller amount of bytes.
- // However, we cannot just skip the leading zero bytes, as that would produce the same hash
- // e.g. if you hash two values that have the same bit pattern when they are swapped.
- // See https://github.com/rust-lang/rust/pull/93014 for context.
- //
- // Therefore, we employ the following strategy:
- // 1) When we encounter a value that fits within a single byte (the most common case), we
- // hash just that byte. This is the most common case that is being optimized. However, we do
- // not do this for the value 0xFF, as that is a reserved prefix (a bit like in UTF-8).
- // 2) When we encounter a larger value, we hash a "marker" 0xFF and then the corresponding
- // 8 bytes. Since this prefix cannot occur when we hash a single byte, when we hash two
- // `isize`s that fit within a different amount of bytes, they should always produce a different
- // byte stream for the hasher.
- if value < 0xFF {
- self.state.write_u8(value as u8);
- } else {
- hash_value(&mut self.state, value);
- }
- }
-}
+pub use rustc_stable_hash::FromStableHash;
+pub use rustc_stable_hash::SipHasher128Hash as StableHasherHash;
+pub use rustc_stable_hash::StableSipHasher128 as StableHasher;
/// Something that implements `HashStable` can be hashed in a way that is
/// stable across multiple compilation sessions.
diff --git a/compiler/rustc_data_structures/src/stable_hasher/tests.rs b/compiler/rustc_data_structures/src/stable_hasher/tests.rs
index c8921f6a7784..aab50a13af0e 100644
--- a/compiler/rustc_data_structures/src/stable_hasher/tests.rs
+++ b/compiler/rustc_data_structures/src/stable_hasher/tests.rs
@@ -7,71 +7,6 @@ use super::*;
// ways). The expected values depend on the hashing algorithm used, so they
// need to be updated whenever StableHasher changes its hashing algorithm.
-#[test]
-fn test_hash_integers() {
- // Test that integers are handled consistently across platforms.
- let test_u8 = 0xAB_u8;
- let test_u16 = 0xFFEE_u16;
- let test_u32 = 0x445577AA_u32;
- let test_u64 = 0x01234567_13243546_u64;
- let test_u128 = 0x22114433_66557788_99AACCBB_EEDDFF77_u128;
- let test_usize = 0xD0C0B0A0_usize;
-
- let test_i8 = -100_i8;
- let test_i16 = -200_i16;
- let test_i32 = -300_i32;
- let test_i64 = -400_i64;
- let test_i128 = -500_i128;
- let test_isize = -600_isize;
-
- let mut h = StableHasher::new();
- test_u8.hash(&mut h);
- test_u16.hash(&mut h);
- test_u32.hash(&mut h);
- test_u64.hash(&mut h);
- test_u128.hash(&mut h);
- test_usize.hash(&mut h);
- test_i8.hash(&mut h);
- test_i16.hash(&mut h);
- test_i32.hash(&mut h);
- test_i64.hash(&mut h);
- test_i128.hash(&mut h);
- test_isize.hash(&mut h);
-
- // This depends on the hashing algorithm. See note at top of file.
- let expected = (13997337031081104755, 6178945012502239489);
-
- assert_eq!(h.finalize(), expected);
-}
-
-#[test]
-fn test_hash_usize() {
- // Test that usize specifically is handled consistently across platforms.
- let test_usize = 0xABCDEF01_usize;
-
- let mut h = StableHasher::new();
- test_usize.hash(&mut h);
-
- // This depends on the hashing algorithm. See note at top of file.
- let expected = (12037165114281468837, 3094087741167521712);
-
- assert_eq!(h.finalize(), expected);
-}
-
-#[test]
-fn test_hash_isize() {
- // Test that isize specifically is handled consistently across platforms.
- let test_isize = -7_isize;
-
- let mut h = StableHasher::new();
- test_isize.hash(&mut h);
-
- // This depends on the hashing algorithm. See note at top of file.
- let expected = (3979067582695659080, 2322428596355037273);
-
- assert_eq!(h.finalize(), expected);
-}
-
fn hash>(t: &T) -> Hash128 {
let mut h = StableHasher::new();
let ctx = &mut ();
diff --git a/compiler/rustc_data_structures/src/tagged_ptr/copy/tests.rs b/compiler/rustc_data_structures/src/tagged_ptr/copy/tests.rs
index bfcc2e603de4..160af8a65d9c 100644
--- a/compiler/rustc_data_structures/src/tagged_ptr/copy/tests.rs
+++ b/compiler/rustc_data_structures/src/tagged_ptr/copy/tests.rs
@@ -1,5 +1,6 @@
use std::ptr;
+use crate::hashes::Hash128;
use crate::stable_hasher::{HashStable, StableHasher};
use crate::tagged_ptr::{CopyTaggedPtr, Pointer, Tag, Tag2};
@@ -31,14 +32,13 @@ fn stable_hash_hashes_as_tuple() {
let hash_packed = {
let mut hasher = StableHasher::new();
tag_ptr(&12, Tag2::B11).hash_stable(&mut (), &mut hasher);
-
- hasher.finalize()
+ hasher.finish::()
};
let hash_tupled = {
let mut hasher = StableHasher::new();
(&12, Tag2::B11).hash_stable(&mut (), &mut hasher);
- hasher.finalize()
+ hasher.finish::()
};
assert_eq!(hash_packed, hash_tupled);
diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs
index 40e16b451157..9da4aa84db52 100644
--- a/compiler/rustc_expand/src/config.rs
+++ b/compiler/rustc_expand/src/config.rs
@@ -187,6 +187,7 @@ impl<'a> StripUnconfigured<'a> {
.iter()
.filter_map(|tree| match tree.clone() {
AttrTokenTree::AttrsTarget(mut target) => {
+ // Expand any `cfg_attr` attributes.
target.attrs.flat_map_in_place(|attr| self.process_cfg_attr(&attr));
if self.in_cfg(&target.attrs) {
@@ -195,6 +196,8 @@ impl<'a> StripUnconfigured<'a> {
);
Some(AttrTokenTree::AttrsTarget(target))
} else {
+ // Remove the target if there's a `cfg` attribute and
+ // the condition isn't satisfied.
None
}
}
@@ -253,9 +256,9 @@ impl<'a> StripUnconfigured<'a> {
/// Gives a compiler warning when the `cfg_attr` contains no attributes and
/// is in the original source file. Gives a compiler error if the syntax of
/// the attribute is incorrect.
- pub(crate) fn expand_cfg_attr(&self, attr: &Attribute, recursive: bool) -> Vec {
+ pub(crate) fn expand_cfg_attr(&self, cfg_attr: &Attribute, recursive: bool) -> Vec {
let Some((cfg_predicate, expanded_attrs)) =
- rustc_parse::parse_cfg_attr(attr, &self.sess.psess)
+ rustc_parse::parse_cfg_attr(cfg_attr, &self.sess.psess)
else {
return vec![];
};
@@ -264,7 +267,7 @@ impl<'a> StripUnconfigured<'a> {
if expanded_attrs.is_empty() {
self.sess.psess.buffer_lint(
rustc_lint_defs::builtin::UNUSED_ATTRIBUTES,
- attr.span,
+ cfg_attr.span,
ast::CRATE_NODE_ID,
BuiltinLintDiag::CfgAttrNoAttributes,
);
@@ -280,20 +283,21 @@ impl<'a> StripUnconfigured<'a> {
// `#[cfg_attr(false, cfg_attr(true, some_attr))]`.
expanded_attrs
.into_iter()
- .flat_map(|item| self.process_cfg_attr(&self.expand_cfg_attr_item(attr, item)))
+ .flat_map(|item| self.process_cfg_attr(&self.expand_cfg_attr_item(cfg_attr, item)))
.collect()
} else {
- expanded_attrs.into_iter().map(|item| self.expand_cfg_attr_item(attr, item)).collect()
+ expanded_attrs
+ .into_iter()
+ .map(|item| self.expand_cfg_attr_item(cfg_attr, item))
+ .collect()
}
}
fn expand_cfg_attr_item(
&self,
- attr: &Attribute,
+ cfg_attr: &Attribute,
(item, item_span): (ast::AttrItem, Span),
) -> Attribute {
- let orig_tokens = attr.get_tokens();
-
// We are taking an attribute of the form `#[cfg_attr(pred, attr)]`
// and producing an attribute of the form `#[attr]`. We
// have captured tokens for `attr` itself, but we need to
@@ -302,11 +306,11 @@ impl<'a> StripUnconfigured<'a> {
// Use the `#` in `#[cfg_attr(pred, attr)]` as the `#` token
// for `attr` when we expand it to `#[attr]`
- let mut orig_trees = orig_tokens.trees();
+ let mut orig_trees = cfg_attr.token_trees().into_iter();
let TokenTree::Token(pound_token @ Token { kind: TokenKind::Pound, .. }, _) =
orig_trees.next().unwrap().clone()
else {
- panic!("Bad tokens for attribute {attr:?}");
+ panic!("Bad tokens for attribute {cfg_attr:?}");
};
// We don't really have a good span to use for the synthesized `[]`
@@ -320,12 +324,12 @@ impl<'a> StripUnconfigured<'a> {
.unwrap_or_else(|| panic!("Missing tokens for {item:?}"))
.to_attr_token_stream(),
);
- let trees = if attr.style == AttrStyle::Inner {
+ let trees = if cfg_attr.style == AttrStyle::Inner {
// For inner attributes, we do the same thing for the `!` in `#![some_attr]`
let TokenTree::Token(bang_token @ Token { kind: TokenKind::Not, .. }, _) =
orig_trees.next().unwrap().clone()
else {
- panic!("Bad tokens for attribute {attr:?}");
+ panic!("Bad tokens for attribute {cfg_attr:?}");
};
vec![
AttrTokenTree::Token(pound_token, Spacing::Joint),
@@ -340,7 +344,7 @@ impl<'a> StripUnconfigured<'a> {
&self.sess.psess.attr_id_generator,
item,
tokens,
- attr.style,
+ cfg_attr.style,
item_span,
);
if attr.has_name(sym::crate_type) {
diff --git a/compiler/rustc_feature/src/accepted.rs b/compiler/rustc_feature/src/accepted.rs
index f082cc2b5699..e671c7682391 100644
--- a/compiler/rustc_feature/src/accepted.rs
+++ b/compiler/rustc_feature/src/accepted.rs
@@ -42,6 +42,10 @@ declare_features! (
// feature-group-start: accepted features
// -------------------------------------------------------------------------
+ // Note that the version indicates when it got *stabilized*.
+ // When moving an unstable feature here, set the version number to
+ // `CURRENT RUSTC VERSION` with ` ` replaced by `_`.
+
/// Allows `#[target_feature(...)]` on aarch64 platforms
(accepted, aarch64_target_feature, "1.61.0", Some(44839)),
/// Allows using the `efiapi` ABI.
diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs
index 910402d5499f..7b27049a579a 100644
--- a/compiler/rustc_feature/src/builtin_attrs.rs
+++ b/compiler/rustc_feature/src/builtin_attrs.rs
@@ -556,7 +556,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
),
// RFC 2632
gated!(
- const_trait, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::Yes, const_trait_impl,
+ const_trait, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No, const_trait_impl,
"`const_trait` is a temporary placeholder for marking a trait that is suitable for `const` \
`impls` and all default bodies as `const`, which may be removed or renamed in the \
future."
diff --git a/compiler/rustc_feature/src/lib.rs b/compiler/rustc_feature/src/lib.rs
index bf4293643183..e9d3ce0a0749 100644
--- a/compiler/rustc_feature/src/lib.rs
+++ b/compiler/rustc_feature/src/lib.rs
@@ -31,6 +31,10 @@ use std::num::NonZero;
#[derive(Debug, Clone)]
pub struct Feature {
pub name: Symbol,
+ /// For unstable features: the version the feature was added in.
+ /// For accepted features: the version the feature got stabilized in.
+ /// For removed features we are inconsistent; sometimes this is the
+ /// version it got added, sometimes the version it got removed.
pub since: &'static str,
issue: Option>,
}
diff --git a/compiler/rustc_feature/src/removed.rs b/compiler/rustc_feature/src/removed.rs
index aea447b2aff1..80a108d2fc87 100644
--- a/compiler/rustc_feature/src/removed.rs
+++ b/compiler/rustc_feature/src/removed.rs
@@ -32,6 +32,12 @@ declare_features! (
// feature-group-start: removed features
// -------------------------------------------------------------------------
+ // Note that the version indicates when it got *removed*.
+ // When moving an unstable feature here, set the version number to
+ // `CURRENT RUSTC VERSION` with ` ` replaced by `_`.
+ // (But not all features below do this properly; many indicate the
+ // version they got originally added in.)
+
/// Allows using the `amdgpu-kernel` ABI.
(removed, abi_amdgpu_kernel, "1.77.0", Some(51575), None),
(removed, advanced_slice_patterns, "1.0.0", Some(62254),
@@ -215,6 +221,9 @@ declare_features! (
/// Permits specifying whether a function should permit unwinding or abort on unwind.
(removed, unwind_attributes, "1.56.0", Some(58760), Some("use the C-unwind ABI instead")),
(removed, visible_private_types, "1.0.0", None, None),
+ /// Allows `extern "wasm" fn`
+ (removed, wasm_abi, "CURRENT_RUSTC_VERSION", Some(83788),
+ Some("non-standard wasm ABI is no longer supported")),
// !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!!
// Features are listed in alphabetical order. Tidy will fail if you don't keep it this way.
// !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!!
diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs
index c05cac155b74..1db3774222a0 100644
--- a/compiler/rustc_feature/src/unstable.rs
+++ b/compiler/rustc_feature/src/unstable.rs
@@ -248,6 +248,8 @@ declare_features! (
(unstable, auto_traits, "1.50.0", Some(13231)),
/// Allows using `box` in patterns (RFC 469).
(unstable, box_patterns, "1.0.0", Some(29641)),
+ /// Allows builtin # foo() syntax
+ (internal, builtin_syntax, "1.71.0", Some(110680)),
/// Allows `#[doc(notable_trait)]`.
/// Renamed from `doc_spotlight`.
(unstable, doc_notable_trait, "1.52.0", Some(45040)),
@@ -361,8 +363,6 @@ declare_features! (
(unstable, async_fn_track_caller, "1.73.0", Some(110011)),
/// Allows `for await` loops.
(unstable, async_for_loop, "1.77.0", Some(118898)),
- /// Allows builtin # foo() syntax
- (unstable, builtin_syntax, "1.71.0", Some(110680)),
/// Allows using C-variadics.
(unstable, c_variadic, "1.34.0", Some(44930)),
/// Allows the use of `#[cfg(overflow_checks)` to check if integer overflow behaviour.
@@ -621,8 +621,6 @@ declare_features! (
(unstable, try_blocks, "1.29.0", Some(31436)),
/// Allows `impl Trait` to be used inside type aliases (RFC 2515).
(unstable, type_alias_impl_trait, "1.38.0", Some(63063)),
- /// Allows the use of type ascription in expressions.
- (unstable, type_ascription, "1.6.0", Some(23416)),
/// Allows creation of instances of a struct by moving fields that have
/// not changed from prior instances of the same struct (RFC #2528)
(unstable, type_changing_struct_update, "1.58.0", Some(86555)),
@@ -640,8 +638,10 @@ declare_features! (
(unstable, unsized_tuple_coercion, "1.20.0", Some(42877)),
/// Allows using the `#[used(linker)]` (or `#[used(compiler)]`) attribute.
(unstable, used_with_arg, "1.60.0", Some(93798)),
- /// Allows `extern "wasm" fn`
- (unstable, wasm_abi, "1.53.0", Some(83788)),
+ /// Allows use of x86 `AMX` target-feature attributes and intrinsics
+ (unstable, x86_amx_intrinsics, "CURRENT_RUSTC_VERSION", Some(126622)),
+ /// Allows use of the `xop` target-feature
+ (unstable, xop_target_feature, "CURRENT_RUSTC_VERSION", Some(127208)),
/// Allows `do yeet` expressions
(unstable, yeet_expr, "1.62.0", Some(96373)),
// !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!!
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index d57fad6ba4c2..3bd7b300758c 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -2708,6 +2708,13 @@ impl PreciseCapturingArg<'_> {
PreciseCapturingArg::Param(param) => param.hir_id,
}
}
+
+ pub fn name(self) -> Symbol {
+ match self {
+ PreciseCapturingArg::Lifetime(lt) => lt.ident.name,
+ PreciseCapturingArg::Param(param) => param.ident.name,
+ }
+ }
}
/// We need to have a [`Node`] for the [`HirId`] that we attach the type/const param
@@ -3211,10 +3218,10 @@ impl<'hir> Item<'hir> {
ItemKind::Static(ty, mutbl, body), (ty, *mutbl, *body);
expect_const, (&'hir Ty<'hir>, &'hir Generics<'hir>, BodyId),
- ItemKind::Const(ty, gen, body), (ty, gen, *body);
+ ItemKind::Const(ty, generics, body), (ty, generics, *body);
expect_fn, (&FnSig<'hir>, &'hir Generics<'hir>, BodyId),
- ItemKind::Fn(sig, gen, body), (sig, gen, *body);
+ ItemKind::Fn(sig, generics, body), (sig, generics, *body);
expect_macro, (&ast::MacroDef, MacroKind), ItemKind::Macro(def, mk), (def, *mk);
@@ -3226,25 +3233,25 @@ impl<'hir> Item<'hir> {
expect_global_asm, &'hir InlineAsm<'hir>, ItemKind::GlobalAsm(asm), asm;
expect_ty_alias, (&'hir Ty<'hir>, &'hir Generics<'hir>),
- ItemKind::TyAlias(ty, gen), (ty, gen);
+ ItemKind::TyAlias(ty, generics), (ty, generics);
expect_opaque_ty, &OpaqueTy<'hir>, ItemKind::OpaqueTy(ty), ty;
- expect_enum, (&EnumDef<'hir>, &'hir Generics<'hir>), ItemKind::Enum(def, gen), (def, gen);
+ expect_enum, (&EnumDef<'hir>, &'hir Generics<'hir>), ItemKind::Enum(def, generics), (def, generics);
expect_struct, (&VariantData<'hir>, &'hir Generics<'hir>),
- ItemKind::Struct(data, gen), (data, gen);
+ ItemKind::Struct(data, generics), (data, generics);
expect_union, (&VariantData<'hir>, &'hir Generics<'hir>),
- ItemKind::Union(data, gen), (data, gen);
+ ItemKind::Union(data, generics), (data, generics);
expect_trait,
(IsAuto, Safety, &'hir Generics<'hir>, GenericBounds<'hir>, &'hir [TraitItemRef]),
- ItemKind::Trait(is_auto, safety, gen, bounds, items),
- (*is_auto, *safety, gen, bounds, items);
+ ItemKind::Trait(is_auto, safety, generics, bounds, items),
+ (*is_auto, *safety, generics, bounds, items);
expect_trait_alias, (&'hir Generics<'hir>, GenericBounds<'hir>),
- ItemKind::TraitAlias(gen, bounds), (gen, bounds);
+ ItemKind::TraitAlias(generics, bounds), (generics, bounds);
expect_impl, &'hir Impl<'hir>, ItemKind::Impl(imp), imp;
}
diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl
index 91b1506d1e4e..24c5377a3b12 100644
--- a/compiler/rustc_hir_analysis/messages.ftl
+++ b/compiler/rustc_hir_analysis/messages.ftl
@@ -373,10 +373,6 @@ hir_analysis_paren_sugar_attribute = the `#[rustc_paren_sugar]` attribute is a t
hir_analysis_parenthesized_fn_trait_expansion =
parenthesized trait syntax expands to `{$expanded_type}`
-hir_analysis_pass_to_variadic_function = can't pass `{$ty}` to variadic function
- .suggestion = cast the value to `{$cast_ty}`
- .help = cast the value to `{$cast_ty}`
-
hir_analysis_pattern_type_non_const_range = range patterns must have constant range start and end
hir_analysis_pattern_type_wild_pat = wildcard patterns are not permitted for pattern types
.label = this type is the same as the inner type without a pattern
diff --git a/compiler/rustc_hir_analysis/src/bounds.rs b/compiler/rustc_hir_analysis/src/bounds.rs
index c7ee89e73c27..c30a6f1eeb91 100644
--- a/compiler/rustc_hir_analysis/src/bounds.rs
+++ b/compiler/rustc_hir_analysis/src/bounds.rs
@@ -7,7 +7,7 @@ use rustc_hir::LangItem;
use rustc_middle::ty::fold::FnMutDelegate;
use rustc_middle::ty::{self, Ty, TyCtxt, Upcast};
use rustc_span::def_id::DefId;
-use rustc_span::{sym, Span};
+use rustc_span::Span;
/// Collects together a list of type bounds. These lists of bounds occur in many places
/// in Rust's syntax:
@@ -80,7 +80,7 @@ impl<'tcx> Bounds<'tcx> {
}
(_, ty::BoundConstness::NotConst) => {
- if !tcx.has_attr(bound_trait_ref.def_id(), sym::const_trait) {
+ if !tcx.is_const_trait(bound_trait_ref.def_id()) {
return;
}
tcx.consts.true_
diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs
index f93777eda529..bf8ef18c04fc 100644
--- a/compiler/rustc_hir_analysis/src/check/check.rs
+++ b/compiler/rustc_hir_analysis/src/check/check.rs
@@ -25,9 +25,9 @@ use rustc_middle::ty::{
};
use rustc_session::lint::builtin::{UNINHABITED_STATIC, UNSUPPORTED_CALLING_CONVENTIONS};
use rustc_target::abi::FieldIdx;
+use rustc_trait_selection::error_reporting::traits::on_unimplemented::OnUnimplementedDirective;
+use rustc_trait_selection::error_reporting::traits::TypeErrCtxtExt as _;
use rustc_trait_selection::traits;
-use rustc_trait_selection::traits::error_reporting::on_unimplemented::OnUnimplementedDirective;
-use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _;
use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _;
use rustc_type_ir::fold::TypeFoldable;
diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
index 8a1ee9374c3d..6c53625b590b 100644
--- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
+++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
@@ -21,9 +21,9 @@ use rustc_middle::ty::{
use rustc_middle::ty::{GenericParamDefKind, TyCtxt};
use rustc_middle::{bug, span_bug};
use rustc_span::Span;
+use rustc_trait_selection::error_reporting::traits::TypeErrCtxtExt;
use rustc_trait_selection::infer::InferCtxtExt;
use rustc_trait_selection::regions::InferCtxtRegionExt;
-use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _;
use rustc_trait_selection::traits::{
self, FulfillmentError, ObligationCause, ObligationCauseCode, ObligationCtxt, Reveal,
diff --git a/compiler/rustc_hir_analysis/src/check/entry.rs b/compiler/rustc_hir_analysis/src/check/entry.rs
index cc52a7658020..ce921f64481a 100644
--- a/compiler/rustc_hir_analysis/src/check/entry.rs
+++ b/compiler/rustc_hir_analysis/src/check/entry.rs
@@ -7,7 +7,7 @@ use rustc_session::config::EntryFnType;
use rustc_span::def_id::{DefId, LocalDefId, CRATE_DEF_ID};
use rustc_span::{symbol::sym, Span};
use rustc_target::spec::abi::Abi;
-use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _;
+use rustc_trait_selection::error_reporting::traits::TypeErrCtxtExt as _;
use rustc_trait_selection::traits::{self, ObligationCause, ObligationCauseCode};
use std::ops::Not;
diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs
index 8469cbbbc7d6..6a36938dd1d5 100644
--- a/compiler/rustc_hir_analysis/src/check/mod.rs
+++ b/compiler/rustc_hir_analysis/src/check/mod.rs
@@ -96,10 +96,10 @@ use rustc_span::symbol::{kw, sym, Ident};
use rustc_span::{def_id::CRATE_DEF_ID, BytePos, Span, Symbol, DUMMY_SP};
use rustc_target::abi::VariantIdx;
use rustc_target::spec::abi::Abi;
-use rustc_trait_selection::traits::error_reporting::suggestions::{
+use rustc_trait_selection::error_reporting::traits::suggestions::{
ReturnsVisitor, TypeErrCtxtExt as _,
};
-use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _;
+use rustc_trait_selection::error_reporting::traits::TypeErrCtxtExt as _;
use rustc_trait_selection::traits::ObligationCtxt;
use crate::errors;
diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
index a0eede31eb07..b2ef07d65c5b 100644
--- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs
+++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
@@ -29,8 +29,8 @@ use rustc_session::parse::feature_err;
use rustc_span::symbol::{sym, Ident};
use rustc_span::{Span, DUMMY_SP};
use rustc_target::spec::abi::Abi;
+use rustc_trait_selection::error_reporting::traits::TypeErrCtxtExt;
use rustc_trait_selection::regions::InferCtxtRegionExt;
-use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
use rustc_trait_selection::traits::misc::{
type_allowed_to_implement_const_param_ty, ConstParamTyImplementationError,
};
diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs
index 61adb7a3cbae..2ecb170ec895 100644
--- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs
+++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs
@@ -17,7 +17,7 @@ use rustc_middle::ty::adjustment::CoerceUnsizedInfo;
use rustc_middle::ty::print::PrintTraitRefExt as _;
use rustc_middle::ty::{self, suggest_constraining_type_params, Ty, TyCtxt, TypeVisitableExt};
use rustc_span::{Span, DUMMY_SP};
-use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
+use rustc_trait_selection::error_reporting::traits::TypeErrCtxtExt;
use rustc_trait_selection::traits::misc::{
type_allowed_to_implement_const_param_ty, type_allowed_to_implement_copy,
ConstParamTyImplementationError, CopyImplementationError, InfringingFieldsReason,
diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs
index 843e4d41e001..e0aad2991632 100644
--- a/compiler/rustc_hir_analysis/src/collect.rs
+++ b/compiler/rustc_hir_analysis/src/collect.rs
@@ -18,7 +18,9 @@ use rustc_ast::Recovered;
use rustc_data_structures::captures::Captures;
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
use rustc_data_structures::unord::UnordMap;
-use rustc_errors::{struct_span_code_err, Applicability, Diag, ErrorGuaranteed, StashKey, E0228};
+use rustc_errors::{
+ struct_span_code_err, Applicability, Diag, DiagCtxtHandle, ErrorGuaranteed, StashKey, E0228,
+};
use rustc_hir::def::DefKind;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::intravisit::{self, walk_generics, Visitor};
@@ -33,10 +35,9 @@ use rustc_middle::ty::{self, AdtKind, Const, IsSuggestable, Ty, TyCtxt, Upcast};
use rustc_middle::{bug, span_bug};
use rustc_span::symbol::{kw, sym, Ident, Symbol};
use rustc_span::{Span, DUMMY_SP};
-use rustc_target::abi::FieldIdx;
use rustc_target::spec::abi;
+use rustc_trait_selection::error_reporting::traits::suggestions::NextTypeParamName;
use rustc_trait_selection::infer::InferCtxtExt;
-use rustc_trait_selection::traits::error_reporting::suggestions::NextTypeParamName;
use rustc_trait_selection::traits::ObligationCtxt;
use std::cell::Cell;
use std::iter;
@@ -83,7 +84,6 @@ pub fn provide(providers: &mut Providers) {
coroutine_kind,
coroutine_for_closure,
is_type_alias_impl_trait,
- find_field,
..*providers
};
}
@@ -161,7 +161,7 @@ pub struct CollectItemTypesVisitor<'tcx> {
/// and suggest adding type parameters in the appropriate place, taking into consideration any and
/// all already existing generic type parameters to avoid suggesting a name that is already in use.
pub(crate) fn placeholder_type_error<'tcx>(
- tcx: TyCtxt<'tcx>,
+ cx: &dyn HirTyLowerer<'tcx>,
generics: Option<&hir::Generics<'_>>,
placeholder_types: Vec,
suggest: bool,
@@ -172,21 +172,21 @@ pub(crate) fn placeholder_type_error<'tcx>(
return;
}
- placeholder_type_error_diag(tcx, generics, placeholder_types, vec![], suggest, hir_ty, kind)
+ placeholder_type_error_diag(cx, generics, placeholder_types, vec![], suggest, hir_ty, kind)
.emit();
}
-pub(crate) fn placeholder_type_error_diag<'tcx>(
- tcx: TyCtxt<'tcx>,
+pub(crate) fn placeholder_type_error_diag<'cx, 'tcx>(
+ cx: &'cx dyn HirTyLowerer<'tcx>,
generics: Option<&hir::Generics<'_>>,
placeholder_types: Vec,
additional_spans: Vec,
suggest: bool,
hir_ty: Option<&hir::Ty<'_>>,
kind: &'static str,
-) -> Diag<'tcx> {
+) -> Diag<'cx> {
if placeholder_types.is_empty() {
- return bad_placeholder(tcx, additional_spans, kind);
+ return bad_placeholder(cx, additional_spans, kind);
}
let params = generics.map(|g| g.params).unwrap_or_default();
@@ -210,7 +210,7 @@ pub(crate) fn placeholder_type_error_diag<'tcx>(
}
let mut err =
- bad_placeholder(tcx, placeholder_types.into_iter().chain(additional_spans).collect(), kind);
+ bad_placeholder(cx, placeholder_types.into_iter().chain(additional_spans).collect(), kind);
// Suggest, but only if it is not a function in const or static
if suggest {
@@ -224,7 +224,7 @@ pub(crate) fn placeholder_type_error_diag<'tcx>(
// Check if parent is const or static
is_const_or_static = matches!(
- tcx.parent_hir_node(hir_ty.hir_id),
+ cx.tcx().parent_hir_node(hir_ty.hir_id),
Node::Item(&hir::Item {
kind: hir::ItemKind::Const(..) | hir::ItemKind::Static(..),
..
@@ -267,7 +267,16 @@ fn reject_placeholder_type_signatures_in_item<'tcx>(
let mut visitor = HirPlaceholderCollector::default();
visitor.visit_item(item);
- placeholder_type_error(tcx, Some(generics), visitor.0, suggest, None, item.kind.descr());
+ let icx = ItemCtxt::new(tcx, item.owner_id.def_id);
+
+ placeholder_type_error(
+ icx.lowerer(),
+ Some(generics),
+ visitor.0,
+ suggest,
+ None,
+ item.kind.descr(),
+ );
}
impl<'tcx> Visitor<'tcx> for CollectItemTypesVisitor<'tcx> {
@@ -329,15 +338,15 @@ impl<'tcx> Visitor<'tcx> for CollectItemTypesVisitor<'tcx> {
///////////////////////////////////////////////////////////////////////////
// Utility types and common code for the above passes.
-fn bad_placeholder<'tcx>(
- tcx: TyCtxt<'tcx>,
+fn bad_placeholder<'cx, 'tcx>(
+ cx: &'cx dyn HirTyLowerer<'tcx>,
mut spans: Vec,
kind: &'static str,
-) -> Diag<'tcx> {
+) -> Diag<'cx> {
let kind = if kind.ends_with('s') { format!("{kind}es") } else { format!("{kind}s") };
spans.sort();
- tcx.dcx().create_err(errors::PlaceholderNotAllowedItemSignatures { spans, kind })
+ cx.dcx().create_err(errors::PlaceholderNotAllowedItemSignatures { spans, kind })
}
impl<'tcx> ItemCtxt<'tcx> {
@@ -370,6 +379,10 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> {
self.tcx
}
+ fn dcx(&self) -> DiagCtxtHandle<'_> {
+ self.tcx.dcx().taintable_handle(&self.tainted_by_errors)
+ }
+
fn item_def_id(&self) -> LocalDefId {
self.item_def_id
}
@@ -377,14 +390,13 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> {
fn re_infer(&self, span: Span, reason: RegionInferReason<'_>) -> ty::Region<'tcx> {
if let RegionInferReason::BorrowedObjectLifetimeDefault = reason {
let e = struct_span_code_err!(
- self.tcx().dcx(),
+ self.dcx(),
span,
E0228,
"the lifetime bound for this object type cannot be deduced \
from context; please supply an explicit bound"
)
.emit();
- self.set_tainted_by_errors(e);
ty::Region::new_error(self.tcx(), e)
} else {
// This indicates an illegal lifetime in a non-assoc-trait position
@@ -509,10 +521,6 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> {
None
}
- fn set_tainted_by_errors(&self, err: ErrorGuaranteed) {
- self.tainted_by_errors.set(Some(err));
- }
-
fn lower_fn_sig(
&self,
decl: &hir::FnDecl<'tcx>,
@@ -570,7 +578,7 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> {
// `ident_span` to not emit an error twice when we have `fn foo(_: fn() -> _)`.
let mut diag = crate::collect::placeholder_type_error_diag(
- tcx,
+ self,
generics,
visitor.0,
infer_replacements.iter().map(|(s, _)| *s).collect(),
@@ -590,7 +598,7 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> {
);
}
- self.set_tainted_by_errors(diag.emit());
+ diag.emit();
}
(input_tys, output_ty)
@@ -639,6 +647,7 @@ fn lower_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) {
let it = tcx.hir().item(item_id);
debug!(item = %it.ident, id = %it.hir_id());
let def_id = item_id.owner_id.def_id;
+ let icx = ItemCtxt::new(tcx, def_id);
match &it.kind {
// These don't define types.
@@ -663,7 +672,7 @@ fn lower_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) {
let mut visitor = HirPlaceholderCollector::default();
visitor.visit_foreign_item(item);
placeholder_type_error(
- tcx,
+ icx.lowerer(),
None,
visitor.0,
false,
@@ -742,7 +751,14 @@ fn lower_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) {
if !ty.is_suggestable_infer_ty() {
let mut visitor = HirPlaceholderCollector::default();
visitor.visit_item(it);
- placeholder_type_error(tcx, None, visitor.0, false, None, it.kind.descr());
+ placeholder_type_error(
+ icx.lowerer(),
+ None,
+ visitor.0,
+ false,
+ None,
+ it.kind.descr(),
+ );
}
}
@@ -760,6 +776,7 @@ fn lower_trait_item(tcx: TyCtxt<'_>, trait_item_id: hir::TraitItemId) {
let trait_item = tcx.hir().trait_item(trait_item_id);
let def_id = trait_item_id.owner_id;
tcx.ensure().generics_of(def_id);
+ let icx = ItemCtxt::new(tcx, def_id.def_id);
match trait_item.kind {
hir::TraitItemKind::Fn(..) => {
@@ -776,7 +793,14 @@ fn lower_trait_item(tcx: TyCtxt<'_>, trait_item_id: hir::TraitItemId) {
// Account for `const C: _;`.
let mut visitor = HirPlaceholderCollector::default();
visitor.visit_trait_item(trait_item);
- placeholder_type_error(tcx, None, visitor.0, false, None, "associated constant");
+ placeholder_type_error(
+ icx.lowerer(),
+ None,
+ visitor.0,
+ false,
+ None,
+ "associated constant",
+ );
}
}
@@ -787,7 +811,7 @@ fn lower_trait_item(tcx: TyCtxt<'_>, trait_item_id: hir::TraitItemId) {
// Account for `type T = _;`.
let mut visitor = HirPlaceholderCollector::default();
visitor.visit_trait_item(trait_item);
- placeholder_type_error(tcx, None, visitor.0, false, None, "associated type");
+ placeholder_type_error(icx.lowerer(), None, visitor.0, false, None, "associated type");
}
hir::TraitItemKind::Type(_, None) => {
@@ -798,7 +822,7 @@ fn lower_trait_item(tcx: TyCtxt<'_>, trait_item_id: hir::TraitItemId) {
let mut visitor = HirPlaceholderCollector::default();
visitor.visit_trait_item(trait_item);
- placeholder_type_error(tcx, None, visitor.0, false, None, "associated type");
+ placeholder_type_error(icx.lowerer(), None, visitor.0, false, None, "associated type");
}
};
@@ -811,6 +835,7 @@ fn lower_impl_item(tcx: TyCtxt<'_>, impl_item_id: hir::ImplItemId) {
tcx.ensure().type_of(def_id);
tcx.ensure().predicates_of(def_id);
let impl_item = tcx.hir().impl_item(impl_item_id);
+ let icx = ItemCtxt::new(tcx, def_id.def_id);
match impl_item.kind {
hir::ImplItemKind::Fn(..) => {
tcx.ensure().codegen_fn_attrs(def_id);
@@ -821,14 +846,21 @@ fn lower_impl_item(tcx: TyCtxt<'_>, impl_item_id: hir::ImplItemId) {
let mut visitor = HirPlaceholderCollector::default();
visitor.visit_impl_item(impl_item);
- placeholder_type_error(tcx, None, visitor.0, false, None, "associated type");
+ placeholder_type_error(icx.lowerer(), None, visitor.0, false, None, "associated type");
}
hir::ImplItemKind::Const(ty, _) => {
// Account for `const T: _ = ..;`
if !ty.is_suggestable_infer_ty() {
let mut visitor = HirPlaceholderCollector::default();
visitor.visit_impl_item(impl_item);
- placeholder_type_error(tcx, None, visitor.0, false, None, "associated constant");
+ placeholder_type_error(
+ icx.lowerer(),
+ None,
+ visitor.0,
+ false,
+ None,
+ "associated constant",
+ );
}
}
}
@@ -880,23 +912,6 @@ fn lower_enum_variant_types(tcx: TyCtxt<'_>, def_id: DefId) {
}
}
-fn find_field(tcx: TyCtxt<'_>, (def_id, ident): (DefId, Ident)) -> Option {
- let adt = tcx.adt_def(def_id);
- if adt.is_enum() {
- return None;
- }
-
- adt.non_enum_variant().fields.iter_enumerated().find_map(|(idx, field)| {
- if field.is_unnamed() {
- let field_ty = tcx.type_of(field.did).instantiate_identity();
- let adt_def = field_ty.ty_adt_def().expect("expect Adt for unnamed field");
- tcx.find_field((adt_def.did(), ident)).map(|_| idx)
- } else {
- (field.ident(tcx).normalize_to_macros_2_0() == ident).then_some(idx)
- }
- })
-}
-
#[derive(Clone, Copy)]
struct NestedSpan {
span: Span,
@@ -1091,7 +1106,10 @@ fn lower_variant(
vis: tcx.visibility(f.def_id),
})
.collect();
- let recovered = matches!(def, hir::VariantData::Struct { recovered: Recovered::Yes(_), .. });
+ let recovered = match def {
+ hir::VariantData::Struct { recovered: Recovered::Yes(guar), .. } => Some(*guar),
+ _ => None,
+ };
ty::VariantDef::new(
ident.name,
variant_did.map(LocalDefId::to_def_id),
@@ -1194,6 +1212,11 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef {
_ => span_bug!(item.span, "trait_def_of_item invoked on non-trait"),
};
+ let constness = if tcx.has_attr(def_id, sym::const_trait) {
+ hir::Constness::Const
+ } else {
+ hir::Constness::NotConst
+ };
let paren_sugar = tcx.has_attr(def_id, sym::rustc_paren_sugar);
if paren_sugar && !tcx.features().unboxed_closures {
tcx.dcx().emit_err(errors::ParenSugarAttribute { span: item.span });
@@ -1349,6 +1372,7 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef {
ty::TraitDef {
def_id: def_id.to_def_id(),
safety,
+ constness,
paren_sugar,
has_auto_impl: is_auto,
is_marker,
@@ -1379,7 +1403,7 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_, ty::PolyFn
..
})
| Item(hir::Item { kind: ItemKind::Fn(sig, generics, _), .. }) => {
- infer_return_ty_for_fn_sig(tcx, sig, generics, def_id, &icx)
+ infer_return_ty_for_fn_sig(sig, generics, def_id, &icx)
}
ImplItem(hir::ImplItem { kind: ImplItemKind::Fn(sig, _), generics, .. }) => {
@@ -1396,7 +1420,7 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_, ty::PolyFn
None,
)
} else {
- infer_return_ty_for_fn_sig(tcx, sig, generics, def_id, &icx)
+ infer_return_ty_for_fn_sig(sig, generics, def_id, &icx)
}
}
@@ -1449,12 +1473,12 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_, ty::PolyFn
}
fn infer_return_ty_for_fn_sig<'tcx>(
- tcx: TyCtxt<'tcx>,
sig: &hir::FnSig<'tcx>,
generics: &hir::Generics<'_>,
def_id: LocalDefId,
icx: &ItemCtxt<'tcx>,
) -> ty::PolyFnSig<'tcx> {
+ let tcx = icx.tcx;
let hir_id = tcx.local_def_id_to_hir_id(def_id);
match sig.decl.output.get_infer_ret_ty() {
@@ -1486,7 +1510,7 @@ fn infer_return_ty_for_fn_sig<'tcx>(
let mut visitor = HirPlaceholderCollector::default();
visitor.visit_ty(ty);
- let mut diag = bad_placeholder(tcx, visitor.0, "return type");
+ let mut diag = bad_placeholder(icx.lowerer(), visitor.0, "return type");
let ret_ty = fn_sig.output();
// Don't leak types into signatures unless they're nameable!
// For example, if a function returns itself, we don't want that
@@ -1682,7 +1706,7 @@ fn check_impl_constness(
}
let trait_def_id = hir_trait_ref.trait_def_id()?;
- if tcx.has_attr(trait_def_id, sym::const_trait) {
+ if tcx.is_const_trait(trait_def_id) {
return None;
}
diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs
index 1e2b0c432338..974dd415f464 100644
--- a/compiler/rustc_hir_analysis/src/collect/type_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs
@@ -12,6 +12,7 @@ use rustc_span::symbol::Ident;
use rustc_span::{Span, DUMMY_SP};
use crate::errors::TypeofReservedKeywordUsed;
+use crate::hir_ty_lowering::HirTyLowerer;
use super::bad_placeholder;
use super::ItemCtxt;
@@ -357,7 +358,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_
.and_then(|body_id| {
ty.is_suggestable_infer_ty().then(|| {
infer_placeholder_type(
- tcx,
+ icx.lowerer(),
def_id,
body_id,
ty.span,
@@ -381,7 +382,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_
ImplItemKind::Const(ty, body_id) => {
if ty.is_suggestable_infer_ty() {
infer_placeholder_type(
- tcx,
+ icx.lowerer(),
def_id,
body_id,
ty.span,
@@ -405,7 +406,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_
ItemKind::Static(ty, .., body_id) => {
if ty.is_suggestable_infer_ty() {
infer_placeholder_type(
- tcx,
+ icx.lowerer(),
def_id,
body_id,
ty.span,
@@ -418,7 +419,14 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_
}
ItemKind::Const(ty, _, body_id) => {
if ty.is_suggestable_infer_ty() {
- infer_placeholder_type(tcx, def_id, body_id, ty.span, item.ident, "constant")
+ infer_placeholder_type(
+ icx.lowerer(),
+ def_id,
+ body_id,
+ ty.span,
+ item.ident,
+ "constant",
+ )
} else {
icx.lower_ty(ty)
}
@@ -559,21 +567,22 @@ pub(super) fn type_of_opaque(
}
}
-fn infer_placeholder_type<'a>(
- tcx: TyCtxt<'a>,
+fn infer_placeholder_type<'tcx>(
+ cx: &dyn HirTyLowerer<'tcx>,
def_id: LocalDefId,
body_id: hir::BodyId,
span: Span,
item_ident: Ident,
kind: &'static str,
-) -> Ty<'a> {
+) -> Ty<'tcx> {
+ let tcx = cx.tcx();
let ty = tcx.diagnostic_only_typeck(def_id).node_type(body_id.hir_id);
// If this came from a free `const` or `static mut?` item,
// then the user may have written e.g. `const A = 42;`.
// In this case, the parser has stashed a diagnostic for
// us to improve in typeck so we do that now.
- let guar = tcx
+ let guar = cx
.dcx()
.try_steal_modify_and_emit_err(span, StashKey::ItemNoType, |err| {
if !ty.references_error() {
@@ -602,7 +611,7 @@ fn infer_placeholder_type<'a>(
}
})
.unwrap_or_else(|| {
- let mut diag = bad_placeholder(tcx, vec![span], kind);
+ let mut diag = bad_placeholder(cx, vec![span], kind);
if !ty.references_error() {
if let Some(ty) = ty.make_suggestable(tcx, false, None) {
diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs
index 7d44ac458de8..0ee87a13e9e3 100644
--- a/compiler/rustc_hir_analysis/src/errors.rs
+++ b/compiler/rustc_hir_analysis/src/errors.rs
@@ -9,6 +9,7 @@ use rustc_middle::ty::Ty;
use rustc_span::{symbol::Ident, Span, Symbol};
mod pattern_types;
pub use pattern_types::*;
+pub mod wrong_number_of_generic_args;
mod precise_captures;
pub(crate) use precise_captures::*;
@@ -692,20 +693,6 @@ pub(crate) struct TypeOf<'tcx> {
pub ty: Ty<'tcx>,
}
-#[derive(Diagnostic)]
-#[diag(hir_analysis_pass_to_variadic_function, code = E0617)]
-pub(crate) struct PassToVariadicFunction<'tcx, 'a> {
- #[primary_span]
- pub span: Span,
- pub ty: Ty<'tcx>,
- pub cast_ty: &'a str,
- #[suggestion(code = "{replace}", applicability = "machine-applicable")]
- pub sugg_span: Option,
- pub replace: String,
- #[help]
- pub help: Option<()>,
-}
-
#[derive(Diagnostic)]
#[diag(hir_analysis_invalid_union_field, code = E0740)]
pub(crate) struct InvalidUnionField {
diff --git a/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs b/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs
similarity index 96%
rename from compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs
rename to compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs
index 5d435a8edf9f..6426ad9dc184 100644
--- a/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs
+++ b/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs
@@ -1,8 +1,8 @@
-use crate::structured_errors::StructuredDiag;
-use rustc_errors::{codes::*, pluralize, Applicability, Diag, MultiSpan};
+use rustc_errors::{
+ codes::*, pluralize, Applicability, Diag, Diagnostic, EmissionGuarantee, MultiSpan,
+};
use rustc_hir as hir;
use rustc_middle::ty::{self as ty, AssocItems, AssocKind, TyCtxt};
-use rustc_session::Session;
use rustc_span::def_id::DefId;
use std::iter;
@@ -541,14 +541,8 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
}
}
- fn start_diagnostics(&self) -> Diag<'tcx> {
- let span = self.path_segment.ident.span;
- let msg = self.create_error_message();
- self.tcx.dcx().struct_span_err(span, msg).with_code(self.code())
- }
-
/// Builds the `expected 1 type argument / supplied 2 type arguments` message.
- fn notify(&self, err: &mut Diag<'_>) {
+ fn notify(&self, err: &mut Diag<'_, impl EmissionGuarantee>) {
let (quantifier, bound) = self.get_quantifier_and_bound();
let provided_args = self.num_provided_args();
@@ -600,7 +594,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
}
}
- fn suggest(&self, err: &mut Diag<'_>) {
+ fn suggest(&self, err: &mut Diag<'_, impl EmissionGuarantee>) {
debug!(
"suggest(self.provided {:?}, self.gen_args.span(): {:?})",
self.num_provided_args(),
@@ -628,7 +622,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
/// ```text
/// type Map = HashMap;
/// ```
- fn suggest_adding_args(&self, err: &mut Diag<'_>) {
+ fn suggest_adding_args(&self, err: &mut Diag<'_, impl EmissionGuarantee>) {
if self.gen_args.parenthesized != hir::GenericArgsParentheses::No {
return;
}
@@ -647,7 +641,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
}
}
- fn suggest_adding_lifetime_args(&self, err: &mut Diag<'_>) {
+ fn suggest_adding_lifetime_args(&self, err: &mut Diag<'_, impl EmissionGuarantee>) {
debug!("suggest_adding_lifetime_args(path_segment: {:?})", self.path_segment);
let num_missing_args = self.num_missing_lifetime_args();
let num_params_to_take = num_missing_args;
@@ -701,7 +695,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
}
}
- fn suggest_adding_type_and_const_args(&self, err: &mut Diag<'_>) {
+ fn suggest_adding_type_and_const_args(&self, err: &mut Diag<'_, impl EmissionGuarantee>) {
let num_missing_args = self.num_missing_type_or_const_args();
let msg = format!("add missing {} argument{}", self.kind(), pluralize!(num_missing_args));
@@ -761,7 +755,10 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
/// ```compile_fail
/// Into::into::>(42) // suggests considering `Into:: >::into(42)`
/// ```
- fn suggest_moving_args_from_assoc_fn_to_trait(&self, err: &mut Diag<'_>) {
+ fn suggest_moving_args_from_assoc_fn_to_trait(
+ &self,
+ err: &mut Diag<'_, impl EmissionGuarantee>,
+ ) {
let trait_ = match self.tcx.trait_of_item(self.def_id) {
Some(def_id) => def_id,
None => return,
@@ -817,7 +814,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
fn suggest_moving_args_from_assoc_fn_to_trait_for_qualified_path(
&self,
- err: &mut Diag<'_>,
+ err: &mut Diag<'_, impl EmissionGuarantee>,
qpath: &'tcx hir::QPath<'tcx>,
msg: String,
num_assoc_fn_excess_args: usize,
@@ -850,7 +847,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
fn suggest_moving_args_from_assoc_fn_to_trait_for_method_call(
&self,
- err: &mut Diag<'_>,
+ err: &mut Diag<'_, impl EmissionGuarantee>,
trait_def_id: DefId,
expr: &'tcx hir::Expr<'tcx>,
msg: String,
@@ -904,7 +901,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
/// ```text
/// type Map = HashMap;
/// ```
- fn suggest_removing_args_or_generics(&self, err: &mut Diag<'_>) {
+ fn suggest_removing_args_or_generics(&self, err: &mut Diag<'_, impl EmissionGuarantee>) {
let num_provided_lt_args = self.num_provided_lifetime_args();
let num_provided_type_const_args = self.num_provided_type_or_const_args();
let unbound_types = self.get_unbound_associated_types();
@@ -922,7 +919,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
let provided_args_matches_unbound_traits =
unbound_types.len() == num_redundant_type_or_const_args;
- let remove_lifetime_args = |err: &mut Diag<'_>| {
+ let remove_lifetime_args = |err: &mut Diag<'_, _>| {
let mut lt_arg_spans = Vec::new();
let mut found_redundant = false;
for arg in self.gen_args.args {
@@ -963,7 +960,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
);
};
- let remove_type_or_const_args = |err: &mut Diag<'_>| {
+ let remove_type_or_const_args = |err: &mut Diag<'_, _>| {
let mut gen_arg_spans = Vec::new();
let mut found_redundant = false;
for arg in self.gen_args.args {
@@ -1060,7 +1057,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
}
/// Builds the `type defined here` message.
- fn show_definition(&self, err: &mut Diag<'_>) {
+ fn show_definition(&self, err: &mut Diag<'_, impl EmissionGuarantee>) {
let mut spans: MultiSpan = if let Some(def_span) = self.tcx.def_ident_span(self.def_id) {
if self.tcx.sess.source_map().is_span_accessible(def_span) {
def_span.into()
@@ -1111,7 +1108,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
}
/// Add note if `impl Trait` is explicitly specified.
- fn note_synth_provided(&self, err: &mut Diag<'_>) {
+ fn note_synth_provided(&self, err: &mut Diag<'_, impl EmissionGuarantee>) {
if !self.is_synth_provided() {
return;
}
@@ -1120,17 +1117,16 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
}
}
-impl<'tcx> StructuredDiag<'tcx> for WrongNumberOfGenericArgs<'_, 'tcx> {
- fn session(&self) -> &Session {
- self.tcx.sess
- }
-
- fn code(&self) -> ErrCode {
- E0107
- }
-
- fn diagnostic_common(&self) -> Diag<'tcx> {
- let mut err = self.start_diagnostics();
+impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for WrongNumberOfGenericArgs<'_, '_> {
+ fn into_diag(
+ self,
+ dcx: rustc_errors::DiagCtxtHandle<'a>,
+ level: rustc_errors::Level,
+ ) -> Diag<'a, G> {
+ let msg = self.create_error_message();
+ let mut err = Diag::new(dcx, level, msg);
+ err.code(E0107);
+ err.span(self.path_segment.ident.span);
self.notify(&mut err);
self.suggest(&mut err);
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
index 802215b2843e..a1feef9e15b7 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
@@ -76,7 +76,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
}
if unbounds.len() > 1 {
- tcx.dcx().emit_err(errors::MultipleRelaxedDefaultBounds {
+ self.dcx().emit_err(errors::MultipleRelaxedDefaultBounds {
spans: unbounds.iter().map(|ptr| ptr.span).collect(),
});
}
@@ -90,7 +90,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
continue;
}
// There was a `?Trait` bound, but it was not `?Sized`; warn.
- tcx.dcx().span_warn(
+ self.dcx().span_warn(
unbound.span,
"relaxing a default bound only does something for `?Sized`; \
all other traits are not bound by default",
@@ -310,7 +310,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
duplicates
.entry(assoc_item.def_id)
.and_modify(|prev_span| {
- tcx.dcx().emit_err(errors::ValueOfAssociatedStructAlreadySpecified {
+ self.dcx().emit_err(errors::ValueOfAssociatedStructAlreadySpecified {
span: constraint.span,
prev_span: *prev_span,
item_name: constraint.ident,
@@ -338,7 +338,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
.into(),
ty::GenericParamDefKind::Type { .. } => {
let guar = *emitted_bad_param_err.get_or_insert_with(|| {
- tcx.dcx().emit_err(
+ self.dcx().emit_err(
crate::errors::ReturnTypeNotationIllegalParam::Type {
span: path_span,
param_span: tcx.def_span(param.def_id),
@@ -349,7 +349,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
}
ty::GenericParamDefKind::Const { .. } => {
let guar = *emitted_bad_param_err.get_or_insert_with(|| {
- tcx.dcx().emit_err(
+ self.dcx().emit_err(
crate::errors::ReturnTypeNotationIllegalParam::Const {
span: path_span,
param_span: tcx.def_span(param.def_id),
@@ -371,7 +371,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
{
alias_ty.into()
} else {
- return Err(tcx.dcx().emit_err(crate::errors::ReturnTypeNotationOnNonRpitit {
+ return Err(self.dcx().emit_err(crate::errors::ReturnTypeNotationOnNonRpitit {
span: constraint.span,
ty: tcx.liberate_late_bound_regions(assoc_item.def_id, output),
fn_span: tcx.hir().span_if_local(assoc_item.def_id),
@@ -417,7 +417,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
let ty = alias_term
.map_bound(|alias| tcx.type_of(alias.def_id).instantiate(tcx, alias.args));
let ty =
- check_assoc_const_binding_type(tcx, constraint.ident, ty, constraint.hir_id);
+ check_assoc_const_binding_type(self, constraint.ident, ty, constraint.hir_id);
tcx.feed_anon_const_type(anon_const.def_id, ty::EarlyBinder::bind(ty));
}
@@ -426,7 +426,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
match constraint.kind {
hir::AssocItemConstraintKind::Equality { .. } if let ty::AssocKind::Fn = assoc_kind => {
- return Err(tcx.dcx().emit_err(crate::errors::ReturnTypeNotationEqualityBound {
+ return Err(self.dcx().emit_err(crate::errors::ReturnTypeNotationEqualityBound {
span: constraint.span,
}));
}
@@ -462,7 +462,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
late_bound_in_term,
|br_name| {
struct_span_code_err!(
- tcx.dcx(),
+ self.dcx(),
constraint.span,
E0582,
"binding for associated type `{}` references {}, \
@@ -519,7 +519,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
///
/// [^1]: .
fn check_assoc_const_binding_type<'tcx>(
- tcx: TyCtxt<'tcx>,
+ cx: &dyn HirTyLowerer<'tcx>,
assoc_const: Ident,
ty: ty::Binder<'tcx, Ty<'tcx>>,
hir_id: hir::HirId,
@@ -536,13 +536,14 @@ fn check_assoc_const_binding_type<'tcx>(
}
let mut collector = GenericParamAndBoundVarCollector {
- tcx,
+ cx,
params: Default::default(),
vars: Default::default(),
depth: ty::INNERMOST,
};
let mut guar = ty.visit_with(&mut collector).break_value();
+ let tcx = cx.tcx();
let ty_note = ty
.make_suggestable(tcx, false, None)
.map(|ty| crate::errors::TyOfAssocConstBindingNote { assoc_const, ty });
@@ -556,7 +557,7 @@ fn check_assoc_const_binding_type<'tcx>(
for index in collector.params {
let param = generics.param_at(index as _, tcx);
let is_self_param = param.name == rustc_span::symbol::kw::SelfUpper;
- guar.get_or_insert(tcx.dcx().emit_err(crate::errors::ParamInTyOfAssocConstBinding {
+ guar.get_or_insert(cx.dcx().emit_err(crate::errors::ParamInTyOfAssocConstBinding {
span: assoc_const.span,
assoc_const,
param_name: param.name,
@@ -574,7 +575,7 @@ fn check_assoc_const_binding_type<'tcx>(
}));
}
for (var_def_id, var_name) in collector.vars {
- guar.get_or_insert(tcx.dcx().emit_err(
+ guar.get_or_insert(cx.dcx().emit_err(
crate::errors::EscapingBoundVarInTyOfAssocConstBinding {
span: assoc_const.span,
assoc_const,
@@ -590,14 +591,14 @@ fn check_assoc_const_binding_type<'tcx>(
Ty::new_error(tcx, guar)
}
-struct GenericParamAndBoundVarCollector<'tcx> {
- tcx: TyCtxt<'tcx>,
+struct GenericParamAndBoundVarCollector<'a, 'tcx> {
+ cx: &'a dyn HirTyLowerer<'tcx>,
params: FxIndexSet,
vars: FxIndexSet<(DefId, Symbol)>,
depth: ty::DebruijnIndex,
}
-impl<'tcx> TypeVisitor> for GenericParamAndBoundVarCollector<'tcx> {
+impl<'tcx> TypeVisitor> for GenericParamAndBoundVarCollector<'_, 'tcx> {
type Result = ControlFlow;
fn visit_binder>>(
@@ -620,7 +621,7 @@ impl<'tcx> TypeVisitor> for GenericParamAndBoundVarCollector<'tcx>
ty::BoundTyKind::Param(def_id, name) => (def_id, name),
ty::BoundTyKind::Anon => {
let reported = self
- .tcx
+ .cx
.dcx()
.delayed_bug(format!("unexpected anon bound ty: {:?}", bt.var));
return ControlFlow::Break(reported);
@@ -643,7 +644,7 @@ impl<'tcx> TypeVisitor> for GenericParamAndBoundVarCollector<'tcx>
ty::BrNamed(def_id, name) => (def_id, name),
ty::BrAnon | ty::BrEnv => {
let guar = self
- .tcx
+ .cx
.dcx()
.delayed_bug(format!("unexpected bound region kind: {:?}", br.kind));
return ControlFlow::Break(guar);
@@ -661,7 +662,7 @@ impl<'tcx> TypeVisitor> for GenericParamAndBoundVarCollector<'tcx>
self.params.insert(param.index);
}
ty::ConstKind::Bound(db, ty::BoundVar { .. }) if db >= self.depth => {
- let guar = self.tcx.dcx().delayed_bug("unexpected escaping late-bound const var");
+ let guar = self.cx.dcx().delayed_bug("unexpected escaping late-bound const var");
return ControlFlow::Break(guar);
}
_ if ct.has_param() || ct.has_bound_vars() => return ct.super_visit_with(self),
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs
index 2d240699105d..5e595488ea7a 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs
@@ -4,7 +4,6 @@ use crate::errors::{
};
use crate::fluent_generated as fluent;
use crate::hir_ty_lowering::HirTyLowerer;
-use crate::traits::error_reporting::report_object_safety_error;
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
use rustc_data_structures::sorted_map::SortedMap;
use rustc_data_structures::unord::UnordMap;
@@ -12,9 +11,9 @@ use rustc_errors::MultiSpan;
use rustc_errors::{
codes::*, pluralize, struct_span_code_err, Applicability, Diag, ErrorGuaranteed,
};
-use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{DefId, LocalDefId};
+use rustc_hir::{self as hir, Node};
use rustc_middle::bug;
use rustc_middle::query::Key;
use rustc_middle::ty::print::{PrintPolyTraitRefExt as _, PrintTraitRefExt as _};
@@ -27,6 +26,7 @@ use rustc_span::edit_distance::find_best_match_for_name;
use rustc_span::symbol::{kw, sym, Ident};
use rustc_span::BytePos;
use rustc_span::{Span, Symbol, DUMMY_SP};
+use rustc_trait_selection::error_reporting::traits::report_object_safety_error;
use rustc_trait_selection::traits::FulfillmentError;
use rustc_trait_selection::traits::{
object_safety_violations_for_assoc_item, TraitAliasExpansionInfo,
@@ -46,7 +46,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
return;
}
- self.tcx().dcx().emit_err(MissingTypeParams {
+ self.dcx().emit_err(MissingTypeParams {
span,
def_span: self.tcx().def_span(def_id),
span_snippet: self.tcx().sess.source_map().span_to_snippet(span).ok(),
@@ -109,7 +109,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
if is_impl {
let trait_name = self.tcx().def_path_str(trait_def_id);
- self.tcx().dcx().emit_err(ManualImplementation { span, trait_name });
+ self.dcx().emit_err(ManualImplementation { span, trait_name });
}
}
@@ -156,7 +156,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
if is_dummy {
err.label = Some(errors::AssocItemNotFoundLabel::NotFound { span });
- return tcx.dcx().emit_err(err);
+ return self.dcx().emit_err(err);
}
let all_candidate_names: Vec<_> = all_candidates()
@@ -174,7 +174,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
assoc_kind: assoc_kind_str,
suggested_name,
});
- return tcx.dcx().emit_err(err);
+ return self.dcx().emit_err(err);
}
// If we didn't find a good item in the supertraits (or couldn't get
@@ -239,10 +239,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
assoc_kind: assoc_kind_str,
suggested_name,
});
- return tcx.dcx().emit_err(err);
+ return self.dcx().emit_err(err);
}
- let mut err = tcx.dcx().create_err(err);
+ let mut err = self.dcx().create_err(err);
if suggest_constraining_type_param(
tcx,
generics,
@@ -264,7 +264,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
}
return err.emit();
}
- return tcx.dcx().emit_err(err);
+ return self.dcx().emit_err(err);
}
}
@@ -291,7 +291,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
err.label = Some(errors::AssocItemNotFoundLabel::NotFound { span: assoc_name.span });
}
- tcx.dcx().emit_err(err)
+ self.dcx().emit_err(err)
}
fn complain_about_assoc_kind_mismatch(
@@ -347,7 +347,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
(ident.span, None, assoc_kind, assoc_item.kind)
};
- tcx.dcx().emit_err(errors::AssocKindMismatch {
+ self.dcx().emit_err(errors::AssocKindMismatch {
span,
expected: super::assoc_kind_str(expected),
got: super::assoc_kind_str(got),
@@ -366,8 +366,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
traits: &[String],
name: Symbol,
) -> ErrorGuaranteed {
- let mut err =
- struct_span_code_err!(self.tcx().dcx(), span, E0223, "ambiguous associated type");
+ let mut err = struct_span_code_err!(self.dcx(), span, E0223, "ambiguous associated type");
if self
.tcx()
.resolutions(())
@@ -463,9 +462,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
}
}
}
- let reported = err.emit();
- self.set_tainted_by_errors(reported);
- reported
+ err.emit()
}
pub(crate) fn complain_about_ambiguous_inherent_assoc_ty(
@@ -475,16 +472,14 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
span: Span,
) -> ErrorGuaranteed {
let mut err = struct_span_code_err!(
- self.tcx().dcx(),
+ self.dcx(),
name.span,
E0034,
"multiple applicable items in scope"
);
err.span_label(name.span, format!("multiple `{name}` found"));
self.note_ambiguous_inherent_assoc_ty(&mut err, candidates, span);
- let reported = err.emit();
- self.set_tainted_by_errors(reported);
- reported
+ err.emit()
}
// FIXME(fmease): Heavily adapted from `rustc_hir_typeck::method::suggest`. Deduplicate.
@@ -576,7 +571,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
};
let mut err = struct_span_code_err!(
- tcx.dcx(),
+ self.dcx(),
name.span,
E0220,
"associated type `{name}` not found for `{self_ty}` in the current scope"
@@ -662,7 +657,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
bounds.sort();
bounds.dedup();
- let mut err = tcx.dcx().struct_span_err(
+ let mut err = self.dcx().struct_span_err(
name.span,
format!("the associated type `{name}` exists for `{self_ty}`, but its trait bounds were not satisfied")
);
@@ -745,7 +740,15 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
if object_safety_violations {
return;
}
+
+ // related to issue #91997, turbofishes added only when in an expr or pat
+ let mut in_expr_or_pat = false;
if let ([], [bound]) = (&potential_assoc_types[..], &trait_bounds) {
+ let grandparent = tcx.parent_hir_node(tcx.parent_hir_id(bound.trait_ref.hir_ref_id));
+ in_expr_or_pat = match grandparent {
+ Node::Expr(_) | Node::Pat(_) => true,
+ _ => false,
+ };
match bound.trait_ref.path.segments {
// FIXME: `trait_ref.path.span` can point to a full path with multiple
// segments, even though `trait_ref.path.segments` is of length `1`. Work
@@ -829,7 +832,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
trait_bound_spans.sort();
let mut err = struct_span_code_err!(
- tcx.dcx(),
+ self.dcx(),
trait_bound_spans,
E0191,
"the value of the associated type{} {} must be specified",
@@ -901,6 +904,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
// `Trait<'a, Item = Type>` while accounting for the `<'a>` in the
// suggestion.
format!("{}, {}>", &snippet[..snippet.len() - 1], types.join(", "))
+ } else if in_expr_or_pat {
+ // The user wrote `Iterator`, so we don't have a type we can suggest, but at
+ // least we can clue them to the correct syntax `Iterator::- `.
+ format!("{}::<{}>", snippet, types.join(", "))
} else {
// The user wrote `Iterator`, so we don't have a type we can suggest, but at
// least we can clue them to the correct syntax `Iterator
- `.
@@ -974,7 +981,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
}
}
- self.set_tainted_by_errors(err.emit());
+ err.emit();
}
/// On ambiguous associated type, look for an associated function whose name matches the
@@ -1011,17 +1018,14 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
.filter_by_name_unhygienic(Symbol::intern(&name))
.next()
{
- let reported =
- struct_span_code_err!(tcx.dcx(), span, E0223, "ambiguous associated type")
- .with_span_suggestion_verbose(
- ident2.span.to(ident3.span),
- format!("there is an associated function with a similar name: `{name}`"),
- name,
- Applicability::MaybeIncorrect,
- )
- .emit();
- self.set_tainted_by_errors(reported);
- Err(reported)
+ Err(struct_span_code_err!(self.dcx(), span, E0223, "ambiguous associated type")
+ .with_span_suggestion_verbose(
+ ident2.span.to(ident3.span),
+ format!("there is an associated function with a similar name: `{name}`"),
+ name,
+ Applicability::MaybeIncorrect,
+ )
+ .emit())
} else {
Ok(())
}
@@ -1120,7 +1124,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
let last_span = *arg_spans.last().unwrap();
let span: MultiSpan = arg_spans.into();
let mut err = struct_span_code_err!(
- self.tcx().dcx(),
+ self.dcx(),
span,
E0109,
"{kind} arguments are not allowed on {this_type}",
@@ -1130,20 +1134,17 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
err.span_label(span, format!("not allowed on {what}"));
}
generics_args_err_extend(self.tcx(), segments, &mut err, err_extend);
- let reported = err.emit();
- self.set_tainted_by_errors(reported);
- reported
+ err.emit()
}
pub fn report_trait_object_addition_traits_error(
&self,
regular_traits: &Vec
>,
) -> ErrorGuaranteed {
- let tcx = self.tcx();
let first_trait = ®ular_traits[0];
let additional_trait = ®ular_traits[1];
let mut err = struct_span_code_err!(
- tcx.dcx(),
+ self.dcx(),
additional_trait.bottom().1,
E0225,
"only auto traits can be used as additional traits in a trait object"
@@ -1169,9 +1170,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
for more information on them, visit \
",
);
- let reported = err.emit();
- self.set_tainted_by_errors(reported);
- reported
+ err.emit()
}
pub fn report_trait_object_with_no_traits_error(
@@ -1185,20 +1184,19 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
.map(|&(trait_ref, _)| trait_ref.def_id())
.find(|&trait_ref| tcx.is_trait_alias(trait_ref))
.map(|trait_ref| tcx.def_span(trait_ref));
- let reported =
- tcx.dcx().emit_err(TraitObjectDeclaredWithNoTraits { span, trait_alias_span });
- self.set_tainted_by_errors(reported);
- reported
+
+ self.dcx().emit_err(TraitObjectDeclaredWithNoTraits { span, trait_alias_span })
}
}
/// Emit an error for the given associated item constraint.
pub fn prohibit_assoc_item_constraint(
- tcx: TyCtxt<'_>,
+ cx: &dyn HirTyLowerer<'_>,
constraint: &hir::AssocItemConstraint<'_>,
segment: Option<(DefId, &hir::PathSegment<'_>, Span)>,
) -> ErrorGuaranteed {
- let mut err = tcx.dcx().create_err(AssocItemConstraintsNotAllowedHere {
+ let tcx = cx.tcx();
+ let mut err = cx.dcx().create_err(AssocItemConstraintsNotAllowedHere {
span: constraint.span,
fn_trait_expansion: if let Some((_, segment, span)) = segment
&& segment.args().parenthesized == hir::GenericArgsParentheses::ParenSugar
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs
index e92c377f0ce2..b1c77db9f370 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs
@@ -1,9 +1,9 @@
-use super::IsMethodCall;
+use super::{HirTyLowerer, IsMethodCall};
+use crate::errors::wrong_number_of_generic_args::{GenericArgsInfo, WrongNumberOfGenericArgs};
use crate::hir_ty_lowering::{
errors::prohibit_assoc_item_constraint, ExplicitLateBound, GenericArgCountMismatch,
GenericArgCountResult, GenericArgPosition, GenericArgsLowerer,
};
-use crate::structured_errors::{GenericArgsInfo, StructuredDiag, WrongNumberOfGenericArgs};
use rustc_ast::ast::ParamKindOrd;
use rustc_errors::{
codes::*, struct_span_code_err, Applicability, Diag, ErrorGuaranteed, MultiSpan,
@@ -13,7 +13,7 @@ use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::DefId;
use rustc_hir::GenericArg;
use rustc_middle::ty::{
- self, GenericArgsRef, GenericParamDef, GenericParamDefKind, IsSuggestable, Ty, TyCtxt,
+ self, GenericArgsRef, GenericParamDef, GenericParamDefKind, IsSuggestable, Ty,
};
use rustc_session::lint::builtin::LATE_BOUND_LIFETIME_ARGUMENTS;
use rustc_span::symbol::{kw, sym};
@@ -22,15 +22,16 @@ use smallvec::SmallVec;
/// Report an error that a generic argument did not match the generic parameter that was
/// expected.
fn generic_arg_mismatch_err(
- tcx: TyCtxt<'_>,
+ cx: &dyn HirTyLowerer<'_>,
arg: &GenericArg<'_>,
param: &GenericParamDef,
possible_ordering_error: bool,
help: Option,
) -> ErrorGuaranteed {
+ let tcx = cx.tcx();
let sess = tcx.sess;
let mut err = struct_span_code_err!(
- tcx.dcx(),
+ cx.dcx(),
arg.span(),
E0747,
"{} provided when a {} was expected",
@@ -171,7 +172,7 @@ fn generic_arg_mismatch_err(
/// - `inferred_kind`: if no parameter was provided, and inference
/// is enabled, then creates a suitable inference variable.
pub fn lower_generic_args<'tcx: 'a, 'a>(
- tcx: TyCtxt<'tcx>,
+ cx: &dyn HirTyLowerer<'tcx>,
def_id: DefId,
parent_args: &[ty::GenericArg<'tcx>],
has_self: bool,
@@ -179,6 +180,7 @@ pub fn lower_generic_args<'tcx: 'a, 'a>(
arg_count: &GenericArgCountResult,
ctx: &mut impl GenericArgsLowerer<'a, 'tcx>,
) -> GenericArgsRef<'tcx> {
+ let tcx = cx.tcx();
// Collect the segments of the path; we need to instantiate arguments
// for parameters throughout the entire path (wherever there are
// generic parameters).
@@ -326,7 +328,7 @@ pub fn lower_generic_args<'tcx: 'a, 'a>(
param_types_present.dedup();
generic_arg_mismatch_err(
- tcx,
+ cx,
arg,
param,
!args_iter.clone().is_sorted_by_key(|arg| arg.to_ord()),
@@ -381,7 +383,7 @@ pub fn lower_generic_args<'tcx: 'a, 'a>(
assert_eq!(kind, "lifetime");
let (provided_arg, param) =
force_infer_lt.expect("lifetimes ought to have been inferred");
- generic_arg_mismatch_err(tcx, provided_arg, param, false, None);
+ generic_arg_mismatch_err(cx, provided_arg, param, false, None);
}
break;
@@ -405,7 +407,7 @@ pub fn lower_generic_args<'tcx: 'a, 'a>(
/// Checks that the correct number of generic arguments have been provided.
/// Used specifically for function calls.
pub fn check_generic_arg_count_for_call(
- tcx: TyCtxt<'_>,
+ cx: &dyn HirTyLowerer<'_>,
def_id: DefId,
generics: &ty::Generics,
seg: &hir::PathSegment<'_>,
@@ -416,14 +418,14 @@ pub fn check_generic_arg_count_for_call(
IsMethodCall::No => GenericArgPosition::Value,
};
let has_self = generics.parent.is_none() && generics.has_self;
- check_generic_arg_count(tcx, def_id, seg, generics, gen_pos, has_self)
+ check_generic_arg_count(cx, def_id, seg, generics, gen_pos, has_self)
}
/// Checks that the correct number of generic arguments have been provided.
/// This is used both for datatypes and function calls.
-#[instrument(skip(tcx, gen_pos), level = "debug")]
+#[instrument(skip(cx, gen_pos), level = "debug")]
pub(crate) fn check_generic_arg_count(
- tcx: TyCtxt<'_>,
+ cx: &dyn HirTyLowerer<'_>,
def_id: DefId,
seg: &hir::PathSegment<'_>,
gen_params: &ty::Generics,
@@ -456,11 +458,11 @@ pub(crate) fn check_generic_arg_count(
if gen_pos != GenericArgPosition::Type
&& let Some(c) = gen_args.constraints.first()
{
- prohibit_assoc_item_constraint(tcx, c, None);
+ prohibit_assoc_item_constraint(cx, c, None);
}
let explicit_late_bound =
- prohibit_explicit_late_bound_lifetimes(tcx, gen_params, gen_args, gen_pos);
+ prohibit_explicit_late_bound_lifetimes(cx, gen_params, gen_args, gen_pos);
let mut invalid_args = vec![];
@@ -486,17 +488,15 @@ pub(crate) fn check_generic_arg_count(
GenericArgsInfo::MissingLifetimes { num_missing_args }
};
- let reported = WrongNumberOfGenericArgs::new(
- tcx,
+ let reported = cx.dcx().emit_err(WrongNumberOfGenericArgs::new(
+ cx.tcx(),
gen_args_info,
seg,
gen_params,
has_self as usize,
gen_args,
def_id,
- )
- .diagnostic()
- .emit();
+ ));
Err(reported)
};
@@ -573,17 +573,17 @@ pub(crate) fn check_generic_arg_count(
debug!(?gen_args_info);
let reported = gen_args.has_err().unwrap_or_else(|| {
- WrongNumberOfGenericArgs::new(
- tcx,
- gen_args_info,
- seg,
- gen_params,
- params_offset,
- gen_args,
- def_id,
- )
- .diagnostic()
- .emit_unless(all_params_are_binded)
+ cx.dcx()
+ .create_err(WrongNumberOfGenericArgs::new(
+ cx.tcx(),
+ gen_args_info,
+ seg,
+ gen_params,
+ params_offset,
+ gen_args,
+ def_id,
+ ))
+ .emit_unless(all_params_are_binded)
});
Err(reported)
@@ -623,7 +623,7 @@ pub(crate) fn check_generic_arg_count(
/// Prohibits explicit lifetime arguments if late-bound lifetime parameters
/// are present. This is used both for datatypes and function calls.
pub(crate) fn prohibit_explicit_late_bound_lifetimes(
- tcx: TyCtxt<'_>,
+ cx: &dyn HirTyLowerer<'_>,
def: &ty::Generics,
args: &hir::GenericArgs<'_>,
position: GenericArgPosition,
@@ -644,13 +644,13 @@ pub(crate) fn prohibit_explicit_late_bound_lifetimes(
if position == GenericArgPosition::Value
&& args.num_lifetime_params() != param_counts.lifetimes
{
- struct_span_code_err!(tcx.dcx(), span, E0794, "{}", msg)
+ struct_span_code_err!(cx.dcx(), span, E0794, "{}", msg)
.with_span_note(span_late, note)
.emit();
} else {
let mut multispan = MultiSpan::from_span(span);
multispan.push_span_label(span_late, note);
- tcx.node_span_lint(
+ cx.tcx().node_span_lint(
LATE_BOUND_LIFETIME_ARGUMENTS,
args.args[0].hir_id(),
multispan,
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs
index 240a749de96a..29c71c3fa50b 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs
@@ -4,7 +4,7 @@ use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_lint_defs::{builtin::BARE_TRAIT_OBJECTS, Applicability};
use rustc_span::Span;
-use rustc_trait_selection::traits::error_reporting::suggestions::NextTypeParamName;
+use rustc_trait_selection::error_reporting::traits::suggestions::NextTypeParamName;
use super::HirTyLowerer;
@@ -60,7 +60,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
let msg = "trait objects must include the `dyn` keyword";
let label = "add `dyn` keyword before this trait";
let mut diag =
- rustc_errors::struct_span_code_err!(tcx.dcx(), self_ty.span, E0782, "{}", msg);
+ rustc_errors::struct_span_code_err!(self.dcx(), self_ty.span, E0782, "{}", msg);
if self_ty.span.can_be_used_for_suggestions()
&& !self.maybe_suggest_impl_trait(self_ty, &mut diag)
{
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
index 02db0352daaf..a665306f2c6a 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
@@ -28,7 +28,8 @@ use crate::require_c_abi_if_c_variadic;
use rustc_ast::TraitObjectSyntax;
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
use rustc_errors::{
- codes::*, struct_span_code_err, Applicability, Diag, ErrorGuaranteed, FatalError,
+ codes::*, struct_span_code_err, Applicability, Diag, DiagCtxtHandle, ErrorGuaranteed,
+ FatalError,
};
use rustc_hir as hir;
use rustc_hir::def::{CtorOf, DefKind, Namespace, Res};
@@ -47,7 +48,7 @@ use rustc_middle::{bug, span_bug};
use rustc_session::lint::builtin::AMBIGUOUS_ASSOCIATED_ITEMS;
use rustc_span::edit_distance::find_best_match_for_name;
use rustc_span::symbol::{kw, Ident, Symbol};
-use rustc_span::{sym, Span, DUMMY_SP};
+use rustc_span::{Span, DUMMY_SP};
use rustc_target::spec::abi;
use rustc_trait_selection::infer::InferCtxtExt;
use rustc_trait_selection::traits::wf::object_region_bounds;
@@ -102,6 +103,8 @@ pub enum RegionInferReason<'a> {
pub trait HirTyLowerer<'tcx> {
fn tcx(&self) -> TyCtxt<'tcx>;
+ fn dcx(&self) -> DiagCtxtHandle<'_>;
+
/// Returns the [`LocalDefId`] of the overarching item whose constituents get lowered.
fn item_def_id(&self) -> LocalDefId;
@@ -177,12 +180,6 @@ pub trait HirTyLowerer<'tcx> {
/// The inference context of the lowering context if applicable.
fn infcx(&self) -> Option<&InferCtxt<'tcx>>;
- /// Taint the context with errors.
- ///
- /// Invoke this when you encounter an error from some prior pass like name resolution.
- /// This is used to help suppress derived errors typeck might otherwise report.
- fn set_tainted_by_errors(&self, e: ErrorGuaranteed);
-
/// Convenience method for coercing the lowering context into a trait object type.
///
/// Most lowering routines are defined on the trait object type directly
@@ -323,7 +320,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
ty::BoundConstness::NotConst,
);
if let Some(c) = item_segment.args().constraints.first() {
- prohibit_assoc_item_constraint(self.tcx(), c, Some((def_id, item_segment, span)));
+ prohibit_assoc_item_constraint(self, c, Some((def_id, item_segment, span)));
}
args
}
@@ -394,7 +391,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
}
let mut arg_count = check_generic_arg_count(
- tcx,
+ self,
def_id,
segment,
generics,
@@ -402,10 +399,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
self_ty.is_some(),
);
- if let Err(err) = &arg_count.correct {
- self.set_tainted_by_errors(err.reported);
- }
-
// Skip processing if type has no generic parameters.
// Traits always have `Self` as a generic parameter, which means they will not return early
// here and so associated item constraints will be handled regardless of whether there are
@@ -560,13 +553,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
}
if let ty::BoundConstness::Const | ty::BoundConstness::ConstIfConst = constness
&& generics.has_self
- && !tcx.has_attr(def_id, sym::const_trait)
+ && !tcx.is_const_trait(def_id)
{
- let reported = tcx.dcx().emit_err(crate::errors::ConstBoundForNonConstTrait {
+ let reported = self.dcx().emit_err(crate::errors::ConstBoundForNonConstTrait {
span,
modifier: constness.as_str(),
});
- self.set_tainted_by_errors(reported);
arg_count.correct = Err(GenericArgCountMismatch { reported, invalid_args: vec![] });
}
@@ -579,7 +571,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
incorrect_args: &arg_count.correct,
};
let args = lower_generic_args(
- tcx,
+ self,
def_id,
parent_args,
self_ty.is_some(),
@@ -609,7 +601,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
ty::BoundConstness::NotConst,
);
if let Some(c) = item_segment.args().constraints.first() {
- prohibit_assoc_item_constraint(self.tcx(), c, Some((item_def_id, item_segment, span)));
+ prohibit_assoc_item_constraint(self, c, Some((item_def_id, item_segment, span)));
}
args
}
@@ -715,7 +707,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
// would not be well-formed!
if polarity != ty::PredicatePolarity::Positive {
assert!(
- self.tcx().dcx().has_errors().is_some(),
+ self.dcx().has_errors().is_some(),
"negative trait bounds should not have assoc item constraints",
);
continue;
@@ -761,11 +753,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
constness,
);
if let Some(c) = trait_segment.args().constraints.first() {
- prohibit_assoc_item_constraint(
- self.tcx(),
- c,
- Some((trait_def_id, trait_segment, span)),
- );
+ prohibit_assoc_item_constraint(self, c, Some((trait_def_id, trait_segment, span)));
}
ty::TraitRef::new_from_args(self.tcx(), trait_def_id, generic_args)
}
@@ -877,7 +865,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
span,
constraint,
);
- self.set_tainted_by_errors(reported);
return Err(reported);
};
debug!(?bound);
@@ -887,7 +874,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
let assoc_kind_str = assoc_kind_str(assoc_kind);
let ty_param_name = &ty_param_name.to_string();
- let mut err = tcx.dcx().create_err(crate::errors::AmbiguousAssocItem {
+ let mut err = self.dcx().create_err(crate::errors::AmbiguousAssocItem {
span,
assoc_kind: assoc_kind_str,
assoc_name,
@@ -960,7 +947,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
));
}
let reported = err.emit();
- self.set_tainted_by_errors(reported);
if !where_bounds.is_empty() {
return Err(reported);
}
@@ -1059,7 +1045,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
// trait reference.
let Some(trait_ref) = tcx.impl_trait_ref(impl_def_id) else {
// A cycle error occurred, most likely.
- tcx.dcx().span_bug(span, "expected cycle error");
+ self.dcx().span_bug(span, "expected cycle error");
};
self.probe_single_bound_for_assoc_item(
@@ -1089,10 +1075,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
let reported = if variant_resolution.is_some() {
// Variant in type position
let msg = format!("expected type, found variant `{assoc_ident}`");
- tcx.dcx().span_err(span, msg)
+ self.dcx().span_err(span, msg)
} else if qself_ty.is_enum() {
let mut err = struct_span_code_err!(
- tcx.dcx(),
+ self.dcx(),
assoc_ident.span,
E0599,
"no variant named `{}` found for enum `{}`",
@@ -1133,7 +1119,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
} else if let ty::Alias(ty::Opaque, alias_ty) = qself_ty.kind() {
// `::Assoc` makes no sense.
struct_span_code_err!(
- tcx.dcx(),
+ self.dcx(),
tcx.def_span(alias_ty.def_id),
E0667,
"`impl Trait` is not allowed in path parameters"
@@ -1153,7 +1139,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
assoc_ident.name,
)
};
- self.set_tainted_by_errors(reported);
return Err(reported);
}
};
@@ -1404,13 +1389,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
let tcx = self.tcx();
if !tcx.visibility(item_def_id).is_accessible_from(scope, tcx) {
- let reported = tcx.dcx().emit_err(crate::errors::AssocItemIsPrivate {
+ self.dcx().emit_err(crate::errors::AssocItemIsPrivate {
span,
kind: tcx.def_descr(item_def_id),
name: ident,
defined_here_label: tcx.def_span(item_def_id),
});
- self.set_tainted_by_errors(reported);
}
tcx.check_stability(item_def_id, Some(block), span, None);
@@ -1564,7 +1548,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
for segment in segments {
// Only emit the first error to avoid overloading the user with error messages.
if let Some(c) = segment.args().constraints.first() {
- return Err(prohibit_assoc_item_constraint(self.tcx(), c, None));
+ return Err(prohibit_assoc_item_constraint(self, c, None));
}
}
@@ -1824,7 +1808,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
// `AlwaysApplicable` impl needs a `T: ?Sized` bound for
// this to compile if we were to normalize here.
if forbid_generic && ty.has_param() {
- let mut err = tcx.dcx().struct_span_err(
+ let mut err = self.dcx().struct_span_err(
path.span,
"generic `Self` types are currently not permitted in anonymous constants",
);
@@ -1836,7 +1820,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
err.span_note(impl_.self_ty.span, "not a concrete type");
}
let reported = err.emit();
- self.set_tainted_by_errors(reported);
Ty::new_error(tcx, reported)
} else {
ty
@@ -1848,19 +1831,13 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
path.segments[..path.segments.len() - 2].iter(),
GenericsArgsErrExtend::None,
);
- // HACK: until we support ``, assume all of them are.
- let constness = if tcx.has_attr(tcx.parent(def_id), sym::const_trait) {
- ty::BoundConstness::ConstIfConst
- } else {
- ty::BoundConstness::NotConst
- };
self.lower_qpath(
span,
opt_self_ty,
def_id,
&path.segments[path.segments.len() - 2],
path.segments.last().unwrap(),
- constness,
+ ty::BoundConstness::NotConst,
)
}
Res::PrimTy(prim_ty) => {
@@ -1883,7 +1860,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
.tcx()
.dcx()
.span_delayed_bug(path.span, "path with `Res::Err` but no error emitted");
- self.set_tainted_by_errors(e);
Ty::new_error(self.tcx(), e)
}
Res::Def(..) => {
@@ -1894,7 +1870,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
);
Ty::new_error(
self.tcx(),
- self.tcx().dcx().span_delayed_bug(span, "incorrect resolution for `Self`"),
+ self.dcx().span_delayed_bug(span, "incorrect resolution for `Self`"),
)
}
_ => span_bug!(span, "unexpected resolution: {:?}", path.res),
@@ -1967,7 +1943,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
let sig_span = self.tcx().def_span(sig_id);
let mut try_emit = |descr| {
if emit {
- self.tcx().dcx().emit_err(crate::errors::NotSupportedDelegation {
+ self.dcx().emit_err(crate::errors::NotSupportedDelegation {
span,
descr,
callee_span: sig_span,
@@ -2017,8 +1993,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
) -> Ty<'tcx> {
if self.check_delegation_constraints(sig_id, span, idx == hir::InferDelegationKind::Output)
{
- let e = self.tcx().dcx().span_delayed_bug(span, "not supported delegation case");
- self.set_tainted_by_errors(e);
+ let e = self.dcx().span_delayed_bug(span, "not supported delegation case");
return Ty::new_error(self.tcx(), e);
};
let sig = self.tcx().fn_sig(sig_id);
@@ -2183,7 +2158,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
let ty = self.lower_ty(ty);
let pat_ty = match pat.kind {
hir::PatKind::Wild => {
- let err = tcx.dcx().emit_err(WildPatTy { span: pat.span });
+ let err = self.dcx().emit_err(WildPatTy { span: pat.span });
Ty::new_error(tcx, err)
}
hir::PatKind::Range(start, end, include_end) => {
@@ -2363,7 +2338,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
self.validate_late_bound_regions(late_bound_in_args, late_bound_in_ret, |br_name| {
struct_span_code_err!(
- tcx.dcx(),
+ self.dcx(),
decl.output.span(),
E0581,
"return type references {}, which is not constrained by the fn input types",
@@ -2414,11 +2389,11 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
}
#[instrument(level = "trace", skip(self, generate_err))]
- fn validate_late_bound_regions(
- &self,
+ fn validate_late_bound_regions<'cx>(
+ &'cx self,
constrained_regions: FxHashSet,
referenced_regions: FxHashSet,
- generate_err: impl Fn(&str) -> Diag<'tcx>,
+ generate_err: impl Fn(&str) -> Diag<'cx>,
) {
for br in referenced_regions.difference(&constrained_regions) {
let br_name = match *br {
@@ -2443,7 +2418,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
err.note("consider introducing a named lifetime parameter");
}
- self.set_tainted_by_errors(err.emit());
+ err.emit();
}
}
@@ -2484,7 +2459,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
// error.
let r = derived_region_bounds[0];
if derived_region_bounds[1..].iter().any(|r1| r != *r1) {
- self.set_tainted_by_errors(tcx.dcx().emit_err(AmbiguousLifetimeBound { span }));
+ self.dcx().emit_err(AmbiguousLifetimeBound { span });
}
Some(r)
}
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/object_safety.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/object_safety.rs
index df69c1938dd7..aafadc7f9cbe 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/object_safety.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/object_safety.rs
@@ -13,7 +13,7 @@ use rustc_middle::ty::fold::BottomUpFolder;
use rustc_middle::ty::{self, ExistentialPredicateStableCmpExt as _, Ty, TyCtxt, TypeFoldable};
use rustc_middle::ty::{DynKind, Upcast};
use rustc_span::{ErrorGuaranteed, Span};
-use rustc_trait_selection::traits::error_reporting::report_object_safety_error;
+use rustc_trait_selection::error_reporting::traits::report_object_safety_error;
use rustc_trait_selection::traits::{self, hir_ty_lowering_object_safety_violations};
use smallvec::{smallvec, SmallVec};
@@ -236,7 +236,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
Ty::new_misc_error(tcx).into()
} else if arg.walk().any(|arg| arg == dummy_self.into()) {
references_self = true;
- let guar = tcx.dcx().span_delayed_bug(
+ let guar = self.dcx().span_delayed_bug(
span,
"trait object trait bounds reference `Self`",
);
@@ -262,8 +262,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
if references_self {
let def_id = i.bottom().0.def_id();
- let reported = struct_span_code_err!(
- tcx.dcx(),
+ struct_span_code_err!(
+ self.dcx(),
i.bottom().1,
E0038,
"the {} `{}` cannot be made into an object",
@@ -275,7 +275,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
.error_msg(),
)
.emit();
- self.set_tainted_by_errors(reported);
}
ty::ExistentialTraitRef { def_id: trait_ref.def_id, args }
diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs
index f3ce3ab66553..5b8b6e981250 100644
--- a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs
+++ b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs
@@ -78,7 +78,7 @@ use rustc_middle::ty::trait_def::TraitSpecializationKind;
use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt};
use rustc_middle::ty::{GenericArg, GenericArgs, GenericArgsRef};
use rustc_span::{ErrorGuaranteed, Span};
-use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
+use rustc_trait_selection::error_reporting::traits::TypeErrCtxtExt;
use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _;
use rustc_trait_selection::traits::{self, translate_args_with_cause, wf, ObligationCtxt};
diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs
index 2f6b0a582e58..dd7fbba753bd 100644
--- a/compiler/rustc_hir_analysis/src/lib.rs
+++ b/compiler/rustc_hir_analysis/src/lib.rs
@@ -92,7 +92,6 @@ mod errors;
pub mod hir_wf_check;
mod impl_wf_check;
mod outlives;
-pub mod structured_errors;
mod variance;
use rustc_hir as hir;
diff --git a/compiler/rustc_hir_analysis/src/structured_errors.rs b/compiler/rustc_hir_analysis/src/structured_errors.rs
deleted file mode 100644
index 61a2400f9e43..000000000000
--- a/compiler/rustc_hir_analysis/src/structured_errors.rs
+++ /dev/null
@@ -1,33 +0,0 @@
-mod missing_cast_for_variadic_arg;
-mod wrong_number_of_generic_args;
-
-pub use self::{missing_cast_for_variadic_arg::*, wrong_number_of_generic_args::*};
-
-use rustc_errors::{Diag, ErrCode};
-use rustc_session::Session;
-
-pub trait StructuredDiag<'tcx> {
- fn session(&self) -> &Session;
-
- fn code(&self) -> ErrCode;
-
- fn diagnostic(&self) -> Diag<'tcx> {
- let err = self.diagnostic_common();
-
- if self.session().teach(self.code()) {
- self.diagnostic_extended(err)
- } else {
- self.diagnostic_regular(err)
- }
- }
-
- fn diagnostic_common(&self) -> Diag<'tcx>;
-
- fn diagnostic_regular(&self, err: Diag<'tcx>) -> Diag<'tcx> {
- err
- }
-
- fn diagnostic_extended(&self, err: Diag<'tcx>) -> Diag<'tcx> {
- err
- }
-}
diff --git a/compiler/rustc_hir_analysis/src/structured_errors/missing_cast_for_variadic_arg.rs b/compiler/rustc_hir_analysis/src/structured_errors/missing_cast_for_variadic_arg.rs
deleted file mode 100644
index 0e78acbeae2d..000000000000
--- a/compiler/rustc_hir_analysis/src/structured_errors/missing_cast_for_variadic_arg.rs
+++ /dev/null
@@ -1,57 +0,0 @@
-use crate::{errors, structured_errors::StructuredDiag};
-use rustc_errors::{codes::*, Diag};
-use rustc_middle::ty::{Ty, TypeVisitableExt};
-use rustc_session::Session;
-use rustc_span::Span;
-
-pub struct MissingCastForVariadicArg<'tcx, 's> {
- pub sess: &'tcx Session,
- pub span: Span,
- pub ty: Ty<'tcx>,
- pub cast_ty: &'s str,
-}
-
-impl<'tcx> StructuredDiag<'tcx> for MissingCastForVariadicArg<'tcx, '_> {
- fn session(&self) -> &Session {
- self.sess
- }
-
- fn code(&self) -> ErrCode {
- E0617
- }
-
- fn diagnostic_common(&self) -> Diag<'tcx> {
- let (sugg_span, replace, help) =
- if let Ok(snippet) = self.sess.source_map().span_to_snippet(self.span) {
- (Some(self.span), format!("{} as {}", snippet, self.cast_ty), None)
- } else {
- (None, "".to_string(), Some(()))
- };
-
- let mut err = self.sess.dcx().create_err(errors::PassToVariadicFunction {
- span: self.span,
- ty: self.ty,
- cast_ty: self.cast_ty,
- help,
- replace,
- sugg_span,
- });
-
- if self.ty.references_error() {
- err.downgrade_to_delayed_bug();
- }
-
- err
- }
-
- fn diagnostic_extended(&self, mut err: Diag<'tcx>) -> Diag<'tcx> {
- err.note(format!(
- "certain types, like `{}`, must be casted before passing them to a \
- variadic function, because of arcane ABI rules dictated by the C \
- standard",
- self.ty
- ));
-
- err
- }
-}
diff --git a/compiler/rustc_hir_typeck/messages.ftl b/compiler/rustc_hir_typeck/messages.ftl
index 239b0c446902..39d430cf73b7 100644
--- a/compiler/rustc_hir_typeck/messages.ftl
+++ b/compiler/rustc_hir_typeck/messages.ftl
@@ -38,6 +38,7 @@ hir_typeck_cast_thin_pointer_to_fat_pointer = cannot cast thin pointer `{$expr_t
For more information about casts, take a look at The Book:
https://doc.rust-lang.org/reference/expressions/operator-expr.html#type-cast-expressions",
+
hir_typeck_cast_unknown_pointer = cannot cast {$to ->
[true] to
*[false] from
@@ -138,6 +139,11 @@ hir_typeck_option_result_asref = use `{$def_path}::as_ref` to convert `{$expecte
hir_typeck_option_result_cloned = use `{$def_path}::cloned` to clone the value inside the `{$def_path}`
hir_typeck_option_result_copied = use `{$def_path}::copied` to copy the value inside the `{$def_path}`
+hir_typeck_pass_to_variadic_function = can't pass `{$ty}` to variadic function
+ .suggestion = cast the value to `{$cast_ty}`
+ .help = cast the value to `{$cast_ty}`
+ .teach_help = certain types, like `{$ty}`, must be casted before passing them to a variadic function, because of arcane ABI rules dictated by the C standard
+
hir_typeck_ptr_cast_add_auto_to_object = adding {$traits_len ->
[1] an auto trait {$traits}
*[other] auto traits {$traits}
diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs
index 3b199b7e3c26..0d2a55a9507c 100644
--- a/compiler/rustc_hir_typeck/src/callee.rs
+++ b/compiler/rustc_hir_typeck/src/callee.rs
@@ -24,8 +24,8 @@ use rustc_span::def_id::LocalDefId;
use rustc_span::symbol::{sym, Ident};
use rustc_span::Span;
use rustc_target::spec::abi;
+use rustc_trait_selection::error_reporting::traits::DefIdOrName;
use rustc_trait_selection::infer::InferCtxtExt as _;
-use rustc_trait_selection::traits::error_reporting::DefIdOrName;
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
use std::{iter, slice};
diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs
index 53cf121dfebc..08de871f6fa9 100644
--- a/compiler/rustc_hir_typeck/src/closure.rs
+++ b/compiler/rustc_hir_typeck/src/closure.rs
@@ -17,9 +17,9 @@ use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor};
use rustc_span::def_id::LocalDefId;
use rustc_span::Span;
use rustc_target::spec::abi::Abi;
+use rustc_trait_selection::error_reporting::traits::ArgKind;
+use rustc_trait_selection::error_reporting::traits::InferCtxtExt as _;
use rustc_trait_selection::traits;
-use rustc_trait_selection::traits::error_reporting::ArgKind;
-use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _;
use rustc_type_ir::ClosureKind;
use std::iter;
use std::ops::ControlFlow;
diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs
index 4f0a089ee956..5e2a68e1f02d 100644
--- a/compiler/rustc_hir_typeck/src/coercion.rs
+++ b/compiler/rustc_hir_typeck/src/coercion.rs
@@ -58,9 +58,9 @@ use rustc_session::parse::feature_err;
use rustc_span::symbol::sym;
use rustc_span::{BytePos, DesugaringKind, Span, DUMMY_SP};
use rustc_target::spec::abi::Abi;
+use rustc_trait_selection::error_reporting::traits::suggestions::TypeErrCtxtExt;
+use rustc_trait_selection::error_reporting::traits::TypeErrCtxtSelectionErrExt as _;
use rustc_trait_selection::infer::InferCtxtExt as _;
-use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt;
-use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _;
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
use rustc_trait_selection::traits::{
self, NormalizeExt, ObligationCause, ObligationCauseCode, ObligationCtxt,
diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs
index 8b5b0a9b92f2..9a38d6d4a719 100644
--- a/compiler/rustc_hir_typeck/src/errors.rs
+++ b/compiler/rustc_hir_typeck/src/errors.rs
@@ -708,3 +708,19 @@ pub(crate) struct CastThinPointerToFatPointer<'tcx> {
#[note(hir_typeck_teach_help)]
pub(crate) teach: Option<()>,
}
+
+#[derive(Diagnostic)]
+#[diag(hir_typeck_pass_to_variadic_function, code = E0617)]
+pub(crate) struct PassToVariadicFunction<'tcx, 'a> {
+ #[primary_span]
+ pub span: Span,
+ pub ty: Ty<'tcx>,
+ pub cast_ty: &'a str,
+ #[suggestion(code = "{replace}", applicability = "machine-applicable")]
+ pub sugg_span: Option,
+ pub replace: String,
+ #[help]
+ pub help: Option<()>,
+ #[note(hir_typeck_teach_help)]
+ pub(crate) teach: Option<()>,
+}
diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs
index bd5e5294983d..035a3429ed76 100644
--- a/compiler/rustc_hir_typeck/src/expr.rs
+++ b/compiler/rustc_hir_typeck/src/expr.rs
@@ -53,12 +53,14 @@ use rustc_span::source_map::Spanned;
use rustc_span::symbol::{kw, sym, Ident, Symbol};
use rustc_span::Span;
use rustc_target::abi::{FieldIdx, FIRST_VARIANT};
+use rustc_trait_selection::error_reporting::traits::suggestions::TypeErrCtxtExt as _;
+use rustc_trait_selection::error_reporting::traits::TypeErrCtxtExt;
use rustc_trait_selection::infer::InferCtxtExt;
-use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt as _;
-use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
use rustc_trait_selection::traits::ObligationCtxt;
use rustc_trait_selection::traits::{self, ObligationCauseCode};
+use smallvec::SmallVec;
+
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
pub fn check_expr_has_type_or_error(
&self,
@@ -2177,10 +2179,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
skip_fields: &[hir::ExprField<'_>],
kind_name: &str,
) -> ErrorGuaranteed {
- if variant.is_recovered() {
- let guar =
- self.dcx().span_delayed_bug(expr.span, "parser recovered but no error was emitted");
- self.set_tainted_by_errors(guar);
+ // we don't care to report errors for a struct if the struct itself is tainted
+ if let Err(guar) = variant.has_errors() {
return guar;
}
let mut err = self.err_ctxt().type_error_struct_with_diag(
@@ -2320,6 +2320,44 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
display
}
+ /// Find the position of a field named `ident` in `base_def`, accounting for unnammed fields.
+ /// Return whether such a field has been found. The path to it is stored in `nested_fields`.
+ /// `ident` must have been adjusted beforehand.
+ fn find_adt_field(
+ &self,
+ base_def: ty::AdtDef<'tcx>,
+ ident: Ident,
+ nested_fields: &mut SmallVec<[(FieldIdx, &'tcx ty::FieldDef); 1]>,
+ ) -> bool {
+ // No way to find a field in an enum.
+ if base_def.is_enum() {
+ return false;
+ }
+
+ for (field_idx, field) in base_def.non_enum_variant().fields.iter_enumerated() {
+ if field.is_unnamed() {
+ // We have an unnamed field, recurse into the nested ADT to find `ident`.
+ // If we find it there, return immediately, and `nested_fields` will contain the
+ // correct path.
+ nested_fields.push((field_idx, field));
+
+ let field_ty = self.tcx.type_of(field.did).instantiate_identity();
+ let adt_def = field_ty.ty_adt_def().expect("expect Adt for unnamed field");
+ if self.find_adt_field(adt_def, ident, &mut *nested_fields) {
+ return true;
+ }
+
+ nested_fields.pop();
+ } else if field.ident(self.tcx).normalize_to_macros_2_0() == ident {
+ // We found the field we wanted.
+ nested_fields.push((field_idx, field));
+ return true;
+ }
+ }
+
+ false
+ }
+
// Check field access expressions
fn check_field(
&self,
@@ -2341,39 +2379,44 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let body_hir_id = self.tcx.local_def_id_to_hir_id(self.body_id);
let (ident, def_scope) =
self.tcx.adjust_ident_and_get_scope(field, base_def.did(), body_hir_id);
- let mut adt_def = *base_def;
- let mut last_ty = None;
- let mut nested_fields = Vec::new();
- let mut index = None;
- while let Some(idx) = self.tcx.find_field((adt_def.did(), ident)) {
- let &mut first_idx = index.get_or_insert(idx);
- let field = &adt_def.non_enum_variant().fields[idx];
- let field_ty = self.field_ty(expr.span, field, args);
- if let Some(ty) = last_ty {
- nested_fields.push((ty, idx));
- }
- if field.ident(self.tcx).normalize_to_macros_2_0() == ident {
- // Save the index of all fields regardless of their visibility in case
- // of error recovery.
- self.write_field_index(expr.hir_id, first_idx, nested_fields);
- let adjustments = self.adjust_steps(&autoderef);
- if field.vis.is_accessible_from(def_scope, self.tcx) {
- self.apply_adjustments(base, adjustments);
- self.register_predicates(autoderef.into_obligations());
- self.tcx.check_stability(
- field.did,
- Some(expr.hir_id),
- expr.span,
- None,
- );
- return field_ty;
- }
- private_candidate = Some((adjustments, base_def.did()));
- break;
+ // we don't care to report errors for a struct if the struct itself is tainted
+ if let Err(guar) = base_def.non_enum_variant().has_errors() {
+ return Ty::new_error(self.tcx(), guar);
+ }
+
+ let mut field_path = SmallVec::new();
+ if self.find_adt_field(*base_def, ident, &mut field_path) {
+ let (first_idx, _) = field_path[0];
+ let (_, last_field) = field_path.last().unwrap();
+
+ // Save the index of all fields regardless of their visibility in case
+ // of error recovery.
+ let nested_fields = field_path[..]
+ .array_windows()
+ .map(|[(_, outer), (inner_idx, _)]| {
+ let outer_ty = self.field_ty(expr.span, outer, args);
+ (outer_ty, *inner_idx)
+ })
+ .collect();
+ self.write_field_index(expr.hir_id, first_idx, nested_fields);
+
+ let adjustments = self.adjust_steps(&autoderef);
+ if last_field.vis.is_accessible_from(def_scope, self.tcx) {
+ self.apply_adjustments(base, adjustments);
+ self.register_predicates(autoderef.into_obligations());
+
+ self.tcx.check_stability(
+ last_field.did,
+ Some(expr.hir_id),
+ expr.span,
+ None,
+ );
+ return self.field_ty(expr.span, last_field, args);
}
- last_ty = Some(field_ty);
- adt_def = field_ty.ty_adt_def().expect("expect Adt for unnamed field");
+
+ // The field is not accessible, fall through to error reporting.
+ private_candidate = Some((adjustments, base_def.did()));
}
}
ty::Tuple(tys) => {
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
index 5450243417f7..ea9567f4e3dc 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
@@ -37,7 +37,7 @@ use rustc_span::hygiene::DesugaringKind;
use rustc_span::symbol::{kw, sym};
use rustc_span::Span;
use rustc_target::abi::FieldIdx;
-use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _;
+use rustc_trait_selection::error_reporting::traits::TypeErrCtxtExt as _;
use rustc_trait_selection::traits::{
self, NormalizeExt, ObligationCauseCode, ObligationCtxt, StructurallyNormalizeExt,
};
@@ -1139,7 +1139,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// parameter's value explicitly, so we have to do some error-
// checking here.
let arg_count =
- check_generic_arg_count_for_call(tcx, def_id, generics, seg, IsMethodCall::No);
+ check_generic_arg_count_for_call(self, def_id, generics, seg, IsMethodCall::No);
if let ExplicitLateBound::Yes = arg_count.explicit_late_bound {
explicit_late_bound = ExplicitLateBound::Yes;
@@ -1375,7 +1375,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let args_raw = self_ctor_args.unwrap_or_else(|| {
lower_generic_args(
- tcx,
+ self,
def_id,
&[],
has_self,
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
index 10092dce5876..9fbb01216bbb 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
@@ -28,7 +28,6 @@ use rustc_hir::{ExprKind, HirId, Node, QPath};
use rustc_hir_analysis::check::intrinsicck::InlineAsmCtxt;
use rustc_hir_analysis::check::potentially_plural_count;
use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer;
-use rustc_hir_analysis::structured_errors::StructuredDiag;
use rustc_index::IndexVec;
use rustc_infer::infer::error_reporting::{FailureCode, ObligationCauseExt};
use rustc_infer::infer::TypeTrace;
@@ -406,9 +405,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
ty: Ty<'tcx>,
cast_ty: &str,
) {
- use rustc_hir_analysis::structured_errors::MissingCastForVariadicArg;
+ let (sugg_span, replace, help) =
+ if let Ok(snippet) = sess.source_map().span_to_snippet(span) {
+ (Some(span), format!("{snippet} as {cast_ty}"), None)
+ } else {
+ (None, "".to_string(), Some(()))
+ };
- MissingCastForVariadicArg { sess, span, ty, cast_ty }.diagnostic().emit();
+ sess.dcx().emit_err(errors::PassToVariadicFunction {
+ span,
+ ty,
+ cast_ty,
+ help,
+ replace,
+ sugg_span,
+ teach: sess.teach(E0617).then_some(()),
+ });
}
// There are a few types which get autopromoted when passed via varargs
@@ -936,6 +948,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
&mut err,
);
+ self.suggest_deref_unwrap_or(
+ &mut err,
+ error_span,
+ callee_ty,
+ call_ident,
+ expected_ty,
+ provided_ty,
+ provided_args[*provided_idx],
+ is_method,
+ );
+
// Call out where the function is defined
self.label_fn_like(
&mut err,
@@ -2542,7 +2565,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
.and_then(|node| node.generics())
.into_iter()
.flat_map(|generics| generics.params)
- .find(|gen| &gen.def_id.to_def_id() == res_def_id)
+ .find(|param| ¶m.def_id.to_def_id() == res_def_id)
} else {
None
}
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
index da68da029b41..b5796fbd48a8 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
@@ -5,7 +5,7 @@ mod checks;
mod inspect_obligations;
mod suggestions;
-use rustc_errors::{DiagCtxtHandle, ErrorGuaranteed};
+use rustc_errors::DiagCtxtHandle;
use crate::coercion::DynamicCoerceMany;
use crate::fallback::DivergingFallbackBehavior;
@@ -217,6 +217,10 @@ impl<'tcx> HirTyLowerer<'tcx> for FnCtxt<'_, 'tcx> {
self.tcx
}
+ fn dcx(&self) -> DiagCtxtHandle<'_> {
+ self.root_ctxt.dcx()
+ }
+
fn item_def_id(&self) -> LocalDefId {
self.body_id
}
@@ -338,10 +342,6 @@ impl<'tcx> HirTyLowerer<'tcx> for FnCtxt<'_, 'tcx> {
Some(&self.infcx)
}
- fn set_tainted_by_errors(&self, e: ErrorGuaranteed) {
- self.infcx.set_tainted_by_errors(e)
- }
-
fn lower_fn_sig(
&self,
decl: &rustc_hir::FnDecl<'tcx>,
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
index fca0a3e195d7..b3b4c5a56fbd 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
@@ -32,10 +32,10 @@ use rustc_session::errors::ExprParenthesesNeeded;
use rustc_span::source_map::Spanned;
use rustc_span::symbol::{sym, Ident};
use rustc_span::{Span, Symbol};
+use rustc_trait_selection::error_reporting::traits::suggestions::TypeErrCtxtExt;
+use rustc_trait_selection::error_reporting::traits::DefIdOrName;
use rustc_trait_selection::infer::InferCtxtExt;
use rustc_trait_selection::traits;
-use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt;
-use rustc_trait_selection::traits::error_reporting::DefIdOrName;
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
@@ -466,21 +466,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
borrow_removal_span,
});
return true;
- } else if let Some((deref_ty, _)) =
- self.autoderef(expr.span, found_ty_inner).silence_errors().nth(1)
- && self.can_eq(self.param_env, deref_ty, peeled)
- && error_tys_equate_as_ref
- {
- let sugg = prefix_wrap(".as_deref()");
- err.subdiagnostic(errors::SuggestConvertViaMethod {
- span: expr.span.shrink_to_hi(),
- sugg,
- expected,
- found,
- borrow_removal_span,
- });
- return true;
- } else if let ty::Adt(adt, _) = found_ty_inner.peel_refs().kind()
+ } else if let ty::Ref(_, peeled_found_ty, _) = found_ty_inner.kind()
+ && let ty::Adt(adt, _) = peeled_found_ty.peel_refs().kind()
&& self.tcx.is_lang_item(adt.did(), LangItem::String)
&& peeled.is_str()
// `Result::map`, conversely, does not take ref of the error type.
@@ -496,12 +483,47 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
Applicability::MachineApplicable,
);
return true;
+ } else {
+ if !error_tys_equate_as_ref {
+ return false;
+ }
+ let mut steps = self.autoderef(expr.span, found_ty_inner).silence_errors();
+ if let Some((deref_ty, _)) = steps.nth(1)
+ && self.can_eq(self.param_env, deref_ty, peeled)
+ {
+ let sugg = prefix_wrap(".as_deref()");
+ err.subdiagnostic(errors::SuggestConvertViaMethod {
+ span: expr.span.shrink_to_hi(),
+ sugg,
+ expected,
+ found,
+ borrow_removal_span,
+ });
+ return true;
+ }
+ for (deref_ty, n_step) in steps {
+ if self.can_eq(self.param_env, deref_ty, peeled) {
+ let explicit_deref = "*".repeat(n_step);
+ let sugg = prefix_wrap(&format!(".map(|v| &{explicit_deref}v)"));
+ err.subdiagnostic(errors::SuggestConvertViaMethod {
+ span: expr.span.shrink_to_hi(),
+ sugg,
+ expected,
+ found,
+ borrow_removal_span,
+ });
+ return true;
+ }
+ }
}
}
false
}
+ /// If `ty` is `Option`, returns `T, T, None`.
+ /// If `ty` is `Result`, returns `T, T, Some(E, E)`.
+ /// Otherwise, returns `None`.
fn deconstruct_option_or_result(
&self,
found_ty: Ty<'tcx>,
@@ -1407,6 +1429,74 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
true
}
+ // Suggest to change `Option<&Vec>::unwrap_or(&[])` to `Option::map_or(&[], |v| v)`.
+ #[instrument(level = "trace", skip(self, err, provided_expr))]
+ pub(crate) fn suggest_deref_unwrap_or(
+ &self,
+ err: &mut Diag<'_>,
+ error_span: Span,
+ callee_ty: Option>,
+ call_ident: Option,
+ expected_ty: Ty<'tcx>,
+ provided_ty: Ty<'tcx>,
+ provided_expr: &Expr<'tcx>,
+ is_method: bool,
+ ) {
+ if !is_method {
+ return;
+ }
+ let Some(callee_ty) = callee_ty else {
+ return;
+ };
+ let ty::Adt(callee_adt, _) = callee_ty.peel_refs().kind() else {
+ return;
+ };
+ let adt_name = if self.tcx.is_diagnostic_item(sym::Option, callee_adt.did()) {
+ "Option"
+ } else if self.tcx.is_diagnostic_item(sym::Result, callee_adt.did()) {
+ "Result"
+ } else {
+ return;
+ };
+
+ let Some(call_ident) = call_ident else {
+ return;
+ };
+ if call_ident.name != sym::unwrap_or {
+ return;
+ }
+
+ let ty::Ref(_, peeled, _mutability) = provided_ty.kind() else {
+ return;
+ };
+
+ // NOTE: Can we reuse `suggest_deref_or_ref`?
+
+ // Create an dummy type `&[_]` so that both &[] and `&Vec` can coerce to it.
+ let dummy_ty = if let ty::Array(elem_ty, size) = peeled.kind()
+ && let ty::Infer(_) = elem_ty.kind()
+ && size.try_eval_target_usize(self.tcx, self.param_env) == Some(0)
+ {
+ let slice = Ty::new_slice(self.tcx, *elem_ty);
+ Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_static, slice)
+ } else {
+ provided_ty
+ };
+
+ if !self.can_coerce(expected_ty, dummy_ty) {
+ return;
+ }
+ let msg = format!("use `{adt_name}::map_or` to deref inner value of `{adt_name}`");
+ err.multipart_suggestion_verbose(
+ msg,
+ vec![
+ (call_ident.span, "map_or".to_owned()),
+ (provided_expr.span.shrink_to_hi(), ", |v| v".to_owned()),
+ ],
+ Applicability::MachineApplicable,
+ );
+ }
+
/// Suggest wrapping the block in square brackets instead of curly braces
/// in case the block was mistaken array syntax, e.g. `{ 1 }` -> `[ 1 ]`.
pub(crate) fn suggest_block_to_brackets(
diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs
index a87ee7b45548..bdbdcee6446d 100644
--- a/compiler/rustc_hir_typeck/src/lib.rs
+++ b/compiler/rustc_hir_typeck/src/lib.rs
@@ -1,6 +1,7 @@
// tidy-alphabetical-start
#![allow(rustc::diagnostic_outside_of_impl)]
#![allow(rustc::untranslatable_diagnostic)]
+#![feature(array_windows)]
#![feature(box_patterns)]
#![feature(control_flow_enum)]
#![feature(if_let_guard)]
diff --git a/compiler/rustc_hir_typeck/src/method/confirm.rs b/compiler/rustc_hir_typeck/src/method/confirm.rs
index 02844bcadac6..e574fde14fb0 100644
--- a/compiler/rustc_hir_typeck/src/method/confirm.rs
+++ b/compiler/rustc_hir_typeck/src/method/confirm.rs
@@ -354,7 +354,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
let generics = self.tcx.generics_of(pick.item.def_id);
let arg_count_correct = check_generic_arg_count_for_call(
- self.tcx,
+ self.fcx,
pick.item.def_id,
generics,
seg,
@@ -425,7 +425,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
}
let args = lower_generic_args(
- self.tcx,
+ self.fcx,
pick.item.def_id,
parent_args,
false,
diff --git a/compiler/rustc_hir_typeck/src/method/mod.rs b/compiler/rustc_hir_typeck/src/method/mod.rs
index ff8899ae0368..dc1b888374cf 100644
--- a/compiler/rustc_hir_typeck/src/method/mod.rs
+++ b/compiler/rustc_hir_typeck/src/method/mod.rs
@@ -21,7 +21,7 @@ use rustc_middle::ty::{self, GenericParamDefKind, Ty, TypeVisitableExt};
use rustc_middle::ty::{GenericArgs, GenericArgsRef};
use rustc_middle::{bug, span_bug};
use rustc_span::symbol::Ident;
-use rustc_span::{sym, Span};
+use rustc_span::Span;
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
use rustc_trait_selection::traits::{self, NormalizeExt};
@@ -359,7 +359,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// FIXME(effects) find a better way to do this
// Operators don't have generic methods, but making them `#[const_trait]` gives them
// `const host: bool`.
- let args = if self.tcx.has_attr(trait_def_id, sym::const_trait) {
+ let args = if self.tcx.is_const_trait(trait_def_id) {
self.tcx.mk_args_from_iter(
args.iter()
.chain([self.tcx.expected_host_effect_param_for_body(self.body_id).into()]),
diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs
index dba3edbc1d72..425289ce3c52 100644
--- a/compiler/rustc_hir_typeck/src/method/suggest.rs
+++ b/compiler/rustc_hir_typeck/src/method/suggest.rs
@@ -35,9 +35,9 @@ use rustc_span::def_id::DefIdSet;
use rustc_span::symbol::{kw, sym, Ident};
use rustc_span::{edit_distance, ErrorGuaranteed, ExpnKind, FileName, MacroKind, Span};
use rustc_span::{Symbol, DUMMY_SP};
+use rustc_trait_selection::error_reporting::traits::on_unimplemented::OnUnimplementedNote;
+use rustc_trait_selection::error_reporting::traits::on_unimplemented::TypeErrCtxtExt as _;
use rustc_trait_selection::infer::InferCtxtExt;
-use rustc_trait_selection::traits::error_reporting::on_unimplemented::OnUnimplementedNote;
-use rustc_trait_selection::traits::error_reporting::on_unimplemented::TypeErrCtxtExt as _;
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
use rustc_trait_selection::traits::{
supertraits, FulfillmentError, Obligation, ObligationCause, ObligationCauseCode,
diff --git a/compiler/rustc_hir_typeck/src/op.rs b/compiler/rustc_hir_typeck/src/op.rs
index 7264bc5a78d8..d59b8276d3ad 100644
--- a/compiler/rustc_hir_typeck/src/op.rs
+++ b/compiler/rustc_hir_typeck/src/op.rs
@@ -18,8 +18,8 @@ use rustc_session::errors::ExprParenthesesNeeded;
use rustc_span::source_map::Spanned;
use rustc_span::symbol::{sym, Ident};
use rustc_span::Span;
+use rustc_trait_selection::error_reporting::traits::suggestions::TypeErrCtxtExt as _;
use rustc_trait_selection::infer::InferCtxtExt;
-use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt as _;
use rustc_trait_selection::traits::{FulfillmentError, ObligationCtxt};
use rustc_type_ir::TyKind::*;
diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs
index be526e1c26c4..6d1e9ff1f952 100644
--- a/compiler/rustc_hir_typeck/src/pat.rs
+++ b/compiler/rustc_hir_typeck/src/pat.rs
@@ -1531,9 +1531,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
.filter(|(_, ident)| !used_fields.contains_key(ident))
.collect::>();
- let inexistent_fields_err = if !(inexistent_fields.is_empty() || variant.is_recovered())
+ let inexistent_fields_err = if !inexistent_fields.is_empty()
&& !inexistent_fields.iter().any(|field| field.ident.name == kw::Underscore)
{
+ // we don't care to report errors for a struct if the struct itself is tainted
+ variant.has_errors()?;
Some(self.error_inexistent_fields(
adt.variant_descr(),
&inexistent_fields,
@@ -1812,6 +1814,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
return Ok(());
}
+ // we don't care to report errors for a struct if the struct itself is tainted
+ variant.has_errors()?;
+
let path = rustc_hir_pretty::qpath_to_string(&self.tcx, qpath);
let mut err = struct_span_code_err!(
self.dcx(),
diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs
index ceb7480686d9..e800c1a97d9e 100644
--- a/compiler/rustc_hir_typeck/src/writeback.rs
+++ b/compiler/rustc_hir_typeck/src/writeback.rs
@@ -18,8 +18,8 @@ use rustc_middle::ty::TypeSuperFoldable;
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_span::symbol::sym;
use rustc_span::Span;
+use rustc_trait_selection::error_reporting::traits::TypeErrCtxtExt;
use rustc_trait_selection::solve;
-use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
use std::mem;
diff --git a/compiler/rustc_infer/messages.ftl b/compiler/rustc_infer/messages.ftl
index fbe8d31370cc..7a5e71599203 100644
--- a/compiler/rustc_infer/messages.ftl
+++ b/compiler/rustc_infer/messages.ftl
@@ -221,6 +221,10 @@ infer_opaque_hidden_type =
infer_outlives_bound = lifetime of the source pointer does not outlive lifetime bound of the object type
infer_outlives_content = lifetime of reference outlives lifetime of borrowed content...
+
+infer_precise_capturing_existing = add `{$new_lifetime}` to the `use<...>` bound to explicitly capture it
+infer_precise_capturing_new = add a `use<...>` bound to explicitly capture `{$new_lifetime}`
+
infer_prlf_defined_with_sub = the lifetime `{$sub_symbol}` defined here...
infer_prlf_defined_without_sub = the lifetime defined here...
infer_prlf_known_limitation = this is a known limitation that will be removed in the future (see issue #100013 for more information)
diff --git a/compiler/rustc_infer/src/errors/mod.rs b/compiler/rustc_infer/src/errors/mod.rs
index a801001eaf98..ce1b0f86d034 100644
--- a/compiler/rustc_infer/src/errors/mod.rs
+++ b/compiler/rustc_infer/src/errors/mod.rs
@@ -1581,3 +1581,32 @@ pub enum ObligationCauseFailureCode {
subdiags: Vec,
},
}
+
+#[derive(Subdiagnostic)]
+pub enum AddPreciseCapturing {
+ #[suggestion(
+ infer_precise_capturing_new,
+ style = "verbose",
+ code = " + use<{concatenated_bounds}>",
+ applicability = "machine-applicable"
+ )]
+ New {
+ #[primary_span]
+ span: Span,
+ new_lifetime: Symbol,
+ concatenated_bounds: String,
+ },
+ #[suggestion(
+ infer_precise_capturing_existing,
+ style = "verbose",
+ code = "{pre}{new_lifetime}{post}",
+ applicability = "machine-applicable"
+ )]
+ Existing {
+ #[primary_span]
+ span: Span,
+ new_lifetime: Symbol,
+ pre: &'static str,
+ post: &'static str,
+ },
+}
diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
index cebb9d09a479..bb1285ee8135 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
@@ -45,13 +45,10 @@
//! ported to this system, and which relies on string concatenation at the
//! time of error detection.
-use super::lexical_region_resolve::RegionResolutionError;
-use super::region_constraints::GenericKind;
-use super::{InferCtxt, RegionVariableOrigin, SubregionOrigin, TypeTrace, ValuePairs};
+use super::{InferCtxt, TypeTrace, ValuePairs};
-use crate::errors::{self, ObligationCauseFailureCode, TypeErrorAdditionalDiags};
+use crate::errors::{ObligationCauseFailureCode, TypeErrorAdditionalDiags};
use crate::infer;
-use crate::infer::error_reporting::nice_region_error::find_anon_type::find_anon_type;
use crate::infer::ExpectedFound;
use crate::traits::{
IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode,
@@ -61,38 +58,36 @@ use crate::traits::{
use crate::infer::relate::{self, RelateResult, TypeRelation};
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
use rustc_errors::{
- codes::*, pluralize, struct_span_code_err, Applicability, Diag, DiagCtxtHandle,
- DiagStyledString, ErrorGuaranteed, IntoDiagArg, StringPart,
+ pluralize, Applicability, Diag, DiagCtxtHandle, DiagStyledString, IntoDiagArg, StringPart,
};
use rustc_hir::def::DefKind;
-use rustc_hir::def_id::{DefId, LocalDefId};
+use rustc_hir::def_id::DefId;
use rustc_hir::intravisit::Visitor;
use rustc_hir::lang_items::LangItem;
-use rustc_hir::{self as hir, ParamName};
+use rustc_hir::{self as hir};
use rustc_macros::extension;
use rustc_middle::bug;
use rustc_middle::dep_graph::DepContext;
use rustc_middle::ty::error::TypeErrorToStringExt;
use rustc_middle::ty::print::{with_forced_trimmed_paths, PrintError, PrintTraitRefExt as _};
-use rustc_middle::ty::Upcast;
use rustc_middle::ty::{
- self, error::TypeError, IsSuggestable, List, Region, Ty, TyCtxt, TypeFoldable,
- TypeSuperVisitable, TypeVisitable, TypeVisitableExt,
+ self, error::TypeError, List, Region, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable,
+ TypeVisitable, TypeVisitableExt,
};
-use rustc_span::{sym, symbol::kw, BytePos, DesugaringKind, Pos, Span};
+use rustc_span::{sym, BytePos, DesugaringKind, Pos, Span};
use rustc_target::spec::abi;
use std::borrow::Cow;
use std::ops::{ControlFlow, Deref};
use std::path::PathBuf;
use std::{cmp, fmt, iter};
-mod note;
mod note_and_explain;
mod suggest;
pub(crate) mod need_type_info;
pub mod sub_relations;
pub use need_type_info::TypeAnnotationNeeded;
+pub mod region;
pub mod nice_region_error;
@@ -159,245 +154,6 @@ impl<'tcx> Deref for TypeErrCtxt<'_, 'tcx> {
}
}
-pub(super) fn note_and_explain_region<'tcx>(
- tcx: TyCtxt<'tcx>,
- err: &mut Diag<'_>,
- generic_param_scope: LocalDefId,
- prefix: &str,
- region: ty::Region<'tcx>,
- suffix: &str,
- alt_span: Option,
-) {
- let (description, span) = match *region {
- ty::ReEarlyParam(_) | ty::ReLateParam(_) | ty::RePlaceholder(_) | ty::ReStatic => {
- msg_span_from_named_region(tcx, generic_param_scope, region, alt_span)
- }
-
- ty::ReError(_) => return,
-
- // FIXME(#125431): `ReVar` shouldn't reach here.
- ty::ReVar(_) => (format!("lifetime `{region}`"), alt_span),
-
- ty::ReBound(..) | ty::ReErased => {
- bug!("unexpected region for note_and_explain_region: {:?}", region);
- }
- };
-
- emit_msg_span(err, prefix, description, span, suffix);
-}
-
-fn explain_free_region<'tcx>(
- tcx: TyCtxt<'tcx>,
- err: &mut Diag<'_>,
- generic_param_scope: LocalDefId,
- prefix: &str,
- region: ty::Region<'tcx>,
- suffix: &str,
-) {
- let (description, span) = msg_span_from_named_region(tcx, generic_param_scope, region, None);
-
- label_msg_span(err, prefix, description, span, suffix);
-}
-
-fn msg_span_from_named_region<'tcx>(
- tcx: TyCtxt<'tcx>,
- generic_param_scope: LocalDefId,
- region: ty::Region<'tcx>,
- alt_span: Option,
-) -> (String, Option) {
- match *region {
- ty::ReEarlyParam(br) => {
- let scope = tcx
- .parent(tcx.generics_of(generic_param_scope).region_param(br, tcx).def_id)
- .expect_local();
- let span = if let Some(param) =
- tcx.hir().get_generics(scope).and_then(|generics| generics.get_named(br.name))
- {
- param.span
- } else {
- tcx.def_span(scope)
- };
- let text = if br.has_name() {
- format!("the lifetime `{}` as defined here", br.name)
- } else {
- "the anonymous lifetime as defined here".to_string()
- };
- (text, Some(span))
- }
- ty::ReLateParam(ref fr) => {
- if !fr.bound_region.is_named()
- && let Some((ty, _)) =
- find_anon_type(tcx, generic_param_scope, region, &fr.bound_region)
- {
- ("the anonymous lifetime defined here".to_string(), Some(ty.span))
- } else {
- match fr.bound_region {
- ty::BoundRegionKind::BrNamed(_, name) => {
- let span = if let Some(param) = tcx
- .hir()
- .get_generics(generic_param_scope)
- .and_then(|generics| generics.get_named(name))
- {
- param.span
- } else {
- tcx.def_span(generic_param_scope)
- };
- let text = if name == kw::UnderscoreLifetime {
- "the anonymous lifetime as defined here".to_string()
- } else {
- format!("the lifetime `{name}` as defined here")
- };
- (text, Some(span))
- }
- ty::BrAnon => (
- "the anonymous lifetime as defined here".to_string(),
- Some(tcx.def_span(generic_param_scope)),
- ),
- _ => (
- format!("the lifetime `{region}` as defined here"),
- Some(tcx.def_span(generic_param_scope)),
- ),
- }
- }
- }
- ty::ReStatic => ("the static lifetime".to_owned(), alt_span),
- ty::RePlaceholder(ty::PlaceholderRegion {
- bound: ty::BoundRegion { kind: ty::BoundRegionKind::BrNamed(def_id, name), .. },
- ..
- }) => (format!("the lifetime `{name}` as defined here"), Some(tcx.def_span(def_id))),
- ty::RePlaceholder(ty::PlaceholderRegion {
- bound: ty::BoundRegion { kind: ty::BoundRegionKind::BrAnon, .. },
- ..
- }) => ("an anonymous lifetime".to_owned(), None),
- _ => bug!("{:?}", region),
- }
-}
-
-fn emit_msg_span(
- err: &mut Diag<'_>,
- prefix: &str,
- description: String,
- span: Option,
- suffix: &str,
-) {
- let message = format!("{prefix}{description}{suffix}");
-
- if let Some(span) = span {
- err.span_note(span, message);
- } else {
- err.note(message);
- }
-}
-
-fn label_msg_span(
- err: &mut Diag<'_>,
- prefix: &str,
- description: String,
- span: Option,
- suffix: &str,
-) {
- let message = format!("{prefix}{description}{suffix}");
-
- if let Some(span) = span {
- err.span_label(span, message);
- } else {
- err.note(message);
- }
-}
-
-#[instrument(level = "trace", skip(infcx))]
-pub fn unexpected_hidden_region_diagnostic<'a, 'tcx>(
- infcx: &'a InferCtxt<'tcx>,
- generic_param_scope: LocalDefId,
- span: Span,
- hidden_ty: Ty<'tcx>,
- hidden_region: ty::Region<'tcx>,
- opaque_ty_key: ty::OpaqueTypeKey<'tcx>,
-) -> Diag<'a> {
- let tcx = infcx.tcx;
- let mut err = infcx.dcx().create_err(errors::OpaqueCapturesLifetime {
- span,
- opaque_ty: Ty::new_opaque(tcx, opaque_ty_key.def_id.to_def_id(), opaque_ty_key.args),
- opaque_ty_span: tcx.def_span(opaque_ty_key.def_id),
- });
-
- // Explain the region we are capturing.
- match *hidden_region {
- ty::ReEarlyParam(_) | ty::ReLateParam(_) | ty::ReStatic => {
- // Assuming regionck succeeded (*), we ought to always be
- // capturing *some* region from the fn header, and hence it
- // ought to be free. So under normal circumstances, we will go
- // down this path which gives a decent human readable
- // explanation.
- //
- // (*) if not, the `tainted_by_errors` field would be set to
- // `Some(ErrorGuaranteed)` in any case, so we wouldn't be here at all.
- explain_free_region(
- tcx,
- &mut err,
- generic_param_scope,
- &format!("hidden type `{hidden_ty}` captures "),
- hidden_region,
- "",
- );
- if let Some(reg_info) = tcx.is_suitable_region(generic_param_scope, hidden_region) {
- let fn_returns = tcx.return_type_impl_or_dyn_traits(reg_info.def_id);
- nice_region_error::suggest_new_region_bound(
- tcx,
- &mut err,
- fn_returns,
- hidden_region.to_string(),
- None,
- format!("captures `{hidden_region}`"),
- None,
- Some(reg_info.def_id),
- )
- }
- }
- ty::RePlaceholder(_) => {
- explain_free_region(
- tcx,
- &mut err,
- generic_param_scope,
- &format!("hidden type `{}` captures ", hidden_ty),
- hidden_region,
- "",
- );
- }
- ty::ReError(_) => {
- err.downgrade_to_delayed_bug();
- }
- _ => {
- // Ugh. This is a painful case: the hidden region is not one
- // that we can easily summarize or explain. This can happen
- // in a case like
- // `tests/ui/multiple-lifetimes/ordinary-bounds-unsuited.rs`:
- //
- // ```
- // fn upper_bounds<'a, 'b>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'a, 'b> {
- // if condition() { a } else { b }
- // }
- // ```
- //
- // Here the captured lifetime is the intersection of `'a` and
- // `'b`, which we can't quite express.
-
- // We can at least report a really cryptic error for now.
- note_and_explain_region(
- tcx,
- &mut err,
- generic_param_scope,
- &format!("hidden type `{hidden_ty}` captures "),
- hidden_region,
- "",
- None,
- );
- }
- }
-
- err
-}
-
impl<'tcx> InferCtxt<'tcx> {
pub fn get_impl_future_output_ty(&self, ty: Ty<'tcx>) -> Option> {
let (def_id, args) = match *ty.kind() {
@@ -438,193 +194,6 @@ impl<'tcx> InferCtxt<'tcx> {
}
impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
- pub fn report_region_errors(
- &self,
- generic_param_scope: LocalDefId,
- errors: &[RegionResolutionError<'tcx>],
- ) -> ErrorGuaranteed {
- assert!(!errors.is_empty());
-
- if let Some(guaranteed) = self.infcx.tainted_by_errors() {
- return guaranteed;
- }
-
- debug!("report_region_errors(): {} errors to start", errors.len());
-
- // try to pre-process the errors, which will group some of them
- // together into a `ProcessedErrors` group:
- let errors = self.process_errors(errors);
-
- debug!("report_region_errors: {} errors after preprocessing", errors.len());
-
- let mut guar = None;
- for error in errors {
- debug!("report_region_errors: error = {:?}", error);
-
- let e = if let Some(guar) =
- self.try_report_nice_region_error(generic_param_scope, &error)
- {
- guar
- } else {
- match error.clone() {
- // These errors could indicate all manner of different
- // problems with many different solutions. Rather
- // than generate a "one size fits all" error, what we
- // attempt to do is go through a number of specific
- // scenarios and try to find the best way to present
- // the error. If all of these fails, we fall back to a rather
- // general bit of code that displays the error information
- RegionResolutionError::ConcreteFailure(origin, sub, sup) => {
- if sub.is_placeholder() || sup.is_placeholder() {
- self.report_placeholder_failure(generic_param_scope, origin, sub, sup)
- .emit()
- } else {
- self.report_concrete_failure(generic_param_scope, origin, sub, sup)
- .emit()
- }
- }
-
- RegionResolutionError::GenericBoundFailure(origin, param_ty, sub) => self
- .report_generic_bound_failure(
- generic_param_scope,
- origin.span(),
- Some(origin),
- param_ty,
- sub,
- ),
-
- RegionResolutionError::SubSupConflict(
- _,
- var_origin,
- sub_origin,
- sub_r,
- sup_origin,
- sup_r,
- _,
- ) => {
- if sub_r.is_placeholder() {
- self.report_placeholder_failure(
- generic_param_scope,
- sub_origin,
- sub_r,
- sup_r,
- )
- .emit()
- } else if sup_r.is_placeholder() {
- self.report_placeholder_failure(
- generic_param_scope,
- sup_origin,
- sub_r,
- sup_r,
- )
- .emit()
- } else {
- self.report_sub_sup_conflict(
- generic_param_scope,
- var_origin,
- sub_origin,
- sub_r,
- sup_origin,
- sup_r,
- )
- }
- }
-
- RegionResolutionError::UpperBoundUniverseConflict(
- _,
- _,
- _,
- sup_origin,
- sup_r,
- ) => {
- assert!(sup_r.is_placeholder());
-
- // Make a dummy value for the "sub region" --
- // this is the initial value of the
- // placeholder. In practice, we expect more
- // tailored errors that don't really use this
- // value.
- let sub_r = self.tcx.lifetimes.re_erased;
-
- self.report_placeholder_failure(
- generic_param_scope,
- sup_origin,
- sub_r,
- sup_r,
- )
- .emit()
- }
-
- RegionResolutionError::CannotNormalize(clause, origin) => {
- let clause: ty::Clause<'tcx> =
- clause.map_bound(ty::ClauseKind::TypeOutlives).upcast(self.tcx);
- self.tcx
- .dcx()
- .struct_span_err(origin.span(), format!("cannot normalize `{clause}`"))
- .emit()
- }
- }
- };
-
- guar = Some(e)
- }
-
- guar.unwrap()
- }
-
- // This method goes through all the errors and try to group certain types
- // of error together, for the purpose of suggesting explicit lifetime
- // parameters to the user. This is done so that we can have a more
- // complete view of what lifetimes should be the same.
- // If the return value is an empty vector, it means that processing
- // failed (so the return value of this method should not be used).
- //
- // The method also attempts to weed out messages that seem like
- // duplicates that will be unhelpful to the end-user. But
- // obviously it never weeds out ALL errors.
- fn process_errors(
- &self,
- errors: &[RegionResolutionError<'tcx>],
- ) -> Vec> {
- debug!("process_errors()");
-
- // We want to avoid reporting generic-bound failures if we can
- // avoid it: these have a very high rate of being unhelpful in
- // practice. This is because they are basically secondary
- // checks that test the state of the region graph after the
- // rest of inference is done, and the other kinds of errors
- // indicate that the region constraint graph is internally
- // inconsistent, so these test results are likely to be
- // meaningless.
- //
- // Therefore, we filter them out of the list unless they are
- // the only thing in the list.
-
- let is_bound_failure = |e: &RegionResolutionError<'tcx>| match *e {
- RegionResolutionError::GenericBoundFailure(..) => true,
- RegionResolutionError::ConcreteFailure(..)
- | RegionResolutionError::SubSupConflict(..)
- | RegionResolutionError::UpperBoundUniverseConflict(..)
- | RegionResolutionError::CannotNormalize(..) => false,
- };
-
- let mut errors = if errors.iter().all(|e| is_bound_failure(e)) {
- errors.to_owned()
- } else {
- errors.iter().filter(|&e| !is_bound_failure(e)).cloned().collect()
- };
-
- // sort the errors by span, for better error message stability.
- errors.sort_by_key(|u| match *u {
- RegionResolutionError::ConcreteFailure(ref sro, _, _) => sro.span(),
- RegionResolutionError::GenericBoundFailure(ref sro, _, _) => sro.span(),
- RegionResolutionError::SubSupConflict(_, ref rvo, _, _, _, _, _) => rvo.span(),
- RegionResolutionError::UpperBoundUniverseConflict(_, ref rvo, _, _, _) => rvo.span(),
- RegionResolutionError::CannotNormalize(_, ref sro) => sro.span(),
- });
- errors
- }
-
/// Adds a note if the types come from similarly named crates
fn check_and_note_conflicting_crates(&self, err: &mut Diag<'_>, terr: TypeError<'tcx>) {
use hir::def_id::CrateNum;
@@ -2341,359 +1910,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
))
}
- pub fn report_generic_bound_failure(
- &self,
- generic_param_scope: LocalDefId,
- span: Span,
- origin: Option>,
- bound_kind: GenericKind<'tcx>,
- sub: Region<'tcx>,
- ) -> ErrorGuaranteed {
- self.construct_generic_bound_failure(generic_param_scope, span, origin, bound_kind, sub)
- .emit()
- }
-
- pub fn construct_generic_bound_failure(
- &self,
- generic_param_scope: LocalDefId,
- span: Span,
- origin: Option>,
- bound_kind: GenericKind<'tcx>,
- sub: Region<'tcx>,
- ) -> Diag<'a> {
- if let Some(SubregionOrigin::CompareImplItemObligation {
- span,
- impl_item_def_id,
- trait_item_def_id,
- }) = origin
- {
- return self.infcx.report_extra_impl_obligation(
- span,
- impl_item_def_id,
- trait_item_def_id,
- &format!("`{bound_kind}: {sub}`"),
- );
- }
-
- let labeled_user_string = match bound_kind {
- GenericKind::Param(ref p) => format!("the parameter type `{p}`"),
- GenericKind::Placeholder(ref p) => format!("the placeholder type `{p:?}`"),
- GenericKind::Alias(ref p) => match p.kind(self.tcx) {
- ty::Projection | ty::Inherent => {
- format!("the associated type `{p}`")
- }
- ty::Weak => format!("the type alias `{p}`"),
- ty::Opaque => format!("the opaque type `{p}`"),
- },
- };
-
- let mut err = self
- .tcx
- .dcx()
- .struct_span_err(span, format!("{labeled_user_string} may not live long enough"));
- err.code(match sub.kind() {
- ty::ReEarlyParam(_) | ty::ReLateParam(_) if sub.has_name() => E0309,
- ty::ReStatic => E0310,
- _ => E0311,
- });
-
- '_explain: {
- let (description, span) = match sub.kind() {
- ty::ReEarlyParam(_) | ty::ReLateParam(_) | ty::ReStatic => {
- msg_span_from_named_region(self.tcx, generic_param_scope, sub, Some(span))
- }
- _ => (format!("lifetime `{sub}`"), Some(span)),
- };
- let prefix = format!("{labeled_user_string} must be valid for ");
- label_msg_span(&mut err, &prefix, description, span, "...");
- if let Some(origin) = origin {
- self.note_region_origin(&mut err, &origin);
- }
- }
-
- 'suggestion: {
- let msg = "consider adding an explicit lifetime bound";
-
- if (bound_kind, sub).has_infer_regions()
- || (bound_kind, sub).has_placeholders()
- || !bound_kind.is_suggestable(self.tcx, false)
- {
- let lt_name = sub.get_name_or_anon().to_string();
- err.help(format!("{msg} `{bound_kind}: {lt_name}`..."));
- break 'suggestion;
- }
-
- let mut generic_param_scope = generic_param_scope;
- while self.tcx.def_kind(generic_param_scope) == DefKind::OpaqueTy {
- generic_param_scope = self.tcx.local_parent(generic_param_scope);
- }
-
- // type_param_sugg_span is (span, has_bounds, needs_parentheses)
- let (type_scope, type_param_sugg_span) = match bound_kind {
- GenericKind::Param(param) => {
- let generics = self.tcx.generics_of(generic_param_scope);
- let type_param = generics.type_param(param, self.tcx);
- let def_id = type_param.def_id.expect_local();
- let scope = self.tcx.local_def_id_to_hir_id(def_id).owner.def_id;
- // Get the `hir::Param` to verify whether it already has any bounds.
- // We do this to avoid suggesting code that ends up as `T: 'a'b`,
- // instead we suggest `T: 'a + 'b` in that case.
- let hir_generics = self.tcx.hir().get_generics(scope).unwrap();
- let sugg_span = match hir_generics.bounds_span_for_suggestions(def_id) {
- Some((span, open_paren_sp)) => Some((span, true, open_paren_sp)),
- // If `param` corresponds to `Self`, no usable suggestion span.
- None if generics.has_self && param.index == 0 => None,
- None => {
- let span = if let Some(param) =
- hir_generics.params.iter().find(|param| param.def_id == def_id)
- && let ParamName::Plain(ident) = param.name
- {
- ident.span.shrink_to_hi()
- } else {
- let span = self.tcx.def_span(def_id);
- span.shrink_to_hi()
- };
- Some((span, false, None))
- }
- };
- (scope, sugg_span)
- }
- _ => (generic_param_scope, None),
- };
- let suggestion_scope = {
- let lifetime_scope = match sub.kind() {
- ty::ReStatic => hir::def_id::CRATE_DEF_ID,
- _ => match self.tcx.is_suitable_region(generic_param_scope, sub) {
- Some(info) => info.def_id,
- None => generic_param_scope,
- },
- };
- match self.tcx.is_descendant_of(type_scope.into(), lifetime_scope.into()) {
- true => type_scope,
- false => lifetime_scope,
- }
- };
-
- let mut suggs = vec![];
- let lt_name = self.suggest_name_region(generic_param_scope, sub, &mut suggs);
-
- if let Some((sp, has_lifetimes, open_paren_sp)) = type_param_sugg_span
- && suggestion_scope == type_scope
- {
- let suggestion =
- if has_lifetimes { format!(" + {lt_name}") } else { format!(": {lt_name}") };
-
- if let Some(open_paren_sp) = open_paren_sp {
- suggs.push((open_paren_sp, "(".to_string()));
- suggs.push((sp, format!("){suggestion}")));
- } else {
- suggs.push((sp, suggestion))
- }
- } else if let GenericKind::Alias(ref p) = bound_kind
- && let ty::Projection = p.kind(self.tcx)
- && let DefKind::AssocTy = self.tcx.def_kind(p.def_id)
- && let Some(ty::ImplTraitInTraitData::Trait { .. }) =
- self.tcx.opt_rpitit_info(p.def_id)
- {
- // The lifetime found in the `impl` is longer than the one on the RPITIT.
- // Do not suggest `::{opaque}: 'static`.
- } else if let Some(generics) = self.tcx.hir().get_generics(suggestion_scope) {
- let pred = format!("{bound_kind}: {lt_name}");
- let suggestion = format!("{} {}", generics.add_where_or_trailing_comma(), pred);
- suggs.push((generics.tail_span_for_predicate_suggestion(), suggestion))
- } else {
- let consider = format!("{msg} `{bound_kind}: {sub}`...");
- err.help(consider);
- }
-
- if !suggs.is_empty() {
- err.multipart_suggestion_verbose(
- msg,
- suggs,
- Applicability::MaybeIncorrect, // Issue #41966
- );
- }
- }
-
- err
- }
-
- pub fn suggest_name_region(
- &self,
- generic_param_scope: LocalDefId,
- lifetime: Region<'tcx>,
- add_lt_suggs: &mut Vec<(Span, String)>,
- ) -> String {
- struct LifetimeReplaceVisitor<'tcx, 'a> {
- tcx: TyCtxt<'tcx>,
- needle: hir::LifetimeName,
- new_lt: &'a str,
- add_lt_suggs: &'a mut Vec<(Span, String)>,
- }
-
- impl<'hir, 'tcx> hir::intravisit::Visitor<'hir> for LifetimeReplaceVisitor<'tcx, '_> {
- fn visit_lifetime(&mut self, lt: &'hir hir::Lifetime) {
- if lt.res == self.needle {
- let (pos, span) = lt.suggestion_position();
- let new_lt = &self.new_lt;
- let sugg = match pos {
- hir::LifetimeSuggestionPosition::Normal => format!("{new_lt}"),
- hir::LifetimeSuggestionPosition::Ampersand => format!("{new_lt} "),
- hir::LifetimeSuggestionPosition::ElidedPath => format!("<{new_lt}>"),
- hir::LifetimeSuggestionPosition::ElidedPathArgument => {
- format!("{new_lt}, ")
- }
- hir::LifetimeSuggestionPosition::ObjectDefault => format!("+ {new_lt}"),
- };
- self.add_lt_suggs.push((span, sugg));
- }
- }
-
- fn visit_ty(&mut self, ty: &'hir hir::Ty<'hir>) {
- let hir::TyKind::OpaqueDef(item_id, _, _) = ty.kind else {
- return hir::intravisit::walk_ty(self, ty);
- };
- let opaque_ty = self.tcx.hir().item(item_id).expect_opaque_ty();
- if let Some(&(_, b)) =
- opaque_ty.lifetime_mapping.iter().find(|&(a, _)| a.res == self.needle)
- {
- let prev_needle =
- std::mem::replace(&mut self.needle, hir::LifetimeName::Param(b));
- for bound in opaque_ty.bounds {
- self.visit_param_bound(bound);
- }
- self.needle = prev_needle;
- }
- }
- }
-
- let (lifetime_def_id, lifetime_scope) =
- match self.tcx.is_suitable_region(generic_param_scope, lifetime) {
- Some(info) if !lifetime.has_name() => {
- (info.bound_region.get_id().unwrap().expect_local(), info.def_id)
- }
- _ => return lifetime.get_name_or_anon().to_string(),
- };
-
- let new_lt = {
- let generics = self.tcx.generics_of(lifetime_scope);
- let mut used_names =
- iter::successors(Some(generics), |g| g.parent.map(|p| self.tcx.generics_of(p)))
- .flat_map(|g| &g.own_params)
- .filter(|p| matches!(p.kind, ty::GenericParamDefKind::Lifetime))
- .map(|p| p.name)
- .collect::>();
- let hir_id = self.tcx.local_def_id_to_hir_id(lifetime_scope);
- // consider late-bound lifetimes ...
- used_names.extend(self.tcx.late_bound_vars(hir_id).into_iter().filter_map(
- |p| match p {
- ty::BoundVariableKind::Region(lt) => lt.get_name(),
- _ => None,
- },
- ));
- (b'a'..=b'z')
- .map(|c| format!("'{}", c as char))
- .find(|candidate| !used_names.iter().any(|e| e.as_str() == candidate))
- .unwrap_or("'lt".to_string())
- };
-
- let mut visitor = LifetimeReplaceVisitor {
- tcx: self.tcx,
- needle: hir::LifetimeName::Param(lifetime_def_id),
- add_lt_suggs,
- new_lt: &new_lt,
- };
- match self.tcx.expect_hir_owner_node(lifetime_scope) {
- hir::OwnerNode::Item(i) => visitor.visit_item(i),
- hir::OwnerNode::ForeignItem(i) => visitor.visit_foreign_item(i),
- hir::OwnerNode::ImplItem(i) => visitor.visit_impl_item(i),
- hir::OwnerNode::TraitItem(i) => visitor.visit_trait_item(i),
- hir::OwnerNode::Crate(_) => bug!("OwnerNode::Crate doesn't not have generics"),
- hir::OwnerNode::Synthetic => unreachable!(),
- }
-
- let ast_generics = self.tcx.hir().get_generics(lifetime_scope).unwrap();
- let sugg = ast_generics
- .span_for_lifetime_suggestion()
- .map(|span| (span, format!("{new_lt}, ")))
- .unwrap_or_else(|| (ast_generics.span, format!("<{new_lt}>")));
- add_lt_suggs.push(sugg);
-
- new_lt
- }
-
- fn report_sub_sup_conflict(
- &self,
- generic_param_scope: LocalDefId,
- var_origin: RegionVariableOrigin,
- sub_origin: SubregionOrigin<'tcx>,
- sub_region: Region<'tcx>,
- sup_origin: SubregionOrigin<'tcx>,
- sup_region: Region<'tcx>,
- ) -> ErrorGuaranteed {
- let mut err = self.report_inference_failure(var_origin);
-
- note_and_explain_region(
- self.tcx,
- &mut err,
- generic_param_scope,
- "first, the lifetime cannot outlive ",
- sup_region,
- "...",
- None,
- );
-
- debug!("report_sub_sup_conflict: var_origin={:?}", var_origin);
- debug!("report_sub_sup_conflict: sub_region={:?}", sub_region);
- debug!("report_sub_sup_conflict: sub_origin={:?}", sub_origin);
- debug!("report_sub_sup_conflict: sup_region={:?}", sup_region);
- debug!("report_sub_sup_conflict: sup_origin={:?}", sup_origin);
-
- if let infer::Subtype(ref sup_trace) = sup_origin
- && let infer::Subtype(ref sub_trace) = sub_origin
- && let Some((sup_expected, sup_found, _)) = self.values_str(sup_trace.values)
- && let Some((sub_expected, sub_found, _)) = self.values_str(sub_trace.values)
- && sub_expected == sup_expected
- && sub_found == sup_found
- {
- note_and_explain_region(
- self.tcx,
- &mut err,
- generic_param_scope,
- "...but the lifetime must also be valid for ",
- sub_region,
- "...",
- None,
- );
- err.span_note(
- sup_trace.cause.span,
- format!("...so that the {}", sup_trace.cause.as_requirement_str()),
- );
-
- err.note_expected_found(&"", sup_expected, &"", sup_found);
- return if sub_region.is_error() | sup_region.is_error() {
- err.delay_as_bug()
- } else {
- err.emit()
- };
- }
-
- self.note_region_origin(&mut err, &sup_origin);
-
- note_and_explain_region(
- self.tcx,
- &mut err,
- generic_param_scope,
- "but, the lifetime must be valid for ",
- sub_region,
- "...",
- None,
- );
-
- self.note_region_origin(&mut err, &sub_origin);
- if sub_region.is_error() | sup_region.is_error() { err.delay_as_bug() } else { err.emit() }
- }
-
/// Determine whether an error associated with the given span and definition
/// should be treated as being caused by the implicit `From` conversion
/// within `?` desugaring.
@@ -2792,55 +2008,6 @@ impl<'tcx> TypeRelation> for SameTypeModuloInfer<'_, 'tcx> {
}
}
-impl<'tcx> InferCtxt<'tcx> {
- fn report_inference_failure(&self, var_origin: RegionVariableOrigin) -> Diag<'_> {
- let br_string = |br: ty::BoundRegionKind| {
- let mut s = match br {
- ty::BrNamed(_, name) => name.to_string(),
- _ => String::new(),
- };
- if !s.is_empty() {
- s.push(' ');
- }
- s
- };
- let var_description = match var_origin {
- infer::MiscVariable(_) => String::new(),
- infer::PatternRegion(_) => " for pattern".to_string(),
- infer::AddrOfRegion(_) => " for borrow expression".to_string(),
- infer::Autoref(_) => " for autoref".to_string(),
- infer::Coercion(_) => " for automatic coercion".to_string(),
- infer::BoundRegion(_, br, infer::FnCall) => {
- format!(" for lifetime parameter {}in function call", br_string(br))
- }
- infer::BoundRegion(_, br, infer::HigherRankedType) => {
- format!(" for lifetime parameter {}in generic type", br_string(br))
- }
- infer::BoundRegion(_, br, infer::AssocTypeProjection(def_id)) => format!(
- " for lifetime parameter {}in trait containing associated type `{}`",
- br_string(br),
- self.tcx.associated_item(def_id).name
- ),
- infer::RegionParameterDefinition(_, name) => {
- format!(" for lifetime parameter `{name}`")
- }
- infer::UpvarRegion(ref upvar_id, _) => {
- let var_name = self.tcx.hir().name(upvar_id.var_path.hir_id);
- format!(" for capture of `{var_name}` by closure")
- }
- infer::Nll(..) => bug!("NLL variable found in lexical phase"),
- };
-
- struct_span_code_err!(
- self.dcx(),
- var_origin.span(),
- E0495,
- "cannot infer an appropriate lifetime{} due to conflicting requirements",
- var_description
- )
- }
-}
-
pub enum FailureCode {
Error0317,
Error0580,
diff --git a/compiler/rustc_infer/src/infer/error_reporting/note.rs b/compiler/rustc_infer/src/infer/error_reporting/note.rs
deleted file mode 100644
index d1fc9c9f140b..000000000000
--- a/compiler/rustc_infer/src/infer/error_reporting/note.rs
+++ /dev/null
@@ -1,421 +0,0 @@
-use crate::errors::{
- note_and_explain, FulfillReqLifetime, LfBoundNotSatisfied, OutlivesBound, OutlivesContent,
- RefLongerThanData, RegionOriginNote, WhereClauseSuggestions,
-};
-use crate::fluent_generated as fluent;
-use crate::infer::error_reporting::{note_and_explain_region, TypeErrCtxt};
-use crate::infer::{self, SubregionOrigin};
-use rustc_errors::{Diag, Subdiagnostic};
-use rustc_hir::def_id::{DefId, LocalDefId};
-use rustc_middle::traits::ObligationCauseCode;
-use rustc_middle::ty::error::TypeError;
-use rustc_middle::ty::{self, IsSuggestable, Region, Ty};
-use rustc_span::symbol::kw;
-
-use super::ObligationCauseAsDiagArg;
-
-impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
- pub(super) fn note_region_origin(&self, err: &mut Diag<'_>, origin: &SubregionOrigin<'tcx>) {
- match *origin {
- infer::Subtype(ref trace) => RegionOriginNote::WithRequirement {
- span: trace.cause.span,
- requirement: ObligationCauseAsDiagArg(trace.cause.clone()),
- expected_found: self.values_str(trace.values).map(|(e, f, _)| (e, f)),
- }
- .add_to_diag(err),
- infer::Reborrow(span) => {
- RegionOriginNote::Plain { span, msg: fluent::infer_reborrow }.add_to_diag(err)
- }
- infer::RelateObjectBound(span) => {
- RegionOriginNote::Plain { span, msg: fluent::infer_relate_object_bound }
- .add_to_diag(err);
- }
- infer::ReferenceOutlivesReferent(ty, span) => {
- RegionOriginNote::WithName {
- span,
- msg: fluent::infer_reference_outlives_referent,
- name: &self.ty_to_string(ty),
- continues: false,
- }
- .add_to_diag(err);
- }
- infer::RelateParamBound(span, ty, opt_span) => {
- RegionOriginNote::WithName {
- span,
- msg: fluent::infer_relate_param_bound,
- name: &self.ty_to_string(ty),
- continues: opt_span.is_some(),
- }
- .add_to_diag(err);
- if let Some(span) = opt_span {
- RegionOriginNote::Plain { span, msg: fluent::infer_relate_param_bound_2 }
- .add_to_diag(err);
- }
- }
- infer::RelateRegionParamBound(span) => {
- RegionOriginNote::Plain { span, msg: fluent::infer_relate_region_param_bound }
- .add_to_diag(err);
- }
- infer::CompareImplItemObligation { span, .. } => {
- RegionOriginNote::Plain { span, msg: fluent::infer_compare_impl_item_obligation }
- .add_to_diag(err);
- }
- infer::CheckAssociatedTypeBounds { ref parent, .. } => {
- self.note_region_origin(err, parent);
- }
- infer::AscribeUserTypeProvePredicate(span) => {
- RegionOriginNote::Plain {
- span,
- msg: fluent::infer_ascribe_user_type_prove_predicate,
- }
- .add_to_diag(err);
- }
- }
- }
-
- pub(super) fn report_concrete_failure(
- &self,
- generic_param_scope: LocalDefId,
- origin: SubregionOrigin<'tcx>,
- sub: Region<'tcx>,
- sup: Region<'tcx>,
- ) -> Diag<'a> {
- let mut err = match origin {
- infer::Subtype(box trace) => {
- let terr = TypeError::RegionsDoesNotOutlive(sup, sub);
- let mut err = self.report_and_explain_type_error(trace, terr);
- match (*sub, *sup) {
- (ty::RePlaceholder(_), ty::RePlaceholder(_)) => {}
- (ty::RePlaceholder(_), _) => {
- note_and_explain_region(
- self.tcx,
- &mut err,
- generic_param_scope,
- "",
- sup,
- " doesn't meet the lifetime requirements",
- None,
- );
- }
- (_, ty::RePlaceholder(_)) => {
- note_and_explain_region(
- self.tcx,
- &mut err,
- generic_param_scope,
- "the required lifetime does not necessarily outlive ",
- sub,
- "",
- None,
- );
- }
- _ => {
- note_and_explain_region(
- self.tcx,
- &mut err,
- generic_param_scope,
- "",
- sup,
- "...",
- None,
- );
- note_and_explain_region(
- self.tcx,
- &mut err,
- generic_param_scope,
- "...does not necessarily outlive ",
- sub,
- "",
- None,
- );
- }
- }
- err
- }
- infer::Reborrow(span) => {
- let reference_valid = note_and_explain::RegionExplanation::new(
- self.tcx,
- generic_param_scope,
- sub,
- None,
- note_and_explain::PrefixKind::RefValidFor,
- note_and_explain::SuffixKind::Continues,
- );
- let content_valid = note_and_explain::RegionExplanation::new(
- self.tcx,
- generic_param_scope,
- sup,
- None,
- note_and_explain::PrefixKind::ContentValidFor,
- note_and_explain::SuffixKind::Empty,
- );
- self.dcx().create_err(OutlivesContent {
- span,
- notes: reference_valid.into_iter().chain(content_valid).collect(),
- })
- }
- infer::RelateObjectBound(span) => {
- let object_valid = note_and_explain::RegionExplanation::new(
- self.tcx,
- generic_param_scope,
- sub,
- None,
- note_and_explain::PrefixKind::TypeObjValidFor,
- note_and_explain::SuffixKind::Empty,
- );
- let pointer_valid = note_and_explain::RegionExplanation::new(
- self.tcx,
- generic_param_scope,
- sup,
- None,
- note_and_explain::PrefixKind::SourcePointerValidFor,
- note_and_explain::SuffixKind::Empty,
- );
- self.dcx().create_err(OutlivesBound {
- span,
- notes: object_valid.into_iter().chain(pointer_valid).collect(),
- })
- }
- infer::RelateParamBound(span, ty, opt_span) => {
- let prefix = match *sub {
- ty::ReStatic => note_and_explain::PrefixKind::TypeSatisfy,
- _ => note_and_explain::PrefixKind::TypeOutlive,
- };
- let suffix = if opt_span.is_some() {
- note_and_explain::SuffixKind::ReqByBinding
- } else {
- note_and_explain::SuffixKind::Empty
- };
- let note = note_and_explain::RegionExplanation::new(
- self.tcx,
- generic_param_scope,
- sub,
- opt_span,
- prefix,
- suffix,
- );
- self.dcx().create_err(FulfillReqLifetime {
- span,
- ty: self.resolve_vars_if_possible(ty),
- note,
- })
- }
- infer::RelateRegionParamBound(span) => {
- let param_instantiated = note_and_explain::RegionExplanation::new(
- self.tcx,
- generic_param_scope,
- sup,
- None,
- note_and_explain::PrefixKind::LfParamInstantiatedWith,
- note_and_explain::SuffixKind::Empty,
- );
- let param_must_outlive = note_and_explain::RegionExplanation::new(
- self.tcx,
- generic_param_scope,
- sub,
- None,
- note_and_explain::PrefixKind::LfParamMustOutlive,
- note_and_explain::SuffixKind::Empty,
- );
- self.dcx().create_err(LfBoundNotSatisfied {
- span,
- notes: param_instantiated.into_iter().chain(param_must_outlive).collect(),
- })
- }
- infer::ReferenceOutlivesReferent(ty, span) => {
- let pointer_valid = note_and_explain::RegionExplanation::new(
- self.tcx,
- generic_param_scope,
- sub,
- None,
- note_and_explain::PrefixKind::PointerValidFor,
- note_and_explain::SuffixKind::Empty,
- );
- let data_valid = note_and_explain::RegionExplanation::new(
- self.tcx,
- generic_param_scope,
- sup,
- None,
- note_and_explain::PrefixKind::DataValidFor,
- note_and_explain::SuffixKind::Empty,
- );
- self.dcx().create_err(RefLongerThanData {
- span,
- ty: self.resolve_vars_if_possible(ty),
- notes: pointer_valid.into_iter().chain(data_valid).collect(),
- })
- }
- infer::CompareImplItemObligation { span, impl_item_def_id, trait_item_def_id } => {
- let mut err = self.infcx.report_extra_impl_obligation(
- span,
- impl_item_def_id,
- trait_item_def_id,
- &format!("`{sup}: {sub}`"),
- );
- // We should only suggest rewriting the `where` clause if the predicate is within that `where` clause
- if let Some(generics) = self.tcx.hir().get_generics(impl_item_def_id)
- && generics.where_clause_span.contains(span)
- {
- self.suggest_copy_trait_method_bounds(
- trait_item_def_id,
- impl_item_def_id,
- &mut err,
- );
- }
- err
- }
- infer::CheckAssociatedTypeBounds { impl_item_def_id, trait_item_def_id, parent } => {
- let mut err = self.report_concrete_failure(generic_param_scope, *parent, sub, sup);
-
- // Don't mention the item name if it's an RPITIT, since that'll just confuse
- // folks.
- if !self.tcx.is_impl_trait_in_trait(impl_item_def_id.to_def_id()) {
- let trait_item_span = self.tcx.def_span(trait_item_def_id);
- let item_name = self.tcx.item_name(impl_item_def_id.to_def_id());
- err.span_label(
- trait_item_span,
- format!("definition of `{item_name}` from trait"),
- );
- }
-
- self.suggest_copy_trait_method_bounds(
- trait_item_def_id,
- impl_item_def_id,
- &mut err,
- );
- err
- }
- infer::AscribeUserTypeProvePredicate(span) => {
- let instantiated = note_and_explain::RegionExplanation::new(
- self.tcx,
- generic_param_scope,
- sup,
- None,
- note_and_explain::PrefixKind::LfInstantiatedWith,
- note_and_explain::SuffixKind::Empty,
- );
- let must_outlive = note_and_explain::RegionExplanation::new(
- self.tcx,
- generic_param_scope,
- sub,
- None,
- note_and_explain::PrefixKind::LfMustOutlive,
- note_and_explain::SuffixKind::Empty,
- );
- self.dcx().create_err(LfBoundNotSatisfied {
- span,
- notes: instantiated.into_iter().chain(must_outlive).collect(),
- })
- }
- };
- if sub.is_error() || sup.is_error() {
- err.downgrade_to_delayed_bug();
- }
- err
- }
-
- pub fn suggest_copy_trait_method_bounds(
- &self,
- trait_item_def_id: DefId,
- impl_item_def_id: LocalDefId,
- err: &mut Diag<'_>,
- ) {
- // FIXME(compiler-errors): Right now this is only being used for region
- // predicate mismatches. Ideally, we'd use it for *all* predicate mismatches,
- // but right now it's not really very smart when it comes to implicit `Sized`
- // predicates and bounds on the trait itself.
-
- let Some(impl_def_id) = self.tcx.associated_item(impl_item_def_id).impl_container(self.tcx)
- else {
- return;
- };
- let Some(trait_ref) = self.tcx.impl_trait_ref(impl_def_id) else {
- return;
- };
- let trait_args = trait_ref
- .instantiate_identity()
- // Replace the explicit self type with `Self` for better suggestion rendering
- .with_self_ty(self.tcx, Ty::new_param(self.tcx, 0, kw::SelfUpper))
- .args;
- let trait_item_args = ty::GenericArgs::identity_for_item(self.tcx, impl_item_def_id)
- .rebase_onto(self.tcx, impl_def_id, trait_args);
-
- let Ok(trait_predicates) =
- self.tcx
- .explicit_predicates_of(trait_item_def_id)
- .instantiate_own(self.tcx, trait_item_args)
- .map(|(pred, _)| {
- if pred.is_suggestable(self.tcx, false) {
- Ok(pred.to_string())
- } else {
- Err(())
- }
- })
- .collect::, ()>>()
- else {
- return;
- };
-
- let Some(generics) = self.tcx.hir().get_generics(impl_item_def_id) else {
- return;
- };
-
- let suggestion = if trait_predicates.is_empty() {
- WhereClauseSuggestions::Remove { span: generics.where_clause_span }
- } else {
- let space = if generics.where_clause_span.is_empty() { " " } else { "" };
- WhereClauseSuggestions::CopyPredicates {
- span: generics.where_clause_span,
- space,
- trait_predicates: trait_predicates.join(", "),
- }
- };
- err.subdiagnostic(suggestion);
- }
-
- pub(super) fn report_placeholder_failure(
- &self,
- generic_param_scope: LocalDefId,
- placeholder_origin: SubregionOrigin<'tcx>,
- sub: Region<'tcx>,
- sup: Region<'tcx>,
- ) -> Diag<'a> {
- // I can't think how to do better than this right now. -nikomatsakis
- debug!(?placeholder_origin, ?sub, ?sup, "report_placeholder_failure");
- match placeholder_origin {
- infer::Subtype(box ref trace)
- if matches!(
- &trace.cause.code().peel_derives(),
- ObligationCauseCode::WhereClause(..)
- | ObligationCauseCode::WhereClauseInExpr(..)
- ) =>
- {
- // Hack to get around the borrow checker because trace.cause has an `Rc`.
- if let ObligationCauseCode::WhereClause(_, span)
- | ObligationCauseCode::WhereClauseInExpr(_, span, ..) =
- &trace.cause.code().peel_derives()
- && !span.is_dummy()
- {
- let span = *span;
- self.report_concrete_failure(generic_param_scope, placeholder_origin, sub, sup)
- .with_span_note(span, "the lifetime requirement is introduced here")
- } else {
- unreachable!(
- "control flow ensures we have a `BindingObligation` or `WhereClauseInExpr` here..."
- )
- }
- }
- infer::Subtype(box trace) => {
- let terr = TypeError::RegionsPlaceholderMismatch;
- return self.report_and_explain_type_error(trace, terr);
- }
- _ => {
- return self.report_concrete_failure(
- generic_param_scope,
- placeholder_origin,
- sub,
- sup,
- );
- }
- }
- }
-}
diff --git a/compiler/rustc_infer/src/infer/error_reporting/region.rs b/compiler/rustc_infer/src/infer/error_reporting/region.rs
new file mode 100644
index 000000000000..191cb23184da
--- /dev/null
+++ b/compiler/rustc_infer/src/infer/error_reporting/region.rs
@@ -0,0 +1,1356 @@
+use std::iter;
+
+use rustc_data_structures::fx::FxIndexSet;
+use rustc_errors::{
+ struct_span_code_err, Applicability, Diag, Subdiagnostic, E0309, E0310, E0311, E0495,
+};
+use rustc_hir::def::DefKind;
+use rustc_hir::def_id::{DefId, LocalDefId};
+use rustc_hir::intravisit::Visitor;
+use rustc_hir::{self as hir, ParamName};
+use rustc_middle::bug;
+use rustc_middle::traits::ObligationCauseCode;
+use rustc_middle::ty::error::TypeError;
+use rustc_middle::ty::{self, IsSuggestable, Region, Ty, TyCtxt, TypeVisitableExt as _};
+use rustc_span::symbol::kw;
+use rustc_span::{BytePos, ErrorGuaranteed, Span, Symbol};
+use rustc_type_ir::Upcast as _;
+
+use super::nice_region_error::find_anon_type;
+use super::{nice_region_error, ObligationCauseAsDiagArg};
+use crate::errors::{
+ self, note_and_explain, FulfillReqLifetime, LfBoundNotSatisfied, OutlivesBound,
+ OutlivesContent, RefLongerThanData, RegionOriginNote, WhereClauseSuggestions,
+};
+use crate::fluent_generated as fluent;
+use crate::infer::error_reporting::{ObligationCauseExt as _, TypeErrCtxt};
+use crate::infer::region_constraints::GenericKind;
+use crate::infer::{self, InferCtxt, RegionResolutionError, RegionVariableOrigin, SubregionOrigin};
+
+impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
+ pub fn report_region_errors(
+ &self,
+ generic_param_scope: LocalDefId,
+ errors: &[RegionResolutionError<'tcx>],
+ ) -> ErrorGuaranteed {
+ assert!(!errors.is_empty());
+
+ if let Some(guaranteed) = self.infcx.tainted_by_errors() {
+ return guaranteed;
+ }
+
+ debug!("report_region_errors(): {} errors to start", errors.len());
+
+ // try to pre-process the errors, which will group some of them
+ // together into a `ProcessedErrors` group:
+ let errors = self.process_errors(errors);
+
+ debug!("report_region_errors: {} errors after preprocessing", errors.len());
+
+ let mut guar = None;
+ for error in errors {
+ debug!("report_region_errors: error = {:?}", error);
+
+ let e = if let Some(guar) =
+ self.try_report_nice_region_error(generic_param_scope, &error)
+ {
+ guar
+ } else {
+ match error.clone() {
+ // These errors could indicate all manner of different
+ // problems with many different solutions. Rather
+ // than generate a "one size fits all" error, what we
+ // attempt to do is go through a number of specific
+ // scenarios and try to find the best way to present
+ // the error. If all of these fails, we fall back to a rather
+ // general bit of code that displays the error information
+ RegionResolutionError::ConcreteFailure(origin, sub, sup) => {
+ if sub.is_placeholder() || sup.is_placeholder() {
+ self.report_placeholder_failure(generic_param_scope, origin, sub, sup)
+ .emit()
+ } else {
+ self.report_concrete_failure(generic_param_scope, origin, sub, sup)
+ .emit()
+ }
+ }
+
+ RegionResolutionError::GenericBoundFailure(origin, param_ty, sub) => self
+ .report_generic_bound_failure(
+ generic_param_scope,
+ origin.span(),
+ Some(origin),
+ param_ty,
+ sub,
+ ),
+
+ RegionResolutionError::SubSupConflict(
+ _,
+ var_origin,
+ sub_origin,
+ sub_r,
+ sup_origin,
+ sup_r,
+ _,
+ ) => {
+ if sub_r.is_placeholder() {
+ self.report_placeholder_failure(
+ generic_param_scope,
+ sub_origin,
+ sub_r,
+ sup_r,
+ )
+ .emit()
+ } else if sup_r.is_placeholder() {
+ self.report_placeholder_failure(
+ generic_param_scope,
+ sup_origin,
+ sub_r,
+ sup_r,
+ )
+ .emit()
+ } else {
+ self.report_sub_sup_conflict(
+ generic_param_scope,
+ var_origin,
+ sub_origin,
+ sub_r,
+ sup_origin,
+ sup_r,
+ )
+ }
+ }
+
+ RegionResolutionError::UpperBoundUniverseConflict(
+ _,
+ _,
+ _,
+ sup_origin,
+ sup_r,
+ ) => {
+ assert!(sup_r.is_placeholder());
+
+ // Make a dummy value for the "sub region" --
+ // this is the initial value of the
+ // placeholder. In practice, we expect more
+ // tailored errors that don't really use this
+ // value.
+ let sub_r = self.tcx.lifetimes.re_erased;
+
+ self.report_placeholder_failure(
+ generic_param_scope,
+ sup_origin,
+ sub_r,
+ sup_r,
+ )
+ .emit()
+ }
+
+ RegionResolutionError::CannotNormalize(clause, origin) => {
+ let clause: ty::Clause<'tcx> =
+ clause.map_bound(ty::ClauseKind::TypeOutlives).upcast(self.tcx);
+ self.tcx
+ .dcx()
+ .struct_span_err(origin.span(), format!("cannot normalize `{clause}`"))
+ .emit()
+ }
+ }
+ };
+
+ guar = Some(e)
+ }
+
+ guar.unwrap()
+ }
+
+ // This method goes through all the errors and try to group certain types
+ // of error together, for the purpose of suggesting explicit lifetime
+ // parameters to the user. This is done so that we can have a more
+ // complete view of what lifetimes should be the same.
+ // If the return value is an empty vector, it means that processing
+ // failed (so the return value of this method should not be used).
+ //
+ // The method also attempts to weed out messages that seem like
+ // duplicates that will be unhelpful to the end-user. But
+ // obviously it never weeds out ALL errors.
+ fn process_errors(
+ &self,
+ errors: &[RegionResolutionError<'tcx>],
+ ) -> Vec> {
+ debug!("process_errors()");
+
+ // We want to avoid reporting generic-bound failures if we can
+ // avoid it: these have a very high rate of being unhelpful in
+ // practice. This is because they are basically secondary
+ // checks that test the state of the region graph after the
+ // rest of inference is done, and the other kinds of errors
+ // indicate that the region constraint graph is internally
+ // inconsistent, so these test results are likely to be
+ // meaningless.
+ //
+ // Therefore, we filter them out of the list unless they are
+ // the only thing in the list.
+
+ let is_bound_failure = |e: &RegionResolutionError<'tcx>| match *e {
+ RegionResolutionError::GenericBoundFailure(..) => true,
+ RegionResolutionError::ConcreteFailure(..)
+ | RegionResolutionError::SubSupConflict(..)
+ | RegionResolutionError::UpperBoundUniverseConflict(..)
+ | RegionResolutionError::CannotNormalize(..) => false,
+ };
+
+ let mut errors = if errors.iter().all(|e| is_bound_failure(e)) {
+ errors.to_owned()
+ } else {
+ errors.iter().filter(|&e| !is_bound_failure(e)).cloned().collect()
+ };
+
+ // sort the errors by span, for better error message stability.
+ errors.sort_by_key(|u| match *u {
+ RegionResolutionError::ConcreteFailure(ref sro, _, _) => sro.span(),
+ RegionResolutionError::GenericBoundFailure(ref sro, _, _) => sro.span(),
+ RegionResolutionError::SubSupConflict(_, ref rvo, _, _, _, _, _) => rvo.span(),
+ RegionResolutionError::UpperBoundUniverseConflict(_, ref rvo, _, _, _) => rvo.span(),
+ RegionResolutionError::CannotNormalize(_, ref sro) => sro.span(),
+ });
+ errors
+ }
+
+ pub(super) fn note_region_origin(&self, err: &mut Diag<'_>, origin: &SubregionOrigin<'tcx>) {
+ match *origin {
+ infer::Subtype(ref trace) => RegionOriginNote::WithRequirement {
+ span: trace.cause.span,
+ requirement: ObligationCauseAsDiagArg(trace.cause.clone()),
+ expected_found: self.values_str(trace.values).map(|(e, f, _)| (e, f)),
+ }
+ .add_to_diag(err),
+ infer::Reborrow(span) => {
+ RegionOriginNote::Plain { span, msg: fluent::infer_reborrow }.add_to_diag(err)
+ }
+ infer::RelateObjectBound(span) => {
+ RegionOriginNote::Plain { span, msg: fluent::infer_relate_object_bound }
+ .add_to_diag(err);
+ }
+ infer::ReferenceOutlivesReferent(ty, span) => {
+ RegionOriginNote::WithName {
+ span,
+ msg: fluent::infer_reference_outlives_referent,
+ name: &self.ty_to_string(ty),
+ continues: false,
+ }
+ .add_to_diag(err);
+ }
+ infer::RelateParamBound(span, ty, opt_span) => {
+ RegionOriginNote::WithName {
+ span,
+ msg: fluent::infer_relate_param_bound,
+ name: &self.ty_to_string(ty),
+ continues: opt_span.is_some(),
+ }
+ .add_to_diag(err);
+ if let Some(span) = opt_span {
+ RegionOriginNote::Plain { span, msg: fluent::infer_relate_param_bound_2 }
+ .add_to_diag(err);
+ }
+ }
+ infer::RelateRegionParamBound(span) => {
+ RegionOriginNote::Plain { span, msg: fluent::infer_relate_region_param_bound }
+ .add_to_diag(err);
+ }
+ infer::CompareImplItemObligation { span, .. } => {
+ RegionOriginNote::Plain { span, msg: fluent::infer_compare_impl_item_obligation }
+ .add_to_diag(err);
+ }
+ infer::CheckAssociatedTypeBounds { ref parent, .. } => {
+ self.note_region_origin(err, parent);
+ }
+ infer::AscribeUserTypeProvePredicate(span) => {
+ RegionOriginNote::Plain {
+ span,
+ msg: fluent::infer_ascribe_user_type_prove_predicate,
+ }
+ .add_to_diag(err);
+ }
+ }
+ }
+
+ pub(super) fn report_concrete_failure(
+ &self,
+ generic_param_scope: LocalDefId,
+ origin: SubregionOrigin<'tcx>,
+ sub: Region<'tcx>,
+ sup: Region<'tcx>,
+ ) -> Diag<'a> {
+ let mut err = match origin {
+ infer::Subtype(box trace) => {
+ let terr = TypeError::RegionsDoesNotOutlive(sup, sub);
+ let mut err = self.report_and_explain_type_error(trace, terr);
+ match (*sub, *sup) {
+ (ty::RePlaceholder(_), ty::RePlaceholder(_)) => {}
+ (ty::RePlaceholder(_), _) => {
+ note_and_explain_region(
+ self.tcx,
+ &mut err,
+ generic_param_scope,
+ "",
+ sup,
+ " doesn't meet the lifetime requirements",
+ None,
+ );
+ }
+ (_, ty::RePlaceholder(_)) => {
+ note_and_explain_region(
+ self.tcx,
+ &mut err,
+ generic_param_scope,
+ "the required lifetime does not necessarily outlive ",
+ sub,
+ "",
+ None,
+ );
+ }
+ _ => {
+ note_and_explain_region(
+ self.tcx,
+ &mut err,
+ generic_param_scope,
+ "",
+ sup,
+ "...",
+ None,
+ );
+ note_and_explain_region(
+ self.tcx,
+ &mut err,
+ generic_param_scope,
+ "...does not necessarily outlive ",
+ sub,
+ "",
+ None,
+ );
+ }
+ }
+ err
+ }
+ infer::Reborrow(span) => {
+ let reference_valid = note_and_explain::RegionExplanation::new(
+ self.tcx,
+ generic_param_scope,
+ sub,
+ None,
+ note_and_explain::PrefixKind::RefValidFor,
+ note_and_explain::SuffixKind::Continues,
+ );
+ let content_valid = note_and_explain::RegionExplanation::new(
+ self.tcx,
+ generic_param_scope,
+ sup,
+ None,
+ note_and_explain::PrefixKind::ContentValidFor,
+ note_and_explain::SuffixKind::Empty,
+ );
+ self.dcx().create_err(OutlivesContent {
+ span,
+ notes: reference_valid.into_iter().chain(content_valid).collect(),
+ })
+ }
+ infer::RelateObjectBound(span) => {
+ let object_valid = note_and_explain::RegionExplanation::new(
+ self.tcx,
+ generic_param_scope,
+ sub,
+ None,
+ note_and_explain::PrefixKind::TypeObjValidFor,
+ note_and_explain::SuffixKind::Empty,
+ );
+ let pointer_valid = note_and_explain::RegionExplanation::new(
+ self.tcx,
+ generic_param_scope,
+ sup,
+ None,
+ note_and_explain::PrefixKind::SourcePointerValidFor,
+ note_and_explain::SuffixKind::Empty,
+ );
+ self.dcx().create_err(OutlivesBound {
+ span,
+ notes: object_valid.into_iter().chain(pointer_valid).collect(),
+ })
+ }
+ infer::RelateParamBound(span, ty, opt_span) => {
+ let prefix = match *sub {
+ ty::ReStatic => note_and_explain::PrefixKind::TypeSatisfy,
+ _ => note_and_explain::PrefixKind::TypeOutlive,
+ };
+ let suffix = if opt_span.is_some() {
+ note_and_explain::SuffixKind::ReqByBinding
+ } else {
+ note_and_explain::SuffixKind::Empty
+ };
+ let note = note_and_explain::RegionExplanation::new(
+ self.tcx,
+ generic_param_scope,
+ sub,
+ opt_span,
+ prefix,
+ suffix,
+ );
+ self.dcx().create_err(FulfillReqLifetime {
+ span,
+ ty: self.resolve_vars_if_possible(ty),
+ note,
+ })
+ }
+ infer::RelateRegionParamBound(span) => {
+ let param_instantiated = note_and_explain::RegionExplanation::new(
+ self.tcx,
+ generic_param_scope,
+ sup,
+ None,
+ note_and_explain::PrefixKind::LfParamInstantiatedWith,
+ note_and_explain::SuffixKind::Empty,
+ );
+ let param_must_outlive = note_and_explain::RegionExplanation::new(
+ self.tcx,
+ generic_param_scope,
+ sub,
+ None,
+ note_and_explain::PrefixKind::LfParamMustOutlive,
+ note_and_explain::SuffixKind::Empty,
+ );
+ self.dcx().create_err(LfBoundNotSatisfied {
+ span,
+ notes: param_instantiated.into_iter().chain(param_must_outlive).collect(),
+ })
+ }
+ infer::ReferenceOutlivesReferent(ty, span) => {
+ let pointer_valid = note_and_explain::RegionExplanation::new(
+ self.tcx,
+ generic_param_scope,
+ sub,
+ None,
+ note_and_explain::PrefixKind::PointerValidFor,
+ note_and_explain::SuffixKind::Empty,
+ );
+ let data_valid = note_and_explain::RegionExplanation::new(
+ self.tcx,
+ generic_param_scope,
+ sup,
+ None,
+ note_and_explain::PrefixKind::DataValidFor,
+ note_and_explain::SuffixKind::Empty,
+ );
+ self.dcx().create_err(RefLongerThanData {
+ span,
+ ty: self.resolve_vars_if_possible(ty),
+ notes: pointer_valid.into_iter().chain(data_valid).collect(),
+ })
+ }
+ infer::CompareImplItemObligation { span, impl_item_def_id, trait_item_def_id } => {
+ let mut err = self.infcx.report_extra_impl_obligation(
+ span,
+ impl_item_def_id,
+ trait_item_def_id,
+ &format!("`{sup}: {sub}`"),
+ );
+ // We should only suggest rewriting the `where` clause if the predicate is within that `where` clause
+ if let Some(generics) = self.tcx.hir().get_generics(impl_item_def_id)
+ && generics.where_clause_span.contains(span)
+ {
+ self.suggest_copy_trait_method_bounds(
+ trait_item_def_id,
+ impl_item_def_id,
+ &mut err,
+ );
+ }
+ err
+ }
+ infer::CheckAssociatedTypeBounds { impl_item_def_id, trait_item_def_id, parent } => {
+ let mut err = self.report_concrete_failure(generic_param_scope, *parent, sub, sup);
+
+ // Don't mention the item name if it's an RPITIT, since that'll just confuse
+ // folks.
+ if !self.tcx.is_impl_trait_in_trait(impl_item_def_id.to_def_id()) {
+ let trait_item_span = self.tcx.def_span(trait_item_def_id);
+ let item_name = self.tcx.item_name(impl_item_def_id.to_def_id());
+ err.span_label(
+ trait_item_span,
+ format!("definition of `{item_name}` from trait"),
+ );
+ }
+
+ self.suggest_copy_trait_method_bounds(
+ trait_item_def_id,
+ impl_item_def_id,
+ &mut err,
+ );
+ err
+ }
+ infer::AscribeUserTypeProvePredicate(span) => {
+ let instantiated = note_and_explain::RegionExplanation::new(
+ self.tcx,
+ generic_param_scope,
+ sup,
+ None,
+ note_and_explain::PrefixKind::LfInstantiatedWith,
+ note_and_explain::SuffixKind::Empty,
+ );
+ let must_outlive = note_and_explain::RegionExplanation::new(
+ self.tcx,
+ generic_param_scope,
+ sub,
+ None,
+ note_and_explain::PrefixKind::LfMustOutlive,
+ note_and_explain::SuffixKind::Empty,
+ );
+ self.dcx().create_err(LfBoundNotSatisfied {
+ span,
+ notes: instantiated.into_iter().chain(must_outlive).collect(),
+ })
+ }
+ };
+ if sub.is_error() || sup.is_error() {
+ err.downgrade_to_delayed_bug();
+ }
+ err
+ }
+
+ pub fn suggest_copy_trait_method_bounds(
+ &self,
+ trait_item_def_id: DefId,
+ impl_item_def_id: LocalDefId,
+ err: &mut Diag<'_>,
+ ) {
+ // FIXME(compiler-errors): Right now this is only being used for region
+ // predicate mismatches. Ideally, we'd use it for *all* predicate mismatches,
+ // but right now it's not really very smart when it comes to implicit `Sized`
+ // predicates and bounds on the trait itself.
+
+ let Some(impl_def_id) = self.tcx.associated_item(impl_item_def_id).impl_container(self.tcx)
+ else {
+ return;
+ };
+ let Some(trait_ref) = self.tcx.impl_trait_ref(impl_def_id) else {
+ return;
+ };
+ let trait_args = trait_ref
+ .instantiate_identity()
+ // Replace the explicit self type with `Self` for better suggestion rendering
+ .with_self_ty(self.tcx, Ty::new_param(self.tcx, 0, kw::SelfUpper))
+ .args;
+ let trait_item_args = ty::GenericArgs::identity_for_item(self.tcx, impl_item_def_id)
+ .rebase_onto(self.tcx, impl_def_id, trait_args);
+
+ let Ok(trait_predicates) =
+ self.tcx
+ .explicit_predicates_of(trait_item_def_id)
+ .instantiate_own(self.tcx, trait_item_args)
+ .map(|(pred, _)| {
+ if pred.is_suggestable(self.tcx, false) {
+ Ok(pred.to_string())
+ } else {
+ Err(())
+ }
+ })
+ .collect::, ()>>()
+ else {
+ return;
+ };
+
+ let Some(generics) = self.tcx.hir().get_generics(impl_item_def_id) else {
+ return;
+ };
+
+ let suggestion = if trait_predicates.is_empty() {
+ WhereClauseSuggestions::Remove { span: generics.where_clause_span }
+ } else {
+ let space = if generics.where_clause_span.is_empty() { " " } else { "" };
+ WhereClauseSuggestions::CopyPredicates {
+ span: generics.where_clause_span,
+ space,
+ trait_predicates: trait_predicates.join(", "),
+ }
+ };
+ err.subdiagnostic(suggestion);
+ }
+
+ pub(super) fn report_placeholder_failure(
+ &self,
+ generic_param_scope: LocalDefId,
+ placeholder_origin: SubregionOrigin<'tcx>,
+ sub: Region<'tcx>,
+ sup: Region<'tcx>,
+ ) -> Diag<'a> {
+ // I can't think how to do better than this right now. -nikomatsakis
+ debug!(?placeholder_origin, ?sub, ?sup, "report_placeholder_failure");
+ match placeholder_origin {
+ infer::Subtype(box ref trace)
+ if matches!(
+ &trace.cause.code().peel_derives(),
+ ObligationCauseCode::WhereClause(..)
+ | ObligationCauseCode::WhereClauseInExpr(..)
+ ) =>
+ {
+ // Hack to get around the borrow checker because trace.cause has an `Rc`.
+ if let ObligationCauseCode::WhereClause(_, span)
+ | ObligationCauseCode::WhereClauseInExpr(_, span, ..) =
+ &trace.cause.code().peel_derives()
+ && !span.is_dummy()
+ {
+ let span = *span;
+ self.report_concrete_failure(generic_param_scope, placeholder_origin, sub, sup)
+ .with_span_note(span, "the lifetime requirement is introduced here")
+ } else {
+ unreachable!(
+ "control flow ensures we have a `BindingObligation` or `WhereClauseInExpr` here..."
+ )
+ }
+ }
+ infer::Subtype(box trace) => {
+ let terr = TypeError::RegionsPlaceholderMismatch;
+ return self.report_and_explain_type_error(trace, terr);
+ }
+ _ => {
+ return self.report_concrete_failure(
+ generic_param_scope,
+ placeholder_origin,
+ sub,
+ sup,
+ );
+ }
+ }
+ }
+
+ pub fn report_generic_bound_failure(
+ &self,
+ generic_param_scope: LocalDefId,
+ span: Span,
+ origin: Option>,
+ bound_kind: GenericKind<'tcx>,
+ sub: Region<'tcx>,
+ ) -> ErrorGuaranteed {
+ self.construct_generic_bound_failure(generic_param_scope, span, origin, bound_kind, sub)
+ .emit()
+ }
+
+ pub fn construct_generic_bound_failure(
+ &self,
+ generic_param_scope: LocalDefId,
+ span: Span,
+ origin: Option>,
+ bound_kind: GenericKind<'tcx>,
+ sub: Region<'tcx>,
+ ) -> Diag<'a> {
+ if let Some(SubregionOrigin::CompareImplItemObligation {
+ span,
+ impl_item_def_id,
+ trait_item_def_id,
+ }) = origin
+ {
+ return self.infcx.report_extra_impl_obligation(
+ span,
+ impl_item_def_id,
+ trait_item_def_id,
+ &format!("`{bound_kind}: {sub}`"),
+ );
+ }
+
+ let labeled_user_string = match bound_kind {
+ GenericKind::Param(ref p) => format!("the parameter type `{p}`"),
+ GenericKind::Placeholder(ref p) => format!("the placeholder type `{p:?}`"),
+ GenericKind::Alias(ref p) => match p.kind(self.tcx) {
+ ty::Projection | ty::Inherent => {
+ format!("the associated type `{p}`")
+ }
+ ty::Weak => format!("the type alias `{p}`"),
+ ty::Opaque => format!("the opaque type `{p}`"),
+ },
+ };
+
+ let mut err = self
+ .tcx
+ .dcx()
+ .struct_span_err(span, format!("{labeled_user_string} may not live long enough"));
+ err.code(match sub.kind() {
+ ty::ReEarlyParam(_) | ty::ReLateParam(_) if sub.has_name() => E0309,
+ ty::ReStatic => E0310,
+ _ => E0311,
+ });
+
+ '_explain: {
+ let (description, span) = match sub.kind() {
+ ty::ReEarlyParam(_) | ty::ReLateParam(_) | ty::ReStatic => {
+ msg_span_from_named_region(self.tcx, generic_param_scope, sub, Some(span))
+ }
+ _ => (format!("lifetime `{sub}`"), Some(span)),
+ };
+ let prefix = format!("{labeled_user_string} must be valid for ");
+ label_msg_span(&mut err, &prefix, description, span, "...");
+ if let Some(origin) = origin {
+ self.note_region_origin(&mut err, &origin);
+ }
+ }
+
+ 'suggestion: {
+ let msg = "consider adding an explicit lifetime bound";
+
+ if (bound_kind, sub).has_infer_regions()
+ || (bound_kind, sub).has_placeholders()
+ || !bound_kind.is_suggestable(self.tcx, false)
+ {
+ let lt_name = sub.get_name_or_anon().to_string();
+ err.help(format!("{msg} `{bound_kind}: {lt_name}`..."));
+ break 'suggestion;
+ }
+
+ let mut generic_param_scope = generic_param_scope;
+ while self.tcx.def_kind(generic_param_scope) == DefKind::OpaqueTy {
+ generic_param_scope = self.tcx.local_parent(generic_param_scope);
+ }
+
+ // type_param_sugg_span is (span, has_bounds, needs_parentheses)
+ let (type_scope, type_param_sugg_span) = match bound_kind {
+ GenericKind::Param(param) => {
+ let generics = self.tcx.generics_of(generic_param_scope);
+ let type_param = generics.type_param(param, self.tcx);
+ let def_id = type_param.def_id.expect_local();
+ let scope = self.tcx.local_def_id_to_hir_id(def_id).owner.def_id;
+ // Get the `hir::Param` to verify whether it already has any bounds.
+ // We do this to avoid suggesting code that ends up as `T: 'a'b`,
+ // instead we suggest `T: 'a + 'b` in that case.
+ let hir_generics = self.tcx.hir().get_generics(scope).unwrap();
+ let sugg_span = match hir_generics.bounds_span_for_suggestions(def_id) {
+ Some((span, open_paren_sp)) => Some((span, true, open_paren_sp)),
+ // If `param` corresponds to `Self`, no usable suggestion span.
+ None if generics.has_self && param.index == 0 => None,
+ None => {
+ let span = if let Some(param) =
+ hir_generics.params.iter().find(|param| param.def_id == def_id)
+ && let ParamName::Plain(ident) = param.name
+ {
+ ident.span.shrink_to_hi()
+ } else {
+ let span = self.tcx.def_span(def_id);
+ span.shrink_to_hi()
+ };
+ Some((span, false, None))
+ }
+ };
+ (scope, sugg_span)
+ }
+ _ => (generic_param_scope, None),
+ };
+ let suggestion_scope = {
+ let lifetime_scope = match sub.kind() {
+ ty::ReStatic => hir::def_id::CRATE_DEF_ID,
+ _ => match self.tcx.is_suitable_region(generic_param_scope, sub) {
+ Some(info) => info.def_id,
+ None => generic_param_scope,
+ },
+ };
+ match self.tcx.is_descendant_of(type_scope.into(), lifetime_scope.into()) {
+ true => type_scope,
+ false => lifetime_scope,
+ }
+ };
+
+ let mut suggs = vec![];
+ let lt_name = self.suggest_name_region(generic_param_scope, sub, &mut suggs);
+
+ if let Some((sp, has_lifetimes, open_paren_sp)) = type_param_sugg_span
+ && suggestion_scope == type_scope
+ {
+ let suggestion =
+ if has_lifetimes { format!(" + {lt_name}") } else { format!(": {lt_name}") };
+
+ if let Some(open_paren_sp) = open_paren_sp {
+ suggs.push((open_paren_sp, "(".to_string()));
+ suggs.push((sp, format!("){suggestion}")));
+ } else {
+ suggs.push((sp, suggestion))
+ }
+ } else if let GenericKind::Alias(ref p) = bound_kind
+ && let ty::Projection = p.kind(self.tcx)
+ && let DefKind::AssocTy = self.tcx.def_kind(p.def_id)
+ && let Some(ty::ImplTraitInTraitData::Trait { .. }) =
+ self.tcx.opt_rpitit_info(p.def_id)
+ {
+ // The lifetime found in the `impl` is longer than the one on the RPITIT.
+ // Do not suggest `::{opaque}: 'static`.
+ } else if let Some(generics) = self.tcx.hir().get_generics(suggestion_scope) {
+ let pred = format!("{bound_kind}: {lt_name}");
+ let suggestion = format!("{} {}", generics.add_where_or_trailing_comma(), pred);
+ suggs.push((generics.tail_span_for_predicate_suggestion(), suggestion))
+ } else {
+ let consider = format!("{msg} `{bound_kind}: {sub}`...");
+ err.help(consider);
+ }
+
+ if !suggs.is_empty() {
+ err.multipart_suggestion_verbose(
+ msg,
+ suggs,
+ Applicability::MaybeIncorrect, // Issue #41966
+ );
+ }
+ }
+
+ err
+ }
+
+ pub fn suggest_name_region(
+ &self,
+ generic_param_scope: LocalDefId,
+ lifetime: Region<'tcx>,
+ add_lt_suggs: &mut Vec<(Span, String)>,
+ ) -> String {
+ struct LifetimeReplaceVisitor<'tcx, 'a> {
+ tcx: TyCtxt<'tcx>,
+ needle: hir::LifetimeName,
+ new_lt: &'a str,
+ add_lt_suggs: &'a mut Vec<(Span, String)>,
+ }
+
+ impl<'hir, 'tcx> hir::intravisit::Visitor<'hir> for LifetimeReplaceVisitor<'tcx, '_> {
+ fn visit_lifetime(&mut self, lt: &'hir hir::Lifetime) {
+ if lt.res == self.needle {
+ let (pos, span) = lt.suggestion_position();
+ let new_lt = &self.new_lt;
+ let sugg = match pos {
+ hir::LifetimeSuggestionPosition::Normal => format!("{new_lt}"),
+ hir::LifetimeSuggestionPosition::Ampersand => format!("{new_lt} "),
+ hir::LifetimeSuggestionPosition::ElidedPath => format!("<{new_lt}>"),
+ hir::LifetimeSuggestionPosition::ElidedPathArgument => {
+ format!("{new_lt}, ")
+ }
+ hir::LifetimeSuggestionPosition::ObjectDefault => format!("+ {new_lt}"),
+ };
+ self.add_lt_suggs.push((span, sugg));
+ }
+ }
+
+ fn visit_ty(&mut self, ty: &'hir hir::Ty<'hir>) {
+ let hir::TyKind::OpaqueDef(item_id, _, _) = ty.kind else {
+ return hir::intravisit::walk_ty(self, ty);
+ };
+ let opaque_ty = self.tcx.hir().item(item_id).expect_opaque_ty();
+ if let Some(&(_, b)) =
+ opaque_ty.lifetime_mapping.iter().find(|&(a, _)| a.res == self.needle)
+ {
+ let prev_needle =
+ std::mem::replace(&mut self.needle, hir::LifetimeName::Param(b));
+ for bound in opaque_ty.bounds {
+ self.visit_param_bound(bound);
+ }
+ self.needle = prev_needle;
+ }
+ }
+ }
+
+ let (lifetime_def_id, lifetime_scope) =
+ match self.tcx.is_suitable_region(generic_param_scope, lifetime) {
+ Some(info) if !lifetime.has_name() => {
+ (info.bound_region.get_id().unwrap().expect_local(), info.def_id)
+ }
+ _ => return lifetime.get_name_or_anon().to_string(),
+ };
+
+ let new_lt = {
+ let generics = self.tcx.generics_of(lifetime_scope);
+ let mut used_names =
+ iter::successors(Some(generics), |g| g.parent.map(|p| self.tcx.generics_of(p)))
+ .flat_map(|g| &g.own_params)
+ .filter(|p| matches!(p.kind, ty::GenericParamDefKind::Lifetime))
+ .map(|p| p.name)
+ .collect::>();
+ let hir_id = self.tcx.local_def_id_to_hir_id(lifetime_scope);
+ // consider late-bound lifetimes ...
+ used_names.extend(self.tcx.late_bound_vars(hir_id).into_iter().filter_map(
+ |p| match p {
+ ty::BoundVariableKind::Region(lt) => lt.get_name(),
+ _ => None,
+ },
+ ));
+ (b'a'..=b'z')
+ .map(|c| format!("'{}", c as char))
+ .find(|candidate| !used_names.iter().any(|e| e.as_str() == candidate))
+ .unwrap_or("'lt".to_string())
+ };
+
+ let mut visitor = LifetimeReplaceVisitor {
+ tcx: self.tcx,
+ needle: hir::LifetimeName::Param(lifetime_def_id),
+ add_lt_suggs,
+ new_lt: &new_lt,
+ };
+ match self.tcx.expect_hir_owner_node(lifetime_scope) {
+ hir::OwnerNode::Item(i) => visitor.visit_item(i),
+ hir::OwnerNode::ForeignItem(i) => visitor.visit_foreign_item(i),
+ hir::OwnerNode::ImplItem(i) => visitor.visit_impl_item(i),
+ hir::OwnerNode::TraitItem(i) => visitor.visit_trait_item(i),
+ hir::OwnerNode::Crate(_) => bug!("OwnerNode::Crate doesn't not have generics"),
+ hir::OwnerNode::Synthetic => unreachable!(),
+ }
+
+ let ast_generics = self.tcx.hir().get_generics(lifetime_scope).unwrap();
+ let sugg = ast_generics
+ .span_for_lifetime_suggestion()
+ .map(|span| (span, format!("{new_lt}, ")))
+ .unwrap_or_else(|| (ast_generics.span, format!("<{new_lt}>")));
+ add_lt_suggs.push(sugg);
+
+ new_lt
+ }
+
+ fn report_sub_sup_conflict(
+ &self,
+ generic_param_scope: LocalDefId,
+ var_origin: RegionVariableOrigin,
+ sub_origin: SubregionOrigin<'tcx>,
+ sub_region: Region<'tcx>,
+ sup_origin: SubregionOrigin<'tcx>,
+ sup_region: Region<'tcx>,
+ ) -> ErrorGuaranteed {
+ let mut err = self.report_inference_failure(var_origin);
+
+ note_and_explain_region(
+ self.tcx,
+ &mut err,
+ generic_param_scope,
+ "first, the lifetime cannot outlive ",
+ sup_region,
+ "...",
+ None,
+ );
+
+ debug!("report_sub_sup_conflict: var_origin={:?}", var_origin);
+ debug!("report_sub_sup_conflict: sub_region={:?}", sub_region);
+ debug!("report_sub_sup_conflict: sub_origin={:?}", sub_origin);
+ debug!("report_sub_sup_conflict: sup_region={:?}", sup_region);
+ debug!("report_sub_sup_conflict: sup_origin={:?}", sup_origin);
+
+ if let infer::Subtype(ref sup_trace) = sup_origin
+ && let infer::Subtype(ref sub_trace) = sub_origin
+ && let Some((sup_expected, sup_found, _)) = self.values_str(sup_trace.values)
+ && let Some((sub_expected, sub_found, _)) = self.values_str(sub_trace.values)
+ && sub_expected == sup_expected
+ && sub_found == sup_found
+ {
+ note_and_explain_region(
+ self.tcx,
+ &mut err,
+ generic_param_scope,
+ "...but the lifetime must also be valid for ",
+ sub_region,
+ "...",
+ None,
+ );
+ err.span_note(
+ sup_trace.cause.span,
+ format!("...so that the {}", sup_trace.cause.as_requirement_str()),
+ );
+
+ err.note_expected_found(&"", sup_expected, &"", sup_found);
+ return if sub_region.is_error() | sup_region.is_error() {
+ err.delay_as_bug()
+ } else {
+ err.emit()
+ };
+ }
+
+ self.note_region_origin(&mut err, &sup_origin);
+
+ note_and_explain_region(
+ self.tcx,
+ &mut err,
+ generic_param_scope,
+ "but, the lifetime must be valid for ",
+ sub_region,
+ "...",
+ None,
+ );
+
+ self.note_region_origin(&mut err, &sub_origin);
+ if sub_region.is_error() | sup_region.is_error() { err.delay_as_bug() } else { err.emit() }
+ }
+
+ fn report_inference_failure(&self, var_origin: RegionVariableOrigin) -> Diag<'_> {
+ let br_string = |br: ty::BoundRegionKind| {
+ let mut s = match br {
+ ty::BrNamed(_, name) => name.to_string(),
+ _ => String::new(),
+ };
+ if !s.is_empty() {
+ s.push(' ');
+ }
+ s
+ };
+ let var_description = match var_origin {
+ infer::MiscVariable(_) => String::new(),
+ infer::PatternRegion(_) => " for pattern".to_string(),
+ infer::AddrOfRegion(_) => " for borrow expression".to_string(),
+ infer::Autoref(_) => " for autoref".to_string(),
+ infer::Coercion(_) => " for automatic coercion".to_string(),
+ infer::BoundRegion(_, br, infer::FnCall) => {
+ format!(" for lifetime parameter {}in function call", br_string(br))
+ }
+ infer::BoundRegion(_, br, infer::HigherRankedType) => {
+ format!(" for lifetime parameter {}in generic type", br_string(br))
+ }
+ infer::BoundRegion(_, br, infer::AssocTypeProjection(def_id)) => format!(
+ " for lifetime parameter {}in trait containing associated type `{}`",
+ br_string(br),
+ self.tcx.associated_item(def_id).name
+ ),
+ infer::RegionParameterDefinition(_, name) => {
+ format!(" for lifetime parameter `{name}`")
+ }
+ infer::UpvarRegion(ref upvar_id, _) => {
+ let var_name = self.tcx.hir().name(upvar_id.var_path.hir_id);
+ format!(" for capture of `{var_name}` by closure")
+ }
+ infer::Nll(..) => bug!("NLL variable found in lexical phase"),
+ };
+
+ struct_span_code_err!(
+ self.dcx(),
+ var_origin.span(),
+ E0495,
+ "cannot infer an appropriate lifetime{} due to conflicting requirements",
+ var_description
+ )
+ }
+}
+
+pub(super) fn note_and_explain_region<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ err: &mut Diag<'_>,
+ generic_param_scope: LocalDefId,
+ prefix: &str,
+ region: ty::Region<'tcx>,
+ suffix: &str,
+ alt_span: Option,
+) {
+ let (description, span) = match *region {
+ ty::ReEarlyParam(_) | ty::ReLateParam(_) | ty::RePlaceholder(_) | ty::ReStatic => {
+ msg_span_from_named_region(tcx, generic_param_scope, region, alt_span)
+ }
+
+ ty::ReError(_) => return,
+
+ // FIXME(#125431): `ReVar` shouldn't reach here.
+ ty::ReVar(_) => (format!("lifetime `{region}`"), alt_span),
+
+ ty::ReBound(..) | ty::ReErased => {
+ bug!("unexpected region for note_and_explain_region: {:?}", region);
+ }
+ };
+
+ emit_msg_span(err, prefix, description, span, suffix);
+}
+
+fn explain_free_region<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ err: &mut Diag<'_>,
+ generic_param_scope: LocalDefId,
+ prefix: &str,
+ region: ty::Region<'tcx>,
+ suffix: &str,
+) {
+ let (description, span) = msg_span_from_named_region(tcx, generic_param_scope, region, None);
+
+ label_msg_span(err, prefix, description, span, suffix);
+}
+
+fn msg_span_from_named_region<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ generic_param_scope: LocalDefId,
+ region: ty::Region<'tcx>,
+ alt_span: Option,
+) -> (String, Option) {
+ match *region {
+ ty::ReEarlyParam(br) => {
+ let scope = tcx
+ .parent(tcx.generics_of(generic_param_scope).region_param(br, tcx).def_id)
+ .expect_local();
+ let span = if let Some(param) =
+ tcx.hir().get_generics(scope).and_then(|generics| generics.get_named(br.name))
+ {
+ param.span
+ } else {
+ tcx.def_span(scope)
+ };
+ let text = if br.has_name() {
+ format!("the lifetime `{}` as defined here", br.name)
+ } else {
+ "the anonymous lifetime as defined here".to_string()
+ };
+ (text, Some(span))
+ }
+ ty::ReLateParam(ref fr) => {
+ if !fr.bound_region.is_named()
+ && let Some((ty, _)) =
+ find_anon_type(tcx, generic_param_scope, region, &fr.bound_region)
+ {
+ ("the anonymous lifetime defined here".to_string(), Some(ty.span))
+ } else {
+ match fr.bound_region {
+ ty::BoundRegionKind::BrNamed(_, name) => {
+ let span = if let Some(param) = tcx
+ .hir()
+ .get_generics(generic_param_scope)
+ .and_then(|generics| generics.get_named(name))
+ {
+ param.span
+ } else {
+ tcx.def_span(generic_param_scope)
+ };
+ let text = if name == kw::UnderscoreLifetime {
+ "the anonymous lifetime as defined here".to_string()
+ } else {
+ format!("the lifetime `{name}` as defined here")
+ };
+ (text, Some(span))
+ }
+ ty::BrAnon => (
+ "the anonymous lifetime as defined here".to_string(),
+ Some(tcx.def_span(generic_param_scope)),
+ ),
+ _ => (
+ format!("the lifetime `{region}` as defined here"),
+ Some(tcx.def_span(generic_param_scope)),
+ ),
+ }
+ }
+ }
+ ty::ReStatic => ("the static lifetime".to_owned(), alt_span),
+ ty::RePlaceholder(ty::PlaceholderRegion {
+ bound: ty::BoundRegion { kind: ty::BoundRegionKind::BrNamed(def_id, name), .. },
+ ..
+ }) => (format!("the lifetime `{name}` as defined here"), Some(tcx.def_span(def_id))),
+ ty::RePlaceholder(ty::PlaceholderRegion {
+ bound: ty::BoundRegion { kind: ty::BoundRegionKind::BrAnon, .. },
+ ..
+ }) => ("an anonymous lifetime".to_owned(), None),
+ _ => bug!("{:?}", region),
+ }
+}
+
+fn emit_msg_span(
+ err: &mut Diag<'_>,
+ prefix: &str,
+ description: String,
+ span: Option,
+ suffix: &str,
+) {
+ let message = format!("{prefix}{description}{suffix}");
+
+ if let Some(span) = span {
+ err.span_note(span, message);
+ } else {
+ err.note(message);
+ }
+}
+
+fn label_msg_span(
+ err: &mut Diag<'_>,
+ prefix: &str,
+ description: String,
+ span: Option,
+ suffix: &str,
+) {
+ let message = format!("{prefix}{description}{suffix}");
+
+ if let Some(span) = span {
+ err.span_label(span, message);
+ } else {
+ err.note(message);
+ }
+}
+
+#[instrument(level = "trace", skip(infcx))]
+pub fn unexpected_hidden_region_diagnostic<'a, 'tcx>(
+ infcx: &'a InferCtxt<'tcx>,
+ generic_param_scope: LocalDefId,
+ span: Span,
+ hidden_ty: Ty<'tcx>,
+ hidden_region: ty::Region<'tcx>,
+ opaque_ty_key: ty::OpaqueTypeKey<'tcx>,
+) -> Diag<'a> {
+ let tcx = infcx.tcx;
+ let mut err = infcx.dcx().create_err(errors::OpaqueCapturesLifetime {
+ span,
+ opaque_ty: Ty::new_opaque(tcx, opaque_ty_key.def_id.to_def_id(), opaque_ty_key.args),
+ opaque_ty_span: tcx.def_span(opaque_ty_key.def_id),
+ });
+
+ // Explain the region we are capturing.
+ match *hidden_region {
+ ty::ReEarlyParam(_) | ty::ReLateParam(_) | ty::ReStatic => {
+ // Assuming regionck succeeded (*), we ought to always be
+ // capturing *some* region from the fn header, and hence it
+ // ought to be free. So under normal circumstances, we will go
+ // down this path which gives a decent human readable
+ // explanation.
+ //
+ // (*) if not, the `tainted_by_errors` field would be set to
+ // `Some(ErrorGuaranteed)` in any case, so we wouldn't be here at all.
+ explain_free_region(
+ tcx,
+ &mut err,
+ generic_param_scope,
+ &format!("hidden type `{hidden_ty}` captures "),
+ hidden_region,
+ "",
+ );
+ if let Some(reg_info) = tcx.is_suitable_region(generic_param_scope, hidden_region) {
+ if infcx.tcx.features().precise_capturing {
+ suggest_precise_capturing(tcx, opaque_ty_key.def_id, hidden_region, &mut err);
+ } else {
+ let fn_returns = tcx.return_type_impl_or_dyn_traits(reg_info.def_id);
+ nice_region_error::suggest_new_region_bound(
+ tcx,
+ &mut err,
+ fn_returns,
+ hidden_region.to_string(),
+ None,
+ format!("captures `{hidden_region}`"),
+ None,
+ Some(reg_info.def_id),
+ )
+ }
+ }
+ }
+ ty::RePlaceholder(_) => {
+ explain_free_region(
+ tcx,
+ &mut err,
+ generic_param_scope,
+ &format!("hidden type `{}` captures ", hidden_ty),
+ hidden_region,
+ "",
+ );
+ }
+ ty::ReError(_) => {
+ err.downgrade_to_delayed_bug();
+ }
+ _ => {
+ // Ugh. This is a painful case: the hidden region is not one
+ // that we can easily summarize or explain. This can happen
+ // in a case like
+ // `tests/ui/multiple-lifetimes/ordinary-bounds-unsuited.rs`:
+ //
+ // ```
+ // fn upper_bounds<'a, 'b>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'a, 'b> {
+ // if condition() { a } else { b }
+ // }
+ // ```
+ //
+ // Here the captured lifetime is the intersection of `'a` and
+ // `'b`, which we can't quite express.
+
+ // We can at least report a really cryptic error for now.
+ note_and_explain_region(
+ tcx,
+ &mut err,
+ generic_param_scope,
+ &format!("hidden type `{hidden_ty}` captures "),
+ hidden_region,
+ "",
+ None,
+ );
+ }
+ }
+
+ err
+}
+
+fn suggest_precise_capturing<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ opaque_def_id: LocalDefId,
+ captured_lifetime: ty::Region<'tcx>,
+ diag: &mut Diag<'_>,
+) {
+ let hir::OpaqueTy { bounds, .. } =
+ tcx.hir_node_by_def_id(opaque_def_id).expect_item().expect_opaque_ty();
+
+ let new_lifetime = Symbol::intern(&captured_lifetime.to_string());
+
+ if let Some((args, span)) = bounds.iter().find_map(|bound| match bound {
+ hir::GenericBound::Use(args, span) => Some((args, span)),
+ _ => None,
+ }) {
+ let last_lifetime_span = args.iter().rev().find_map(|arg| match arg {
+ hir::PreciseCapturingArg::Lifetime(lt) => Some(lt.ident.span),
+ _ => None,
+ });
+
+ let first_param_span = args.iter().find_map(|arg| match arg {
+ hir::PreciseCapturingArg::Param(p) => Some(p.ident.span),
+ _ => None,
+ });
+
+ let (span, pre, post) = if let Some(last_lifetime_span) = last_lifetime_span {
+ (last_lifetime_span.shrink_to_hi(), ", ", "")
+ } else if let Some(first_param_span) = first_param_span {
+ (first_param_span.shrink_to_lo(), "", ", ")
+ } else {
+ // If we have no args, then have `use<>` and need to fall back to using
+ // span math. This sucks, but should be reliable due to the construction
+ // of the `use<>` span.
+ (span.with_hi(span.hi() - BytePos(1)).shrink_to_hi(), "", "")
+ };
+
+ diag.subdiagnostic(errors::AddPreciseCapturing::Existing { span, new_lifetime, pre, post });
+ } else {
+ let mut captured_lifetimes = FxIndexSet::default();
+ let mut captured_non_lifetimes = FxIndexSet::default();
+
+ let variances = tcx.variances_of(opaque_def_id);
+ let mut generics = tcx.generics_of(opaque_def_id);
+ loop {
+ for param in &generics.own_params {
+ if variances[param.index as usize] == ty::Bivariant {
+ continue;
+ }
+
+ match param.kind {
+ ty::GenericParamDefKind::Lifetime => {
+ captured_lifetimes.insert(param.name);
+ }
+ ty::GenericParamDefKind::Type { synthetic: true, .. } => {
+ // FIXME: We can't provide a good suggestion for
+ // `use<...>` if we have an APIT. Bail for now.
+ return;
+ }
+ ty::GenericParamDefKind::Type { .. }
+ | ty::GenericParamDefKind::Const { .. } => {
+ captured_non_lifetimes.insert(param.name);
+ }
+ }
+ }
+
+ if let Some(parent) = generics.parent {
+ generics = tcx.generics_of(parent);
+ } else {
+ break;
+ }
+ }
+
+ if !captured_lifetimes.insert(new_lifetime) {
+ // Uh, strange. This lifetime appears to already be captured...
+ return;
+ }
+
+ let concatenated_bounds = captured_lifetimes
+ .into_iter()
+ .chain(captured_non_lifetimes)
+ .map(|sym| sym.to_string())
+ .collect::>()
+ .join(", ");
+
+ diag.subdiagnostic(errors::AddPreciseCapturing::New {
+ span: tcx.def_span(opaque_def_id).shrink_to_hi(),
+ new_lifetime,
+ concatenated_bounds,
+ });
+ }
+}
diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs
index 9f55939c165e..a0be545d46fe 100644
--- a/compiler/rustc_infer/src/infer/mod.rs
+++ b/compiler/rustc_infer/src/infer/mod.rs
@@ -1264,6 +1264,9 @@ impl<'tcx> InferCtxt<'tcx> {
where
T: TypeFoldable>,
{
+ if let Err(guar) = value.error_reported() {
+ self.set_tainted_by_errors(guar);
+ }
if !value.has_non_region_infer() {
return value;
}
diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs
index e2ba75dfd190..7d7a6a08bee4 100644
--- a/compiler/rustc_interface/src/tests.rs
+++ b/compiler/rustc_interface/src/tests.rs
@@ -691,6 +691,7 @@ fn test_unstable_options_tracking_hash() {
untracked!(dump_mir, Some(String::from("abc")));
untracked!(dump_mir_dataflow, true);
untracked!(dump_mir_dir, String::from("abc"));
+ untracked!(dump_mir_exclude_alloc_bytes, true);
untracked!(dump_mir_exclude_pass_number, true);
untracked!(dump_mir_graphviz, true);
untracked!(dump_mono_stats, SwitchWithOptPath::Enabled(Some("mono-items-dir/".into())));
diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl
index 3e952558d29d..de04d882f516 100644
--- a/compiler/rustc_lint/messages.ftl
+++ b/compiler/rustc_lint/messages.ftl
@@ -52,10 +52,6 @@ lint_builtin_allow_internal_unsafe =
lint_builtin_anonymous_params = anonymous parameters are deprecated and will be removed in the next edition
.suggestion = try naming the parameter or explicitly ignoring it
-lint_builtin_asm_labels = avoid using named labels in inline assembly
- .help = only local labels of the form `:` should be used in inline asm
- .note = see the asm section of Rust By Example for more information
-
lint_builtin_clashing_extern_diff_name = `{$this}` redeclares `{$orig}` with a different signature
.previous_decl_label = `{$orig}` previously declared here
.mismatch_label = this signature doesn't match the previous declaration
@@ -163,6 +159,8 @@ lint_builtin_unreachable_pub = unreachable `pub` {$what}
lint_builtin_unsafe_block = usage of an `unsafe` block
+lint_builtin_unsafe_extern_block = usage of an `unsafe extern` block
+
lint_builtin_unsafe_impl = implementation of an `unsafe` trait
lint_builtin_unsafe_trait = declaration of an `unsafe` trait
@@ -185,6 +183,10 @@ lint_cfg_attr_no_attributes =
lint_check_name_unknown_tool = unknown lint tool: `{$tool_name}`
+lint_closure_returning_async_block = closure returning async block can be made into an async closure
+ .label = this async block can be removed, and the closure can be turned into an async closure
+ .suggestion = turn this into an async closure
+
lint_command_line_source = `forbid` lint level was set on command line
lint_confusable_identifier_pair = found both `{$existing_sym}` and `{$sym}` as identifiers, which look alike
@@ -399,6 +401,19 @@ lint_incomplete_include =
lint_inner_macro_attribute_unstable = inner macro attributes are unstable
+lint_invalid_asm_label_binary = avoid using labels containing only the digits `0` and `1` in inline assembly
+ .label = use a different label that doesn't start with `0` or `1`
+ .note = an LLVM bug makes these labels ambiguous with a binary literal number
+ .note = see for more information
+
+lint_invalid_asm_label_format_arg = avoid using named labels in inline assembly
+ .help = only local labels of the form `:` should be used in inline asm
+ .note1 = format arguments may expand to a non-numeric value
+ .note2 = see the asm section of Rust By Example for more information
+lint_invalid_asm_label_named = avoid using named labels in inline assembly
+ .help = only local labels of the form `