From 7919e4208b0a7937a1407523c7f4181aca8c85b7 Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Mon, 21 Feb 2022 17:30:42 -0500 Subject: [PATCH 01/24] Fix slice::ChunksMut aliasing --- library/core/src/slice/iter.rs | 171 +++++++++++++++++++-------------- library/core/tests/slice.rs | 44 +++++++++ 2 files changed, 143 insertions(+), 72 deletions(-) diff --git a/library/core/src/slice/iter.rs b/library/core/src/slice/iter.rs index 35d00b9dda663..d085b5166677f 100644 --- a/library/core/src/slice/iter.rs +++ b/library/core/src/slice/iter.rs @@ -1629,14 +1629,15 @@ unsafe impl<'a, T> TrustedRandomAccessNoCoerce for Chunks<'a, T> { #[stable(feature = "rust1", since = "1.0.0")] #[must_use = "iterators are lazy and do nothing unless consumed"] pub struct ChunksMut<'a, T: 'a> { - v: &'a mut [T], + v: *mut [T], chunk_size: usize, + _marker: PhantomData<&'a mut T>, } impl<'a, T: 'a> ChunksMut<'a, T> { #[inline] pub(super) fn new(slice: &'a mut [T], size: usize) -> Self { - Self { v: slice, chunk_size: size } + Self { v: slice, chunk_size: size, _marker: PhantomData } } } @@ -1650,10 +1651,11 @@ impl<'a, T> Iterator for ChunksMut<'a, T> { None } else { let sz = cmp::min(self.v.len(), self.chunk_size); - let tmp = mem::replace(&mut self.v, &mut []); - let (head, tail) = tmp.split_at_mut(sz); + // SAFETY: sz cannot exceed the slice length based on the calculation above + let (head, tail) = unsafe { self.v.split_at_mut(sz) }; self.v = tail; - Some(head) + // SAFETY: Nothing points to or will point to the contents of this slice + Some(unsafe { &mut *head }) } } @@ -1685,11 +1687,13 @@ impl<'a, T> Iterator for ChunksMut<'a, T> { Some(sum) => cmp::min(self.v.len(), sum), None => self.v.len(), }; - let tmp = mem::replace(&mut self.v, &mut []); - let (head, tail) = tmp.split_at_mut(end); - let (_, nth) = head.split_at_mut(start); + // SAFETY: end is inbounds because we compared above against self.v.len() + let (head, tail) = unsafe { self.v.split_at_mut(end) }; + // SAFETY: start is inbounds because + let (_, nth) = unsafe { head.split_at_mut(start) }; self.v = tail; - Some(nth) + // SAFETY: Nothing points to or will point to the contents of this slice + Some(unsafe { &mut *nth }) } } @@ -1699,7 +1703,8 @@ impl<'a, T> Iterator for ChunksMut<'a, T> { None } else { let start = (self.v.len() - 1) / self.chunk_size * self.chunk_size; - Some(&mut self.v[start..]) + // SAFETY: Nothing points to or will point to the contents of this slice + Some(unsafe { &mut *self.v.get_unchecked_mut(start..) }) } } @@ -1727,12 +1732,12 @@ impl<'a, T> DoubleEndedIterator for ChunksMut<'a, T> { } else { let remainder = self.v.len() % self.chunk_size; let sz = if remainder != 0 { remainder } else { self.chunk_size }; - let tmp = mem::replace(&mut self.v, &mut []); - let tmp_len = tmp.len(); + let len = self.v.len(); // SAFETY: Similar to `Chunks::next_back` - let (head, tail) = unsafe { tmp.split_at_mut_unchecked(tmp_len - sz) }; + let (head, tail) = unsafe { self.v.split_at_mut_unchecked(len - sz) }; self.v = head; - Some(tail) + // SAFETY: Nothing points to or will point to the contents of this slice + Some(unsafe { &mut *tail }) } } @@ -1748,10 +1753,12 @@ impl<'a, T> DoubleEndedIterator for ChunksMut<'a, T> { Some(res) => cmp::min(self.v.len(), res), None => self.v.len(), }; - let (temp, _tail) = mem::replace(&mut self.v, &mut []).split_at_mut(end); - let (head, nth_back) = temp.split_at_mut(start); + // SAFETY: end is inbounds because we compared above against self.v.len() + let (temp, _tail) = unsafe { self.v.split_at_mut(end) }; + let (head, nth_back) = unsafe { temp.split_at_mut(start) }; self.v = head; - Some(nth_back) + // SAFETY: Nothing points to or will point to the contents of this slice + Some(unsafe { &mut *nth_back }) } } } @@ -1957,9 +1964,10 @@ unsafe impl<'a, T> TrustedRandomAccessNoCoerce for ChunksExact<'a, T> { #[stable(feature = "chunks_exact", since = "1.31.0")] #[must_use = "iterators are lazy and do nothing unless consumed"] pub struct ChunksExactMut<'a, T: 'a> { - v: &'a mut [T], - rem: &'a mut [T], + v: *mut [T], + rem: &'a mut [T], // The iterator never yields from here, so this can be unique chunk_size: usize, + _marker: PhantomData<&'a mut T>, } impl<'a, T> ChunksExactMut<'a, T> { @@ -1969,7 +1977,7 @@ impl<'a, T> ChunksExactMut<'a, T> { let fst_len = slice.len() - rem; // SAFETY: 0 <= fst_len <= slice.len() by construction above let (fst, snd) = unsafe { slice.split_at_mut_unchecked(fst_len) }; - Self { v: fst, rem: snd, chunk_size } + Self { v: fst, rem: snd, chunk_size, _marker: PhantomData } } /// Returns the remainder of the original slice that is not going to be @@ -1991,10 +1999,11 @@ impl<'a, T> Iterator for ChunksExactMut<'a, T> { if self.v.len() < self.chunk_size { None } else { - let tmp = mem::replace(&mut self.v, &mut []); - let (head, tail) = tmp.split_at_mut(self.chunk_size); + // SAFETY: self.chunk_size is inbounds because we compared above against self.v.len() + let (head, tail) = unsafe { self.v.split_at_mut(self.chunk_size) }; self.v = tail; - Some(head) + // SAFETY: Nothing points to or will point to the contents of this slice + Some(unsafe { &mut *head }) } } @@ -2016,8 +2025,7 @@ impl<'a, T> Iterator for ChunksExactMut<'a, T> { self.v = &mut []; None } else { - let tmp = mem::replace(&mut self.v, &mut []); - let (_, snd) = tmp.split_at_mut(start); + let (_, snd) = unsafe { self.v.split_at_mut(start) }; self.v = snd; self.next() } @@ -2042,11 +2050,11 @@ impl<'a, T> DoubleEndedIterator for ChunksExactMut<'a, T> { if self.v.len() < self.chunk_size { None } else { - let tmp = mem::replace(&mut self.v, &mut []); - let tmp_len = tmp.len(); - let (head, tail) = tmp.split_at_mut(tmp_len - self.chunk_size); + // SAFETY: This subtraction is inbounds because of the check above + let (head, tail) = unsafe { self.v.split_at_mut(self.v.len() - self.chunk_size) }; self.v = head; - Some(tail) + // SAFETY: Nothing points to or will point to the contents of this slice + Some(unsafe { &mut *tail }) } } @@ -2059,10 +2067,11 @@ impl<'a, T> DoubleEndedIterator for ChunksExactMut<'a, T> { } else { let start = (len - 1 - n) * self.chunk_size; let end = start + self.chunk_size; - let (temp, _tail) = mem::replace(&mut self.v, &mut []).split_at_mut(end); - let (head, nth_back) = temp.split_at_mut(start); + let (temp, _tail) = unsafe { mem::replace(&mut self.v, &mut []).split_at_mut(end) }; + let (head, nth_back) = unsafe { temp.split_at_mut(start) }; self.v = head; - Some(nth_back) + // SAFETY: Nothing points to or will point to the contents of this slice + Some(unsafe { &mut *nth_back }) } } } @@ -2646,14 +2655,15 @@ unsafe impl<'a, T> TrustedRandomAccessNoCoerce for RChunks<'a, T> { #[stable(feature = "rchunks", since = "1.31.0")] #[must_use = "iterators are lazy and do nothing unless consumed"] pub struct RChunksMut<'a, T: 'a> { - v: &'a mut [T], + v: *mut [T], chunk_size: usize, + _marker: PhantomData<&'a mut T>, } impl<'a, T: 'a> RChunksMut<'a, T> { #[inline] pub(super) fn new(slice: &'a mut [T], size: usize) -> Self { - Self { v: slice, chunk_size: size } + Self { v: slice, chunk_size: size, _marker: PhantomData } } } @@ -2667,16 +2677,16 @@ impl<'a, T> Iterator for RChunksMut<'a, T> { None } else { let sz = cmp::min(self.v.len(), self.chunk_size); - let tmp = mem::replace(&mut self.v, &mut []); - let tmp_len = tmp.len(); + let len = self.v.len(); // SAFETY: split_at_mut_unchecked just requires the argument be less // than the length. This could only happen if the expression - // `tmp_len - sz` overflows. This could only happen if `sz > - // tmp_len`, which is impossible as we initialize it as the `min` of - // `self.v.len()` (e.g. `tmp_len`) and `self.chunk_size`. - let (head, tail) = unsafe { tmp.split_at_mut_unchecked(tmp_len - sz) }; + // `len - sz` overflows. This could only happen if `sz > + // len`, which is impossible as we initialize it as the `min` of + // `self.v.len()` (e.g. `len`) and `self.chunk_size`. + let (head, tail) = unsafe { self.v.split_at_mut_unchecked(len - sz) }; self.v = head; - Some(tail) + // SAFETY: Nothing points to or will point to the contents of this slice + Some(unsafe { &mut *tail }) } } @@ -2710,11 +2720,11 @@ impl<'a, T> Iterator for RChunksMut<'a, T> { Some(sum) => sum, None => 0, }; - let tmp = mem::replace(&mut self.v, &mut []); - let (head, tail) = tmp.split_at_mut(start); - let (nth, _) = tail.split_at_mut(end - start); + let (head, tail) = unsafe { self.v.split_at_mut(start) }; + let (nth, _) = unsafe { tail.split_at_mut(end - start) }; self.v = head; - Some(nth) + // SAFETY: Nothing points to or will point to the contents of this slice + Some(unsafe { &mut *nth }) } } @@ -2725,7 +2735,8 @@ impl<'a, T> Iterator for RChunksMut<'a, T> { } else { let rem = self.v.len() % self.chunk_size; let end = if rem == 0 { self.chunk_size } else { rem }; - Some(&mut self.v[0..end]) + // SAFETY: Nothing points to or will point to the contents of this slice + Some(unsafe { &mut *self.v.get_unchecked_mut(0..end) }) } } @@ -2750,11 +2761,11 @@ impl<'a, T> DoubleEndedIterator for RChunksMut<'a, T> { } else { let remainder = self.v.len() % self.chunk_size; let sz = if remainder != 0 { remainder } else { self.chunk_size }; - let tmp = mem::replace(&mut self.v, &mut []); // SAFETY: Similar to `Chunks::next_back` - let (head, tail) = unsafe { tmp.split_at_mut_unchecked(sz) }; + let (head, tail) = unsafe { self.v.split_at_mut_unchecked(sz) }; self.v = tail; - Some(head) + // SAFETY: Nothing points to or will point to the contents of this slice + Some(unsafe { &mut *head }) } } @@ -2769,10 +2780,11 @@ impl<'a, T> DoubleEndedIterator for RChunksMut<'a, T> { let offset_from_end = (len - 1 - n) * self.chunk_size; let end = self.v.len() - offset_from_end; let start = end.saturating_sub(self.chunk_size); - let (tmp, tail) = mem::replace(&mut self.v, &mut []).split_at_mut(end); - let (_, nth_back) = tmp.split_at_mut(start); + let (tmp, tail) = unsafe { self.v.split_at_mut(end) }; + let (_, nth_back) = unsafe { tmp.split_at_mut(start) }; self.v = tail; - Some(nth_back) + // SAFETY: Nothing points to or will point to the contents of this slice + Some(unsafe { &mut *nth_back }) } } } @@ -2898,8 +2910,7 @@ impl<'a, T> Iterator for RChunksExact<'a, T> { unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item { let end = self.v.len() - idx * self.chunk_size; let start = end - self.chunk_size; - // SAFETY: - // SAFETY: mostmy identical to `Chunks::__iterator_get_unchecked`. + // SAFETY: mostly identical to `Chunks::__iterator_get_unchecked`. unsafe { from_raw_parts(self.v.as_ptr().add(start), self.chunk_size) } } } @@ -2982,7 +2993,7 @@ unsafe impl<'a, T> TrustedRandomAccessNoCoerce for RChunksExact<'a, T> { #[stable(feature = "rchunks", since = "1.31.0")] #[must_use = "iterators are lazy and do nothing unless consumed"] pub struct RChunksExactMut<'a, T: 'a> { - v: &'a mut [T], + v: *mut [T], rem: &'a mut [T], chunk_size: usize, } @@ -3015,11 +3026,11 @@ impl<'a, T> Iterator for RChunksExactMut<'a, T> { if self.v.len() < self.chunk_size { None } else { - let tmp = mem::replace(&mut self.v, &mut []); - let tmp_len = tmp.len(); - let (head, tail) = tmp.split_at_mut(tmp_len - self.chunk_size); + let len = self.v.len(); + let (head, tail) = unsafe { self.v.split_at_mut(len - self.chunk_size) }; self.v = head; - Some(tail) + // SAFETY: Nothing points to or will point to the contents of this slice + Some(unsafe { &mut *tail }) } } @@ -3041,9 +3052,8 @@ impl<'a, T> Iterator for RChunksExactMut<'a, T> { self.v = &mut []; None } else { - let tmp = mem::replace(&mut self.v, &mut []); - let tmp_len = tmp.len(); - let (fst, _) = tmp.split_at_mut(tmp_len - end); + let len = self.v.len(); + let (fst, _) = unsafe { self.v.split_at_mut(len - end) }; self.v = fst; self.next() } @@ -3069,10 +3079,10 @@ impl<'a, T> DoubleEndedIterator for RChunksExactMut<'a, T> { if self.v.len() < self.chunk_size { None } else { - let tmp = mem::replace(&mut self.v, &mut []); - let (head, tail) = tmp.split_at_mut(self.chunk_size); + let (head, tail) = unsafe { self.v.split_at_mut(self.chunk_size) }; self.v = tail; - Some(head) + // SAFETY: Nothing points to or will point to the contents of this slice + Some(unsafe { &mut *head }) } } @@ -3088,10 +3098,11 @@ impl<'a, T> DoubleEndedIterator for RChunksExactMut<'a, T> { let offset = (len - n) * self.chunk_size; let start = self.v.len() - offset; let end = start + self.chunk_size; - let (tmp, tail) = mem::replace(&mut self.v, &mut []).split_at_mut(end); - let (_, nth_back) = tmp.split_at_mut(start); + let (tmp, tail) = unsafe { self.v.split_at_mut(end) }; + let (_, nth_back) = unsafe { tmp.split_at_mut(start) }; self.v = tail; - Some(nth_back) + // SAFETY: Nothing points to or will point to the contents of this slice + Some(unsafe { &mut *nth_back }) } } } @@ -3174,7 +3185,11 @@ where let mut len = 1; let mut iter = self.slice.windows(2); while let Some([l, r]) = iter.next() { - if (self.predicate)(l, r) { len += 1 } else { break } + if (self.predicate)(l, r) { + len += 1 + } else { + break; + } } let (head, tail) = self.slice.split_at(len); self.slice = tail; @@ -3206,7 +3221,11 @@ where let mut len = 1; let mut iter = self.slice.windows(2); while let Some([l, r]) = iter.next_back() { - if (self.predicate)(l, r) { len += 1 } else { break } + if (self.predicate)(l, r) { + len += 1 + } else { + break; + } } let (head, tail) = self.slice.split_at(self.slice.len() - len); self.slice = head; @@ -3261,7 +3280,11 @@ where let mut len = 1; let mut iter = self.slice.windows(2); while let Some([l, r]) = iter.next() { - if (self.predicate)(l, r) { len += 1 } else { break } + if (self.predicate)(l, r) { + len += 1 + } else { + break; + } } let slice = mem::take(&mut self.slice); let (head, tail) = slice.split_at_mut(len); @@ -3294,7 +3317,11 @@ where let mut len = 1; let mut iter = self.slice.windows(2); while let Some([l, r]) = iter.next_back() { - if (self.predicate)(l, r) { len += 1 } else { break } + if (self.predicate)(l, r) { + len += 1 + } else { + break; + } } let slice = mem::take(&mut self.slice); let (head, tail) = slice.split_at_mut(slice.len() - len); diff --git a/library/core/tests/slice.rs b/library/core/tests/slice.rs index 5751a91721d1f..6d1516958f39b 100644 --- a/library/core/tests/slice.rs +++ b/library/core/tests/slice.rs @@ -409,6 +409,50 @@ fn test_chunks_mut_zip() { assert_eq!(v1, [13, 14, 19, 20, 14]); } +#[test] +fn test_chunks_mut_zip_aliasing() { + let v1: &mut [i32] = &mut [0, 1, 2, 3, 4]; + let v2: &[i32] = &[6, 7, 8, 9, 10]; + + let mut it = v1.chunks_mut(2).zip(v2.chunks(2)); + let first = it.next().unwrap(); + let _ = it.next().unwrap(); + assert_eq!(first, (&mut [0, 1][..], &[6, 7][..])); +} + +#[test] +fn test_chunks_exact_mut_zip_aliasing() { + let v1: &mut [i32] = &mut [0, 1, 2, 3, 4]; + let v2: &[i32] = &[6, 7, 8, 9, 10]; + + let mut it = v1.chunks_exact_mut(2).zip(v2.chunks(2)); + let first = it.next().unwrap(); + let _ = it.next().unwrap(); + assert_eq!(first, (&mut [0, 1][..], &[6, 7][..])); +} + +#[test] +fn test_rchunks_mut_zip_aliasing() { + let v1: &mut [i32] = &mut [0, 1, 2, 3, 4]; + let v2: &[i32] = &[6, 7, 8, 9, 10]; + + let mut it = v1.rchunks_mut(2).zip(v2.chunks(2)); + let first = it.next().unwrap(); + let _ = it.next().unwrap(); + assert_eq!(first, (&mut [3, 4][..], &[6, 7][..])); +} + +#[test] +fn test_rchunks_exact_mut_zip_aliasing() { + let v1: &mut [i32] = &mut [0, 1, 2, 3, 4]; + let v2: &[i32] = &[6, 7, 8, 9, 10]; + + let mut it = v1.rchunks_exact_mut(2).zip(v2.chunks(2)); + let first = it.next().unwrap(); + let _ = it.next().unwrap(); + assert_eq!(first, (&mut [3, 4][..], &[6, 7][..])); +} + #[test] fn test_chunks_exact_count() { let v: &[i32] = &[0, 1, 2, 3, 4, 5]; From 1152e70363702919a729b9a83eb7550c9392d306 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sat, 23 Jul 2022 20:09:52 +0000 Subject: [PATCH 02/24] Deeply deny fn and raw ptrs in const generics --- .../src/transform/check_consts/qualifs.rs | 2 +- compiler/rustc_middle/src/ty/consts.rs | 2 +- .../src/thir/pattern/const_to_pat.rs | 8 ++- .../src/traits/structural_match.rs | 70 +++++++++++++------ compiler/rustc_typeck/src/check/wfcheck.rs | 38 +++++----- .../fn-const-param-call.full.stderr | 5 +- .../fn-const-param-infer.full.stderr | 3 +- .../issues/issue-71381.full.stderr | 7 +- .../issues/issue-71382.full.stderr | 3 +- .../issues/issue-71611.full.stderr | 5 +- .../issues/issue-72352.full.stderr | 3 +- .../ui/const-generics/issues/issue-99641.rs | 18 +++++ .../const-generics/issues/issue-99641.stderr | 15 ++++ .../raw-ptr-const-param-deref.full.stderr | 5 +- .../raw-ptr-const-param.full.stderr | 3 +- .../consts/refs_check_const_eq-issue-88384.rs | 4 +- .../refs_check_const_eq-issue-88384.stderr | 17 ++++- 17 files changed, 147 insertions(+), 61 deletions(-) create mode 100644 src/test/ui/const-generics/issues/issue-99641.rs create mode 100644 src/test/ui/const-generics/issues/issue-99641.stderr diff --git a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs index 29464cf8c4e4f..b97dd1ae8d592 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs @@ -226,7 +226,7 @@ impl Qualif for CustomEq { // because that component may be part of an enum variant (e.g., // `Option::::Some`), in which case some values of this type may be // structural-match (`Option::None`). - traits::search_for_structural_match_violation(cx.body.span, cx.tcx, ty, true).is_some() + traits::search_for_structural_match_violation(cx.body.span, cx.tcx, ty, false).is_some() } fn in_adt_inherently<'tcx>( diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs index a4e7a12bba323..f8792edc017b2 100644 --- a/compiler/rustc_middle/src/ty/consts.rs +++ b/compiler/rustc_middle/src/ty/consts.rs @@ -203,7 +203,7 @@ impl<'tcx> Const<'tcx> { pub fn to_valtree(self) -> ty::ValTree<'tcx> { match self.kind() { ty::ConstKind::Value(valtree) => valtree, - _ => bug!("expected ConstKind::Value"), + _ => bug!("expected ConstKind::Value, got {:?}", self.kind()), } } diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs index e32e0b11ba497..44faed3fa9fb9 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs @@ -120,7 +120,7 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { } fn search_for_structural_match_violation(&self, ty: Ty<'tcx>) -> Option { - traits::search_for_structural_match_violation(self.span, self.tcx(), ty, true).map( + traits::search_for_structural_match_violation(self.span, self.tcx(), ty, false).map( |non_sm_ty| { with_no_trimmed_paths!(match non_sm_ty.kind { traits::NonStructuralMatchTyKind::Adt(adt) => self.adt_derive_msg(adt), @@ -139,6 +139,12 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { traits::NonStructuralMatchTyKind::Float => { "floating-point numbers cannot be used in patterns".to_string() } + traits::NonStructuralMatchTyKind::FnPtr => { + "function pointers cannot be used in patterns".to_string() + } + traits::NonStructuralMatchTyKind::RawPtr => { + "raw pointers cannot be used in patterns".to_string() + } traits::NonStructuralMatchTyKind::Param => { bug!("use of a constant whose type is a parameter inside a pattern") } diff --git a/compiler/rustc_trait_selection/src/traits/structural_match.rs b/compiler/rustc_trait_selection/src/traits/structural_match.rs index 6c0b83fbd0304..25a66bb9d08af 100644 --- a/compiler/rustc_trait_selection/src/traits/structural_match.rs +++ b/compiler/rustc_trait_selection/src/traits/structural_match.rs @@ -27,6 +27,8 @@ pub enum NonStructuralMatchTyKind<'tcx> { Generator, Projection, Float, + FnPtr, + RawPtr, } /// This method traverses the structure of `ty`, trying to find an @@ -55,14 +57,15 @@ pub enum NonStructuralMatchTyKind<'tcx> { /// that arose when the requirement was not enforced completely, see /// Rust RFC 1445, rust-lang/rust#61188, and rust-lang/rust#62307. /// -/// The floats_allowed flag is used to deny constants in floating point +/// When the `valtree_semantics` flag is set, then we also deny additional +/// types that are not evaluatable to valtrees, such as floats and fn ptrs. pub fn search_for_structural_match_violation<'tcx>( span: Span, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, - floats_allowed: bool, + valtree_semantics: bool, ) -> Option> { - ty.visit_with(&mut Search { tcx, span, seen: FxHashSet::default(), floats_allowed }) + ty.visit_with(&mut Search { tcx, span, seen: FxHashSet::default(), valtree_semantics }) .break_value() } @@ -125,7 +128,9 @@ struct Search<'tcx> { /// we will not recur on them again. seen: FxHashSet, - floats_allowed: bool, + // Additionally deny things that have been allowed in patterns, + // but are not evaluatable to a valtree, such as floats and fn ptrs. + valtree_semantics: bool, } impl<'tcx> Search<'tcx> { @@ -170,24 +175,7 @@ impl<'tcx> TypeVisitor<'tcx> for Search<'tcx> { let kind = NonStructuralMatchTyKind::Generator; return ControlFlow::Break(NonStructuralMatchTy { ty, kind }); } - ty::RawPtr(..) => { - // structural-match ignores substructure of - // `*const _`/`*mut _`, so skip `super_visit_with`. - // - // For example, if you have: - // ``` - // struct NonStructural; - // #[derive(PartialEq, Eq)] - // struct T(*const NonStructural); - // const C: T = T(std::ptr::null()); - // ``` - // - // Even though `NonStructural` does not implement `PartialEq`, - // structural equality on `T` does not recur into the raw - // pointer. Therefore, one can still use `C` in a pattern. - return ControlFlow::CONTINUE; - } - ty::FnDef(..) | ty::FnPtr(..) => { + ty::FnDef(..) => { // Types of formals and return in `fn(_) -> _` are also irrelevant; // so we do not recur into them via `super_visit_with` return ControlFlow::CONTINUE; @@ -206,8 +194,44 @@ impl<'tcx> TypeVisitor<'tcx> for Search<'tcx> { return ControlFlow::CONTINUE; } + ty::FnPtr(..) => { + if !self.valtree_semantics { + return ControlFlow::CONTINUE; + } else { + return ControlFlow::Break(NonStructuralMatchTy { + ty, + kind: NonStructuralMatchTyKind::FnPtr, + }); + } + } + + ty::RawPtr(..) => { + if !self.valtree_semantics { + // structural-match ignores substructure of + // `*const _`/`*mut _`, so skip `super_visit_with`. + // + // For example, if you have: + // ``` + // struct NonStructural; + // #[derive(PartialEq, Eq)] + // struct T(*const NonStructural); + // const C: T = T(std::ptr::null()); + // ``` + // + // Even though `NonStructural` does not implement `PartialEq`, + // structural equality on `T` does not recur into the raw + // pointer. Therefore, one can still use `C` in a pattern. + return ControlFlow::CONTINUE; + } else { + return ControlFlow::Break(NonStructuralMatchTy { + ty, + kind: NonStructuralMatchTyKind::FnPtr, + }); + } + } + ty::Float(_) => { - if self.floats_allowed { + if !self.valtree_semantics { return ControlFlow::CONTINUE; } else { return ControlFlow::Break(NonStructuralMatchTy { diff --git a/compiler/rustc_typeck/src/check/wfcheck.rs b/compiler/rustc_typeck/src/check/wfcheck.rs index b8e998229ba8e..324b229fa5941 100644 --- a/compiler/rustc_typeck/src/check/wfcheck.rs +++ b/compiler/rustc_typeck/src/check/wfcheck.rs @@ -848,29 +848,13 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) { let ty = tcx.type_of(tcx.hir().local_def_id(param.hir_id)); if tcx.features().adt_const_params { - let err = match ty.peel_refs().kind() { - ty::FnPtr(_) => Some("function pointers"), - ty::RawPtr(_) => Some("raw pointers"), - _ => None, - }; - - if let Some(unsupported_type) = err { - tcx.sess.span_err( - hir_ty.span, - &format!( - "using {} as const generic parameters is forbidden", - unsupported_type - ), - ); - } - if let Some(non_structural_match_ty) = - traits::search_for_structural_match_violation(param.span, tcx, ty, false) + traits::search_for_structural_match_violation(param.span, tcx, ty, true) { // We use the same error code in both branches, because this is really the same // issue: we just special-case the message for type parameters to make it // clearer. - match ty.peel_refs().kind() { + match non_structural_match_ty.ty.kind() { ty::Param(_) => { // Const parameters may not have type parameters as their types, // because we cannot be sure that the type parameter derives `PartialEq` @@ -902,6 +886,24 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) { .note("floats do not derive `Eq` or `Ord`, which are required for const parameters") .emit(); } + ty::FnPtr(_) => { + struct_span_err!( + tcx.sess, + hir_ty.span, + E0741, + "using function pointers as const generic parameters is forbidden", + ) + .emit(); + } + ty::RawPtr(_) => { + struct_span_err!( + tcx.sess, + hir_ty.span, + E0741, + "using raw pointers as const generic parameters is forbidden", + ) + .emit(); + } _ => { let mut diag = struct_span_err!( tcx.sess, diff --git a/src/test/ui/const-generics/fn-const-param-call.full.stderr b/src/test/ui/const-generics/fn-const-param-call.full.stderr index d984449e6ca6e..b55c2449858c9 100644 --- a/src/test/ui/const-generics/fn-const-param-call.full.stderr +++ b/src/test/ui/const-generics/fn-const-param-call.full.stderr @@ -1,10 +1,10 @@ -error: using function pointers as const generic parameters is forbidden +error[E0741]: using function pointers as const generic parameters is forbidden --> $DIR/fn-const-param-call.rs:11:25 | LL | struct Wrapper u32>; | ^^^^^^^^^^^ -error: using function pointers as const generic parameters is forbidden +error[E0741]: using function pointers as const generic parameters is forbidden --> $DIR/fn-const-param-call.rs:13:15 | LL | impl u32> Wrapper { @@ -12,3 +12,4 @@ LL | impl u32> Wrapper { error: aborting due to 2 previous errors +For more information about this error, try `rustc --explain E0741`. diff --git a/src/test/ui/const-generics/fn-const-param-infer.full.stderr b/src/test/ui/const-generics/fn-const-param-infer.full.stderr index f0767a10994a5..2d66a19233269 100644 --- a/src/test/ui/const-generics/fn-const-param-infer.full.stderr +++ b/src/test/ui/const-generics/fn-const-param-infer.full.stderr @@ -1,4 +1,4 @@ -error: using function pointers as const generic parameters is forbidden +error[E0741]: using function pointers as const generic parameters is forbidden --> $DIR/fn-const-param-infer.rs:6:25 | LL | struct Checked bool>; @@ -6,3 +6,4 @@ LL | struct Checked bool>; error: aborting due to previous error +For more information about this error, try `rustc --explain E0741`. diff --git a/src/test/ui/const-generics/issues/issue-71381.full.stderr b/src/test/ui/const-generics/issues/issue-71381.full.stderr index 3950317b37053..e17cf96aa3e3d 100644 --- a/src/test/ui/const-generics/issues/issue-71381.full.stderr +++ b/src/test/ui/const-generics/issues/issue-71381.full.stderr @@ -10,13 +10,13 @@ error[E0770]: the type of const parameters must not depend on other generic para LL | const FN: unsafe extern "C" fn(Args), | ^^^^ the type must not depend on the parameter `Args` -error: using function pointers as const generic parameters is forbidden +error[E0741]: using function pointers as const generic parameters is forbidden --> $DIR/issue-71381.rs:14:61 | LL | pub fn call_me(&self) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: using function pointers as const generic parameters is forbidden +error[E0741]: using function pointers as const generic parameters is forbidden --> $DIR/issue-71381.rs:23:19 | LL | const FN: unsafe extern "C" fn(Args), @@ -24,4 +24,5 @@ LL | const FN: unsafe extern "C" fn(Args), error: aborting due to 4 previous errors -For more information about this error, try `rustc --explain E0770`. +Some errors have detailed explanations: E0741, E0770. +For more information about an error, try `rustc --explain E0741`. diff --git a/src/test/ui/const-generics/issues/issue-71382.full.stderr b/src/test/ui/const-generics/issues/issue-71382.full.stderr index 715037bd5f1e8..ab2a4e64a8389 100644 --- a/src/test/ui/const-generics/issues/issue-71382.full.stderr +++ b/src/test/ui/const-generics/issues/issue-71382.full.stderr @@ -1,4 +1,4 @@ -error: using function pointers as const generic parameters is forbidden +error[E0741]: using function pointers as const generic parameters is forbidden --> $DIR/issue-71382.rs:16:23 | LL | fn test(&self) { @@ -6,3 +6,4 @@ LL | fn test(&self) { error: aborting due to previous error +For more information about this error, try `rustc --explain E0741`. diff --git a/src/test/ui/const-generics/issues/issue-71611.full.stderr b/src/test/ui/const-generics/issues/issue-71611.full.stderr index 01a85b745ce39..656aa29e19b25 100644 --- a/src/test/ui/const-generics/issues/issue-71611.full.stderr +++ b/src/test/ui/const-generics/issues/issue-71611.full.stderr @@ -4,7 +4,7 @@ error[E0770]: the type of const parameters must not depend on other generic para LL | fn func(outer: A) { | ^ the type must not depend on the parameter `A` -error: using function pointers as const generic parameters is forbidden +error[E0741]: using function pointers as const generic parameters is forbidden --> $DIR/issue-71611.rs:5:21 | LL | fn func(outer: A) { @@ -12,4 +12,5 @@ LL | fn func(outer: A) { error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0770`. +Some errors have detailed explanations: E0741, E0770. +For more information about an error, try `rustc --explain E0741`. diff --git a/src/test/ui/const-generics/issues/issue-72352.full.stderr b/src/test/ui/const-generics/issues/issue-72352.full.stderr index eedd73c4dcc0a..92580b33685d1 100644 --- a/src/test/ui/const-generics/issues/issue-72352.full.stderr +++ b/src/test/ui/const-generics/issues/issue-72352.full.stderr @@ -1,4 +1,4 @@ -error: using function pointers as const generic parameters is forbidden +error[E0741]: using function pointers as const generic parameters is forbidden --> $DIR/issue-72352.rs:7:42 | LL | unsafe fn unsafely_do_the_thing usize>(ptr: *const i8) -> usize { @@ -6,3 +6,4 @@ LL | unsafe fn unsafely_do_the_thing usize>(ptr: *const i8 error: aborting due to previous error +For more information about this error, try `rustc --explain E0741`. diff --git a/src/test/ui/const-generics/issues/issue-99641.rs b/src/test/ui/const-generics/issues/issue-99641.rs new file mode 100644 index 0000000000000..fae6d3fc41fb8 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-99641.rs @@ -0,0 +1,18 @@ +#![feature(adt_const_params)] +#![allow(incomplete_features)] + +fn main() { + pub struct Color; + //~^ ERROR using function pointers + + impl Color { + //~^ ERROR using function pointers + pub fn new() -> Self { + Color:: + } + } + + pub const D65: (fn(),) = (|| {},); + + Color::::new(); +} diff --git a/src/test/ui/const-generics/issues/issue-99641.stderr b/src/test/ui/const-generics/issues/issue-99641.stderr new file mode 100644 index 0000000000000..349ebba08d53f --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-99641.stderr @@ -0,0 +1,15 @@ +error[E0741]: using function pointers as const generic parameters is forbidden + --> $DIR/issue-99641.rs:5:35 + | +LL | pub struct Color; + | ^^^^^^^ + +error[E0741]: using function pointers as const generic parameters is forbidden + --> $DIR/issue-99641.rs:8:23 + | +LL | impl Color { + | ^^^^^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0741`. diff --git a/src/test/ui/const-generics/raw-ptr-const-param-deref.full.stderr b/src/test/ui/const-generics/raw-ptr-const-param-deref.full.stderr index 04bc46cb4ab12..657eee2be2443 100644 --- a/src/test/ui/const-generics/raw-ptr-const-param-deref.full.stderr +++ b/src/test/ui/const-generics/raw-ptr-const-param-deref.full.stderr @@ -1,10 +1,10 @@ -error: using raw pointers as const generic parameters is forbidden +error[E0741]: using raw pointers as const generic parameters is forbidden --> $DIR/raw-ptr-const-param-deref.rs:9:23 | LL | struct Const; | ^^^^^^^^^^ -error: using raw pointers as const generic parameters is forbidden +error[E0741]: using raw pointers as const generic parameters is forbidden --> $DIR/raw-ptr-const-param-deref.rs:11:15 | LL | impl Const

{ @@ -12,3 +12,4 @@ LL | impl Const

{ error: aborting due to 2 previous errors +For more information about this error, try `rustc --explain E0741`. diff --git a/src/test/ui/const-generics/raw-ptr-const-param.full.stderr b/src/test/ui/const-generics/raw-ptr-const-param.full.stderr index 310422aafcd35..69f1aae5681a4 100644 --- a/src/test/ui/const-generics/raw-ptr-const-param.full.stderr +++ b/src/test/ui/const-generics/raw-ptr-const-param.full.stderr @@ -1,4 +1,4 @@ -error: using raw pointers as const generic parameters is forbidden +error[E0741]: using raw pointers as const generic parameters is forbidden --> $DIR/raw-ptr-const-param.rs:6:23 | LL | struct Const; @@ -6,3 +6,4 @@ LL | struct Const; error: aborting due to previous error +For more information about this error, try `rustc --explain E0741`. diff --git a/src/test/ui/consts/refs_check_const_eq-issue-88384.rs b/src/test/ui/consts/refs_check_const_eq-issue-88384.rs index 204d18ea25de4..1496b28bd3ee6 100644 --- a/src/test/ui/consts/refs_check_const_eq-issue-88384.rs +++ b/src/test/ui/consts/refs_check_const_eq-issue-88384.rs @@ -1,5 +1,3 @@ -// check-pass - #![feature(fn_traits)] #![feature(adt_const_params)] //~^ WARNING the feature `adt_const_params` is incomplete @@ -10,8 +8,10 @@ struct CompileTimeSettings{ } struct Foo; +//~^ ERROR using function pointers as const generic parameters is forbidden impl Foo { + //~^ ERROR using function pointers as const generic parameters is forbidden fn call_hooks(){ } } diff --git a/src/test/ui/consts/refs_check_const_eq-issue-88384.stderr b/src/test/ui/consts/refs_check_const_eq-issue-88384.stderr index f2bad2f552759..4f2f5e244b67e 100644 --- a/src/test/ui/consts/refs_check_const_eq-issue-88384.stderr +++ b/src/test/ui/consts/refs_check_const_eq-issue-88384.stderr @@ -1,5 +1,5 @@ warning: the feature `adt_const_params` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/refs_check_const_eq-issue-88384.rs:4:12 + --> $DIR/refs_check_const_eq-issue-88384.rs:2:12 | LL | #![feature(adt_const_params)] | ^^^^^^^^^^^^^^^^ @@ -7,5 +7,18 @@ LL | #![feature(adt_const_params)] = note: `#[warn(incomplete_features)]` on by default = note: see issue #95174 for more information -warning: 1 warning emitted +error[E0741]: using function pointers as const generic parameters is forbidden + --> $DIR/refs_check_const_eq-issue-88384.rs:10:21 + | +LL | struct Foo; + | ^^^^^^^^^^^^^^^^^^^ + +error[E0741]: using function pointers as const generic parameters is forbidden + --> $DIR/refs_check_const_eq-issue-88384.rs:13:15 + | +LL | impl Foo { + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors; 1 warning emitted +For more information about this error, try `rustc --explain E0741`. From c1f54c30bb3200239849649ebc08d7ac2877db6c Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sat, 23 Jul 2022 23:10:08 +0000 Subject: [PATCH 03/24] Get rid of redundant NonStructuralMatchTyKind --- .../src/thir/pattern/const_to_pat.rs | 28 ++++---- .../rustc_trait_selection/src/traits/mod.rs | 1 - .../src/traits/structural_match.rs | 66 ++++--------------- compiler/rustc_typeck/src/check/wfcheck.rs | 6 +- 4 files changed, 28 insertions(+), 73 deletions(-) diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs index 44faed3fa9fb9..14133ebc17535 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs @@ -122,37 +122,31 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { fn search_for_structural_match_violation(&self, ty: Ty<'tcx>) -> Option { traits::search_for_structural_match_violation(self.span, self.tcx(), ty, false).map( |non_sm_ty| { - with_no_trimmed_paths!(match non_sm_ty.kind { - traits::NonStructuralMatchTyKind::Adt(adt) => self.adt_derive_msg(adt), - traits::NonStructuralMatchTyKind::Dynamic => { + with_no_trimmed_paths!(match non_sm_ty.kind() { + ty::Adt(adt, _) => self.adt_derive_msg(*adt), + ty::Dynamic(..) => { "trait objects cannot be used in patterns".to_string() } - traits::NonStructuralMatchTyKind::Opaque => { + ty::Opaque(..) => { "opaque types cannot be used in patterns".to_string() } - traits::NonStructuralMatchTyKind::Closure => { + ty::Closure(..) => { "closures cannot be used in patterns".to_string() } - traits::NonStructuralMatchTyKind::Generator => { + ty::Generator(..) | ty::GeneratorWitness(..) => { "generators cannot be used in patterns".to_string() } - traits::NonStructuralMatchTyKind::Float => { + ty::Float(..) => { "floating-point numbers cannot be used in patterns".to_string() } - traits::NonStructuralMatchTyKind::FnPtr => { + ty::FnPtr(..) => { "function pointers cannot be used in patterns".to_string() } - traits::NonStructuralMatchTyKind::RawPtr => { + ty::RawPtr(..) => { "raw pointers cannot be used in patterns".to_string() } - traits::NonStructuralMatchTyKind::Param => { - bug!("use of a constant whose type is a parameter inside a pattern") - } - traits::NonStructuralMatchTyKind::Projection => { - bug!("use of a constant whose type is a projection inside a pattern") - } - traits::NonStructuralMatchTyKind::Foreign => { - bug!("use of a value of a foreign type inside a pattern") + _ => { + bug!("use of a value of `{non_sm_ty}` inside a pattern") } }) }, diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 3ef51b0c27abd..5397baefb9cf4 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -61,7 +61,6 @@ pub use self::specialize::specialization_graph::FutureCompatOverlapError; pub use self::specialize::specialization_graph::FutureCompatOverlapErrorKind; pub use self::specialize::{specialization_graph, translate_substs, OverlapError}; pub use self::structural_match::search_for_structural_match_violation; -pub use self::structural_match::{NonStructuralMatchTy, NonStructuralMatchTyKind}; pub use self::util::{ elaborate_obligations, elaborate_predicates, elaborate_predicates_with_span, elaborate_trait_ref, elaborate_trait_refs, diff --git a/compiler/rustc_trait_selection/src/traits/structural_match.rs b/compiler/rustc_trait_selection/src/traits/structural_match.rs index 25a66bb9d08af..ea11670ee777d 100644 --- a/compiler/rustc_trait_selection/src/traits/structural_match.rs +++ b/compiler/rustc_trait_selection/src/traits/structural_match.rs @@ -6,31 +6,10 @@ use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_hir::lang_items::LangItem; use rustc_middle::ty::query::Providers; -use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor}; use rustc_span::Span; use std::ops::ControlFlow; -#[derive(Debug)] -pub struct NonStructuralMatchTy<'tcx> { - pub ty: Ty<'tcx>, - pub kind: NonStructuralMatchTyKind<'tcx>, -} - -#[derive(Debug)] -pub enum NonStructuralMatchTyKind<'tcx> { - Adt(AdtDef<'tcx>), - Param, - Dynamic, - Foreign, - Opaque, - Closure, - Generator, - Projection, - Float, - FnPtr, - RawPtr, -} - /// This method traverses the structure of `ty`, trying to find an /// instance of an ADT (i.e. struct or enum) that doesn't implement /// the structural-match traits, or a generic type parameter @@ -64,7 +43,7 @@ pub fn search_for_structural_match_violation<'tcx>( tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, valtree_semantics: bool, -) -> Option> { +) -> Option> { ty.visit_with(&mut Search { tcx, span, seen: FxHashSet::default(), valtree_semantics }) .break_value() } @@ -140,7 +119,7 @@ impl<'tcx> Search<'tcx> { } impl<'tcx> TypeVisitor<'tcx> for Search<'tcx> { - type BreakTy = NonStructuralMatchTy<'tcx>; + type BreakTy = Ty<'tcx>; fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow { debug!("Search visiting ty: {:?}", ty); @@ -148,32 +127,25 @@ impl<'tcx> TypeVisitor<'tcx> for Search<'tcx> { let (adt_def, substs) = match *ty.kind() { ty::Adt(adt_def, substs) => (adt_def, substs), ty::Param(_) => { - let kind = NonStructuralMatchTyKind::Param; - return ControlFlow::Break(NonStructuralMatchTy { ty, kind }); + return ControlFlow::Break(ty); } ty::Dynamic(..) => { - let kind = NonStructuralMatchTyKind::Dynamic; - return ControlFlow::Break(NonStructuralMatchTy { ty, kind }); + return ControlFlow::Break(ty); } ty::Foreign(_) => { - let kind = NonStructuralMatchTyKind::Foreign; - return ControlFlow::Break(NonStructuralMatchTy { ty, kind }); + return ControlFlow::Break(ty); } ty::Opaque(..) => { - let kind = NonStructuralMatchTyKind::Opaque; - return ControlFlow::Break(NonStructuralMatchTy { ty, kind }); + return ControlFlow::Break(ty); } ty::Projection(..) => { - let kind = NonStructuralMatchTyKind::Projection; - return ControlFlow::Break(NonStructuralMatchTy { ty, kind }); + return ControlFlow::Break(ty); } ty::Closure(..) => { - let kind = NonStructuralMatchTyKind::Closure; - return ControlFlow::Break(NonStructuralMatchTy { ty, kind }); + return ControlFlow::Break(ty); } ty::Generator(..) | ty::GeneratorWitness(..) => { - let kind = NonStructuralMatchTyKind::Generator; - return ControlFlow::Break(NonStructuralMatchTy { ty, kind }); + return ControlFlow::Break(ty); } ty::FnDef(..) => { // Types of formals and return in `fn(_) -> _` are also irrelevant; @@ -198,10 +170,7 @@ impl<'tcx> TypeVisitor<'tcx> for Search<'tcx> { if !self.valtree_semantics { return ControlFlow::CONTINUE; } else { - return ControlFlow::Break(NonStructuralMatchTy { - ty, - kind: NonStructuralMatchTyKind::FnPtr, - }); + return ControlFlow::Break(ty); } } @@ -223,10 +192,7 @@ impl<'tcx> TypeVisitor<'tcx> for Search<'tcx> { // pointer. Therefore, one can still use `C` in a pattern. return ControlFlow::CONTINUE; } else { - return ControlFlow::Break(NonStructuralMatchTy { - ty, - kind: NonStructuralMatchTyKind::FnPtr, - }); + return ControlFlow::Break(ty); } } @@ -234,10 +200,7 @@ impl<'tcx> TypeVisitor<'tcx> for Search<'tcx> { if !self.valtree_semantics { return ControlFlow::CONTINUE; } else { - return ControlFlow::Break(NonStructuralMatchTy { - ty, - kind: NonStructuralMatchTyKind::Float, - }); + return ControlFlow::Break(ty); } } @@ -263,8 +226,7 @@ impl<'tcx> TypeVisitor<'tcx> for Search<'tcx> { if !self.type_marked_structural(ty) { debug!("Search found ty: {:?}", ty); - let kind = NonStructuralMatchTyKind::Adt(adt_def); - return ControlFlow::Break(NonStructuralMatchTy { ty, kind }); + return ControlFlow::Break(ty); } // structural-match does not care about the diff --git a/compiler/rustc_typeck/src/check/wfcheck.rs b/compiler/rustc_typeck/src/check/wfcheck.rs index 324b229fa5941..3bf76ad38ee2b 100644 --- a/compiler/rustc_typeck/src/check/wfcheck.rs +++ b/compiler/rustc_typeck/src/check/wfcheck.rs @@ -854,7 +854,7 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) { // We use the same error code in both branches, because this is really the same // issue: we just special-case the message for type parameters to make it // clearer. - match non_structural_match_ty.ty.kind() { + match non_structural_match_ty.kind() { ty::Param(_) => { // Const parameters may not have type parameters as their types, // because we cannot be sure that the type parameter derives `PartialEq` @@ -911,10 +911,10 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) { E0741, "`{}` must be annotated with `#[derive(PartialEq, Eq)]` to be used as \ the type of a const parameter", - non_structural_match_ty.ty, + non_structural_match_ty, ); - if ty == non_structural_match_ty.ty { + if ty == non_structural_match_ty { diag.span_label( hir_ty.span, format!("`{ty}` doesn't derive both `PartialEq` and `Eq`"), From 10b69ab0d20bdbbcdfc5bfe443a50cf2b12b66de Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 24 Jul 2022 20:44:19 +0000 Subject: [PATCH 04/24] Remove non-descriptive boolean from search_for_structural_match_violation --- .../src/transform/check_consts/qualifs.rs | 2 +- .../src/thir/pattern/const_to_pat.rs | 60 +++++++++---------- .../rustc_trait_selection/src/traits/mod.rs | 4 +- .../src/traits/structural_match.rs | 33 ++++++---- compiler/rustc_typeck/src/check/wfcheck.rs | 2 +- 5 files changed, 57 insertions(+), 44 deletions(-) diff --git a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs index b97dd1ae8d592..e099445117225 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs @@ -226,7 +226,7 @@ impl Qualif for CustomEq { // because that component may be part of an enum variant (e.g., // `Option::::Some`), in which case some values of this type may be // structural-match (`Option::None`). - traits::search_for_structural_match_violation(cx.body.span, cx.tcx, ty, false).is_some() + traits::search_for_structural_match_violation(cx.body.span, cx.tcx, ty).is_some() } fn in_adt_inherently<'tcx>( diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs index 14133ebc17535..d6dd0f017941a 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs @@ -120,37 +120,35 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { } fn search_for_structural_match_violation(&self, ty: Ty<'tcx>) -> Option { - traits::search_for_structural_match_violation(self.span, self.tcx(), ty, false).map( - |non_sm_ty| { - with_no_trimmed_paths!(match non_sm_ty.kind() { - ty::Adt(adt, _) => self.adt_derive_msg(*adt), - ty::Dynamic(..) => { - "trait objects cannot be used in patterns".to_string() - } - ty::Opaque(..) => { - "opaque types cannot be used in patterns".to_string() - } - ty::Closure(..) => { - "closures cannot be used in patterns".to_string() - } - ty::Generator(..) | ty::GeneratorWitness(..) => { - "generators cannot be used in patterns".to_string() - } - ty::Float(..) => { - "floating-point numbers cannot be used in patterns".to_string() - } - ty::FnPtr(..) => { - "function pointers cannot be used in patterns".to_string() - } - ty::RawPtr(..) => { - "raw pointers cannot be used in patterns".to_string() - } - _ => { - bug!("use of a value of `{non_sm_ty}` inside a pattern") - } - }) - }, - ) + traits::search_for_structural_match_violation(self.span, self.tcx(), ty).map(|non_sm_ty| { + with_no_trimmed_paths!(match non_sm_ty.kind() { + ty::Adt(adt, _) => self.adt_derive_msg(*adt), + ty::Dynamic(..) => { + "trait objects cannot be used in patterns".to_string() + } + ty::Opaque(..) => { + "opaque types cannot be used in patterns".to_string() + } + ty::Closure(..) => { + "closures cannot be used in patterns".to_string() + } + ty::Generator(..) | ty::GeneratorWitness(..) => { + "generators cannot be used in patterns".to_string() + } + ty::Float(..) => { + "floating-point numbers cannot be used in patterns".to_string() + } + ty::FnPtr(..) => { + "function pointers cannot be used in patterns".to_string() + } + ty::RawPtr(..) => { + "raw pointers cannot be used in patterns".to_string() + } + _ => { + bug!("use of a value of `{non_sm_ty}` inside a pattern") + } + }) + }) } fn type_marked_structural(&self, ty: Ty<'tcx>) -> bool { diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 5397baefb9cf4..d0a17f712d3df 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -60,7 +60,9 @@ pub use self::select::{EvaluationResult, IntercrateAmbiguityCause, OverflowError pub use self::specialize::specialization_graph::FutureCompatOverlapError; pub use self::specialize::specialization_graph::FutureCompatOverlapErrorKind; pub use self::specialize::{specialization_graph, translate_substs, OverlapError}; -pub use self::structural_match::search_for_structural_match_violation; +pub use self::structural_match::{ + search_for_adt_const_param_violation, search_for_structural_match_violation, +}; pub use self::util::{ elaborate_obligations, elaborate_predicates, elaborate_predicates_with_span, elaborate_trait_ref, elaborate_trait_refs, diff --git a/compiler/rustc_trait_selection/src/traits/structural_match.rs b/compiler/rustc_trait_selection/src/traits/structural_match.rs index ea11670ee777d..c278752e3d9f4 100644 --- a/compiler/rustc_trait_selection/src/traits/structural_match.rs +++ b/compiler/rustc_trait_selection/src/traits/structural_match.rs @@ -35,16 +35,28 @@ use std::ops::ControlFlow; /// For more background on why Rust has this requirement, and issues /// that arose when the requirement was not enforced completely, see /// Rust RFC 1445, rust-lang/rust#61188, and rust-lang/rust#62307. -/// -/// When the `valtree_semantics` flag is set, then we also deny additional -/// types that are not evaluatable to valtrees, such as floats and fn ptrs. pub fn search_for_structural_match_violation<'tcx>( span: Span, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, - valtree_semantics: bool, ) -> Option> { - ty.visit_with(&mut Search { tcx, span, seen: FxHashSet::default(), valtree_semantics }) + ty.visit_with(&mut Search { tcx, span, seen: FxHashSet::default(), adt_const_param: false }) + .break_value() +} + +/// This method traverses the structure of `ty`, trying to find any +/// types that are not allowed to be used in a const generic. +/// +/// This is either because the type does not implement `StructuralEq` +/// and `StructuralPartialEq`, or because the type is intentionally +/// not supported in const generics (such as floats and raw pointers, +/// which are allowed in match blocks). +pub fn search_for_adt_const_param_violation<'tcx>( + span: Span, + tcx: TyCtxt<'tcx>, + ty: Ty<'tcx>, +) -> Option> { + ty.visit_with(&mut Search { tcx, span, seen: FxHashSet::default(), adt_const_param: true }) .break_value() } @@ -108,8 +120,9 @@ struct Search<'tcx> { seen: FxHashSet, // Additionally deny things that have been allowed in patterns, - // but are not evaluatable to a valtree, such as floats and fn ptrs. - valtree_semantics: bool, + // but are not allowed in adt const params, such as floats and + // fn ptrs. + adt_const_param: bool, } impl<'tcx> Search<'tcx> { @@ -167,7 +180,7 @@ impl<'tcx> TypeVisitor<'tcx> for Search<'tcx> { } ty::FnPtr(..) => { - if !self.valtree_semantics { + if !self.adt_const_param { return ControlFlow::CONTINUE; } else { return ControlFlow::Break(ty); @@ -175,7 +188,7 @@ impl<'tcx> TypeVisitor<'tcx> for Search<'tcx> { } ty::RawPtr(..) => { - if !self.valtree_semantics { + if !self.adt_const_param { // structural-match ignores substructure of // `*const _`/`*mut _`, so skip `super_visit_with`. // @@ -197,7 +210,7 @@ impl<'tcx> TypeVisitor<'tcx> for Search<'tcx> { } ty::Float(_) => { - if !self.valtree_semantics { + if !self.adt_const_param { return ControlFlow::CONTINUE; } else { return ControlFlow::Break(ty); diff --git a/compiler/rustc_typeck/src/check/wfcheck.rs b/compiler/rustc_typeck/src/check/wfcheck.rs index 3bf76ad38ee2b..ca5defd16882a 100644 --- a/compiler/rustc_typeck/src/check/wfcheck.rs +++ b/compiler/rustc_typeck/src/check/wfcheck.rs @@ -849,7 +849,7 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) { if tcx.features().adt_const_params { if let Some(non_structural_match_ty) = - traits::search_for_structural_match_violation(param.span, tcx, ty, true) + traits::search_for_adt_const_param_violation(param.span, tcx, ty) { // We use the same error code in both branches, because this is really the same // issue: we just special-case the message for type parameters to make it From 91e91d83be184ba743053350a6b837dcda38d1cb Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 26 Jul 2022 07:05:31 +0000 Subject: [PATCH 05/24] Allow try_to_raw_bytes on u8 array --- .../rustc_middle/src/ty/consts/valtree.rs | 34 +- compiler/rustc_middle/src/ty/print/pretty.rs | 2 +- src/test/mir-opt/issue-99325.rs | 12 + .../mir-opt/issue_99325.main.mir_map.0.mir | 295 ++++++++++++++++++ 4 files changed, 322 insertions(+), 21 deletions(-) create mode 100644 src/test/mir-opt/issue-99325.rs create mode 100644 src/test/mir-opt/issue_99325.main.mir_map.0.mir diff --git a/compiler/rustc_middle/src/ty/consts/valtree.rs b/compiler/rustc_middle/src/ty/consts/valtree.rs index c7653bdbe84a2..93707bb18ceec 100644 --- a/compiler/rustc_middle/src/ty/consts/valtree.rs +++ b/compiler/rustc_middle/src/ty/consts/valtree.rs @@ -80,31 +80,25 @@ impl<'tcx> ValTree<'tcx> { } /// Get the values inside the ValTree as a slice of bytes. This only works for - /// constants with types &str and &[u8]. + /// constants with types &str, &[u8], or [u8; _]. pub fn try_to_raw_bytes(self, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option<&'tcx [u8]> { match ty.kind() { ty::Ref(_, inner_ty, _) => match inner_ty.kind() { - ty::Str => { - let leafs = self - .unwrap_branch() - .into_iter() - .map(|v| v.unwrap_leaf().try_to_u8().unwrap()); - - return Some(tcx.arena.alloc_from_iter(leafs)); - } - ty::Slice(slice_ty) if *slice_ty == tcx.types.u8 => { - let leafs = self - .unwrap_branch() - .into_iter() - .map(|v| v.unwrap_leaf().try_to_u8().unwrap()); - - return Some(tcx.arena.alloc_from_iter(leafs)); - } - _ => {} + // `&str` can be interpreted as raw bytes + ty::Str => {} + // `&[u8]` can be interpreted as raw bytes + ty::Slice(slice_ty) if *slice_ty == tcx.types.u8 => {} + // other `&_` can't be interpreted as raw bytes + _ => return None, }, - _ => {} + // `[u8; N]` can be interpreted as raw bytes + ty::Array(array_ty, _) if *array_ty == tcx.types.u8 => {} + // Otherwise, type cannot be interpreted as raw bytes + _ => return None, } - None + Some(tcx.arena.alloc_from_iter( + self.unwrap_branch().into_iter().map(|v| v.unwrap_leaf().try_to_u8().unwrap()), + )) } } diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 03bb515904cd0..1da7f4220e705 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -1452,7 +1452,7 @@ pub trait PrettyPrinter<'tcx>: } }, (ty::ValTree::Branch(_), ty::Array(t, _)) if *t == u8_type => { - let bytes = valtree.try_to_raw_bytes(self.tcx(), *t).unwrap_or_else(|| { + let bytes = valtree.try_to_raw_bytes(self.tcx(), ty).unwrap_or_else(|| { bug!("expected to convert valtree to raw bytes for type {:?}", t) }); p!("*"); diff --git a/src/test/mir-opt/issue-99325.rs b/src/test/mir-opt/issue-99325.rs new file mode 100644 index 0000000000000..b79946ea8b56c --- /dev/null +++ b/src/test/mir-opt/issue-99325.rs @@ -0,0 +1,12 @@ +#![feature(adt_const_params)] +#![allow(incomplete_features)] + +pub fn function_with_bytes() -> &'static [u8] { + BYTES +} + +// EMIT_MIR issue_99325.main.mir_map.0.mir +pub fn main() { + assert_eq!(function_with_bytes::(), &[0x41, 0x41, 0x41, 0x41]); + assert_eq!(function_with_bytes::<{ &[0x41, 0x41, 0x41, 0x41] }>(), b"AAAA"); +} diff --git a/src/test/mir-opt/issue_99325.main.mir_map.0.mir b/src/test/mir-opt/issue_99325.main.mir_map.0.mir new file mode 100644 index 0000000000000..175f244aa5dc9 --- /dev/null +++ b/src/test/mir-opt/issue_99325.main.mir_map.0.mir @@ -0,0 +1,295 @@ +// MIR for `main` 0 mir_map + +| User Type Annotations +| 0: user_ty: Canonical { max_universe: U0, variables: [], value: TypeOf(DefId(0:3 ~ issue_99325[8f58]::function_with_bytes), UserSubsts { substs: [Const { ty: &'static [u8; 4], kind: Value(Branch([Leaf(0x41), Leaf(0x41), Leaf(0x41), Leaf(0x41)])) }], user_self_ty: None }) }, span: $DIR/issue-99325.rs:10:16: 10:46, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">} +| 1: user_ty: Canonical { max_universe: U0, variables: [], value: TypeOf(DefId(0:3 ~ issue_99325[8f58]::function_with_bytes), UserSubsts { substs: [Const { ty: &'static [u8; 4], kind: Unevaluated(Unevaluated { def: WithOptConstParam { did: DefId(0:8 ~ issue_99325[8f58]::main::{constant#1}), const_param_did: Some(DefId(0:4 ~ issue_99325[8f58]::function_with_bytes::BYTES)) }, substs: [], promoted: None }) }], user_self_ty: None }) }, span: $DIR/issue-99325.rs:11:16: 11:68, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">} +| +fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/issue-99325.rs:9:15: 9:15 + let _1: (); // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _2: (&&[u8], &&[u8; 4]); // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _3: &&[u8]; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let _4: &[u8]; // in scope 0 at $DIR/issue-99325.rs:10:16: 10:48 + let mut _5: &&[u8; 4]; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let _6: &[u8; 4]; // in scope 0 at $DIR/issue-99325.rs:10:50: 10:75 + let _7: [u8; 4]; // in scope 0 at $DIR/issue-99325.rs:10:51: 10:75 + let _8: &&[u8]; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let _9: &&[u8; 4]; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _10: bool; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _11: bool; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _12: &&[u8]; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _13: &&[u8; 4]; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _14: !; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let _16: !; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _17: core::panicking::AssertKind; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _18: &&[u8]; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let _19: &&[u8]; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _20: &&[u8; 4]; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let _21: &&[u8; 4]; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _22: std::option::Option; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let _23: (); // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _24: (&&[u8], &&[u8; 4]); // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _25: &&[u8]; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let _26: &[u8]; // in scope 0 at $DIR/issue-99325.rs:11:16: 11:70 + let mut _27: &&[u8; 4]; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let _28: &[u8; 4]; // in scope 0 at $DIR/issue-99325.rs:11:72: 11:79 + let _29: &&[u8]; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let _30: &&[u8; 4]; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _31: bool; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _32: bool; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _33: &&[u8]; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _34: &&[u8; 4]; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _35: !; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let _37: !; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _38: core::panicking::AssertKind; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _39: &&[u8]; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let _40: &&[u8]; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _41: &&[u8; 4]; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let _42: &&[u8; 4]; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _43: std::option::Option; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + scope 1 { + debug left_val => _8; // in scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + debug right_val => _9; // in scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let _15: core::panicking::AssertKind; // in scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + scope 2 { + debug kind => _15; // in scope 2 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + } + } + scope 3 { + debug left_val => _29; // in scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + debug right_val => _30; // in scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let _36: core::panicking::AssertKind; // in scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + scope 4 { + debug kind => _36; // in scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + } + } + + bb0: { + StorageLive(_1); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_2); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_3); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_4); // scope 0 at $DIR/issue-99325.rs:10:16: 10:48 + _4 = function_with_bytes::<&*b"AAAA">() -> [return: bb1, unwind: bb19]; // scope 0 at $DIR/issue-99325.rs:10:16: 10:48 + // mir::Constant + // + span: $DIR/issue-99325.rs:10:16: 10:46 + // + user_ty: UserType(0) + // + literal: Const { ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">}, val: Value() } + } + + bb1: { + _3 = &_4; // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_5); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_6); // scope 0 at $DIR/issue-99325.rs:10:50: 10:75 + StorageLive(_7); // scope 0 at $DIR/issue-99325.rs:10:51: 10:75 + _7 = [const 65_u8, const 65_u8, const 65_u8, const 65_u8]; // scope 0 at $DIR/issue-99325.rs:10:51: 10:75 + _6 = &_7; // scope 0 at $DIR/issue-99325.rs:10:50: 10:75 + _5 = &_6; // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _2 = (move _3, move _5); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_5); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_3); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + FakeRead(ForMatchedPlace(None), _2); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_8); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _8 = (_2.0: &&[u8]); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_9); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _9 = (_2.1: &&[u8; 4]); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_10); // scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_11); // scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_12); // scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _12 = &(*_8); // scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_13); // scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _13 = &(*_9); // scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _11 = <&[u8] as PartialEq<&[u8; 4]>>::eq(move _12, move _13) -> [return: bb2, unwind: bb19]; // scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + // mir::Constant + // + span: $SRC_DIR/core/src/macros/mod.rs:LL:COL + // + literal: Const { ty: for<'r, 's> fn(&'r &[u8], &'s &[u8; 4]) -> bool {<&[u8] as PartialEq<&[u8; 4]>>::eq}, val: Value() } + } + + bb2: { + StorageDead(_13); // scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_12); // scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _10 = Not(move _11); // scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_11); // scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + switchInt(move _10) -> [false: bb4, otherwise: bb3]; // scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + } + + bb3: { + StorageLive(_15); // scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _15 = core::panicking::AssertKind::Eq; // scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + FakeRead(ForLet(None), _15); // scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_16); // scope 2 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_17); // scope 2 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _17 = move _15; // scope 2 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_18); // scope 2 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_19); // scope 2 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _19 = &(*_8); // scope 2 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _18 = &(*_19); // scope 2 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_20); // scope 2 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_21); // scope 2 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _21 = &(*_9); // scope 2 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _20 = &(*_21); // scope 2 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_22); // scope 2 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _22 = Option::::None; // scope 2 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _16 = core::panicking::assert_failed::<&[u8], &[u8; 4]>(move _17, move _18, move _20, move _22) -> bb19; // scope 2 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + // mir::Constant + // + span: $SRC_DIR/core/src/macros/mod.rs:LL:COL + // + literal: Const { ty: for<'r, 's, 't0> fn(core::panicking::AssertKind, &'r &[u8], &'s &[u8; 4], Option>) -> ! {core::panicking::assert_failed::<&[u8], &[u8; 4]>}, val: Value() } + } + + bb4: { + goto -> bb7; // scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + } + + bb5: { + StorageDead(_22); // scope 2 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_20); // scope 2 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_18); // scope 2 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_17); // scope 2 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_21); // scope 2 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_19); // scope 2 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_16); // scope 2 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_15); // scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + unreachable; // scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + } + + bb6: { + goto -> bb8; // scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + } + + bb7: { + _1 = const (); // scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + goto -> bb8; // scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + } + + bb8: { + StorageDead(_10); // scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_9); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_8); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + goto -> bb9; // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + } + + bb9: { + StorageDead(_7); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_6); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_4); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_2); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_1); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_23); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_24); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_25); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_26); // scope 0 at $DIR/issue-99325.rs:11:16: 11:70 + _26 = function_with_bytes::<&*b"AAAA">() -> [return: bb10, unwind: bb19]; // scope 0 at $DIR/issue-99325.rs:11:16: 11:70 + // mir::Constant + // + span: $DIR/issue-99325.rs:11:16: 11:68 + // + user_ty: UserType(1) + // + literal: Const { ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">}, val: Value() } + } + + bb10: { + _25 = &_26; // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_27); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_28); // scope 0 at $DIR/issue-99325.rs:11:72: 11:79 + _28 = const b"AAAA"; // scope 0 at $DIR/issue-99325.rs:11:72: 11:79 + // mir::Constant + // + span: $DIR/issue-99325.rs:11:72: 11:79 + // + literal: Const { ty: &[u8; 4], val: Value(Scalar(alloc4)) } + _27 = &_28; // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _24 = (move _25, move _27); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_27); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_25); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + FakeRead(ForMatchedPlace(None), _24); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_29); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _29 = (_24.0: &&[u8]); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_30); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _30 = (_24.1: &&[u8; 4]); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_31); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_32); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_33); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _33 = &(*_29); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_34); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _34 = &(*_30); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _32 = <&[u8] as PartialEq<&[u8; 4]>>::eq(move _33, move _34) -> [return: bb11, unwind: bb19]; // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + // mir::Constant + // + span: $SRC_DIR/core/src/macros/mod.rs:LL:COL + // + literal: Const { ty: for<'r, 's> fn(&'r &[u8], &'s &[u8; 4]) -> bool {<&[u8] as PartialEq<&[u8; 4]>>::eq}, val: Value() } + } + + bb11: { + StorageDead(_34); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_33); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _31 = Not(move _32); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_32); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + switchInt(move _31) -> [false: bb13, otherwise: bb12]; // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + } + + bb12: { + StorageLive(_36); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _36 = core::panicking::AssertKind::Eq; // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + FakeRead(ForLet(None), _36); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_37); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_38); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _38 = move _36; // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_39); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_40); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _40 = &(*_29); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _39 = &(*_40); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_41); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_42); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _42 = &(*_30); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _41 = &(*_42); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_43); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _43 = Option::::None; // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _37 = core::panicking::assert_failed::<&[u8], &[u8; 4]>(move _38, move _39, move _41, move _43) -> bb19; // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + // mir::Constant + // + span: $SRC_DIR/core/src/macros/mod.rs:LL:COL + // + literal: Const { ty: for<'r, 's, 't0> fn(core::panicking::AssertKind, &'r &[u8], &'s &[u8; 4], Option>) -> ! {core::panicking::assert_failed::<&[u8], &[u8; 4]>}, val: Value() } + } + + bb13: { + goto -> bb16; // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + } + + bb14: { + StorageDead(_43); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_41); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_39); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_38); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_42); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_40); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_37); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_36); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + unreachable; // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + } + + bb15: { + goto -> bb17; // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + } + + bb16: { + _23 = const (); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + goto -> bb17; // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + } + + bb17: { + StorageDead(_31); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_30); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_29); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + goto -> bb18; // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + } + + bb18: { + StorageDead(_28); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_26); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_24); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_23); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _0 = const (); // scope 0 at $DIR/issue-99325.rs:9:15: 12:2 + return; // scope 0 at $DIR/issue-99325.rs:12:2: 12:2 + } + + bb19 (cleanup): { + resume; // scope 0 at $DIR/issue-99325.rs:9:1: 12:2 + } +} + +alloc4 (size: 4, align: 1) { + 41 41 41 41 │ AAAA +} From a2254d5d7c57be00cc640c0f7a8e21b320e947aa Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Mon, 13 Jun 2022 07:36:45 +0200 Subject: [PATCH 06/24] Do not produce extra lifetime parameters when not needed. --- compiler/rustc_resolve/src/late.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 9b5fd4ea6d133..bc5668bcec00f 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -1632,6 +1632,9 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { | PathSource::Struct | PathSource::TupleStruct(..) => false, }; + if !missing && !segment.has_generic_args { + continue; + } let elided_lifetime_span = if segment.has_generic_args { // If there are brackets, but not generic arguments, then use the opening bracket From ab63591f00cb985fe95e1226a2ab3898f1b9e471 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sun, 22 May 2022 10:22:20 +0200 Subject: [PATCH 07/24] Remove the distinction between LifetimeName::Implicit and LifetimeName::Underscore. --- compiler/rustc_ast_lowering/src/lib.rs | 8 +-- .../src/diagnostics/region_name.rs | 1 - compiler/rustc_hir/src/def.rs | 2 - compiler/rustc_hir/src/hir.rs | 14 ++---- compiler/rustc_hir/src/intravisit.rs | 1 - compiler/rustc_resolve/src/late.rs | 50 +++++-------------- compiler/rustc_resolve/src/late/lifetimes.rs | 8 +-- 7 files changed, 21 insertions(+), 63 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index d4b41aad08ca4..a031480611641 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -1883,7 +1883,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } hir::LifetimeName::Param(param, ParamName::Fresh) } - LifetimeRes::Anonymous { binder, elided } => { + LifetimeRes::Anonymous { binder } => { let mut l_name = None; if let Some(mut captured_lifetimes) = self.captured_lifetimes.take() { if !captured_lifetimes.binders_to_ignore.contains(&binder) { @@ -1900,11 +1900,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } self.captured_lifetimes = Some(captured_lifetimes); }; - l_name.unwrap_or(if elided { - hir::LifetimeName::Implicit - } else { - hir::LifetimeName::Underscore - }) + l_name.unwrap_or(hir::LifetimeName::Underscore) } LifetimeRes::Static => hir::LifetimeName::Static, LifetimeRes::Error => hir::LifetimeName::Error, diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs index 4cf1ac4d7abc0..8ba76979c39c6 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs @@ -589,7 +589,6 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { hir::LifetimeName::Param(_, hir::ParamName::Fresh) | hir::LifetimeName::ImplicitObjectLifetimeDefault - | hir::LifetimeName::Implicit | hir::LifetimeName::Underscore => { // In this case, the user left off the lifetime; so // they wrote something like: diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs index c0d5d2bc46d0d..2003d70835c6f 100644 --- a/compiler/rustc_hir/src/def.rs +++ b/compiler/rustc_hir/src/def.rs @@ -742,8 +742,6 @@ pub enum LifetimeRes { Anonymous { /// Id of the introducing place. See `Param`. binder: NodeId, - /// Whether this lifetime was spelled or elided. - elided: bool, }, /// Explicit `'static` lifetime. Static, diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 18ffc227fed86..7a0bf265fafdb 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -90,9 +90,6 @@ pub enum LifetimeName { /// User-given names or fresh (synthetic) names. Param(LocalDefId, ParamName), - /// User wrote nothing (e.g., the lifetime in `&u32`). - Implicit, - /// Implicit lifetime in a context like `dyn Foo`. This is /// distinguished from implicit lifetimes elsewhere because the /// lifetime that they default to must appear elsewhere within the @@ -110,7 +107,7 @@ pub enum LifetimeName { /// that was already reported. Error, - /// User wrote specifies `'_`. + /// User wrote an anonymous lifetime, either `'_` or nothing. Underscore, /// User wrote `'static`. @@ -120,9 +117,7 @@ pub enum LifetimeName { impl LifetimeName { pub fn ident(&self) -> Ident { match *self { - LifetimeName::ImplicitObjectLifetimeDefault - | LifetimeName::Implicit - | LifetimeName::Error => Ident::empty(), + LifetimeName::ImplicitObjectLifetimeDefault | LifetimeName::Error => Ident::empty(), LifetimeName::Underscore => Ident::with_dummy_span(kw::UnderscoreLifetime), LifetimeName::Static => Ident::with_dummy_span(kw::StaticLifetime), LifetimeName::Param(_, param_name) => param_name.ident(), @@ -132,7 +127,6 @@ impl LifetimeName { pub fn is_anonymous(&self) -> bool { match *self { LifetimeName::ImplicitObjectLifetimeDefault - | LifetimeName::Implicit | LifetimeName::Underscore | LifetimeName::Param(_, ParamName::Fresh) | LifetimeName::Error => true, @@ -142,9 +136,7 @@ impl LifetimeName { pub fn is_elided(&self) -> bool { match self { - LifetimeName::ImplicitObjectLifetimeDefault - | LifetimeName::Implicit - | LifetimeName::Underscore => true, + LifetimeName::ImplicitObjectLifetimeDefault | LifetimeName::Underscore => true, // It might seem surprising that `Fresh` counts as // *not* elided -- but this is because, as far as the code diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index d00b65da7e6a5..b1d8c616fb81d 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -496,7 +496,6 @@ pub fn walk_lifetime<'v, V: Visitor<'v>>(visitor: &mut V, lifetime: &'v Lifetime | LifetimeName::Param(_, ParamName::Error) | LifetimeName::Static | LifetimeName::Error - | LifetimeName::Implicit | LifetimeName::ImplicitObjectLifetimeDefault | LifetimeName::Underscore => {} } diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index bc5668bcec00f..a12495c51ad7c 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -262,9 +262,6 @@ enum LifetimeRibKind { /// error on default object bounds (e.g., `Box`). AnonymousReportError, - /// Pass responsibility to `resolve_lifetime` code for all cases. - AnonymousPassThrough(NodeId), - /// Replace all anonymous lifetimes by provided lifetime. Elided(LifetimeRes), @@ -868,7 +865,7 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { let previous_state = replace(&mut this.in_func_body, true); // Resolve the function body, potentially inside the body of an async closure this.with_lifetime_rib( - LifetimeRibKind::AnonymousPassThrough(fn_id), + LifetimeRibKind::Elided(LifetimeRes::Anonymous { binder: fn_id }), |this| this.visit_block(body), ); @@ -896,7 +893,9 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { this.with_lifetime_rib( match binder { ClosureBinder::NotPresent => { - LifetimeRibKind::AnonymousPassThrough(fn_id) + LifetimeRibKind::Elided(LifetimeRes::Anonymous { + binder: fn_id, + }) } ClosureBinder::For { .. } => LifetimeRibKind::AnonymousReportError, }, @@ -908,7 +907,7 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { let previous_state = replace(&mut this.in_func_body, true); // Resolve the function body, potentially inside the body of an async closure this.with_lifetime_rib( - LifetimeRibKind::AnonymousPassThrough(fn_id), + LifetimeRibKind::Elided(LifetimeRes::Anonymous { binder: fn_id }), |this| this.visit_expr(body), ); @@ -1053,8 +1052,7 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { visit::walk_generic_args(self, path_span, args); break; } - LifetimeRibKind::AnonymousPassThrough(..) - | LifetimeRibKind::AnonymousCreateParameter { .. } + LifetimeRibKind::AnonymousCreateParameter { .. } | LifetimeRibKind::AnonymousReportError | LifetimeRibKind::Elided(_) | LifetimeRibKind::ElisionFailure @@ -1415,8 +1413,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { | LifetimeRibKind::AnonymousReportError | LifetimeRibKind::ElisionFailure => Some(LifetimeUseSet::Many), // An anonymous lifetime is legal here, go ahead. - LifetimeRibKind::AnonymousPassThrough(_) - | LifetimeRibKind::AnonymousCreateParameter { .. } => { + LifetimeRibKind::AnonymousCreateParameter { .. } => { Some(LifetimeUseSet::One { use_span: ident.span, use_ctxt }) } // Only report if eliding the lifetime would have the same @@ -1527,14 +1524,6 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { self.record_lifetime_res(lifetime.id, LifetimeRes::Error, elision_candidate); return; } - LifetimeRibKind::AnonymousPassThrough(node_id) => { - self.record_lifetime_res( - lifetime.id, - LifetimeRes::Anonymous { binder: node_id, elided }, - elision_candidate, - ); - return; - } LifetimeRibKind::Elided(res) => { self.record_lifetime_res(lifetime.id, res, elision_candidate); return; @@ -1658,8 +1647,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { // Do not create a parameter for patterns and expressions. for rib in self.lifetime_ribs.iter().rev() { match rib.kind { - LifetimeRibKind::AnonymousPassThrough(binder) => { - let res = LifetimeRes::Anonymous { binder, elided: true }; + LifetimeRibKind::Elided(res @ LifetimeRes::Anonymous { .. }) => { for id in node_ids { self.record_lifetime_res(id, res, LifetimeElisionCandidate::Named); } @@ -1673,8 +1661,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { // FIXME(cjgillot) This resolution is wrong, but this does not matter // since these cases are erroneous anyway. Lifetime resolution should // emit a "missing lifetime specifier" diagnostic. - let res = - LifetimeRes::Anonymous { binder: DUMMY_NODE_ID, elided: true }; + let res = LifetimeRes::Anonymous { binder: DUMMY_NODE_ID }; for id in node_ids { self.record_lifetime_res(id, res, LifetimeElisionCandidate::Named); } @@ -1753,19 +1740,6 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { } break; } - // `PassThrough` is the normal case. - LifetimeRibKind::AnonymousPassThrough(binder) => { - let res = LifetimeRes::Anonymous { binder, elided: true }; - let mut candidate = LifetimeElisionCandidate::Missing(missing_lifetime); - for id in node_ids { - self.record_lifetime_res( - id, - res, - replace(&mut candidate, LifetimeElisionCandidate::Ignore), - ); - } - break; - } LifetimeRibKind::Elided(res) => { let mut candidate = LifetimeElisionCandidate::Missing(missing_lifetime); for id in node_ids { @@ -2272,7 +2246,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { this.visit_ty(ty); }); this.with_lifetime_rib( - LifetimeRibKind::AnonymousPassThrough(item.id), + LifetimeRibKind::Elided(LifetimeRes::Anonymous { binder: item.id }), |this| { if let Some(expr) = expr { let constant_item_kind = match item.kind { @@ -2547,7 +2521,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { // Type parameters can already be used and as associated consts are // not used as part of the type system, this is far less surprising. self.with_lifetime_rib( - LifetimeRibKind::AnonymousPassThrough(item.id), + LifetimeRibKind::Elided(LifetimeRes::Anonymous { binder: item.id }), |this| { this.with_constant_rib( IsRepeatExpr::No, @@ -2721,7 +2695,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { // Type parameters can already be used and as associated consts are // not used as part of the type system, this is far less surprising. self.with_lifetime_rib( - LifetimeRibKind::AnonymousPassThrough(item.id), + LifetimeRibKind::Elided(LifetimeRes::Anonymous { binder: item.id }), |this| { this.with_constant_rib( IsRepeatExpr::No, diff --git a/compiler/rustc_resolve/src/late/lifetimes.rs b/compiler/rustc_resolve/src/late/lifetimes.rs index a7fd7c427c759..e9cbbedc7b0b2 100644 --- a/compiler/rustc_resolve/src/late/lifetimes.rs +++ b/compiler/rustc_resolve/src/late/lifetimes.rs @@ -819,7 +819,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { // `Box`. self.resolve_object_lifetime_default(lifetime) } - LifetimeName::Implicit | LifetimeName::Underscore => { + LifetimeName::Underscore => { // If the user writes `'_`, we use the *ordinary* elision // rules. So the `'_` in e.g., `Box` will be // resolved the same as the `'_` in `&'_ Foo`. @@ -1135,9 +1135,9 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { #[tracing::instrument(level = "debug", skip(self))] fn visit_lifetime(&mut self, lifetime_ref: &'tcx hir::Lifetime) { match lifetime_ref.name { - hir::LifetimeName::ImplicitObjectLifetimeDefault - | hir::LifetimeName::Implicit - | hir::LifetimeName::Underscore => self.resolve_elided_lifetimes(&[lifetime_ref]), + hir::LifetimeName::ImplicitObjectLifetimeDefault | hir::LifetimeName::Underscore => { + self.resolve_elided_lifetimes(&[lifetime_ref]) + } hir::LifetimeName::Static => self.insert_lifetime(lifetime_ref, Region::Static), hir::LifetimeName::Param(param_def_id, _) => { self.resolve_lifetime_ref(param_def_id, lifetime_ref) From 10be0dd8dfc46eda4dc4d1555df31de2e8b7551a Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Mon, 13 Jun 2022 08:22:06 +0200 Subject: [PATCH 08/24] Replace LifetimeRes::Anonymous by LifetimeRes::Infer. --- compiler/rustc_ast_lowering/src/lib.rs | 20 +--- .../src/diagnostics/region_name.rs | 2 +- compiler/rustc_hir/src/def.rs | 7 +- compiler/rustc_hir/src/hir.rs | 9 +- compiler/rustc_hir/src/intravisit.rs | 2 +- compiler/rustc_resolve/src/late.rs | 108 ++++++------------ compiler/rustc_resolve/src/late/lifetimes.rs | 4 +- .../clippy_lints/src/manual_async_fn.rs | 2 +- src/tools/clippy/clippy_lints/src/ptr.rs | 2 +- 9 files changed, 51 insertions(+), 105 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index a031480611641..a1bf0f94964bb 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -1883,25 +1883,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } hir::LifetimeName::Param(param, ParamName::Fresh) } - LifetimeRes::Anonymous { binder } => { - let mut l_name = None; - if let Some(mut captured_lifetimes) = self.captured_lifetimes.take() { - if !captured_lifetimes.binders_to_ignore.contains(&binder) { - let p_id = self.next_node_id(); - let p_def_id = self.create_def( - captured_lifetimes.parent_def_id, - p_id, - DefPathData::LifetimeNs(kw::UnderscoreLifetime), - ); - captured_lifetimes - .captures - .insert(p_def_id, (span, p_id, ParamName::Fresh, res)); - l_name = Some(hir::LifetimeName::Param(p_def_id, ParamName::Fresh)); - } - self.captured_lifetimes = Some(captured_lifetimes); - }; - l_name.unwrap_or(hir::LifetimeName::Underscore) - } + LifetimeRes::Infer => hir::LifetimeName::Infer, LifetimeRes::Static => hir::LifetimeName::Static, LifetimeRes::Error => hir::LifetimeName::Error, res => panic!("Unexpected lifetime resolution {:?} for {:?} at {:?}", res, ident, span), diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs index 8ba76979c39c6..0662d4d882f6a 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs @@ -589,7 +589,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { hir::LifetimeName::Param(_, hir::ParamName::Fresh) | hir::LifetimeName::ImplicitObjectLifetimeDefault - | hir::LifetimeName::Underscore => { + | hir::LifetimeName::Infer => { // In this case, the user left off the lifetime; so // they wrote something like: // diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs index 2003d70835c6f..be5b7eccbafb2 100644 --- a/compiler/rustc_hir/src/def.rs +++ b/compiler/rustc_hir/src/def.rs @@ -738,11 +738,8 @@ pub enum LifetimeRes { binder: NodeId, }, /// This variant is used for anonymous lifetimes that we did not resolve during - /// late resolution. Shifting the work to the HIR lifetime resolver. - Anonymous { - /// Id of the introducing place. See `Param`. - binder: NodeId, - }, + /// late resolution. Those lifetimes will be inferred by typechecking. + Infer, /// Explicit `'static` lifetime. Static, /// Resolution failure. diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 7a0bf265fafdb..f71400898e60b 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -108,7 +108,8 @@ pub enum LifetimeName { Error, /// User wrote an anonymous lifetime, either `'_` or nothing. - Underscore, + /// The semantics of this lifetime should be inferred by typechecking code. + Infer, /// User wrote `'static`. Static, @@ -118,7 +119,7 @@ impl LifetimeName { pub fn ident(&self) -> Ident { match *self { LifetimeName::ImplicitObjectLifetimeDefault | LifetimeName::Error => Ident::empty(), - LifetimeName::Underscore => Ident::with_dummy_span(kw::UnderscoreLifetime), + LifetimeName::Infer => Ident::with_dummy_span(kw::UnderscoreLifetime), LifetimeName::Static => Ident::with_dummy_span(kw::StaticLifetime), LifetimeName::Param(_, param_name) => param_name.ident(), } @@ -127,7 +128,7 @@ impl LifetimeName { pub fn is_anonymous(&self) -> bool { match *self { LifetimeName::ImplicitObjectLifetimeDefault - | LifetimeName::Underscore + | LifetimeName::Infer | LifetimeName::Param(_, ParamName::Fresh) | LifetimeName::Error => true, LifetimeName::Static | LifetimeName::Param(..) => false, @@ -136,7 +137,7 @@ impl LifetimeName { pub fn is_elided(&self) -> bool { match self { - LifetimeName::ImplicitObjectLifetimeDefault | LifetimeName::Underscore => true, + LifetimeName::ImplicitObjectLifetimeDefault | LifetimeName::Infer => true, // It might seem surprising that `Fresh` counts as // *not* elided -- but this is because, as far as the code diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index b1d8c616fb81d..640974115b926 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -497,7 +497,7 @@ pub fn walk_lifetime<'v, V: Visitor<'v>>(visitor: &mut V, lifetime: &'v Lifetime | LifetimeName::Static | LifetimeName::Error | LifetimeName::ImplicitObjectLifetimeDefault - | LifetimeName::Underscore => {} + | LifetimeName::Infer => {} } } diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index a12495c51ad7c..f5fb2585c748e 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -865,7 +865,7 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { let previous_state = replace(&mut this.in_func_body, true); // Resolve the function body, potentially inside the body of an async closure this.with_lifetime_rib( - LifetimeRibKind::Elided(LifetimeRes::Anonymous { binder: fn_id }), + LifetimeRibKind::Elided(LifetimeRes::Infer), |this| this.visit_block(body), ); @@ -893,9 +893,7 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { this.with_lifetime_rib( match binder { ClosureBinder::NotPresent => { - LifetimeRibKind::Elided(LifetimeRes::Anonymous { - binder: fn_id, - }) + LifetimeRibKind::Elided(LifetimeRes::Infer) } ClosureBinder::For { .. } => LifetimeRibKind::AnonymousReportError, }, @@ -907,7 +905,7 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { let previous_state = replace(&mut this.in_func_body, true); // Resolve the function body, potentially inside the body of an async closure this.with_lifetime_rib( - LifetimeRibKind::Elided(LifetimeRes::Anonymous { binder: fn_id }), + LifetimeRibKind::Elided(LifetimeRes::Infer), |this| this.visit_expr(body), ); @@ -1645,35 +1643,12 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { if !missing { // Do not create a parameter for patterns and expressions. - for rib in self.lifetime_ribs.iter().rev() { - match rib.kind { - LifetimeRibKind::Elided(res @ LifetimeRes::Anonymous { .. }) => { - for id in node_ids { - self.record_lifetime_res(id, res, LifetimeElisionCandidate::Named); - } - break; - } - // `LifetimeRes::Error`, which would usually be used in the case of - // `ReportError`, is unsuitable here, as we don't emit an error yet. Instead, - // we simply resolve to an implicit lifetime, which will be checked later, at - // which point a suitable error will be emitted. - LifetimeRibKind::AnonymousReportError | LifetimeRibKind::Item => { - // FIXME(cjgillot) This resolution is wrong, but this does not matter - // since these cases are erroneous anyway. Lifetime resolution should - // emit a "missing lifetime specifier" diagnostic. - let res = LifetimeRes::Anonymous { binder: DUMMY_NODE_ID }; - for id in node_ids { - self.record_lifetime_res(id, res, LifetimeElisionCandidate::Named); - } - break; - } - LifetimeRibKind::AnonymousCreateParameter { .. } - | LifetimeRibKind::Elided(_) - | LifetimeRibKind::ElisionFailure - | LifetimeRibKind::Generics { .. } - | LifetimeRibKind::ConstGeneric - | LifetimeRibKind::AnonConst => {} - } + for id in node_ids { + self.record_lifetime_res( + id, + LifetimeRes::Infer, + LifetimeElisionCandidate::Named, + ); } continue; } @@ -1814,15 +1789,12 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { ) } match res { - LifetimeRes::Param { .. } - | LifetimeRes::Fresh { .. } - | LifetimeRes::Anonymous { .. } - | LifetimeRes::Static => { + LifetimeRes::Param { .. } | LifetimeRes::Fresh { .. } | LifetimeRes::Static => { if let Some(ref mut candidates) = self.lifetime_elision_candidates { candidates.insert(res, candidate); } } - LifetimeRes::Error | LifetimeRes::ElidedAnchor { .. } => {} + LifetimeRes::Infer | LifetimeRes::Error | LifetimeRes::ElidedAnchor { .. } => {} } } @@ -2245,26 +2217,23 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { this.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Static), |this| { this.visit_ty(ty); }); - this.with_lifetime_rib( - LifetimeRibKind::Elided(LifetimeRes::Anonymous { binder: item.id }), - |this| { - if let Some(expr) = expr { - let constant_item_kind = match item.kind { - ItemKind::Const(..) => ConstantItemKind::Const, - ItemKind::Static(..) => ConstantItemKind::Static, - _ => unreachable!(), - }; - // We already forbid generic params because of the above item rib, - // so it doesn't matter whether this is a trivial constant. - this.with_constant_rib( - IsRepeatExpr::No, - HasGenericParams::Yes, - Some((item.ident, constant_item_kind)), - |this| this.visit_expr(expr), - ); - } - }, - ); + this.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Infer), |this| { + if let Some(expr) = expr { + let constant_item_kind = match item.kind { + ItemKind::Const(..) => ConstantItemKind::Const, + ItemKind::Static(..) => ConstantItemKind::Static, + _ => unreachable!(), + }; + // We already forbid generic params because of the above item rib, + // so it doesn't matter whether this is a trivial constant. + this.with_constant_rib( + IsRepeatExpr::No, + HasGenericParams::Yes, + Some((item.ident, constant_item_kind)), + |this| this.visit_expr(expr), + ); + } + }); }); } @@ -2521,7 +2490,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { // Type parameters can already be used and as associated consts are // not used as part of the type system, this is far less surprising. self.with_lifetime_rib( - LifetimeRibKind::Elided(LifetimeRes::Anonymous { binder: item.id }), + LifetimeRibKind::Elided(LifetimeRes::Infer), |this| { this.with_constant_rib( IsRepeatExpr::No, @@ -2694,17 +2663,14 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { // // Type parameters can already be used and as associated consts are // not used as part of the type system, this is far less surprising. - self.with_lifetime_rib( - LifetimeRibKind::Elided(LifetimeRes::Anonymous { binder: item.id }), - |this| { - this.with_constant_rib( - IsRepeatExpr::No, - HasGenericParams::Yes, - None, - |this| this.visit_expr(expr), - ) - }, - ); + self.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Infer), |this| { + this.with_constant_rib( + IsRepeatExpr::No, + HasGenericParams::Yes, + None, + |this| this.visit_expr(expr), + ) + }); } } AssocItemKind::Fn(box Fn { generics, .. }) => { diff --git a/compiler/rustc_resolve/src/late/lifetimes.rs b/compiler/rustc_resolve/src/late/lifetimes.rs index e9cbbedc7b0b2..51aea02cb7a62 100644 --- a/compiler/rustc_resolve/src/late/lifetimes.rs +++ b/compiler/rustc_resolve/src/late/lifetimes.rs @@ -819,7 +819,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { // `Box`. self.resolve_object_lifetime_default(lifetime) } - LifetimeName::Underscore => { + LifetimeName::Infer => { // If the user writes `'_`, we use the *ordinary* elision // rules. So the `'_` in e.g., `Box` will be // resolved the same as the `'_` in `&'_ Foo`. @@ -1135,7 +1135,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { #[tracing::instrument(level = "debug", skip(self))] fn visit_lifetime(&mut self, lifetime_ref: &'tcx hir::Lifetime) { match lifetime_ref.name { - hir::LifetimeName::ImplicitObjectLifetimeDefault | hir::LifetimeName::Underscore => { + hir::LifetimeName::ImplicitObjectLifetimeDefault | hir::LifetimeName::Infer => { self.resolve_elided_lifetimes(&[lifetime_ref]) } hir::LifetimeName::Static => self.insert_lifetime(lifetime_ref, Region::Static), diff --git a/src/tools/clippy/clippy_lints/src/manual_async_fn.rs b/src/tools/clippy/clippy_lints/src/manual_async_fn.rs index 945880d21471b..a0ca7e6ff1e22 100644 --- a/src/tools/clippy/clippy_lints/src/manual_async_fn.rs +++ b/src/tools/clippy/clippy_lints/src/manual_async_fn.rs @@ -166,7 +166,7 @@ fn captures_all_lifetimes(inputs: &[Ty<'_>], output_lifetimes: &[LifetimeName]) // - There's only one output lifetime bound using `+ '_` // - All input lifetimes are explicitly bound to the output input_lifetimes.is_empty() - || (output_lifetimes.len() == 1 && matches!(output_lifetimes[0], LifetimeName::Underscore)) + || (output_lifetimes.len() == 1 && matches!(output_lifetimes[0], LifetimeName::Infer)) || input_lifetimes .iter() .all(|in_lt| output_lifetimes.iter().any(|out_lt| in_lt == out_lt)) diff --git a/src/tools/clippy/clippy_lints/src/ptr.rs b/src/tools/clippy/clippy_lints/src/ptr.rs index 8534d8a29f10d..3c5ea2d94144f 100644 --- a/src/tools/clippy/clippy_lints/src/ptr.rs +++ b/src/tools/clippy/clippy_lints/src/ptr.rs @@ -351,7 +351,7 @@ impl fmt::Display for RefPrefix { name.fmt(f)?; f.write_char(' ')?; }, - LifetimeName::Underscore => f.write_str("'_ ")?, + LifetimeName::Infer => f.write_str("'_ ")?, LifetimeName::Static => f.write_str("'static ")?, _ => (), } From ae70e366f3b77c127f6cb799fcaab4de96974d5c Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Wed, 6 Jul 2022 18:27:43 +0200 Subject: [PATCH 09/24] Check that we do not ICE when anonymous lifetimes appear in AnonConst. Fixes #98932. --- .../constant-used-as-arraylen.rs | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 src/test/ui/impl-header-lifetime-elision/constant-used-as-arraylen.rs diff --git a/src/test/ui/impl-header-lifetime-elision/constant-used-as-arraylen.rs b/src/test/ui/impl-header-lifetime-elision/constant-used-as-arraylen.rs new file mode 100644 index 0000000000000..929b82bfc432e --- /dev/null +++ b/src/test/ui/impl-header-lifetime-elision/constant-used-as-arraylen.rs @@ -0,0 +1,24 @@ +// check-pass +// Verify that we do not ICE when anonymous lifetimes appear inside an AnonConst. + +pub struct EntriesBuffer(Box<[[u8; HashesEntry::LEN]; 5]>); + +impl EntriesBuffer { + pub fn iter_child_buffers(&mut self) -> impl Iterator { + self.0.iter_mut() + } + + pub fn iter_child_buffers_explicit( + &mut self, + ) -> impl Iterator::LEN]> { + self.0.iter_mut() + } +} + +pub struct HashesEntry<'a>(&'a [u8]); + +impl HashesEntry<'_> { + pub const LEN: usize = 1; +} + +fn main() {} From 267d3620a59c66a58d4d53f77d3f31e44f0f07fe Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sun, 22 May 2022 23:31:39 +0200 Subject: [PATCH 10/24] Move fn parameter ribs outwards. --- compiler/rustc_resolve/src/late.rs | 98 +++++++++++++++++++----------- 1 file changed, 62 insertions(+), 36 deletions(-) diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index f5fb2585c748e..ed65100ae7751 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -695,14 +695,25 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { }, |this| { this.visit_generic_params(&bare_fn.generic_params, false); - this.resolve_fn_signature( - ty.id, - None, - false, - // We don't need to deal with patterns in parameters, because - // they are not possible for foreign or bodiless functions. - bare_fn.decl.inputs.iter().map(|Param { ty, .. }| (None, &**ty)), - &bare_fn.decl.output, + this.with_lifetime_rib( + LifetimeRibKind::AnonymousCreateParameter { + binder: ty.id, + report_in_path: false, + }, + |this| { + this.resolve_fn_signature( + ty.id, + false, + // We don't need to deal with patterns in parameters, because + // they are not possible for foreign or bodiless functions. + bare_fn + .decl + .inputs + .iter() + .map(|Param { ty, .. }| (None, &**ty)), + &bare_fn.decl.output, + ) + }, ); }, ) @@ -782,12 +793,19 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { | FnKind::Fn(_, _, sig, _, generics, None) => { self.visit_fn_header(&sig.header); self.visit_generics(generics); - self.resolve_fn_signature( - fn_id, - None, - sig.decl.has_self(), - sig.decl.inputs.iter().map(|Param { ty, .. }| (None, &**ty)), - &sig.decl.output, + self.with_lifetime_rib( + LifetimeRibKind::AnonymousCreateParameter { + binder: fn_id, + report_in_path: false, + }, + |this| { + this.resolve_fn_signature( + fn_id, + sig.decl.has_self(), + sig.decl.inputs.iter().map(|Param { ty, .. }| (None, &**ty)), + &sig.decl.output, + ) + }, ); return; } @@ -812,15 +830,22 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { let declaration = &sig.decl; let async_node_id = sig.header.asyncness.opt_return_id(); - this.resolve_fn_signature( - fn_id, - async_node_id, - declaration.has_self(), - declaration - .inputs - .iter() - .map(|Param { pat, ty, .. }| (Some(&**pat), &**ty)), - &declaration.output, + this.with_lifetime_rib( + LifetimeRibKind::AnonymousCreateParameter { + binder: fn_id, + report_in_path: async_node_id.is_some(), + }, + |this| { + this.resolve_fn_signature( + fn_id, + declaration.has_self(), + declaration + .inputs + .iter() + .map(|Param { pat, ty, .. }| (Some(&**pat), &**ty)), + &declaration.output, + ) + }, ); // Construct the list of in-scope lifetime parameters for async lowering. @@ -1035,12 +1060,19 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { kind: LifetimeBinderKind::PolyTrait, .. } => { - self.resolve_fn_signature( - binder, - None, - false, - p_args.inputs.iter().map(|ty| (None, &**ty)), - &p_args.output, + self.with_lifetime_rib( + LifetimeRibKind::AnonymousCreateParameter { + binder, + report_in_path: false, + }, + |this| { + this.resolve_fn_signature( + binder, + false, + p_args.inputs.iter().map(|ty| (None, &**ty)), + &p_args.output, + ) + }, ); break; } @@ -1813,18 +1845,12 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { fn resolve_fn_signature( &mut self, fn_id: NodeId, - async_node_id: Option, has_self: bool, inputs: impl Iterator, &'ast Ty)> + Clone, output_ty: &'ast FnRetTy, ) { // Add each argument to the rib. - let parameter_rib = LifetimeRibKind::AnonymousCreateParameter { - binder: fn_id, - report_in_path: async_node_id.is_some(), - }; - let elision_lifetime = - self.with_lifetime_rib(parameter_rib, |this| this.resolve_fn_params(has_self, inputs)); + let elision_lifetime = self.resolve_fn_params(has_self, inputs); debug!(?elision_lifetime); let outer_failures = take(&mut self.diagnostic_metadata.current_elision_failures); From 30565e5871ce59e1277879bcd391adf9f544b39e Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Mon, 23 May 2022 16:27:52 +0200 Subject: [PATCH 11/24] Stop resolving lifetime elision on HIR. --- compiler/rustc_resolve/src/late/lifetimes.rs | 477 ++----------------- 1 file changed, 52 insertions(+), 425 deletions(-) diff --git a/compiler/rustc_resolve/src/late/lifetimes.rs b/compiler/rustc_resolve/src/late/lifetimes.rs index 51aea02cb7a62..c6af81ed507ce 100644 --- a/compiler/rustc_resolve/src/late/lifetimes.rs +++ b/compiler/rustc_resolve/src/late/lifetimes.rs @@ -21,7 +21,7 @@ use rustc_middle::hir::nested_filter; use rustc_middle::middle::resolve_lifetime::*; use rustc_middle::ty::{self, GenericParamDefKind, TyCtxt}; use rustc_span::def_id::DefId; -use rustc_span::symbol::{kw, sym, Ident}; +use rustc_span::symbol::{sym, Ident}; use rustc_span::Span; use std::borrow::Cow; use std::cell::Cell; @@ -215,9 +215,8 @@ enum Scope<'a> { }, /// A scope which either determines unspecified lifetimes or errors - /// on them (e.g., due to ambiguity). For more details, see `Elide`. + /// on them (e.g., due to ambiguity). Elision { - elide: Elide, s: ScopeRef<'a>, }, @@ -290,9 +289,7 @@ impl<'a> fmt::Debug for TruncatedScopeDebug<'a> { Scope::Body { id, s: _ } => { f.debug_struct("Body").field("id", id).field("s", &"..").finish() } - Scope::Elision { elide, s: _ } => { - f.debug_struct("Elision").field("elide", elide).field("s", &"..").finish() - } + Scope::Elision { s: _ } => f.debug_struct("Elision").field("s", &"..").finish(), Scope::ObjectLifetimeDefault { lifetime, s: _ } => f .debug_struct("ObjectLifetimeDefault") .field("lifetime", lifetime) @@ -309,21 +306,6 @@ impl<'a> fmt::Debug for TruncatedScopeDebug<'a> { } } -#[derive(Clone, Debug)] -enum Elide { - /// Use a fresh anonymous late-bound lifetime each time, by - /// incrementing the counter to generate sequential indices. All - /// anonymous lifetimes must start *after* named bound vars. - FreshLateAnon(u32, Cell), - /// Always use this one lifetime. - Exact(Region), - /// Less or more than one lifetime were found, error on unspecified. - Error, - /// Forbid lifetime elision inside of a larger scope where it would be - /// permitted. For example, in let position impl trait. - Forbid, -} - type ScopeRef<'a> = &'a Scope<'a>; const ROOT_SCOPE: ScopeRef<'static> = &Scope::Root; @@ -664,8 +646,9 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { } hir::ItemKind::Static(..) | hir::ItemKind::Const(..) => { // No lifetime parameters, but implied 'static. - let scope = Scope::Elision { elide: Elide::Exact(Region::Static), s: ROOT_SCOPE }; - self.with(scope, |this| intravisit::walk_item(this, item)); + self.with(Scope::Elision { s: self.scope }, |this| { + intravisit::walk_item(this, item) + }); } hir::ItemKind::OpaqueTy(hir::OpaqueTy { .. }) => { // Opaque types are visited when we visit the @@ -860,7 +843,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { // position impl Trait let scope = Scope::TraitRefBoundary { s: self.scope }; self.with(scope, |this| { - let scope = Scope::Elision { elide: Elide::Forbid, s: this.scope }; + let scope = Scope::Elision { s: this.scope }; this.with(scope, |this| { intravisit::walk_item(this, opaque_ty); }) @@ -936,7 +919,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { let mut index = self.next_early_index_for_opaque_type(); debug!(?index); - let mut elision = None; let mut lifetimes = FxIndexMap::default(); let mut non_lifetime_count = 0; debug!(?generics.params); @@ -945,15 +927,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { GenericParamKind::Lifetime { .. } => { let (def_id, reg) = Region::early(self.tcx.hir(), &mut index, ¶m); lifetimes.insert(def_id, reg); - if let hir::ParamName::Plain(Ident { - name: kw::UnderscoreLifetime, - .. - }) = param.name - { - // Pick the elided lifetime "definition" if one exists - // and use it to make an elision scope. - elision = Some(reg); - } } GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => { non_lifetime_count += 1; @@ -963,51 +936,25 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { let next_early_index = index + non_lifetime_count; self.map.late_bound_vars.insert(ty.hir_id, vec![]); - if let Some(elision_region) = elision { - let scope = - Scope::Elision { elide: Elide::Exact(elision_region), s: self.scope }; - self.with(scope, |this| { - let scope = Scope::Binder { - hir_id: ty.hir_id, - lifetimes, - next_early_index, - s: this.scope, - opaque_type_parent: false, - scope_type: BinderScopeType::Normal, - allow_late_bound: false, - where_bound_origin: None, - }; - this.with(scope, |this| { - this.visit_generics(generics); - let scope = Scope::TraitRefBoundary { s: this.scope }; - this.with(scope, |this| { - for bound in bounds { - this.visit_param_bound(bound); - } - }) - }); - }); - } else { - let scope = Scope::Binder { - hir_id: ty.hir_id, - lifetimes, - next_early_index, - s: self.scope, - opaque_type_parent: false, - scope_type: BinderScopeType::Normal, - allow_late_bound: false, - where_bound_origin: None, - }; - self.with(scope, |this| { - let scope = Scope::TraitRefBoundary { s: this.scope }; - this.with(scope, |this| { - this.visit_generics(generics); - for bound in bounds { - this.visit_param_bound(bound); - } - }) - }); - } + let scope = Scope::Binder { + hir_id: ty.hir_id, + lifetimes, + next_early_index, + s: self.scope, + opaque_type_parent: false, + scope_type: BinderScopeType::Normal, + allow_late_bound: false, + where_bound_origin: None, + }; + self.with(scope, |this| { + let scope = Scope::TraitRefBoundary { s: this.scope }; + this.with(scope, |this| { + this.visit_generics(generics); + for bound in bounds { + this.visit_param_bound(bound); + } + }) + }); } _ => intravisit::walk_ty(self, ty), } @@ -1156,12 +1103,21 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { } } - fn visit_fn_decl(&mut self, fd: &'tcx hir::FnDecl<'tcx>) { + fn visit_fn( + &mut self, + fk: intravisit::FnKind<'tcx>, + fd: &'tcx hir::FnDecl<'tcx>, + body_id: hir::BodyId, + _: Span, + _: hir::HirId, + ) { let output = match fd.output { hir::FnRetTy::DefaultReturn(_) => None, hir::FnRetTy::Return(ref ty) => Some(&**ty), }; - self.visit_fn_like_elision(&fd.inputs, output); + self.visit_fn_like_elision(&fd.inputs, output, matches!(fk, intravisit::FnKind::Closure)); + intravisit::walk_fn_kind(self, fk); + self.visit_nested_body(body_id) } fn visit_generics(&mut self, generics: &'tcx hir::Generics<'tcx>) { @@ -1773,7 +1729,11 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { ); if generic_args.parenthesized { - self.visit_fn_like_elision(generic_args.inputs(), Some(generic_args.bindings[0].ty())); + self.visit_fn_like_elision( + generic_args.inputs(), + Some(generic_args.bindings[0].ty()), + false, + ); return; } @@ -2052,319 +2012,18 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { &mut self, inputs: &'tcx [hir::Ty<'tcx>], output: Option<&'tcx hir::Ty<'tcx>>, + in_closure: bool, ) { - debug!("visit_fn_like_elision: enter"); - let mut scope = &*self.scope; - let hir_id = loop { - match scope { - Scope::Binder { hir_id, allow_late_bound: true, .. } => { - break *hir_id; - } - Scope::ObjectLifetimeDefault { ref s, .. } - | Scope::Elision { ref s, .. } - | Scope::Supertrait { ref s, .. } - | Scope::TraitRefBoundary { ref s, .. } => { - scope = *s; - } - Scope::Root - | Scope::Body { .. } - | Scope::Binder { allow_late_bound: false, .. } => { - // See issues #83907 and #83693. Just bail out from looking inside. - // See the issue #95023 for not allowing late bound - self.tcx.sess.delay_span_bug( - rustc_span::DUMMY_SP, - "In fn_like_elision without appropriate scope above", - ); - return; - } - } - }; - // While not strictly necessary, we gather anon lifetimes *before* actually - // visiting the argument types. - let mut gather = GatherAnonLifetimes { anon_count: 0 }; - for input in inputs { - gather.visit_ty(input); - } - trace!(?gather.anon_count); - let late_bound_vars = self.map.late_bound_vars.entry(hir_id).or_default(); - let named_late_bound_vars = late_bound_vars.len() as u32; - late_bound_vars.extend( - (0..gather.anon_count).map(|var| ty::BoundVariableKind::Region(ty::BrAnon(var))), - ); - let arg_scope = Scope::Elision { - elide: Elide::FreshLateAnon(named_late_bound_vars, Cell::new(0)), - s: self.scope, - }; - self.with(arg_scope, |this| { + self.with(Scope::Elision { s: self.scope }, |this| { for input in inputs { this.visit_ty(input); } - }); - - let Some(output) = output else { return }; - - debug!("determine output"); - - // Figure out if there's a body we can get argument names from, - // and whether there's a `self` argument (treated specially). - let mut assoc_item_kind = None; - let mut impl_self = None; - let parent = self.tcx.hir().get_parent_node(output.hir_id); - match self.tcx.hir().get(parent) { - // `fn` definitions and methods. - Node::Item(&hir::Item { kind: hir::ItemKind::Fn(..), .. }) => {} - - Node::TraitItem(&hir::TraitItem { kind: hir::TraitItemKind::Fn(..), .. }) => { - if let hir::ItemKind::Trait(.., ref trait_items) = - self.tcx.hir().expect_item(self.tcx.hir().get_parent_item(parent)).kind - { - assoc_item_kind = - trait_items.iter().find(|ti| ti.id.hir_id() == parent).map(|ti| ti.kind); - } - } - - Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Fn(_, _), .. }) => { - if let hir::ItemKind::Impl(hir::Impl { ref self_ty, ref items, .. }) = - self.tcx.hir().expect_item(self.tcx.hir().get_parent_item(parent)).kind - { - impl_self = Some(self_ty); - assoc_item_kind = - items.iter().find(|ii| ii.id.hir_id() == parent).map(|ii| ii.kind); - } - } - - // Foreign functions, `fn(...) -> R` and `Trait(...) -> R` (both types and bounds). - Node::ForeignItem(_) | Node::Ty(_) | Node::TraitRef(_) => {}, - - Node::TypeBinding(_) if let Node::TraitRef(_) = self.tcx.hir().get(self.tcx.hir().get_parent_node(parent)) => {}, - - // Everything else (only closures?) doesn't - // actually enjoy elision in return types. - _ => { - self.visit_ty(output); - return; - } - }; - - let has_self = match assoc_item_kind { - Some(hir::AssocItemKind::Fn { has_self }) => has_self, - _ => false, - }; - - // In accordance with the rules for lifetime elision, we can determine - // what region to use for elision in the output type in two ways. - // First (determined here), if `self` is by-reference, then the - // implied output region is the region of the self parameter. - if has_self { - struct SelfVisitor<'a> { - map: &'a NamedRegionMap, - impl_self: Option<&'a hir::TyKind<'a>>, - lifetime: Set1, - } - - impl SelfVisitor<'_> { - // Look for `self: &'a Self` - also desugared from `&'a self`, - // and if that matches, use it for elision and return early. - fn is_self_ty(&self, res: Res) -> bool { - if let Res::SelfTy { .. } = res { - return true; - } - - // Can't always rely on literal (or implied) `Self` due - // to the way elision rules were originally specified. - if let Some(&hir::TyKind::Path(hir::QPath::Resolved(None, ref path))) = - self.impl_self - { - match path.res { - // Permit the types that unambiguously always - // result in the same type constructor being used - // (it can't differ between `Self` and `self`). - Res::Def(DefKind::Struct | DefKind::Union | DefKind::Enum, _) - | Res::PrimTy(_) => return res == path.res, - _ => {} - } - } - - false - } - } - - impl<'a> Visitor<'a> for SelfVisitor<'a> { - fn visit_ty(&mut self, ty: &'a hir::Ty<'a>) { - if let hir::TyKind::Rptr(lifetime_ref, ref mt) = ty.kind { - if let hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) = mt.ty.kind - { - if self.is_self_ty(path.res) { - if let Some(lifetime) = self.map.defs.get(&lifetime_ref.hir_id) { - self.lifetime.insert(*lifetime); - } - } - } - } - intravisit::walk_ty(self, ty) - } - } - - let mut visitor = SelfVisitor { - map: self.map, - impl_self: impl_self.map(|ty| &ty.kind), - lifetime: Set1::Empty, - }; - visitor.visit_ty(&inputs[0]); - if let Set1::One(lifetime) = visitor.lifetime { - let scope = Scope::Elision { elide: Elide::Exact(lifetime), s: self.scope }; - self.with(scope, |this| this.visit_ty(output)); - return; - } - } - - // Second, if there was exactly one lifetime (either a substitution or a - // reference) in the arguments, then any anonymous regions in the output - // have that lifetime. - let mut possible_implied_output_region = None; - let mut lifetime_count = 0; - for input in inputs.iter().skip(has_self as usize) { - let mut gather = GatherLifetimes { - map: self.map, - outer_index: ty::INNERMOST, - have_bound_regions: false, - lifetimes: Default::default(), - }; - gather.visit_ty(input); - - lifetime_count += gather.lifetimes.len(); - - if lifetime_count == 1 && gather.lifetimes.len() == 1 { - // there's a chance that the unique lifetime of this - // iteration will be the appropriate lifetime for output - // parameters, so lets store it. - possible_implied_output_region = gather.lifetimes.iter().cloned().next(); - } - } - - let elide = if lifetime_count == 1 { - Elide::Exact(possible_implied_output_region.unwrap()) - } else { - Elide::Error - }; - - debug!(?elide); - - let scope = Scope::Elision { elide, s: self.scope }; - self.with(scope, |this| this.visit_ty(output)); - - struct GatherLifetimes<'a> { - map: &'a NamedRegionMap, - outer_index: ty::DebruijnIndex, - have_bound_regions: bool, - lifetimes: FxHashSet, - } - - impl<'v, 'a> Visitor<'v> for GatherLifetimes<'a> { - fn visit_ty(&mut self, ty: &hir::Ty<'_>) { - if let hir::TyKind::BareFn(_) = ty.kind { - self.outer_index.shift_in(1); - } - match ty.kind { - hir::TyKind::TraitObject(bounds, ref lifetime, _) => { - for bound in bounds { - self.visit_poly_trait_ref(bound, hir::TraitBoundModifier::None); - } - - // Stay on the safe side and don't include the object - // lifetime default (which may not end up being used). - if !lifetime.is_elided() { - self.visit_lifetime(lifetime); - } - } - _ => { - intravisit::walk_ty(self, ty); - } - } - if let hir::TyKind::BareFn(_) = ty.kind { - self.outer_index.shift_out(1); - } - } - - fn visit_generic_param(&mut self, param: &hir::GenericParam<'_>) { - if let hir::GenericParamKind::Lifetime { .. } = param.kind { - // FIXME(eddyb) Do we want this? It only makes a difference - // if this `for<'a>` lifetime parameter is never used. - self.have_bound_regions = true; - } - - intravisit::walk_generic_param(self, param); - } - - fn visit_poly_trait_ref( - &mut self, - trait_ref: &hir::PolyTraitRef<'_>, - modifier: hir::TraitBoundModifier, - ) { - self.outer_index.shift_in(1); - intravisit::walk_poly_trait_ref(self, trait_ref, modifier); - self.outer_index.shift_out(1); - } - - fn visit_param_bound(&mut self, bound: &hir::GenericBound<'_>) { - if let hir::GenericBound::LangItemTrait { .. } = bound { - self.outer_index.shift_in(1); - intravisit::walk_param_bound(self, bound); - self.outer_index.shift_out(1); - } else { - intravisit::walk_param_bound(self, bound); - } - } - - fn visit_lifetime(&mut self, lifetime_ref: &hir::Lifetime) { - if let Some(&lifetime) = self.map.defs.get(&lifetime_ref.hir_id) { - match lifetime { - Region::LateBound(debruijn, _, _) - | Region::LateBoundAnon(debruijn, _, _) - if debruijn < self.outer_index => - { - self.have_bound_regions = true; - } - _ => { - // FIXME(jackh726): nested trait refs? - self.lifetimes.insert(lifetime.shifted_out_to_binder(self.outer_index)); - } - } - } - } - } - - struct GatherAnonLifetimes { - anon_count: u32, - } - impl<'v> Visitor<'v> for GatherAnonLifetimes { - #[instrument(skip(self), level = "trace")] - fn visit_ty(&mut self, ty: &hir::Ty<'_>) { - // If we enter a `BareFn`, then we enter a *new* binding scope - if let hir::TyKind::BareFn(_) = ty.kind { - return; - } - intravisit::walk_ty(self, ty); - } - - fn visit_generic_args( - &mut self, - path_span: Span, - generic_args: &'v hir::GenericArgs<'v>, - ) { - // parenthesized args enter a new elision scope - if generic_args.parenthesized { - return; - } - intravisit::walk_generic_args(self, path_span, generic_args) - } - - #[instrument(skip(self), level = "trace")] - fn visit_lifetime(&mut self, lifetime_ref: &hir::Lifetime) { - if lifetime_ref.is_elided() { - self.anon_count += 1; - } + if !in_closure && let Some(output) = output { + this.visit_ty(output); } + }); + if in_closure && let Some(output) = output { + self.visit_ty(output); } } @@ -2375,48 +2034,16 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { return; } - let mut late_depth = 0; let mut scope = self.scope; loop { match *scope { // Do not assign any resolution, it will be inferred. Scope::Body { .. } => return, - Scope::Root => break, - - Scope::Binder { s, scope_type, .. } => { - match scope_type { - BinderScopeType::Normal => late_depth += 1, - BinderScopeType::Concatenating => {} - } - scope = s; - } - - Scope::Elision { - elide: Elide::FreshLateAnon(named_late_bound_vars, ref counter), - .. - } => { - for lifetime_ref in lifetime_refs { - let lifetime = - Region::late_anon(named_late_bound_vars, counter).shifted(late_depth); + Scope::Root | Scope::Elision { .. } => break, - self.insert_lifetime(lifetime_ref, lifetime); - } - return; - } - - Scope::Elision { elide: Elide::Exact(l), .. } => { - let lifetime = l.shifted(late_depth); - for lifetime_ref in lifetime_refs { - self.insert_lifetime(lifetime_ref, lifetime); - } - return; - } - - Scope::Elision { elide: Elide::Error, .. } - | Scope::Elision { elide: Elide::Forbid, .. } => break, - - Scope::ObjectLifetimeDefault { s, .. } + Scope::Binder { s, .. } + | Scope::ObjectLifetimeDefault { s, .. } | Scope::Supertrait { s, .. } | Scope::TraitRefBoundary { s, .. } => { scope = s; From ad1b1819eb65e30b8351e14b93bfac8740077242 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Mon, 23 May 2022 17:03:14 +0200 Subject: [PATCH 12/24] Remove resolve_elided_lifetimes. --- compiler/rustc_resolve/src/late/lifetimes.rs | 59 ++------------------ 1 file changed, 6 insertions(+), 53 deletions(-) diff --git a/compiler/rustc_resolve/src/late/lifetimes.rs b/compiler/rustc_resolve/src/late/lifetimes.rs index c6af81ed507ce..7b751c663d24b 100644 --- a/compiler/rustc_resolve/src/late/lifetimes.rs +++ b/compiler/rustc_resolve/src/late/lifetimes.rs @@ -808,7 +808,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { // resolved the same as the `'_` in `&'_ Foo`. // // cc #48468 - self.resolve_elided_lifetimes(&[lifetime]) } LifetimeName::Param(..) | LifetimeName::Static => { // If the user wrote an explicit name, use that. @@ -1082,15 +1081,14 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { #[tracing::instrument(level = "debug", skip(self))] fn visit_lifetime(&mut self, lifetime_ref: &'tcx hir::Lifetime) { match lifetime_ref.name { - hir::LifetimeName::ImplicitObjectLifetimeDefault | hir::LifetimeName::Infer => { - self.resolve_elided_lifetimes(&[lifetime_ref]) - } hir::LifetimeName::Static => self.insert_lifetime(lifetime_ref, Region::Static), hir::LifetimeName::Param(param_def_id, _) => { self.resolve_lifetime_ref(param_def_id, lifetime_ref) } // If we've already reported an error, just ignore `lifetime_ref`. hir::LifetimeName::Error => {} + // Those will be resolved by typechecking. + hir::LifetimeName::ImplicitObjectLifetimeDefault | hir::LifetimeName::Infer => {} } } @@ -1737,26 +1735,10 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { return; } - let mut elide_lifetimes = true; - let lifetimes: Vec<_> = generic_args - .args - .iter() - .filter_map(|arg| match arg { - hir::GenericArg::Lifetime(lt) => { - if !lt.is_elided() { - elide_lifetimes = false; - } - Some(lt) - } - _ => None, - }) - .collect(); - // We short-circuit here if all are elided in order to pluralize - // possible errors - if elide_lifetimes { - self.resolve_elided_lifetimes(&lifetimes); - } else { - lifetimes.iter().for_each(|lt| self.visit_lifetime(lt)); + for arg in generic_args.args { + if let hir::GenericArg::Lifetime(lt) = arg { + self.visit_lifetime(lt); + } } // Figure out if this is a type/trait segment, @@ -2027,35 +2009,6 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { } } - fn resolve_elided_lifetimes(&mut self, lifetime_refs: &[&'tcx hir::Lifetime]) { - debug!("resolve_elided_lifetimes(lifetime_refs={:?})", lifetime_refs); - - if lifetime_refs.is_empty() { - return; - } - - let mut scope = self.scope; - loop { - match *scope { - // Do not assign any resolution, it will be inferred. - Scope::Body { .. } => return, - - Scope::Root | Scope::Elision { .. } => break, - - Scope::Binder { s, .. } - | Scope::ObjectLifetimeDefault { s, .. } - | Scope::Supertrait { s, .. } - | Scope::TraitRefBoundary { s, .. } => { - scope = s; - } - } - } - - for lt in lifetime_refs { - self.tcx.sess.delay_span_bug(lt.span, "Missing lifetime specifier"); - } - } - fn resolve_object_lifetime_default(&mut self, lifetime_ref: &'tcx hir::Lifetime) { debug!("resolve_object_lifetime_default(lifetime_ref={:?})", lifetime_ref); let mut late_depth = 0; From 556b02704f338e121d5129c714d774bff67bf35c Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Tue, 24 May 2022 08:23:24 +0200 Subject: [PATCH 13/24] Stop creating anonymous late lifetimes. --- .../nice_region_error/find_anon_type.rs | 31 +--------------- .../src/middle/resolve_lifetime.rs | 1 - compiler/rustc_resolve/src/late/lifetimes.rs | 37 +------------------ compiler/rustc_typeck/src/astconv/mod.rs | 8 ---- compiler/rustc_typeck/src/collect.rs | 12 +----- 5 files changed, 4 insertions(+), 85 deletions(-) diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs index e5ae835e81349..c1b201da69121 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs @@ -100,23 +100,6 @@ impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> { // the lifetime of the TyRptr let hir_id = lifetime.hir_id; match (self.tcx.named_region(hir_id), self.bound_region) { - // Find the index of the anonymous region that was part of the - // error. We will then search the function parameters for a bound - // region at the right depth with the same index - ( - Some(rl::Region::LateBoundAnon(debruijn_index, _, anon_index)), - ty::BrAnon(br_index), - ) => { - debug!( - "LateBoundAnon depth = {:?} anon_index = {:?} br_index={:?}", - debruijn_index, anon_index, br_index - ); - if debruijn_index == self.current_index && anon_index == br_index { - self.found_type = Some(arg); - return; // we can stop visiting now - } - } - // Find the index of the named region that was part of the // error. We will then search the function parameters for a bound // region at the right depth with the same index @@ -151,8 +134,7 @@ impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> { rl::Region::Static | rl::Region::Free(_, _) | rl::Region::EarlyBound(_, _) - | rl::Region::LateBound(_, _, _) - | rl::Region::LateBoundAnon(_, _, _), + | rl::Region::LateBound(_, _, _), ) | None, _, @@ -206,16 +188,6 @@ impl<'tcx> Visitor<'tcx> for TyPathVisitor<'tcx> { fn visit_lifetime(&mut self, lifetime: &hir::Lifetime) { match (self.tcx.named_region(lifetime.hir_id), self.bound_region) { // the lifetime of the TyPath! - ( - Some(rl::Region::LateBoundAnon(debruijn_index, _, anon_index)), - ty::BrAnon(br_index), - ) => { - if debruijn_index == self.current_index && anon_index == br_index { - self.found_it = true; - return; - } - } - (Some(rl::Region::EarlyBound(_, id)), ty::BrNamed(def_id, _)) => { debug!("EarlyBound id={:?} def_id={:?}", id, def_id); if id == def_id { @@ -239,7 +211,6 @@ impl<'tcx> Visitor<'tcx> for TyPathVisitor<'tcx> { rl::Region::Static | rl::Region::EarlyBound(_, _) | rl::Region::LateBound(_, _, _) - | rl::Region::LateBoundAnon(_, _, _) | rl::Region::Free(_, _), ) | None, diff --git a/compiler/rustc_middle/src/middle/resolve_lifetime.rs b/compiler/rustc_middle/src/middle/resolve_lifetime.rs index c71ba7b175313..9b2f445670532 100644 --- a/compiler/rustc_middle/src/middle/resolve_lifetime.rs +++ b/compiler/rustc_middle/src/middle/resolve_lifetime.rs @@ -12,7 +12,6 @@ pub enum Region { Static, EarlyBound(/* index */ u32, /* lifetime decl */ DefId), LateBound(ty::DebruijnIndex, /* late-bound index */ u32, /* lifetime decl */ DefId), - LateBoundAnon(ty::DebruijnIndex, /* late-bound index */ u32, /* anon index */ u32), Free(DefId, /* lifetime decl */ DefId), } diff --git a/compiler/rustc_resolve/src/late/lifetimes.rs b/compiler/rustc_resolve/src/late/lifetimes.rs index 7b751c663d24b..1d83d57fe383c 100644 --- a/compiler/rustc_resolve/src/late/lifetimes.rs +++ b/compiler/rustc_resolve/src/late/lifetimes.rs @@ -24,7 +24,6 @@ use rustc_span::def_id::DefId; use rustc_span::symbol::{sym, Ident}; use rustc_span::Span; use std::borrow::Cow; -use std::cell::Cell; use std::fmt; use std::mem::take; @@ -33,8 +32,6 @@ trait RegionExt { fn late(index: u32, hir_map: Map<'_>, param: &GenericParam<'_>) -> (LocalDefId, Region); - fn late_anon(named_late_bound_vars: u32, index: &Cell) -> Region; - fn id(&self) -> Option; fn shifted(self, amount: u32) -> Region; @@ -65,16 +62,9 @@ impl RegionExt for Region { (def_id, Region::LateBound(depth, idx, def_id.to_def_id())) } - fn late_anon(named_late_bound_vars: u32, index: &Cell) -> Region { - let i = index.get(); - index.set(i + 1); - let depth = ty::INNERMOST; - Region::LateBoundAnon(depth, named_late_bound_vars + i, i) - } - fn id(&self) -> Option { match *self { - Region::Static | Region::LateBoundAnon(..) => None, + Region::Static => None, Region::EarlyBound(_, id) | Region::LateBound(_, _, id) | Region::Free(_, id) => { Some(id) @@ -87,9 +77,6 @@ impl RegionExt for Region { Region::LateBound(debruijn, idx, id) => { Region::LateBound(debruijn.shifted_in(amount), idx, id) } - Region::LateBoundAnon(debruijn, index, anon_index) => { - Region::LateBoundAnon(debruijn.shifted_in(amount), index, anon_index) - } _ => self, } } @@ -99,9 +86,6 @@ impl RegionExt for Region { Region::LateBound(debruijn, index, id) => { Region::LateBound(debruijn.shifted_out_to_binder(binder), index, id) } - Region::LateBoundAnon(debruijn, index, anon_index) => { - Region::LateBoundAnon(debruijn.shifted_out_to_binder(binder), index, anon_index) - } _ => self, } } @@ -193,10 +177,6 @@ enum Scope<'a> { s: ScopeRef<'a>, - /// In some cases not allowing late bounds allows us to avoid ICEs. - /// This is almost ways set to true. - allow_late_bound: bool, - /// If this binder comes from a where clause, specify how it was created. /// This is used to diagnose inaccessible lifetimes in APIT: /// ```ignore (illustrative) @@ -272,7 +252,6 @@ impl<'a> fmt::Debug for TruncatedScopeDebug<'a> { opaque_type_parent, scope_type, hir_id, - allow_late_bound, where_bound_origin, s: _, } => f @@ -282,7 +261,6 @@ impl<'a> fmt::Debug for TruncatedScopeDebug<'a> { .field("opaque_type_parent", opaque_type_parent) .field("scope_type", scope_type) .field("hir_id", hir_id) - .field("allow_late_bound", allow_late_bound) .field("where_bound_origin", where_bound_origin) .field("s", &"..") .finish(), @@ -468,9 +446,6 @@ fn late_region_as_bound_region<'tcx>(tcx: TyCtxt<'tcx>, region: &Region) -> ty:: let name = tcx.hir().name(tcx.hir().local_def_id_to_hir_id(def_id.expect_local())); ty::BoundVariableKind::Region(ty::BrNamed(*def_id, name)) } - Region::LateBoundAnon(_, _, anon_idx) => { - ty::BoundVariableKind::Region(ty::BrAnon(*anon_idx)) - } _ => bug!("{:?} is not a late region", region), } } @@ -605,7 +580,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { next_early_index, opaque_type_parent: false, scope_type: BinderScopeType::Normal, - allow_late_bound: true, where_bound_origin: None, }; @@ -724,7 +698,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { opaque_type_parent: true, scope_type: BinderScopeType::Normal, s: ROOT_SCOPE, - allow_late_bound: false, where_bound_origin: None, }; self.with(scope, |this| { @@ -777,7 +750,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { next_early_index, opaque_type_parent: false, scope_type: BinderScopeType::Normal, - allow_late_bound: true, where_bound_origin: None, }; self.with(scope, |this| { @@ -942,7 +914,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { s: self.scope, opaque_type_parent: false, scope_type: BinderScopeType::Normal, - allow_late_bound: false, where_bound_origin: None, }; self.with(scope, |this| { @@ -997,7 +968,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { s: self.scope, opaque_type_parent: true, scope_type: BinderScopeType::Normal, - allow_late_bound: false, where_bound_origin: None, }; self.with(scope, |this| { @@ -1059,7 +1029,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { s: self.scope, opaque_type_parent: true, scope_type: BinderScopeType::Normal, - allow_late_bound: true, where_bound_origin: None, }; self.with(scope, |this| { @@ -1173,7 +1142,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { next_early_index, opaque_type_parent: false, scope_type: BinderScopeType::Normal, - allow_late_bound: true, where_bound_origin: Some(origin), }; this.with(scope, |this| { @@ -1246,7 +1214,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { next_early_index: self.next_early_index(), opaque_type_parent: false, scope_type, - allow_late_bound: true, where_bound_origin: None, }; self.with(scope, |this| { @@ -1297,7 +1264,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { next_early_index, opaque_type_parent: false, scope_type, - allow_late_bound: true, where_bound_origin: None, }; self.with(scope, |this| { @@ -1551,7 +1517,6 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { s: self.scope, opaque_type_parent: true, scope_type: BinderScopeType::Normal, - allow_late_bound: true, where_bound_origin: None, }; self.with(scope, walk); diff --git a/compiler/rustc_typeck/src/astconv/mod.rs b/compiler/rustc_typeck/src/astconv/mod.rs index 58f4f02052f8b..08e8e6f7d0f40 100644 --- a/compiler/rustc_typeck/src/astconv/mod.rs +++ b/compiler/rustc_typeck/src/astconv/mod.rs @@ -221,14 +221,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { tcx.mk_region(ty::ReLateBound(debruijn, br)) } - Some(rl::Region::LateBoundAnon(debruijn, index, anon_index)) => { - let br = ty::BoundRegion { - var: ty::BoundVar::from_u32(index), - kind: ty::BrAnon(anon_index), - }; - tcx.mk_region(ty::ReLateBound(debruijn, br)) - } - Some(rl::Region::EarlyBound(index, id)) => { let name = lifetime_name(id.expect_local()); tcx.mk_region(ty::ReEarlyBound(ty::EarlyBoundRegion { def_id: id, index, name })) diff --git a/compiler/rustc_typeck/src/collect.rs b/compiler/rustc_typeck/src/collect.rs index 0fcbfa734f8fe..d725c9d28ac1a 100644 --- a/compiler/rustc_typeck/src/collect.rs +++ b/compiler/rustc_typeck/src/collect.rs @@ -1346,16 +1346,8 @@ fn has_late_bound_regions<'tcx>(tcx: TyCtxt<'tcx>, node: Node<'tcx>) -> Option {} - Some( - rl::Region::LateBound(debruijn, _, _) - | rl::Region::LateBoundAnon(debruijn, _, _), - ) if debruijn < self.outer_index => {} - Some( - rl::Region::LateBound(..) - | rl::Region::LateBoundAnon(..) - | rl::Region::Free(..), - ) - | None => { + Some(rl::Region::LateBound(debruijn, _, _)) if debruijn < self.outer_index => {} + Some(rl::Region::LateBound(..) | rl::Region::Free(..)) | None => { self.has_late_bound_regions = Some(lt.span); } } From e2b8f0d55c3594b3bc1797131421f63edcfbe81d Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Mon, 13 Jun 2022 11:53:31 +0200 Subject: [PATCH 14/24] Clippy fallout. --- src/tools/clippy/clippy_lints/src/lifetimes.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/clippy/clippy_lints/src/lifetimes.rs b/src/tools/clippy/clippy_lints/src/lifetimes.rs index 826353aafc069..3f69cc2038839 100644 --- a/src/tools/clippy/clippy_lints/src/lifetimes.rs +++ b/src/tools/clippy/clippy_lints/src/lifetimes.rs @@ -615,7 +615,7 @@ struct BodyLifetimeChecker { impl<'tcx> Visitor<'tcx> for BodyLifetimeChecker { // for lifetimes as parameters of generics fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) { - if lifetime.name.ident().name != kw::Empty && lifetime.name.ident().name != kw::StaticLifetime { + if lifetime.name.ident().name != kw::UnderscoreLifetime && lifetime.name.ident().name != kw::StaticLifetime { self.lifetimes_used_in_body = true; } } From 4b2f06b8a91fde63a744c4b297030893d4a92c92 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sun, 19 Jun 2022 16:35:16 +0200 Subject: [PATCH 15/24] Pacify tidy. --- compiler/rustc_resolve/src/late/lifetimes.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/rustc_resolve/src/late/lifetimes.rs b/compiler/rustc_resolve/src/late/lifetimes.rs index 1d83d57fe383c..94460e33d8b01 100644 --- a/compiler/rustc_resolve/src/late/lifetimes.rs +++ b/compiler/rustc_resolve/src/late/lifetimes.rs @@ -1,4 +1,3 @@ -// ignore-tidy-filelength //! Resolution of early vs late bound lifetimes. //! //! Name resolution for lifetimes is performed on the AST and embedded into HIR. From this From e2e3a887710b281ccc0e4e841c6fd4d5f95d85fc Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Tue, 26 Jul 2022 18:37:00 -0400 Subject: [PATCH 16/24] Explain how *mut [T] helps, and how we rely on the check in split_at_mut --- library/core/src/slice/iter.rs | 75 +++++++++++++++++++++++++--------- 1 file changed, 55 insertions(+), 20 deletions(-) diff --git a/library/core/src/slice/iter.rs b/library/core/src/slice/iter.rs index d085b5166677f..fdbc9fd0b5b19 100644 --- a/library/core/src/slice/iter.rs +++ b/library/core/src/slice/iter.rs @@ -1629,6 +1629,11 @@ unsafe impl<'a, T> TrustedRandomAccessNoCoerce for Chunks<'a, T> { #[stable(feature = "rust1", since = "1.0.0")] #[must_use = "iterators are lazy and do nothing unless consumed"] pub struct ChunksMut<'a, T: 'a> { + // This slice pointer must point at a valid region of T with at least length v.len(). Normally, + // those requirements would mean that we could instead use a &mut [T] here, but we cannot + // because __iterator_get_unchecked needs to return &mut [T], which guarantees certain aliasing + // properties that we cannot uphold if we hold on to the full original &mut [T]. Wrapping a raw + // slice instead lets us hand out non-overlapping &mut [T] subslices of the slice we wrap. v: *mut [T], chunk_size: usize, _marker: PhantomData<&'a mut T>, @@ -1651,10 +1656,10 @@ impl<'a, T> Iterator for ChunksMut<'a, T> { None } else { let sz = cmp::min(self.v.len(), self.chunk_size); - // SAFETY: sz cannot exceed the slice length based on the calculation above + // SAFETY: This type ensures that any split_at_mut on self.v is valid. let (head, tail) = unsafe { self.v.split_at_mut(sz) }; self.v = tail; - // SAFETY: Nothing points to or will point to the contents of this slice + // SAFETY: Nothing else points to or will point to the contents of this slice. Some(unsafe { &mut *head }) } } @@ -1687,12 +1692,12 @@ impl<'a, T> Iterator for ChunksMut<'a, T> { Some(sum) => cmp::min(self.v.len(), sum), None => self.v.len(), }; - // SAFETY: end is inbounds because we compared above against self.v.len() + // SAFETY: This type ensures that any split_at_mut on self.v is valid. let (head, tail) = unsafe { self.v.split_at_mut(end) }; - // SAFETY: start is inbounds because + // SAFETY: This type ensures that any split_at_mut on self.v is valid. let (_, nth) = unsafe { head.split_at_mut(start) }; self.v = tail; - // SAFETY: Nothing points to or will point to the contents of this slice + // SAFETY: Nothing else points to or will point to the contents of this slice. Some(unsafe { &mut *nth }) } } @@ -1703,7 +1708,7 @@ impl<'a, T> Iterator for ChunksMut<'a, T> { None } else { let start = (self.v.len() - 1) / self.chunk_size * self.chunk_size; - // SAFETY: Nothing points to or will point to the contents of this slice + // SAFETY: Nothing else points to or will point to the contents of this slice. Some(unsafe { &mut *self.v.get_unchecked_mut(start..) }) } } @@ -1736,7 +1741,7 @@ impl<'a, T> DoubleEndedIterator for ChunksMut<'a, T> { // SAFETY: Similar to `Chunks::next_back` let (head, tail) = unsafe { self.v.split_at_mut_unchecked(len - sz) }; self.v = head; - // SAFETY: Nothing points to or will point to the contents of this slice + // SAFETY: Nothing else points to or will point to the contents of this slice. Some(unsafe { &mut *tail }) } } @@ -1753,11 +1758,12 @@ impl<'a, T> DoubleEndedIterator for ChunksMut<'a, T> { Some(res) => cmp::min(self.v.len(), res), None => self.v.len(), }; - // SAFETY: end is inbounds because we compared above against self.v.len() + // SAFETY: This type ensures that any split_at_mut on self.v is valid. let (temp, _tail) = unsafe { self.v.split_at_mut(end) }; + // SAFETY: This type ensures that any split_at_mut on self.v is valid. let (head, nth_back) = unsafe { temp.split_at_mut(start) }; self.v = head; - // SAFETY: Nothing points to or will point to the contents of this slice + // SAFETY: Nothing else points to or will point to the contents of this slice. Some(unsafe { &mut *nth_back }) } } @@ -1964,6 +1970,11 @@ unsafe impl<'a, T> TrustedRandomAccessNoCoerce for ChunksExact<'a, T> { #[stable(feature = "chunks_exact", since = "1.31.0")] #[must_use = "iterators are lazy and do nothing unless consumed"] pub struct ChunksExactMut<'a, T: 'a> { + // This slice pointer must point at a valid region of T with at least length v.len(). Normally, + // those requirements would mean that we could instead use a &mut [T] here, but we cannot + // because __iterator_get_unchecked needs to return &mut [T], which guarantees certain aliasing + // properties that we cannot uphold if we hold on to the full original &mut [T]. Wrapping a raw + // slice instead lets us hand out non-overlapping &mut [T] subslices of the slice we wrap. v: *mut [T], rem: &'a mut [T], // The iterator never yields from here, so this can be unique chunk_size: usize, @@ -2002,7 +2013,7 @@ impl<'a, T> Iterator for ChunksExactMut<'a, T> { // SAFETY: self.chunk_size is inbounds because we compared above against self.v.len() let (head, tail) = unsafe { self.v.split_at_mut(self.chunk_size) }; self.v = tail; - // SAFETY: Nothing points to or will point to the contents of this slice + // SAFETY: Nothing else points to or will point to the contents of this slice. Some(unsafe { &mut *head }) } } @@ -2025,6 +2036,7 @@ impl<'a, T> Iterator for ChunksExactMut<'a, T> { self.v = &mut []; None } else { + // SAFETY: This type ensures that any split_at_mut on self.v is valid. let (_, snd) = unsafe { self.v.split_at_mut(start) }; self.v = snd; self.next() @@ -2053,7 +2065,7 @@ impl<'a, T> DoubleEndedIterator for ChunksExactMut<'a, T> { // SAFETY: This subtraction is inbounds because of the check above let (head, tail) = unsafe { self.v.split_at_mut(self.v.len() - self.chunk_size) }; self.v = head; - // SAFETY: Nothing points to or will point to the contents of this slice + // SAFETY: Nothing else points to or will point to the contents of this slice. Some(unsafe { &mut *tail }) } } @@ -2067,10 +2079,12 @@ impl<'a, T> DoubleEndedIterator for ChunksExactMut<'a, T> { } else { let start = (len - 1 - n) * self.chunk_size; let end = start + self.chunk_size; + // SAFETY: This type ensures that any split_at_mut on self.v is valid. let (temp, _tail) = unsafe { mem::replace(&mut self.v, &mut []).split_at_mut(end) }; + // SAFETY: This type ensures that any split_at_mut on self.v is valid. let (head, nth_back) = unsafe { temp.split_at_mut(start) }; self.v = head; - // SAFETY: Nothing points to or will point to the contents of this slice + // SAFETY: Nothing else points to or will point to the contents of this slice. Some(unsafe { &mut *nth_back }) } } @@ -2655,6 +2669,11 @@ unsafe impl<'a, T> TrustedRandomAccessNoCoerce for RChunks<'a, T> { #[stable(feature = "rchunks", since = "1.31.0")] #[must_use = "iterators are lazy and do nothing unless consumed"] pub struct RChunksMut<'a, T: 'a> { + // This slice pointer must point at a valid region of T with at least length v.len(). Normally, + // those requirements would mean that we could instead use a &mut [T] here, but we cannot + // because __iterator_get_unchecked needs to return &mut [T], which guarantees certain aliasing + // properties that we cannot uphold if we hold on to the full original &mut [T]. Wrapping a raw + // slice instead lets us hand out non-overlapping &mut [T] subslices of the slice we wrap. v: *mut [T], chunk_size: usize, _marker: PhantomData<&'a mut T>, @@ -2685,7 +2704,7 @@ impl<'a, T> Iterator for RChunksMut<'a, T> { // `self.v.len()` (e.g. `len`) and `self.chunk_size`. let (head, tail) = unsafe { self.v.split_at_mut_unchecked(len - sz) }; self.v = head; - // SAFETY: Nothing points to or will point to the contents of this slice + // SAFETY: Nothing else points to or will point to the contents of this slice. Some(unsafe { &mut *tail }) } } @@ -2720,10 +2739,14 @@ impl<'a, T> Iterator for RChunksMut<'a, T> { Some(sum) => sum, None => 0, }; + // SAFETY: This type ensures that self.v is a valid pointer with a correct len. + // Therefore the bounds check in split_at_mut guarantess the split point is inbounds. let (head, tail) = unsafe { self.v.split_at_mut(start) }; + // SAFETY: This type ensures that self.v is a valid pointer with a correct len. + // Therefore the bounds check in split_at_mut guarantess the split point is inbounds. let (nth, _) = unsafe { tail.split_at_mut(end - start) }; self.v = head; - // SAFETY: Nothing points to or will point to the contents of this slice + // SAFETY: Nothing else points to or will point to the contents of this slice. Some(unsafe { &mut *nth }) } } @@ -2735,7 +2758,7 @@ impl<'a, T> Iterator for RChunksMut<'a, T> { } else { let rem = self.v.len() % self.chunk_size; let end = if rem == 0 { self.chunk_size } else { rem }; - // SAFETY: Nothing points to or will point to the contents of this slice + // SAFETY: Nothing else points to or will point to the contents of this slice. Some(unsafe { &mut *self.v.get_unchecked_mut(0..end) }) } } @@ -2764,7 +2787,7 @@ impl<'a, T> DoubleEndedIterator for RChunksMut<'a, T> { // SAFETY: Similar to `Chunks::next_back` let (head, tail) = unsafe { self.v.split_at_mut_unchecked(sz) }; self.v = tail; - // SAFETY: Nothing points to or will point to the contents of this slice + // SAFETY: Nothing else points to or will point to the contents of this slice. Some(unsafe { &mut *head }) } } @@ -2780,10 +2803,12 @@ impl<'a, T> DoubleEndedIterator for RChunksMut<'a, T> { let offset_from_end = (len - 1 - n) * self.chunk_size; let end = self.v.len() - offset_from_end; let start = end.saturating_sub(self.chunk_size); + // SAFETY: This type ensures that any split_at_mut on self.v is valid. let (tmp, tail) = unsafe { self.v.split_at_mut(end) }; + // SAFETY: This type ensures that any split_at_mut on self.v is valid. let (_, nth_back) = unsafe { tmp.split_at_mut(start) }; self.v = tail; - // SAFETY: Nothing points to or will point to the contents of this slice + // SAFETY: Nothing else points to or will point to the contents of this slice. Some(unsafe { &mut *nth_back }) } } @@ -2993,6 +3018,11 @@ unsafe impl<'a, T> TrustedRandomAccessNoCoerce for RChunksExact<'a, T> { #[stable(feature = "rchunks", since = "1.31.0")] #[must_use = "iterators are lazy and do nothing unless consumed"] pub struct RChunksExactMut<'a, T: 'a> { + // This slice pointer must point at a valid region of T with at least length v.len(). Normally, + // those requirements would mean that we could instead use a &mut [T] here, but we cannot + // because __iterator_get_unchecked needs to return &mut [T], which guarantees certain aliasing + // properties that we cannot uphold if we hold on to the full original &mut [T]. Wrapping a raw + // slice instead lets us hand out non-overlapping &mut [T] subslices of the slice we wrap. v: *mut [T], rem: &'a mut [T], chunk_size: usize, @@ -3027,9 +3057,10 @@ impl<'a, T> Iterator for RChunksExactMut<'a, T> { None } else { let len = self.v.len(); + // SAFETY: This type ensures that any split_at_mut on self.v is valid. let (head, tail) = unsafe { self.v.split_at_mut(len - self.chunk_size) }; self.v = head; - // SAFETY: Nothing points to or will point to the contents of this slice + // SAFETY: Nothing else points to or will point to the contents of this slice. Some(unsafe { &mut *tail }) } } @@ -3053,6 +3084,7 @@ impl<'a, T> Iterator for RChunksExactMut<'a, T> { None } else { let len = self.v.len(); + // SAFETY: This type ensures that any split_at_mut on self.v is valid. let (fst, _) = unsafe { self.v.split_at_mut(len - end) }; self.v = fst; self.next() @@ -3079,9 +3111,10 @@ impl<'a, T> DoubleEndedIterator for RChunksExactMut<'a, T> { if self.v.len() < self.chunk_size { None } else { + // SAFETY: This type ensures that any split_at_mut on self.v is valid. let (head, tail) = unsafe { self.v.split_at_mut(self.chunk_size) }; self.v = tail; - // SAFETY: Nothing points to or will point to the contents of this slice + // SAFETY: Nothing else points to or will point to the contents of this slice. Some(unsafe { &mut *head }) } } @@ -3098,10 +3131,12 @@ impl<'a, T> DoubleEndedIterator for RChunksExactMut<'a, T> { let offset = (len - n) * self.chunk_size; let start = self.v.len() - offset; let end = start + self.chunk_size; + // SAFETY: This type ensures that any split_at_mut on self.v is valid. let (tmp, tail) = unsafe { self.v.split_at_mut(end) }; + // SAFETY: This type ensures that any split_at_mut on self.v is valid. let (_, nth_back) = unsafe { tmp.split_at_mut(start) }; self.v = tail; - // SAFETY: Nothing points to or will point to the contents of this slice + // SAFETY: Nothing else points to or will point to the contents of this slice. Some(unsafe { &mut *nth_back }) } } From 746afe8952a026c24fd229474f40658cbc9e12c7 Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Tue, 26 Jul 2022 20:47:53 -0400 Subject: [PATCH 17/24] Clarify safety comments --- library/core/src/slice/iter.rs | 106 +++++++++++++++------------------ 1 file changed, 47 insertions(+), 59 deletions(-) diff --git a/library/core/src/slice/iter.rs b/library/core/src/slice/iter.rs index fdbc9fd0b5b19..dea72813365b2 100644 --- a/library/core/src/slice/iter.rs +++ b/library/core/src/slice/iter.rs @@ -1629,11 +1629,12 @@ unsafe impl<'a, T> TrustedRandomAccessNoCoerce for Chunks<'a, T> { #[stable(feature = "rust1", since = "1.0.0")] #[must_use = "iterators are lazy and do nothing unless consumed"] pub struct ChunksMut<'a, T: 'a> { - // This slice pointer must point at a valid region of T with at least length v.len(). Normally, - // those requirements would mean that we could instead use a &mut [T] here, but we cannot - // because __iterator_get_unchecked needs to return &mut [T], which guarantees certain aliasing - // properties that we cannot uphold if we hold on to the full original &mut [T]. Wrapping a raw - // slice instead lets us hand out non-overlapping &mut [T] subslices of the slice we wrap. + /// # Safety + /// This slice pointer must point at a valid region of `T` with at least length `v.len()`. Normally, + /// those requirements would mean that we could instead use a `&mut [T]` here, but we cannot + /// because `__iterator_get_unchecked` needs to return `&mut [T]`, which guarantees certain aliasing + /// properties that we cannot uphold if we hold on to the full original `&mut [T]`. Wrapping a raw + /// slice instead lets us hand out non-overlapping `&mut [T]` subslices of the slice we wrap. v: *mut [T], chunk_size: usize, _marker: PhantomData<&'a mut T>, @@ -1656,7 +1657,7 @@ impl<'a, T> Iterator for ChunksMut<'a, T> { None } else { let sz = cmp::min(self.v.len(), self.chunk_size); - // SAFETY: This type ensures that any split_at_mut on self.v is valid. + // SAFETY: The self.v contract ensures that any split_at_mut is valid. let (head, tail) = unsafe { self.v.split_at_mut(sz) }; self.v = tail; // SAFETY: Nothing else points to or will point to the contents of this slice. @@ -1692,9 +1693,9 @@ impl<'a, T> Iterator for ChunksMut<'a, T> { Some(sum) => cmp::min(self.v.len(), sum), None => self.v.len(), }; - // SAFETY: This type ensures that any split_at_mut on self.v is valid. + // SAFETY: The self.v contract ensures that any split_at_mut is valid. let (head, tail) = unsafe { self.v.split_at_mut(end) }; - // SAFETY: This type ensures that any split_at_mut on self.v is valid. + // SAFETY: The self.v contract ensures that any split_at_mut is valid. let (_, nth) = unsafe { head.split_at_mut(start) }; self.v = tail; // SAFETY: Nothing else points to or will point to the contents of this slice. @@ -1715,7 +1716,7 @@ impl<'a, T> Iterator for ChunksMut<'a, T> { unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item { let start = idx * self.chunk_size; - // SAFETY: see comments for `Chunks::__iterator_get_unchecked`. + // SAFETY: see comments for `Chunks::__iterator_get_unchecked` and `self.v`. // // Also note that the caller also guarantees that we're never called // with the same index again, and that no other methods that will @@ -1758,9 +1759,9 @@ impl<'a, T> DoubleEndedIterator for ChunksMut<'a, T> { Some(res) => cmp::min(self.v.len(), res), None => self.v.len(), }; - // SAFETY: This type ensures that any split_at_mut on self.v is valid. + // SAFETY: The self.v contract ensures that any split_at_mut is valid. let (temp, _tail) = unsafe { self.v.split_at_mut(end) }; - // SAFETY: This type ensures that any split_at_mut on self.v is valid. + // SAFETY: The self.v contract ensures that any split_at_mut is valid. let (head, nth_back) = unsafe { temp.split_at_mut(start) }; self.v = head; // SAFETY: Nothing else points to or will point to the contents of this slice. @@ -1970,11 +1971,12 @@ unsafe impl<'a, T> TrustedRandomAccessNoCoerce for ChunksExact<'a, T> { #[stable(feature = "chunks_exact", since = "1.31.0")] #[must_use = "iterators are lazy and do nothing unless consumed"] pub struct ChunksExactMut<'a, T: 'a> { - // This slice pointer must point at a valid region of T with at least length v.len(). Normally, - // those requirements would mean that we could instead use a &mut [T] here, but we cannot - // because __iterator_get_unchecked needs to return &mut [T], which guarantees certain aliasing - // properties that we cannot uphold if we hold on to the full original &mut [T]. Wrapping a raw - // slice instead lets us hand out non-overlapping &mut [T] subslices of the slice we wrap. + /// # Safety + /// This slice pointer must point at a valid region of `T` with at least length `v.len()`. Normally, + /// those requirements would mean that we could instead use a `&mut [T]` here, but we cannot + /// because `__iterator_get_unchecked` needs to return `&mut [T]`, which guarantees certain aliasing + /// properties that we cannot uphold if we hold on to the full original `&mut [T]`. Wrapping a raw + /// slice instead lets us hand out non-overlapping `&mut [T]` subslices of the slice we wrap. v: *mut [T], rem: &'a mut [T], // The iterator never yields from here, so this can be unique chunk_size: usize, @@ -2036,7 +2038,7 @@ impl<'a, T> Iterator for ChunksExactMut<'a, T> { self.v = &mut []; None } else { - // SAFETY: This type ensures that any split_at_mut on self.v is valid. + // SAFETY: The self.v contract ensures that any split_at_mut is valid. let (_, snd) = unsafe { self.v.split_at_mut(start) }; self.v = snd; self.next() @@ -2050,7 +2052,7 @@ impl<'a, T> Iterator for ChunksExactMut<'a, T> { unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item { let start = idx * self.chunk_size; - // SAFETY: see comments for `ChunksMut::__iterator_get_unchecked`. + // SAFETY: see comments for `Chunks::__iterator_get_unchecked` and `self.v`. unsafe { from_raw_parts_mut(self.v.as_mut_ptr().add(start), self.chunk_size) } } } @@ -2079,9 +2081,9 @@ impl<'a, T> DoubleEndedIterator for ChunksExactMut<'a, T> { } else { let start = (len - 1 - n) * self.chunk_size; let end = start + self.chunk_size; - // SAFETY: This type ensures that any split_at_mut on self.v is valid. + // SAFETY: The self.v contract ensures that any split_at_mut is valid. let (temp, _tail) = unsafe { mem::replace(&mut self.v, &mut []).split_at_mut(end) }; - // SAFETY: This type ensures that any split_at_mut on self.v is valid. + // SAFETY: The self.v contract ensures that any split_at_mut is valid. let (head, nth_back) = unsafe { temp.split_at_mut(start) }; self.v = head; // SAFETY: Nothing else points to or will point to the contents of this slice. @@ -2669,11 +2671,12 @@ unsafe impl<'a, T> TrustedRandomAccessNoCoerce for RChunks<'a, T> { #[stable(feature = "rchunks", since = "1.31.0")] #[must_use = "iterators are lazy and do nothing unless consumed"] pub struct RChunksMut<'a, T: 'a> { - // This slice pointer must point at a valid region of T with at least length v.len(). Normally, - // those requirements would mean that we could instead use a &mut [T] here, but we cannot - // because __iterator_get_unchecked needs to return &mut [T], which guarantees certain aliasing - // properties that we cannot uphold if we hold on to the full original &mut [T]. Wrapping a raw - // slice instead lets us hand out non-overlapping &mut [T] subslices of the slice we wrap. + /// # Safety + /// This slice pointer must point at a valid region of `T` with at least length `v.len()`. Normally, + /// those requirements would mean that we could instead use a `&mut [T]` here, but we cannot + /// because `__iterator_get_unchecked` needs to return `&mut [T]`, which guarantees certain aliasing + /// properties that we cannot uphold if we hold on to the full original `&mut [T]`. Wrapping a raw + /// slice instead lets us hand out non-overlapping `&mut [T]` subslices of the slice we wrap. v: *mut [T], chunk_size: usize, _marker: PhantomData<&'a mut T>, @@ -2770,7 +2773,7 @@ impl<'a, T> Iterator for RChunksMut<'a, T> { Some(start) => start, }; // SAFETY: see comments for `RChunks::__iterator_get_unchecked` and - // `ChunksMut::__iterator_get_unchecked` + // `ChunksMut::__iterator_get_unchecked`, `self.v`. unsafe { from_raw_parts_mut(self.v.as_mut_ptr().add(start), end - start) } } } @@ -2803,9 +2806,9 @@ impl<'a, T> DoubleEndedIterator for RChunksMut<'a, T> { let offset_from_end = (len - 1 - n) * self.chunk_size; let end = self.v.len() - offset_from_end; let start = end.saturating_sub(self.chunk_size); - // SAFETY: This type ensures that any split_at_mut on self.v is valid. + // SAFETY: The self.v contract ensures that any split_at_mut is valid. let (tmp, tail) = unsafe { self.v.split_at_mut(end) }; - // SAFETY: This type ensures that any split_at_mut on self.v is valid. + // SAFETY: The self.v contract ensures that any split_at_mut is valid. let (_, nth_back) = unsafe { tmp.split_at_mut(start) }; self.v = tail; // SAFETY: Nothing else points to or will point to the contents of this slice. @@ -3018,11 +3021,12 @@ unsafe impl<'a, T> TrustedRandomAccessNoCoerce for RChunksExact<'a, T> { #[stable(feature = "rchunks", since = "1.31.0")] #[must_use = "iterators are lazy and do nothing unless consumed"] pub struct RChunksExactMut<'a, T: 'a> { - // This slice pointer must point at a valid region of T with at least length v.len(). Normally, - // those requirements would mean that we could instead use a &mut [T] here, but we cannot - // because __iterator_get_unchecked needs to return &mut [T], which guarantees certain aliasing - // properties that we cannot uphold if we hold on to the full original &mut [T]. Wrapping a raw - // slice instead lets us hand out non-overlapping &mut [T] subslices of the slice we wrap. + /// # Safety + /// This slice pointer must point at a valid region of `T` with at least length `v.len()`. Normally, + /// those requirements would mean that we could instead use a `&mut [T]` here, but we cannot + /// because `__iterator_get_unchecked` needs to return `&mut [T]`, which guarantees certain aliasing + /// properties that we cannot uphold if we hold on to the full original `&mut [T]`. Wrapping a raw + /// slice instead lets us hand out non-overlapping `&mut [T]` subslices of the slice we wrap. v: *mut [T], rem: &'a mut [T], chunk_size: usize, @@ -3057,7 +3061,7 @@ impl<'a, T> Iterator for RChunksExactMut<'a, T> { None } else { let len = self.v.len(); - // SAFETY: This type ensures that any split_at_mut on self.v is valid. + // SAFETY: The self.v contract ensures that any split_at_mut is valid. let (head, tail) = unsafe { self.v.split_at_mut(len - self.chunk_size) }; self.v = head; // SAFETY: Nothing else points to or will point to the contents of this slice. @@ -3084,7 +3088,7 @@ impl<'a, T> Iterator for RChunksExactMut<'a, T> { None } else { let len = self.v.len(); - // SAFETY: This type ensures that any split_at_mut on self.v is valid. + // SAFETY: The self.v contract ensures that any split_at_mut is valid. let (fst, _) = unsafe { self.v.split_at_mut(len - end) }; self.v = fst; self.next() @@ -3099,7 +3103,7 @@ impl<'a, T> Iterator for RChunksExactMut<'a, T> { unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item { let end = self.v.len() - idx * self.chunk_size; let start = end - self.chunk_size; - // SAFETY: see comments for `RChunksMut::__iterator_get_unchecked`. + // SAFETY: see comments for `RChunksMut::__iterator_get_unchecked` and `self.v`. unsafe { from_raw_parts_mut(self.v.as_mut_ptr().add(start), self.chunk_size) } } } @@ -3111,7 +3115,7 @@ impl<'a, T> DoubleEndedIterator for RChunksExactMut<'a, T> { if self.v.len() < self.chunk_size { None } else { - // SAFETY: This type ensures that any split_at_mut on self.v is valid. + // SAFETY: The self.v contract ensures that any split_at_mut is valid. let (head, tail) = unsafe { self.v.split_at_mut(self.chunk_size) }; self.v = tail; // SAFETY: Nothing else points to or will point to the contents of this slice. @@ -3131,9 +3135,9 @@ impl<'a, T> DoubleEndedIterator for RChunksExactMut<'a, T> { let offset = (len - n) * self.chunk_size; let start = self.v.len() - offset; let end = start + self.chunk_size; - // SAFETY: This type ensures that any split_at_mut on self.v is valid. + // SAFETY: The self.v contract ensures that any split_at_mut is valid. let (tmp, tail) = unsafe { self.v.split_at_mut(end) }; - // SAFETY: This type ensures that any split_at_mut on self.v is valid. + // SAFETY: The self.v contract ensures that any split_at_mut is valid. let (_, nth_back) = unsafe { tmp.split_at_mut(start) }; self.v = tail; // SAFETY: Nothing else points to or will point to the contents of this slice. @@ -3220,11 +3224,7 @@ where let mut len = 1; let mut iter = self.slice.windows(2); while let Some([l, r]) = iter.next() { - if (self.predicate)(l, r) { - len += 1 - } else { - break; - } + if (self.predicate)(l, r) { len += 1 } else { break } } let (head, tail) = self.slice.split_at(len); self.slice = tail; @@ -3256,11 +3256,7 @@ where let mut len = 1; let mut iter = self.slice.windows(2); while let Some([l, r]) = iter.next_back() { - if (self.predicate)(l, r) { - len += 1 - } else { - break; - } + if (self.predicate)(l, r) { len += 1 } else { break } } let (head, tail) = self.slice.split_at(self.slice.len() - len); self.slice = head; @@ -3315,11 +3311,7 @@ where let mut len = 1; let mut iter = self.slice.windows(2); while let Some([l, r]) = iter.next() { - if (self.predicate)(l, r) { - len += 1 - } else { - break; - } + if (self.predicate)(l, r) { len += 1 } else { break } } let slice = mem::take(&mut self.slice); let (head, tail) = slice.split_at_mut(len); @@ -3352,11 +3344,7 @@ where let mut len = 1; let mut iter = self.slice.windows(2); while let Some([l, r]) = iter.next_back() { - if (self.predicate)(l, r) { - len += 1 - } else { - break; - } + if (self.predicate)(l, r) { len += 1 } else { break } } let slice = mem::take(&mut self.slice); let (head, tail) = slice.split_at_mut(slice.len() - len); From 1b8e4b9391550147683c6cbec91faa95289a5827 Mon Sep 17 00:00:00 2001 From: David Wood Date: Tue, 26 Jul 2022 11:46:01 +0100 Subject: [PATCH 18/24] lint: add comment about diag lints in group Add a brief comment explaining why the diagnostic migration lints aren't included in the `rustc::internal` diagnostic group. Signed-off-by: David Wood --- compiler/rustc_lint/src/lib.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 8726d36498bed..188a91661b674 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -511,6 +511,10 @@ fn register_internals(store: &mut LintStore) { store.register_late_pass(|| Box::new(Diagnostics)); store.register_lints(&PassByValue::get_lints()); store.register_late_pass(|| Box::new(PassByValue)); + // FIXME(davidtwco): deliberately do not include `UNTRANSLATABLE_DIAGNOSTIC` and + // `DIAGNOSTIC_OUTSIDE_OF_IMPL` here because `-Wrustc::internal` is provided to every crate and + // these lints will trigger all of the time - change this once migration to diagnostic structs + // and translation is completed store.register_group( false, "rustc::internal", From e29ab16d96074dde70e01605fdd6a23a56a7bd1d Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Mon, 25 Jul 2022 22:50:16 +0900 Subject: [PATCH 19/24] Add some comments to the docs issue template to clarify Signed-off-by: Yuki Okushi --- .github/ISSUE_TEMPLATE/documentation.md | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/documentation.md b/.github/ISSUE_TEMPLATE/documentation.md index 1d93939e23360..9ccda17a6ef47 100644 --- a/.github/ISSUE_TEMPLATE/documentation.md +++ b/.github/ISSUE_TEMPLATE/documentation.md @@ -4,13 +4,28 @@ about: Create a report for a documentation problem. labels: A-docs --- ### Location ### Summary - From f5e005f0ca0b6fbc427315fa7e6943f4b738e12d Mon Sep 17 00:00:00 2001 From: David Wood Date: Tue, 26 Jul 2022 11:42:27 +0100 Subject: [PATCH 20/24] session: disable internal lints for rustdoc If an internal lint uses `typeck_results` or similar queries then that can result in rustdoc checking code that it shouldn't (e.g. from other platforms) and emit compilation errors. Signed-off-by: David Wood --- compiler/rustc_driver/src/lib.rs | 2 +- compiler/rustc_interface/src/passes.rs | 2 +- compiler/rustc_lint/src/internal.rs | 14 -------------- compiler/rustc_session/src/session.rs | 8 ++++++++ src/librustdoc/lib.rs | 2 +- 5 files changed, 11 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_driver/src/lib.rs b/compiler/rustc_driver/src/lib.rs index f5b059793cf4b..b6e36ee39d551 100644 --- a/compiler/rustc_driver/src/lib.rs +++ b/compiler/rustc_driver/src/lib.rs @@ -249,7 +249,7 @@ fn run_compiler( if sopts.describe_lints { let mut lint_store = rustc_lint::new_lint_store( sopts.unstable_opts.no_interleave_lints, - compiler.session().unstable_options(), + compiler.session().enable_internal_lints(), ); let registered_lints = if let Some(register_lints) = compiler.register_lints() { diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 334a595a88ae6..8f0835917861a 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -210,7 +210,7 @@ pub fn register_plugins<'a>( let mut lint_store = rustc_lint::new_lint_store( sess.opts.unstable_opts.no_interleave_lints, - sess.unstable_options(), + sess.enable_internal_lints(), ); register_lints(sess, &mut lint_store); diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs index 738f475983e9b..f92e842ba014b 100644 --- a/compiler/rustc_lint/src/internal.rs +++ b/compiler/rustc_lint/src/internal.rs @@ -51,20 +51,6 @@ fn typeck_results_of_method_fn<'tcx>( cx: &LateContext<'tcx>, expr: &Expr<'_>, ) -> Option<(Span, DefId, ty::subst::SubstsRef<'tcx>)> { - // FIXME(rustdoc): Lints which use this function use typecheck results which can cause - // `rustdoc` to error if there are resolution failures. - // - // As internal lints are currently always run if there are `unstable_options`, they are added - // to the lint store of rustdoc. Internal lints are also not used via the `lint_mod` query. - // Crate lints run outside of a query so rustdoc currently doesn't disable them. - // - // Instead of relying on this, either change crate lints to a query disabled by rustdoc, only - // run internal lints if the user is explicitly opting in or figure out a different way to - // avoid running lints for rustdoc. - if cx.tcx.sess.opts.actually_rustdoc { - return None; - } - match expr.kind { ExprKind::MethodCall(segment, _, _) if let Some(def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) => diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 854cad79a2040..ac4a6b6da6f40 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -589,6 +589,14 @@ impl Session { pub fn time_passes(&self) -> bool { self.opts.unstable_opts.time_passes || self.opts.unstable_opts.time } + + /// Returns `true` if internal lints should be added to the lint store - i.e. if + /// `-Zunstable-options` is provided and this isn't rustdoc (internal lints can trigger errors + /// to be emitted under rustdoc). + pub fn enable_internal_lints(&self) -> bool { + self.unstable_options() && !self.opts.actually_rustdoc + } + pub fn instrument_mcount(&self) -> bool { self.opts.unstable_opts.instrument_mcount } diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 7429f2b6ab148..3a96884d45d9f 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -788,7 +788,7 @@ fn main_options(options: config::Options) -> MainResult { if sess.opts.describe_lints { let mut lint_store = rustc_lint::new_lint_store( sess.opts.unstable_opts.no_interleave_lints, - sess.unstable_options(), + sess.enable_internal_lints(), ); let registered_lints = if let Some(register_lints) = compiler.register_lints() { register_lints(sess, &mut lint_store); From 7bab769b58db292721ddcb73cc6a7e56cb2b08ab Mon Sep 17 00:00:00 2001 From: David Wood Date: Mon, 25 Jul 2022 13:02:39 +0100 Subject: [PATCH 21/24] lint: add bad opt access internal lint Some command-line options accessible through `sess.opts` are best accessed through wrapper functions on `Session`, `TyCtxt` or otherwise, rather than through field access on the option struct in the `Session`. Adds a new lint which triggers on those options that should be accessed through a wrapper function so that this is prohibited. Options are annotated with a new attribute `rustc_lint_opt_deny_field_access` which can specify the error message (i.e. "use this other function instead") to be emitted. A simpler alternative would be to simply rename the options in the option type so that it is clear they should not be used, however this doesn't prevent uses, just discourages them. Another alternative would be to make the option fields private, and adding accessor functions on the option types, however the wrapper functions sometimes rely on additional state from `Session` or `TyCtxt` which wouldn't be available in an function on the option type, so the accessor would simply make the field available and its use would be discouraged too. Signed-off-by: David Wood --- compiler/rustc_driver/src/lib.rs | 3 +- .../locales/en-US/passes.ftl | 6 + compiler/rustc_feature/src/builtin_attrs.rs | 6 + compiler/rustc_interface/src/interface.rs | 2 + compiler/rustc_interface/src/tests.rs | 1 + compiler/rustc_interface/src/util.rs | 2 + compiler/rustc_lint/src/internal.rs | 35 + compiler/rustc_lint/src/lib.rs | 3 + .../src/lower_slice_len.rs | 2 +- .../rustc_mir_transform/src/reveal_all.rs | 2 +- compiler/rustc_passes/src/check_attr.rs | 33 + compiler/rustc_passes/src/errors.rs | 18 + compiler/rustc_session/src/config.rs | 4 + compiler/rustc_session/src/options.rs | 75 +- compiler/rustc_session/src/session.rs | 659 +++++++++--------- compiler/rustc_span/src/symbol.rs | 2 + .../internal-lints/bad_opt_access.rs | 22 + .../internal-lints/bad_opt_access.stderr | 20 + src/tools/clippy/src/driver.rs | 2 + 19 files changed, 553 insertions(+), 344 deletions(-) create mode 100644 src/test/ui-fulldeps/internal-lints/bad_opt_access.rs create mode 100644 src/test/ui-fulldeps/internal-lints/bad_opt_access.stderr diff --git a/compiler/rustc_driver/src/lib.rs b/compiler/rustc_driver/src/lib.rs index b6e36ee39d551..53ae913f94f12 100644 --- a/compiler/rustc_driver/src/lib.rs +++ b/compiler/rustc_driver/src/lib.rs @@ -123,8 +123,7 @@ impl Callbacks for TimePassesCallbacks { fn config(&mut self, config: &mut interface::Config) { // If a --prints=... option has been given, we don't print the "total" // time because it will mess up the --prints output. See #64339. - self.time_passes = config.opts.prints.is_empty() - && (config.opts.unstable_opts.time_passes || config.opts.unstable_opts.time); + self.time_passes = config.opts.prints.is_empty() && config.opts.time_passes(); config.opts.trimmed_def_paths = TrimmedDefPaths::GoodPath; } } diff --git a/compiler/rustc_error_messages/locales/en-US/passes.ftl b/compiler/rustc_error_messages/locales/en-US/passes.ftl index f95e33cd16a7d..b17eb9c2d260e 100644 --- a/compiler/rustc_error_messages/locales/en-US/passes.ftl +++ b/compiler/rustc_error_messages/locales/en-US/passes.ftl @@ -256,3 +256,9 @@ passes-unused-duplicate = unused attribute passes-unused-multiple = multiple `{$name}` attributes .suggestion = remove this attribute .note = attribute also specified here + +passes-rustc-lint-opt-ty = `#[rustc_lint_opt_ty]` should be applied to a struct + .label = not a struct + +passes-rustc-lint-opt-deny-field-access = `#[rustc_lint_opt_deny_field_access]` should be applied to a field + .label = not a field diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 7b540e67aab3d..0e73d8fd7f600 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -619,6 +619,12 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ // Used by the `rustc::untranslatable_diagnostic` and `rustc::diagnostic_outside_of_impl` lints // to assist in changes to diagnostic APIs. rustc_attr!(rustc_lint_diagnostics, Normal, template!(Word), WarnFollowing, INTERNAL_UNSTABLE), + // Used by the `rustc::bad_opt_access` lint to identify `DebuggingOptions` and `CodegenOptions` + // types (as well as any others in future). + rustc_attr!(rustc_lint_opt_ty, Normal, template!(Word), WarnFollowing, INTERNAL_UNSTABLE), + // Used by the `rustc::bad_opt_access` lint on fields + // types (as well as any others in future). + rustc_attr!(rustc_lint_opt_deny_field_access, Normal, template!(List: "message"), WarnFollowing, INTERNAL_UNSTABLE), // ========================================================================== // Internal attributes, Const related: diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index 6c7ddb4531ef8..94f81b6607798 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -329,6 +329,8 @@ pub fn create_compiler_and_run(config: Config, f: impl FnOnce(&Compiler) -> R }) } +// JUSTIFICATION: before session exists, only config +#[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))] pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Send) -> R { tracing::trace!("run_compiler"); util::run_in_thread_pool_with_globals( diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 0a0eb99cd9266..21d9eaccf67e7 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -1,3 +1,4 @@ +#![cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))] use crate::interface::parse_cfgspecs; use rustc_data_structures::fx::FxHashSet; diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index 97856ecf22c66..4c64e679b9571 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -559,6 +559,8 @@ pub fn collect_crate_types(session: &Session, attrs: &[ast::Attribute]) -> Vec for Diagnostics { } } } + +declare_tool_lint! { + pub rustc::BAD_OPT_ACCESS, + Deny, + "prevent using options by field access when there is a wrapper function", + report_in_external_macro: true +} + +declare_lint_pass!(BadOptAccess => [ BAD_OPT_ACCESS ]); + +impl LateLintPass<'_> for BadOptAccess { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + let ExprKind::Field(base, target) = expr.kind else { return }; + let Some(adt_def) = cx.typeck_results().expr_ty(base).ty_adt_def() else { return }; + // Skip types without `#[rustc_lint_opt_ty]` - only so that the rest of the lint can be + // avoided. + if !cx.tcx.has_attr(adt_def.did(), sym::rustc_lint_opt_ty) { + return; + } + + for field in adt_def.all_fields() { + if field.name == target.name && + let Some(attr) = cx.tcx.get_attr(field.did, sym::rustc_lint_opt_deny_field_access) && + let Some(items) = attr.meta_item_list() && + let Some(item) = items.first() && + let Some(literal) = item.literal() && + let ast::LitKind::Str(val, _) = literal.kind + { + cx.struct_span_lint(BAD_OPT_ACCESS, expr.span, |lint| { + lint.build(val.as_str()).emit(); } + ); + } + } + } +} diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 188a91661b674..7b0702dad75de 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -509,6 +509,8 @@ fn register_internals(store: &mut LintStore) { store.register_late_pass(|| Box::new(TyTyKind)); store.register_lints(&Diagnostics::get_lints()); store.register_late_pass(|| Box::new(Diagnostics)); + store.register_lints(&BadOptAccess::get_lints()); + store.register_late_pass(|| Box::new(BadOptAccess)); store.register_lints(&PassByValue::get_lints()); store.register_late_pass(|| Box::new(PassByValue)); // FIXME(davidtwco): deliberately do not include `UNTRANSLATABLE_DIAGNOSTIC` and @@ -527,6 +529,7 @@ fn register_internals(store: &mut LintStore) { LintId::of(LINT_PASS_IMPL_WITHOUT_MACRO), LintId::of(USAGE_OF_QUALIFIED_TY), LintId::of(EXISTING_DOC_KEYWORD), + LintId::of(BAD_OPT_ACCESS), ], ); } diff --git a/compiler/rustc_mir_transform/src/lower_slice_len.rs b/compiler/rustc_mir_transform/src/lower_slice_len.rs index 47848cfa497f3..2f02d00ec9fb0 100644 --- a/compiler/rustc_mir_transform/src/lower_slice_len.rs +++ b/compiler/rustc_mir_transform/src/lower_slice_len.rs @@ -11,7 +11,7 @@ pub struct LowerSliceLenCalls; impl<'tcx> MirPass<'tcx> for LowerSliceLenCalls { fn is_enabled(&self, sess: &rustc_session::Session) -> bool { - sess.opts.mir_opt_level() > 0 + sess.mir_opt_level() > 0 } fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { diff --git a/compiler/rustc_mir_transform/src/reveal_all.rs b/compiler/rustc_mir_transform/src/reveal_all.rs index 8ea550fa123b6..4919ad40098cb 100644 --- a/compiler/rustc_mir_transform/src/reveal_all.rs +++ b/compiler/rustc_mir_transform/src/reveal_all.rs @@ -9,7 +9,7 @@ pub struct RevealAll; impl<'tcx> MirPass<'tcx> for RevealAll { fn is_enabled(&self, sess: &rustc_session::Session) -> bool { - sess.opts.mir_opt_level() >= 3 || super::inline::Inline.is_enabled(sess) + sess.mir_opt_level() >= 3 || super::inline::Inline.is_enabled(sess) } fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index f3ccbbb56792d..fde12b9eee6b9 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -121,6 +121,10 @@ impl CheckAttrVisitor<'_> { sym::rustc_lint_diagnostics => { self.check_rustc_lint_diagnostics(&attr, span, target) } + sym::rustc_lint_opt_ty => self.check_rustc_lint_opt_ty(&attr, span, target), + sym::rustc_lint_opt_deny_field_access => { + self.check_rustc_lint_opt_deny_field_access(&attr, span, target) + } sym::rustc_clean | sym::rustc_dirty | sym::rustc_if_this_changed @@ -1382,6 +1386,35 @@ impl CheckAttrVisitor<'_> { self.check_applied_to_fn_or_method(attr, span, target) } + /// Checks that the `#[rustc_lint_opt_ty]` attribute is only applied to a struct. + fn check_rustc_lint_opt_ty(&self, attr: &Attribute, span: Span, target: Target) -> bool { + match target { + Target::Struct => true, + _ => { + self.tcx.sess.emit_err(errors::RustcLintOptTy { attr_span: attr.span, span }); + false + } + } + } + + /// Checks that the `#[rustc_lint_opt_deny_field_access]` attribute is only applied to a field. + fn check_rustc_lint_opt_deny_field_access( + &self, + attr: &Attribute, + span: Span, + target: Target, + ) -> bool { + match target { + Target::Field => true, + _ => { + self.tcx + .sess + .emit_err(errors::RustcLintOptDenyFieldAccess { attr_span: attr.span, span }); + false + } + } + } + /// Checks that the dep-graph debugging attributes are only present when the query-dep-graph /// option is passed to the compiler. fn check_rustc_dirty_clean(&self, attr: &Attribute) -> bool { diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index 0d4317f6b8881..5feb0e2956b74 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -625,3 +625,21 @@ pub struct UnusedMultiple { pub other: Span, pub name: Symbol, } + +#[derive(SessionDiagnostic)] +#[error(passes::rustc_lint_opt_ty)] +pub struct RustcLintOptTy { + #[primary_span] + pub attr_span: Span, + #[label] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::rustc_lint_opt_deny_field_access)] +pub struct RustcLintOptDenyFieldAccess { + #[primary_span] + pub attr_span: Span, + #[label] + pub span: Span, +} diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 55307b9cebb70..fe9ef6045415e 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -948,6 +948,8 @@ fn default_configuration(sess: &Session) -> CrateConfig { if sess.opts.debug_assertions { ret.insert((sym::debug_assertions, None)); } + // JUSTIFICATION: before wrapper fn is available + #[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))] if sess.opts.crate_types.contains(&CrateType::ProcMacro) { ret.insert((sym::proc_macro, None)); } @@ -2196,6 +2198,8 @@ fn parse_remap_path_prefix( mapping } +// JUSTIFICATION: before wrapper fn is available +#[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))] pub fn build_session_options(matches: &getopts::Matches) -> Options { let color = parse_color(matches); diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 28e2e0db89a1f..501997679f4bf 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -102,28 +102,6 @@ macro_rules! top_level_options { ); } -impl Options { - pub fn mir_opt_level(&self) -> usize { - self.unstable_opts - .mir_opt_level - .unwrap_or_else(|| if self.optimize != OptLevel::No { 2 } else { 1 }) - } - - pub fn instrument_coverage(&self) -> bool { - self.cg.instrument_coverage.unwrap_or(InstrumentCoverage::Off) != InstrumentCoverage::Off - } - - pub fn instrument_coverage_except_unused_generics(&self) -> bool { - self.cg.instrument_coverage.unwrap_or(InstrumentCoverage::Off) - == InstrumentCoverage::ExceptUnusedGenerics - } - - pub fn instrument_coverage_except_unused_functions(&self) -> bool { - self.cg.instrument_coverage.unwrap_or(InstrumentCoverage::Off) - == InstrumentCoverage::ExceptUnusedFunctions - } -} - top_level_options!( /// The top-level command-line options struct. /// @@ -149,9 +127,11 @@ top_level_options!( /// `CodegenOptions`, think about how it influences incremental compilation. If in /// doubt, specify `[TRACKED]`, which is always "correct" but might lead to /// unnecessary re-compilation. + #[cfg_attr(not(bootstrap), rustc_lint_opt_ty)] pub struct Options { /// The crate config requested for the session, which may be combined /// with additional crate configurations during the compile process. + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::crate_types` instead of this field"))] crate_types: Vec [TRACKED], optimize: OptLevel [TRACKED], /// Include the `debug_assertions` flag in dependency tracking, since it @@ -198,7 +178,9 @@ top_level_options!( /// what rustc was invoked with, but massaged a bit to agree with /// commands like `--emit llvm-ir` which they're often incompatible with /// if we otherwise use the defaults of rustc. + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::codegen_units` instead of this field"))] cli_forced_codegen_units: Option [UNTRACKED], + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::lto` instead of this field"))] cli_forced_thinlto_off: bool [UNTRACKED], /// Remap source path prefixes in all output (messages, object files, debug, etc.). @@ -249,11 +231,12 @@ macro_rules! options { ),* ,) => ( #[derive(Clone)] - pub struct $struct_name { $(pub $opt: $t),* } + #[cfg_attr(not(bootstrap), rustc_lint_opt_ty)] + pub struct $struct_name { $( $( #[$attr] )* pub $opt: $t),* } impl Default for $struct_name { fn default() -> $struct_name { - $struct_name { $( $( #[$attr] )* $opt: $init),* } + $struct_name { $($opt: $init),* } } } @@ -297,6 +280,22 @@ macro_rules! options { ) } +impl Options { + // JUSTIFICATION: defn of the suggested wrapper fn + #[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))] + pub fn time_passes(&self) -> bool { + self.unstable_opts.time_passes || self.unstable_opts.time + } +} + +impl CodegenOptions { + // JUSTIFICATION: defn of the suggested wrapper fn + #[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))] + pub fn instrument_coverage(&self) -> InstrumentCoverage { + self.instrument_coverage.unwrap_or(InstrumentCoverage::Off) + } +} + // Sometimes different options need to build a common structure. // That structure can be kept in one of the options' fields, the others become dummy. macro_rules! redirect_field { @@ -1076,6 +1075,7 @@ options! { ar: String = (String::new(), parse_string, [UNTRACKED], "this option is deprecated and does nothing"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::code_model` instead of this field"))] code_model: Option = (None, parse_code_model, [TRACKED], "choose the code model to use (`rustc --print code-models` for details)"), codegen_units: Option = (None, parse_opt_number, [UNTRACKED], @@ -1095,12 +1095,14 @@ options! { "extra data to put in each output filename"), force_frame_pointers: Option = (None, parse_opt_bool, [TRACKED], "force use of the frame pointers"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::must_emit_unwind_tables` instead of this field"))] force_unwind_tables: Option = (None, parse_opt_bool, [TRACKED], "force use of unwind tables"), incremental: Option = (None, parse_opt_string, [UNTRACKED], "enable incremental compilation"), inline_threshold: Option = (None, parse_opt_number, [TRACKED], "set the threshold for inlining a function"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::instrument_coverage` instead of this field"))] instrument_coverage: Option = (None, parse_instrument_coverage, [TRACKED], "instrument the generated code to support LLVM source-based code coverage \ reports (note, the compiler build config must include `profiler = true`); \ @@ -1113,6 +1115,7 @@ options! { "a single extra argument to append to the linker invocation (can be used several times)"), link_args: Vec = (Vec::new(), parse_list, [UNTRACKED], "extra arguments to append to the linker invocation (space separated)"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::link_dead_code` instead of this field"))] link_dead_code: Option = (None, parse_opt_bool, [TRACKED], "keep dead code at link time (useful for code coverage) (default: no)"), link_self_contained: Option = (None, parse_opt_bool, [UNTRACKED], @@ -1127,6 +1130,7 @@ options! { "generate build artifacts that are compatible with linker-based LTO"), llvm_args: Vec = (Vec::new(), parse_list, [TRACKED], "a list of arguments to pass to LLVM (space separated)"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::lto` instead of this field"))] lto: LtoCli = (LtoCli::Unspecified, parse_lto, [TRACKED], "perform LLVM link-time optimizations"), metadata: Vec = (Vec::new(), parse_list, [TRACKED], @@ -1143,8 +1147,10 @@ options! { "disable LLVM's SLP vectorization pass"), opt_level: String = ("0".to_string(), parse_string, [TRACKED], "optimization level (0-3, s, or z; default: 0)"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::overflow_checks` instead of this field"))] overflow_checks: Option = (None, parse_opt_bool, [TRACKED], "use overflow checks for integer arithmetic"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::panic_strategy` instead of this field"))] panic: Option = (None, parse_opt_panic_strategy, [TRACKED], "panic strategy to compile crate with"), passes: Vec = (Vec::new(), parse_list, [TRACKED], @@ -1156,6 +1162,7 @@ options! { "compile the program with profiling instrumentation"), profile_use: Option = (None, parse_opt_pathbuf, [TRACKED], "use the given `.profdata` file for profile-guided optimization"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::relocation_model` instead of this field"))] relocation_model: Option = (None, parse_relocation_model, [TRACKED], "control generation of position-independent code (PIC) \ (`rustc --print relocation-models` for details)"), @@ -1167,6 +1174,7 @@ options! { "save all temporary output files during compilation (default: no)"), soft_float: bool = (false, parse_bool, [TRACKED], "use soft float ABI (*eabihf targets only) (default: no)"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::split_debuginfo` instead of this field"))] split_debuginfo: Option = (None, parse_split_debuginfo, [TRACKED], "how to handle split-debuginfo, a platform-specific option"), strip: Strip = (Strip::None, parse_strip, [UNTRACKED], @@ -1202,11 +1210,13 @@ options! { "encode MIR of all functions into the crate metadata (default: no)"), assume_incomplete_release: bool = (false, parse_bool, [TRACKED], "make cfg(version) treat the current version as incomplete (default: no)"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::asm_comments` instead of this field"))] asm_comments: bool = (false, parse_bool, [TRACKED], "generate comments into the assembly (may change behavior) (default: no)"), assert_incr_state: Option = (None, parse_opt_string, [UNTRACKED], "assert that the incremental cache is in given state: \ either `loaded` or `not-loaded`."), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::binary_dep_depinfo` instead of this field"))] binary_dep_depinfo: bool = (false, parse_bool, [TRACKED], "include artifacts (sysroot, crate dependencies) used during compilation in dep-info \ (default: no)"), @@ -1284,6 +1294,7 @@ options! { "emit the bc module with thin LTO info (default: yes)"), export_executable_symbols: bool = (false, parse_bool, [TRACKED], "export symbols from executables, as if they were dynamic libraries"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::fewer_names` instead of this field"))] fewer_names: Option = (None, parse_opt_bool, [TRACKED], "reduce memory use by retaining fewer names within compilation artifacts (LLVM-IR) \ (default: no)"), @@ -1326,6 +1337,7 @@ options! { "control whether `#[inline]` functions are in all CGUs"), input_stats: bool = (false, parse_bool, [UNTRACKED], "gather statistics about the input (default: no)"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::instrument_coverage` instead of this field"))] instrument_coverage: Option = (None, parse_instrument_coverage, [TRACKED], "instrument the generated code to support LLVM source-based code coverage \ reports (note, the compiler build config must include `profiler = true`); \ @@ -1334,6 +1346,7 @@ options! { `=except-unused-generics` `=except-unused-functions` `=off` (default)"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::instrument_mcount` instead of this field"))] instrument_mcount: bool = (false, parse_bool, [TRACKED], "insert function instrument code for mcount-based tracing (default: no)"), keep_hygiene_data: bool = (false, parse_bool, [UNTRACKED], @@ -1356,6 +1369,7 @@ options! { merge_functions: Option = (None, parse_merge_functions, [TRACKED], "control the operation of the MergeFunctions LLVM pass, taking \ the same values as the target option of the same name"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::meta_stats` instead of this field"))] meta_stats: bool = (false, parse_bool, [UNTRACKED], "gather metadata statistics (default: no)"), mir_emit_retag: bool = (false, parse_bool, [TRACKED], @@ -1365,6 +1379,7 @@ options! { "use like `-Zmir-enable-passes=+DestProp,-InstCombine`. Forces the specified passes to be \ enabled, overriding all other checks. Passes that are not specified are enabled or \ disabled by other flags as usual."), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::mir_opt_level` instead of this field"))] mir_opt_level: Option = (None, parse_opt_number, [TRACKED], "MIR optimization level (0-4; default: 1 in non optimized builds and 2 in optimized builds)"), move_size_limit: Option = (None, parse_opt_number, [TRACKED], @@ -1431,6 +1446,7 @@ options! { See #77382 and #74551."), print_fuel: Option = (None, parse_opt_string, [TRACKED], "make rustc print the total optimization fuel used by a crate"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::print_llvm_passes` instead of this field"))] print_llvm_passes: bool = (false, parse_bool, [UNTRACKED], "print the LLVM optimization passes being run (default: no)"), print_mono_items: Option = (None, parse_opt_string, [UNTRACKED], @@ -1505,6 +1521,7 @@ options! { "exclude spans when debug-printing compiler state (default: no)"), src_hash_algorithm: Option = (None, parse_src_file_hash, [TRACKED], "hash algorithm of source files in debug info (`md5`, `sha1`, or `sha256`)"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::stack_protector` instead of this field"))] stack_protector: StackProtector = (StackProtector::None, parse_stack_protector, [TRACKED], "control stack smash protection strategy (`rustc --print stack-protector-strategies` for details)"), strict_init_checks: bool = (false, parse_bool, [TRACKED], @@ -1525,6 +1542,7 @@ options! { symbol_mangling_version: Option = (None, parse_symbol_mangling_version, [TRACKED], "which mangling version to use for symbol names ('legacy' (default) or 'v0')"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::teach` instead of this field"))] teach: bool = (false, parse_bool, [TRACKED], "show extended diagnostic help (default: no)"), temps_dir: Option = (None, parse_opt_string, [UNTRACKED], @@ -1540,6 +1558,7 @@ options! { "emit directionality isolation markers in translated diagnostics"), tune_cpu: Option = (None, parse_opt_string, [TRACKED], "select processor to schedule for (`rustc --print target-cpus` for details)"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::lto` instead of this field"))] thinlto: Option = (None, parse_opt_bool, [TRACKED], "enable ThinLTO when possible"), thir_unsafeck: bool = (false, parse_bool, [TRACKED], @@ -1548,14 +1567,19 @@ options! { /// a sequential compiler for now. This'll likely be adjusted /// in the future. Note that -Zthreads=0 is the way to get /// the num_cpus behavior. + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::threads` instead of this field"))] threads: usize = (1, parse_threads, [UNTRACKED], "use a thread pool with N threads"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::time_passes` instead of this field"))] time: bool = (false, parse_bool, [UNTRACKED], "measure time of rustc processes (default: no)"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::time_llvm_passes` instead of this field"))] time_llvm_passes: bool = (false, parse_bool, [UNTRACKED], "measure time of each LLVM pass (default: no)"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::time_passes` instead of this field"))] time_passes: bool = (false, parse_bool, [UNTRACKED], "measure time of each rustc pass (default: no)"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::tls_model` instead of this field"))] tls_model: Option = (None, parse_tls_model, [TRACKED], "choose the TLS model to use (`rustc --print tls-models` for details)"), trace_macros: bool = (false, parse_bool, [UNTRACKED], @@ -1590,14 +1614,17 @@ options! { "enable unsound and buggy MIR optimizations (default: no)"), /// This name is kind of confusing: Most unstable options enable something themselves, while /// this just allows "normal" options to be feature-gated. + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::unstable_options` instead of this field"))] unstable_options: bool = (false, parse_bool, [UNTRACKED], "adds unstable command line options to rustc interface (default: no)"), use_ctors_section: Option = (None, parse_opt_bool, [TRACKED], "use legacy .ctors section for initializers rather than .init_array"), validate_mir: bool = (false, parse_bool, [UNTRACKED], "validate MIR after each transformation"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::verbose` instead of this field"))] verbose: bool = (false, parse_bool, [UNTRACKED], "in general, enable more debug printouts (default: no)"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::verify_llvm_ir` instead of this field"))] verify_llvm_ir: bool = (false, parse_bool, [TRACKED], "verify LLVM IR (default: no)"), virtual_function_elimination: bool = (false, parse_bool, [TRACKED], diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index ac4a6b6da6f40..9669287b3f370 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -1,7 +1,7 @@ use crate::cgu_reuse_tracker::CguReuseTracker; use crate::code_stats::CodeStats; pub use crate::code_stats::{DataTypeKind, FieldInfo, SizeKind, VariantInfo}; -use crate::config::{self, CrateType, OutputType, SwitchWithOptPath}; +use crate::config::{self, CrateType, InstrumentCoverage, OptLevel, OutputType, SwitchWithOptPath}; use crate::parse::{add_feature_diagnostics, ParseSess}; use crate::search_paths::{PathKind, SearchPath}; use crate::{filesearch, lint}; @@ -583,11 +583,9 @@ impl Session { pub fn source_map(&self) -> &SourceMap { self.parse_sess.source_map() } - pub fn verbose(&self) -> bool { - self.opts.unstable_opts.verbose - } + pub fn time_passes(&self) -> bool { - self.opts.unstable_opts.time_passes || self.opts.unstable_opts.time + self.opts.time_passes() } /// Returns `true` if internal lints should be added to the lint store - i.e. if @@ -597,44 +595,344 @@ impl Session { self.unstable_options() && !self.opts.actually_rustdoc } + pub fn instrument_coverage(&self) -> bool { + self.opts.cg.instrument_coverage() != InstrumentCoverage::Off + } + + pub fn instrument_coverage_except_unused_generics(&self) -> bool { + self.opts.cg.instrument_coverage() == InstrumentCoverage::ExceptUnusedGenerics + } + + pub fn instrument_coverage_except_unused_functions(&self) -> bool { + self.opts.cg.instrument_coverage() == InstrumentCoverage::ExceptUnusedFunctions + } + + /// Gets the features enabled for the current compilation session. + /// DO NOT USE THIS METHOD if there is a TyCtxt available, as it circumvents + /// dependency tracking. Use tcx.features() instead. + #[inline] + pub fn features_untracked(&self) -> &rustc_feature::Features { + self.features.get().unwrap() + } + + pub fn init_features(&self, features: rustc_feature::Features) { + match self.features.set(features) { + Ok(()) => {} + Err(_) => panic!("`features` was initialized twice"), + } + } + + pub fn is_sanitizer_cfi_enabled(&self) -> bool { + self.opts.unstable_opts.sanitizer.contains(SanitizerSet::CFI) + } + + /// Check whether this compile session and crate type use static crt. + pub fn crt_static(&self, crate_type: Option) -> bool { + if !self.target.crt_static_respected { + // If the target does not opt in to crt-static support, use its default. + return self.target.crt_static_default; + } + + let requested_features = self.opts.cg.target_feature.split(','); + let found_negative = requested_features.clone().any(|r| r == "-crt-static"); + let found_positive = requested_features.clone().any(|r| r == "+crt-static"); + + // JUSTIFICATION: necessary use of crate_types directly (see FIXME below) + #[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))] + if found_positive || found_negative { + found_positive + } else if crate_type == Some(CrateType::ProcMacro) + || crate_type == None && self.opts.crate_types.contains(&CrateType::ProcMacro) + { + // FIXME: When crate_type is not available, + // we use compiler options to determine the crate_type. + // We can't check `#![crate_type = "proc-macro"]` here. + false + } else { + self.target.crt_static_default + } + } + + pub fn is_wasi_reactor(&self) -> bool { + self.target.options.os == "wasi" + && matches!( + self.opts.unstable_opts.wasi_exec_model, + Some(config::WasiExecModel::Reactor) + ) + } + + pub fn target_can_use_split_dwarf(&self) -> bool { + !self.target.is_like_windows && !self.target.is_like_osx + } + + pub fn generate_proc_macro_decls_symbol(&self, stable_crate_id: StableCrateId) -> String { + format!("__rustc_proc_macro_decls_{:08x}__", stable_crate_id.to_u64()) + } + + pub fn target_filesearch(&self, kind: PathKind) -> filesearch::FileSearch<'_> { + filesearch::FileSearch::new( + &self.sysroot, + self.opts.target_triple.triple(), + &self.opts.search_paths, + &self.target_tlib_path, + kind, + ) + } + pub fn host_filesearch(&self, kind: PathKind) -> filesearch::FileSearch<'_> { + filesearch::FileSearch::new( + &self.sysroot, + config::host_triple(), + &self.opts.search_paths, + &self.host_tlib_path, + kind, + ) + } + + /// Returns a list of directories where target-specific tool binaries are located. + pub fn get_tools_search_paths(&self, self_contained: bool) -> Vec { + let rustlib_path = rustc_target::target_rustlib_path(&self.sysroot, &config::host_triple()); + let p = PathBuf::from_iter([ + Path::new(&self.sysroot), + Path::new(&rustlib_path), + Path::new("bin"), + ]); + if self_contained { vec![p.clone(), p.join("self-contained")] } else { vec![p] } + } + + pub fn init_incr_comp_session( + &self, + session_dir: PathBuf, + lock_file: flock::Lock, + load_dep_graph: bool, + ) { + let mut incr_comp_session = self.incr_comp_session.borrow_mut(); + + if let IncrCompSession::NotInitialized = *incr_comp_session { + } else { + panic!("Trying to initialize IncrCompSession `{:?}`", *incr_comp_session) + } + + *incr_comp_session = + IncrCompSession::Active { session_directory: session_dir, lock_file, load_dep_graph }; + } + + pub fn finalize_incr_comp_session(&self, new_directory_path: PathBuf) { + let mut incr_comp_session = self.incr_comp_session.borrow_mut(); + + if let IncrCompSession::Active { .. } = *incr_comp_session { + } else { + panic!("trying to finalize `IncrCompSession` `{:?}`", *incr_comp_session); + } + + // Note: this will also drop the lock file, thus unlocking the directory. + *incr_comp_session = IncrCompSession::Finalized { session_directory: new_directory_path }; + } + + pub fn mark_incr_comp_session_as_invalid(&self) { + let mut incr_comp_session = self.incr_comp_session.borrow_mut(); + + let session_directory = match *incr_comp_session { + IncrCompSession::Active { ref session_directory, .. } => session_directory.clone(), + IncrCompSession::InvalidBecauseOfErrors { .. } => return, + _ => panic!("trying to invalidate `IncrCompSession` `{:?}`", *incr_comp_session), + }; + + // Note: this will also drop the lock file, thus unlocking the directory. + *incr_comp_session = IncrCompSession::InvalidBecauseOfErrors { session_directory }; + } + + pub fn incr_comp_session_dir(&self) -> cell::Ref<'_, PathBuf> { + let incr_comp_session = self.incr_comp_session.borrow(); + cell::Ref::map(incr_comp_session, |incr_comp_session| match *incr_comp_session { + IncrCompSession::NotInitialized => panic!( + "trying to get session directory from `IncrCompSession`: {:?}", + *incr_comp_session, + ), + IncrCompSession::Active { ref session_directory, .. } + | IncrCompSession::Finalized { ref session_directory } + | IncrCompSession::InvalidBecauseOfErrors { ref session_directory } => { + session_directory + } + }) + } + + pub fn incr_comp_session_dir_opt(&self) -> Option> { + self.opts.incremental.as_ref().map(|_| self.incr_comp_session_dir()) + } + + pub fn print_perf_stats(&self) { + eprintln!( + "Total time spent computing symbol hashes: {}", + duration_to_secs_str(*self.perf_stats.symbol_hash_time.lock()) + ); + eprintln!( + "Total queries canonicalized: {}", + self.perf_stats.queries_canonicalized.load(Ordering::Relaxed) + ); + eprintln!( + "normalize_generic_arg_after_erasing_regions: {}", + self.perf_stats.normalize_generic_arg_after_erasing_regions.load(Ordering::Relaxed) + ); + eprintln!( + "normalize_projection_ty: {}", + self.perf_stats.normalize_projection_ty.load(Ordering::Relaxed) + ); + } + + /// We want to know if we're allowed to do an optimization for crate foo from -z fuel=foo=n. + /// This expends fuel if applicable, and records fuel if applicable. + pub fn consider_optimizing String>(&self, crate_name: &str, msg: T) -> bool { + let mut ret = true; + if let Some((ref c, _)) = self.opts.unstable_opts.fuel { + if c == crate_name { + assert_eq!(self.threads(), 1); + let mut fuel = self.optimization_fuel.lock(); + ret = fuel.remaining != 0; + if fuel.remaining == 0 && !fuel.out_of_fuel { + if self.diagnostic().can_emit_warnings() { + // We only call `msg` in case we can actually emit warnings. + // Otherwise, this could cause a `delay_good_path_bug` to + // trigger (issue #79546). + self.warn(&format!("optimization-fuel-exhausted: {}", msg())); + } + fuel.out_of_fuel = true; + } else if fuel.remaining > 0 { + fuel.remaining -= 1; + } + } + } + if let Some(ref c) = self.opts.unstable_opts.print_fuel { + if c == crate_name { + assert_eq!(self.threads(), 1); + self.print_fuel.fetch_add(1, SeqCst); + } + } + ret + } + + pub fn rust_2015(&self) -> bool { + self.edition() == Edition::Edition2015 + } + + /// Are we allowed to use features from the Rust 2018 edition? + pub fn rust_2018(&self) -> bool { + self.edition() >= Edition::Edition2018 + } + + /// Are we allowed to use features from the Rust 2021 edition? + pub fn rust_2021(&self) -> bool { + self.edition() >= Edition::Edition2021 + } + + /// Are we allowed to use features from the Rust 2024 edition? + pub fn rust_2024(&self) -> bool { + self.edition() >= Edition::Edition2024 + } + + /// Returns `true` if we cannot skip the PLT for shared library calls. + pub fn needs_plt(&self) -> bool { + // Check if the current target usually needs PLT to be enabled. + // The user can use the command line flag to override it. + let needs_plt = self.target.needs_plt; + + let dbg_opts = &self.opts.unstable_opts; + + let relro_level = dbg_opts.relro_level.unwrap_or(self.target.relro_level); + + // Only enable this optimization by default if full relro is also enabled. + // In this case, lazy binding was already unavailable, so nothing is lost. + // This also ensures `-Wl,-z,now` is supported by the linker. + let full_relro = RelroLevel::Full == relro_level; + + // If user didn't explicitly forced us to use / skip the PLT, + // then try to skip it where possible. + dbg_opts.plt.unwrap_or(needs_plt || !full_relro) + } + + /// Checks if LLVM lifetime markers should be emitted. + pub fn emit_lifetime_markers(&self) -> bool { + self.opts.optimize != config::OptLevel::No + // AddressSanitizer uses lifetimes to detect use after scope bugs. + // MemorySanitizer uses lifetimes to detect use of uninitialized stack variables. + // HWAddressSanitizer will use lifetimes to detect use after scope bugs in the future. + || self.opts.unstable_opts.sanitizer.intersects(SanitizerSet::ADDRESS | SanitizerSet::MEMORY | SanitizerSet::HWADDRESS) + } + + pub fn is_proc_macro_attr(&self, attr: &Attribute) -> bool { + [sym::proc_macro, sym::proc_macro_attribute, sym::proc_macro_derive] + .iter() + .any(|kind| attr.has_name(*kind)) + } + + pub fn contains_name(&self, attrs: &[Attribute], name: Symbol) -> bool { + attrs.iter().any(|item| item.has_name(name)) + } + + pub fn find_by_name<'a>( + &'a self, + attrs: &'a [Attribute], + name: Symbol, + ) -> Option<&'a Attribute> { + attrs.iter().find(|attr| attr.has_name(name)) + } + + pub fn filter_by_name<'a>( + &'a self, + attrs: &'a [Attribute], + name: Symbol, + ) -> impl Iterator { + attrs.iter().filter(move |attr| attr.has_name(name)) + } + + pub fn first_attr_value_str_by_name( + &self, + attrs: &[Attribute], + name: Symbol, + ) -> Option { + attrs.iter().find(|at| at.has_name(name)).and_then(|at| at.value_str()) + } +} + +// JUSTIFICATION: defn of the suggested wrapper fns +#[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))] +impl Session { + pub fn verbose(&self) -> bool { + self.opts.unstable_opts.verbose + } + pub fn instrument_mcount(&self) -> bool { self.opts.unstable_opts.instrument_mcount } + pub fn time_llvm_passes(&self) -> bool { self.opts.unstable_opts.time_llvm_passes } + pub fn meta_stats(&self) -> bool { self.opts.unstable_opts.meta_stats } + pub fn asm_comments(&self) -> bool { self.opts.unstable_opts.asm_comments } + pub fn verify_llvm_ir(&self) -> bool { self.opts.unstable_opts.verify_llvm_ir || option_env!("RUSTC_VERIFY_LLVM_IR").is_some() } + pub fn print_llvm_passes(&self) -> bool { self.opts.unstable_opts.print_llvm_passes } + pub fn binary_dep_depinfo(&self) -> bool { self.opts.unstable_opts.binary_dep_depinfo } - pub fn mir_opt_level(&self) -> usize { - self.opts.mir_opt_level() - } - - /// Gets the features enabled for the current compilation session. - /// DO NOT USE THIS METHOD if there is a TyCtxt available, as it circumvents - /// dependency tracking. Use tcx.features() instead. - #[inline] - pub fn features_untracked(&self) -> &rustc_feature::Features { - self.features.get().unwrap() - } - pub fn init_features(&self, features: rustc_feature::Features) { - match self.features.set(features) { - Ok(()) => {} - Err(_) => panic!("`features` was initialized twice"), - } + pub fn mir_opt_level(&self) -> usize { + self.opts + .unstable_opts + .mir_opt_level + .unwrap_or_else(|| if self.opts.optimize != OptLevel::No { 2 } else { 1 }) } /// Calculates the flavor of LTO to use for this compilation. @@ -710,6 +1008,7 @@ impl Session { pub fn panic_strategy(&self) -> PanicStrategy { self.opts.cg.panic.unwrap_or(self.target.panic_strategy) } + pub fn fewer_names(&self) -> bool { if let Some(fewer_names) = self.opts.unstable_opts.fewer_names { fewer_names @@ -721,43 +1020,17 @@ impl Session { !more_names } } - - pub fn unstable_options(&self) -> bool { - self.opts.unstable_opts.unstable_options - } - pub fn is_nightly_build(&self) -> bool { - self.opts.unstable_features.is_nightly_build() - } - pub fn is_sanitizer_cfi_enabled(&self) -> bool { - self.opts.unstable_opts.sanitizer.contains(SanitizerSet::CFI) - } - pub fn overflow_checks(&self) -> bool { - self.opts.cg.overflow_checks.unwrap_or(self.opts.debug_assertions) - } - - /// Check whether this compile session and crate type use static crt. - pub fn crt_static(&self, crate_type: Option) -> bool { - if !self.target.crt_static_respected { - // If the target does not opt in to crt-static support, use its default. - return self.target.crt_static_default; - } - - let requested_features = self.opts.cg.target_feature.split(','); - let found_negative = requested_features.clone().any(|r| r == "-crt-static"); - let found_positive = requested_features.clone().any(|r| r == "+crt-static"); - - if found_positive || found_negative { - found_positive - } else if crate_type == Some(CrateType::ProcMacro) - || crate_type == None && self.opts.crate_types.contains(&CrateType::ProcMacro) - { - // FIXME: When crate_type is not available, - // we use compiler options to determine the crate_type. - // We can't check `#![crate_type = "proc-macro"]` here. - false - } else { - self.target.crt_static_default - } + + pub fn unstable_options(&self) -> bool { + self.opts.unstable_opts.unstable_options + } + + pub fn is_nightly_build(&self) -> bool { + self.opts.unstable_features.is_nightly_build() + } + + pub fn overflow_checks(&self) -> bool { + self.opts.cg.overflow_checks.unwrap_or(self.opts.debug_assertions) } pub fn relocation_model(&self) -> RelocModel { @@ -772,14 +1045,6 @@ impl Session { self.opts.unstable_opts.tls_model.unwrap_or(self.target.tls_model) } - pub fn is_wasi_reactor(&self) -> bool { - self.target.options.os == "wasi" - && matches!( - self.opts.unstable_opts.wasi_exec_model, - Some(config::WasiExecModel::Reactor) - ) - } - pub fn split_debuginfo(&self) -> SplitDebuginfo { self.opts.cg.split_debuginfo.unwrap_or(self.target.split_debuginfo) } @@ -792,10 +1057,6 @@ impl Session { } } - pub fn target_can_use_split_dwarf(&self) -> bool { - !self.target.is_like_windows && !self.target.is_like_osx - } - pub fn must_emit_unwind_tables(&self) -> bool { // This is used to control the emission of the `uwtable` attribute on // LLVM functions. @@ -823,151 +1084,6 @@ impl Session { ) } - pub fn generate_proc_macro_decls_symbol(&self, stable_crate_id: StableCrateId) -> String { - format!("__rustc_proc_macro_decls_{:08x}__", stable_crate_id.to_u64()) - } - - pub fn target_filesearch(&self, kind: PathKind) -> filesearch::FileSearch<'_> { - filesearch::FileSearch::new( - &self.sysroot, - self.opts.target_triple.triple(), - &self.opts.search_paths, - &self.target_tlib_path, - kind, - ) - } - pub fn host_filesearch(&self, kind: PathKind) -> filesearch::FileSearch<'_> { - filesearch::FileSearch::new( - &self.sysroot, - config::host_triple(), - &self.opts.search_paths, - &self.host_tlib_path, - kind, - ) - } - - /// Returns a list of directories where target-specific tool binaries are located. - pub fn get_tools_search_paths(&self, self_contained: bool) -> Vec { - let rustlib_path = rustc_target::target_rustlib_path(&self.sysroot, &config::host_triple()); - let p = PathBuf::from_iter([ - Path::new(&self.sysroot), - Path::new(&rustlib_path), - Path::new("bin"), - ]); - if self_contained { vec![p.clone(), p.join("self-contained")] } else { vec![p] } - } - - pub fn init_incr_comp_session( - &self, - session_dir: PathBuf, - lock_file: flock::Lock, - load_dep_graph: bool, - ) { - let mut incr_comp_session = self.incr_comp_session.borrow_mut(); - - if let IncrCompSession::NotInitialized = *incr_comp_session { - } else { - panic!("Trying to initialize IncrCompSession `{:?}`", *incr_comp_session) - } - - *incr_comp_session = - IncrCompSession::Active { session_directory: session_dir, lock_file, load_dep_graph }; - } - - pub fn finalize_incr_comp_session(&self, new_directory_path: PathBuf) { - let mut incr_comp_session = self.incr_comp_session.borrow_mut(); - - if let IncrCompSession::Active { .. } = *incr_comp_session { - } else { - panic!("trying to finalize `IncrCompSession` `{:?}`", *incr_comp_session); - } - - // Note: this will also drop the lock file, thus unlocking the directory. - *incr_comp_session = IncrCompSession::Finalized { session_directory: new_directory_path }; - } - - pub fn mark_incr_comp_session_as_invalid(&self) { - let mut incr_comp_session = self.incr_comp_session.borrow_mut(); - - let session_directory = match *incr_comp_session { - IncrCompSession::Active { ref session_directory, .. } => session_directory.clone(), - IncrCompSession::InvalidBecauseOfErrors { .. } => return, - _ => panic!("trying to invalidate `IncrCompSession` `{:?}`", *incr_comp_session), - }; - - // Note: this will also drop the lock file, thus unlocking the directory. - *incr_comp_session = IncrCompSession::InvalidBecauseOfErrors { session_directory }; - } - - pub fn incr_comp_session_dir(&self) -> cell::Ref<'_, PathBuf> { - let incr_comp_session = self.incr_comp_session.borrow(); - cell::Ref::map(incr_comp_session, |incr_comp_session| match *incr_comp_session { - IncrCompSession::NotInitialized => panic!( - "trying to get session directory from `IncrCompSession`: {:?}", - *incr_comp_session, - ), - IncrCompSession::Active { ref session_directory, .. } - | IncrCompSession::Finalized { ref session_directory } - | IncrCompSession::InvalidBecauseOfErrors { ref session_directory } => { - session_directory - } - }) - } - - pub fn incr_comp_session_dir_opt(&self) -> Option> { - self.opts.incremental.as_ref().map(|_| self.incr_comp_session_dir()) - } - - pub fn print_perf_stats(&self) { - eprintln!( - "Total time spent computing symbol hashes: {}", - duration_to_secs_str(*self.perf_stats.symbol_hash_time.lock()) - ); - eprintln!( - "Total queries canonicalized: {}", - self.perf_stats.queries_canonicalized.load(Ordering::Relaxed) - ); - eprintln!( - "normalize_generic_arg_after_erasing_regions: {}", - self.perf_stats.normalize_generic_arg_after_erasing_regions.load(Ordering::Relaxed) - ); - eprintln!( - "normalize_projection_ty: {}", - self.perf_stats.normalize_projection_ty.load(Ordering::Relaxed) - ); - } - - /// We want to know if we're allowed to do an optimization for crate foo from -z fuel=foo=n. - /// This expends fuel if applicable, and records fuel if applicable. - pub fn consider_optimizing String>(&self, crate_name: &str, msg: T) -> bool { - let mut ret = true; - if let Some((ref c, _)) = self.opts.unstable_opts.fuel { - if c == crate_name { - assert_eq!(self.threads(), 1); - let mut fuel = self.optimization_fuel.lock(); - ret = fuel.remaining != 0; - if fuel.remaining == 0 && !fuel.out_of_fuel { - if self.diagnostic().can_emit_warnings() { - // We only call `msg` in case we can actually emit warnings. - // Otherwise, this could cause a `delay_good_path_bug` to - // trigger (issue #79546). - self.warn(&format!("optimization-fuel-exhausted: {}", msg())); - } - fuel.out_of_fuel = true; - } else if fuel.remaining > 0 { - fuel.remaining -= 1; - } - } - } - if let Some(ref c) = self.opts.unstable_opts.print_fuel { - if c == crate_name { - assert_eq!(self.threads(), 1); - self.print_fuel.fetch_add(1, SeqCst); - } - } - ret - } - /// Returns the number of query threads that should be used for this /// compilation pub fn threads(&self) -> usize { @@ -1048,109 +1164,17 @@ impl Session { self.opts.unstable_opts.teach && self.diagnostic().must_teach(code) } - pub fn rust_2015(&self) -> bool { - self.opts.edition == Edition::Edition2015 - } - - /// Are we allowed to use features from the Rust 2018 edition? - pub fn rust_2018(&self) -> bool { - self.opts.edition >= Edition::Edition2018 - } - - /// Are we allowed to use features from the Rust 2021 edition? - pub fn rust_2021(&self) -> bool { - self.opts.edition >= Edition::Edition2021 - } - - /// Are we allowed to use features from the Rust 2024 edition? - pub fn rust_2024(&self) -> bool { - self.opts.edition >= Edition::Edition2024 - } - pub fn edition(&self) -> Edition { self.opts.edition } - /// Returns `true` if we cannot skip the PLT for shared library calls. - pub fn needs_plt(&self) -> bool { - // Check if the current target usually needs PLT to be enabled. - // The user can use the command line flag to override it. - let needs_plt = self.target.needs_plt; - - let dbg_opts = &self.opts.unstable_opts; - - let relro_level = dbg_opts.relro_level.unwrap_or(self.target.relro_level); - - // Only enable this optimization by default if full relro is also enabled. - // In this case, lazy binding was already unavailable, so nothing is lost. - // This also ensures `-Wl,-z,now` is supported by the linker. - let full_relro = RelroLevel::Full == relro_level; - - // If user didn't explicitly forced us to use / skip the PLT, - // then try to skip it where possible. - dbg_opts.plt.unwrap_or(needs_plt || !full_relro) - } - - /// Checks if LLVM lifetime markers should be emitted. - pub fn emit_lifetime_markers(&self) -> bool { - self.opts.optimize != config::OptLevel::No - // AddressSanitizer uses lifetimes to detect use after scope bugs. - // MemorySanitizer uses lifetimes to detect use of uninitialized stack variables. - // HWAddressSanitizer will use lifetimes to detect use after scope bugs in the future. - || self.opts.unstable_opts.sanitizer.intersects(SanitizerSet::ADDRESS | SanitizerSet::MEMORY | SanitizerSet::HWADDRESS) - } - pub fn link_dead_code(&self) -> bool { self.opts.cg.link_dead_code.unwrap_or(false) } - - pub fn instrument_coverage(&self) -> bool { - self.opts.instrument_coverage() - } - - pub fn instrument_coverage_except_unused_generics(&self) -> bool { - self.opts.instrument_coverage_except_unused_generics() - } - - pub fn instrument_coverage_except_unused_functions(&self) -> bool { - self.opts.instrument_coverage_except_unused_functions() - } - - pub fn is_proc_macro_attr(&self, attr: &Attribute) -> bool { - [sym::proc_macro, sym::proc_macro_attribute, sym::proc_macro_derive] - .iter() - .any(|kind| attr.has_name(*kind)) - } - - pub fn contains_name(&self, attrs: &[Attribute], name: Symbol) -> bool { - attrs.iter().any(|item| item.has_name(name)) - } - - pub fn find_by_name<'a>( - &'a self, - attrs: &'a [Attribute], - name: Symbol, - ) -> Option<&'a Attribute> { - attrs.iter().find(|attr| attr.has_name(name)) - } - - pub fn filter_by_name<'a>( - &'a self, - attrs: &'a [Attribute], - name: Symbol, - ) -> impl Iterator { - attrs.iter().filter(move |attr| attr.has_name(name)) - } - - pub fn first_attr_value_str_by_name( - &self, - attrs: &[Attribute], - name: Symbol, - ) -> Option { - attrs.iter().find(|at| at.has_name(name)).and_then(|at| at.value_str()) - } } +// JUSTIFICATION: part of session construction +#[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))] fn default_emitter( sopts: &config::Options, registry: rustc_errors::registry::Registry, @@ -1235,6 +1259,8 @@ pub enum DiagnosticOutput { Raw(Box), } +// JUSTIFICATION: literally session construction +#[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))] pub fn build_session( sopts: config::Options, local_crate_source_file: Option, @@ -1356,11 +1382,8 @@ pub fn build_session( CguReuseTracker::new_disabled() }; - let prof = SelfProfilerRef::new( - self_profiler, - sopts.unstable_opts.time_passes || sopts.unstable_opts.time, - sopts.unstable_opts.time_passes, - ); + let prof = + SelfProfilerRef::new(self_profiler, sopts.time_passes(), sopts.unstable_opts.time_passes); let ctfe_backtrace = Lock::new(match env::var("RUSTC_CTFE_BACKTRACE") { Ok(ref val) if val == "immediate" => CtfeBacktrace::Immediate, @@ -1409,8 +1432,12 @@ pub fn build_session( sess } -// If it is useful to have a Session available already for validating a -// commandline argument, you can do so here. +/// Validate command line arguments with a `Session`. +/// +/// If it is useful to have a Session available already for validating a commandline argument, you +/// can do so here. +// JUSTIFICATION: needs to access args to validate them +#[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))] fn validate_commandline_args_with_session_available(sess: &Session) { // Since we don't know if code in an rlib will be linked to statically or // dynamically downstream, rustc generates `__imp_` symbols that help linkers diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index c75b6772487f9..060e7a7b90aee 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1238,6 +1238,8 @@ symbols! { rustc_layout_scalar_valid_range_start, rustc_legacy_const_generics, rustc_lint_diagnostics, + rustc_lint_opt_deny_field_access, + rustc_lint_opt_ty, rustc_lint_query_instability, rustc_macro_transparency, rustc_main, diff --git a/src/test/ui-fulldeps/internal-lints/bad_opt_access.rs b/src/test/ui-fulldeps/internal-lints/bad_opt_access.rs new file mode 100644 index 0000000000000..d6bd6945e150a --- /dev/null +++ b/src/test/ui-fulldeps/internal-lints/bad_opt_access.rs @@ -0,0 +1,22 @@ +// compile-flags: -Z unstable-options + +// Test that accessing command line options by field access triggers a lint for those fields +// that have wrapper functions which should be used. + +#![crate_type = "lib"] +#![feature(rustc_private)] +#![deny(rustc::bad_opt_access)] + +extern crate rustc_session; +use rustc_session::Session; + +pub fn access_bad_option(sess: Session) { + let _ = sess.opts.cg.split_debuginfo; + //~^ ERROR use `Session::split_debuginfo` instead of this field + + let _ = sess.opts.crate_types; + //~^ ERROR use `Session::crate_types` instead of this field + + let _ = sess.opts.crate_name; + // okay! +} diff --git a/src/test/ui-fulldeps/internal-lints/bad_opt_access.stderr b/src/test/ui-fulldeps/internal-lints/bad_opt_access.stderr new file mode 100644 index 0000000000000..e4145bff8bee9 --- /dev/null +++ b/src/test/ui-fulldeps/internal-lints/bad_opt_access.stderr @@ -0,0 +1,20 @@ +error: use `Session::split_debuginfo` instead of this field + --> $DIR/bad_opt_access.rs:14:13 + | +LL | let _ = sess.opts.cg.split_debuginfo; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/bad_opt_access.rs:8:9 + | +LL | #![deny(rustc::bad_opt_access)] + | ^^^^^^^^^^^^^^^^^^^^^ + +error: use `Session::crate_types` instead of this field + --> $DIR/bad_opt_access.rs:17:13 + | +LL | let _ = sess.opts.crate_types; + | ^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/src/driver.rs b/src/tools/clippy/src/driver.rs index c219c7de830ef..c1ec2bd5bd665 100644 --- a/src/tools/clippy/src/driver.rs +++ b/src/tools/clippy/src/driver.rs @@ -94,6 +94,8 @@ struct ClippyCallbacks { } impl rustc_driver::Callbacks for ClippyCallbacks { + // JUSTIFICATION: necessary in clippy driver to set `mir_opt_level` + #[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))] fn config(&mut self, config: &mut interface::Config) { let previous = config.register_lints.take(); let clippy_args_var = self.clippy_args_var.take(); From 4df8e78a59bba8811d7614f170fd032c9f20281a Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 27 Jul 2022 15:00:43 +0200 Subject: [PATCH 22/24] Fix invalid css property value --- src/librustdoc/html/static/css/rustdoc.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index 70b7a47bcd58b..68f8de15ba287 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -652,7 +652,7 @@ h2.location a { } .top-doc .docblock h2 { font-size: 1.375rem; } -.top-doc .docblock h3 { font-size: 1.25; } +.top-doc .docblock h3 { font-size: 1.25rem; } .top-doc .docblock h4, .top-doc .docblock h5 { font-size: 1.125rem; From 88b137d5fe0cbe28dab4f9af61045c726f831872 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 27 Jul 2022 15:36:27 +0200 Subject: [PATCH 23/24] Fix headings border --- src/librustdoc/html/static/css/rustdoc.css | 2 +- src/librustdoc/html/static/css/themes/dark.css | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index 68f8de15ba287..0b9a16c57347b 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -167,7 +167,7 @@ h2, h3, h4 { h2, .top-doc .docblock > h3, .top-doc .docblock > h4 { - border-bottom: 1px solid; + border-bottom: 1px solid var(--headings-border-bottom-color); } h3.code-header { font-size: 1.125rem; /* 18px */ diff --git a/src/librustdoc/html/static/css/themes/dark.css b/src/librustdoc/html/static/css/themes/dark.css index 8e753f5768206..1ded3730e848d 100644 --- a/src/librustdoc/html/static/css/themes/dark.css +++ b/src/librustdoc/html/static/css/themes/dark.css @@ -42,10 +42,6 @@ input:focus + .slider { background-color: #0a042f !important; } -.docblock h1, .docblock h2, .docblock h3, .docblock h4, .docblock h5, .docblock h6 { - border-bottom-color: #DDD; -} - .docblock table td, .docblock table th { border-color: #ddd; } From bff43d0e37c5299152c61d6ad959860dcd1ec15d Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 27 Jul 2022 15:36:43 +0200 Subject: [PATCH 24/24] Add GUI tests for headings border and text color --- src/test/rustdoc-gui/docblock-details.goml | 4 +- src/test/rustdoc-gui/headings.goml | 100 ++++++++++++++++++++- src/test/rustdoc-gui/src/test_docs/lib.rs | 4 + 3 files changed, 103 insertions(+), 5 deletions(-) diff --git a/src/test/rustdoc-gui/docblock-details.goml b/src/test/rustdoc-gui/docblock-details.goml index 2edbf1e4e2d8c..f6287ade2f295 100644 --- a/src/test/rustdoc-gui/docblock-details.goml +++ b/src/test/rustdoc-gui/docblock-details.goml @@ -9,12 +9,12 @@ reload: assert-text: (".top-doc .docblock > h3", "Hello") assert-css: ( ".top-doc .docblock > h3", - {"border-bottom": "1px solid rgb(221, 221, 221)"}, + {"border-bottom": "1px solid rgb(210, 210, 210)"}, ) // We now check that the `

` doesn't have a bottom border and has the correct display. assert-css: ( ".top-doc .docblock summary h4", - {"border-bottom": "0px none rgb(221, 221, 221)"}, + {"border-bottom": "0px none rgb(210, 210, 210)"}, ) // This allows to ensure that summary is on one line only! assert-property: (".top-doc .docblock summary h4", {"offsetHeight": "33"}) diff --git a/src/test/rustdoc-gui/headings.goml b/src/test/rustdoc-gui/headings.goml index cc3dd61e99a7a..8c2c3df1588f5 100644 --- a/src/test/rustdoc-gui/headings.goml +++ b/src/test/rustdoc-gui/headings.goml @@ -150,15 +150,109 @@ assert-css: ("h2#top-doc-prose-title", {"border-bottom-width": "1px"}) assert-css: ("h3#top-doc-prose-sub-heading", {"font-size": "20px"}) assert-css: ("h3#top-doc-prose-sub-heading", {"border-bottom-width": "1px"}) -goto: file://|DOC_PATH|/staged_api/struct.Foo.html +// Checking colors now. show-text: true local-storage: {"rustdoc-theme": "light", "rustdoc-use-system-theme": "false"} +goto: file://|DOC_PATH|/test_docs/struct.HeavilyDocumentedStruct.html +assert-css: ( + ".top-doc .docblock h2", + {"color": "rgb(0, 0, 0)", "border-bottom": "1px solid rgb(221, 221, 221)"}, +) +assert-css: ( + ".top-doc .docblock h3", + {"color": "rgb(0, 0, 0)", "border-bottom": "1px solid rgb(221, 221, 221)"}, +) +assert-css: ( + ".top-doc .docblock h4", + {"color": "rgb(0, 0, 0)", "border-bottom": "1px solid rgb(221, 221, 221)"}, +) +assert-css: ( + ".top-doc .docblock h5", + {"color": "rgb(0, 0, 0)", "border-bottom": "0px none rgb(221, 221, 221)"}, +) +assert-css: ( + "#implementations-list .docblock h4", + {"color": "rgb(0, 0, 0)", "border-bottom": "0px none rgb(221, 221, 221)"}, +) +assert-css: ( + "#implementations-list .docblock h5", + {"color": "rgb(0, 0, 0)", "border-bottom": "0px none rgb(221, 221, 221)"}, +) +assert-css: ( + "#implementations-list .docblock h6", + {"color": "rgb(0, 0, 0)", "border-bottom": "0px none rgb(221, 221, 221)"}, +) + +local-storage: {"rustdoc-theme": "dark"} +reload: +assert-css: ( + ".top-doc .docblock h2", + {"color": "rgb(221, 221, 221)", "border-bottom": "1px solid rgb(210, 210, 210)"}, +) +assert-css: ( + ".top-doc .docblock h3", + {"color": "rgb(221, 221, 221)", "border-bottom": "1px solid rgb(210, 210, 210)"}, +) +assert-css: ( + ".top-doc .docblock h4", + {"color": "rgb(221, 221, 221)", "border-bottom": "1px solid rgb(210, 210, 210)"}, +) +assert-css: ( + ".top-doc .docblock h5", + {"color": "rgb(221, 221, 221)", "border-bottom": "0px none rgb(210, 210, 210)"}, +) +assert-css: ( + "#implementations-list .docblock h4", + {"color": "rgb(221, 221, 221)", "border-bottom": "0px none rgb(210, 210, 210)"}, +) +assert-css: ( + "#implementations-list .docblock h5", + {"color": "rgb(221, 221, 221)", "border-bottom": "0px none rgb(210, 210, 210)"}, +) +assert-css: ( + "#implementations-list .docblock h6", + {"color": "rgb(221, 221, 221)", "border-bottom": "0px none rgb(210, 210, 210)"}, +) + +local-storage: {"rustdoc-theme": "ayu"} +reload: +assert-css: ( + ".top-doc .docblock h2", + {"color": "rgb(255, 255, 255)", "border-bottom": "1px solid rgb(92, 103, 115)"}, +) +assert-css: ( + ".top-doc .docblock h2", + {"color": "rgb(255, 255, 255)", "border-bottom": "1px solid rgb(92, 103, 115)"}, +) +assert-css: ( + ".top-doc .docblock h4", + {"color": "rgb(255, 255, 255)", "border-bottom": "1px solid rgb(92, 103, 115)"}, +) +assert-css: ( + ".top-doc .docblock h5", + {"color": "rgb(197, 197, 197)", "border-bottom": "0px none rgb(92, 103, 115)"}, +) +assert-css: ( + "#implementations-list .docblock h4", + {"color": "rgb(255, 255, 255)", "border-bottom": "0px none rgb(92, 103, 115)"}, +) +assert-css: ( + "#implementations-list .docblock h5", + {"color": "rgb(197, 197, 197)", "border-bottom": "0px none rgb(92, 103, 115)"}, +) +assert-css: ( + "#implementations-list .docblock h6", + {"color": "rgb(197, 197, 197)", "border-bottom": "0px none rgb(92, 103, 115)"}, +) + +local-storage: {"rustdoc-theme": "light"} +goto: file://|DOC_PATH|/staged_api/struct.Foo.html assert-css: (".since", {"color": "rgb(128, 128, 128)"}) -local-storage: {"rustdoc-theme": "dark", "rustdoc-use-system-theme": "false"} +local-storage: {"rustdoc-theme": "dark"} reload: assert-css: (".since", {"color": "rgb(128, 128, 128)"}) -local-storage: {"rustdoc-theme": "ayu", "rustdoc-use-system-theme": "false"} +local-storage: {"rustdoc-theme": "ayu"} reload: assert-css: (".since", {"color": "rgb(128, 128, 128)"}) diff --git a/src/test/rustdoc-gui/src/test_docs/lib.rs b/src/test/rustdoc-gui/src/test_docs/lib.rs index aa2f78289bea2..1b26aaecb5ef3 100644 --- a/src/test/rustdoc-gui/src/test_docs/lib.rs +++ b/src/test/rustdoc-gui/src/test_docs/lib.rs @@ -146,6 +146,10 @@ pub use crate::repro as repro2; /// ### Top-doc Prose sub-sub-heading /// /// Text below sub-sub-heading +/// +/// #### You know the drill. +/// +/// More text. pub struct HeavilyDocumentedStruct { /// # Title for field /// ## Sub-heading for field