From 46f6e39ac61241729b81671ed8fb917b8bb2e9e5 Mon Sep 17 00:00:00 2001 From: raffimolero <49224759+raffimolero@users.noreply.github.com> Date: Mon, 12 Dec 2022 12:28:40 +0800 Subject: [PATCH 01/30] add assert messages if chunks/windows are length 0 --- library/core/src/slice/mod.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index d9281a9252c08..9e89c9f832fa4 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -893,7 +893,7 @@ impl [T] { #[stable(feature = "chunks_exact", since = "1.31.0")] #[inline] pub fn chunks_exact(&self, chunk_size: usize) -> ChunksExact<'_, T> { - assert_ne!(chunk_size, 0); + assert_ne!(chunk_size, 0, "chunks cannot have a size of zero"); ChunksExact::new(self, chunk_size) } @@ -935,7 +935,7 @@ impl [T] { #[stable(feature = "chunks_exact", since = "1.31.0")] #[inline] pub fn chunks_exact_mut(&mut self, chunk_size: usize) -> ChunksExactMut<'_, T> { - assert_ne!(chunk_size, 0); + assert_ne!(chunk_size, 0, "chunks cannot have a size of zero"); ChunksExactMut::new(self, chunk_size) } @@ -1006,7 +1006,7 @@ impl [T] { #[inline] #[must_use] pub fn as_chunks(&self) -> (&[[T; N]], &[T]) { - assert_ne!(N, 0); + assert_ne!(N, 0, "chunks cannot have a size of zero"); let len = self.len() / N; let (multiple_of_n, remainder) = self.split_at(len * N); // SAFETY: We already panicked for zero, and ensured by construction @@ -1037,7 +1037,7 @@ impl [T] { #[inline] #[must_use] pub fn as_rchunks(&self) -> (&[T], &[[T; N]]) { - assert_ne!(N, 0); + assert_ne!(N, 0, "chunks cannot have a size of zero"); let len = self.len() / N; let (remainder, multiple_of_n) = self.split_at(self.len() - len * N); // SAFETY: We already panicked for zero, and ensured by construction @@ -1076,7 +1076,7 @@ impl [T] { #[unstable(feature = "array_chunks", issue = "74985")] #[inline] pub fn array_chunks(&self) -> ArrayChunks<'_, T, N> { - assert_ne!(N, 0); + assert_ne!(N, 0, "chunks cannot have a size of zero"); ArrayChunks::new(self) } @@ -1155,7 +1155,7 @@ impl [T] { #[inline] #[must_use] pub fn as_chunks_mut(&mut self) -> (&mut [[T; N]], &mut [T]) { - assert_ne!(N, 0); + assert_ne!(N, 0, "chunks cannot have a size of zero"); let len = self.len() / N; let (multiple_of_n, remainder) = self.split_at_mut(len * N); // SAFETY: We already panicked for zero, and ensured by construction @@ -1192,7 +1192,7 @@ impl [T] { #[inline] #[must_use] pub fn as_rchunks_mut(&mut self) -> (&mut [T], &mut [[T; N]]) { - assert_ne!(N, 0); + assert_ne!(N, 0, "chunks cannot have a size of zero"); let len = self.len() / N; let (remainder, multiple_of_n) = self.split_at_mut(self.len() - len * N); // SAFETY: We already panicked for zero, and ensured by construction @@ -1233,7 +1233,7 @@ impl [T] { #[unstable(feature = "array_chunks", issue = "74985")] #[inline] pub fn array_chunks_mut(&mut self) -> ArrayChunksMut<'_, T, N> { - assert_ne!(N, 0); + assert_ne!(N, 0, "chunks cannot have a size of zero"); ArrayChunksMut::new(self) } @@ -1265,7 +1265,7 @@ impl [T] { #[unstable(feature = "array_windows", issue = "75027")] #[inline] pub fn array_windows(&self) -> ArrayWindows<'_, T, N> { - assert_ne!(N, 0); + assert_ne!(N, 0, "windows cannot have a size of zero"); ArrayWindows::new(self) } From 158894464c5e4b143c7b307f932d9eb51afc24ee Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 12 Dec 2022 11:10:19 +0100 Subject: [PATCH 02/30] interpret: add read_machine_[ui]size convenience methods --- .../src/interpret/intrinsics.rs | 8 ++--- .../rustc_const_eval/src/interpret/operand.rs | 11 +++++++ .../src/interpret/projection.rs | 4 +-- src/tools/miri/src/eval.rs | 2 +- src/tools/miri/src/shims/env.rs | 2 +- src/tools/miri/src/shims/foreign_items.rs | 32 +++++++++---------- src/tools/miri/src/shims/intrinsics/mod.rs | 4 +-- src/tools/miri/src/shims/mod.rs | 2 +- .../miri/src/shims/unix/foreign_items.rs | 18 +++++------ src/tools/miri/src/shims/unix/fs.rs | 8 ++--- .../src/shims/unix/linux/foreign_items.rs | 6 ++-- src/tools/miri/src/shims/unix/macos/dlsym.rs | 2 +- .../src/shims/unix/macos/foreign_items.rs | 4 +-- src/tools/miri/src/shims/unix/thread.rs | 4 +-- src/tools/miri/src/shims/windows/dlsym.rs | 4 +-- .../miri/src/shims/windows/foreign_items.rs | 16 +++++----- src/tools/miri/src/shims/windows/sync.rs | 2 +- src/tools/miri/src/shims/windows/thread.rs | 2 +- 18 files changed, 71 insertions(+), 60 deletions(-) diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index b9be7fa48000b..7031a0b45d6ec 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -305,7 +305,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } sym::offset => { let ptr = self.read_pointer(&args[0])?; - let offset_count = self.read_scalar(&args[1])?.to_machine_isize(self)?; + let offset_count = self.read_machine_isize(&args[1])?; let pointee_ty = substs.type_at(0); let offset_ptr = self.ptr_offset_inbounds(ptr, pointee_ty, offset_count)?; @@ -313,7 +313,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } sym::arith_offset => { let ptr = self.read_pointer(&args[0])?; - let offset_count = self.read_scalar(&args[1])?.to_machine_isize(self)?; + let offset_count = self.read_machine_isize(&args[1])?; let pointee_ty = substs.type_at(0); let pointee_size = i64::try_from(self.layout_of(pointee_ty)?.size.bytes()).unwrap(); @@ -670,7 +670,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { count: &OpTy<'tcx, >::Provenance>, nonoverlapping: bool, ) -> InterpResult<'tcx> { - let count = self.read_scalar(&count)?.to_machine_usize(self)?; + let count = self.read_machine_usize(&count)?; let layout = self.layout_of(src.layout.ty.builtin_deref(true).unwrap().ty)?; let (size, align) = (layout.size, layout.align.abi); // `checked_mul` enforces a too small bound (the correct one would probably be machine_isize_max), @@ -698,7 +698,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let dst = self.read_pointer(&dst)?; let byte = self.read_scalar(&byte)?.to_u8()?; - let count = self.read_scalar(&count)?.to_machine_usize(self)?; + let count = self.read_machine_usize(&count)?; // `checked_mul` enforces a too small bound (the correct one would probably be machine_isize_max), // but no actual allocation can be big enough for the difference to be noticeable. diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs index 221e359d24ab8..68a3318ab4e0b 100644 --- a/compiler/rustc_const_eval/src/interpret/operand.rs +++ b/compiler/rustc_const_eval/src/interpret/operand.rs @@ -407,6 +407,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Ok(self.read_immediate(op)?.to_scalar()) } + // Pointer-sized reads are fairly common and need target layout access, so we wrap them in + // convenience functions. + /// Read a pointer from a place. pub fn read_pointer( &self, @@ -414,6 +417,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ) -> InterpResult<'tcx, Pointer>> { self.read_scalar(op)?.to_pointer(self) } + /// Read a pointer-sized unsigned integer from a place. + pub fn read_machine_usize(&self, op: &OpTy<'tcx, M::Provenance>) -> InterpResult<'tcx, u64> { + self.read_scalar(op)?.to_machine_usize(self) + } + /// Read a pointer-sized signed integer from a place. + pub fn read_machine_isize(&self, op: &OpTy<'tcx, M::Provenance>) -> InterpResult<'tcx, i64> { + self.read_scalar(op)?.to_machine_isize(self) + } /// Turn the wide MPlace into a string (must already be dereferenced!) pub fn read_str(&self, mplace: &MPlaceTy<'tcx, M::Provenance>) -> InterpResult<'tcx, &str> { diff --git a/compiler/rustc_const_eval/src/interpret/projection.rs b/compiler/rustc_const_eval/src/interpret/projection.rs index 2ffd73eef3ef8..291464ab58ae2 100644 --- a/compiler/rustc_const_eval/src/interpret/projection.rs +++ b/compiler/rustc_const_eval/src/interpret/projection.rs @@ -363,7 +363,7 @@ where Index(local) => { let layout = self.layout_of(self.tcx.types.usize)?; let n = self.local_to_op(self.frame(), local, Some(layout))?; - let n = self.read_scalar(&n)?.to_machine_usize(self)?; + let n = self.read_machine_usize(&n)?; self.place_index(base, n)? } ConstantIndex { offset, min_length, from_end } => { @@ -392,7 +392,7 @@ where Index(local) => { let layout = self.layout_of(self.tcx.types.usize)?; let n = self.local_to_op(self.frame(), local, Some(layout))?; - let n = self.read_scalar(&n)?.to_machine_usize(self)?; + let n = self.read_machine_usize(&n)?; self.operand_index(base, n)? } ConstantIndex { offset, min_length, from_end } => { diff --git a/src/tools/miri/src/eval.rs b/src/tools/miri/src/eval.rs index 7b4973f3b9daf..30288e5a999bb 100644 --- a/src/tools/miri/src/eval.rs +++ b/src/tools/miri/src/eval.rs @@ -233,7 +233,7 @@ impl MainThreadState { this.machine.main_fn_ret_place.unwrap().ptr, this.machine.layouts.isize, ); - let exit_code = this.read_scalar(&ret_place.into())?.to_machine_isize(this)?; + let exit_code = this.read_machine_isize(&ret_place.into())?; // Need to call this ourselves since we are not going to return to the scheduler // loop, and we want the main thread TLS to not show up as memory leaks. this.terminate_active_thread()?; diff --git a/src/tools/miri/src/shims/env.rs b/src/tools/miri/src/shims/env.rs index 80fb4ff2fe980..218aa89b3f9b0 100644 --- a/src/tools/miri/src/shims/env.rs +++ b/src/tools/miri/src/shims/env.rs @@ -321,7 +321,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.assert_target_os_is_unix("getcwd"); let buf = this.read_pointer(buf_op)?; - let size = this.read_scalar(size_op)?.to_machine_usize(&*this.tcx)?; + let size = this.read_machine_usize(size_op)?; if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`getcwd`", reject_with)?; diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index 8370e02b588af..b7ed63e17c5b2 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -485,14 +485,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // Standard C allocation "malloc" => { let [size] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - let size = this.read_scalar(size)?.to_machine_usize(this)?; + let size = this.read_machine_usize(size)?; let res = this.malloc(size, /*zero_init:*/ false, MiriMemoryKind::C)?; this.write_pointer(res, dest)?; } "calloc" => { let [items, len] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - let items = this.read_scalar(items)?.to_machine_usize(this)?; - let len = this.read_scalar(len)?.to_machine_usize(this)?; + let items = this.read_machine_usize(items)?; + let len = this.read_machine_usize(len)?; let size = items.checked_mul(len).ok_or_else(|| err_ub_format!("overflow during calloc size computation"))?; let res = this.malloc(size, /*zero_init:*/ true, MiriMemoryKind::C)?; @@ -506,7 +506,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { "realloc" => { let [old_ptr, new_size] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; let old_ptr = this.read_pointer(old_ptr)?; - let new_size = this.read_scalar(new_size)?.to_machine_usize(this)?; + let new_size = this.read_machine_usize(new_size)?; let res = this.realloc(old_ptr, new_size, MiriMemoryKind::C)?; this.write_pointer(res, dest)?; } @@ -514,8 +514,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // Rust allocation "__rust_alloc" | "miri_alloc" => { let [size, align] = this.check_shim(abi, Abi::Rust, link_name, args)?; - let size = this.read_scalar(size)?.to_machine_usize(this)?; - let align = this.read_scalar(align)?.to_machine_usize(this)?; + let size = this.read_machine_usize(size)?; + let align = this.read_machine_usize(align)?; let default = |this: &mut MiriInterpCx<'mir, 'tcx>| { Self::check_alloc_request(size, align)?; @@ -546,8 +546,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } "__rust_alloc_zeroed" => { let [size, align] = this.check_shim(abi, Abi::Rust, link_name, args)?; - let size = this.read_scalar(size)?.to_machine_usize(this)?; - let align = this.read_scalar(align)?.to_machine_usize(this)?; + let size = this.read_machine_usize(size)?; + let align = this.read_machine_usize(align)?; return this.emulate_allocator(Symbol::intern("__rg_alloc_zeroed"), |this| { Self::check_alloc_request(size, align)?; @@ -566,8 +566,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { "__rust_dealloc" | "miri_dealloc" => { let [ptr, old_size, align] = this.check_shim(abi, Abi::Rust, link_name, args)?; let ptr = this.read_pointer(ptr)?; - let old_size = this.read_scalar(old_size)?.to_machine_usize(this)?; - let align = this.read_scalar(align)?.to_machine_usize(this)?; + let old_size = this.read_machine_usize(old_size)?; + let align = this.read_machine_usize(align)?; let default = |this: &mut MiriInterpCx<'mir, 'tcx>| { let memory_kind = match link_name.as_str() { @@ -596,9 +596,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { "__rust_realloc" => { let [ptr, old_size, align, new_size] = this.check_shim(abi, Abi::Rust, link_name, args)?; let ptr = this.read_pointer(ptr)?; - let old_size = this.read_scalar(old_size)?.to_machine_usize(this)?; - let align = this.read_scalar(align)?.to_machine_usize(this)?; - let new_size = this.read_scalar(new_size)?.to_machine_usize(this)?; + let old_size = this.read_machine_usize(old_size)?; + let align = this.read_machine_usize(align)?; + let new_size = this.read_machine_usize(new_size)?; // No need to check old_size; we anyway check that they match the allocation. return this.emulate_allocator(Symbol::intern("__rg_realloc"), |this| { @@ -621,7 +621,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let [left, right, n] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; let left = this.read_pointer(left)?; let right = this.read_pointer(right)?; - let n = Size::from_bytes(this.read_scalar(n)?.to_machine_usize(this)?); + let n = Size::from_bytes(this.read_machine_usize(n)?); let result = { let left_bytes = this.read_bytes_ptr_strip_provenance(left, n)?; @@ -641,7 +641,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let [ptr, val, num] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; let ptr = this.read_pointer(ptr)?; let val = this.read_scalar(val)?.to_i32()?; - let num = this.read_scalar(num)?.to_machine_usize(this)?; + let num = this.read_machine_usize(num)?; // The docs say val is "interpreted as unsigned char". #[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)] let val = val as u8; @@ -664,7 +664,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let [ptr, val, num] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; let ptr = this.read_pointer(ptr)?; let val = this.read_scalar(val)?.to_i32()?; - let num = this.read_scalar(num)?.to_machine_usize(this)?; + let num = this.read_machine_usize(num)?; // The docs say val is "interpreted as unsigned char". #[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)] let val = val as u8; diff --git a/src/tools/miri/src/shims/intrinsics/mod.rs b/src/tools/miri/src/shims/intrinsics/mod.rs index 5ea82adb9c69c..1b97a9d20de0c 100644 --- a/src/tools/miri/src/shims/intrinsics/mod.rs +++ b/src/tools/miri/src/shims/intrinsics/mod.rs @@ -111,7 +111,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let ty_layout = this.layout_of(ty)?; let val_byte = this.read_scalar(val_byte)?.to_u8()?; let ptr = this.read_pointer(ptr)?; - let count = this.read_scalar(count)?.to_machine_usize(this)?; + let count = this.read_machine_usize(count)?; // `checked_mul` enforces a too small bound (the correct one would probably be machine_isize_max), // but no actual allocation can be big enough for the difference to be noticeable. let byte_count = ty_layout.size.checked_mul(count, this).ok_or_else(|| { @@ -124,7 +124,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let [ptr, mask] = check_arg_count(args)?; let ptr = this.read_pointer(ptr)?; - let mask = this.read_scalar(mask)?.to_machine_usize(this)?; + let mask = this.read_machine_usize(mask)?; let masked_addr = Size::from_bytes(ptr.addr().bytes() & mask); diff --git a/src/tools/miri/src/shims/mod.rs b/src/tools/miri/src/shims/mod.rs index b6efad6b5ee08..39db97b72e2ce 100644 --- a/src/tools/miri/src/shims/mod.rs +++ b/src/tools/miri/src/shims/mod.rs @@ -80,7 +80,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { return Ok(false); } - let req_align = this.read_scalar(align_op)?.to_machine_usize(this)?; + let req_align = this.read_machine_usize(align_op)?; // Stop if the alignment is not a power of two. if !req_align.is_power_of_two() { diff --git a/src/tools/miri/src/shims/unix/foreign_items.rs b/src/tools/miri/src/shims/unix/foreign_items.rs index d746f9df90ac3..63cc132f3fc93 100644 --- a/src/tools/miri/src/shims/unix/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/foreign_items.rs @@ -78,7 +78,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let [fd, buf, count] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; let fd = this.read_scalar(fd)?.to_i32()?; let buf = this.read_pointer(buf)?; - let count = this.read_scalar(count)?.to_machine_usize(this)?; + let count = this.read_machine_usize(count)?; let result = this.read(fd, buf, count)?; this.write_scalar(Scalar::from_machine_isize(result, this), dest)?; } @@ -86,7 +86,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let [fd, buf, n] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; let fd = this.read_scalar(fd)?.to_i32()?; let buf = this.read_pointer(buf)?; - let count = this.read_scalar(n)?.to_machine_usize(this)?; + let count = this.read_machine_usize(n)?; trace!("Called write({:?}, {:?}, {:?})", fd, buf, count); let result = this.write(fd, buf, count)?; // Now, `result` is the value we return back to the program. @@ -157,8 +157,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let [fd, offset, len, advice] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; this.read_scalar(fd)?.to_i32()?; - this.read_scalar(offset)?.to_machine_isize(this)?; - this.read_scalar(len)?.to_machine_isize(this)?; + this.read_machine_isize(offset)?; + this.read_machine_isize(len)?; this.read_scalar(advice)?.to_i32()?; // fadvise is only informational, we can ignore it. this.write_null(dest)?; @@ -191,8 +191,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { "posix_memalign" => { let [ret, align, size] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; let ret = this.deref_operand(ret)?; - let align = this.read_scalar(align)?.to_machine_usize(this)?; - let size = this.read_scalar(size)?.to_machine_usize(this)?; + let align = this.read_machine_usize(align)?; + let size = this.read_machine_usize(size)?; // Align must be power of 2, and also at least ptr-sized (POSIX rules). // But failure to adhere to this is not UB, it's an error condition. if !align.is_power_of_two() || align < this.pointer_size().bytes() { @@ -216,7 +216,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // Dynamic symbol loading "dlsym" => { let [handle, symbol] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - this.read_scalar(handle)?.to_machine_usize(this)?; + this.read_machine_usize(handle)?; let symbol = this.read_pointer(symbol)?; let symbol_name = this.read_c_str(symbol)?; if let Some(dlsym) = Dlsym::from_str(symbol_name, &this.tcx.sess.target.os)? { @@ -472,7 +472,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let [errnum, buf, buflen] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; let errnum = this.read_scalar(errnum)?; let buf = this.read_pointer(buf)?; - let buflen = this.read_scalar(buflen)?.to_machine_usize(this)?; + let buflen = this.read_machine_usize(buflen)?; let error = this.try_errnum_to_io_error(errnum)?; let formatted = match error { @@ -565,7 +565,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let uid = this.read_scalar(uid)?.to_u32()?; let pwd = this.deref_operand(pwd)?; let buf = this.read_pointer(buf)?; - let buflen = this.read_scalar(buflen)?.to_machine_usize(this)?; + let buflen = this.read_machine_usize(buflen)?; let result = this.deref_operand(result)?; // Must be for "us". diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs index 988627db5611c..5af1b354e7bb1 100644 --- a/src/tools/miri/src/shims/unix/fs.rs +++ b/src/tools/miri/src/shims/unix/fs.rs @@ -1293,7 +1293,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.assert_target_os("linux", "readdir64"); - let dirp = this.read_scalar(dirp_op)?.to_machine_usize(this)?; + let dirp = this.read_machine_usize(dirp_op)?; // Reject if isolation is enabled. if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { @@ -1385,7 +1385,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.assert_target_os("macos", "readdir_r"); - let dirp = this.read_scalar(dirp_op)?.to_machine_usize(this)?; + let dirp = this.read_machine_usize(dirp_op)?; // Reject if isolation is enabled. if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { @@ -1478,7 +1478,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn closedir(&mut self, dirp_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); - let dirp = this.read_scalar(dirp_op)?.to_machine_usize(this)?; + let dirp = this.read_machine_usize(dirp_op)?; // Reject if isolation is enabled. if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { @@ -1642,7 +1642,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let pathname = this.read_path_from_c_str(this.read_pointer(pathname_op)?)?; let buf = this.read_pointer(buf_op)?; - let bufsize = this.read_scalar(bufsize_op)?.to_machine_usize(this)?; + let bufsize = this.read_machine_usize(bufsize_op)?; // Reject if isolation is enabled. if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { diff --git a/src/tools/miri/src/shims/unix/linux/foreign_items.rs b/src/tools/miri/src/shims/unix/linux/foreign_items.rs index 34076e842d55b..acf47fe924803 100644 --- a/src/tools/miri/src/shims/unix/linux/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/linux/foreign_items.rs @@ -99,7 +99,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { "incorrect number of arguments for syscall: got 0, expected at least 1" ); } - match this.read_scalar(&args[0])?.to_machine_usize(this)? { + match this.read_machine_usize(&args[0])? { // `libc::syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), GRND_NONBLOCK)` // is called if a `HashMap` is created the regular way (e.g. HashMap). id if id == sys_getrandom => { @@ -147,7 +147,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let [pid, cpusetsize, mask] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; this.read_scalar(pid)?.to_i32()?; - this.read_scalar(cpusetsize)?.to_machine_usize(this)?; + this.read_machine_usize(cpusetsize)?; this.deref_operand(mask)?; // FIXME: we just return an error; `num_cpus` then falls back to `sysconf`. let einval = this.eval_libc("EINVAL")?; @@ -179,7 +179,7 @@ fn getrandom<'tcx>( dest: &PlaceTy<'tcx, Provenance>, ) -> InterpResult<'tcx> { let ptr = this.read_pointer(ptr)?; - let len = this.read_scalar(len)?.to_machine_usize(this)?; + let len = this.read_machine_usize(len)?; // The only supported flags are GRND_RANDOM and GRND_NONBLOCK, // neither of which have any effect on our current PRNG. diff --git a/src/tools/miri/src/shims/unix/macos/dlsym.rs b/src/tools/miri/src/shims/unix/macos/dlsym.rs index 18804b45efca9..44b9af79005a9 100644 --- a/src/tools/miri/src/shims/unix/macos/dlsym.rs +++ b/src/tools/miri/src/shims/unix/macos/dlsym.rs @@ -39,7 +39,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { Dlsym::getentropy => { let [ptr, len] = check_arg_count(args)?; let ptr = this.read_pointer(ptr)?; - let len = this.read_scalar(len)?.to_machine_usize(this)?; + let len = this.read_machine_usize(len)?; this.gen_random(ptr, len)?; this.write_null(dest)?; } diff --git a/src/tools/miri/src/shims/unix/macos/foreign_items.rs b/src/tools/miri/src/shims/unix/macos/foreign_items.rs index 221dc39697f90..2554fc7798454 100644 --- a/src/tools/miri/src/shims/unix/macos/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/macos/foreign_items.rs @@ -161,13 +161,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // Querying system information "pthread_get_stackaddr_np" => { let [thread] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - this.read_scalar(thread)?.to_machine_usize(this)?; + this.read_machine_usize(thread)?; let stack_addr = Scalar::from_uint(STACK_ADDR, this.pointer_size()); this.write_scalar(stack_addr, dest)?; } "pthread_get_stacksize_np" => { let [thread] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - this.read_scalar(thread)?.to_machine_usize(this)?; + this.read_machine_usize(thread)?; let stack_size = Scalar::from_uint(STACK_SIZE, this.pointer_size()); this.write_scalar(stack_size, dest)?; } diff --git a/src/tools/miri/src/shims/unix/thread.rs b/src/tools/miri/src/shims/unix/thread.rs index 5b9dc90f0f006..2cb4858fdfdee 100644 --- a/src/tools/miri/src/shims/unix/thread.rs +++ b/src/tools/miri/src/shims/unix/thread.rs @@ -42,7 +42,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { throw_unsup_format!("Miri supports pthread_join only with retval==NULL"); } - let thread_id = this.read_scalar(thread)?.to_machine_usize(this)?; + let thread_id = this.read_machine_usize(thread)?; this.join_thread_exclusive(thread_id.try_into().expect("thread ID should fit in u32"))?; Ok(0) @@ -51,7 +51,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn pthread_detach(&mut self, thread: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); - let thread_id = this.read_scalar(thread)?.to_machine_usize(this)?; + let thread_id = this.read_machine_usize(thread)?; this.detach_thread( thread_id.try_into().expect("thread ID should fit in u32"), /*allow_terminated_joined*/ false, diff --git a/src/tools/miri/src/shims/windows/dlsym.rs b/src/tools/miri/src/shims/windows/dlsym.rs index 4b2a90723c79c..857cf1ae7037b 100644 --- a/src/tools/miri/src/shims/windows/dlsym.rs +++ b/src/tools/miri/src/shims/windows/dlsym.rs @@ -67,10 +67,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { byte_offset, _key, ] = check_arg_count(args)?; - let handle = this.read_scalar(handle)?.to_machine_isize(this)?; + let handle = this.read_machine_isize(handle)?; let buf = this.read_pointer(buf)?; let n = this.read_scalar(n)?.to_u32()?; - let byte_offset = this.read_scalar(byte_offset)?.to_machine_usize(this)?; // is actually a pointer + let byte_offset = this.read_machine_usize(byte_offset)?; // is actually a pointer let io_status_block = this.deref_operand(io_status_block)?; if byte_offset != 0 { diff --git a/src/tools/miri/src/shims/windows/foreign_items.rs b/src/tools/miri/src/shims/windows/foreign_items.rs index e16749c986b16..656f1a4ae7223 100644 --- a/src/tools/miri/src/shims/windows/foreign_items.rs +++ b/src/tools/miri/src/shims/windows/foreign_items.rs @@ -73,9 +73,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { "HeapAlloc" => { let [handle, flags, size] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?; - this.read_scalar(handle)?.to_machine_isize(this)?; + this.read_machine_isize(handle)?; let flags = this.read_scalar(flags)?.to_u32()?; - let size = this.read_scalar(size)?.to_machine_usize(this)?; + let size = this.read_machine_usize(size)?; let zero_init = (flags & 0x00000008) != 0; // HEAP_ZERO_MEMORY let res = this.malloc(size, zero_init, MiriMemoryKind::WinHeap)?; this.write_pointer(res, dest)?; @@ -83,7 +83,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { "HeapFree" => { let [handle, flags, ptr] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?; - this.read_scalar(handle)?.to_machine_isize(this)?; + this.read_machine_isize(handle)?; this.read_scalar(flags)?.to_u32()?; let ptr = this.read_pointer(ptr)?; this.free(ptr, MiriMemoryKind::WinHeap)?; @@ -92,10 +92,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { "HeapReAlloc" => { let [handle, flags, ptr, size] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?; - this.read_scalar(handle)?.to_machine_isize(this)?; + this.read_machine_isize(handle)?; this.read_scalar(flags)?.to_u32()?; let ptr = this.read_pointer(ptr)?; - let size = this.read_scalar(size)?.to_machine_usize(this)?; + let size = this.read_machine_usize(size)?; let res = this.realloc(ptr, size, MiriMemoryKind::WinHeap)?; this.write_pointer(res, dest)?; } @@ -298,7 +298,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { #[allow(non_snake_case)] let [hModule, lpProcName] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?; - this.read_scalar(hModule)?.to_machine_isize(this)?; + this.read_machine_isize(hModule)?; let name = this.read_c_str(this.read_pointer(lpProcName)?)?; if let Some(dlsym) = Dlsym::from_str(name, &this.tcx.sess.target.os)? { let ptr = this.create_fn_alloc_ptr(FnVal::Other(dlsym)); @@ -356,7 +356,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // `term` needs this, so we fake it. let [console, buffer_info] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?; - this.read_scalar(console)?.to_machine_isize(this)?; + this.read_machine_isize(console)?; this.deref_operand(buffer_info)?; // Indicate an error. // FIXME: we should set last_error, but to what? @@ -432,7 +432,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { "GetConsoleMode" if this.frame_in_std() => { let [console, mode] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?; - this.read_scalar(console)?.to_machine_isize(this)?; + this.read_machine_isize(console)?; this.deref_operand(mode)?; // Indicate an error. this.write_null(dest)?; diff --git a/src/tools/miri/src/shims/windows/sync.rs b/src/tools/miri/src/shims/windows/sync.rs index 6b043c6d2c9e1..7892f35f7b05c 100644 --- a/src/tools/miri/src/shims/windows/sync.rs +++ b/src/tools/miri/src/shims/windows/sync.rs @@ -273,7 +273,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let ptr = this.read_pointer(ptr_op)?; let compare = this.read_pointer(compare_op)?; - let size = this.read_scalar(size_op)?.to_machine_usize(this)?; + let size = this.read_machine_usize(size_op)?; let timeout_ms = this.read_scalar(timeout_op)?.to_u32()?; let thread = this.get_active_thread(); diff --git a/src/tools/miri/src/shims/windows/thread.rs b/src/tools/miri/src/shims/windows/thread.rs index 25a5194caa096..1dbc848b03055 100644 --- a/src/tools/miri/src/shims/windows/thread.rs +++ b/src/tools/miri/src/shims/windows/thread.rs @@ -21,7 +21,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let security = this.read_pointer(security_op)?; // stacksize is ignored, but still needs to be a valid usize - this.read_scalar(stacksize_op)?.to_machine_usize(this)?; + this.read_machine_usize(stacksize_op)?; let start_routine = this.read_pointer(start_op)?; let func_arg = this.read_immediate(arg_op)?; let flags = this.read_scalar(flags_op)?.to_u32()?; From 8b2a7da3b0d5ef813c3ecbdde2a550eedef27712 Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Mon, 12 Dec 2022 15:47:32 +0100 Subject: [PATCH 03/30] Rename `assert_uninit_valid` intrinsic It's not about "uninit" anymore but about "filling with 0x01 bytes" so the name should at least try to reflect that. --- compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs | 6 ++++-- compiler/rustc_codegen_ssa/src/mir/block.rs | 6 +++--- compiler/rustc_const_eval/src/interpret/intrinsics.rs | 6 ++++-- compiler/rustc_hir_analysis/src/check/intrinsic.rs | 8 ++++---- compiler/rustc_span/src/symbol.rs | 2 +- library/core/src/intrinsics.rs | 7 ++++--- library/core/src/mem/mod.rs | 3 ++- src/test/ui/consts/assert-type-intrinsics.rs | 2 +- src/test/ui/consts/assert-type-intrinsics.stderr | 4 ++-- 9 files changed, 25 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs index 0302b843aa226..e4a27f1bb6d40 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs @@ -713,7 +713,7 @@ fn codegen_regular_intrinsic_call<'tcx>( let res = CValue::by_val(swap(&mut fx.bcx, val), arg.layout()); ret.write_cvalue(fx, res); } - sym::assert_inhabited | sym::assert_zero_valid | sym::assert_uninit_valid => { + sym::assert_inhabited | sym::assert_zero_valid | sym::assert_mem_uninitialized_valid => { intrinsic_args!(fx, args => (); intrinsic); let layout = fx.layout_of(substs.type_at(0)); @@ -742,7 +742,9 @@ fn codegen_regular_intrinsic_call<'tcx>( return; } - if intrinsic == sym::assert_uninit_valid && !fx.tcx.permits_uninit_init(layout) { + if intrinsic == sym::assert_mem_uninitialized_valid + && !fx.tcx.permits_uninit_init(layout) + { with_no_trimmed_paths!({ crate::base::codegen_panic( fx, diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index f3f5ddb52d6a4..a1cd43f749ec0 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -666,12 +666,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { enum AssertIntrinsic { Inhabited, ZeroValid, - UninitValid, + MemUninitializedValid, } let panic_intrinsic = intrinsic.and_then(|i| match i { sym::assert_inhabited => Some(AssertIntrinsic::Inhabited), sym::assert_zero_valid => Some(AssertIntrinsic::ZeroValid), - sym::assert_uninit_valid => Some(AssertIntrinsic::UninitValid), + sym::assert_mem_uninitialized_valid => Some(AssertIntrinsic::MemUninitializedValid), _ => None, }); if let Some(intrinsic) = panic_intrinsic { @@ -682,7 +682,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let do_panic = match intrinsic { Inhabited => layout.abi.is_uninhabited(), ZeroValid => !bx.tcx().permits_zero_init(layout), - UninitValid => !bx.tcx().permits_uninit_init(layout), + MemUninitializedValid => !bx.tcx().permits_uninit_init(layout), }; Some(if do_panic { let msg_str = with_no_visible_paths!({ diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index 7940efcd2b11f..27abc2b97b028 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -432,7 +432,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { sym::transmute => { self.copy_op(&args[0], dest, /*allow_transmute*/ true)?; } - sym::assert_inhabited | sym::assert_zero_valid | sym::assert_uninit_valid => { + sym::assert_inhabited + | sym::assert_zero_valid + | sym::assert_mem_uninitialized_valid => { let ty = instance.substs.type_at(0); let layout = self.layout_of(ty)?; @@ -464,7 +466,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } } - if intrinsic_name == sym::assert_uninit_valid { + if intrinsic_name == sym::assert_mem_uninitialized_valid { let should_panic = !self.tcx.permits_uninit_init(layout); if should_panic { diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 69e54b41d4c04..598dc2dca5c62 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -75,7 +75,7 @@ pub fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: DefId) -> hir sym::abort | sym::assert_inhabited | sym::assert_zero_valid - | sym::assert_uninit_valid + | sym::assert_mem_uninitialized_valid | sym::size_of | sym::min_align_of | sym::needs_drop @@ -193,9 +193,9 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { } sym::rustc_peek => (1, vec![param(0)], param(0)), sym::caller_location => (0, vec![], tcx.caller_location_ty()), - sym::assert_inhabited | sym::assert_zero_valid | sym::assert_uninit_valid => { - (1, Vec::new(), tcx.mk_unit()) - } + sym::assert_inhabited + | sym::assert_zero_valid + | sym::assert_mem_uninitialized_valid => (1, Vec::new(), tcx.mk_unit()), sym::forget => (1, vec![param(0)], tcx.mk_unit()), sym::transmute => (2, vec![param(0)], param(1)), sym::prefetch_read_data diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 1fcf8c7a8bf13..c97340983c010 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -377,9 +377,9 @@ symbols! { assert_eq_macro, assert_inhabited, assert_macro, + assert_mem_uninitialized_valid, assert_ne_macro, assert_receiver_is_total_eq, - assert_uninit_valid, assert_zero_valid, asserting, associated_const_equality, diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index 7ed7d767f2fb5..ed58a7f179935 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -959,13 +959,14 @@ extern "rust-intrinsic" { #[rustc_safe_intrinsic] pub fn assert_zero_valid(); - /// A guard for unsafe functions that cannot ever be executed if `T` has invalid - /// bit patterns: This will statically either panic, or do nothing. + /// A guard for `std::mem::uninitialized`. Checks whether a repeated bit pattern `0x01` + /// is legal for `T`: This will statically either panic, or do nothing. /// /// This intrinsic does not have a stable counterpart. #[rustc_const_unstable(feature = "const_assert_type2", issue = "none")] #[rustc_safe_intrinsic] - pub fn assert_uninit_valid(); + #[cfg(not(bootstrap))] + pub fn assert_mem_uninitialized_valid(); /// Gets a reference to a static `Location` indicating where it was called. /// diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs index 383bdc7b6e2e9..5e01ccc07d8fd 100644 --- a/library/core/src/mem/mod.rs +++ b/library/core/src/mem/mod.rs @@ -682,7 +682,8 @@ pub unsafe fn zeroed() -> T { pub unsafe fn uninitialized() -> T { // SAFETY: the caller must guarantee that an uninitialized value is valid for `T`. unsafe { - intrinsics::assert_uninit_valid::(); + #[cfg(not(bootstrap))] // If the compiler hits this itself then it deserves the UB. + intrinsics::assert_mem_uninitialized_valid::(); let mut val = MaybeUninit::::uninit(); // Fill memory with 0x01, as an imperfect mitigation for old code that uses this function on diff --git a/src/test/ui/consts/assert-type-intrinsics.rs b/src/test/ui/consts/assert-type-intrinsics.rs index 263d1ae6a3ec7..b4fd423becd9d 100644 --- a/src/test/ui/consts/assert-type-intrinsics.rs +++ b/src/test/ui/consts/assert-type-intrinsics.rs @@ -13,7 +13,7 @@ fn main() { //~^ERROR: evaluation of constant value failed }; const _BAD2: () = { - intrinsics::assert_uninit_valid::<&'static i32>(); + intrinsics::assert_mem_uninitialized_valid::<&'static i32>(); //~^ERROR: evaluation of constant value failed }; const _BAD3: () = { diff --git a/src/test/ui/consts/assert-type-intrinsics.stderr b/src/test/ui/consts/assert-type-intrinsics.stderr index f92f9fda069ad..70aec91e22622 100644 --- a/src/test/ui/consts/assert-type-intrinsics.stderr +++ b/src/test/ui/consts/assert-type-intrinsics.stderr @@ -7,8 +7,8 @@ LL | MaybeUninit::::uninit().assume_init(); error[E0080]: evaluation of constant value failed --> $DIR/assert-type-intrinsics.rs:16:9 | -LL | intrinsics::assert_uninit_valid::<&'static i32>(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ aborted execution: attempted to leave type `&i32` uninitialized, which is invalid +LL | intrinsics::assert_mem_uninitialized_valid::<&'static i32>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ aborted execution: attempted to leave type `&i32` uninitialized, which is invalid error[E0080]: evaluation of constant value failed --> $DIR/assert-type-intrinsics.rs:20:9 From 7f9438910c2c86813b597dfc518990198691d170 Mon Sep 17 00:00:00 2001 From: Stefano Zacchiroli Date: Sat, 17 Dec 2022 12:20:56 +0100 Subject: [PATCH 04/30] str.lines() docstring: clarify that line endings are not returned Previously, the str.lines() docstring stated that lines are split at line endings, but not whether those were returned or not. This new version of the docstring states this explicitly, avoiding the need of getting to doctests to get an answer to this FAQ. --- library/core/src/str/mod.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index 45fd2caae52f4..863ded5e5ec03 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -970,8 +970,10 @@ impl str { /// An iterator over the lines of a string, as string slices. /// - /// Lines are ended with either a newline (`\n`) or a carriage return with - /// a line feed (`\r\n`). + /// Lines are split at line endings that are either newlines (`\n`) or + /// sequences of a carriage return followed by a line feed (`\r\n`). + /// + /// Line terminators are not included in the lines returned by the iterator. /// /// The final line ending is optional. A string that ends with a final line /// ending will return the same lines as an otherwise identical string From ee1a905f00ab5ec426d45eb29e168a20dfe94498 Mon Sep 17 00:00:00 2001 From: Marcus Calhoun-Lopez Date: Mon, 19 Dec 2022 08:34:06 -0700 Subject: [PATCH 05/30] Fix arch flag on i686-apple-darwin i686-apple-darwin should use `-arch i386` instead of `-arch i686` --- src/bootstrap/native.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/bootstrap/native.rs b/src/bootstrap/native.rs index f6c453ebe107b..68f917d352859 100644 --- a/src/bootstrap/native.rs +++ b/src/bootstrap/native.rs @@ -600,6 +600,9 @@ fn configure_cmake( if target.starts_with("aarch64") { // macOS uses a different name for building arm64 cfg.define("CMAKE_OSX_ARCHITECTURES", "arm64"); + } else if target.starts_with("i686") { + // macOS uses a different name for building i386 + cfg.define("CMAKE_OSX_ARCHITECTURES", "i386"); } else { cfg.define("CMAKE_OSX_ARCHITECTURES", target.triple.split('-').next().unwrap()); } From a213bb36c953d2c342d8ae9303704e57bc8aab33 Mon Sep 17 00:00:00 2001 From: lcnr Date: Sun, 4 Dec 2022 03:19:10 +0000 Subject: [PATCH 06/30] implement the skeleton of the updated trait solver --- .../src/infer/canonical/query_response.rs | 6 +- compiler/rustc_middle/src/infer/canonical.rs | 10 + compiler/rustc_middle/src/traits/query.rs | 2 +- .../src/traits/specialization_graph.rs | 1 + compiler/rustc_middle/src/ty/mod.rs | 41 ++- compiler/rustc_middle/src/ty/sty.rs | 12 +- compiler/rustc_middle/src/ty/subst.rs | 4 + compiler/rustc_trait_selection/src/lib.rs | 2 + .../rustc_trait_selection/src/solve/cache.rs | 257 ++++++++++++++ .../src/solve/fulfill.rs | 92 +++++ .../src/solve/infcx_ext.rs | 55 +++ .../rustc_trait_selection/src/solve/mod.rs | 308 +++++++++++++++++ .../src/solve/overflow.rs | 80 +++++ .../src/solve/project_goals.rs | 324 ++++++++++++++++++ .../src/solve/trait_goals.rs | 282 +++++++++++++++ .../src/traits/project.rs | 59 +--- .../src/traits/select/mod.rs | 14 +- .../traits/specialize/specialization_graph.rs | 49 +++ .../inductive-canonical-cycle.rs | 28 ++ .../inductive-canonical-cycle.stderr | 26 ++ triagebot.toml | 4 + 21 files changed, 1569 insertions(+), 87 deletions(-) create mode 100644 compiler/rustc_trait_selection/src/solve/cache.rs create mode 100644 compiler/rustc_trait_selection/src/solve/fulfill.rs create mode 100644 compiler/rustc_trait_selection/src/solve/infcx_ext.rs create mode 100644 compiler/rustc_trait_selection/src/solve/mod.rs create mode 100644 compiler/rustc_trait_selection/src/solve/overflow.rs create mode 100644 compiler/rustc_trait_selection/src/solve/project_goals.rs create mode 100644 compiler/rustc_trait_selection/src/solve/trait_goals.rs create mode 100644 src/test/ui/traits/solver-cycles/inductive-canonical-cycle.rs create mode 100644 src/test/ui/traits/solver-cycles/inductive-canonical-cycle.stderr diff --git a/compiler/rustc_infer/src/infer/canonical/query_response.rs b/compiler/rustc_infer/src/infer/canonical/query_response.rs index 996b1c40e3fd6..a722613e3310e 100644 --- a/compiler/rustc_infer/src/infer/canonical/query_response.rs +++ b/compiler/rustc_infer/src/infer/canonical/query_response.rs @@ -151,7 +151,11 @@ impl<'tcx> InferCtxt<'tcx> { }) } - fn take_opaque_types_for_query_response(&self) -> Vec<(Ty<'tcx>, Ty<'tcx>)> { + /// FIXME: This method should only be used for canonical queries and therefore be private. + /// + /// As the new solver does canonicalization slightly differently, this is also used there + /// for now. This should hopefully change fairly soon. + pub fn take_opaque_types_for_query_response(&self) -> Vec<(Ty<'tcx>, Ty<'tcx>)> { self.inner .borrow_mut() .opaque_type_storage diff --git a/compiler/rustc_middle/src/infer/canonical.rs b/compiler/rustc_middle/src/infer/canonical.rs index 0331d764b38a1..0b32f67a81e16 100644 --- a/compiler/rustc_middle/src/infer/canonical.rs +++ b/compiler/rustc_middle/src/infer/canonical.rs @@ -300,6 +300,16 @@ impl<'tcx, V> Canonical<'tcx, V> { let Canonical { max_universe, variables, value } = self; Canonical { max_universe, variables, value: map_op(value) } } + + /// Allows you to map the `value` of a canonical while keeping the same set of + /// bound variables. + /// + /// **WARNING:** This function is very easy to mis-use, hence the name! See + /// the comment of [Canonical::unchecked_map] for more details. + pub fn unchecked_rebind(self, value: W) -> Canonical<'tcx, W> { + let Canonical { max_universe, variables, value: _ } = self; + Canonical { max_universe, variables, value } + } } pub type QueryOutlivesConstraint<'tcx> = ( diff --git a/compiler/rustc_middle/src/traits/query.rs b/compiler/rustc_middle/src/traits/query.rs index 7380c62a6693a..6a149be3137ee 100644 --- a/compiler/rustc_middle/src/traits/query.rs +++ b/compiler/rustc_middle/src/traits/query.rs @@ -96,7 +96,7 @@ pub type CanonicalTypeOpProvePredicateGoal<'tcx> = pub type CanonicalTypeOpNormalizeGoal<'tcx, T> = Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::Normalize>>; -#[derive(Copy, Clone, Debug, HashStable)] +#[derive(Copy, Clone, Debug, HashStable, PartialEq, Eq)] pub struct NoSolution; pub type Fallible = Result; diff --git a/compiler/rustc_middle/src/traits/specialization_graph.rs b/compiler/rustc_middle/src/traits/specialization_graph.rs index cccedc9ec6ea9..a4dd22801e69d 100644 --- a/compiler/rustc_middle/src/traits/specialization_graph.rs +++ b/compiler/rustc_middle/src/traits/specialization_graph.rs @@ -180,6 +180,7 @@ impl Iterator for Ancestors<'_> { } /// Information about the most specialized definition of an associated item. +#[derive(Debug)] pub struct LeafDef { /// The associated item described by this `LeafDef`. pub item: ty::AssocItem, diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index a8e1253e67057..f01d74539a12e 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -535,6 +535,17 @@ impl<'tcx> Predicate<'tcx> { self } + #[instrument(level = "debug", skip(tcx), ret)] + pub fn is_coinductive(self, tcx: TyCtxt<'tcx>) -> bool { + match self.kind().skip_binder() { + ty::PredicateKind::Clause(ty::Clause::Trait(data)) => { + tcx.trait_is_coinductive(data.def_id()) + } + ty::PredicateKind::WellFormed(_) => true, + _ => false, + } + } + /// Whether this projection can be soundly normalized. /// /// Wf predicates must not be normalized, as normalization @@ -1018,6 +1029,24 @@ pub struct ProjectionPredicate<'tcx> { pub term: Term<'tcx>, } +impl<'tcx> ProjectionPredicate<'tcx> { + pub fn self_ty(self) -> Ty<'tcx> { + self.projection_ty.self_ty() + } + + pub fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> ProjectionPredicate<'tcx> { + Self { projection_ty: self.projection_ty.with_self_ty(tcx, self_ty), ..self } + } + + pub fn trait_def_id(self, tcx: TyCtxt<'tcx>) -> DefId { + self.projection_ty.trait_def_id(tcx) + } + + pub fn def_id(self) -> DefId { + self.projection_ty.def_id + } +} + pub type PolyProjectionPredicate<'tcx> = Binder<'tcx, ProjectionPredicate<'tcx>>; impl<'tcx> PolyProjectionPredicate<'tcx> { @@ -1054,18 +1083,6 @@ impl<'tcx> PolyProjectionPredicate<'tcx> { } } -impl<'tcx> ProjectionPredicate<'tcx> { - pub fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self { - Self { - projection_ty: tcx.mk_alias_ty( - self.projection_ty.def_id, - [self_ty.into()].into_iter().chain(self.projection_ty.substs.iter().skip(1)), - ), - ..self - } - } -} - pub trait ToPolyTraitRef<'tcx> { fn to_poly_trait_ref(&self) -> PolyTraitRef<'tcx>; } diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 66aeebab88ba0..890a4da638785 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -1169,7 +1169,7 @@ pub struct AliasTy<'tcx> { } impl<'tcx> AliasTy<'tcx> { - pub fn trait_def_id(&self, tcx: TyCtxt<'tcx>) -> DefId { + pub fn trait_def_id(self, tcx: TyCtxt<'tcx>) -> DefId { match tcx.def_kind(self.def_id) { DefKind::AssocTy | DefKind::AssocConst => tcx.parent(self.def_id), DefKind::ImplTraitPlaceholder => { @@ -1183,7 +1183,7 @@ impl<'tcx> AliasTy<'tcx> { /// For example, if this is a projection of `::Item<'a>`, /// then this function would return a `T: Iterator` trait reference and `['a]` as the own substs pub fn trait_ref_and_own_substs( - &self, + self, tcx: TyCtxt<'tcx>, ) -> (ty::TraitRef<'tcx>, &'tcx [ty::GenericArg<'tcx>]) { debug_assert!(matches!(tcx.def_kind(self.def_id), DefKind::AssocTy | DefKind::AssocConst)); @@ -1202,14 +1202,18 @@ impl<'tcx> AliasTy<'tcx> { /// WARNING: This will drop the substs for generic associated types /// consider calling [Self::trait_ref_and_own_substs] to get those /// as well. - pub fn trait_ref(&self, tcx: TyCtxt<'tcx>) -> ty::TraitRef<'tcx> { + pub fn trait_ref(self, tcx: TyCtxt<'tcx>) -> ty::TraitRef<'tcx> { let def_id = self.trait_def_id(tcx); tcx.mk_trait_ref(def_id, self.substs.truncate_to(tcx, tcx.generics_of(def_id))) } - pub fn self_ty(&self) -> Ty<'tcx> { + pub fn self_ty(self) -> Ty<'tcx> { self.substs.type_at(0) } + + pub fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self { + tcx.mk_alias_ty(self.def_id, [self_ty.into()].into_iter().chain(self.substs.iter().skip(1))) + } } #[derive(Copy, Clone, Debug, TypeFoldable, TypeVisitable, Lift)] diff --git a/compiler/rustc_middle/src/ty/subst.rs b/compiler/rustc_middle/src/ty/subst.rs index a04b15f8cf13c..237b36701c2b6 100644 --- a/compiler/rustc_middle/src/ty/subst.rs +++ b/compiler/rustc_middle/src/ty/subst.rs @@ -573,6 +573,10 @@ impl EarlyBinder { pub fn rebind(&self, value: U) -> EarlyBinder { EarlyBinder(value) } + + pub fn skip_binder(self) -> T { + self.0 + } } impl EarlyBinder> { diff --git a/compiler/rustc_trait_selection/src/lib.rs b/compiler/rustc_trait_selection/src/lib.rs index 975ff31a60788..a30d1df4ede52 100644 --- a/compiler/rustc_trait_selection/src/lib.rs +++ b/compiler/rustc_trait_selection/src/lib.rs @@ -19,6 +19,7 @@ #![feature(let_chains)] #![feature(if_let_guard)] #![feature(never_type)] +#![feature(result_option_inspect)] #![feature(type_alias_impl_trait)] #![recursion_limit = "512"] // For rustdoc @@ -37,4 +38,5 @@ extern crate smallvec; pub mod autoderef; pub mod errors; pub mod infer; +pub mod solve; pub mod traits; diff --git a/compiler/rustc_trait_selection/src/solve/cache.rs b/compiler/rustc_trait_selection/src/solve/cache.rs new file mode 100644 index 0000000000000..993b79890669c --- /dev/null +++ b/compiler/rustc_trait_selection/src/solve/cache.rs @@ -0,0 +1,257 @@ +//! This module both handles the global cache which stores "finished" goals, +//! and the provisional cache which contains partially computed goals. +//! +//! The provisional cache is necessary when dealing with coinductive cycles. +//! +//! For more information about the provisional cache and coinduction in general, +//! check out the relevant section of the rustc-dev-guide. +//! +//! FIXME(@lcnr): Write that section, feel free to ping me if you need help here +//! before then or if I still haven't done that before January 2023. +use super::overflow::OverflowData; +use super::CanonicalGoal; +use super::{EvalCtxt, QueryResult}; + +use rustc_data_structures::fx::FxHashMap; +use rustc_middle::ty::TyCtxt; +use std::{cmp::Ordering, collections::hash_map::Entry}; + +#[derive(Debug, Clone)] +struct ProvisionalEntry<'tcx> { + // In case we have a coinductive cycle, this is the + // the currently least restrictive result of this goal. + response: QueryResult<'tcx>, + // The lowest element on the stack on which this result + // relies on. Starts out as just being the depth at which + // we've proven this obligation, but gets lowered to the + // depth of another goal if we rely on it in a cycle. + depth: usize, +} + +struct StackElem<'tcx> { + goal: CanonicalGoal<'tcx>, + has_been_used: bool, +} + +/// The cache used for goals which are currently in progress or which depend +/// on in progress results. +/// +/// Once we're done with a goal we can store it in the global trait solver +/// cache of the `TyCtxt`. For goals which we're currently proving, or which +/// have only been proven via a coinductive cycle using a goal still on our stack +/// we have to use this separate data structure. +/// +/// The current data structure is not perfect, so there may still be room for +/// improvement here. We have the following requirements: +/// +/// ## Is there is a provisional entry for the given goal: +/// +/// ```ignore (for syntax highlighting) +/// self.entries.get(goal) +/// ``` +/// +/// ## Get all goals on the stack involved in a cycle: +/// +/// ```ignore (for syntax highlighting) +/// let entry = self.entries.get(goal).unwrap(); +/// let involved_goals = self.stack.iter().skip(entry.depth); +/// ``` +/// +/// ## Capping the depth of all entries +/// +/// Needed whenever we encounter a cycle. The current implementation always +/// iterates over all entries instead of only the ones with a larger depth. +/// Changing this may result in notable performance improvements. +/// +/// ```ignore (for syntax highlighting) +/// let cycle_depth = self.entries.get(goal).unwrap().depth; +/// for e in &mut self.entries { +/// e.depth = e.depth.min(cycle_depth); +/// } +/// ``` +/// +/// ## Checking whether we have to rerun the current goal +/// +/// A goal has to be rerun if its provisional result was used in a cycle +/// and that result is different from its final result. We update +/// [StackElem::has_been_used] for the deepest stack element involved in a cycle. +/// +/// ## Moving all finished goals into the global cache +/// +/// If `stack_elem.has_been_used` is true, iterate over all entries, moving the ones +/// with equal depth. If not, simply move this single entry. +pub(super) struct ProvisionalCache<'tcx> { + stack: Vec>, + entries: FxHashMap, ProvisionalEntry<'tcx>>, +} + +impl<'tcx> ProvisionalCache<'tcx> { + pub(super) fn empty() -> ProvisionalCache<'tcx> { + ProvisionalCache { stack: Vec::new(), entries: Default::default() } + } + + pub(super) fn current_depth(&self) -> usize { + self.stack.len() + } +} + +impl<'tcx> EvalCtxt<'tcx> { + /// Tries putting the new goal on the stack, returning an error if it is already cached. + /// + /// This correctly updates the provisional cache if there is a cycle. + pub(super) fn try_push_stack( + &mut self, + goal: CanonicalGoal<'tcx>, + ) -> Result<(), QueryResult<'tcx>> { + // FIXME: start by checking the global cache + + // Look at the provisional cache to check for cycles. + let cache = &mut self.provisional_cache; + match cache.entries.entry(goal) { + // No entry, simply push this goal on the stack after dealing with overflow. + Entry::Vacant(v) => { + if self.overflow_data.has_overflow(cache.stack.len()) { + return Err(self.deal_with_overflow()); + } + + v.insert(ProvisionalEntry { + response: fixme_response_yes_no_constraints(), + depth: cache.stack.len(), + }); + cache.stack.push(StackElem { goal, has_been_used: false }); + Ok(()) + } + // We have a nested goal which relies on a goal `root` deeper in the stack. + // + // We first store that we may have to rerun `evaluate_goal` for `root` in case the + // provisional response is not equal to the final response. We also update the depth + // of all goals which recursively depend on our current goal to depend on `root` + // instead. + // + // Finally we can return either the provisional response for that goal if we have a + // coinductive cycle or an ambiguous result if the cycle is inductive. + Entry::Occupied(entry) => { + // FIXME: `ProvisionalEntry` should be `Copy`. + let entry = entry.get().clone(); + cache.stack[entry.depth].has_been_used = true; + for provisional_entry in cache.entries.values_mut() { + provisional_entry.depth = provisional_entry.depth.min(entry.depth); + } + + // NOTE: The goals on the stack aren't the only goals involved in this cycle. + // We can also depend on goals which aren't part of the stack but coinductively + // depend on the stack themselves. We already checked whether all the goals + // between these goals and their root on the stack. This means that as long as + // each goal in a cycle is checked for coinductivity by itself simply checking + // the stack is enough. + if cache.stack[entry.depth..] + .iter() + .all(|g| g.goal.value.predicate.is_coinductive(self.tcx)) + { + Err(entry.response) + } else { + Err(fixme_response_maybe_no_constraints()) + } + } + } + } + + /// We cannot simply store the result of [EvalCtxt::compute_goal] as we have to deal with + /// coinductive cycles. + /// + /// When we encounter a coinductive cycle, we have to prove the final result of that cycle + /// while we are still computing that result. Because of this we continously recompute the + /// cycle until the result of the previous iteration is equal to the final result, at which + /// point we are done. + /// + /// This function returns `true` if we were able to finalize the goal and `false` if it has + /// updated the provisional cache and we have to recompute the current goal. + /// + /// FIXME: Refer to the rustc-dev-guide entry once it exists. + pub(super) fn try_finalize_goal( + &mut self, + actual_goal: CanonicalGoal<'tcx>, + response: QueryResult<'tcx>, + ) -> bool { + let cache = &mut self.provisional_cache; + let StackElem { goal, has_been_used } = cache.stack.pop().unwrap(); + assert_eq!(goal, actual_goal); + + let provisional_entry = cache.entries.get_mut(&goal).unwrap(); + // Check whether the current stack entry is the root of a cycle. + // + // If so, we either move all participants of that cycle to the global cache + // or, in case the provisional response used in the cycle is not equal to the + // final response, have to recompute the goal after updating the provisional + // response to the final response of this iteration. + if has_been_used { + if provisional_entry.response == response { + // We simply drop all entries according to an immutable condition, so + // query instability is not a concern here. + #[allow(rustc::potential_query_instability)] + cache.entries.retain(|goal, entry| match entry.depth.cmp(&cache.stack.len()) { + Ordering::Less => true, + Ordering::Equal => { + Self::try_move_finished_goal_to_global_cache( + self.tcx, + &mut self.overflow_data, + &cache.stack, + // FIXME: these should be `Copy` :( + goal.clone(), + entry.response.clone(), + ); + false + } + Ordering::Greater => bug!("entry with greater depth than the current leaf"), + }); + + true + } else { + provisional_entry.response = response; + cache.stack.push(StackElem { goal, has_been_used: false }); + false + } + } else { + Self::try_move_finished_goal_to_global_cache( + self.tcx, + &mut self.overflow_data, + &cache.stack, + goal, + response, + ); + cache.entries.remove(&goal); + true + } + } + + fn try_move_finished_goal_to_global_cache( + tcx: TyCtxt<'tcx>, + overflow_data: &mut OverflowData, + stack: &[StackElem<'tcx>], + goal: CanonicalGoal<'tcx>, + response: QueryResult<'tcx>, + ) { + // We move goals to the global cache if we either did not hit an overflow or if it's + // the root goal as that will now always hit the same overflow limit. + // + // NOTE: We cannot move any non-root goals to the global cache even if their final result + // isn't impacted by the overflow as that goal still has unstable query dependencies + // because it didn't go its full depth. + // + // FIXME(@lcnr): We could still cache subtrees which are not impacted by overflow though. + // Tracking that info correctly isn't trivial, so I haven't implemented it for now. + let should_cache_globally = !overflow_data.did_overflow() || stack.is_empty(); + if should_cache_globally { + // FIXME: move the provisional entry to the global cache. + let _ = (tcx, goal, response); + } + } +} + +fn fixme_response_yes_no_constraints<'tcx>() -> QueryResult<'tcx> { + unimplemented!() +} + +fn fixme_response_maybe_no_constraints<'tcx>() -> QueryResult<'tcx> { + unimplemented!() +} diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs new file mode 100644 index 0000000000000..80115d78d88d1 --- /dev/null +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -0,0 +1,92 @@ +use std::mem; + +use rustc_data_structures::fx::FxHashMap; +use rustc_infer::{ + infer::InferCtxt, + traits::{query::NoSolution, FulfillmentError, PredicateObligation, TraitEngine}, +}; +use rustc_middle::ty; + +use super::{Certainty, EvalCtxt}; + +/// A trait engine using the new trait solver. +/// +/// This is mostly identical to how `evaluate_all` works inside of the +/// solver, except that the requirements are slightly different. +/// +/// Unlike `evaluate_all` it is possible to add new obligations later on +/// and we also have to track diagnostics information by using `Obligation` +/// instead of `Goal`. +/// +/// It is also likely that we want to use slightly different datastructures +/// here as this will have to deal with far more root goals than `evaluate_all`. +pub struct FulfillmentCtxt<'tcx> { + obligations: Vec>, +} + +impl<'tcx> FulfillmentCtxt<'tcx> { + pub fn new() -> FulfillmentCtxt<'tcx> { + FulfillmentCtxt { obligations: Vec::new() } + } +} + +impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> { + fn register_predicate_obligation( + &mut self, + _infcx: &InferCtxt<'tcx>, + obligation: PredicateObligation<'tcx>, + ) { + self.obligations.push(obligation); + } + + fn select_all_or_error(&mut self, infcx: &InferCtxt<'tcx>) -> Vec> { + let errors = self.select_where_possible(infcx); + if !errors.is_empty() { + return errors; + } + + if self.obligations.is_empty() { + Vec::new() + } else { + unimplemented!("ambiguous obligations") + } + } + + fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec> { + let errors = Vec::new(); + for i in 0.. { + if !infcx.tcx.recursion_limit().value_within_limit(i) { + unimplemented!("overflow") + } + + let mut has_changed = false; + for o in mem::take(&mut self.obligations) { + let mut cx = EvalCtxt::new(infcx.tcx); + let (changed, certainty) = match cx.evaluate_goal(infcx, o.clone().into()) { + Ok(result) => result, + Err(NoSolution) => unimplemented!("error"), + }; + + has_changed |= changed; + match certainty { + Certainty::Yes => {} + Certainty::Maybe(_) => self.obligations.push(o), + } + } + + if !has_changed { + break; + } + } + + errors + } + + fn pending_obligations(&self) -> Vec> { + self.obligations.clone() + } + + fn relationships(&mut self) -> &mut FxHashMap { + unimplemented!("Should be moved out of `TraitEngine`") + } +} diff --git a/compiler/rustc_trait_selection/src/solve/infcx_ext.rs b/compiler/rustc_trait_selection/src/solve/infcx_ext.rs new file mode 100644 index 0000000000000..436f4eea6625b --- /dev/null +++ b/compiler/rustc_trait_selection/src/solve/infcx_ext.rs @@ -0,0 +1,55 @@ +use rustc_infer::infer::canonical::CanonicalVarValues; +use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use rustc_infer::infer::InferCtxt; +use rustc_infer::traits::query::NoSolution; +use rustc_middle::ty::Ty; +use rustc_span::DUMMY_SP; + +use crate::solve::ExternalConstraints; + +use super::{Certainty, QueryResult, Response}; + +/// Methods used inside of the canonical queries of the solver. +pub(super) trait InferCtxtExt<'tcx> { + fn next_ty_infer(&self) -> Ty<'tcx>; + + fn make_canonical_response( + &self, + var_values: CanonicalVarValues<'tcx>, + certainty: Certainty, + ) -> QueryResult<'tcx>; +} + +impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { + fn next_ty_infer(&self) -> Ty<'tcx> { + self.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::MiscVariable, + span: DUMMY_SP, + }) + } + + fn make_canonical_response( + &self, + var_values: CanonicalVarValues<'tcx>, + certainty: Certainty, + ) -> QueryResult<'tcx> { + let external_constraints = take_external_constraints(self)?; + + Ok(self.canonicalize_response(Response { var_values, external_constraints, certainty })) + } +} + +#[instrument(level = "debug", skip(infcx), ret)] +fn take_external_constraints<'tcx>( + infcx: &InferCtxt<'tcx>, +) -> Result, NoSolution> { + let region_obligations = infcx.take_registered_region_obligations(); + let opaque_types = infcx.take_opaque_types_for_query_response(); + Ok(ExternalConstraints { + // FIXME: Now that's definitely wrong :) + // + // Should also do the leak check here I think + regions: drop(region_obligations), + opaque_types, + }) +} diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs new file mode 100644 index 0000000000000..5d6529f85423b --- /dev/null +++ b/compiler/rustc_trait_selection/src/solve/mod.rs @@ -0,0 +1,308 @@ +//! The new trait solver, currently still WIP. +//! +//! As a user of the trait system, you can use `TyCtxt::evaluate_goal` to +//! interact with this solver. +//! +//! For a high-level overview of how this solver works, check out the relevant +//! section of the rustc-dev-guide. +//! +//! FIXME(@lcnr): Write that section. If you read this before then ask me +//! about it on zulip. + +// FIXME: Instead of using `infcx.canonicalize_query` we have to add a new routine which +// preserves universes and creates a unique var (in the highest universe) for each +// appearance of a region. + +// FIXME: `CanonicalVarValues` should be interned and `Copy`. + +// FIXME: uses of `infcx.at` need to enable deferred projection equality once that's implemented. + +use std::mem; + +use rustc_infer::infer::canonical::OriginalQueryValues; +use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; +use rustc_infer::traits::query::NoSolution; +use rustc_infer::traits::Obligation; +use rustc_middle::infer::canonical::{Canonical, CanonicalVarValues}; +use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::{RegionOutlivesPredicate, ToPredicate, TypeOutlivesPredicate}; +use rustc_span::DUMMY_SP; + +use self::infcx_ext::InferCtxtExt; + +mod cache; +mod fulfill; +mod infcx_ext; +mod overflow; +mod project_goals; +mod trait_goals; + +pub use fulfill::FulfillmentCtxt; + +/// A goal is a statement, i.e. `predicate`, we want to prove +/// given some assumptions, i.e. `param_env`. +/// +/// Most of the time the `param_env` contains the `where`-bounds of the function +/// we're currently typechecking while the `predicate` is some trait bound. +#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, TypeFoldable, TypeVisitable)] +pub struct Goal<'tcx, P> { + param_env: ty::ParamEnv<'tcx>, + predicate: P, +} + +impl<'tcx, P> Goal<'tcx, P> { + pub fn new( + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + predicate: impl ToPredicate<'tcx, P>, + ) -> Goal<'tcx, P> { + Goal { param_env, predicate: predicate.to_predicate(tcx) } + } + + /// Updates the goal to one with a different `predicate` but the same `param_env`. + fn with(self, tcx: TyCtxt<'tcx>, predicate: impl ToPredicate<'tcx, Q>) -> Goal<'tcx, Q> { + Goal { param_env: self.param_env, predicate: predicate.to_predicate(tcx) } + } +} + +impl<'tcx, P> From> for Goal<'tcx, P> { + fn from(obligation: Obligation<'tcx, P>) -> Goal<'tcx, P> { + Goal { param_env: obligation.param_env, predicate: obligation.predicate } + } +} + +#[derive(Debug, PartialEq, Eq, Clone, Hash, TypeFoldable, TypeVisitable)] +pub struct Response<'tcx> { + pub var_values: CanonicalVarValues<'tcx>, + /// Additional constraints returned by this query. + pub external_constraints: ExternalConstraints<'tcx>, + pub certainty: Certainty, +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, TypeFoldable, TypeVisitable)] +pub enum Certainty { + Yes, + Maybe(MaybeCause), +} + +impl Certainty { + /// When proving multiple goals using **AND**, e.g. nested obligations for an impl, + /// use this function to unify the certainty of these goals + pub fn unify_and(self, other: Certainty) -> Certainty { + match (self, other) { + (Certainty::Yes, Certainty::Yes) => Certainty::Yes, + (Certainty::Yes, Certainty::Maybe(_)) => other, + (Certainty::Maybe(_), Certainty::Yes) => self, + (Certainty::Maybe(MaybeCause::Overflow), Certainty::Maybe(MaybeCause::Overflow)) => { + Certainty::Maybe(MaybeCause::Overflow) + } + // If at least one of the goals is ambiguous, hide the overflow as the ambiguous goal + // may still result in failure. + (Certainty::Maybe(MaybeCause::Ambiguity), Certainty::Maybe(_)) + | (Certainty::Maybe(_), Certainty::Maybe(MaybeCause::Ambiguity)) => { + Certainty::Maybe(MaybeCause::Ambiguity) + } + } + } +} + +/// Why we failed to evaluate a goal. +#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, TypeFoldable, TypeVisitable)] +pub enum MaybeCause { + /// We failed due to ambiguity. This ambiguity can either + /// be a true ambiguity, i.e. there are multiple different answers, + /// or we hit a case where we just don't bother, e.g. `?x: Trait` goals. + Ambiguity, + /// We gave up due to an overflow, most often by hitting the recursion limit. + Overflow, +} + +/// Additional constraints returned on success. +#[derive(Debug, PartialEq, Eq, Clone, Hash, TypeFoldable, TypeVisitable)] +pub struct ExternalConstraints<'tcx> { + // FIXME: implement this. + regions: (), + opaque_types: Vec<(Ty<'tcx>, Ty<'tcx>)>, +} + +type CanonicalGoal<'tcx, T = ty::Predicate<'tcx>> = Canonical<'tcx, Goal<'tcx, T>>; +type CanonicalResponse<'tcx> = Canonical<'tcx, Response<'tcx>>; +/// The result of evaluating a canonical query. +/// +/// FIXME: We use a different type than the existing canonical queries. This is because +/// we need to add a `Certainty` for `overflow` and may want to restructure this code without +/// having to worry about changes to currently used code. Once we've made progress on this +/// solver, merge the two responses again. +pub type QueryResult<'tcx> = Result, NoSolution>; + +pub trait TyCtxtExt<'tcx> { + fn evaluate_goal(self, goal: CanonicalGoal<'tcx>) -> QueryResult<'tcx>; +} + +impl<'tcx> TyCtxtExt<'tcx> for TyCtxt<'tcx> { + fn evaluate_goal(self, goal: CanonicalGoal<'tcx>) -> QueryResult<'tcx> { + let mut cx = EvalCtxt::new(self); + cx.evaluate_canonical_goal(goal) + } +} + +struct EvalCtxt<'tcx> { + tcx: TyCtxt<'tcx>, + + provisional_cache: cache::ProvisionalCache<'tcx>, + overflow_data: overflow::OverflowData, +} + +impl<'tcx> EvalCtxt<'tcx> { + fn new(tcx: TyCtxt<'tcx>) -> EvalCtxt<'tcx> { + EvalCtxt { + tcx, + provisional_cache: cache::ProvisionalCache::empty(), + overflow_data: overflow::OverflowData::new(tcx), + } + } + + /// Recursively evaluates `goal`, returning whether any inference vars have + /// been constrained and the certainty of the result. + fn evaluate_goal( + &mut self, + infcx: &InferCtxt<'tcx>, + goal: Goal<'tcx, ty::Predicate<'tcx>>, + ) -> Result<(bool, Certainty), NoSolution> { + let mut orig_values = OriginalQueryValues::default(); + let canonical_goal = infcx.canonicalize_query(goal, &mut orig_values); + let canonical_response = self.evaluate_canonical_goal(canonical_goal)?; + Ok(( + true, // FIXME: check whether `var_values` are an identity substitution. + fixme_instantiate_canonical_query_response(infcx, &orig_values, canonical_response), + )) + } + + fn evaluate_canonical_goal(&mut self, goal: CanonicalGoal<'tcx>) -> QueryResult<'tcx> { + match self.try_push_stack(goal) { + Ok(()) => {} + // Our goal is already on the stack, eager return. + Err(response) => return response, + } + + // We may have to repeatedly recompute the goal in case of coinductive cycles, + // check out the `cache` module for more information. + // + // FIXME: Similar to `evaluate_all`, this has to check for overflow. + loop { + let result = self.compute_goal(goal); + + // FIXME: `Response` should be `Copy` + if self.try_finalize_goal(goal, result.clone()) { + return result; + } + } + } + + fn compute_goal(&mut self, canonical_goal: CanonicalGoal<'tcx>) -> QueryResult<'tcx> { + // WARNING: We're looking at a canonical value without instantiating it here. + // + // We have to be incredibly careful to not change the order of bound variables or + // remove any. As we go from `Goal<'tcx, Predicate>` to `Goal` with the variants + // of `PredicateKind` this is the case and it is and faster than instantiating and + // recanonicalizing. + let Goal { param_env, predicate } = canonical_goal.value; + if let Some(kind) = predicate.kind().no_bound_vars() { + match kind { + ty::PredicateKind::Clause(ty::Clause::Trait(predicate)) => self.compute_trait_goal( + canonical_goal.unchecked_rebind(Goal { param_env, predicate }), + ), + ty::PredicateKind::Clause(ty::Clause::Projection(predicate)) => self + .compute_projection_goal( + canonical_goal.unchecked_rebind(Goal { param_env, predicate }), + ), + ty::PredicateKind::Clause(ty::Clause::TypeOutlives(predicate)) => self + .compute_type_outlives_goal( + canonical_goal.unchecked_rebind(Goal { param_env, predicate }), + ), + ty::PredicateKind::Clause(ty::Clause::RegionOutlives(predicate)) => self + .compute_region_outlives_goal( + canonical_goal.unchecked_rebind(Goal { param_env, predicate }), + ), + // FIXME: implement these predicates :) + ty::PredicateKind::WellFormed(_) + | ty::PredicateKind::ObjectSafe(_) + | ty::PredicateKind::ClosureKind(_, _, _) + | ty::PredicateKind::Subtype(_) + | ty::PredicateKind::Coerce(_) + | ty::PredicateKind::ConstEvaluatable(_) + | ty::PredicateKind::ConstEquate(_, _) + | ty::PredicateKind::TypeWellFormedFromEnv(_) + | ty::PredicateKind::Ambiguous => unimplemented!(), + } + } else { + let (infcx, goal, var_values) = + self.tcx.infer_ctxt().build_with_canonical(DUMMY_SP, &canonical_goal); + let kind = infcx.replace_bound_vars_with_placeholders(goal.predicate.kind()); + let goal = goal.with(self.tcx, ty::Binder::dummy(kind)); + let (_, certainty) = self.evaluate_goal(&infcx, goal)?; + infcx.make_canonical_response(var_values, certainty) + } + } + + fn compute_type_outlives_goal( + &mut self, + _goal: CanonicalGoal<'tcx, TypeOutlivesPredicate<'tcx>>, + ) -> QueryResult<'tcx> { + todo!() + } + + fn compute_region_outlives_goal( + &mut self, + _goal: CanonicalGoal<'tcx, RegionOutlivesPredicate<'tcx>>, + ) -> QueryResult<'tcx> { + todo!() + } +} + +impl<'tcx> EvalCtxt<'tcx> { + fn evaluate_all( + &mut self, + infcx: &InferCtxt<'tcx>, + mut goals: Vec>>, + ) -> Result { + let mut new_goals = Vec::new(); + self.repeat_while_none(|this| { + let mut has_changed = Err(Certainty::Yes); + for goal in goals.drain(..) { + let (changed, certainty) = match this.evaluate_goal(infcx, goal) { + Ok(result) => result, + Err(NoSolution) => return Some(Err(NoSolution)), + }; + + if changed { + has_changed = Ok(()); + } + + match certainty { + Certainty::Yes => {} + Certainty::Maybe(_) => { + new_goals.push(goal); + has_changed = has_changed.map_err(|c| c.unify_and(certainty)); + } + } + } + + match has_changed { + Ok(()) => { + mem::swap(&mut new_goals, &mut goals); + None + } + Err(certainty) => Some(Ok(certainty)), + } + }) + } +} + +fn fixme_instantiate_canonical_query_response<'tcx>( + _: &InferCtxt<'tcx>, + _: &OriginalQueryValues<'tcx>, + _: CanonicalResponse<'tcx>, +) -> Certainty { + unimplemented!() +} diff --git a/compiler/rustc_trait_selection/src/solve/overflow.rs b/compiler/rustc_trait_selection/src/solve/overflow.rs new file mode 100644 index 0000000000000..8d73a83aec96e --- /dev/null +++ b/compiler/rustc_trait_selection/src/solve/overflow.rs @@ -0,0 +1,80 @@ +use rustc_infer::traits::query::NoSolution; +use rustc_middle::ty::TyCtxt; +use rustc_session::Limit; + +use super::{Certainty, EvalCtxt, MaybeCause, QueryResult}; + +/// When detecting a solver overflow, we return ambiguity. Overflow can be +/// *hidden* by either a fatal error in an **AND** or a trivial success in an **OR**. +/// +/// This is in issue in case of exponential blowup, e.g. if each goal on the stack +/// has multiple nested (overflowing) candidates. To deal with this, we reduce the limit +/// used by the solver when hitting the default limit for the first time. +/// +/// FIXME: Get tests where always using the `default_limit` results in a hang and refer +/// to them here. We can also improve the overflow strategy if necessary. +pub(super) struct OverflowData { + default_limit: Limit, + current_limit: Limit, + /// When proving an **AND** we have to repeatedly iterate over the yet unproven goals. + /// + /// Because of this each iteration also increases the depth in addition to the stack + /// depth. + additional_depth: usize, +} + +impl OverflowData { + pub(super) fn new(tcx: TyCtxt<'_>) -> OverflowData { + let default_limit = tcx.recursion_limit(); + OverflowData { default_limit, current_limit: default_limit, additional_depth: 0 } + } + + #[inline] + pub(super) fn did_overflow(&self) -> bool { + self.default_limit.0 != self.current_limit.0 + } + + #[inline] + pub(super) fn has_overflow(&self, depth: usize) -> bool { + self.current_limit.value_within_limit(depth + self.additional_depth) + } + + /// Updating the current limit when hitting overflow. + fn deal_with_overflow(&mut self) { + // When first hitting overflow we reduce the overflow limit + // for all future goals to prevent hangs if there's an exponental + // blowup. + self.current_limit.0 = self.default_limit.0 / 8; + } +} + +impl<'tcx> EvalCtxt<'tcx> { + pub(super) fn deal_with_overflow(&mut self) -> QueryResult<'tcx> { + self.overflow_data.deal_with_overflow(); + fixme_response_overflow_no_constraints() + } + + /// A `while`-loop which tracks overflow. + pub(super) fn repeat_while_none( + &mut self, + mut loop_body: impl FnMut(&mut Self) -> Option>, + ) -> Result { + let start_depth = self.overflow_data.additional_depth; + let depth = self.provisional_cache.current_depth(); + while !self.overflow_data.has_overflow(depth) { + if let Some(result) = loop_body(self) { + self.overflow_data.additional_depth = start_depth; + return result; + } + + self.overflow_data.additional_depth += 1; + } + self.overflow_data.additional_depth = start_depth; + self.overflow_data.deal_with_overflow(); + Ok(Certainty::Maybe(MaybeCause::Overflow)) + } +} + +fn fixme_response_overflow_no_constraints<'tcx>() -> QueryResult<'tcx> { + unimplemented!() +} diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs new file mode 100644 index 0000000000000..47a8f59ceb77f --- /dev/null +++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs @@ -0,0 +1,324 @@ +use crate::traits::{specialization_graph, translate_substs}; + +use super::infcx_ext::InferCtxtExt; +use super::{ + fixme_instantiate_canonical_query_response, CanonicalGoal, CanonicalResponse, Certainty, + EvalCtxt, Goal, QueryResult, +}; +use rustc_errors::ErrorGuaranteed; +use rustc_hir::def::DefKind; +use rustc_hir::def_id::DefId; +use rustc_infer::infer::canonical::{CanonicalVarValues, OriginalQueryValues}; +use rustc_infer::infer::{InferCtxt, InferOk, TyCtxtInferExt}; +use rustc_infer::traits::query::NoSolution; +use rustc_infer::traits::specialization_graph::LeafDef; +use rustc_infer::traits::{ObligationCause, Reveal}; +use rustc_middle::ty; +use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams}; +use rustc_middle::ty::ProjectionPredicate; +use rustc_middle::ty::TypeVisitable; +use rustc_span::DUMMY_SP; +use std::iter; + +// FIXME: Deduplicate the candidate code between projection and trait goal. + +/// Similar to [super::trait_goals::Candidate] but for `Projection` goals. +#[derive(Debug, Clone)] +struct Candidate<'tcx> { + source: CandidateSource, + result: CanonicalResponse<'tcx>, +} + +#[allow(dead_code)] // FIXME: implement and use all variants. +#[derive(Debug, Clone, Copy)] +enum CandidateSource { + Impl(DefId), + ParamEnv(usize), + Builtin, +} + +impl<'tcx> EvalCtxt<'tcx> { + pub(super) fn compute_projection_goal( + &mut self, + goal: CanonicalGoal<'tcx, ProjectionPredicate<'tcx>>, + ) -> QueryResult<'tcx> { + let candidates = self.assemble_and_evaluate_project_candidates(goal); + self.merge_project_candidates(candidates) + } + + fn assemble_and_evaluate_project_candidates( + &mut self, + goal: CanonicalGoal<'tcx, ProjectionPredicate<'tcx>>, + ) -> Vec> { + let (ref infcx, goal, var_values) = + self.tcx.infer_ctxt().build_with_canonical(DUMMY_SP, &goal); + let mut acx = AssemblyCtxt { cx: self, infcx, var_values, candidates: Vec::new() }; + + acx.assemble_candidates_after_normalizing_self_ty(goal); + acx.assemble_impl_candidates(goal); + acx.candidates + } + + fn merge_project_candidates( + &mut self, + mut candidates: Vec>, + ) -> QueryResult<'tcx> { + match candidates.len() { + 0 => return Err(NoSolution), + 1 => return Ok(candidates.pop().unwrap().result), + _ => {} + } + + if candidates.len() > 1 { + let mut i = 0; + 'outer: while i < candidates.len() { + for j in (0..candidates.len()).filter(|&j| i != j) { + if self.project_candidate_should_be_dropped_in_favor_of( + &candidates[i], + &candidates[j], + ) { + debug!(candidate = ?candidates[i], "Dropping candidate #{}/{}", i, candidates.len()); + candidates.swap_remove(i); + continue 'outer; + } + } + + debug!(candidate = ?candidates[i], "Retaining candidate #{}/{}", i, candidates.len()); + // If there are *STILL* multiple candidates, give up + // and report ambiguity. + i += 1; + if i > 1 { + debug!("multiple matches, ambig"); + // FIXME: return overflow if all candidates overflow, otherwise return ambiguity. + unimplemented!(); + } + } + } + + Ok(candidates.pop().unwrap().result) + } + + fn project_candidate_should_be_dropped_in_favor_of( + &self, + candidate: &Candidate<'tcx>, + other: &Candidate<'tcx>, + ) -> bool { + // FIXME: implement this + match (candidate.source, other.source) { + (CandidateSource::Impl(_), _) + | (CandidateSource::ParamEnv(_), _) + | (CandidateSource::Builtin, _) => unimplemented!(), + } + } +} + +/// Similar to [super::trait_goals::AssemblyCtxt] but for `Projection` goals. +struct AssemblyCtxt<'a, 'tcx> { + cx: &'a mut EvalCtxt<'tcx>, + infcx: &'a InferCtxt<'tcx>, + var_values: CanonicalVarValues<'tcx>, + candidates: Vec>, +} + +impl<'tcx> AssemblyCtxt<'_, 'tcx> { + fn try_insert_candidate(&mut self, source: CandidateSource, certainty: Certainty) { + match self.infcx.make_canonical_response(self.var_values.clone(), certainty) { + Ok(result) => self.candidates.push(Candidate { source, result }), + Err(NoSolution) => debug!(?source, ?certainty, "failed leakcheck"), + } + } + + fn assemble_candidates_after_normalizing_self_ty( + &mut self, + goal: Goal<'tcx, ProjectionPredicate<'tcx>>, + ) { + let tcx = self.cx.tcx; + let &ty::Alias(ty::Projection, projection_ty) = goal.predicate.projection_ty.self_ty().kind() else { + return + }; + self.infcx.probe(|_| { + let normalized_ty = self.infcx.next_ty_infer(); + let normalizes_to_goal = goal.with( + tcx, + ty::Binder::dummy(ty::ProjectionPredicate { + projection_ty, + term: normalized_ty.into(), + }), + ); + let normalization_certainty = + match self.cx.evaluate_goal(&self.infcx, normalizes_to_goal) { + Ok((_, certainty)) => certainty, + Err(NoSolution) => return, + }; + + // NOTE: Alternatively we could call `evaluate_goal` here and only have a `Normalized` candidate. + // This doesn't work as long as we use `CandidateSource` in both winnowing and to resolve associated items. + let goal = goal.with(tcx, goal.predicate.with_self_ty(tcx, normalized_ty)); + let mut orig_values = OriginalQueryValues::default(); + let goal = self.infcx.canonicalize_query(goal, &mut orig_values); + let normalized_candidates = self.cx.assemble_and_evaluate_project_candidates(goal); + // Map each candidate from being canonical wrt the current inference context to being + // canonical wrt the caller. + for Candidate { source, result } in normalized_candidates { + self.infcx.probe(|_| { + let candidate_certainty = fixme_instantiate_canonical_query_response( + self.infcx, + &orig_values, + result, + ); + self.try_insert_candidate( + source, + normalization_certainty.unify_and(candidate_certainty), + ) + }) + } + }) + } + + fn assemble_impl_candidates(&mut self, goal: Goal<'tcx, ProjectionPredicate<'tcx>>) { + self.cx.tcx.for_each_relevant_impl( + goal.predicate.trait_def_id(self.cx.tcx), + goal.predicate.self_ty(), + |impl_def_id| self.consider_impl_candidate(goal, impl_def_id), + ); + } + + fn consider_impl_candidate( + &mut self, + goal: Goal<'tcx, ProjectionPredicate<'tcx>>, + impl_def_id: DefId, + ) { + let tcx = self.cx.tcx; + let goal_trait_ref = goal.predicate.projection_ty.trait_ref(tcx); + let impl_trait_ref = tcx.bound_impl_trait_ref(impl_def_id).unwrap(); + let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::AsPlaceholder }; + if iter::zip(goal_trait_ref.substs, impl_trait_ref.skip_binder().substs) + .any(|(goal, imp)| !drcx.generic_args_may_unify(goal, imp)) + { + return; + } + + self.infcx.probe(|_| { + let impl_substs = self.infcx.fresh_substs_for_item(DUMMY_SP, impl_def_id); + let impl_trait_ref = impl_trait_ref.subst(tcx, impl_substs); + + let Ok(InferOk { obligations, .. }) = self + .infcx + .at(&ObligationCause::dummy(), goal.param_env) + .define_opaque_types(false) + .eq(goal_trait_ref, impl_trait_ref) + .map_err(|e| debug!("failed to equate trait refs: {e:?}")) + else { + return + }; + + let nested_goals = obligations.into_iter().map(|o| o.into()).collect(); + let Ok(trait_ref_certainty) = self.cx.evaluate_all(self.infcx, nested_goals) else { return }; + + let Some(assoc_def) = self.fetch_eligible_assoc_item_def( + goal.param_env, + goal_trait_ref, + goal.predicate.def_id(), + impl_def_id + ) else { + return + }; + + if !assoc_def.item.defaultness(tcx).has_value() { + tcx.sess.delay_span_bug( + tcx.def_span(assoc_def.item.def_id), + "missing value for assoc item in impl", + ); + } + + // Getting the right substitutions here is complex, e.g. given: + // - a goal ` as Trait>::Assoc` + // - the applicable impl `impl Trait for Vec` + // - and the impl which defines `Assoc` being `impl Trait for Vec` + // + // We first rebase the goal substs onto the impl, going from `[Vec, i32, u64]` + // to `[u32, u64]`. + // + // And then map these substs to the substs of the defining impl of `Assoc`, going + // from `[u32, u64]` to `[u32, i32, u64]`. + let impl_substs_with_gat = goal.predicate.projection_ty.substs.rebase_onto( + tcx, + goal_trait_ref.def_id, + impl_trait_ref.substs, + ); + let substs = translate_substs( + self.infcx, + goal.param_env, + impl_def_id, + impl_substs_with_gat, + assoc_def.defining_node, + ); + + // Finally we construct the actual value of the associated type. + let is_const = matches!(tcx.def_kind(assoc_def.item.def_id), DefKind::AssocConst); + let ty = tcx.bound_type_of(assoc_def.item.def_id); + let term: ty::EarlyBinder> = if is_const { + let identity_substs = ty::InternalSubsts::identity_for_item(tcx, assoc_def.item.def_id); + let did = ty::WithOptConstParam::unknown(assoc_def.item.def_id); + let kind = + ty::ConstKind::Unevaluated(ty::UnevaluatedConst::new(did, identity_substs)); + ty.map_bound(|ty| tcx.mk_const(kind, ty).into()) + } else { + ty.map_bound(|ty| ty.into()) + }; + + let Ok(InferOk { obligations, .. }) = self + .infcx + .at(&ObligationCause::dummy(), goal.param_env) + .define_opaque_types(false) + .eq(goal.predicate.term, term.subst(tcx, substs)) + .map_err(|e| debug!("failed to equate trait refs: {e:?}")) + else { + return + }; + + let nested_goals = obligations.into_iter().map(|o| o.into()).collect(); + let Ok(rhs_certainty) = self.cx.evaluate_all(self.infcx, nested_goals) else { return }; + + let certainty = trait_ref_certainty.unify_and(rhs_certainty); + self.try_insert_candidate(CandidateSource::Impl(impl_def_id), certainty); + }) + } + + /// This behavior is also implemented in `rustc_ty_utils` and in the old `project` code. + /// + /// FIXME: We should merge these 3 implementations as it's likely that they otherwise + /// diverge. + #[instrument(level = "debug", skip(self, param_env), ret)] + fn fetch_eligible_assoc_item_def( + &self, + param_env: ty::ParamEnv<'tcx>, + goal_trait_ref: ty::TraitRef<'tcx>, + trait_assoc_def_id: DefId, + impl_def_id: DefId, + ) -> Option { + let node_item = + specialization_graph::assoc_def(self.cx.tcx, impl_def_id, trait_assoc_def_id) + .map_err(|ErrorGuaranteed { .. }| ()) + .ok()?; + + let eligible = if node_item.is_final() { + // Non-specializable items are always projectable. + true + } else { + // Only reveal a specializable default if we're past type-checking + // and the obligation is monomorphic, otherwise passes such as + // transmute checking and polymorphic MIR optimizations could + // get a result which isn't correct for all monomorphizations. + if param_env.reveal() == Reveal::All { + let poly_trait_ref = self.infcx.resolve_vars_if_possible(goal_trait_ref); + !poly_trait_ref.still_further_specializable() + } else { + debug!(?node_item.item.def_id, "not eligible due to default"); + false + } + }; + + if eligible { Some(node_item) } else { None } + } +} diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs new file mode 100644 index 0000000000000..4ea9081bf46a6 --- /dev/null +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -0,0 +1,282 @@ +//! Dealing with trait goals, i.e. `T: Trait<'a, U>`. + +use std::iter; + +use super::infcx_ext::InferCtxtExt; +use super::{ + fixme_instantiate_canonical_query_response, CanonicalGoal, CanonicalResponse, Certainty, + EvalCtxt, Goal, QueryResult, +}; +use rustc_hir::def_id::DefId; +use rustc_infer::infer::canonical::{CanonicalVarValues, OriginalQueryValues}; +use rustc_infer::infer::TyCtxtInferExt; +use rustc_infer::infer::{InferCtxt, InferOk}; +use rustc_infer::traits::query::NoSolution; +use rustc_infer::traits::ObligationCause; +use rustc_middle::ty; +use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams}; +use rustc_middle::ty::TraitPredicate; +use rustc_span::DUMMY_SP; + +/// A candidate is a possible way to prove a goal. +/// +/// It consists of both the `source`, which describes how that goal +/// would be proven, and the `result` when using the given `source`. +/// +/// For the list of possible candidates, please look at the documentation +/// of [CandidateSource]. +#[derive(Debug, Clone)] +pub(super) struct Candidate<'tcx> { + source: CandidateSource, + result: CanonicalResponse<'tcx>, +} + +#[allow(dead_code)] // FIXME: implement and use all variants. +#[derive(Debug, Clone, Copy)] +pub(super) enum CandidateSource { + /// Some user-defined impl with the given `DefId`. + Impl(DefId), + /// The n-th caller bound in the `param_env` of our goal. + /// + /// This is pretty much always a bound from the `where`-clauses of the + /// currently checked item. + ParamEnv(usize), + /// A bound on the `self_ty` in case it is a projection or an opaque type. + /// + /// # Examples + /// + /// ```ignore (for syntax highlighting) + /// trait Trait { + /// type Assoc: OtherTrait; + /// } + /// ``` + /// + /// We know that `::Assoc: OtherTrait` holds by looking at + /// the bounds on `Trait::Assoc`. + AliasBound(usize), + /// A builtin implementation for some specific traits, used in cases + /// where we cannot rely an ordinary library implementations. + /// + /// The most notable examples are `Sized`, `Copy` and `Clone`. This is also + /// used for the `DiscriminantKind` and `Pointee` trait, both of which have + /// an associated type. + Builtin, + /// An automatic impl for an auto trait, e.g. `Send`. These impls recursively look + /// at the constituent types of the `self_ty` to check whether the auto trait + /// is implemented for those. + AutoImpl, +} + +struct AssemblyCtxt<'a, 'tcx> { + cx: &'a mut EvalCtxt<'tcx>, + infcx: &'a InferCtxt<'tcx>, + var_values: CanonicalVarValues<'tcx>, + candidates: Vec>, +} + +impl<'tcx> EvalCtxt<'tcx> { + pub(super) fn compute_trait_goal( + &mut self, + goal: CanonicalGoal<'tcx, TraitPredicate<'tcx>>, + ) -> QueryResult<'tcx> { + let candidates = self.assemble_and_evaluate_trait_candidates(goal); + self.merge_trait_candidates_discard_reservation_impls(candidates) + } + + pub(super) fn assemble_and_evaluate_trait_candidates( + &mut self, + goal: CanonicalGoal<'tcx, TraitPredicate<'tcx>>, + ) -> Vec> { + let (ref infcx, goal, var_values) = + self.tcx.infer_ctxt().build_with_canonical(DUMMY_SP, &goal); + let mut acx = AssemblyCtxt { cx: self, infcx, var_values, candidates: Vec::new() }; + + acx.assemble_candidates_after_normalizing_self_ty(goal); + acx.assemble_impl_candidates(goal); + + // FIXME: Remaining candidates + acx.candidates + } + + #[instrument(level = "debug", skip(self), ret)] + pub(super) fn merge_trait_candidates_discard_reservation_impls( + &mut self, + mut candidates: Vec>, + ) -> QueryResult<'tcx> { + match candidates.len() { + 0 => return Err(NoSolution), + 1 => return Ok(self.discard_reservation_impl(candidates.pop().unwrap()).result), + _ => {} + } + + if candidates.len() > 1 { + let mut i = 0; + 'outer: while i < candidates.len() { + for j in (0..candidates.len()).filter(|&j| i != j) { + if self.trait_candidate_should_be_dropped_in_favor_of( + &candidates[i], + &candidates[j], + ) { + debug!(candidate = ?candidates[i], "Dropping candidate #{}/{}", i, candidates.len()); + candidates.swap_remove(i); + continue 'outer; + } + } + + debug!(candidate = ?candidates[i], "Retaining candidate #{}/{}", i, candidates.len()); + // If there are *STILL* multiple candidates, give up + // and report ambiguity. + i += 1; + if i > 1 { + debug!("multiple matches, ambig"); + // FIXME: return overflow if all candidates overflow, otherwise return ambiguity. + unimplemented!(); + } + } + } + + Ok(self.discard_reservation_impl(candidates.pop().unwrap()).result) + } + + fn trait_candidate_should_be_dropped_in_favor_of( + &self, + candidate: &Candidate<'tcx>, + other: &Candidate<'tcx>, + ) -> bool { + // FIXME: implement this + match (candidate.source, other.source) { + (CandidateSource::Impl(_), _) + | (CandidateSource::ParamEnv(_), _) + | (CandidateSource::AliasBound(_), _) + | (CandidateSource::Builtin, _) + | (CandidateSource::AutoImpl, _) => unimplemented!(), + } + } + + fn discard_reservation_impl(&self, candidate: Candidate<'tcx>) -> Candidate<'tcx> { + if let CandidateSource::Impl(def_id) = candidate.source { + if let ty::ImplPolarity::Reservation = self.tcx.impl_polarity(def_id) { + debug!("Selected reservation impl"); + // FIXME: reduce candidate to ambiguous + // FIXME: replace `var_values` with identity, yeet external constraints. + unimplemented!() + } + } + + candidate + } +} + +impl<'tcx> AssemblyCtxt<'_, 'tcx> { + /// Adds a new candidate using the current state of the inference context. + /// + /// This does require each assembly method to correctly use `probe` to not taint + /// the results of other candidates. + fn try_insert_candidate(&mut self, source: CandidateSource, certainty: Certainty) { + match self.infcx.make_canonical_response(self.var_values.clone(), certainty) { + Ok(result) => self.candidates.push(Candidate { source, result }), + Err(NoSolution) => debug!(?source, ?certainty, "failed leakcheck"), + } + } + + /// If the self type of a trait goal is a projection, computing the relevant candidates is difficult. + /// + /// To deal with this, we first try to normalize the self type and add the candidates for the normalized + /// self type to the list of candidates in case that succeeds. Note that we can't just eagerly return in + /// this case as projections as self types add ` + fn assemble_candidates_after_normalizing_self_ty( + &mut self, + goal: Goal<'tcx, TraitPredicate<'tcx>>, + ) { + let tcx = self.cx.tcx; + // FIXME: We also have to normalize opaque types, not sure where to best fit that in. + let &ty::Alias(ty::Projection, projection_ty) = goal.predicate.self_ty().kind() else { + return + }; + self.infcx.probe(|_| { + let normalized_ty = self.infcx.next_ty_infer(); + let normalizes_to_goal = goal.with( + tcx, + ty::Binder::dummy(ty::ProjectionPredicate { + projection_ty, + term: normalized_ty.into(), + }), + ); + let normalization_certainty = + match self.cx.evaluate_goal(&self.infcx, normalizes_to_goal) { + Ok((_, certainty)) => certainty, + Err(NoSolution) => return, + }; + + // NOTE: Alternatively we could call `evaluate_goal` here and only have a `Normalized` candidate. + // This doesn't work as long as we use `CandidateSource` in both winnowing and to resolve associated items. + let goal = goal.with(tcx, goal.predicate.with_self_type(tcx, normalized_ty)); + let mut orig_values = OriginalQueryValues::default(); + let goal = self.infcx.canonicalize_query(goal, &mut orig_values); + let normalized_candidates = self.cx.assemble_and_evaluate_trait_candidates(goal); + + // Map each candidate from being canonical wrt the current inference context to being + // canonical wrt the caller. + for Candidate { source, result } in normalized_candidates { + self.infcx.probe(|_| { + let candidate_certainty = fixme_instantiate_canonical_query_response( + self.infcx, + &orig_values, + result, + ); + + // FIXME: This is a bit scary if the `normalizes_to_goal` overflows. + // + // If we have an ambiguous candidate it hides that normalization + // caused an overflow which may cause issues. + self.try_insert_candidate( + source, + normalization_certainty.unify_and(candidate_certainty), + ) + }) + } + }) + } + + fn assemble_impl_candidates(&mut self, goal: Goal<'tcx, TraitPredicate<'tcx>>) { + self.cx.tcx.for_each_relevant_impl( + goal.predicate.def_id(), + goal.predicate.self_ty(), + |impl_def_id| self.consider_impl_candidate(goal, impl_def_id), + ); + } + + fn consider_impl_candidate( + &mut self, + goal: Goal<'tcx, TraitPredicate<'tcx>>, + impl_def_id: DefId, + ) { + let impl_trait_ref = self.cx.tcx.bound_impl_trait_ref(impl_def_id).unwrap(); + let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::AsPlaceholder }; + if iter::zip(goal.predicate.trait_ref.substs, impl_trait_ref.skip_binder().substs) + .any(|(goal, imp)| !drcx.generic_args_may_unify(goal, imp)) + { + return; + } + + self.infcx.probe(|_| { + let impl_substs = self.infcx.fresh_substs_for_item(DUMMY_SP, impl_def_id); + let impl_trait_ref = impl_trait_ref.subst(self.cx.tcx, impl_substs); + + let Ok(InferOk { obligations, .. }) = self + .infcx + .at(&ObligationCause::dummy(), goal.param_env) + .define_opaque_types(false) + .eq(goal.predicate.trait_ref, impl_trait_ref) + .map_err(|e| debug!("failed to equate trait refs: {e:?}")) + else { + return + }; + + let nested_goals = obligations.into_iter().map(|o| o.into()).collect(); + + let Ok(certainty) = self.cx.evaluate_all(self.infcx, nested_goals) else { return }; + self.try_insert_candidate(CandidateSource::Impl(impl_def_id), certainty); + }) + } +} diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 84d7244c1db78..5276da2e49c75 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -25,7 +25,6 @@ use rustc_data_structures::sso::SsoHashSet; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::ErrorGuaranteed; use rustc_hir::def::DefKind; -use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; use rustc_infer::infer::at::At; use rustc_infer::infer::resolve::OpportunisticRegionResolver; @@ -1553,7 +1552,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( // NOTE: This should be kept in sync with the similar code in // `rustc_ty_utils::instance::resolve_associated_item()`. let node_item = - assoc_def(selcx, impl_data.impl_def_id, obligation.predicate.def_id) + specialization_graph::assoc_def(selcx.tcx(), impl_data.impl_def_id, obligation.predicate.def_id) .map_err(|ErrorGuaranteed { .. }| ())?; if node_item.is_final() { @@ -2113,7 +2112,7 @@ fn confirm_impl_candidate<'cx, 'tcx>( let trait_def_id = tcx.trait_id_of_impl(impl_def_id).unwrap(); let param_env = obligation.param_env; - let Ok(assoc_ty) = assoc_def(selcx, impl_def_id, assoc_item_id) else { + let Ok(assoc_ty) = specialization_graph::assoc_def(tcx, impl_def_id, assoc_item_id) else { return Progress { term: tcx.ty_error().into(), obligations: nested }; }; @@ -2210,7 +2209,7 @@ fn confirm_impl_trait_in_trait_candidate<'tcx>( let mut obligations = data.nested; let trait_fn_def_id = tcx.impl_trait_in_trait_parent(obligation.predicate.def_id); - let Ok(leaf_def) = assoc_def(selcx, data.impl_def_id, trait_fn_def_id) else { + let Ok(leaf_def) = specialization_graph::assoc_def(tcx, data.impl_def_id, trait_fn_def_id) else { return Progress { term: tcx.ty_error().into(), obligations }; }; if !leaf_def.item.defaultness(tcx).has_value() { @@ -2347,58 +2346,6 @@ fn assoc_ty_own_obligations<'cx, 'tcx>( } } -/// Locate the definition of an associated type in the specialization hierarchy, -/// starting from the given impl. -/// -/// Based on the "projection mode", this lookup may in fact only examine the -/// topmost impl. See the comments for `Reveal` for more details. -fn assoc_def( - selcx: &SelectionContext<'_, '_>, - impl_def_id: DefId, - assoc_def_id: DefId, -) -> Result { - let tcx = selcx.tcx(); - let trait_def_id = tcx.impl_trait_ref(impl_def_id).unwrap().def_id; - let trait_def = tcx.trait_def(trait_def_id); - - // This function may be called while we are still building the - // specialization graph that is queried below (via TraitDef::ancestors()), - // so, in order to avoid unnecessary infinite recursion, we manually look - // for the associated item at the given impl. - // If there is no such item in that impl, this function will fail with a - // cycle error if the specialization graph is currently being built. - if let Some(&impl_item_id) = tcx.impl_item_implementor_ids(impl_def_id).get(&assoc_def_id) { - let item = tcx.associated_item(impl_item_id); - let impl_node = specialization_graph::Node::Impl(impl_def_id); - return Ok(specialization_graph::LeafDef { - item: *item, - defining_node: impl_node, - finalizing_node: if item.defaultness(tcx).is_default() { - None - } else { - Some(impl_node) - }, - }); - } - - let ancestors = trait_def.ancestors(tcx, impl_def_id)?; - if let Some(assoc_item) = ancestors.leaf_def(tcx, assoc_def_id) { - Ok(assoc_item) - } else { - // This is saying that neither the trait nor - // the impl contain a definition for this - // associated type. Normally this situation - // could only arise through a compiler bug -- - // if the user wrote a bad item name, it - // should have failed in astconv. - bug!( - "No associated type `{}` for {}", - tcx.item_name(assoc_def_id), - tcx.def_path_str(impl_def_id) - ) - } -} - pub(crate) trait ProjectionCacheKeyExt<'cx, 'tcx>: Sized { fn from_poly_projection_predicate( selcx: &mut SelectionContext<'cx, 'tcx>, diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 792933096b15c..81e8f9e914c23 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -1171,19 +1171,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { where I: Iterator>, { - cycle.all(|predicate| self.coinductive_predicate(predicate)) - } - - fn coinductive_predicate(&self, predicate: ty::Predicate<'tcx>) -> bool { - let result = match predicate.kind().skip_binder() { - ty::PredicateKind::Clause(ty::Clause::Trait(ref data)) => { - self.tcx().trait_is_coinductive(data.def_id()) - } - ty::PredicateKind::WellFormed(_) => true, - _ => false, - }; - debug!(?predicate, ?result, "coinductive_predicate"); - result + cycle.all(|predicate| predicate.is_coinductive(self.tcx())) } /// Further evaluates `candidate` to decide whether all type parameters match and whether nested diff --git a/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs b/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs index 4546c95339300..02b0667774028 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs @@ -1,6 +1,7 @@ use super::OverlapError; use crate::traits; +use rustc_errors::ErrorGuaranteed; use rustc_hir::def_id::DefId; use rustc_middle::ty::fast_reject::{self, SimplifiedType, TreatParams}; use rustc_middle::ty::{self, TyCtxt, TypeVisitable}; @@ -379,3 +380,51 @@ impl<'tcx> GraphExt<'tcx> for Graph { self.children.entry(parent).or_default().insert_blindly(tcx, child); } } + +/// Locate the definition of an associated type in the specialization hierarchy, +/// starting from the given impl. +pub(crate) fn assoc_def( + tcx: TyCtxt<'_>, + impl_def_id: DefId, + assoc_def_id: DefId, +) -> Result { + let trait_def_id = tcx.impl_trait_ref(impl_def_id).unwrap().def_id; + let trait_def = tcx.trait_def(trait_def_id); + + // This function may be called while we are still building the + // specialization graph that is queried below (via TraitDef::ancestors()), + // so, in order to avoid unnecessary infinite recursion, we manually look + // for the associated item at the given impl. + // If there is no such item in that impl, this function will fail with a + // cycle error if the specialization graph is currently being built. + if let Some(&impl_item_id) = tcx.impl_item_implementor_ids(impl_def_id).get(&assoc_def_id) { + let &item = tcx.associated_item(impl_item_id); + let impl_node = Node::Impl(impl_def_id); + return Ok(LeafDef { + item, + defining_node: impl_node, + finalizing_node: if item.defaultness(tcx).is_default() { + None + } else { + Some(impl_node) + }, + }); + } + + let ancestors = trait_def.ancestors(tcx, impl_def_id)?; + if let Some(assoc_item) = ancestors.leaf_def(tcx, assoc_def_id) { + Ok(assoc_item) + } else { + // This is saying that neither the trait nor + // the impl contain a definition for this + // associated type. Normally this situation + // could only arise through a compiler bug -- + // if the user wrote a bad item name, it + // should have failed in astconv. + bug!( + "No associated type `{}` for {}", + tcx.item_name(assoc_def_id), + tcx.def_path_str(impl_def_id) + ) + } +} diff --git a/src/test/ui/traits/solver-cycles/inductive-canonical-cycle.rs b/src/test/ui/traits/solver-cycles/inductive-canonical-cycle.rs new file mode 100644 index 0000000000000..a3bb76d7e3b73 --- /dev/null +++ b/src/test/ui/traits/solver-cycles/inductive-canonical-cycle.rs @@ -0,0 +1,28 @@ +// known-bug + +// This should compile but fails with the current solver. +// +// This checks that the new solver uses `Ambiguous` when hitting the +// inductive cycle here when proving `exists<^0, ^1> (): Trait<^0, ^1>` +// which requires proving `Trait` but that has the same +// canonical representation. +trait Trait {} + +impl Trait for () +where + (): Trait, + T: OtherTrait, +{} + +trait OtherTrait {} +impl OtherTrait for u32 {} + +fn require_trait() +where + (): Trait +{} + +fn main() { + require_trait::<_, _>(); + //~^ ERROR overflow evaluating +} diff --git a/src/test/ui/traits/solver-cycles/inductive-canonical-cycle.stderr b/src/test/ui/traits/solver-cycles/inductive-canonical-cycle.stderr new file mode 100644 index 0000000000000..e4b84e07822d2 --- /dev/null +++ b/src/test/ui/traits/solver-cycles/inductive-canonical-cycle.stderr @@ -0,0 +1,26 @@ +error[E0275]: overflow evaluating the requirement `_: Sized` + --> $DIR/inductive-canonical-cycle.rs:26:5 + | +LL | require_trait::<_, _>(); + | ^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`inductive_canonical_cycle`) +note: required for `()` to implement `Trait<_, _>` + --> $DIR/inductive-canonical-cycle.rs:11:12 + | +LL | impl Trait for () + | ^^^^^^^^^^^ ^^ + = note: 128 redundant requirements hidden + = note: required for `()` to implement `Trait<_, _>` +note: required by a bound in `require_trait` + --> $DIR/inductive-canonical-cycle.rs:22:9 + | +LL | fn require_trait() + | ------------- required by a bound in this +LL | where +LL | (): Trait + | ^^^^^^^^^^^ required by this bound in `require_trait` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0275`. diff --git a/triagebot.toml b/triagebot.toml index 46a3bab42a17e..c7158a51861e5 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -342,6 +342,10 @@ cc = ["@BoxyUwU"] message = "Some changes occured in `rustc_ty_utils::consts.rs`" cc = ["@BoxyUwU"] +[mentions."compiler/rustc_trait_selection/src/solve] +message = "Some changes occurred to the core trait solver" +cc = ["@lcnr"] + [mentions."compiler/rustc_trait_selection/src/traits/engine.rs"] message = """ Some changes occurred in engine.rs, potentially modifying the public API \ From 55c4164fffb8c4389727cff554dd77012c624087 Mon Sep 17 00:00:00 2001 From: Arvind Mukund Date: Mon, 19 Dec 2022 19:06:30 -0800 Subject: [PATCH 07/30] Correct ModFlagBehavior for Aarch64 on LLVM-15 When building with Fat LTO and BTI enabled on aarch64, the BTI is set to `Module::Min` for alloc shim but is set to `Module::Error` for the crate. This was fine when we were using LLVM-14 but LLVM-15 changes it's behaviour to support for compiling with different `mbranch-protection` flags. Refer: https://github.com/rust-lang/llvm-project/commit/b0343a38a5910e980bb031e4014655d77cd0c162 --- compiler/rustc_codegen_llvm/src/context.rs | 8 ++++---- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index f3bff5d57161e..9c7d3dabd6fb5 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -283,26 +283,26 @@ pub unsafe fn create_module<'ll>( if sess.target.arch == "aarch64" { llvm::LLVMRustAddModuleFlag( llmod, - llvm::LLVMModFlagBehavior::Error, + llvm::LLVMModFlagBehavior::Min, "branch-target-enforcement\0".as_ptr().cast(), bti.into(), ); llvm::LLVMRustAddModuleFlag( llmod, - llvm::LLVMModFlagBehavior::Error, + llvm::LLVMModFlagBehavior::Min, "sign-return-address\0".as_ptr().cast(), pac_ret.is_some().into(), ); let pac_opts = pac_ret.unwrap_or(PacRet { leaf: false, key: PAuthKey::A }); llvm::LLVMRustAddModuleFlag( llmod, - llvm::LLVMModFlagBehavior::Error, + llvm::LLVMModFlagBehavior::Min, "sign-return-address-all\0".as_ptr().cast(), pac_opts.leaf.into(), ); llvm::LLVMRustAddModuleFlag( llmod, - llvm::LLVMModFlagBehavior::Error, + llvm::LLVMModFlagBehavior::Min, "sign-return-address-with-bkey\0".as_ptr().cast(), u32::from(pac_opts.key == PAuthKey::B), ); diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index e61dbe8b8fc50..6c78966a98d81 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -79,6 +79,7 @@ pub enum LLVMModFlagBehavior { Append = 5, AppendUnique = 6, Max = 7, + Min = 8, } // Consts for the LLVM CallConv type, pre-cast to usize. From 750bf36c33bb6d2d4d7fdc609020507e5f966037 Mon Sep 17 00:00:00 2001 From: lcnr Date: Mon, 19 Dec 2022 07:01:38 +0000 Subject: [PATCH 08/30] dedup assembly --- .../src/solve/assembly.rs | 150 +++++++++++++ .../rustc_trait_selection/src/solve/mod.rs | 1 + .../src/solve/project_goals.rs | 202 +++++------------ .../src/solve/trait_goals.rs | 212 +++++------------- 4 files changed, 267 insertions(+), 298 deletions(-) create mode 100644 compiler/rustc_trait_selection/src/solve/assembly.rs diff --git a/compiler/rustc_trait_selection/src/solve/assembly.rs b/compiler/rustc_trait_selection/src/solve/assembly.rs new file mode 100644 index 0000000000000..e9ddad11ff23e --- /dev/null +++ b/compiler/rustc_trait_selection/src/solve/assembly.rs @@ -0,0 +1,150 @@ +//! Code shared by trait and projection goals for candidate assembly. + +use super::infcx_ext::InferCtxtExt; +use super::{ + fixme_instantiate_canonical_query_response, CanonicalGoal, CanonicalResponse, Certainty, + EvalCtxt, Goal, +}; +use rustc_hir::def_id::DefId; +use rustc_infer::infer::TyCtxtInferExt; +use rustc_infer::infer::{ + canonical::{CanonicalVarValues, OriginalQueryValues}, + InferCtxt, +}; +use rustc_infer::traits::query::NoSolution; +use rustc_middle::ty::TypeFoldable; +use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_span::DUMMY_SP; +use std::fmt::Debug; + +/// A candidate is a possible way to prove a goal. +/// +/// It consists of both the `source`, which describes how that goal would be proven, +/// and the `result` when using the given `source`. +/// +/// For the list of possible candidates, please look at the documentation of +/// [super::trait_goals::CandidateSource] and [super::project_goals::CandidateSource]. +#[derive(Debug, Clone)] +pub(super) struct Candidate<'tcx, G: GoalKind<'tcx>> { + pub(super) source: G::CandidateSource, + pub(super) result: CanonicalResponse<'tcx>, +} + +pub(super) trait GoalKind<'tcx>: TypeFoldable<'tcx> + Copy { + type CandidateSource: Debug + Copy; + + fn self_ty(self) -> Ty<'tcx>; + + fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self; + + fn trait_def_id(self, tcx: TyCtxt<'tcx>) -> DefId; + + fn consider_impl_candidate( + acx: &mut AssemblyCtxt<'_, 'tcx, Self>, + goal: Goal<'tcx, Self>, + impl_def_id: DefId, + ); +} + +/// An abstraction which correctly deals with the canonical results for candidates. +/// +/// It also deduplicates the behavior between trait and projection predicates. +pub(super) struct AssemblyCtxt<'a, 'tcx, G: GoalKind<'tcx>> { + pub(super) cx: &'a mut EvalCtxt<'tcx>, + pub(super) infcx: &'a InferCtxt<'tcx>, + var_values: CanonicalVarValues<'tcx>, + candidates: Vec>, +} + +impl<'a, 'tcx, G: GoalKind<'tcx>> AssemblyCtxt<'a, 'tcx, G> { + pub(super) fn assemble_and_evaluate_candidates( + cx: &'a mut EvalCtxt<'tcx>, + goal: CanonicalGoal<'tcx, G>, + ) -> Vec> { + let (ref infcx, goal, var_values) = + cx.tcx.infer_ctxt().build_with_canonical(DUMMY_SP, &goal); + let mut acx = AssemblyCtxt { cx, infcx, var_values, candidates: Vec::new() }; + + acx.assemble_candidates_after_normalizing_self_ty(goal); + + acx.assemble_impl_candidates(goal); + + acx.candidates + } + + pub(super) fn try_insert_candidate( + &mut self, + source: G::CandidateSource, + certainty: Certainty, + ) { + match self.infcx.make_canonical_response(self.var_values.clone(), certainty) { + Ok(result) => self.candidates.push(Candidate { source, result }), + Err(NoSolution) => debug!(?source, ?certainty, "failed leakcheck"), + } + } + + /// If the self type of a goal is a projection, computing the relevant candidates is difficult. + /// + /// To deal with this, we first try to normalize the self type and add the candidates for the normalized + /// self type to the list of candidates in case that succeeds. Note that we can't just eagerly return in + /// this case as projections as self types add ` + fn assemble_candidates_after_normalizing_self_ty(&mut self, goal: Goal<'tcx, G>) { + let tcx = self.cx.tcx; + // FIXME: We also have to normalize opaque types, not sure where to best fit that in. + let &ty::Alias(ty::Projection, projection_ty) = goal.predicate.self_ty().kind() else { + return + }; + self.infcx.probe(|_| { + let normalized_ty = self.infcx.next_ty_infer(); + let normalizes_to_goal = goal.with( + tcx, + ty::Binder::dummy(ty::ProjectionPredicate { + projection_ty, + term: normalized_ty.into(), + }), + ); + let normalization_certainty = + match self.cx.evaluate_goal(&self.infcx, normalizes_to_goal) { + Ok((_, certainty)) => certainty, + Err(NoSolution) => return, + }; + + // NOTE: Alternatively we could call `evaluate_goal` here and only have a `Normalized` candidate. + // This doesn't work as long as we use `CandidateSource` in both winnowing and to resolve associated items. + let goal = goal.with(tcx, goal.predicate.with_self_ty(tcx, normalized_ty)); + let mut orig_values = OriginalQueryValues::default(); + let goal = self.infcx.canonicalize_query(goal, &mut orig_values); + let normalized_candidates = + AssemblyCtxt::assemble_and_evaluate_candidates(self.cx, goal); + + // Map each candidate from being canonical wrt the current inference context to being + // canonical wrt the caller. + for Candidate { source, result } in normalized_candidates { + self.infcx.probe(|_| { + let candidate_certainty = fixme_instantiate_canonical_query_response( + &self.infcx, + &orig_values, + result, + ); + + // FIXME: This is a bit scary if the `normalizes_to_goal` overflows. + // + // If we have an ambiguous candidate it hides that normalization + // caused an overflow which may cause issues. + self.try_insert_candidate( + source, + normalization_certainty.unify_and(candidate_certainty), + ) + }) + } + }) + } + + fn assemble_impl_candidates(&mut self, goal: Goal<'tcx, G>) { + self.cx.tcx.for_each_relevant_impl( + goal.predicate.trait_def_id(self.cx.tcx), + goal.predicate.self_ty(), + |impl_def_id| G::consider_impl_candidate(self, goal, impl_def_id), + ); + } +} diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs index 5d6529f85423b..7f5e3208f4e7c 100644 --- a/compiler/rustc_trait_selection/src/solve/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/mod.rs @@ -30,6 +30,7 @@ use rustc_span::DUMMY_SP; use self::infcx_ext::InferCtxtExt; +mod assembly; mod cache; mod fulfill; mod infcx_ext; diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs index 47a8f59ceb77f..b50f42c4d9416 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs @@ -1,64 +1,40 @@ use crate::traits::{specialization_graph, translate_substs}; -use super::infcx_ext::InferCtxtExt; -use super::{ - fixme_instantiate_canonical_query_response, CanonicalGoal, CanonicalResponse, Certainty, - EvalCtxt, Goal, QueryResult, -}; +use super::assembly::{self, AssemblyCtxt}; +use super::{CanonicalGoal, EvalCtxt, Goal, QueryResult}; use rustc_errors::ErrorGuaranteed; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; -use rustc_infer::infer::canonical::{CanonicalVarValues, OriginalQueryValues}; -use rustc_infer::infer::{InferCtxt, InferOk, TyCtxtInferExt}; +use rustc_infer::infer::{InferCtxt, InferOk}; use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::specialization_graph::LeafDef; use rustc_infer::traits::{ObligationCause, Reveal}; -use rustc_middle::ty; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams}; use rustc_middle::ty::ProjectionPredicate; use rustc_middle::ty::TypeVisitable; +use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::DUMMY_SP; use std::iter; -// FIXME: Deduplicate the candidate code between projection and trait goal. - -/// Similar to [super::trait_goals::Candidate] but for `Projection` goals. -#[derive(Debug, Clone)] -struct Candidate<'tcx> { - source: CandidateSource, - result: CanonicalResponse<'tcx>, -} - #[allow(dead_code)] // FIXME: implement and use all variants. #[derive(Debug, Clone, Copy)] -enum CandidateSource { +pub(super) enum CandidateSource { Impl(DefId), ParamEnv(usize), Builtin, } +type Candidate<'tcx> = assembly::Candidate<'tcx, ProjectionPredicate<'tcx>>; + impl<'tcx> EvalCtxt<'tcx> { pub(super) fn compute_projection_goal( &mut self, goal: CanonicalGoal<'tcx, ProjectionPredicate<'tcx>>, ) -> QueryResult<'tcx> { - let candidates = self.assemble_and_evaluate_project_candidates(goal); + let candidates = AssemblyCtxt::assemble_and_evaluate_candidates(self, goal); self.merge_project_candidates(candidates) } - fn assemble_and_evaluate_project_candidates( - &mut self, - goal: CanonicalGoal<'tcx, ProjectionPredicate<'tcx>>, - ) -> Vec> { - let (ref infcx, goal, var_values) = - self.tcx.infer_ctxt().build_with_canonical(DUMMY_SP, &goal); - let mut acx = AssemblyCtxt { cx: self, infcx, var_values, candidates: Vec::new() }; - - acx.assemble_candidates_after_normalizing_self_ty(goal); - acx.assemble_impl_candidates(goal); - acx.candidates - } - fn merge_project_candidates( &mut self, mut candidates: Vec>, @@ -112,83 +88,27 @@ impl<'tcx> EvalCtxt<'tcx> { } } -/// Similar to [super::trait_goals::AssemblyCtxt] but for `Projection` goals. -struct AssemblyCtxt<'a, 'tcx> { - cx: &'a mut EvalCtxt<'tcx>, - infcx: &'a InferCtxt<'tcx>, - var_values: CanonicalVarValues<'tcx>, - candidates: Vec>, -} +impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { + type CandidateSource = CandidateSource; -impl<'tcx> AssemblyCtxt<'_, 'tcx> { - fn try_insert_candidate(&mut self, source: CandidateSource, certainty: Certainty) { - match self.infcx.make_canonical_response(self.var_values.clone(), certainty) { - Ok(result) => self.candidates.push(Candidate { source, result }), - Err(NoSolution) => debug!(?source, ?certainty, "failed leakcheck"), - } + fn self_ty(self) -> Ty<'tcx> { + self.self_ty() } - fn assemble_candidates_after_normalizing_self_ty( - &mut self, - goal: Goal<'tcx, ProjectionPredicate<'tcx>>, - ) { - let tcx = self.cx.tcx; - let &ty::Alias(ty::Projection, projection_ty) = goal.predicate.projection_ty.self_ty().kind() else { - return - }; - self.infcx.probe(|_| { - let normalized_ty = self.infcx.next_ty_infer(); - let normalizes_to_goal = goal.with( - tcx, - ty::Binder::dummy(ty::ProjectionPredicate { - projection_ty, - term: normalized_ty.into(), - }), - ); - let normalization_certainty = - match self.cx.evaluate_goal(&self.infcx, normalizes_to_goal) { - Ok((_, certainty)) => certainty, - Err(NoSolution) => return, - }; - - // NOTE: Alternatively we could call `evaluate_goal` here and only have a `Normalized` candidate. - // This doesn't work as long as we use `CandidateSource` in both winnowing and to resolve associated items. - let goal = goal.with(tcx, goal.predicate.with_self_ty(tcx, normalized_ty)); - let mut orig_values = OriginalQueryValues::default(); - let goal = self.infcx.canonicalize_query(goal, &mut orig_values); - let normalized_candidates = self.cx.assemble_and_evaluate_project_candidates(goal); - // Map each candidate from being canonical wrt the current inference context to being - // canonical wrt the caller. - for Candidate { source, result } in normalized_candidates { - self.infcx.probe(|_| { - let candidate_certainty = fixme_instantiate_canonical_query_response( - self.infcx, - &orig_values, - result, - ); - self.try_insert_candidate( - source, - normalization_certainty.unify_and(candidate_certainty), - ) - }) - } - }) + fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self { + self.with_self_ty(tcx, self_ty) } - fn assemble_impl_candidates(&mut self, goal: Goal<'tcx, ProjectionPredicate<'tcx>>) { - self.cx.tcx.for_each_relevant_impl( - goal.predicate.trait_def_id(self.cx.tcx), - goal.predicate.self_ty(), - |impl_def_id| self.consider_impl_candidate(goal, impl_def_id), - ); + fn trait_def_id(self, tcx: TyCtxt<'tcx>) -> DefId { + self.trait_def_id(tcx) } fn consider_impl_candidate( - &mut self, + acx: &mut AssemblyCtxt<'_, 'tcx, ProjectionPredicate<'tcx>>, goal: Goal<'tcx, ProjectionPredicate<'tcx>>, impl_def_id: DefId, ) { - let tcx = self.cx.tcx; + let tcx = acx.cx.tcx; let goal_trait_ref = goal.predicate.projection_ty.trait_ref(tcx); let impl_trait_ref = tcx.bound_impl_trait_ref(impl_def_id).unwrap(); let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::AsPlaceholder }; @@ -198,11 +118,11 @@ impl<'tcx> AssemblyCtxt<'_, 'tcx> { return; } - self.infcx.probe(|_| { - let impl_substs = self.infcx.fresh_substs_for_item(DUMMY_SP, impl_def_id); + acx.infcx.probe(|_| { + let impl_substs = acx.infcx.fresh_substs_for_item(DUMMY_SP, impl_def_id); let impl_trait_ref = impl_trait_ref.subst(tcx, impl_substs); - let Ok(InferOk { obligations, .. }) = self + let Ok(InferOk { obligations, .. }) = acx .infcx .at(&ObligationCause::dummy(), goal.param_env) .define_opaque_types(false) @@ -213,9 +133,10 @@ impl<'tcx> AssemblyCtxt<'_, 'tcx> { }; let nested_goals = obligations.into_iter().map(|o| o.into()).collect(); - let Ok(trait_ref_certainty) = self.cx.evaluate_all(self.infcx, nested_goals) else { return }; + let Ok(trait_ref_certainty) = acx.cx.evaluate_all(acx.infcx, nested_goals) else { return }; - let Some(assoc_def) = self.fetch_eligible_assoc_item_def( + let Some(assoc_def) = fetch_eligible_assoc_item_def( + acx.infcx, goal.param_env, goal_trait_ref, goal.predicate.def_id(), @@ -247,7 +168,7 @@ impl<'tcx> AssemblyCtxt<'_, 'tcx> { impl_trait_ref.substs, ); let substs = translate_substs( - self.infcx, + acx.infcx, goal.param_env, impl_def_id, impl_substs_with_gat, @@ -267,7 +188,7 @@ impl<'tcx> AssemblyCtxt<'_, 'tcx> { ty.map_bound(|ty| ty.into()) }; - let Ok(InferOk { obligations, .. }) = self + let Ok(InferOk { obligations, .. }) = acx .infcx .at(&ObligationCause::dummy(), goal.param_env) .define_opaque_types(false) @@ -278,47 +199,46 @@ impl<'tcx> AssemblyCtxt<'_, 'tcx> { }; let nested_goals = obligations.into_iter().map(|o| o.into()).collect(); - let Ok(rhs_certainty) = self.cx.evaluate_all(self.infcx, nested_goals) else { return }; + let Ok(rhs_certainty) = acx.cx.evaluate_all(acx.infcx, nested_goals) else { return }; let certainty = trait_ref_certainty.unify_and(rhs_certainty); - self.try_insert_candidate(CandidateSource::Impl(impl_def_id), certainty); + acx.try_insert_candidate(CandidateSource::Impl(impl_def_id), certainty); }) } +} - /// This behavior is also implemented in `rustc_ty_utils` and in the old `project` code. - /// - /// FIXME: We should merge these 3 implementations as it's likely that they otherwise - /// diverge. - #[instrument(level = "debug", skip(self, param_env), ret)] - fn fetch_eligible_assoc_item_def( - &self, - param_env: ty::ParamEnv<'tcx>, - goal_trait_ref: ty::TraitRef<'tcx>, - trait_assoc_def_id: DefId, - impl_def_id: DefId, - ) -> Option { - let node_item = - specialization_graph::assoc_def(self.cx.tcx, impl_def_id, trait_assoc_def_id) - .map_err(|ErrorGuaranteed { .. }| ()) - .ok()?; - - let eligible = if node_item.is_final() { - // Non-specializable items are always projectable. - true +/// This behavior is also implemented in `rustc_ty_utils` and in the old `project` code. +/// +/// FIXME: We should merge these 3 implementations as it's likely that they otherwise +/// diverge. +#[instrument(level = "debug", skip(infcx, param_env), ret)] +fn fetch_eligible_assoc_item_def<'tcx>( + infcx: &InferCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + goal_trait_ref: ty::TraitRef<'tcx>, + trait_assoc_def_id: DefId, + impl_def_id: DefId, +) -> Option { + let node_item = specialization_graph::assoc_def(infcx.tcx, impl_def_id, trait_assoc_def_id) + .map_err(|ErrorGuaranteed { .. }| ()) + .ok()?; + + let eligible = if node_item.is_final() { + // Non-specializable items are always projectable. + true + } else { + // Only reveal a specializable default if we're past type-checking + // and the obligation is monomorphic, otherwise passes such as + // transmute checking and polymorphic MIR optimizations could + // get a result which isn't correct for all monomorphizations. + if param_env.reveal() == Reveal::All { + let poly_trait_ref = infcx.resolve_vars_if_possible(goal_trait_ref); + !poly_trait_ref.still_further_specializable() } else { - // Only reveal a specializable default if we're past type-checking - // and the obligation is monomorphic, otherwise passes such as - // transmute checking and polymorphic MIR optimizations could - // get a result which isn't correct for all monomorphizations. - if param_env.reveal() == Reveal::All { - let poly_trait_ref = self.infcx.resolve_vars_if_possible(goal_trait_ref); - !poly_trait_ref.still_further_specializable() - } else { - debug!(?node_item.item.def_id, "not eligible due to default"); - false - } - }; + debug!(?node_item.item.def_id, "not eligible due to default"); + false + } + }; - if eligible { Some(node_item) } else { None } - } + if eligible { Some(node_item) } else { None } } diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index 4ea9081bf46a6..10b45a77dabb0 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -2,35 +2,17 @@ use std::iter; -use super::infcx_ext::InferCtxtExt; -use super::{ - fixme_instantiate_canonical_query_response, CanonicalGoal, CanonicalResponse, Certainty, - EvalCtxt, Goal, QueryResult, -}; +use super::assembly::{self, AssemblyCtxt}; +use super::{CanonicalGoal, EvalCtxt, Goal, QueryResult}; use rustc_hir::def_id::DefId; -use rustc_infer::infer::canonical::{CanonicalVarValues, OriginalQueryValues}; -use rustc_infer::infer::TyCtxtInferExt; -use rustc_infer::infer::{InferCtxt, InferOk}; +use rustc_infer::infer::InferOk; use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::ObligationCause; -use rustc_middle::ty; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams}; use rustc_middle::ty::TraitPredicate; +use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::DUMMY_SP; -/// A candidate is a possible way to prove a goal. -/// -/// It consists of both the `source`, which describes how that goal -/// would be proven, and the `result` when using the given `source`. -/// -/// For the list of possible candidates, please look at the documentation -/// of [CandidateSource]. -#[derive(Debug, Clone)] -pub(super) struct Candidate<'tcx> { - source: CandidateSource, - result: CanonicalResponse<'tcx>, -} - #[allow(dead_code)] // FIXME: implement and use all variants. #[derive(Debug, Clone, Copy)] pub(super) enum CandidateSource { @@ -67,11 +49,56 @@ pub(super) enum CandidateSource { AutoImpl, } -struct AssemblyCtxt<'a, 'tcx> { - cx: &'a mut EvalCtxt<'tcx>, - infcx: &'a InferCtxt<'tcx>, - var_values: CanonicalVarValues<'tcx>, - candidates: Vec>, +type Candidate<'tcx> = assembly::Candidate<'tcx, TraitPredicate<'tcx>>; + +impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { + type CandidateSource = CandidateSource; + + fn self_ty(self) -> Ty<'tcx> { + self.self_ty() + } + + fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self { + self.with_self_ty(tcx, self_ty) + } + + fn trait_def_id(self, _: TyCtxt<'tcx>) -> DefId { + self.def_id() + } + + fn consider_impl_candidate( + acx: &mut AssemblyCtxt<'_, 'tcx, Self>, + goal: Goal<'tcx, TraitPredicate<'tcx>>, + impl_def_id: DefId, + ) { + let impl_trait_ref = acx.cx.tcx.bound_impl_trait_ref(impl_def_id).unwrap(); + let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::AsPlaceholder }; + if iter::zip(goal.predicate.trait_ref.substs, impl_trait_ref.skip_binder().substs) + .any(|(goal, imp)| !drcx.generic_args_may_unify(goal, imp)) + { + return; + } + + acx.infcx.probe(|_| { + let impl_substs = acx.infcx.fresh_substs_for_item(DUMMY_SP, impl_def_id); + let impl_trait_ref = impl_trait_ref.subst(acx.cx.tcx, impl_substs); + + let Ok(InferOk { obligations, .. }) = acx + .infcx + .at(&ObligationCause::dummy(), goal.param_env) + .define_opaque_types(false) + .eq(goal.predicate.trait_ref, impl_trait_ref) + .map_err(|e| debug!("failed to equate trait refs: {e:?}")) + else { + return + }; + + let nested_goals = obligations.into_iter().map(|o| o.into()).collect(); + + let Ok(certainty) = acx.cx.evaluate_all(acx.infcx, nested_goals) else { return }; + acx.try_insert_candidate(CandidateSource::Impl(impl_def_id), certainty); + }) + } } impl<'tcx> EvalCtxt<'tcx> { @@ -79,25 +106,10 @@ impl<'tcx> EvalCtxt<'tcx> { &mut self, goal: CanonicalGoal<'tcx, TraitPredicate<'tcx>>, ) -> QueryResult<'tcx> { - let candidates = self.assemble_and_evaluate_trait_candidates(goal); + let candidates = AssemblyCtxt::assemble_and_evaluate_candidates(self, goal); self.merge_trait_candidates_discard_reservation_impls(candidates) } - pub(super) fn assemble_and_evaluate_trait_candidates( - &mut self, - goal: CanonicalGoal<'tcx, TraitPredicate<'tcx>>, - ) -> Vec> { - let (ref infcx, goal, var_values) = - self.tcx.infer_ctxt().build_with_canonical(DUMMY_SP, &goal); - let mut acx = AssemblyCtxt { cx: self, infcx, var_values, candidates: Vec::new() }; - - acx.assemble_candidates_after_normalizing_self_ty(goal); - acx.assemble_impl_candidates(goal); - - // FIXME: Remaining candidates - acx.candidates - } - #[instrument(level = "debug", skip(self), ret)] pub(super) fn merge_trait_candidates_discard_reservation_impls( &mut self, @@ -166,117 +178,3 @@ impl<'tcx> EvalCtxt<'tcx> { candidate } } - -impl<'tcx> AssemblyCtxt<'_, 'tcx> { - /// Adds a new candidate using the current state of the inference context. - /// - /// This does require each assembly method to correctly use `probe` to not taint - /// the results of other candidates. - fn try_insert_candidate(&mut self, source: CandidateSource, certainty: Certainty) { - match self.infcx.make_canonical_response(self.var_values.clone(), certainty) { - Ok(result) => self.candidates.push(Candidate { source, result }), - Err(NoSolution) => debug!(?source, ?certainty, "failed leakcheck"), - } - } - - /// If the self type of a trait goal is a projection, computing the relevant candidates is difficult. - /// - /// To deal with this, we first try to normalize the self type and add the candidates for the normalized - /// self type to the list of candidates in case that succeeds. Note that we can't just eagerly return in - /// this case as projections as self types add ` - fn assemble_candidates_after_normalizing_self_ty( - &mut self, - goal: Goal<'tcx, TraitPredicate<'tcx>>, - ) { - let tcx = self.cx.tcx; - // FIXME: We also have to normalize opaque types, not sure where to best fit that in. - let &ty::Alias(ty::Projection, projection_ty) = goal.predicate.self_ty().kind() else { - return - }; - self.infcx.probe(|_| { - let normalized_ty = self.infcx.next_ty_infer(); - let normalizes_to_goal = goal.with( - tcx, - ty::Binder::dummy(ty::ProjectionPredicate { - projection_ty, - term: normalized_ty.into(), - }), - ); - let normalization_certainty = - match self.cx.evaluate_goal(&self.infcx, normalizes_to_goal) { - Ok((_, certainty)) => certainty, - Err(NoSolution) => return, - }; - - // NOTE: Alternatively we could call `evaluate_goal` here and only have a `Normalized` candidate. - // This doesn't work as long as we use `CandidateSource` in both winnowing and to resolve associated items. - let goal = goal.with(tcx, goal.predicate.with_self_type(tcx, normalized_ty)); - let mut orig_values = OriginalQueryValues::default(); - let goal = self.infcx.canonicalize_query(goal, &mut orig_values); - let normalized_candidates = self.cx.assemble_and_evaluate_trait_candidates(goal); - - // Map each candidate from being canonical wrt the current inference context to being - // canonical wrt the caller. - for Candidate { source, result } in normalized_candidates { - self.infcx.probe(|_| { - let candidate_certainty = fixme_instantiate_canonical_query_response( - self.infcx, - &orig_values, - result, - ); - - // FIXME: This is a bit scary if the `normalizes_to_goal` overflows. - // - // If we have an ambiguous candidate it hides that normalization - // caused an overflow which may cause issues. - self.try_insert_candidate( - source, - normalization_certainty.unify_and(candidate_certainty), - ) - }) - } - }) - } - - fn assemble_impl_candidates(&mut self, goal: Goal<'tcx, TraitPredicate<'tcx>>) { - self.cx.tcx.for_each_relevant_impl( - goal.predicate.def_id(), - goal.predicate.self_ty(), - |impl_def_id| self.consider_impl_candidate(goal, impl_def_id), - ); - } - - fn consider_impl_candidate( - &mut self, - goal: Goal<'tcx, TraitPredicate<'tcx>>, - impl_def_id: DefId, - ) { - let impl_trait_ref = self.cx.tcx.bound_impl_trait_ref(impl_def_id).unwrap(); - let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::AsPlaceholder }; - if iter::zip(goal.predicate.trait_ref.substs, impl_trait_ref.skip_binder().substs) - .any(|(goal, imp)| !drcx.generic_args_may_unify(goal, imp)) - { - return; - } - - self.infcx.probe(|_| { - let impl_substs = self.infcx.fresh_substs_for_item(DUMMY_SP, impl_def_id); - let impl_trait_ref = impl_trait_ref.subst(self.cx.tcx, impl_substs); - - let Ok(InferOk { obligations, .. }) = self - .infcx - .at(&ObligationCause::dummy(), goal.param_env) - .define_opaque_types(false) - .eq(goal.predicate.trait_ref, impl_trait_ref) - .map_err(|e| debug!("failed to equate trait refs: {e:?}")) - else { - return - }; - - let nested_goals = obligations.into_iter().map(|o| o.into()).collect(); - - let Ok(certainty) = self.cx.evaluate_all(self.infcx, nested_goals) else { return }; - self.try_insert_candidate(CandidateSource::Impl(impl_def_id), certainty); - }) - } -} From 082ca1e46133f9720ca3f0342a6d43c5bcbd0510 Mon Sep 17 00:00:00 2001 From: Ezra Shaw Date: Fri, 16 Dec 2022 15:59:47 +1300 Subject: [PATCH 09/30] docs: add long error explanation for error E0472 --- compiler/rustc_error_codes/src/error_codes.rs | 2 +- .../src/error_codes/E0472.md | 31 +++++++++++++++++++ src/test/ui/asm/bad-arch.mirunsafeck.stderr | 1 + src/test/ui/asm/bad-arch.thirunsafeck.stderr | 1 + 4 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 compiler/rustc_error_codes/src/error_codes/E0472.md diff --git a/compiler/rustc_error_codes/src/error_codes.rs b/compiler/rustc_error_codes/src/error_codes.rs index 883a4bbe8e823..2835e62fd8341 100644 --- a/compiler/rustc_error_codes/src/error_codes.rs +++ b/compiler/rustc_error_codes/src/error_codes.rs @@ -248,6 +248,7 @@ E0464: include_str!("./error_codes/E0464.md"), E0466: include_str!("./error_codes/E0466.md"), E0468: include_str!("./error_codes/E0468.md"), E0469: include_str!("./error_codes/E0469.md"), +E0472: include_str!("./error_codes/E0472.md"), E0477: include_str!("./error_codes/E0477.md"), E0478: include_str!("./error_codes/E0478.md"), E0482: include_str!("./error_codes/E0482.md"), @@ -600,7 +601,6 @@ E0791: include_str!("./error_codes/E0791.md"), // E0467, // removed // E0470, // removed // E0471, // constant evaluation error (in pattern) - E0472, // llvm_asm! is unsupported on this target // E0473, // dereference of reference outside its lifetime // E0474, // captured variable `..` does not outlive the enclosing closure // E0475, // index of slice outside its lifetime diff --git a/compiler/rustc_error_codes/src/error_codes/E0472.md b/compiler/rustc_error_codes/src/error_codes/E0472.md new file mode 100644 index 0000000000000..0005cd41911a9 --- /dev/null +++ b/compiler/rustc_error_codes/src/error_codes/E0472.md @@ -0,0 +1,31 @@ +Inline assembly (`asm!`) is not supported on this target. + +Example of erroneous code: + +```ignore (cannot-change-target) +// compile-flags: --target sparc64-unknown-linux-gnu +#![no_std] + +use core::arch::asm; + +fn main() { + unsafe { + asm!(""); // error: inline assembly is not supported on this target + } +} +``` + +The Rust compiler does not support inline assembly, with the `asm!` macro +(previously `llvm_asm!`), for all targets. All Tier 1 targets do support this +macro but support among Tier 2 and 3 targets is not guaranteed (even when they +have `std` support). Note that this error is related to +`error[E0658]: inline assembly is not stable yet on this architecture`, but +distinct in that with `E0472` support is not planned or in progress. + +There is no way to easily fix this issue, however: + * Consider if you really need inline assembly, is there some other way to + achieve your goal (intrinsics, etc)? + * Consider writing your assembly externally, linking with it and calling it + from Rust. + * Consider contributing to and help + integrate support for your target! diff --git a/src/test/ui/asm/bad-arch.mirunsafeck.stderr b/src/test/ui/asm/bad-arch.mirunsafeck.stderr index 4aa27180758e2..d7af296152f79 100644 --- a/src/test/ui/asm/bad-arch.mirunsafeck.stderr +++ b/src/test/ui/asm/bad-arch.mirunsafeck.stderr @@ -14,3 +14,4 @@ LL | global_asm!(""); error: aborting due to 2 previous errors +For more information about this error, try `rustc --explain E0472`. diff --git a/src/test/ui/asm/bad-arch.thirunsafeck.stderr b/src/test/ui/asm/bad-arch.thirunsafeck.stderr index 4aa27180758e2..d7af296152f79 100644 --- a/src/test/ui/asm/bad-arch.thirunsafeck.stderr +++ b/src/test/ui/asm/bad-arch.thirunsafeck.stderr @@ -14,3 +14,4 @@ LL | global_asm!(""); error: aborting due to 2 previous errors +For more information about this error, try `rustc --explain E0472`. From b859b8b62bb080c315abd3d6ea10a15eddce0ce4 Mon Sep 17 00:00:00 2001 From: Chris Denton Date: Tue, 20 Dec 2022 13:03:45 +0000 Subject: [PATCH 10/30] Bump `cfg-if` to `1.0` --- Cargo.lock | 4 ++-- compiler/rustc_data_structures/Cargo.toml | 8 ++++++-- compiler/rustc_span/Cargo.toml | 2 +- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1d9a9240ccf2c..3d09068dbdf33 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3590,7 +3590,7 @@ version = "0.0.0" dependencies = [ "arrayvec", "bitflags", - "cfg-if 0.1.10", + "cfg-if 1.0.0", "ena", "indexmap", "jobserver", @@ -4374,7 +4374,7 @@ dependencies = [ name = "rustc_span" version = "0.0.0" dependencies = [ - "cfg-if 0.1.10", + "cfg-if 1.0.0", "md-5", "rustc_arena", "rustc_data_structures", diff --git a/compiler/rustc_data_structures/Cargo.toml b/compiler/rustc_data_structures/Cargo.toml index 5afce15e26bfc..0366fb0a148c7 100644 --- a/compiler/rustc_data_structures/Cargo.toml +++ b/compiler/rustc_data_structures/Cargo.toml @@ -8,7 +8,7 @@ edition = "2021" [dependencies] arrayvec = { version = "0.7", default-features = false } bitflags = "1.2.1" -cfg-if = "0.1.2" +cfg-if = "1.0" ena = "0.14" indexmap = { version = "1.9.1" } jobserver_crate = { version = "0.1.13", package = "jobserver" } @@ -21,7 +21,11 @@ rustc-hash = "1.1.0" rustc_index = { path = "../rustc_index", package = "rustc_index" } rustc_macros = { path = "../rustc_macros" } rustc_serialize = { path = "../rustc_serialize" } -smallvec = { version = "1.8.1", features = ["const_generics", "union", "may_dangle"] } +smallvec = { version = "1.8.1", features = [ + "const_generics", + "union", + "may_dangle", +] } stable_deref_trait = "1.0.0" stacker = "0.1.15" tempfile = "3.2" diff --git a/compiler/rustc_span/Cargo.toml b/compiler/rustc_span/Cargo.toml index 48a2ab0f904ac..5ce2577b63c1d 100644 --- a/compiler/rustc_span/Cargo.toml +++ b/compiler/rustc_span/Cargo.toml @@ -13,7 +13,7 @@ rustc_index = { path = "../rustc_index" } rustc_arena = { path = "../rustc_arena" } scoped-tls = "1.0" unicode-width = "0.1.4" -cfg-if = "0.1.2" +cfg-if = "1.0" tracing = "0.1" sha1 = { package = "sha-1", version = "0.10.0" } sha2 = "0.10.1" From 8b5a96ec45886bc42090fe313150e648e488eee1 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 19 Dec 2022 10:45:35 +0000 Subject: [PATCH 11/30] Some tracing cleanups --- compiler/rustc_borrowck/src/type_check/mod.rs | 6 ++---- compiler/rustc_middle/src/mir/tcx.rs | 7 +++---- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 8d4a720f8ce9b..36397227d88d0 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -1153,6 +1153,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { category: ConstraintCategory<'tcx>, ) -> Fallible<()> { let annotated_type = self.user_type_annotations[user_ty.base].inferred_ty; + trace!(?annotated_type); let mut curr_projected_ty = PlaceTy::from_ty(annotated_type); let tcx = self.infcx.tcx; @@ -1170,10 +1171,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { ); curr_projected_ty = projected_ty; } - debug!( - "user_ty base: {:?} freshened: {:?} projs: {:?} yields: {:?}", - user_ty.base, annotated_type, user_ty.projs, curr_projected_ty - ); + trace!(?curr_projected_ty); let ty = curr_projected_ty.ty; self.relate_types(ty, v.xform(ty::Variance::Contravariant), a, locations, category)?; diff --git a/compiler/rustc_middle/src/mir/tcx.rs b/compiler/rustc_middle/src/mir/tcx.rs index 8d2a8f33d6aa9..599f0b9d3fab4 100644 --- a/compiler/rustc_middle/src/mir/tcx.rs +++ b/compiler/rustc_middle/src/mir/tcx.rs @@ -32,8 +32,9 @@ impl<'tcx> PlaceTy<'tcx> { /// not carry a `Ty` for `T`.) /// /// Note that the resulting type has not been normalized. + #[instrument(level = "debug", skip(tcx), ret)] pub fn field_ty(self, tcx: TyCtxt<'tcx>, f: Field) -> Ty<'tcx> { - let answer = match self.ty.kind() { + match self.ty.kind() { ty::Adt(adt_def, substs) => { let variant_def = match self.variant_index { None => adt_def.non_enum_variant(), @@ -47,9 +48,7 @@ impl<'tcx> PlaceTy<'tcx> { } ty::Tuple(tys) => tys[f.index()], _ => bug!("extracting field of non-tuple non-adt: {:?}", self), - }; - debug!("field_ty self: {:?} f: {:?} yields: {:?}", self, f, answer); - answer + } } /// Convenience wrapper around `projection_ty_core` for From 562d846ff03af63ede8e12c0b02046fa54889ee3 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 19 Dec 2022 12:10:26 +0000 Subject: [PATCH 12/30] Make it easier to debug where a region error was created --- .../src/diagnostics/region_errors.rs | 20 +++++++++++++++++-- .../rustc_borrowck/src/region_infer/mod.rs | 2 +- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index b5a0044e9e8cd..500ce9038bbb9 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -22,7 +22,7 @@ use rustc_middle::ty::Region; use rustc_middle::ty::TypeVisitor; use rustc_middle::ty::{self, RegionVid, Ty}; use rustc_span::symbol::{kw, Ident}; -use rustc_span::Span; +use rustc_span::{Span, DUMMY_SP}; use crate::borrowck_errors; use crate::session_diagnostics::{ @@ -70,7 +70,23 @@ impl<'tcx> ConstraintDescription for ConstraintCategory<'tcx> { /// /// Usually we expect this to either be empty or contain a small number of items, so we can avoid /// allocation most of the time. -pub(crate) type RegionErrors<'tcx> = Vec>; +#[derive(Default)] +pub(crate) struct RegionErrors<'tcx>(Vec>); + +impl<'tcx> RegionErrors<'tcx> { + #[track_caller] + pub fn push(&mut self, val: impl Into>) { + let val = val.into(); + ty::tls::with(|tcx| tcx.sess.delay_span_bug(DUMMY_SP, "{val:?}")); + self.0.push(val); + } + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + pub fn into_iter(self) -> impl Iterator> { + self.0.into_iter() + } +} #[derive(Clone, Debug)] pub(crate) enum RegionErrorKind<'tcx> { diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index 0d03346ef0a5e..7ccc3cc289637 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -562,7 +562,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { let mir_def_id = body.source.def_id(); self.propagate_constraints(body); - let mut errors_buffer = RegionErrors::new(); + let mut errors_buffer = RegionErrors::default(); // If this is a closure, we can propagate unsatisfied // `outlives_requirements` to our creator, so create a vector From 8b1530260c66069ce1f5cd8d5d32a98b9e1b49cb Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 19 Dec 2022 12:49:10 +0000 Subject: [PATCH 13/30] Replace a `find` with a loop to simplify the logic. --- .../rustc_borrowck/src/region_infer/mod.rs | 41 ++++++++++--------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index 7ccc3cc289637..931732af90414 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -1647,26 +1647,29 @@ impl<'tcx> RegionInferenceContext<'tcx> { let longer_fr_scc = self.constraint_sccs.scc(longer_fr); debug!("check_bound_universal_region: longer_fr_scc={:?}", longer_fr_scc,); - // If we have some bound universal region `'a`, then the only - // elements it can contain is itself -- we don't know anything - // else about it! - let Some(error_element) = ({ - self.scc_values.elements_contained_in(longer_fr_scc).find(|element| match element { - RegionElement::Location(_) => true, - RegionElement::RootUniversalRegion(_) => true, - RegionElement::PlaceholderRegion(placeholder1) => placeholder != *placeholder1, - }) - }) else { - return; - }; - debug!("check_bound_universal_region: error_element = {:?}", error_element); + for error_element in self.scc_values.elements_contained_in(longer_fr_scc) { + match error_element { + RegionElement::Location(_) | RegionElement::RootUniversalRegion(_) => {} + // If we have some bound universal region `'a`, then the only + // elements it can contain is itself -- we don't know anything + // else about it! + RegionElement::PlaceholderRegion(placeholder1) => { + if placeholder == placeholder1 { + continue; + } + } + } - // Find the region that introduced this `error_element`. - errors_buffer.push(RegionErrorKind::BoundUniversalRegionError { - longer_fr, - error_element, - placeholder, - }); + errors_buffer.push(RegionErrorKind::BoundUniversalRegionError { + longer_fr, + error_element, + placeholder, + }); + + // Stop after the first error, it gets too noisy otherwise, and does not provide more information. + break; + } + debug!("check_bound_universal_region: all bounds satisfied"); } #[instrument(level = "debug", skip(self, infcx, errors_buffer))] From e405dabf7dc99cbcd5f483592e8f9b5ea621d110 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 19 Dec 2022 13:14:03 +0000 Subject: [PATCH 14/30] Some style nits --- compiler/rustc_borrowck/src/type_check/mod.rs | 2 +- compiler/rustc_mir_build/src/build/expr/as_constant.rs | 4 ++-- compiler/rustc_mir_build/src/build/matches/mod.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 36397227d88d0..2973f8b7f85f9 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -1163,7 +1163,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { tcx, self.param_env, proj, - |this, field, _| { + |this, field, ()| { let ty = this.field_ty(tcx, field); self.normalize(ty, locations) }, diff --git a/compiler/rustc_mir_build/src/build/expr/as_constant.rs b/compiler/rustc_mir_build/src/build/expr/as_constant.rs index 3b7ed818dc9b7..1d96893c7a3ea 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_constant.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_constant.rs @@ -66,14 +66,14 @@ pub fn as_constant_inner<'tcx>( let literal = ConstantKind::Val(ConstValue::Scalar(Scalar::Int(lit)), ty); - Constant { span, user_ty: user_ty, literal } + Constant { span, user_ty, literal } } ExprKind::ZstLiteral { ref user_ty } => { let user_ty = user_ty.as_ref().map(push_cuta).flatten(); let literal = ConstantKind::Val(ConstValue::ZeroSized, ty); - Constant { span, user_ty: user_ty, literal } + Constant { span, user_ty, literal } } ExprKind::NamedConst { def_id, substs, ref user_ty } => { let user_ty = user_ty.as_ref().map(push_cuta).flatten(); diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index 7edcd46a34f29..f90aba80bf3cf 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -2210,7 +2210,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { BindingMode::ByValue => ty::BindingMode::BindByValue(mutability), BindingMode::ByRef(_) => ty::BindingMode::BindByReference(mutability), }; - let local = LocalDecl::<'tcx> { + let local = LocalDecl { mutability, ty: var_ty, user_ty: if user_ty.is_empty() { None } else { Some(Box::new(user_ty)) }, From c9588d5bf8db14d80249b029b07f79026956ae85 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 19 Dec 2022 13:14:22 +0000 Subject: [PATCH 15/30] Hackily fix an opaque type ICE --- .../rustc_borrowck/src/diagnostics/region_errors.rs | 10 ++++++---- compiler/rustc_borrowck/src/region_infer/mod.rs | 2 +- compiler/rustc_borrowck/src/type_check/mod.rs | 6 ++++++ src/test/ui/type-alias-impl-trait/destructuring.rs | 10 ++++++++++ 4 files changed, 23 insertions(+), 5 deletions(-) create mode 100644 src/test/ui/type-alias-impl-trait/destructuring.rs diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index 500ce9038bbb9..bcc8afbfd952d 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -18,9 +18,9 @@ use rustc_infer::infer::{ use rustc_middle::hir::place::PlaceBase; use rustc_middle::mir::{ConstraintCategory, ReturnConstraint}; use rustc_middle::ty::subst::InternalSubsts; -use rustc_middle::ty::Region; use rustc_middle::ty::TypeVisitor; use rustc_middle::ty::{self, RegionVid, Ty}; +use rustc_middle::ty::{Region, TyCtxt}; use rustc_span::symbol::{kw, Ident}; use rustc_span::{Span, DUMMY_SP}; @@ -70,14 +70,16 @@ impl<'tcx> ConstraintDescription for ConstraintCategory<'tcx> { /// /// Usually we expect this to either be empty or contain a small number of items, so we can avoid /// allocation most of the time. -#[derive(Default)] -pub(crate) struct RegionErrors<'tcx>(Vec>); +pub(crate) struct RegionErrors<'tcx>(Vec>, TyCtxt<'tcx>); impl<'tcx> RegionErrors<'tcx> { + pub fn new(tcx: TyCtxt<'tcx>) -> Self { + Self(vec![], tcx) + } #[track_caller] pub fn push(&mut self, val: impl Into>) { let val = val.into(); - ty::tls::with(|tcx| tcx.sess.delay_span_bug(DUMMY_SP, "{val:?}")); + self.1.sess.delay_span_bug(DUMMY_SP, "{val:?}"); self.0.push(val); } pub fn is_empty(&self) -> bool { diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index 931732af90414..308f6e19a73e8 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -562,7 +562,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { let mir_def_id = body.source.def_id(); self.propagate_constraints(body); - let mut errors_buffer = RegionErrors::default(); + let mut errors_buffer = RegionErrors::new(infcx.tcx); // If this is a closure, we can propagate unsatisfied // `outlives_requirements` to our creator, so create a vector diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 2973f8b7f85f9..247607ff29e20 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -1159,6 +1159,12 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { let tcx = self.infcx.tcx; for proj in &user_ty.projs { + if let ty::Alias(ty::Opaque, ..) = curr_projected_ty.ty.kind() { + // There is nothing that we can compare here if we go through an opaque type. + // We're always in its defining scope as we can otherwise not project through + // it, so we're constraining it anyways. + return Ok(()); + } let projected_ty = curr_projected_ty.projection_ty_core( tcx, self.param_env, diff --git a/src/test/ui/type-alias-impl-trait/destructuring.rs b/src/test/ui/type-alias-impl-trait/destructuring.rs new file mode 100644 index 0000000000000..b752e58380a89 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/destructuring.rs @@ -0,0 +1,10 @@ +#![feature(type_alias_impl_trait)] + +// check-pass + +// issue: https://github.com/rust-lang/rust/issues/104551 + +fn main() { + type T = impl Sized; + let (_a, _b): T = (1u32, 2u32); +} From f1ef038ae401d8ee7611b2f9fb2aec3af4e9241f Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 30 Nov 2022 11:09:22 +0000 Subject: [PATCH 16/30] use `track_caller` to show where the panic is actually from --- compiler/rustc_middle/src/ty/subst.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/compiler/rustc_middle/src/ty/subst.rs b/compiler/rustc_middle/src/ty/subst.rs index a04b15f8cf13c..2071d01027266 100644 --- a/compiler/rustc_middle/src/ty/subst.rs +++ b/compiler/rustc_middle/src/ty/subst.rs @@ -400,6 +400,7 @@ impl<'tcx> InternalSubsts<'tcx> { } #[inline] + #[track_caller] pub fn type_at(&self, i: usize) -> Ty<'tcx> { if let GenericArgKind::Type(ty) = self[i].unpack() { ty @@ -409,6 +410,7 @@ impl<'tcx> InternalSubsts<'tcx> { } #[inline] + #[track_caller] pub fn region_at(&self, i: usize) -> ty::Region<'tcx> { if let GenericArgKind::Lifetime(lt) = self[i].unpack() { lt @@ -418,6 +420,7 @@ impl<'tcx> InternalSubsts<'tcx> { } #[inline] + #[track_caller] pub fn const_at(&self, i: usize) -> ty::Const<'tcx> { if let GenericArgKind::Const(ct) = self[i].unpack() { ct @@ -427,6 +430,7 @@ impl<'tcx> InternalSubsts<'tcx> { } #[inline] + #[track_caller] pub fn type_for_def(&self, def: &ty::GenericParamDef) -> GenericArg<'tcx> { self.type_at(def.index as usize).into() } From 872a6da935afa11c9b836d685efd591ca7a64481 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Thu, 6 Oct 2022 11:44:42 +0000 Subject: [PATCH 17/30] Remove an unused function --- compiler/rustc_middle/src/ty/generics.rs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/compiler/rustc_middle/src/ty/generics.rs b/compiler/rustc_middle/src/ty/generics.rs index 2e70ac256a719..705adecd3b90f 100644 --- a/compiler/rustc_middle/src/ty/generics.rs +++ b/compiler/rustc_middle/src/ty/generics.rs @@ -70,14 +70,6 @@ impl GenericParamDef { } } - pub fn has_default(&self) -> bool { - match self.kind { - GenericParamDefKind::Type { has_default, .. } - | GenericParamDefKind::Const { has_default } => has_default, - GenericParamDefKind::Lifetime => false, - } - } - pub fn is_anonymous_lifetime(&self) -> bool { match self.kind { GenericParamDefKind::Lifetime => { From c787de3bbd307cccbfaeb56421ad7ad898330676 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 11 Oct 2022 19:11:56 +0000 Subject: [PATCH 18/30] Fix some `~const` usage in libcore --- library/core/src/const_closure.rs | 4 ++-- library/core/src/hash/mod.rs | 4 ++-- library/core/src/ops/index.rs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/library/core/src/const_closure.rs b/library/core/src/const_closure.rs index 151c8e6d8986a..920c31233c187 100644 --- a/library/core/src/const_closure.rs +++ b/library/core/src/const_closure.rs @@ -51,7 +51,7 @@ macro_rules! impl_fn_mut_tuple { impl<'a, $($var,)* ClosureArguments, Function, ClosureReturnValue> const FnOnce for ConstFnMutClosure<($(&'a mut $var),*), Function> where - Function: ~const Fn(($(&mut $var),*), ClosureArguments) -> ClosureReturnValue+ ~const Destruct, + Function: ~const Fn(($(&mut $var),*), ClosureArguments) -> ClosureReturnValue + ~const Destruct, { type Output = ClosureReturnValue; @@ -64,7 +64,7 @@ macro_rules! impl_fn_mut_tuple { impl<'a, $($var,)* ClosureArguments, Function, ClosureReturnValue> const FnMut for ConstFnMutClosure<($(&'a mut $var),*), Function> where - Function: ~const Fn(($(&mut $var),*), ClosureArguments)-> ClosureReturnValue, + Function: ~const Fn(($(&mut $var),*), ClosureArguments)-> ClosureReturnValue + ~const Destruct, { extern "rust-call" fn call_mut(&mut self, args: ClosureArguments) -> Self::Output { #[allow(non_snake_case)] diff --git a/library/core/src/hash/mod.rs b/library/core/src/hash/mod.rs index c755afa39eb60..71a0d1825efec 100644 --- a/library/core/src/hash/mod.rs +++ b/library/core/src/hash/mod.rs @@ -199,7 +199,7 @@ pub trait Hash { /// println!("Hash is {:x}!", hasher.finish()); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - fn hash(&self, state: &mut H); + fn hash(&self, state: &mut H); /// Feeds a slice of this type into the given [`Hasher`]. /// @@ -980,7 +980,7 @@ mod impls { #[rustc_const_unstable(feature = "const_hash", issue = "104061")] impl const Hash for &mut T { #[inline] - fn hash(&self, state: &mut H) { + fn hash(&self, state: &mut H) { (**self).hash(state); } } diff --git a/library/core/src/ops/index.rs b/library/core/src/ops/index.rs index 5e3dc48b6ca1c..228efb0bc0a5c 100644 --- a/library/core/src/ops/index.rs +++ b/library/core/src/ops/index.rs @@ -165,7 +165,7 @@ see chapter in The Book : Index { +pub trait IndexMut: ~const Index { /// Performs the mutable indexing (`container[index]`) operation. /// /// # Panics From b0ed631ad47a15df6e23f4a34fd2f75162341889 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Thu, 10 Nov 2022 08:47:16 +0000 Subject: [PATCH 19/30] Some hir cleanups --- compiler/rustc_middle/src/hir/map/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index 0450abed51b06..cf5ebad40328a 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -408,7 +408,7 @@ impl<'hir> Map<'hir> { /// item (possibly associated), a closure, or a `hir::AnonConst`. pub fn body_owner(self, BodyId { hir_id }: BodyId) -> HirId { let parent = self.get_parent_node(hir_id); - assert!(self.find(parent).map_or(false, |n| is_body_owner(n, hir_id))); + assert!(self.find(parent).map_or(false, |n| is_body_owner(n, hir_id)), "{hir_id:?}"); parent } @@ -419,7 +419,7 @@ impl<'hir> Map<'hir> { /// Given a `LocalDefId`, returns the `BodyId` associated with it, /// if the node is a body owner, otherwise returns `None`. pub fn maybe_body_owned_by(self, id: LocalDefId) -> Option { - self.get_if_local(id.to_def_id()).map(associated_body).flatten() + self.find_by_def_id(id).and_then(associated_body) } /// Given a body owner's id, returns the `BodyId` associated with it. From b2b859be8ccb2951ffbba7de7461111d9a62883a Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 12 Oct 2022 14:16:31 +0000 Subject: [PATCH 20/30] Some track_caller additions --- compiler/rustc_middle/src/hir/map/mod.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index cf5ebad40328a..f5cb89fa62463 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -170,6 +170,7 @@ impl<'hir> Map<'hir> { } #[inline] + #[track_caller] pub fn local_def_id(self, hir_id: HirId) -> LocalDefId { self.opt_local_def_id(hir_id).unwrap_or_else(|| { bug!( @@ -310,6 +311,7 @@ impl<'hir> Map<'hir> { } } + #[track_caller] pub fn get_parent_node(self, hir_id: HirId) -> HirId { self.find_parent_node(hir_id) .unwrap_or_else(|| bug!("No parent for node {:?}", self.node_to_string(hir_id))) @@ -334,12 +336,14 @@ impl<'hir> Map<'hir> { } /// Retrieves the `Node` corresponding to `id`, panicking if it cannot be found. + #[track_caller] pub fn get(self, id: HirId) -> Node<'hir> { self.find(id).unwrap_or_else(|| bug!("couldn't find hir id {} in the HIR map", id)) } /// Retrieves the `Node` corresponding to `id`, panicking if it cannot be found. #[inline] + #[track_caller] pub fn get_by_def_id(self, id: LocalDefId) -> Node<'hir> { self.find_by_def_id(id).unwrap_or_else(|| bug!("couldn't find {:?} in the HIR map", id)) } @@ -377,6 +381,7 @@ impl<'hir> Map<'hir> { self.tcx.hir_owner_nodes(id.hir_id.owner).unwrap().bodies[&id.hir_id.local_id] } + #[track_caller] pub fn fn_decl_by_hir_id(self, hir_id: HirId) -> Option<&'hir FnDecl<'hir>> { if let Some(node) = self.find(hir_id) { node.fn_decl() @@ -385,6 +390,7 @@ impl<'hir> Map<'hir> { } } + #[track_caller] pub fn fn_sig_by_hir_id(self, hir_id: HirId) -> Option<&'hir FnSig<'hir>> { if let Some(node) = self.find(hir_id) { node.fn_sig() @@ -393,6 +399,7 @@ impl<'hir> Map<'hir> { } } + #[track_caller] pub fn enclosing_body_owner(self, hir_id: HirId) -> LocalDefId { for (_, node) in self.parent_iter(hir_id) { if let Some(body) = associated_body(node) { @@ -423,6 +430,7 @@ impl<'hir> Map<'hir> { } /// Given a body owner's id, returns the `BodyId` associated with it. + #[track_caller] pub fn body_owned_by(self, id: LocalDefId) -> BodyId { self.maybe_body_owned_by(id).unwrap_or_else(|| { let hir_id = self.local_def_id_to_hir_id(id); From fedcc739c6788dbfbb1bfa07cacceaa9fa924382 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 11 Oct 2022 11:05:41 +0000 Subject: [PATCH 21/30] tracing: make flag checking less noisy --- compiler/rustc_middle/src/ty/visit.rs | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_middle/src/ty/visit.rs b/compiler/rustc_middle/src/ty/visit.rs index b302572f3cabd..d5553f84f7517 100644 --- a/compiler/rustc_middle/src/ty/visit.rs +++ b/compiler/rustc_middle/src/ty/visit.rs @@ -88,9 +88,11 @@ pub trait TypeVisitable<'tcx>: fmt::Debug + Clone { self.has_vars_bound_at_or_above(ty::INNERMOST) } - #[instrument(level = "trace", ret)] fn has_type_flags(&self, flags: TypeFlags) -> bool { - self.visit_with(&mut HasTypeFlagsVisitor { flags }).break_value() == Some(FoundFlags) + let res = + self.visit_with(&mut HasTypeFlagsVisitor { flags }).break_value() == Some(FoundFlags); + trace!(?self, ?flags, ?res, "has_type_flags"); + res } fn has_projections(&self) -> bool { self.has_type_flags(TypeFlags::HAS_PROJECTION) @@ -560,10 +562,8 @@ impl<'tcx> TypeVisitor<'tcx> for HasTypeFlagsVisitor { type BreakTy = FoundFlags; #[inline] - #[instrument(skip(self), level = "trace", ret)] fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow { let flags = t.flags(); - trace!(t.flags=?t.flags()); if flags.intersects(self.flags) { ControlFlow::Break(FoundFlags) } else { @@ -572,10 +572,8 @@ impl<'tcx> TypeVisitor<'tcx> for HasTypeFlagsVisitor { } #[inline] - #[instrument(skip(self), level = "trace", ret)] fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow { let flags = r.type_flags(); - trace!(r.flags=?flags); if flags.intersects(self.flags) { ControlFlow::Break(FoundFlags) } else { @@ -584,7 +582,6 @@ impl<'tcx> TypeVisitor<'tcx> for HasTypeFlagsVisitor { } #[inline] - #[instrument(level = "trace", ret)] fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow { let flags = FlagComputation::for_const(c); trace!(r.flags=?flags); @@ -596,14 +593,7 @@ impl<'tcx> TypeVisitor<'tcx> for HasTypeFlagsVisitor { } #[inline] - #[instrument(level = "trace", ret)] fn visit_predicate(&mut self, predicate: ty::Predicate<'tcx>) -> ControlFlow { - debug!( - "HasTypeFlagsVisitor: predicate={:?} predicate.flags={:?} self.flags={:?}", - predicate, - predicate.flags(), - self.flags - ); if predicate.flags().intersects(self.flags) { ControlFlow::Break(FoundFlags) } else { From ed61be60da3b5dd28abd4ec4956df9eb896183c9 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 21 Sep 2022 08:14:31 +0000 Subject: [PATCH 22/30] Some ICE debugging aids --- compiler/rustc_middle/src/ty/subst.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_middle/src/ty/subst.rs b/compiler/rustc_middle/src/ty/subst.rs index a04b15f8cf13c..eeb2afb034928 100644 --- a/compiler/rustc_middle/src/ty/subst.rs +++ b/compiler/rustc_middle/src/ty/subst.rs @@ -348,7 +348,7 @@ impl<'tcx> InternalSubsts<'tcx> { substs.reserve(defs.params.len()); for param in &defs.params { let kind = mk_kind(param, substs); - assert_eq!(param.index as usize, substs.len()); + assert_eq!(param.index as usize, substs.len(), "{substs:#?}, {defs:#?}"); substs.push(kind); } } From 168e3da8126f78d213e17de7fb548a9bf76ee393 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Tue, 20 Dec 2022 10:32:35 -0700 Subject: [PATCH 23/30] rustdoc: prevent CSS layout of line numbers shrinking into nothing --- src/librustdoc/html/static/css/rustdoc.css | 1 + .../rustdoc-gui/scrape-examples-layout.goml | 35 +++++++++++++++++++ .../scrape_examples/examples/check-many-1.rs | 10 ++++++ .../scrape_examples/examples/check-many-2.rs | 12 ++++++- .../scrape_examples/examples/check-many-3.rs | 12 ++++++- .../scrape_examples/examples/check-many-4.rs | 10 ++++++ .../scrape_examples/examples/check-many-5.rs | 10 ++++++ .../scrape_examples/examples/check-many-6.rs | 10 ++++++ .../scrape_examples/examples/check-many-7.rs | 10 ++++++ 9 files changed, 108 insertions(+), 2 deletions(-) create mode 100644 src/test/rustdoc-gui/scrape-examples-layout.goml diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index 11e34a3fc7d1f..055a5665cf0fd 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -543,6 +543,7 @@ ul.block, .block li { .rustdoc .example-wrap > pre.example-line-numbers, .rustdoc .example-wrap > pre.src-line-numbers { flex-grow: 0; + min-width: fit-content; /* prevent collapsing into nothing in truncated scraped examples */ overflow: initial; text-align: right; -webkit-user-select: none; diff --git a/src/test/rustdoc-gui/scrape-examples-layout.goml b/src/test/rustdoc-gui/scrape-examples-layout.goml new file mode 100644 index 0000000000000..988c911b7839a --- /dev/null +++ b/src/test/rustdoc-gui/scrape-examples-layout.goml @@ -0,0 +1,35 @@ +// Check that the line number column has the correct layout. +goto: "file://" + |DOC_PATH| + "/scrape_examples/fn.test_many.html" + +// Check that it's not zero. +assert-property-false: ( + ".more-scraped-examples .scraped-example .code-wrapper .src-line-numbers", + {"clientWidth": "0"} +) + +// Check that examples with very long lines have the same width as ones that don't. +store-property: ( + clientWidth, + ".more-scraped-examples .scraped-example:nth-child(1) .code-wrapper .src-line-numbers", + "clientWidth" +) + +assert-property: ( + ".more-scraped-examples .scraped-example:nth-child(2) .code-wrapper .src-line-numbers", + {"clientWidth": |clientWidth|} +) + +assert-property: ( + ".more-scraped-examples .scraped-example:nth-child(3) .code-wrapper .src-line-numbers", + {"clientWidth": |clientWidth|} +) + +assert-property: ( + ".more-scraped-examples .scraped-example:nth-child(4) .code-wrapper .src-line-numbers", + {"clientWidth": |clientWidth|} +) + +assert-property: ( + ".more-scraped-examples .scraped-example:nth-child(5) .code-wrapper .src-line-numbers", + {"clientWidth": |clientWidth|} +) diff --git a/src/test/rustdoc-gui/src/scrape_examples/examples/check-many-1.rs b/src/test/rustdoc-gui/src/scrape_examples/examples/check-many-1.rs index 1d1bc5002aa8f..81a48ac50c813 100644 --- a/src/test/rustdoc-gui/src/scrape_examples/examples/check-many-1.rs +++ b/src/test/rustdoc-gui/src/scrape_examples/examples/check-many-1.rs @@ -1,3 +1,13 @@ fn main() { + // all examples have same line count + scrape_examples::test_many(); + scrape_examples::test_many(); + scrape_examples::test_many(); + scrape_examples::test_many(); + scrape_examples::test_many(); + scrape_examples::test_many(); + scrape_examples::test_many(); + scrape_examples::test_many(); + scrape_examples::test_many(); scrape_examples::test_many(); } diff --git a/src/test/rustdoc-gui/src/scrape_examples/examples/check-many-2.rs b/src/test/rustdoc-gui/src/scrape_examples/examples/check-many-2.rs index 1d1bc5002aa8f..c9fdf68d3be0c 100644 --- a/src/test/rustdoc-gui/src/scrape_examples/examples/check-many-2.rs +++ b/src/test/rustdoc-gui/src/scrape_examples/examples/check-many-2.rs @@ -1,3 +1,13 @@ fn main() { - scrape_examples::test_many(); + // ignore-tidy-linelength + scrape_examples::test_many(); /* Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. */ + scrape_examples::test_many(); /* Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. */ + scrape_examples::test_many(); /* Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. */ + scrape_examples::test_many(); /* Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. */ + scrape_examples::test_many(); /* Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. */ + scrape_examples::test_many(); /* Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. */ + scrape_examples::test_many(); /* Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. */ + scrape_examples::test_many(); /* Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. */ + scrape_examples::test_many(); /* Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. */ + scrape_examples::test_many(); /* Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. */ } diff --git a/src/test/rustdoc-gui/src/scrape_examples/examples/check-many-3.rs b/src/test/rustdoc-gui/src/scrape_examples/examples/check-many-3.rs index 1d1bc5002aa8f..c9fdf68d3be0c 100644 --- a/src/test/rustdoc-gui/src/scrape_examples/examples/check-many-3.rs +++ b/src/test/rustdoc-gui/src/scrape_examples/examples/check-many-3.rs @@ -1,3 +1,13 @@ fn main() { - scrape_examples::test_many(); + // ignore-tidy-linelength + scrape_examples::test_many(); /* Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. */ + scrape_examples::test_many(); /* Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. */ + scrape_examples::test_many(); /* Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. */ + scrape_examples::test_many(); /* Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. */ + scrape_examples::test_many(); /* Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. */ + scrape_examples::test_many(); /* Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. */ + scrape_examples::test_many(); /* Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. */ + scrape_examples::test_many(); /* Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. */ + scrape_examples::test_many(); /* Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. */ + scrape_examples::test_many(); /* Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. */ } diff --git a/src/test/rustdoc-gui/src/scrape_examples/examples/check-many-4.rs b/src/test/rustdoc-gui/src/scrape_examples/examples/check-many-4.rs index 1d1bc5002aa8f..81a48ac50c813 100644 --- a/src/test/rustdoc-gui/src/scrape_examples/examples/check-many-4.rs +++ b/src/test/rustdoc-gui/src/scrape_examples/examples/check-many-4.rs @@ -1,3 +1,13 @@ fn main() { + // all examples have same line count + scrape_examples::test_many(); + scrape_examples::test_many(); + scrape_examples::test_many(); + scrape_examples::test_many(); + scrape_examples::test_many(); + scrape_examples::test_many(); + scrape_examples::test_many(); + scrape_examples::test_many(); + scrape_examples::test_many(); scrape_examples::test_many(); } diff --git a/src/test/rustdoc-gui/src/scrape_examples/examples/check-many-5.rs b/src/test/rustdoc-gui/src/scrape_examples/examples/check-many-5.rs index 1d1bc5002aa8f..81a48ac50c813 100644 --- a/src/test/rustdoc-gui/src/scrape_examples/examples/check-many-5.rs +++ b/src/test/rustdoc-gui/src/scrape_examples/examples/check-many-5.rs @@ -1,3 +1,13 @@ fn main() { + // all examples have same line count + scrape_examples::test_many(); + scrape_examples::test_many(); + scrape_examples::test_many(); + scrape_examples::test_many(); + scrape_examples::test_many(); + scrape_examples::test_many(); + scrape_examples::test_many(); + scrape_examples::test_many(); + scrape_examples::test_many(); scrape_examples::test_many(); } diff --git a/src/test/rustdoc-gui/src/scrape_examples/examples/check-many-6.rs b/src/test/rustdoc-gui/src/scrape_examples/examples/check-many-6.rs index 1d1bc5002aa8f..81a48ac50c813 100644 --- a/src/test/rustdoc-gui/src/scrape_examples/examples/check-many-6.rs +++ b/src/test/rustdoc-gui/src/scrape_examples/examples/check-many-6.rs @@ -1,3 +1,13 @@ fn main() { + // all examples have same line count + scrape_examples::test_many(); + scrape_examples::test_many(); + scrape_examples::test_many(); + scrape_examples::test_many(); + scrape_examples::test_many(); + scrape_examples::test_many(); + scrape_examples::test_many(); + scrape_examples::test_many(); + scrape_examples::test_many(); scrape_examples::test_many(); } diff --git a/src/test/rustdoc-gui/src/scrape_examples/examples/check-many-7.rs b/src/test/rustdoc-gui/src/scrape_examples/examples/check-many-7.rs index 1d1bc5002aa8f..81a48ac50c813 100644 --- a/src/test/rustdoc-gui/src/scrape_examples/examples/check-many-7.rs +++ b/src/test/rustdoc-gui/src/scrape_examples/examples/check-many-7.rs @@ -1,3 +1,13 @@ fn main() { + // all examples have same line count + scrape_examples::test_many(); + scrape_examples::test_many(); + scrape_examples::test_many(); + scrape_examples::test_many(); + scrape_examples::test_many(); + scrape_examples::test_many(); + scrape_examples::test_many(); + scrape_examples::test_many(); + scrape_examples::test_many(); scrape_examples::test_many(); } From 1c5b53be1c5ea2f95b1b3e4327c69f14127a923c Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 20 Dec 2022 18:59:52 +0000 Subject: [PATCH 24/30] Avoid going through the happy path in case of non-fn builtin calls --- compiler/rustc_hir_typeck/src/callee.rs | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index 7a5191b77f1d4..4ec71a78a0031 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -4,7 +4,7 @@ use super::{Expectation, FnCtxt, TupleArgumentsFlag}; use crate::type_error_struct; use rustc_ast::util::parser::PREC_POSTFIX; -use rustc_errors::{struct_span_err, Applicability, Diagnostic, StashKey}; +use rustc_errors::{struct_span_err, Applicability, Diagnostic, ErrorGuaranteed, StashKey}; use rustc_hir as hir; use rustc_hir::def::{self, CtorKind, Namespace, Res}; use rustc_hir::def_id::DefId; @@ -424,21 +424,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - self.report_invalid_callee(call_expr, callee_expr, callee_ty, arg_exprs); - - // This is the "default" function signature, used in case of error. - // In that case, we check each argument against "error" in order to - // set up all the node type bindings. - ( - ty::Binder::dummy(self.tcx.mk_fn_sig( - self.err_args(arg_exprs.len()).into_iter(), - self.tcx.ty_error(), - false, - hir::Unsafety::Normal, - abi::Abi::Rust, - )), - None, - ) + let err = self.report_invalid_callee(call_expr, callee_expr, callee_ty, arg_exprs); + + return self.tcx.ty_error_with_guaranteed(err); } }; @@ -591,7 +579,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { callee_expr: &'tcx hir::Expr<'tcx>, callee_ty: Ty<'tcx>, arg_exprs: &'tcx [hir::Expr<'tcx>], - ) { + ) -> ErrorGuaranteed { let mut unit_variant = None; if let hir::ExprKind::Path(qpath) = &callee_expr.kind && let Res::Def(def::DefKind::Ctor(kind, CtorKind::Const), _) @@ -720,7 +708,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.span_label(span, label); } } - err.emit(); + err.emit() } fn confirm_deferred_closure_call( From b29a9e3b3f415591240c800a0e7a274cc49e2465 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Tue, 20 Dec 2022 12:44:22 -0700 Subject: [PATCH 25/30] rustdoc: simplify section anchor CSS Since f50bf8636e3b0296db82e631fe95c84324a46ccc changed anchors to be always positioned absolute, specifying it on hover as well is redundant. --- src/librustdoc/html/static/css/rustdoc.css | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index 11e34a3fc7d1f..30d70f1a09947 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -689,14 +689,10 @@ a { position: relative; } -.small-section-header:hover > .anchor { +.small-section-header:hover > .anchor, .impl:hover > .anchor, +.trait-impl:hover > .anchor, .variant:hover > .anchor { display: initial; } - -.impl:hover > .anchor, .trait-impl:hover > .anchor, .variant:hover > .anchor { - display: inline-block; - position: absolute; -} .anchor { display: none; position: absolute; From 5480ac540cdb88a1512334c5680ccf7cd76a38c8 Mon Sep 17 00:00:00 2001 From: Arvind Mukund Date: Tue, 20 Dec 2022 11:44:12 -0800 Subject: [PATCH 26/30] Use `Error` behavior for LLVM versions prior to 15 CI fails when building with LLVM-13. This raises unknown behavior constant `8` from IRVerifier. --- compiler/rustc_codegen_llvm/src/context.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 9c7d3dabd6fb5..d9ccba07a346a 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -280,29 +280,35 @@ pub unsafe fn create_module<'ll>( } if let Some(BranchProtection { bti, pac_ret }) = sess.opts.unstable_opts.branch_protection { + let behavior = if llvm_version >= (15, 0, 0) { + llvm::LLVMModFlagBehavior::Min + } else { + llvm::LLVMModFlagBehavior::Error + }; + if sess.target.arch == "aarch64" { llvm::LLVMRustAddModuleFlag( llmod, - llvm::LLVMModFlagBehavior::Min, + behavior, "branch-target-enforcement\0".as_ptr().cast(), bti.into(), ); llvm::LLVMRustAddModuleFlag( llmod, - llvm::LLVMModFlagBehavior::Min, + behavior, "sign-return-address\0".as_ptr().cast(), pac_ret.is_some().into(), ); let pac_opts = pac_ret.unwrap_or(PacRet { leaf: false, key: PAuthKey::A }); llvm::LLVMRustAddModuleFlag( llmod, - llvm::LLVMModFlagBehavior::Min, + behavior, "sign-return-address-all\0".as_ptr().cast(), pac_opts.leaf.into(), ); llvm::LLVMRustAddModuleFlag( llmod, - llvm::LLVMModFlagBehavior::Min, + behavior, "sign-return-address-with-bkey\0".as_ptr().cast(), u32::from(pac_opts.key == PAuthKey::B), ); From fb89ae4092deb4b3b90f0bdc2d5467b4029ba258 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Tue, 20 Dec 2022 15:39:32 -0600 Subject: [PATCH 27/30] Remove unused `check-stage2-T-arm-linux-androideabi-H-x86_64-unknown-linux-gnu` It's not used anywhere in CI, and it seems of questionable use. It was first added in 0e272de69f4a9c889e5f1a024a88b3e1f60cb6c5, which looks like it's just intended for CI, not as a user-facing feature. --- src/bootstrap/mk/Makefile.in | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/bootstrap/mk/Makefile.in b/src/bootstrap/mk/Makefile.in index 9a08a7be0f5f7..5b2aba9aa2dc8 100644 --- a/src/bootstrap/mk/Makefile.in +++ b/src/bootstrap/mk/Makefile.in @@ -57,11 +57,6 @@ tidy: prepare: $(Q)$(BOOTSTRAP) build --stage 2 nonexistent/path/to/trigger/cargo/metadata -check-stage2-T-arm-linux-androideabi-H-x86_64-unknown-linux-gnu: - $(Q)$(BOOTSTRAP) test --stage 2 --target arm-linux-androideabi -check-stage2-T-x86_64-unknown-linux-musl-H-x86_64-unknown-linux-gnu: - $(Q)$(BOOTSTRAP) test --stage 2 --target x86_64-unknown-linux-musl - TESTS_IN_2 := \ src/test/ui \ src/tools/linkchecker From f5e776c3f7afc2020d9828b593bac9db27290376 Mon Sep 17 00:00:00 2001 From: Geoffry Song Date: Tue, 20 Dec 2022 14:51:24 -0800 Subject: [PATCH 28/30] Refer to "Waker" rather than "RawWaker" in `drop` comment --- library/core/src/task/wake.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs index 0cff972df3a5a..1b7578376b42b 100644 --- a/library/core/src/task/wake.rs +++ b/library/core/src/task/wake.rs @@ -104,7 +104,7 @@ pub struct RawWakerVTable { /// pointer. wake_by_ref: unsafe fn(*const ()), - /// This function gets called when a [`RawWaker`] gets dropped. + /// This function gets called when a [`Waker`] gets dropped. /// /// The implementation of this function must make sure to release any /// resources that are associated with this instance of a [`RawWaker`] and @@ -151,7 +151,7 @@ impl RawWakerVTable { /// /// # `drop` /// - /// This function gets called when a [`RawWaker`] gets dropped. + /// This function gets called when a [`Waker`] gets dropped. /// /// The implementation of this function must make sure to release any /// resources that are associated with this instance of a [`RawWaker`] and From 5538c92837ef66369ea80faf0badd1c85f1512c9 Mon Sep 17 00:00:00 2001 From: Ikko Ashimine Date: Wed, 21 Dec 2022 15:34:21 +0900 Subject: [PATCH 29/30] Fix typo in reading_half_a_pointer.rs gurantee -> guarantee --- src/tools/miri/tests/fail/reading_half_a_pointer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/tests/fail/reading_half_a_pointer.rs b/src/tools/miri/tests/fail/reading_half_a_pointer.rs index 2d66913262476..7dd98eab78526 100644 --- a/src/tools/miri/tests/fail/reading_half_a_pointer.rs +++ b/src/tools/miri/tests/fail/reading_half_a_pointer.rs @@ -7,7 +7,7 @@ struct Data { ptr: &'static i32, } -// But we need to gurantee some alignment +// But we need to guarantee some alignment struct Wrapper { align: u64, data: Data, From 6f21ba4a06b7fa17113b7029c4aa7805db16aa32 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 21 Dec 2022 11:06:20 +0100 Subject: [PATCH 30/30] less specific wording --- library/core/src/intrinsics.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index ed58a7f179935..a521905a9e735 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -959,8 +959,7 @@ extern "rust-intrinsic" { #[rustc_safe_intrinsic] pub fn assert_zero_valid(); - /// A guard for `std::mem::uninitialized`. Checks whether a repeated bit pattern `0x01` - /// is legal for `T`: This will statically either panic, or do nothing. + /// A guard for `std::mem::uninitialized`. This will statically either panic, or do nothing. /// /// This intrinsic does not have a stable counterpart. #[rustc_const_unstable(feature = "const_assert_type2", issue = "none")]