CACHE_DOMAIN: ci-caches-gha.rust-lang.org
if: "github.event_name == 'push' && github.ref == 'refs/heads/auto' && github.repository == 'rust-lang-ci/rust'"
strategy:
+ fail-fast: false
matrix:
include:
- name: aarch64-gnu
"winapi 0.3.8",
]
-[[package]]
-name = "autocfg"
-version = "0.1.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2"
-
[[package]]
name = "autocfg"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8"
dependencies = [
- "autocfg 1.0.0",
+ "autocfg",
"cfg-if",
"lazy_static",
]
[[package]]
name = "hashbrown"
-version = "0.6.2"
+version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3cd9867f119b19fecb08cd5c326ad4488d7a1da4bf75b4d95d71db742525aaab"
+checksum = "e91b62f79061a0bc2e046024cb7ba44b08419ed238ecbd9adbd787434b9e8c25"
dependencies = [
- "autocfg 0.1.7",
+ "autocfg",
"compiler_builtins",
"rustc-std-workspace-alloc",
"rustc-std-workspace-core",
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1024c0a59774200a555087a6da3f253a9095a5f344e353b212ac4c8b8e450986"
dependencies = [
- "autocfg 1.0.0",
+ "autocfg",
"cc",
"libc",
"openssl-src",
"rustc_macros",
"rustc_serialize",
"rustc_span",
- "scoped-tls",
"smallvec 1.4.0",
"tracing",
]
"ansi_term 0.12.1",
"lazy_static",
"matchers",
- "parking_lot 0.9.0",
+ "parking_lot 0.10.2",
"regex",
"sharded-slab",
"smallvec 1.4.0",
#[unstable(feature = "allocator_api", issue = "32838")]
unsafe impl AllocRef for Global {
#[inline]
- fn alloc(&mut self, layout: Layout, init: AllocInit) -> Result<MemoryBlock, AllocErr> {
- unsafe {
- let size = layout.size();
- if size == 0 {
- Ok(MemoryBlock { ptr: layout.dangling(), size: 0 })
- } else {
- let raw_ptr = match init {
- AllocInit::Uninitialized => alloc(layout),
- AllocInit::Zeroed => alloc_zeroed(layout),
- };
- let ptr = NonNull::new(raw_ptr).ok_or(AllocErr)?;
- Ok(MemoryBlock { ptr, size })
- }
- }
+ fn alloc(&mut self, layout: Layout) -> Result<NonNull<[u8]>, AllocErr> {
+ let size = layout.size();
+ let ptr = if size == 0 {
+ layout.dangling()
+ } else {
+ // SAFETY: `layout` is non-zero in size,
+ unsafe { NonNull::new(alloc(layout)).ok_or(AllocErr)? }
+ };
+ Ok(NonNull::slice_from_raw_parts(ptr, size))
+ }
+
+ #[inline]
+ fn alloc_zeroed(&mut self, layout: Layout) -> Result<NonNull<[u8]>, AllocErr> {
+ let size = layout.size();
+ let ptr = if size == 0 {
+ layout.dangling()
+ } else {
+ // SAFETY: `layout` is non-zero in size,
+ unsafe { NonNull::new(alloc_zeroed(layout)).ok_or(AllocErr)? }
+ };
+ Ok(NonNull::slice_from_raw_parts(ptr, size))
}
#[inline]
unsafe fn dealloc(&mut self, ptr: NonNull<u8>, layout: Layout) {
if layout.size() != 0 {
+ // SAFETY: `layout` is non-zero in size,
+ // other conditions must be upheld by the caller
unsafe { dealloc(ptr.as_ptr(), layout) }
}
}
ptr: NonNull<u8>,
layout: Layout,
new_size: usize,
- placement: ReallocPlacement,
- init: AllocInit,
- ) -> Result<MemoryBlock, AllocErr> {
- let size = layout.size();
+ ) -> Result<NonNull<[u8]>, AllocErr> {
debug_assert!(
- new_size >= size,
- "`new_size` must be greater than or equal to `memory.size()`"
+ new_size >= layout.size(),
+ "`new_size` must be greater than or equal to `layout.size()`"
);
- if size == new_size {
- return Ok(MemoryBlock { ptr, size });
+ // SAFETY: `new_size` must be non-zero, which is checked in the match expression.
+ // Other conditions must be upheld by the caller
+ unsafe {
+ match layout.size() {
+ old_size if old_size == new_size => {
+ Ok(NonNull::slice_from_raw_parts(ptr, new_size))
+ }
+ 0 => self.alloc(Layout::from_size_align_unchecked(new_size, layout.align())),
+ old_size => {
+ // `realloc` probably checks for `new_size > size` or something similar.
+ intrinsics::assume(new_size > old_size);
+ let raw_ptr = realloc(ptr.as_ptr(), layout, new_size);
+ let ptr = NonNull::new(raw_ptr).ok_or(AllocErr)?;
+ Ok(NonNull::slice_from_raw_parts(ptr, new_size))
+ }
+ }
}
+ }
- match placement {
- ReallocPlacement::InPlace => Err(AllocErr),
- ReallocPlacement::MayMove if layout.size() == 0 => {
- let new_layout =
- unsafe { Layout::from_size_align_unchecked(new_size, layout.align()) };
- self.alloc(new_layout, init)
- }
- ReallocPlacement::MayMove => {
- // `realloc` probably checks for `new_size > size` or something similar.
- let ptr = unsafe {
- intrinsics::assume(new_size > size);
- realloc(ptr.as_ptr(), layout, new_size)
- };
- let memory =
- MemoryBlock { ptr: NonNull::new(ptr).ok_or(AllocErr)?, size: new_size };
- unsafe {
- init.init_offset(memory, size);
+ #[inline]
+ unsafe fn grow_zeroed(
+ &mut self,
+ ptr: NonNull<u8>,
+ layout: Layout,
+ new_size: usize,
+ ) -> Result<NonNull<[u8]>, AllocErr> {
+ debug_assert!(
+ new_size >= layout.size(),
+ "`new_size` must be greater than or equal to `layout.size()`"
+ );
+
+ // SAFETY: `new_size` must be non-zero, which is checked in the match expression.
+ // Other conditions must be upheld by the caller
+ unsafe {
+ match layout.size() {
+ old_size if old_size == new_size => {
+ Ok(NonNull::slice_from_raw_parts(ptr, new_size))
+ }
+ 0 => self.alloc_zeroed(Layout::from_size_align_unchecked(new_size, layout.align())),
+ old_size => {
+ // `realloc` probably checks for `new_size > size` or something similar.
+ intrinsics::assume(new_size > old_size);
+ let raw_ptr = realloc(ptr.as_ptr(), layout, new_size);
+ raw_ptr.add(old_size).write_bytes(0, new_size - old_size);
+ let ptr = NonNull::new(raw_ptr).ok_or(AllocErr)?;
+ Ok(NonNull::slice_from_raw_parts(ptr, new_size))
}
- Ok(memory)
}
}
}
ptr: NonNull<u8>,
layout: Layout,
new_size: usize,
- placement: ReallocPlacement,
- ) -> Result<MemoryBlock, AllocErr> {
- let size = layout.size();
+ ) -> Result<NonNull<[u8]>, AllocErr> {
+ let old_size = layout.size();
debug_assert!(
- new_size <= size,
- "`new_size` must be smaller than or equal to `memory.size()`"
+ new_size <= old_size,
+ "`new_size` must be smaller than or equal to `layout.size()`"
);
- if size == new_size {
- return Ok(MemoryBlock { ptr, size });
- }
-
- match placement {
- ReallocPlacement::InPlace => Err(AllocErr),
- ReallocPlacement::MayMove if new_size == 0 => {
- unsafe {
- self.dealloc(ptr, layout);
- }
- Ok(MemoryBlock { ptr: layout.dangling(), size: 0 })
- }
- ReallocPlacement::MayMove => {
- // `realloc` probably checks for `new_size < size` or something similar.
- let ptr = unsafe {
- intrinsics::assume(new_size < size);
- realloc(ptr.as_ptr(), layout, new_size)
- };
- Ok(MemoryBlock { ptr: NonNull::new(ptr).ok_or(AllocErr)?, size: new_size })
+ let ptr = if new_size == old_size {
+ ptr
+ } else if new_size == 0 {
+ // SAFETY: `layout` is non-zero in size as `old_size` != `new_size`
+ // Other conditions must be upheld by the caller
+ unsafe {
+ self.dealloc(ptr, layout);
}
- }
+ layout.dangling()
+ } else {
+ // SAFETY: new_size is not zero,
+ // Other conditions must be upheld by the caller
+ let raw_ptr = unsafe {
+ // `realloc` probably checks for `new_size < old_size` or something similar.
+ intrinsics::assume(new_size < old_size);
+ realloc(ptr.as_ptr(), layout, new_size)
+ };
+ NonNull::new(raw_ptr).ok_or(AllocErr)?
+ };
+
+ Ok(NonNull::slice_from_raw_parts(ptr, new_size))
}
}
#[inline]
unsafe fn exchange_malloc(size: usize, align: usize) -> *mut u8 {
let layout = unsafe { Layout::from_size_align_unchecked(size, align) };
- match Global.alloc(layout, AllocInit::Uninitialized) {
- Ok(memory) => memory.ptr.as_ptr(),
+ match Global.alloc(layout) {
+ Ok(ptr) => ptr.as_non_null_ptr().as_ptr(),
Err(_) => handle_alloc_error(layout),
}
}
fn allocate_zeroed() {
unsafe {
let layout = Layout::from_size_align(1024, 1).unwrap();
- let memory = Global
- .alloc(layout.clone(), AllocInit::Zeroed)
- .unwrap_or_else(|_| handle_alloc_error(layout));
+ let ptr =
+ Global.alloc_zeroed(layout.clone()).unwrap_or_else(|_| handle_alloc_error(layout));
- let mut i = memory.ptr.cast::<u8>().as_ptr();
+ let mut i = ptr.as_non_null_ptr().as_ptr();
let end = i.add(layout.size());
while i < end {
assert_eq!(*i, 0);
i = i.offset(1);
}
- Global.dealloc(memory.ptr, layout);
+ Global.dealloc(ptr.as_non_null_ptr(), layout);
}
}
use core::ptr::{self, Unique};
use core::task::{Context, Poll};
-use crate::alloc::{self, AllocInit, AllocRef, Global};
+use crate::alloc::{self, AllocRef, Global};
use crate::borrow::Cow;
use crate::raw_vec::RawVec;
use crate::str::from_boxed_utf8_unchecked;
#[unstable(feature = "new_uninit", issue = "63291")]
pub fn new_uninit() -> Box<mem::MaybeUninit<T>> {
let layout = alloc::Layout::new::<mem::MaybeUninit<T>>();
- let ptr = Global
- .alloc(layout, AllocInit::Uninitialized)
- .unwrap_or_else(|_| alloc::handle_alloc_error(layout))
- .ptr
- .cast();
+ let ptr = Global.alloc(layout).unwrap_or_else(|_| alloc::handle_alloc_error(layout)).cast();
unsafe { Box::from_raw(ptr.as_ptr()) }
}
pub fn new_zeroed() -> Box<mem::MaybeUninit<T>> {
let layout = alloc::Layout::new::<mem::MaybeUninit<T>>();
let ptr = Global
- .alloc(layout, AllocInit::Zeroed)
+ .alloc_zeroed(layout)
.unwrap_or_else(|_| alloc::handle_alloc_error(layout))
- .ptr
.cast();
unsafe { Box::from_raw(ptr.as_ptr()) }
}
+// ignore-tidy-filelength
+
use core::borrow::Borrow;
use core::cmp::Ordering;
use core::fmt::Debug;
inner: IterMut<'a, K, V>,
}
+/// An owning iterator over the keys of a `BTreeMap`.
+///
+/// This `struct` is created by the [`into_keys`] method on [`BTreeMap`].
+/// See its documentation for more.
+///
+/// [`into_keys`]: BTreeMap::into_keys
+#[unstable(feature = "map_into_keys_values", issue = "75294")]
+#[derive(Debug)]
+pub struct IntoKeys<K, V> {
+ inner: IntoIter<K, V>,
+}
+
+/// An owning iterator over the values of a `BTreeMap`.
+///
+/// This `struct` is created by the [`into_values`] method on [`BTreeMap`].
+/// See its documentation for more.
+///
+/// [`into_values`]: BTreeMap::into_values
+#[unstable(feature = "map_into_keys_values", issue = "75294")]
+#[derive(Debug)]
+pub struct IntoValues<K, V> {
+ inner: IntoIter<K, V>,
+}
+
/// An iterator over a sub-range of entries in a `BTreeMap`.
///
/// This `struct` is created by the [`range`] method on [`BTreeMap`]. See its
self.length = dfs(self.root.as_ref().unwrap().as_ref());
}
+
+ /// Creates a consuming iterator visiting all the keys, in sorted order.
+ /// The map cannot be used after calling this.
+ /// The iterator element type is `K`.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// #![feature(map_into_keys_values)]
+ /// use std::collections::BTreeMap;
+ ///
+ /// let mut a = BTreeMap::new();
+ /// a.insert(2, "b");
+ /// a.insert(1, "a");
+ ///
+ /// let keys: Vec<i32> = a.into_keys().collect();
+ /// assert_eq!(keys, [1, 2]);
+ /// ```
+ #[inline]
+ #[unstable(feature = "map_into_keys_values", issue = "75294")]
+ pub fn into_keys(self) -> IntoKeys<K, V> {
+ IntoKeys { inner: self.into_iter() }
+ }
+
+ /// Creates a consuming iterator visiting all the values, in order by key.
+ /// The map cannot be used after calling this.
+ /// The iterator element type is `V`.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// #![feature(map_into_keys_values)]
+ /// use std::collections::BTreeMap;
+ ///
+ /// let mut a = BTreeMap::new();
+ /// a.insert(1, "hello");
+ /// a.insert(2, "goodbye");
+ ///
+ /// let values: Vec<&str> = a.into_values().collect();
+ /// assert_eq!(values, ["hello", "goodbye"]);
+ /// ```
+ #[inline]
+ #[unstable(feature = "map_into_keys_values", issue = "75294")]
+ pub fn into_values(self) -> IntoValues<K, V> {
+ IntoValues { inner: self.into_iter() }
+ }
}
#[stable(feature = "rust1", since = "1.0.0")]
-impl<'a, K: 'a, V: 'a> IntoIterator for &'a BTreeMap<K, V> {
+impl<'a, K, V> IntoIterator for &'a BTreeMap<K, V> {
type Item = (&'a K, &'a V);
type IntoIter = Iter<'a, K, V>;
}
#[stable(feature = "rust1", since = "1.0.0")]
-impl<'a, K: 'a, V: 'a> IntoIterator for &'a mut BTreeMap<K, V> {
+impl<'a, K, V> IntoIterator for &'a mut BTreeMap<K, V> {
type Item = (&'a K, &'a mut V);
type IntoIter = IterMut<'a, K, V>;
let (k, v) = kv.kv_mut();
if pred(k, v) {
*self.length -= 1;
- let RemoveResult { old_kv, pos, emptied_internal_root } = kv.remove_kv_tracking();
+ let (kv, pos) = kv.remove_kv_tracking(|_| self.emptied_internal_root = true);
self.cur_leaf_edge = Some(pos);
- self.emptied_internal_root |= emptied_internal_root;
- return Some(old_kv);
+ return Some(kv);
}
self.cur_leaf_edge = Some(kv.next_leaf_edge());
}
}
}
+#[unstable(feature = "map_into_keys_values", issue = "75294")]
+impl<K, V> Iterator for IntoKeys<K, V> {
+ type Item = K;
+
+ fn next(&mut self) -> Option<K> {
+ self.inner.next().map(|(k, _)| k)
+ }
+
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ self.inner.size_hint()
+ }
+
+ fn last(mut self) -> Option<K> {
+ self.next_back()
+ }
+
+ fn min(mut self) -> Option<K> {
+ self.next()
+ }
+
+ fn max(mut self) -> Option<K> {
+ self.next_back()
+ }
+}
+
+#[unstable(feature = "map_into_keys_values", issue = "75294")]
+impl<K, V> DoubleEndedIterator for IntoKeys<K, V> {
+ fn next_back(&mut self) -> Option<K> {
+ self.inner.next_back().map(|(k, _)| k)
+ }
+}
+
+#[unstable(feature = "map_into_keys_values", issue = "75294")]
+impl<K, V> ExactSizeIterator for IntoKeys<K, V> {
+ fn len(&self) -> usize {
+ self.inner.len()
+ }
+}
+
+#[unstable(feature = "map_into_keys_values", issue = "75294")]
+impl<K, V> FusedIterator for IntoKeys<K, V> {}
+
+#[unstable(feature = "map_into_keys_values", issue = "75294")]
+impl<K, V> Iterator for IntoValues<K, V> {
+ type Item = V;
+
+ fn next(&mut self) -> Option<V> {
+ self.inner.next().map(|(_, v)| v)
+ }
+
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ self.inner.size_hint()
+ }
+
+ fn last(mut self) -> Option<V> {
+ self.next_back()
+ }
+}
+
+#[unstable(feature = "map_into_keys_values", issue = "75294")]
+impl<K, V> DoubleEndedIterator for IntoValues<K, V> {
+ fn next_back(&mut self) -> Option<V> {
+ self.inner.next_back().map(|(_, v)| v)
+ }
+}
+
+#[unstable(feature = "map_into_keys_values", issue = "75294")]
+impl<K, V> ExactSizeIterator for IntoValues<K, V> {
+ fn len(&self) -> usize {
+ self.inner.len()
+ }
+}
+
+#[unstable(feature = "map_into_keys_values", issue = "75294")]
+impl<K, V> FusedIterator for IntoValues<K, V> {}
+
#[stable(feature = "btree_range", since = "1.17.0")]
impl<'a, K, V> DoubleEndedIterator for Range<'a, K, V> {
fn next_back(&mut self) -> Option<(&'a K, &'a V)> {
fn remove_kv(self) -> (K, V) {
*self.length -= 1;
- let RemoveResult { old_kv, pos, emptied_internal_root } = self.handle.remove_kv_tracking();
- let root = pos.into_node().into_root_mut();
- if emptied_internal_root {
- root.pop_internal_level();
- }
+ let (old_kv, _) =
+ self.handle.remove_kv_tracking(|root| root.into_root_mut().pop_internal_level());
old_kv
}
}
-struct RemoveResult<'a, K, V> {
- // Key and value removed.
- old_kv: (K, V),
- // Unique location at the leaf level that the removed KV lopgically collapsed into.
- pos: Handle<NodeRef<marker::Mut<'a>, K, V, marker::Leaf>, marker::Edge>,
- // Whether the remove left behind and empty internal root node, that should be removed
- // using `pop_internal_level`.
- emptied_internal_root: bool,
-}
-
impl<'a, K: 'a, V: 'a> Handle<NodeRef<marker::Mut<'a>, K, V, marker::LeafOrInternal>, marker::KV> {
/// Removes a key/value-pair from the tree, and returns that pair, as well as
/// the leaf edge corresponding to that former pair. It's possible this leaves
/// an empty internal root node, which the caller should subsequently pop from
/// the map holding the tree. The caller should also decrement the map's length.
- fn remove_kv_tracking(self) -> RemoveResult<'a, K, V> {
- let (mut pos, old_key, old_val, was_internal) = match self.force() {
+ fn remove_kv_tracking<F>(
+ self,
+ handle_emptied_internal_root: F,
+ ) -> ((K, V), Handle<NodeRef<marker::Mut<'a>, K, V, marker::Leaf>, marker::Edge>)
+ where
+ F: FnOnce(NodeRef<marker::Mut<'a>, K, V, marker::Internal>),
+ {
+ let (old_kv, mut pos, was_internal) = match self.force() {
Leaf(leaf) => {
- let (hole, old_key, old_val) = leaf.remove();
- (hole, old_key, old_val, false)
+ let (old_kv, pos) = leaf.remove();
+ (old_kv, pos, false)
}
Internal(mut internal) => {
// Replace the location freed in the internal node with the next KV,
let to_remove = internal.left_edge().descend().last_leaf_edge().left_kv().ok();
let to_remove = unsafe { unwrap_unchecked(to_remove) };
- let (hole, key, val) = to_remove.remove();
+ let (kv, pos) = to_remove.remove();
- let old_key = unsafe { mem::replace(&mut *key_loc, key) };
- let old_val = unsafe { mem::replace(&mut *val_loc, val) };
+ let old_key = unsafe { mem::replace(&mut *key_loc, kv.0) };
+ let old_val = unsafe { mem::replace(&mut *val_loc, kv.1) };
- (hole, old_key, old_val, true)
+ ((old_key, old_val), pos, true)
}
};
// Handle underflow
- let mut emptied_internal_root = false;
let mut cur_node = unsafe { ptr::read(&pos).into_node().forget_type() };
let mut at_leaf = true;
while cur_node.len() < node::MIN_LEN {
let parent = edge.into_node();
if parent.len() == 0 {
- // This empty parent must be the root, and should be popped off the tree.
- emptied_internal_root = true;
+ // The parent that was just emptied must be the root,
+ // because nodes on a lower level would not have been
+ // left underfull. It has to be popped off the tree soon.
+ handle_emptied_internal_root(parent);
break;
} else {
cur_node = parent.forget_type();
pos = unsafe { unwrap_unchecked(pos.next_kv().ok()).next_leaf_edge() };
}
- RemoveResult { old_kv: (old_key, old_val), pos, emptied_internal_root }
+ (old_kv, pos)
}
}
+use core::intrinsics;
+use core::mem;
use core::ptr;
use super::node::{marker, ForceResult::*, Handle, NodeRef};
def_next_kv_uncheched_dealloc! {unsafe fn next_back_kv_unchecked_dealloc: left_kv}
/// This replaces the value behind the `v` unique reference by calling the
-/// relevant function.
+/// relevant function, and returns a result obtained along the way.
///
-/// Safety: The change closure must not panic.
+/// If a panic occurs in the `change` closure, the entire process will be aborted.
#[inline]
-unsafe fn replace<T, R>(v: &mut T, change: impl FnOnce(T) -> (T, R)) -> R {
+fn replace<T, R>(v: &mut T, change: impl FnOnce(T) -> (T, R)) -> R {
+ struct PanicGuard;
+ impl Drop for PanicGuard {
+ fn drop(&mut self) {
+ intrinsics::abort()
+ }
+ }
+ let guard = PanicGuard;
let value = unsafe { ptr::read(v) };
let (new_value, ret) = change(value);
unsafe {
ptr::write(v, new_value);
}
+ mem::forget(guard);
ret
}
/// key and value in between.
/// Unsafe because the caller must ensure that the leaf edge is not the last one in the tree.
pub unsafe fn next_unchecked(&mut self) -> (&'a K, &'a V) {
- unsafe {
- replace(self, |leaf_edge| {
- let kv = leaf_edge.next_kv();
- let kv = unwrap_unchecked(kv.ok());
- (kv.next_leaf_edge(), kv.into_kv())
- })
- }
+ replace(self, |leaf_edge| {
+ let kv = leaf_edge.next_kv();
+ let kv = unsafe { unwrap_unchecked(kv.ok()) };
+ (kv.next_leaf_edge(), kv.into_kv())
+ })
}
/// Moves the leaf edge handle to the previous leaf edge and returns references to the
/// key and value in between.
/// Unsafe because the caller must ensure that the leaf edge is not the first one in the tree.
pub unsafe fn next_back_unchecked(&mut self) -> (&'a K, &'a V) {
- unsafe {
- replace(self, |leaf_edge| {
- let kv = leaf_edge.next_back_kv();
- let kv = unwrap_unchecked(kv.ok());
- (kv.next_back_leaf_edge(), kv.into_kv())
- })
- }
+ replace(self, |leaf_edge| {
+ let kv = leaf_edge.next_back_kv();
+ let kv = unsafe { unwrap_unchecked(kv.ok()) };
+ (kv.next_back_leaf_edge(), kv.into_kv())
+ })
}
}
/// - The caller must ensure that the leaf edge is not the last one in the tree.
/// - Using the updated handle may well invalidate the returned references.
pub unsafe fn next_unchecked(&mut self) -> (&'a mut K, &'a mut V) {
- unsafe {
- let kv = replace(self, |leaf_edge| {
- let kv = leaf_edge.next_kv();
- let kv = unwrap_unchecked(kv.ok());
- (ptr::read(&kv).next_leaf_edge(), kv)
- });
- // Doing the descend (and perhaps another move) invalidates the references
- // returned by `into_kv_mut`, so we have to do this last.
- kv.into_kv_mut()
- }
+ let kv = replace(self, |leaf_edge| {
+ let kv = leaf_edge.next_kv();
+ let kv = unsafe { unwrap_unchecked(kv.ok()) };
+ (unsafe { ptr::read(&kv) }.next_leaf_edge(), kv)
+ });
+ // Doing the descend (and perhaps another move) invalidates the references
+ // returned by `into_kv_mut`, so we have to do this last.
+ kv.into_kv_mut()
}
/// Moves the leaf edge handle to the previous leaf and returns references to the
/// - The caller must ensure that the leaf edge is not the first one in the tree.
/// - Using the updated handle may well invalidate the returned references.
pub unsafe fn next_back_unchecked(&mut self) -> (&'a mut K, &'a mut V) {
- unsafe {
- let kv = replace(self, |leaf_edge| {
- let kv = leaf_edge.next_back_kv();
- let kv = unwrap_unchecked(kv.ok());
- (ptr::read(&kv).next_back_leaf_edge(), kv)
- });
- // Doing the descend (and perhaps another move) invalidates the references
- // returned by `into_kv_mut`, so we have to do this last.
- kv.into_kv_mut()
- }
+ let kv = replace(self, |leaf_edge| {
+ let kv = leaf_edge.next_back_kv();
+ let kv = unsafe { unwrap_unchecked(kv.ok()) };
+ (unsafe { ptr::read(&kv) }.next_back_leaf_edge(), kv)
+ });
+ // Doing the descend (and perhaps another move) invalidates the references
+ // returned by `into_kv_mut`, so we have to do this last.
+ kv.into_kv_mut()
}
}
/// call this method again subject to both preconditions listed in the first point,
/// or call counterpart `next_back_unchecked` subject to its preconditions.
pub unsafe fn next_unchecked(&mut self) -> (K, V) {
- unsafe {
- replace(self, |leaf_edge| {
- let kv = next_kv_unchecked_dealloc(leaf_edge);
- let k = ptr::read(kv.reborrow().into_kv().0);
- let v = ptr::read(kv.reborrow().into_kv().1);
- (kv.next_leaf_edge(), (k, v))
- })
- }
+ replace(self, |leaf_edge| {
+ let kv = unsafe { next_kv_unchecked_dealloc(leaf_edge) };
+ let k = unsafe { ptr::read(kv.reborrow().into_kv().0) };
+ let v = unsafe { ptr::read(kv.reborrow().into_kv().1) };
+ (kv.next_leaf_edge(), (k, v))
+ })
}
/// Moves the leaf edge handle to the previous leaf edge and returns the key
/// call this method again subject to both preconditions listed in the first point,
/// or call counterpart `next_unchecked` subject to its preconditions.
pub unsafe fn next_back_unchecked(&mut self) -> (K, V) {
- unsafe {
- replace(self, |leaf_edge| {
- let kv = next_back_kv_unchecked_dealloc(leaf_edge);
- let k = ptr::read(kv.reborrow().into_kv().0);
- let v = ptr::read(kv.reborrow().into_kv().1);
- (kv.next_back_leaf_edge(), (k, v))
- })
- }
+ replace(self, |leaf_edge| {
+ let kv = unsafe { next_back_kv_unchecked_dealloc(leaf_edge) };
+ let k = unsafe { ptr::read(kv.reborrow().into_kv().0) };
+ let v = unsafe { ptr::read(kv.reborrow().into_kv().1) };
+ (kv.next_back_leaf_edge(), (k, v))
+ })
}
}
/// between the now adjacent key/value pairs (if any) to the left and right of this handle.
pub fn remove(
mut self,
- ) -> (Handle<NodeRef<marker::Mut<'a>, K, V, marker::Leaf>, marker::Edge>, K, V) {
+ ) -> ((K, V), Handle<NodeRef<marker::Mut<'a>, K, V, marker::Leaf>, marker::Edge>) {
unsafe {
let k = slice_remove(self.node.keys_mut(), self.idx);
let v = slice_remove(self.node.vals_mut(), self.idx);
(*self.node.as_leaf_mut()).len -= 1;
- (self.left_edge(), k, v)
+ ((k, v), self.left_edge())
}
}
}
#![feature(negative_impls)]
#![feature(new_uninit)]
#![feature(nll)]
+#![feature(nonnull_slice_from_raw_parts)]
#![feature(optin_builtin_traits)]
#![feature(or_patterns)]
#![feature(pattern)]
#![feature(rustc_attrs)]
#![feature(receiver_trait)]
#![feature(min_specialization)]
+#![feature(slice_ptr_get)]
+#![feature(slice_ptr_len)]
#![feature(staged_api)]
#![feature(std_internals)]
#![feature(str_internals)]
#![unstable(feature = "raw_vec_internals", reason = "implementation detail", issue = "none")]
#![doc(hidden)]
-use core::alloc::{LayoutErr, MemoryBlock};
+use core::alloc::LayoutErr;
use core::cmp;
use core::mem::{self, ManuallyDrop, MaybeUninit};
use core::ops::Drop;
use core::ptr::{NonNull, Unique};
use core::slice;
-use crate::alloc::{
- handle_alloc_error,
- AllocInit::{self, *},
- AllocRef, Global, Layout,
- ReallocPlacement::{self, *},
-};
+use crate::alloc::{handle_alloc_error, AllocRef, Global, Layout};
use crate::boxed::Box;
use crate::collections::TryReserveError::{self, *};
#[cfg(test)]
mod tests;
+enum AllocInit {
+ /// The contents of the new memory are uninitialized.
+ Uninitialized,
+ /// The new memory is guaranteed to be zeroed.
+ Zeroed,
+}
+
/// A low-level utility for more ergonomically allocating, reallocating, and deallocating
/// a buffer of memory on the heap without having to worry about all the corner cases
/// involved. This type is excellent for building your own data structures like Vec and VecDeque.
/// allocator for the returned `RawVec`.
#[inline]
pub fn with_capacity_in(capacity: usize, alloc: A) -> Self {
- Self::allocate_in(capacity, Uninitialized, alloc)
+ Self::allocate_in(capacity, AllocInit::Uninitialized, alloc)
}
/// Like `with_capacity_zeroed`, but parameterized over the choice
/// of allocator for the returned `RawVec`.
#[inline]
pub fn with_capacity_zeroed_in(capacity: usize, alloc: A) -> Self {
- Self::allocate_in(capacity, Zeroed, alloc)
+ Self::allocate_in(capacity, AllocInit::Zeroed, alloc)
}
fn allocate_in(capacity: usize, init: AllocInit, mut alloc: A) -> Self {
Ok(_) => {}
Err(_) => capacity_overflow(),
}
- let memory = match alloc.alloc(layout, init) {
- Ok(memory) => memory,
+ let result = match init {
+ AllocInit::Uninitialized => alloc.alloc(layout),
+ AllocInit::Zeroed => alloc.alloc_zeroed(layout),
+ };
+ let ptr = match result {
+ Ok(ptr) => ptr,
Err(_) => handle_alloc_error(layout),
};
Self {
- ptr: unsafe { Unique::new_unchecked(memory.ptr.cast().as_ptr()) },
- cap: Self::capacity_from_bytes(memory.size),
+ ptr: unsafe { Unique::new_unchecked(ptr.cast().as_ptr()) },
+ cap: Self::capacity_from_bytes(ptr.len()),
alloc,
}
}
///
/// # Safety
///
- /// The `ptr` must be allocated (via the given allocator `a`), and with the given `capacity`.
+ /// The `ptr` must be allocated (via the given allocator `alloc`), and with the given
+ /// `capacity`.
/// The `capacity` cannot exceed `isize::MAX` for sized types. (only a concern on 32-bit
/// systems). ZST vectors may have a capacity up to `usize::MAX`.
- /// If the `ptr` and `capacity` come from a `RawVec` created via `a`, then this is guaranteed.
+ /// If the `ptr` and `capacity` come from a `RawVec` created via `alloc`, then this is
+ /// guaranteed.
#[inline]
- pub unsafe fn from_raw_parts_in(ptr: *mut T, capacity: usize, a: A) -> Self {
- Self { ptr: unsafe { Unique::new_unchecked(ptr) }, cap: capacity, alloc: a }
+ pub unsafe fn from_raw_parts_in(ptr: *mut T, capacity: usize, alloc: A) -> Self {
+ Self { ptr: unsafe { Unique::new_unchecked(ptr) }, cap: capacity, alloc }
}
/// Gets a raw pointer to the start of the allocation. Note that this is
///
/// Aborts on OOM.
pub fn shrink_to_fit(&mut self, amount: usize) {
- match self.shrink(amount, MayMove) {
+ match self.shrink(amount) {
Err(CapacityOverflow) => capacity_overflow(),
Err(AllocError { layout, .. }) => handle_alloc_error(layout),
Ok(()) => { /* yay */ }
excess / mem::size_of::<T>()
}
- fn set_memory(&mut self, memory: MemoryBlock) {
- self.ptr = unsafe { Unique::new_unchecked(memory.ptr.cast().as_ptr()) };
- self.cap = Self::capacity_from_bytes(memory.size);
+ fn set_ptr(&mut self, ptr: NonNull<[u8]>) {
+ self.ptr = unsafe { Unique::new_unchecked(ptr.cast().as_ptr()) };
+ self.cap = Self::capacity_from_bytes(ptr.len());
}
// This method is usually instantiated many times. So we want it to be as
let new_layout = Layout::array::<T>(cap);
// `finish_grow` is non-generic over `T`.
- let memory = finish_grow(new_layout, self.current_memory(), &mut self.alloc)?;
- self.set_memory(memory);
+ let ptr = finish_grow(new_layout, self.current_memory(), &mut self.alloc)?;
+ self.set_ptr(ptr);
Ok(())
}
let new_layout = Layout::array::<T>(cap);
// `finish_grow` is non-generic over `T`.
- let memory = finish_grow(new_layout, self.current_memory(), &mut self.alloc)?;
- self.set_memory(memory);
+ let ptr = finish_grow(new_layout, self.current_memory(), &mut self.alloc)?;
+ self.set_ptr(ptr);
Ok(())
}
- fn shrink(
- &mut self,
- amount: usize,
- placement: ReallocPlacement,
- ) -> Result<(), TryReserveError> {
+ fn shrink(&mut self, amount: usize) -> Result<(), TryReserveError> {
assert!(amount <= self.capacity(), "Tried to shrink to a larger capacity");
let (ptr, layout) = if let Some(mem) = self.current_memory() { mem } else { return Ok(()) };
let new_size = amount * mem::size_of::<T>();
- let memory = unsafe {
- self.alloc.shrink(ptr, layout, new_size, placement).map_err(|_| {
- TryReserveError::AllocError {
- layout: Layout::from_size_align_unchecked(new_size, layout.align()),
- non_exhaustive: (),
- }
+ let ptr = unsafe {
+ self.alloc.shrink(ptr, layout, new_size).map_err(|_| TryReserveError::AllocError {
+ layout: Layout::from_size_align_unchecked(new_size, layout.align()),
+ non_exhaustive: (),
})?
};
- self.set_memory(memory);
+ self.set_ptr(ptr);
Ok(())
}
}
new_layout: Result<Layout, LayoutErr>,
current_memory: Option<(NonNull<u8>, Layout)>,
alloc: &mut A,
-) -> Result<MemoryBlock, TryReserveError>
+) -> Result<NonNull<[u8]>, TryReserveError>
where
A: AllocRef,
{
let memory = if let Some((ptr, old_layout)) = current_memory {
debug_assert_eq!(old_layout.align(), new_layout.align());
- unsafe { alloc.grow(ptr, old_layout, new_layout.size(), MayMove, Uninitialized) }
+ unsafe { alloc.grow(ptr, old_layout, new_layout.size()) }
} else {
- alloc.alloc(new_layout, Uninitialized)
+ alloc.alloc(new_layout)
}
.map_err(|_| AllocError { layout: new_layout, non_exhaustive: () })?;
fuel: usize,
}
unsafe impl AllocRef for BoundedAlloc {
- fn alloc(&mut self, layout: Layout, init: AllocInit) -> Result<MemoryBlock, AllocErr> {
+ fn alloc(&mut self, layout: Layout) -> Result<NonNull<[u8]>, AllocErr> {
let size = layout.size();
if size > self.fuel {
return Err(AllocErr);
}
- match Global.alloc(layout, init) {
+ match Global.alloc(layout) {
ok @ Ok(_) => {
self.fuel -= size;
ok
use core::ptr::{self, NonNull};
use core::slice::from_raw_parts_mut;
-use crate::alloc::{box_free, handle_alloc_error, AllocInit, AllocRef, Global, Layout};
+use crate::alloc::{box_free, handle_alloc_error, AllocRef, Global, Layout};
use crate::borrow::{Cow, ToOwned};
use crate::string::String;
use crate::vec::Vec;
let layout = Layout::new::<RcBox<()>>().extend(value_layout).unwrap().0.pad_to_align();
// Allocate for the layout.
- let mem = Global
- .alloc(layout, AllocInit::Uninitialized)
- .unwrap_or_else(|_| handle_alloc_error(layout));
+ let ptr = Global.alloc(layout).unwrap_or_else(|_| handle_alloc_error(layout));
// Initialize the RcBox
- let inner = mem_to_rcbox(mem.ptr.as_ptr());
+ let inner = mem_to_rcbox(ptr.as_non_null_ptr().as_ptr());
unsafe {
debug_assert_eq!(Layout::for_value(&*inner), layout);
use core::sync::atomic;
use core::sync::atomic::Ordering::{Acquire, Relaxed, Release, SeqCst};
-use crate::alloc::{box_free, handle_alloc_error, AllocInit, AllocRef, Global, Layout};
+use crate::alloc::{box_free, handle_alloc_error, AllocRef, Global, Layout};
use crate::borrow::{Cow, ToOwned};
use crate::boxed::Box;
use crate::rc::is_dangling;
// reference (see #54908).
let layout = Layout::new::<ArcInner<()>>().extend(value_layout).unwrap().0.pad_to_align();
- let mem = Global
- .alloc(layout, AllocInit::Uninitialized)
- .unwrap_or_else(|_| handle_alloc_error(layout));
+ let ptr = Global.alloc(layout).unwrap_or_else(|_| handle_alloc_error(layout));
// Initialize the ArcInner
- let inner = mem_to_arcinner(mem.ptr.as_ptr());
+ let inner = mem_to_arcinner(ptr.as_non_null_ptr().as_ptr());
debug_assert_eq!(unsafe { Layout::for_value(&*inner) }, layout);
unsafe {
let slice = from_raw_parts_mut(self.elems, self.n_elems);
ptr::drop_in_place(slice);
- Global.dealloc(self.mem.cast(), self.layout);
+ Global.dealloc(self.mem, self.layout);
}
}
}
assert_eq!(DROPS.load(Ordering::SeqCst), size);
}
}
+
+#[test]
+fn test_into_keys() {
+ let vec = vec![(1, 'a'), (2, 'b'), (3, 'c')];
+ let map: BTreeMap<_, _> = vec.into_iter().collect();
+ let keys: Vec<_> = map.into_keys().collect();
+
+ assert_eq!(keys.len(), 3);
+ assert!(keys.contains(&1));
+ assert!(keys.contains(&2));
+ assert!(keys.contains(&3));
+}
+
+#[test]
+fn test_into_values() {
+ let vec = vec![(1, 'a'), (2, 'b'), (3, 'c')];
+ let map: BTreeMap<_, _> = vec.into_iter().collect();
+ let values: Vec<_> = map.into_values().collect();
+
+ assert_eq!(values.len(), 3);
+ assert!(values.contains(&'a'));
+ assert!(values.contains(&'b'));
+ assert!(values.contains(&'c'));
+}
-use std::alloc::{AllocInit, AllocRef, Global, Layout, System};
+use std::alloc::{AllocRef, Global, Layout, System};
/// Issue #45955 and #62251.
#[test]
unsafe {
let pointers: Vec<_> = (0..iterations)
.map(|_| {
- allocator
- .alloc(
- Layout::from_size_align(size, align).unwrap(),
- AllocInit::Uninitialized,
- )
- .unwrap()
- .ptr
+ allocator.alloc(Layout::from_size_align(size, align).unwrap()).unwrap()
})
.collect();
for &ptr in &pointers {
assert_eq!(
- (ptr.as_ptr() as usize) % align,
+ (ptr.as_non_null_ptr().as_ptr() as usize) % align,
0,
"Got a pointer less aligned than requested"
)
// Clean up
for &ptr in &pointers {
- allocator.dealloc(ptr, Layout::from_size_align(size, align).unwrap())
+ allocator.dealloc(
+ ptr.as_non_null_ptr(),
+ Layout::from_size_align(size, align).unwrap(),
+ )
}
}
}
#![feature(drain_filter)]
#![feature(exact_size_is_empty)]
#![feature(map_first_last)]
+#![feature(map_into_keys_values)]
#![feature(new_uninit)]
#![feature(pattern)]
#![feature(str_split_once)]
#![feature(associated_type_bounds)]
#![feature(binary_heap_into_iter_sorted)]
#![feature(binary_heap_drain_sorted)]
+#![feature(slice_ptr_get)]
#![feature(split_inclusive)]
#![feature(binary_heap_retain)]
}
}
-/// A desired initial state for allocated memory.
-#[derive(Debug, Copy, Clone, PartialEq, Eq)]
-#[unstable(feature = "allocator_api", issue = "32838")]
-pub enum AllocInit {
- /// The contents of the new memory are uninitialized.
- Uninitialized,
- /// The new memory is guaranteed to be zeroed.
- Zeroed,
-}
-
-impl AllocInit {
- /// Initialize the specified memory block.
- ///
- /// This behaves like calling [`AllocInit::init_offset(memory, 0)`][off].
- ///
- /// [off]: AllocInit::init_offset
- ///
- /// # Safety
- ///
- /// * `memory.ptr` must be [valid] for writes of `memory.size` bytes.
- ///
- /// [valid]: ../../core/ptr/index.html#safety
- #[inline]
- #[unstable(feature = "allocator_api", issue = "32838")]
- pub unsafe fn init(self, memory: MemoryBlock) {
- // SAFETY: the safety contract for `init_offset` must be
- // upheld by the caller.
- unsafe { self.init_offset(memory, 0) }
- }
-
- /// Initialize the memory block like specified by `init` at the specified `offset`.
- ///
- /// This is a no-op for [`AllocInit::Uninitialized`][] and writes zeroes for
- /// [`AllocInit::Zeroed`][] at `ptr + offset` until `ptr + layout.size()`.
- ///
- /// # Safety
- ///
- /// * `memory.ptr` must be [valid] for writes of `memory.size` bytes.
- /// * `offset` must be smaller than or equal to `memory.size`
- ///
- /// [valid]: ../../core/ptr/index.html#safety
- #[inline]
- #[unstable(feature = "allocator_api", issue = "32838")]
- pub unsafe fn init_offset(self, memory: MemoryBlock, offset: usize) {
- debug_assert!(
- offset <= memory.size,
- "`offset` must be smaller than or equal to `memory.size`"
- );
- match self {
- AllocInit::Uninitialized => (),
- AllocInit::Zeroed => {
- // SAFETY: the caller must guarantee that `offset` is smaller than or equal to `memory.size`,
- // so the memory from `memory.ptr + offset` of length `memory.size - offset`
- // is guaranteed to be contaned in `memory` and thus valid for writes.
- unsafe { memory.ptr.as_ptr().add(offset).write_bytes(0, memory.size - offset) }
- }
- }
- }
-}
-
-/// Represents a block of allocated memory returned by an allocator.
-#[derive(Debug, Copy, Clone)]
-#[unstable(feature = "allocator_api", issue = "32838")]
-pub struct MemoryBlock {
- pub ptr: NonNull<u8>,
- pub size: usize,
-}
-
-/// A placement constraint when growing or shrinking an existing allocation.
-#[derive(Debug, Copy, Clone, PartialEq, Eq)]
-#[unstable(feature = "allocator_api", issue = "32838")]
-pub enum ReallocPlacement {
- /// The allocator is allowed to move the allocation to a different memory address.
- // FIXME(wg-allocators#46): Add a section to the module documentation "What is a legal
- // allocator" and link it at "valid location".
- ///
- /// If the allocation _does_ move, it's the responsibility of the allocator
- /// to also move the data from the previous location to the new location.
- MayMove,
- /// The address of the new memory must not change.
- ///
- /// If the allocation would have to be moved to a new location to fit, the
- /// reallocation request will fail.
- InPlace,
-}
-
/// An implementation of `AllocRef` can allocate, grow, shrink, and deallocate arbitrary blocks of
/// data described via [`Layout`][].
///
pub unsafe trait AllocRef {
/// Attempts to allocate a block of memory.
///
- /// On success, returns a [`MemoryBlock`][] meeting the size and alignment guarantees of `layout`.
+ /// On success, returns a [`NonNull<[u8]>`] meeting the size and alignment guarantees of `layout`.
///
- /// The returned block may have a larger size than specified by `layout.size()` and is
- /// initialized as specified by [`init`], all the way up to the returned size of the block.
+ /// The returned block may have a larger size than specified by `layout.size()`, and may or may
+ /// not have its contents initialized.
///
- /// [`init`]: AllocInit
+ /// [`NonNull<[u8]>`]: NonNull
///
/// # Errors
///
/// call the [`handle_alloc_error`] function, rather than directly invoking `panic!` or similar.
///
/// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html
- fn alloc(&mut self, layout: Layout, init: AllocInit) -> Result<MemoryBlock, AllocErr>;
+ fn alloc(&mut self, layout: Layout) -> Result<NonNull<[u8]>, AllocErr>;
+
+ /// Behaves like `alloc`, but also ensures that the returned memory is zero-initialized.
+ ///
+ /// # Errors
+ ///
+ /// Returning `Err` indicates that either memory is exhausted or `layout` does not meet
+ /// allocator's size or alignment constraints.
+ ///
+ /// Implementations are encouraged to return `Err` on memory exhaustion rather than panicking or
+ /// aborting, but this is not a strict requirement. (Specifically: it is *legal* to implement
+ /// this trait atop an underlying native allocation library that aborts on memory exhaustion.)
+ ///
+ /// Clients wishing to abort computation in response to an allocation error are encouraged to
+ /// call the [`handle_alloc_error`] function, rather than directly invoking `panic!` or similar.
+ ///
+ /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html
+ fn alloc_zeroed(&mut self, layout: Layout) -> Result<NonNull<[u8]>, AllocErr> {
+ let ptr = self.alloc(layout)?;
+ // SAFETY: `alloc` returns a valid memory block
+ unsafe { ptr.as_non_null_ptr().as_ptr().write_bytes(0, ptr.len()) }
+ Ok(ptr)
+ }
/// Deallocates the memory referenced by `ptr`.
///
/// Attempts to extend the memory block.
///
- /// Returns a new [`MemoryBlock`][] containing a pointer and the actual size of the allocated
+ /// Returns a new [`NonNull<[u8]>`] containing a pointer and the actual size of the allocated
/// memory. The pointer is suitable for holding data described by a new layout with `layout`’s
/// alignment and a size given by `new_size`. To accomplish this, the allocator may extend the
- /// allocation referenced by `ptr` to fit the new layout. If the [`placement`] is
- /// [`InPlace`], the returned pointer is guaranteed to be the same as the passed `ptr`.
- ///
- /// If [`MayMove`] is used then ownership of the memory block referenced by `ptr`
- /// is transferred to this allocator. The memory may or may not be freed, and should be
- /// considered unusable (unless of course it is transferred back to the caller again via the
- /// return value of this method).
+ /// allocation referenced by `ptr` to fit the new layout.
///
/// If this method returns `Err`, then ownership of the memory block has not been transferred to
/// this allocator, and the contents of the memory block are unaltered.
///
- /// The memory block will contain the following contents after a successful call to `grow`:
+ /// [`NonNull<[u8]>`]: NonNull
+ ///
+ /// # Safety
+ ///
+ /// * `ptr` must denote a block of memory [*currently allocated*] via this allocator,
+ /// * `layout` must [*fit*] that block of memory (The `new_size` argument need not fit it.),
+ /// * `new_size` must be greater than or equal to `layout.size()`, and
+ /// * `new_size`, when rounded up to the nearest multiple of `layout.align()`, must not overflow
+ /// (i.e., the rounded value must be less than or equal to `usize::MAX`).
+ // Note: We can't require that `new_size` is strictly greater than `layout.size()` because of ZSTs.
+ // alternative: `new_size` must be strictly greater than `layout.size()` or both are zero
+ ///
+ /// [*currently allocated*]: #currently-allocated-memory
+ /// [*fit*]: #memory-fitting
+ ///
+ /// # Errors
+ ///
+ /// Returns `Err` if the new layout does not meet the allocator's size and alignment
+ /// constraints of the allocator, or if growing otherwise fails.
+ ///
+ /// Implementations are encouraged to return `Err` on memory exhaustion rather than panicking or
+ /// aborting, but this is not a strict requirement. (Specifically: it is *legal* to implement
+ /// this trait atop an underlying native allocation library that aborts on memory exhaustion.)
+ ///
+ /// Clients wishing to abort computation in response to an allocation error are encouraged to
+ /// call the [`handle_alloc_error`] function, rather than directly invoking `panic!` or similar.
+ ///
+ /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html
+ unsafe fn grow(
+ &mut self,
+ ptr: NonNull<u8>,
+ layout: Layout,
+ new_size: usize,
+ ) -> Result<NonNull<[u8]>, AllocErr> {
+ let size = layout.size();
+ debug_assert!(
+ new_size >= size,
+ "`new_size` must be greater than or equal to `layout.size()`"
+ );
+
+ if size == new_size {
+ return Ok(NonNull::slice_from_raw_parts(ptr, size));
+ }
+
+ let new_layout =
+ // SAFETY: the caller must ensure that the `new_size` does not overflow.
+ // `layout.align()` comes from a `Layout` and is thus guaranteed to be valid for a Layout.
+ // The caller must ensure that `new_size` is greater than or equal to zero. If it's equal
+ // to zero, it's catched beforehand.
+ unsafe { Layout::from_size_align_unchecked(new_size, layout.align()) };
+ let new_ptr = self.alloc(new_layout)?;
+
+ // SAFETY: because `new_size` must be greater than or equal to `size`, both the old and new
+ // memory allocation are valid for reads and writes for `size` bytes. Also, because the old
+ // allocation wasn't yet deallocated, it cannot overlap `new_ptr`. Thus, the call to
+ // `copy_nonoverlapping` is safe.
+ // The safety contract for `dealloc` must be upheld by the caller.
+ unsafe {
+ ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_non_null_ptr().as_ptr(), size);
+ self.dealloc(ptr, layout);
+ Ok(new_ptr)
+ }
+ }
+
+ /// Behaves like `grow`, but also ensures that the new contents are set to zero before being
+ /// returned.
+ ///
+ /// The memory block will contain the following contents after a successful call to
+ /// `grow_zeroed`:
/// * Bytes `0..layout.size()` are preserved from the original allocation.
- /// * Bytes `layout.size()..old_size` will either be preserved or initialized according to
- /// [`init`], depending on the allocator implementation. `old_size` refers to the size of
- /// the `MemoryBlock` prior to the `grow` call, which may be larger than the size
+ /// * Bytes `layout.size()..old_size` will either be preserved or zeroed,
+ /// depending on the allocator implementation. `old_size` refers to the size of
+ /// the `MemoryBlock` prior to the `grow_zeroed` call, which may be larger than the size
/// that was originally requested when it was allocated.
- /// * Bytes `old_size..new_size` are initialized according to [`init`]. `new_size` refers to
+ /// * Bytes `old_size..new_size` are zeroed. `new_size` refers to
/// the size of the `MemoryBlock` returned by the `grow` call.
///
- /// [`InPlace`]: ReallocPlacement::InPlace
- /// [`MayMove`]: ReallocPlacement::MayMove
- /// [`placement`]: ReallocPlacement
- /// [`init`]: AllocInit
- ///
/// # Safety
///
/// * `ptr` must denote a block of memory [*currently allocated*] via this allocator,
/// * `layout` must [*fit*] that block of memory (The `new_size` argument need not fit it.),
- // We can't require that `new_size` is strictly greater than `memory.size` because of ZSTs.
- // An alternative would be
- // * `new_size must be strictly greater than `memory.size` or both are zero
/// * `new_size` must be greater than or equal to `layout.size()`, and
/// * `new_size`, when rounded up to the nearest multiple of `layout.align()`, must not overflow
/// (i.e., the rounded value must be less than or equal to `usize::MAX`).
+ // Note: We can't require that `new_size` is strictly greater than `layout.size()` because of ZSTs.
+ // alternative: `new_size` must be strictly greater than `layout.size()` or both are zero
///
/// [*currently allocated*]: #currently-allocated-memory
/// [*fit*]: #memory-fitting
/// call the [`handle_alloc_error`] function, rather than directly invoking `panic!` or similar.
///
/// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html
- unsafe fn grow(
+ unsafe fn grow_zeroed(
&mut self,
ptr: NonNull<u8>,
layout: Layout,
new_size: usize,
- placement: ReallocPlacement,
- init: AllocInit,
- ) -> Result<MemoryBlock, AllocErr> {
- match placement {
- ReallocPlacement::InPlace => Err(AllocErr),
- ReallocPlacement::MayMove => {
- let size = layout.size();
- debug_assert!(
- new_size >= size,
- "`new_size` must be greater than or equal to `layout.size()`"
- );
+ ) -> Result<NonNull<[u8]>, AllocErr> {
+ let size = layout.size();
+ debug_assert!(
+ new_size >= size,
+ "`new_size` must be greater than or equal to `layout.size()`"
+ );
- if new_size == size {
- return Ok(MemoryBlock { ptr, size });
- }
+ if size == new_size {
+ return Ok(NonNull::slice_from_raw_parts(ptr, size));
+ }
- let new_layout =
- // SAFETY: the caller must ensure that the `new_size` does not overflow.
- // `layout.align()` comes from a `Layout` and is thus guaranteed to be valid for a Layout.
- // The caller must ensure that `new_size` is greater than zero.
- unsafe { Layout::from_size_align_unchecked(new_size, layout.align()) };
- let new_memory = self.alloc(new_layout, init)?;
+ let new_layout =
+ // SAFETY: the caller must ensure that the `new_size` does not overflow.
+ // `layout.align()` comes from a `Layout` and is thus guaranteed to be valid for a Layout.
+ // The caller must ensure that `new_size` is greater than or equal to zero. If it's equal
+ // to zero, it's caught beforehand.
+ unsafe { Layout::from_size_align_unchecked(new_size, layout.align()) };
+ let new_ptr = self.alloc_zeroed(new_layout)?;
- // SAFETY: because `new_size` must be greater than or equal to `size`, both the old and new
- // memory allocation are valid for reads and writes for `size` bytes. Also, because the old
- // allocation wasn't yet deallocated, it cannot overlap `new_memory`. Thus, the call to
- // `copy_nonoverlapping` is safe.
- // The safety contract for `dealloc` must be upheld by the caller.
- unsafe {
- ptr::copy_nonoverlapping(ptr.as_ptr(), new_memory.ptr.as_ptr(), size);
- self.dealloc(ptr, layout);
- Ok(new_memory)
- }
- }
+ // SAFETY: because `new_size` must be greater than or equal to `size`, both the old and new
+ // memory allocation are valid for reads and writes for `size` bytes. Also, because the old
+ // allocation wasn't yet deallocated, it cannot overlap `new_ptr`. Thus, the call to
+ // `copy_nonoverlapping` is safe.
+ // The safety contract for `dealloc` must be upheld by the caller.
+ unsafe {
+ ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_non_null_ptr().as_ptr(), size);
+ self.dealloc(ptr, layout);
+ Ok(new_ptr)
}
}
/// Attempts to shrink the memory block.
///
- /// Returns a new [`MemoryBlock`][] containing a pointer and the actual size of the allocated
+ /// Returns a new [`NonNull<[u8]>`] containing a pointer and the actual size of the allocated
/// memory. The pointer is suitable for holding data described by a new layout with `layout`’s
/// alignment and a size given by `new_size`. To accomplish this, the allocator may shrink the
- /// allocation referenced by `ptr` to fit the new layout. If the [`placement`] is
- /// [`InPlace`], the returned pointer is guaranteed to be the same as the passed `ptr`.
+ /// allocation referenced by `ptr` to fit the new layout.
///
/// If this returns `Ok`, then ownership of the memory block referenced by `ptr` has been
/// transferred to this allocator. The memory may or may not have been freed, and should be
/// If this method returns `Err`, then ownership of the memory block has not been transferred to
/// this allocator, and the contents of the memory block are unaltered.
///
- /// The behavior of how the allocator tries to shrink the memory is specified by [`placement`].
- ///
- /// [`InPlace`]: ReallocPlacement::InPlace
- /// [`placement`]: ReallocPlacement
+ /// [`NonNull<[u8]>`]: NonNull
///
/// # Safety
///
/// * `ptr` must denote a block of memory [*currently allocated*] via this allocator,
/// * `layout` must [*fit*] that block of memory (The `new_size` argument need not fit it.), and
- // We can't require that `new_size` is strictly smaller than `memory.size` because of ZSTs.
- // An alternative would be
- // * `new_size must be strictly smaller than `memory.size` or both are zero
/// * `new_size` must be smaller than or equal to `layout.size()`.
+ // Note: We can't require that `new_size` is strictly smaller than `layout.size()` because of ZSTs.
+ // alternative: `new_size` must be smaller than `layout.size()` or both are zero
///
/// [*currently allocated*]: #currently-allocated-memory
/// [*fit*]: #memory-fitting
ptr: NonNull<u8>,
layout: Layout,
new_size: usize,
- placement: ReallocPlacement,
- ) -> Result<MemoryBlock, AllocErr> {
- match placement {
- ReallocPlacement::InPlace => Err(AllocErr),
- ReallocPlacement::MayMove => {
- let size = layout.size();
- debug_assert!(
- new_size <= size,
- "`new_size` must be smaller than or equal to `layout.size()`"
- );
+ ) -> Result<NonNull<[u8]>, AllocErr> {
+ let size = layout.size();
+ debug_assert!(
+ new_size <= size,
+ "`new_size` must be smaller than or equal to `layout.size()`"
+ );
- if new_size == size {
- return Ok(MemoryBlock { ptr, size });
- }
+ if size == new_size {
+ return Ok(NonNull::slice_from_raw_parts(ptr, size));
+ }
- let new_layout =
- // SAFETY: the caller must ensure that the `new_size` does not overflow.
- // `layout.align()` comes from a `Layout` and is thus guaranteed to be valid for a Layout.
- // The caller must ensure that `new_size` is greater than zero.
- unsafe { Layout::from_size_align_unchecked(new_size, layout.align()) };
- let new_memory = self.alloc(new_layout, AllocInit::Uninitialized)?;
+ let new_layout =
+ // SAFETY: the caller must ensure that the `new_size` does not overflow.
+ // `layout.align()` comes from a `Layout` and is thus guaranteed to be valid for a Layout.
+ // The caller must ensure that `new_size` is greater than zero.
+ unsafe { Layout::from_size_align_unchecked(new_size, layout.align()) };
+ let new_ptr = self.alloc(new_layout)?;
- // SAFETY: because `new_size` must be lower than or equal to `size`, both the old and new
- // memory allocation are valid for reads and writes for `new_size` bytes. Also, because the
- // old allocation wasn't yet deallocated, it cannot overlap `new_memory`. Thus, the call to
- // `copy_nonoverlapping` is safe.
- // The safety contract for `dealloc` must be upheld by the caller.
- unsafe {
- ptr::copy_nonoverlapping(ptr.as_ptr(), new_memory.ptr.as_ptr(), new_size);
- self.dealloc(ptr, layout);
- Ok(new_memory)
- }
- }
+ // SAFETY: because `new_size` must be lower than or equal to `size`, both the old and new
+ // memory allocation are valid for reads and writes for `new_size` bytes. Also, because the
+ // old allocation wasn't yet deallocated, it cannot overlap `new_ptr`. Thus, the call to
+ // `copy_nonoverlapping` is safe.
+ // The safety contract for `dealloc` must be upheld by the caller.
+ unsafe {
+ ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_non_null_ptr().as_ptr(), size);
+ self.dealloc(ptr, layout);
+ Ok(new_ptr)
}
}
A: AllocRef + ?Sized,
{
#[inline]
- fn alloc(&mut self, layout: Layout, init: AllocInit) -> Result<MemoryBlock, AllocErr> {
- (**self).alloc(layout, init)
+ fn alloc(&mut self, layout: Layout) -> Result<NonNull<[u8]>, AllocErr> {
+ (**self).alloc(layout)
+ }
+
+ #[inline]
+ fn alloc_zeroed(&mut self, layout: Layout) -> Result<NonNull<[u8]>, AllocErr> {
+ (**self).alloc_zeroed(layout)
}
#[inline]
ptr: NonNull<u8>,
layout: Layout,
new_size: usize,
- placement: ReallocPlacement,
- init: AllocInit,
- ) -> Result<MemoryBlock, AllocErr> {
+ ) -> Result<NonNull<[u8]>, AllocErr> {
+ // SAFETY: the safety contract must be upheld by the caller
+ unsafe { (**self).grow(ptr, layout, new_size) }
+ }
+
+ #[inline]
+ unsafe fn grow_zeroed(
+ &mut self,
+ ptr: NonNull<u8>,
+ layout: Layout,
+ new_size: usize,
+ ) -> Result<NonNull<[u8]>, AllocErr> {
// SAFETY: the safety contract must be upheld by the caller
- unsafe { (**self).grow(ptr, layout, new_size, placement, init) }
+ unsafe { (**self).grow_zeroed(ptr, layout, new_size) }
}
#[inline]
ptr: NonNull<u8>,
layout: Layout,
new_size: usize,
- placement: ReallocPlacement,
- ) -> Result<MemoryBlock, AllocErr> {
+ ) -> Result<NonNull<[u8]>, AllocErr> {
// SAFETY: the safety contract must be upheld by the caller
- unsafe { (**self).shrink(ptr, layout, new_size, placement) }
+ unsafe { (**self).shrink(ptr, layout, new_size) }
}
}
// FIXME(LukasKalbertodt): actually use `mem::transmute` here, once it
// works with const generics:
- // `mem::transmute::<[T; {N}], [MaybeUninit<T>; {N}]>(array)`
+ // `mem::transmute::<[T; N], [MaybeUninit<T>; N]>(array)`
//
- // Until then, we do it manually here. We first create a bitwise copy
- // but cast the pointer so that it is treated as a different type. Then
- // we forget `array` so that it is not dropped.
- let data = unsafe {
- let data = ptr::read(&array as *const [T; N] as *const [MaybeUninit<T>; N]);
+ // Until then, we can use `mem::transmute_copy` to create a bitwise copy
+ // as a different type, then forget `array` so that it is not dropped.
+ unsafe {
+ let iter = Self { data: mem::transmute_copy(&array), alive: 0..N };
mem::forget(array);
- data
- };
-
- Self { data, alive: 0..N }
+ iter
+ }
}
/// Returns an immutable slice of all elements that have not been yielded
/// yet.
fn as_slice(&self) -> &[T] {
- let slice = &self.data[self.alive.clone()];
- // SAFETY: This transmute is safe. As mentioned in `new`, `MaybeUninit` retains
- // the size and alignment of `T`. Furthermore, we know that all
- // elements within `alive` are properly initialized.
- unsafe { mem::transmute::<&[MaybeUninit<T>], &[T]>(slice) }
+ // SAFETY: We know that all elements within `alive` are properly initialized.
+ unsafe {
+ let slice = self.data.get_unchecked(self.alive.clone());
+ MaybeUninit::slice_get_ref(slice)
+ }
}
/// Returns a mutable slice of all elements that have not been yielded yet.
fn as_mut_slice(&mut self) -> &mut [T] {
- // This transmute is safe, same as in `as_slice` above.
- let slice = &mut self.data[self.alive.clone()];
- // SAFETY: This transmute is safe. As mentioned in `new`, `MaybeUninit` retains
- // the size and alignment of `T`. Furthermore, we know that all
- // elements within `alive` are properly initialized.
- unsafe { mem::transmute::<&mut [MaybeUninit<T>], &mut [T]>(slice) }
+ // SAFETY: We know that all elements within `alive` are properly initialized.
+ unsafe {
+ let slice = self.data.get_unchecked_mut(self.alive.clone());
+ MaybeUninit::slice_get_mut(slice)
+ }
}
}
impl<T, const N: usize> Iterator for IntoIter<T, N> {
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
- if self.alive.start == self.alive.end {
- return None;
- }
-
- // Bump start index.
+ // Get the next index from the front.
//
- // From the check above we know that `alive.start != alive.end`.
- // Combine this with the invariant `alive.start <= alive.end`, we know
- // that `alive.start < alive.end`. Increasing `alive.start` by 1
- // maintains the invariant regarding `alive`. However, due to this
- // change, for a short time, the alive zone is not `data[alive]`
- // anymore, but `data[idx..alive.end]`.
- let idx = self.alive.start;
- self.alive.start += 1;
-
- // Read the element from the array.
- // SAFETY: This is safe: `idx` is an index
- // into the "alive" region of the array. Reading this element means
- // that `data[idx]` is regarded as dead now (i.e. do not touch). As
- // `idx` was the start of the alive-zone, the alive zone is now
- // `data[alive]` again, restoring all invariants.
- let out = unsafe { self.data.get_unchecked(idx).read() };
-
- Some(out)
+ // Increasing `alive.start` by 1 maintains the invariant regarding
+ // `alive`. However, due to this change, for a short time, the alive
+ // zone is not `data[alive]` anymore, but `data[idx..alive.end]`.
+ self.alive.next().map(|idx| {
+ // Read the element from the array.
+ // SAFETY: `idx` is an index into the former "alive" region of the
+ // array. Reading this element means that `data[idx]` is regarded as
+ // dead now (i.e. do not touch). As `idx` was the start of the
+ // alive-zone, the alive zone is now `data[alive]` again, restoring
+ // all invariants.
+ unsafe { self.data.get_unchecked(idx).read() }
+ })
}
fn size_hint(&self) -> (usize, Option<usize>) {
#[stable(feature = "array_value_iter_impls", since = "1.40.0")]
impl<T, const N: usize> DoubleEndedIterator for IntoIter<T, N> {
fn next_back(&mut self) -> Option<Self::Item> {
- if self.alive.start == self.alive.end {
- return None;
- }
-
- // Decrease end index.
+ // Get the next index from the back.
//
- // From the check above we know that `alive.start != alive.end`.
- // Combine this with the invariant `alive.start <= alive.end`, we know
- // that `alive.start < alive.end`. As `alive.start` cannot be negative,
- // `alive.end` is at least 1, meaning that we can safely decrement it
- // by one. This also maintains the invariant `alive.start <=
- // alive.end`. However, due to this change, for a short time, the alive
- // zone is not `data[alive]` anymore, but `data[alive.start..alive.end
- // + 1]`.
- self.alive.end -= 1;
-
- // Read the element from the array.
- // SAFETY: This is safe: `alive.end` is an
- // index into the "alive" region of the array. Compare the previous
- // comment that states that the alive region is
- // `data[alive.start..alive.end + 1]`. Reading this element means that
- // `data[alive.end]` is regarded as dead now (i.e. do not touch). As
- // `alive.end` was the end of the alive-zone, the alive zone is now
- // `data[alive]` again, restoring all invariants.
- let out = unsafe { self.data.get_unchecked(self.alive.end).read() };
-
- Some(out)
+ // Decreasing `alive.end` by 1 maintains the invariant regarding
+ // `alive`. However, due to this change, for a short time, the alive
+ // zone is not `data[alive]` anymore, but `data[alive.start..=idx]`.
+ self.alive.next_back().map(|idx| {
+ // Read the element from the array.
+ // SAFETY: `idx` is an index into the former "alive" region of the
+ // array. Reading this element means that `data[idx]` is regarded as
+ // dead now (i.e. do not touch). As `idx` was the end of the
+ // alive-zone, the alive zone is now `data[alive]` again, restoring
+ // all invariants.
+ unsafe { self.data.get_unchecked(idx).read() }
+ })
}
}
#[stable(feature = "array_value_iter_impls", since = "1.40.0")]
impl<T: Clone, const N: usize> Clone for IntoIter<T, N> {
fn clone(&self) -> Self {
- // SAFETY: each point of unsafety is documented inside the unsafe block
- unsafe {
- // This creates a new uninitialized array. Note that the `assume_init`
- // refers to the array, not the individual elements. And it is Ok if
- // the array is in an uninitialized state as all elements may be
- // uninitialized (all bit patterns are valid). Compare the
- // `MaybeUninit` docs for more information.
- let mut new_data: [MaybeUninit<T>; N] = MaybeUninit::uninit().assume_init();
-
- // Clone all alive elements.
- for idx in self.alive.clone() {
- // The element at `idx` in the old array is alive, so we can
- // safely call `get_ref()`. We then clone it, and write the
- // clone into the new array.
- let clone = self.data.get_unchecked(idx).get_ref().clone();
- new_data.get_unchecked_mut(idx).write(clone);
- }
-
- Self { data: new_data, alive: self.alive.clone() }
+ // Note, we don't really need to match the exact same alive range, so
+ // we can just clone into offset 0 regardless of where `self` is.
+ let mut new = Self { data: MaybeUninit::uninit_array(), alive: 0..0 };
+
+ // Clone all alive elements.
+ for (src, dst) in self.as_slice().iter().zip(&mut new.data) {
+ // Write a clone into the new array, then update its alive range.
+ // If cloning panics, we'll correctly drop the previous items.
+ dst.write(src.clone());
+ new.alive.end += 1;
}
+
+ new
}
}
// box. This isn't the greatest implementation since it probably deoptimizes
// more than we want, but it's so far good enough.
+ #[cfg(not(miri))] // This is just a hint, so it is fine to skip in Miri.
// SAFETY: the inline assembly is a no-op.
unsafe {
llvm_asm!("" : : "r"(&dummy));
- dummy
}
+
+ dummy
}
pub fn ptr_offset_from<T>(ptr: *const T, base: *const T) -> isize;
/// Internal placeholder for injecting code coverage counters when the "instrument-coverage"
- /// option is enabled. The placeholder is replaced with `llvm.instrprof.increment` during code
- /// generation.
+ /// option is enabled. The source code region information is extracted prior to code generation,
+ /// and added to the "coverage map", which is injected into the generated code as additional
+ /// data. This intrinsic then triggers the generation of LLVM intrinsic call
+ /// `instrprof.increment`, using the remaining args (`function_source_hash` and `index`).
#[cfg(not(bootstrap))]
#[lang = "count_code_region"]
pub fn count_code_region(
function_source_hash: u64,
index: u32,
- start_byte_pos: u32,
- end_byte_pos: u32,
+ file_name: &'static str,
+ start_line: u32,
+ start_col: u32,
+ end_line: u32,
+ end_col: u32,
);
/// Internal marker for code coverage expressions, injected into the MIR when the
index: u32,
left_index: u32,
right_index: u32,
- start_byte_pos: u32,
- end_byte_pos: u32,
+ file_name: &'static str,
+ start_line: u32,
+ start_col: u32,
+ end_line: u32,
+ end_col: u32,
);
/// This marker identifies a code region and two other counters or counter expressions
index: u32,
left_index: u32,
right_index: u32,
- start_byte_pos: u32,
- end_byte_pos: u32,
+ file_name: &'static str,
+ start_line: u32,
+ start_col: u32,
+ end_line: u32,
+ end_col: u32,
);
/// This marker identifies a code region to be added to the "coverage map" to indicate source
/// code that can never be reached.
/// (See `coverage_counter_add` for more information.)
- pub fn coverage_unreachable(start_byte_pos: u32, end_byte_pos: u32);
+ #[cfg(not(bootstrap))]
+ pub fn coverage_unreachable(
+ file_name: &'static str,
+ start_line: u32,
+ start_col: u32,
+ end_line: u32,
+ end_col: u32,
+ );
/// See documentation of `<*const T>::guaranteed_eq` for details.
#[rustc_const_unstable(feature = "const_raw_ptr_comparison", issue = "53020")]
/// (Notice that the rules around references to uninitialized data are not finalized yet, but
/// until they are, it is advisable to avoid them.)
#[stable(feature = "maybe_uninit", since = "1.36.0")]
+ #[rustc_const_unstable(feature = "const_maybe_uninit_as_ptr", issue = "75251")]
#[inline(always)]
- pub fn as_ptr(&self) -> *const T {
- unsafe { &*self.value as *const T }
+ pub const fn as_ptr(&self) -> *const T {
+ // `MaybeUninit` and `ManuallyDrop` are both `repr(transparent)` so we can cast the pointer.
+ self as *const _ as *const T
}
/// Gets a mutable pointer to the contained value. Reading from this pointer or turning it
/// (Notice that the rules around references to uninitialized data are not finalized yet, but
/// until they are, it is advisable to avoid them.)
#[stable(feature = "maybe_uninit", since = "1.36.0")]
+ #[rustc_const_unstable(feature = "const_maybe_uninit_as_ptr", issue = "75251")]
#[inline(always)]
- pub fn as_mut_ptr(&mut self) -> *mut T {
- unsafe { &mut *self.value as *mut T }
+ pub const fn as_mut_ptr(&mut self) -> *mut T {
+ // `MaybeUninit` and `ManuallyDrop` are both `repr(transparent)` so we can cast the pointer.
+ self as *mut _ as *mut T
}
/// Extracts the value from the `MaybeUninit<T>` container. This is a great way
#[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")]
// SAFETY: const sound because integers are plain old datatypes so we can always
// transmute them to arrays of bytes
- #[allow_internal_unstable(const_fn_union)]
+ #[allow_internal_unstable(const_fn_transmute)]
#[inline]
pub const fn to_ne_bytes(self) -> [u8; mem::size_of::<Self>()] {
- #[repr(C)]
- union Bytes {
- val: $SelfT,
- bytes: [u8; mem::size_of::<$SelfT>()],
- }
// SAFETY: integers are plain old datatypes so we can always transmute them to
// arrays of bytes
- unsafe { Bytes { val: self }.bytes }
+ unsafe { mem::transmute(self) }
}
}
#[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")]
// SAFETY: const sound because integers are plain old datatypes so we can always
// transmute to them
- #[allow_internal_unstable(const_fn_union)]
+ #[allow_internal_unstable(const_fn_transmute)]
#[inline]
pub const fn from_ne_bytes(bytes: [u8; mem::size_of::<Self>()]) -> Self {
- #[repr(C)]
- union Bytes {
- val: $SelfT,
- bytes: [u8; mem::size_of::<$SelfT>()],
- }
// SAFETY: integers are plain old datatypes so we can always transmute to them
- unsafe { Bytes { bytes }.val }
+ unsafe { mem::transmute(bytes) }
}
}
#[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")]
// SAFETY: const sound because integers are plain old datatypes so we can always
// transmute them to arrays of bytes
- #[allow_internal_unstable(const_fn_union)]
+ #[allow_internal_unstable(const_fn_transmute)]
#[inline]
pub const fn to_ne_bytes(self) -> [u8; mem::size_of::<Self>()] {
- #[repr(C)]
- union Bytes {
- val: $SelfT,
- bytes: [u8; mem::size_of::<$SelfT>()],
- }
// SAFETY: integers are plain old datatypes so we can always transmute them to
// arrays of bytes
- unsafe { Bytes { val: self }.bytes }
+ unsafe { mem::transmute(self) }
}
}
doc_comment! {
- concat!("Create an integer value from its representation as a byte array in
-big endian.
+ concat!("Create a native endian integer value from its representation
+as a byte array in big endian.
",
$from_xe_bytes_doc,
"
doc_comment! {
concat!("
-Create an integer value from its representation as a byte array in
-little endian.
+Create a native endian integer value from its representation
+as a byte array in little endian.
",
$from_xe_bytes_doc,
"
}
doc_comment! {
- concat!("Create an integer value from its memory representation as a byte
-array in native endianness.
+ concat!("Create a native endian integer value from its memory representation
+as a byte array in native endianness.
As the target platform's native endianness is used, portable code
likely wants to use [`from_be_bytes`] or [`from_le_bytes`], as
#[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")]
// SAFETY: const sound because integers are plain old datatypes so we can always
// transmute to them
- #[allow_internal_unstable(const_fn_union)]
+ #[allow_internal_unstable(const_fn_transmute)]
#[inline]
pub const fn from_ne_bytes(bytes: [u8; mem::size_of::<Self>()]) -> Self {
- #[repr(C)]
- union Bytes {
- val: $SelfT,
- bytes: [u8; mem::size_of::<$SelfT>()],
- }
// SAFETY: integers are plain old datatypes so we can always transmute to them
- unsafe { Bytes { bytes }.val }
+ unsafe { mem::transmute(bytes) }
}
}
self.wrapping_offset((count as isize).wrapping_neg())
}
+ /// Sets the pointer value to `ptr`.
+ ///
+ /// In case `self` is a (fat) pointer to an unsized type, this operation
+ /// will only affect the pointer part, whereas for (thin) pointers to
+ /// sized types, this has the same effect as a simple assignment.
+ ///
+ /// # Examples
+ ///
+ /// This function is primarily useful for allowing byte-wise pointer
+ /// arithmetic on potentially fat pointers:
+ ///
+ /// ```
+ /// #![feature(set_ptr_value)]
+ /// # use core::fmt::Debug;
+ /// let arr: [i32; 3] = [1, 2, 3];
+ /// let mut ptr = &arr[0] as *const dyn Debug;
+ /// let thin = ptr as *const u8;
+ /// ptr = ptr.set_ptr_value(unsafe { thin.add(8).cast() });
+ /// assert_eq!(unsafe { *(ptr as *const i32) }, 3);
+ /// ```
+ #[unstable(feature = "set_ptr_value", issue = "75091")]
+ #[inline]
+ pub fn set_ptr_value(mut self, val: *const ()) -> Self {
+ let thin = &mut self as *mut *const T as *mut *const ();
+ // SAFETY: In case of a thin pointer, this operations is identical
+ // to a simple assignment. In case of a fat pointer, with the current
+ // fat pointer layout implementation, the first field of such a
+ // pointer is always the data pointer, which is likewise assigned.
+ unsafe { *thin = val };
+ self
+ }
+
/// Reads the value from `self` without moving it. This leaves the
/// memory in `self` unchanged.
///
self.wrapping_offset((count as isize).wrapping_neg())
}
+ /// Sets the pointer value to `ptr`.
+ ///
+ /// In case `self` is a (fat) pointer to an unsized type, this operation
+ /// will only affect the pointer part, whereas for (thin) pointers to
+ /// sized types, this has the same effect as a simple assignment.
+ ///
+ /// # Examples
+ ///
+ /// This function is primarily useful for allowing byte-wise pointer
+ /// arithmetic on potentially fat pointers:
+ ///
+ /// ```
+ /// #![feature(set_ptr_value)]
+ /// # use core::fmt::Debug;
+ /// let mut arr: [i32; 3] = [1, 2, 3];
+ /// let mut ptr = &mut arr[0] as *mut dyn Debug;
+ /// let thin = ptr as *mut u8;
+ /// ptr = ptr.set_ptr_value(unsafe { thin.add(8).cast() });
+ /// assert_eq!(unsafe { *(ptr as *mut i32) }, 3);
+ /// ```
+ #[unstable(feature = "set_ptr_value", issue = "75091")]
+ #[inline]
+ pub fn set_ptr_value(mut self, val: *mut ()) -> Self {
+ let thin = &mut self as *mut *mut T as *mut *mut ();
+ // SAFETY: In case of a thin pointer, this operations is identical
+ // to a simple assignment. In case of a fat pointer, with the current
+ // fat pointer layout implementation, the first field of such a
+ // pointer is always the data pointer, which is likewise assigned.
+ unsafe { *thin = val };
+ self
+ }
+
/// Reads the value from `self` without moving it. This leaves the
/// memory in `self` unchanged.
///
/// The resulting lifetime is bound to self so this behaves "as if"
/// it were actually an instance of T that is getting borrowed. If a longer
/// (unbound) lifetime is needed, use `&*my_ptr.as_ptr()`.
+ ///
+ /// # Safety
+ ///
+ /// When calling this method, you have to ensure that all of the following is true:
+ /// - `self` is properly aligned
+ /// - `self` must point to an initialized instance of T; in particular, the pointer must be
+ /// "dereferencable" in the sense defined [here].
+ ///
+ /// This applies even if the result of this method is unused!
+ /// (The part about being initialized is not yet fully decided, but until
+ /// it is, the only safe approach is to ensure that they are indeed initialized.)
+ ///
+ /// Additionally, the lifetime of `self` does not necessarily reflect the actual
+ /// lifetime of the data. *You* must enforce Rust's aliasing rules. In particular,
+ /// for the duration of this lifetime, the memory the pointer points to must not
+ /// get mutated (except inside `UnsafeCell`).
+ ///
+ /// [here]: crate::ptr#safety
#[stable(feature = "nonnull", since = "1.25.0")]
#[inline]
pub unsafe fn as_ref(&self) -> &T {
/// The resulting lifetime is bound to self so this behaves "as if"
/// it were actually an instance of T that is getting borrowed. If a longer
/// (unbound) lifetime is needed, use `&mut *my_ptr.as_ptr()`.
+ ///
+ /// # Safety
+ ///
+ /// When calling this method, you have to ensure that all of the following is true:
+ /// - `self` is properly aligned
+ /// - `self` must point to an initialized instance of T; in particular, the pointer must be
+ /// "dereferenceable" in the sense defined [here].
+ ///
+ /// This applies even if the result of this method is unused!
+ /// (The part about being initialized is not yet fully decided, but until
+ /// it is the only safe approach is to ensure that they are indeed initialized.)
+ ///
+ /// Additionally, the lifetime of `self` does not necessarily reflect the actual
+ /// lifetime of the data. *You* must enforce Rust's aliasing rules. In particular,
+ /// for the duration of this lifetime, the memory this pointer points to must not
+ /// get accessed (read or written) through any other pointer.
+ ///
+ /// [here]: crate::ptr#safety
#[stable(feature = "nonnull", since = "1.25.0")]
#[inline]
pub unsafe fn as_mut(&mut self) -> &mut T {
unsafe { NonNull::new_unchecked(self.as_ptr().as_mut_ptr()) }
}
+ /// Returns a raw pointer to the slice's buffer.
+ ///
+ /// # Examples
+ ///
+ /// ```rust
+ /// #![feature(slice_ptr_get, nonnull_slice_from_raw_parts)]
+ /// use std::ptr::NonNull;
+ ///
+ /// let slice: NonNull<[i8]> = NonNull::slice_from_raw_parts(NonNull::dangling(), 3);
+ /// assert_eq!(slice.as_mut_ptr(), 1 as *mut i8);
+ /// ```
+ #[inline]
+ #[unstable(feature = "slice_ptr_get", issue = "74265")]
+ #[rustc_const_unstable(feature = "slice_ptr_get", issue = "74265")]
+ pub const fn as_mut_ptr(self) -> *mut T {
+ self.as_non_null_ptr().as_ptr()
+ }
+
/// Returns a raw pointer to an element or subslice, without doing bounds
/// checking.
///
#[inline]
fn index(self, slice: &str) -> &Self::Output {
let (start, end) = (self.start, self.end);
- self.get(slice).unwrap_or_else(|| super::slice_error_fail(slice, start, end))
+ match self.get(slice) {
+ Some(s) => s,
+ None => super::slice_error_fail(slice, start, end),
+ }
}
#[inline]
fn index_mut(self, slice: &mut str) -> &mut Self::Output {
#[inline]
fn index(self, slice: &str) -> &Self::Output {
let end = self.end;
- self.get(slice).unwrap_or_else(|| super::slice_error_fail(slice, 0, end))
+ match self.get(slice) {
+ Some(s) => s,
+ None => super::slice_error_fail(slice, 0, end),
+ }
}
#[inline]
fn index_mut(self, slice: &mut str) -> &mut Self::Output {
#[inline]
fn index(self, slice: &str) -> &Self::Output {
let (start, end) = (self.start, slice.len());
- self.get(slice).unwrap_or_else(|| super::slice_error_fail(slice, start, end))
+ match self.get(slice) {
+ Some(s) => s,
+ None => super::slice_error_fail(slice, start, end),
+ }
}
#[inline]
fn index_mut(self, slice: &mut str) -> &mut Self::Output {
} else if #[cfg(any(
all(target_family = "windows", target_env = "gnu"),
target_os = "cloudabi",
+ target_os = "psp",
target_family = "unix",
all(target_vendor = "fortanix", target_env = "sgx"),
))] {
// - os=uefi
// - nvptx64-nvidia-cuda
// - avr-unknown-unknown
- // - mipsel-sony-psp
#[path = "dummy.rs"]
mod real_imp;
}
compiler_builtins = { version = "0.1.32" }
profiler_builtins = { path = "../profiler_builtins", optional = true }
unwind = { path = "../unwind" }
-hashbrown = { version = "0.6.2", default-features = false, features = ['rustc-dep-of-std'] }
+hashbrown = { version = "0.8.1", default-features = false, features = ['rustc-dep-of-std'] }
# Dependencies of the `backtrace` crate
addr2line = { version = "0.13.0", optional = true, default-features = false }
#[unstable(feature = "allocator_api", issue = "32838")]
unsafe impl AllocRef for System {
#[inline]
- fn alloc(&mut self, layout: Layout, init: AllocInit) -> Result<MemoryBlock, AllocErr> {
- unsafe {
- let size = layout.size();
- if size == 0 {
- Ok(MemoryBlock { ptr: layout.dangling(), size: 0 })
- } else {
- let raw_ptr = match init {
- AllocInit::Uninitialized => GlobalAlloc::alloc(self, layout),
- AllocInit::Zeroed => GlobalAlloc::alloc_zeroed(self, layout),
- };
- let ptr = NonNull::new(raw_ptr).ok_or(AllocErr)?;
- Ok(MemoryBlock { ptr, size })
- }
- }
+ fn alloc(&mut self, layout: Layout) -> Result<NonNull<[u8]>, AllocErr> {
+ let size = layout.size();
+ let ptr = if size == 0 {
+ layout.dangling()
+ } else {
+ // SAFETY: `layout` is non-zero in size,
+ unsafe { NonNull::new(GlobalAlloc::alloc(&System, layout)).ok_or(AllocErr)? }
+ };
+ Ok(NonNull::slice_from_raw_parts(ptr, size))
+ }
+
+ #[inline]
+ fn alloc_zeroed(&mut self, layout: Layout) -> Result<NonNull<[u8]>, AllocErr> {
+ let size = layout.size();
+ let ptr = if size == 0 {
+ layout.dangling()
+ } else {
+ // SAFETY: `layout` is non-zero in size,
+ unsafe { NonNull::new(GlobalAlloc::alloc_zeroed(&System, layout)).ok_or(AllocErr)? }
+ };
+ Ok(NonNull::slice_from_raw_parts(ptr, size))
}
#[inline]
unsafe fn dealloc(&mut self, ptr: NonNull<u8>, layout: Layout) {
if layout.size() != 0 {
- // SAFETY: The safety guarantees are explained in the documentation
- // for the `GlobalAlloc` trait and its `dealloc` method.
- unsafe { GlobalAlloc::dealloc(self, ptr.as_ptr(), layout) }
+ // SAFETY: `layout` is non-zero in size,
+ // other conditions must be upheld by the caller
+ unsafe { GlobalAlloc::dealloc(&System, ptr.as_ptr(), layout) }
}
}
ptr: NonNull<u8>,
layout: Layout,
new_size: usize,
- placement: ReallocPlacement,
- init: AllocInit,
- ) -> Result<MemoryBlock, AllocErr> {
- let size = layout.size();
+ ) -> Result<NonNull<[u8]>, AllocErr> {
debug_assert!(
- new_size >= size,
- "`new_size` must be greater than or equal to `memory.size()`"
+ new_size >= layout.size(),
+ "`new_size` must be greater than or equal to `layout.size()`"
);
- if size == new_size {
- return Ok(MemoryBlock { ptr, size });
+ // SAFETY: `new_size` must be non-zero, which is checked in the match expression.
+ // Other conditions must be upheld by the caller
+ unsafe {
+ match layout.size() {
+ old_size if old_size == new_size => {
+ Ok(NonNull::slice_from_raw_parts(ptr, new_size))
+ }
+ 0 => self.alloc(Layout::from_size_align_unchecked(new_size, layout.align())),
+ old_size => {
+ // `realloc` probably checks for `new_size > size` or something similar.
+ intrinsics::assume(new_size > old_size);
+ let raw_ptr = GlobalAlloc::realloc(&System, ptr.as_ptr(), layout, new_size);
+ let ptr = NonNull::new(raw_ptr).ok_or(AllocErr)?;
+ Ok(NonNull::slice_from_raw_parts(ptr, new_size))
+ }
+ }
}
+ }
- match placement {
- ReallocPlacement::InPlace => Err(AllocErr),
- ReallocPlacement::MayMove if layout.size() == 0 => {
- let new_layout =
- // SAFETY: The new size and layout alignement guarantees
- // are transferred to the caller (they come from parameters).
- //
- // See the preconditions for `Layout::from_size_align` to
- // see what must be checked.
- unsafe { Layout::from_size_align_unchecked(new_size, layout.align()) };
- self.alloc(new_layout, init)
- }
- ReallocPlacement::MayMove => {
- // SAFETY:
- //
- // The safety guarantees are explained in the documentation
- // for the `GlobalAlloc` trait and its `dealloc` method.
- //
- // `realloc` probably checks for `new_size > size` or something
- // similar.
- //
- // For the guarantees about `init_offset`, see its documentation:
- // `ptr` is assumed valid (and checked for non-NUL) and
- // `memory.size` is set to `new_size` so the offset being `size`
- // is valid.
- let memory = unsafe {
- intrinsics::assume(new_size > size);
- let ptr = GlobalAlloc::realloc(self, ptr.as_ptr(), layout, new_size);
- let memory =
- MemoryBlock { ptr: NonNull::new(ptr).ok_or(AllocErr)?, size: new_size };
- init.init_offset(memory, size);
- memory
- };
- Ok(memory)
+ #[inline]
+ unsafe fn grow_zeroed(
+ &mut self,
+ ptr: NonNull<u8>,
+ layout: Layout,
+ new_size: usize,
+ ) -> Result<NonNull<[u8]>, AllocErr> {
+ debug_assert!(
+ new_size >= layout.size(),
+ "`new_size` must be greater than or equal to `layout.size()`"
+ );
+
+ // SAFETY: `new_size` must be non-zero, which is checked in the match expression.
+ // Other conditions must be upheld by the caller
+ unsafe {
+ match layout.size() {
+ old_size if old_size == new_size => {
+ Ok(NonNull::slice_from_raw_parts(ptr, new_size))
+ }
+ 0 => self.alloc_zeroed(Layout::from_size_align_unchecked(new_size, layout.align())),
+ old_size => {
+ // `realloc` probably checks for `new_size > size` or something similar.
+ intrinsics::assume(new_size > old_size);
+ let raw_ptr = GlobalAlloc::realloc(&System, ptr.as_ptr(), layout, new_size);
+ raw_ptr.add(old_size).write_bytes(0, new_size - old_size);
+ let ptr = NonNull::new(raw_ptr).ok_or(AllocErr)?;
+ Ok(NonNull::slice_from_raw_parts(ptr, new_size))
+ }
}
}
}
ptr: NonNull<u8>,
layout: Layout,
new_size: usize,
- placement: ReallocPlacement,
- ) -> Result<MemoryBlock, AllocErr> {
- let size = layout.size();
+ ) -> Result<NonNull<[u8]>, AllocErr> {
+ let old_size = layout.size();
debug_assert!(
- new_size <= size,
- "`new_size` must be smaller than or equal to `memory.size()`"
+ new_size <= old_size,
+ "`new_size` must be smaller than or equal to `layout.size()`"
);
- if size == new_size {
- return Ok(MemoryBlock { ptr, size });
- }
-
- match placement {
- ReallocPlacement::InPlace => Err(AllocErr),
- ReallocPlacement::MayMove if new_size == 0 => {
- // SAFETY: see `GlobalAlloc::dealloc` for the guarantees that
- // must be respected. `ptr` and `layout` are parameters and so
- // those guarantees must be checked by the caller.
- unsafe { self.dealloc(ptr, layout) };
- Ok(MemoryBlock { ptr: layout.dangling(), size: 0 })
+ let ptr = if new_size == old_size {
+ ptr
+ } else if new_size == 0 {
+ // SAFETY: `layout` is non-zero in size as `old_size` != `new_size`
+ // Other conditions must be upheld by the caller
+ unsafe {
+ self.dealloc(ptr, layout);
}
- ReallocPlacement::MayMove => {
- // SAFETY:
- //
- // See `GlobalAlloc::realloc` for more informations about the
- // guarantees expected by this method. `ptr`, `layout` and
- // `new_size` are parameters and the responsibility for their
- // correctness is left to the caller.
- //
- // `realloc` probably checks for `new_size < size` or something
- // similar.
- let memory = unsafe {
- intrinsics::assume(new_size < size);
- let ptr = GlobalAlloc::realloc(self, ptr.as_ptr(), layout, new_size);
- MemoryBlock { ptr: NonNull::new(ptr).ok_or(AllocErr)?, size: new_size }
- };
- Ok(memory)
- }
- }
+ layout.dangling()
+ } else {
+ // SAFETY: new_size is not zero,
+ // Other conditions must be upheld by the caller
+ let raw_ptr = unsafe {
+ // `realloc` probably checks for `new_size < old_size` or something similar.
+ intrinsics::assume(new_size < old_size);
+ GlobalAlloc::realloc(&System, ptr.as_ptr(), layout, new_size)
+ };
+ NonNull::new(raw_ptr).ok_or(AllocErr)?
+ };
+
+ Ok(NonNull::slice_from_raw_parts(ptr, new_size))
}
}
static HOOK: AtomicPtr<()> = AtomicPtr::new(ptr::null_mut());
#[inline]
#[unstable(feature = "try_reserve", reason = "new API", issue = "48043")]
pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError> {
- self.base.try_reserve(additional).map_err(map_collection_alloc_err)
+ self.base.try_reserve(additional).map_err(map_try_reserve_error)
}
/// Shrinks the capacity of the map as much as possible. It will drop
{
self.base.retain(f)
}
+
+ /// Creates a consuming iterator visiting all the keys in arbitrary order.
+ /// The map cannot be used after calling this.
+ /// The iterator element type is `K`.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// #![feature(map_into_keys_values)]
+ /// use std::collections::HashMap;
+ ///
+ /// let mut map = HashMap::new();
+ /// map.insert("a", 1);
+ /// map.insert("b", 2);
+ /// map.insert("c", 3);
+ ///
+ /// let vec: Vec<&str> = map.into_keys().collect();
+ /// ```
+ #[inline]
+ #[unstable(feature = "map_into_keys_values", issue = "75294")]
+ pub fn into_keys(self) -> IntoKeys<K, V> {
+ IntoKeys { inner: self.into_iter() }
+ }
+
+ /// Creates a consuming iterator visiting all the values in arbitrary order.
+ /// The map cannot be used after calling this.
+ /// The iterator element type is `V`.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// #![feature(map_into_keys_values)]
+ /// use std::collections::HashMap;
+ ///
+ /// let mut map = HashMap::new();
+ /// map.insert("a", 1);
+ /// map.insert("b", 2);
+ /// map.insert("c", 3);
+ ///
+ /// let vec: Vec<i32> = map.into_values().collect();
+ /// ```
+ #[inline]
+ #[unstable(feature = "map_into_keys_values", issue = "75294")]
+ pub fn into_values(self) -> IntoValues<K, V> {
+ IntoValues { inner: self.into_iter() }
+ }
}
impl<K, V, S> HashMap<K, V, S>
inner: IterMut<'a, K, V>,
}
+/// An owning iterator over the keys of a `HashMap`.
+///
+/// This `struct` is created by the [`into_keys`] method on [`HashMap`].
+/// See its documentation for more.
+///
+/// [`into_keys`]: HashMap::into_keys
+#[unstable(feature = "map_into_keys_values", issue = "75294")]
+pub struct IntoKeys<K, V> {
+ inner: IntoIter<K, V>,
+}
+
+/// An owning iterator over the values of a `HashMap`.
+///
+/// This `struct` is created by the [`into_values`] method on [`HashMap`].
+/// See its documentation for more.
+///
+/// [`into_values`]: HashMap::into_values
+#[unstable(feature = "map_into_keys_values", issue = "75294")]
+pub struct IntoValues<K, V> {
+ inner: IntoIter<K, V>,
+}
+
/// A builder for computing where in a HashMap a key-value pair would be stored.
///
/// See the [`HashMap::raw_entry_mut`] docs for usage examples.
}
}
+#[unstable(feature = "map_into_keys_values", issue = "75294")]
+impl<K, V> Iterator for IntoKeys<K, V> {
+ type Item = K;
+
+ #[inline]
+ fn next(&mut self) -> Option<K> {
+ self.inner.next().map(|(k, _)| k)
+ }
+ #[inline]
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ self.inner.size_hint()
+ }
+}
+#[unstable(feature = "map_into_keys_values", issue = "75294")]
+impl<K, V> ExactSizeIterator for IntoKeys<K, V> {
+ #[inline]
+ fn len(&self) -> usize {
+ self.inner.len()
+ }
+}
+#[unstable(feature = "map_into_keys_values", issue = "75294")]
+impl<K, V> FusedIterator for IntoKeys<K, V> {}
+
+#[unstable(feature = "map_into_keys_values", issue = "75294")]
+impl<K: Debug, V: Debug> fmt::Debug for IntoKeys<K, V> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_list().entries(self.inner.iter().map(|(k, _)| k)).finish()
+ }
+}
+
+#[unstable(feature = "map_into_keys_values", issue = "75294")]
+impl<K, V> Iterator for IntoValues<K, V> {
+ type Item = V;
+
+ #[inline]
+ fn next(&mut self) -> Option<V> {
+ self.inner.next().map(|(_, v)| v)
+ }
+ #[inline]
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ self.inner.size_hint()
+ }
+}
+#[unstable(feature = "map_into_keys_values", issue = "75294")]
+impl<K, V> ExactSizeIterator for IntoValues<K, V> {
+ #[inline]
+ fn len(&self) -> usize {
+ self.inner.len()
+ }
+}
+#[unstable(feature = "map_into_keys_values", issue = "75294")]
+impl<K, V> FusedIterator for IntoValues<K, V> {}
+
+#[unstable(feature = "map_into_keys_values", issue = "75294")]
+impl<K: Debug, V: Debug> fmt::Debug for IntoValues<K, V> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_list().entries(self.inner.iter().map(|(_, v)| v)).finish()
+ }
+}
+
#[stable(feature = "drain", since = "1.6.0")]
impl<'a, K, V> Iterator for Drain<'a, K, V> {
type Item = (K, V);
}
#[inline]
-fn map_collection_alloc_err(err: hashbrown::CollectionAllocErr) -> TryReserveError {
+fn map_try_reserve_error(err: hashbrown::TryReserveError) -> TryReserveError {
match err {
- hashbrown::CollectionAllocErr::CapacityOverflow => TryReserveError::CapacityOverflow,
- hashbrown::CollectionAllocErr::AllocErr { layout } => {
+ hashbrown::TryReserveError::CapacityOverflow => TryReserveError::CapacityOverflow,
+ hashbrown::TryReserveError::AllocError { layout } => {
TryReserveError::AllocError { layout, non_exhaustive: () }
}
}
assert!(values.contains(&6));
}
+ #[test]
+ fn test_into_keys() {
+ let vec = vec![(1, 'a'), (2, 'b'), (3, 'c')];
+ let map: HashMap<_, _> = vec.into_iter().collect();
+ let keys: Vec<_> = map.into_keys().collect();
+
+ assert_eq!(keys.len(), 3);
+ assert!(keys.contains(&1));
+ assert!(keys.contains(&2));
+ assert!(keys.contains(&3));
+ }
+
+ #[test]
+ fn test_into_values() {
+ let vec = vec![(1, 'a'), (2, 'b'), (3, 'c')];
+ let map: HashMap<_, _> = vec.into_iter().collect();
+ let values: Vec<_> = map.into_values().collect();
+
+ assert_eq!(values.len(), 3);
+ assert!(values.contains(&'a'));
+ assert!(values.contains(&'b'));
+ assert!(values.contains(&'c'));
+ }
+
#[test]
fn test_find() {
let mut m = HashMap::new();
/// Capture a [closure]'s environment by value.
///
/// `move` converts any variables captured by reference or mutable reference
-/// to owned by value variables. The three [`Fn` trait]'s mirror the ways to capture
-/// variables, when `move` is used, the closures is represented by the `FnOnce` trait.
+/// to owned by value variables.
///
/// ```rust
/// let capture = "hello";
/// };
/// ```
///
+/// Note: `move` closures may still implement [`Fn`] or [`FnMut`], even though
+/// they capture variables by `move`. This is because the traits implemented by
+/// a closure type are determined by *what* the closure does with captured
+/// values, not *how* it captures them:
+///
+/// ```rust
+/// fn create_fn() -> impl Fn() {
+/// let text = "Fn".to_owned();
+///
+/// move || println!("This is a: {}", text)
+/// }
+///
+/// let fn_plain = create_fn();
+///
+/// fn_plain();
+/// ```
+///
/// `move` is often used when [threads] are involved.
///
/// ```rust
#![feature(negative_impls)]
#![feature(never_type)]
#![feature(nll)]
+#![feature(nonnull_slice_from_raw_parts)]
#![feature(once_cell)]
#![feature(optin_builtin_traits)]
#![feature(or_patterns)]
#![feature(shrink_to)]
#![feature(slice_concat_ext)]
#![feature(slice_internals)]
+#![feature(slice_ptr_get)]
+#![feature(slice_ptr_len)]
#![feature(slice_strip)]
#![feature(staged_api)]
#![feature(std_internals)]
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_stable(feature = "const_ipv4", since = "1.32.0")]
pub const fn new(a: u8, b: u8, c: u8, d: u8) -> Ipv4Addr {
- // FIXME: should just be u32::from_be_bytes([a, b, c, d]),
- // once that method is no longer rustc_const_unstable
- Ipv4Addr {
- inner: c::in_addr {
- s_addr: u32::to_be(
- ((a as u32) << 24) | ((b as u32) << 16) | ((c as u32) << 8) | (d as u32),
- ),
- },
- }
+ // `s_addr` is stored as BE on all machine and the array is in BE order.
+ // So the native endian conversion method is used so that it's never swapped.
+ Ipv4Addr { inner: c::in_addr { s_addr: u32::from_ne_bytes([a, b, c, d]) } }
}
/// An IPv4 address with the address pointing to localhost: 127.0.0.1.
&self.inner
}
}
-impl FromInner<c::in_addr> for Ipv4Addr {
- fn from_inner(addr: c::in_addr) -> Ipv4Addr {
- Ipv4Addr { inner: addr }
- }
-}
#[stable(feature = "ip_u32", since = "1.1.0")]
impl From<Ipv4Addr> for u32 {
/// ```
/// use std::net::Ipv4Addr;
///
- /// let addr = Ipv4Addr::new(13, 12, 11, 10);
- /// assert_eq!(0x0d0c0b0au32, u32::from(addr));
+ /// let addr = Ipv4Addr::new(0xca, 0xfe, 0xba, 0xbe);
+ /// assert_eq!(0xcafebabe, u32::from(addr));
/// ```
fn from(ip: Ipv4Addr) -> u32 {
let ip = ip.octets();
/// ```
/// use std::net::Ipv4Addr;
///
- /// let addr = Ipv4Addr::from(0x0d0c0b0au32);
- /// assert_eq!(Ipv4Addr::new(13, 12, 11, 10), addr);
+ /// let addr = Ipv4Addr::from(0xcafebabe);
+ /// assert_eq!(Ipv4Addr::new(0xca, 0xfe, 0xba, 0xbe), addr);
/// ```
fn from(ip: u32) -> Ipv4Addr {
Ipv4Addr::from(ip.to_be_bytes())
let loc = info.location().unwrap(); // The current implementation always returns Some
let msg = info.message().unwrap(); // The current implementation always returns Some
- rust_panic_with_hook(&mut PanicPayload::new(msg), info.message(), loc);
+ crate::sys_common::backtrace::__rust_end_short_backtrace(move || {
+ rust_panic_with_hook(&mut PanicPayload::new(msg), info.message(), loc);
+ })
}
/// This is the entry point of panicking for the non-format-string variants of
intrinsics::abort()
}
- rust_panic_with_hook(&mut PanicPayload::new(msg), None, Location::caller());
+ let loc = Location::caller();
+ return crate::sys_common::backtrace::__rust_end_short_backtrace(move || {
+ rust_panic_with_hook(&mut PanicPayload::new(msg), None, loc)
+ });
struct PanicPayload<A> {
inner: Option<A>,
//! let path: PathBuf = ["c:\\", "windows", "system32.dll"].iter().collect();
//! ```
//!
-//! [`Component`]: ../../std/path/enum.Component.html
-//! [`components`]: ../../std/path/struct.Path.html#method.components
-//! [`PathBuf`]: ../../std/path/struct.PathBuf.html
-//! [`Path`]: ../../std/path/struct.Path.html
-//! [`push`]: ../../std/path/struct.PathBuf.html#method.push
-//! [`String`]: ../../std/string/struct.String.html
-//!
-//! [`str`]: ../../std/primitive.str.html
-//! [`OsString`]: ../../std/ffi/struct.OsString.html
-//! [`OsStr`]: ../../std/ffi/struct.OsStr.html
+//! [`components`]: Path::components
+//! [`push`]: PathBuf::push
#![stable(feature = "rust1", since = "1.0.0")]
/// # }
/// ```
///
-/// [`as_os_str`]: #method.as_os_str
-/// [`Component`]: enum.Component.html
-/// [`kind`]: #method.kind
-/// [`OsStr`]: ../../std/ffi/struct.OsStr.html
-/// [`Prefix` variant]: enum.Component.html#variant.Prefix
-/// [`Prefix`]: enum.Prefix.html
+/// [`as_os_str`]: PrefixComponent::as_os_str
+/// [`kind`]: PrefixComponent::kind
+/// [`Prefix` variant]: Component::Prefix
#[stable(feature = "rust1", since = "1.0.0")]
#[derive(Copy, Clone, Eq, Debug)]
pub struct PrefixComponent<'a> {
///
/// See [`Prefix`]'s documentation for more information on the different
/// kinds of prefixes.
- ///
- /// [`Prefix`]: enum.Prefix.html
#[stable(feature = "rust1", since = "1.0.0")]
pub fn kind(&self) -> Prefix<'a> {
self.parsed
}
/// Returns the raw [`OsStr`] slice for this prefix.
- ///
- /// [`OsStr`]: ../../std/ffi/struct.OsStr.html
#[stable(feature = "rust1", since = "1.0.0")]
pub fn as_os_str(&self) -> &'a OsStr {
self.raw
/// Component::Normal("bar.txt".as_ref()),
/// ]);
/// ```
-///
-/// [`Components`]: struct.Components.html
-/// [`Path`]: struct.Path.html
-/// [`Path::components`]: struct.Path.html#method.components
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
#[stable(feature = "rust1", since = "1.0.0")]
pub enum Component<'a> {
/// for more.
///
/// Does not occur on Unix.
- ///
- /// [`Prefix`]: enum.Prefix.html
#[stable(feature = "rust1", since = "1.0.0")]
Prefix(#[stable(feature = "rust1", since = "1.0.0")] PrefixComponent<'a>),
/// let components: Vec<_> = path.components().map(|comp| comp.as_os_str()).collect();
/// assert_eq!(&components, &[".", "tmp", "foo", "bar.txt"]);
/// ```
- ///
- /// [`OsStr`]: ../../std/ffi/struct.OsStr.html
#[stable(feature = "rust1", since = "1.0.0")]
pub fn as_os_str(self) -> &'a OsStr {
match self {
/// }
/// ```
///
-/// [`Component`]: enum.Component.html
-/// [`components`]: struct.Path.html#method.components
-/// [`Path`]: struct.Path.html
+/// [`components`]: Path::components
#[derive(Clone)]
#[stable(feature = "rust1", since = "1.0.0")]
pub struct Components<'a> {
/// This `struct` is created by the [`iter`] method on [`Path`].
/// See its documentation for more.
///
-/// [`Component`]: enum.Component.html
-/// [`iter`]: struct.Path.html#method.iter
-/// [`OsStr`]: ../../std/ffi/struct.OsStr.html
-/// [`Path`]: struct.Path.html
+/// [`iter`]: Path::iter
#[derive(Clone)]
#[stable(feature = "rust1", since = "1.0.0")]
pub struct Iter<'a> {
/// }
/// ```
///
-/// [`ancestors`]: struct.Path.html#method.ancestors
-/// [`Path`]: struct.Path.html
+/// [`ancestors`]: Path::ancestors
#[derive(Copy, Clone, Debug)]
#[stable(feature = "path_ancestors", since = "1.28.0")]
pub struct Ancestors<'a> {
/// the path in place. It also implements [`Deref`] to [`Path`], meaning that
/// all methods on [`Path`] slices are available on `PathBuf` values as well.
///
-/// [`String`]: ../string/struct.String.html
-/// [`Path`]: struct.Path.html
-/// [`push`]: struct.PathBuf.html#method.push
-/// [`set_extension`]: struct.PathBuf.html#method.set_extension
-/// [`Deref`]: ../ops/trait.Deref.html
+/// [`push`]: PathBuf::push
+/// [`set_extension`]: PathBuf::set_extension
///
/// More details about the overall approach can be found in
/// the [module documentation](index.html).
/// assert_eq!(capacity, path.capacity());
/// ```
///
- /// [`with_capacity`]: ../ffi/struct.OsString.html#method.with_capacity
- /// [`OsString`]: ../ffi/struct.OsString.html
+ /// [`with_capacity`]: OsString::with_capacity
#[stable(feature = "path_buf_capacity", since = "1.44.0")]
pub fn with_capacity(capacity: usize) -> PathBuf {
PathBuf { inner: OsString::with_capacity(capacity) }
/// Coerces to a [`Path`] slice.
///
- /// [`Path`]: struct.Path.html
- ///
/// # Examples
///
/// ```
/// Returns `false` and does nothing if [`self.parent`] is [`None`].
/// Otherwise, returns `true`.
///
- /// [`None`]: ../../std/option/enum.Option.html#variant.None
- /// [`self.parent`]: struct.PathBuf.html#method.parent
+ /// [`self.parent`]: Path::parent
///
/// # Examples
///
/// ```
/// use std::path::{Path, PathBuf};
///
- /// let mut p = PathBuf::from("/test/test.rs");
+ /// let mut p = PathBuf::from("/spirited/away.rs");
///
/// p.pop();
- /// assert_eq!(Path::new("/test"), p);
+ /// assert_eq!(Path::new("/spirited"), p);
/// p.pop();
/// assert_eq!(Path::new("/"), p);
/// ```
/// `file_name`. The new path will be a sibling of the original path.
/// (That is, it will have the same parent.)
///
- /// [`self.file_name`]: struct.PathBuf.html#method.file_name
- /// [`None`]: ../../std/option/enum.Option.html#variant.None
- /// [`pop`]: struct.PathBuf.html#method.pop
+ /// [`self.file_name`]: Path::file_name
+ /// [`pop`]: PathBuf::pop
///
/// # Examples
///
/// If [`self.extension`] is [`None`], the extension is added; otherwise
/// it is replaced.
///
- /// [`self.file_name`]: struct.PathBuf.html#method.file_name
- /// [`self.extension`]: struct.PathBuf.html#method.extension
- /// [`None`]: ../../std/option/enum.Option.html#variant.None
+ /// [`self.file_name`]: Path::file_name
+ /// [`self.extension`]: Path::extension
///
/// # Examples
///
/// Consumes the `PathBuf`, yielding its internal [`OsString`] storage.
///
- /// [`OsString`]: ../ffi/struct.OsString.html
- ///
/// # Examples
///
/// ```
}
/// Converts this `PathBuf` into a [boxed][`Box`] [`Path`].
- ///
- /// [`Box`]: ../../std/boxed/struct.Box.html
- /// [`Path`]: struct.Path.html
#[stable(feature = "into_boxed_path", since = "1.20.0")]
pub fn into_boxed_path(self) -> Box<Path> {
let rw = Box::into_raw(self.inner.into_boxed_os_str()) as *mut Path;
/// Invokes [`capacity`] on the underlying instance of [`OsString`].
///
- /// [`capacity`]: ../ffi/struct.OsString.html#method.capacity
- /// [`OsString`]: ../ffi/struct.OsString.html
+ /// [`capacity`]: OsString::capacity
#[stable(feature = "path_buf_capacity", since = "1.44.0")]
pub fn capacity(&self) -> usize {
self.inner.capacity()
/// Invokes [`clear`] on the underlying instance of [`OsString`].
///
- /// [`clear`]: ../ffi/struct.OsString.html#method.clear
- /// [`OsString`]: ../ffi/struct.OsString.html
+ /// [`clear`]: OsString::clear
#[stable(feature = "path_buf_capacity", since = "1.44.0")]
pub fn clear(&mut self) {
self.inner.clear()
/// Invokes [`reserve`] on the underlying instance of [`OsString`].
///
- /// [`reserve`]: ../ffi/struct.OsString.html#method.reserve
- /// [`OsString`]: ../ffi/struct.OsString.html
+ /// [`reserve`]: OsString::reserve
#[stable(feature = "path_buf_capacity", since = "1.44.0")]
pub fn reserve(&mut self, additional: usize) {
self.inner.reserve(additional)
/// Invokes [`reserve_exact`] on the underlying instance of [`OsString`].
///
- /// [`reserve_exact`]: ../ffi/struct.OsString.html#method.reserve_exact
- /// [`OsString`]: ../ffi/struct.OsString.html
+ /// [`reserve_exact`]: OsString::reserve_exact
#[stable(feature = "path_buf_capacity", since = "1.44.0")]
pub fn reserve_exact(&mut self, additional: usize) {
self.inner.reserve_exact(additional)
/// Invokes [`shrink_to_fit`] on the underlying instance of [`OsString`].
///
- /// [`shrink_to_fit`]: ../ffi/struct.OsString.html#method.shrink_to_fit
- /// [`OsString`]: ../ffi/struct.OsString.html
+ /// [`shrink_to_fit`]: OsString::shrink_to_fit
#[stable(feature = "path_buf_capacity", since = "1.44.0")]
pub fn shrink_to_fit(&mut self) {
self.inner.shrink_to_fit()
/// Invokes [`shrink_to`] on the underlying instance of [`OsString`].
///
- /// [`shrink_to`]: ../ffi/struct.OsString.html#method.shrink_to
- /// [`OsString`]: ../ffi/struct.OsString.html
+ /// [`shrink_to`]: OsString::shrink_to
#[unstable(feature = "shrink_to", issue = "56431")]
pub fn shrink_to(&mut self, min_capacity: usize) {
self.inner.shrink_to(min_capacity)
/// pointer like `&` or [`Box`]. For an owned version of this type,
/// see [`PathBuf`].
///
-/// [`str`]: ../primitive.str.html
-/// [`Box`]: ../boxed/struct.Box.html
-/// [`PathBuf`]: struct.PathBuf.html
-///
/// More details about the overall approach can be found in
/// the [module documentation](index.html).
///
/// This `struct` is created by the [`strip_prefix`] method on [`Path`].
/// See its documentation for more.
///
-/// [`strip_prefix`]: struct.Path.html#method.strip_prefix
-/// [`Path`]: struct.Path.html
+/// [`strip_prefix`]: Path::strip_prefix
#[derive(Debug, Clone, PartialEq, Eq)]
#[stable(since = "1.7.0", feature = "strip_prefix")]
pub struct StripPrefixError(());
/// Yields the underlying [`OsStr`] slice.
///
- /// [`OsStr`]: ../ffi/struct.OsStr.html
- ///
/// # Examples
///
/// ```
/// Note that validation is performed because non-UTF-8 strings are
/// perfectly valid for some OS.
///
- /// [`&str`]: ../primitive.str.html
+ /// [`&str`]: str
///
/// # Examples
///
/// Any non-Unicode sequences are replaced with
/// [`U+FFFD REPLACEMENT CHARACTER`][U+FFFD].
///
- /// [`Cow<str>`]: ../borrow/enum.Cow.html
- /// [U+FFFD]: ../char/constant.REPLACEMENT_CHARACTER.html
+ /// [`Cow<str>`]: Cow
+ /// [U+FFFD]: super::char::REPLACEMENT_CHARACTER
///
/// # Examples
///
/// Converts a `Path` to an owned [`PathBuf`].
///
- /// [`PathBuf`]: struct.PathBuf.html
- ///
/// # Examples
///
/// ```
/// assert!(!Path::new("foo.txt").is_absolute());
/// ```
///
- /// [`has_root`]: #method.has_root
+ /// [`has_root`]: Path::has_root
#[stable(feature = "rust1", since = "1.0.0")]
#[allow(deprecated)]
pub fn is_absolute(&self) -> bool {
/// assert!(Path::new("foo.txt").is_relative());
/// ```
///
- /// [`is_absolute`]: #method.is_absolute
+ /// [`is_absolute`]: Path::is_absolute
#[stable(feature = "rust1", since = "1.0.0")]
pub fn is_relative(&self) -> bool {
!self.is_absolute()
///
/// Returns [`None`] if the path terminates in a root or prefix.
///
- /// [`None`]: ../../std/option/enum.Option.html#variant.None
- ///
/// # Examples
///
/// ```
/// assert_eq!(ancestors.next(), Some(Path::new("/foo")));
/// assert_eq!(ancestors.next(), Some(Path::new("/")));
/// assert_eq!(ancestors.next(), None);
+ ///
+ /// let mut ancestors = Path::new("../foo/bar").ancestors();
+ /// assert_eq!(ancestors.next(), Some(Path::new("../foo/bar")));
+ /// assert_eq!(ancestors.next(), Some(Path::new("../foo")));
+ /// assert_eq!(ancestors.next(), Some(Path::new("..")));
+ /// assert_eq!(ancestors.next(), Some(Path::new("")));
+ /// assert_eq!(ancestors.next(), None);
/// ```
///
- /// [`None`]: ../../std/option/enum.Option.html#variant.None
- /// [`parent`]: struct.Path.html#method.parent
+ /// [`parent`]: Path::parent
#[stable(feature = "path_ancestors", since = "1.28.0")]
pub fn ancestors(&self) -> Ancestors<'_> {
Ancestors { next: Some(&self) }
///
/// Returns [`None`] if the path terminates in `..`.
///
- /// [`None`]: ../../std/option/enum.Option.html#variant.None
- ///
/// # Examples
///
/// ```
/// If `base` is not a prefix of `self` (i.e., [`starts_with`]
/// returns `false`), returns [`Err`].
///
- /// [`starts_with`]: #method.starts_with
- /// [`Err`]: ../../std/result/enum.Result.html#variant.Err
+ /// [`starts_with`]: Path::starts_with
///
/// # Examples
///
/// assert_eq!(path.strip_prefix("/test/"), Ok(Path::new("haha/foo.txt")));
/// assert_eq!(path.strip_prefix("/test/haha/foo.txt"), Ok(Path::new("")));
/// assert_eq!(path.strip_prefix("/test/haha/foo.txt/"), Ok(Path::new("")));
- /// assert_eq!(path.strip_prefix("test").is_ok(), false);
- /// assert_eq!(path.strip_prefix("/haha").is_ok(), false);
+ ///
+ /// assert!(path.strip_prefix("test").is_err());
+ /// assert!(path.strip_prefix("/haha").is_err());
///
/// let prefix = PathBuf::from("/test/");
/// assert_eq!(path.strip_prefix(prefix), Ok(Path::new("haha/foo.txt")));
/// Extracts the stem (non-extension) portion of [`self.file_name`].
///
- /// [`self.file_name`]: struct.Path.html#method.file_name
+ /// [`self.file_name`]: Path::file_name
///
/// The stem is:
///
/// * The entire file name if the file name begins with `.` and has no other `.`s within;
/// * Otherwise, the portion of the file name before the final `.`
///
- /// [`None`]: ../../std/option/enum.Option.html#variant.None
- ///
/// # Examples
///
/// ```
/// use std::path::Path;
///
- /// let path = Path::new("foo.rs");
- ///
- /// assert_eq!("foo", path.file_stem().unwrap());
+ /// assert_eq!("foo", Path::new("foo.rs").file_stem().unwrap());
+ /// assert_eq!("foo.tar", Path::new("foo.tar.gz").file_stem().unwrap());
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn file_stem(&self) -> Option<&OsStr> {
/// * [`None`], if the file name begins with `.` and has no other `.`s within;
/// * Otherwise, the portion of the file name after the final `.`
///
- /// [`self.file_name`]: struct.Path.html#method.file_name
- /// [`None`]: ../../std/option/enum.Option.html#variant.None
+ /// [`self.file_name`]: Path::file_name
///
/// # Examples
///
/// ```
/// use std::path::Path;
///
- /// let path = Path::new("foo.rs");
- ///
- /// assert_eq!("rs", path.extension().unwrap());
+ /// assert_eq!("rs", Path::new("foo.rs").extension().unwrap());
+ /// assert_eq!("gz", Path::new("foo.tar.gz").extension().unwrap());
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn extension(&self) -> Option<&OsStr> {
///
/// See [`PathBuf::push`] for more details on what it means to adjoin a path.
///
- /// [`PathBuf`]: struct.PathBuf.html
- /// [`PathBuf::push`]: struct.PathBuf.html#method.push
- ///
/// # Examples
///
/// ```
///
/// See [`PathBuf::set_file_name`] for more details.
///
- /// [`PathBuf`]: struct.PathBuf.html
- /// [`PathBuf::set_file_name`]: struct.PathBuf.html#method.set_file_name
- ///
/// # Examples
///
/// ```
///
/// See [`PathBuf::set_extension`] for more details.
///
- /// [`PathBuf`]: struct.PathBuf.html
- /// [`PathBuf::set_extension`]: struct.PathBuf.html#method.set_extension
- ///
/// # Examples
///
/// ```
///
/// let path = Path::new("foo.tar.gz");
/// assert_eq!(path.with_extension(""), PathBuf::from("foo.tar"));
+ /// assert_eq!(path.with_extension("xz"), PathBuf::from("foo.tar.xz"));
+ /// assert_eq!(path.with_extension("").with_extension("txt"), PathBuf::from("foo.txt"));
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn with_extension<S: AsRef<OsStr>>(&self, extension: S) -> PathBuf {
/// assert_eq!(components.next(), None)
/// ```
///
- /// [`Component`]: enum.Component.html
- /// [`CurDir`]: enum.Component.html#variant.CurDir
+ /// [`CurDir`]: Component::CurDir
#[stable(feature = "rust1", since = "1.0.0")]
pub fn components(&self) -> Components<'_> {
let prefix = parse_prefix(self.as_os_str());
/// For more information about the particulars of how the path is separated
/// into components, see [`components`].
///
- /// [`components`]: #method.components
- /// [`OsStr`]: ../ffi/struct.OsStr.html
+ /// [`components`]: Path::components
///
/// # Examples
///
/// Returns an object that implements [`Display`] for safely printing paths
/// that may contain non-Unicode data.
///
- /// [`Display`]: ../fmt/trait.Display.html
+ /// [`Display`]: fmt::Display
///
/// # Examples
///
///
/// This is an alias to [`fs::metadata`].
///
- /// [`fs::metadata`]: ../fs/fn.metadata.html
- ///
/// # Examples
///
/// ```no_run
///
/// This is an alias to [`fs::symlink_metadata`].
///
- /// [`fs::symlink_metadata`]: ../fs/fn.symlink_metadata.html
- ///
/// # Examples
///
/// ```no_run
///
/// This is an alias to [`fs::canonicalize`].
///
- /// [`fs::canonicalize`]: ../fs/fn.canonicalize.html
- ///
/// # Examples
///
/// ```no_run
///
/// This is an alias to [`fs::read_link`].
///
- /// [`fs::read_link`]: ../fs/fn.read_link.html
- ///
/// # Examples
///
/// ```no_run
/// Returns an iterator over the entries within a directory.
///
- /// The iterator will yield instances of [`io::Result`]`<`[`DirEntry`]`>`. New
+ /// The iterator will yield instances of [`io::Result`]`<`[`fs::DirEntry`]`>`. New
/// errors may be encountered after an iterator is initially constructed.
///
/// This is an alias to [`fs::read_dir`].
///
- /// [`io::Result`]: ../io/type.Result.html
- /// [`DirEntry`]: ../fs/struct.DirEntry.html
- /// [`fs::read_dir`]: ../fs/fn.read_dir.html
- ///
/// # Examples
///
/// ```no_run
///
/// ```no_run
/// use std::path::Path;
- /// assert_eq!(Path::new("does_not_exist.txt").exists(), false);
+ /// assert!(!Path::new("does_not_exist.txt").exists());
/// ```
///
/// # See Also
///
/// This is a convenience function that coerces errors to false. If you want to
- /// check errors, call [fs::metadata].
- ///
- /// [fs::metadata]: ../../std/fs/fn.metadata.html
+ /// check errors, call [`fs::metadata`].
#[stable(feature = "path_ext", since = "1.5.0")]
pub fn exists(&self) -> bool {
fs::metadata(self).is_ok()
/// # See Also
///
/// This is a convenience function that coerces errors to false. If you want to
- /// check errors, call [`fs::metadata`] and handle its Result. Then call
- /// [`fs::Metadata::is_file`] if it was Ok.
+ /// check errors, call [`fs::metadata`] and handle its [`Result`]. Then call
+ /// [`fs::Metadata::is_file`] if it was [`Ok`].
///
/// When the goal is simply to read from (or write to) the source, the most
/// reliable way to test the source can be read (or written to) is to open
/// it. Only using `is_file` can break workflows like `diff <( prog_a )` on
- /// a Unix-like system for example. See [`File::open`] or
- /// [`OpenOptions::open`] for more information.
- ///
- /// [`fs::metadata`]: ../../std/fs/fn.metadata.html
- /// [`fs::Metadata`]: ../../std/fs/struct.Metadata.html
- /// [`fs::Metadata::is_file`]: ../../std/fs/struct.Metadata.html#method.is_file
- /// [`File::open`]: ../../std/fs/struct.File.html#method.open
- /// [`OpenOptions::open`]: ../../std/fs/struct.OpenOptions.html#method.open
+ /// a Unix-like system for example. See [`fs::File::open`] or
+ /// [`fs::OpenOptions::open`] for more information.
#[stable(feature = "path_ext", since = "1.5.0")]
pub fn is_file(&self) -> bool {
fs::metadata(self).map(|m| m.is_file()).unwrap_or(false)
/// # See Also
///
/// This is a convenience function that coerces errors to false. If you want to
- /// check errors, call [fs::metadata] and handle its Result. Then call
- /// [fs::Metadata::is_dir] if it was Ok.
- ///
- /// [fs::metadata]: ../../std/fs/fn.metadata.html
- /// [fs::Metadata::is_dir]: ../../std/fs/struct.Metadata.html#method.is_dir
+ /// check errors, call [`fs::metadata`] and handle its [`Result`]. Then call
+ /// [`fs::Metadata::is_dir`] if it was [`Ok`].
#[stable(feature = "path_ext", since = "1.5.0")]
pub fn is_dir(&self) -> bool {
fs::metadata(self).map(|m| m.is_dir()).unwrap_or(false)
/// Converts a [`Box<Path>`][`Box`] into a [`PathBuf`] without copying or
/// allocating.
- ///
- /// [`Box`]: ../../std/boxed/struct.Box.html
- /// [`PathBuf`]: struct.PathBuf.html
#[stable(feature = "into_boxed_path", since = "1.20.0")]
pub fn into_path_buf(self: Box<Path>) -> PathBuf {
let rw = Box::into_raw(self) as *mut OsStr;
/// println!("{}", path.display());
/// ```
///
-/// [`Display`]: ../../std/fmt/trait.Display.html
-/// [`format!`]: ../../std/macro.format.html
-/// [`Path`]: struct.Path.html
-/// [`Path::display`]: struct.Path.html#method.display
+/// [`Display`]: fmt::Display
+/// [`format!`]: crate::format
#[stable(feature = "rust1", since = "1.0.0")]
pub struct Display<'a> {
path: &'a Path,
sys::args::init(argc, argv);
// Let's run some code!
- let exit_code = panic::catch_unwind(|| {
- sys_common::backtrace::__rust_begin_short_backtrace(move || main())
- });
+ let exit_code = panic::catch_unwind(main);
sys_common::cleanup();
exit_code.unwrap_or(101) as isize
argc: isize,
argv: *const *const u8,
) -> isize {
- lang_start_internal(&move || main().report(), argc, argv)
+ lang_start_internal(
+ &move || crate::sys_common::backtrace::__rust_begin_short_backtrace(main).report(),
+ argc,
+ argv,
+ )
}
argv: *const *const c_char,
env: *const *const c_char,
) -> ! {
- use crate::sys::hermit::fast_thread_local::run_dtors;
+ use crate::sys::hermit::thread_local_dtor::run_dtors;
extern "C" {
fn main(argc: isize, argv: *const *const c_char) -> i32;
}
use crate::io;
use crate::mem;
use crate::sys::hermit::abi;
-use crate::sys::hermit::fast_thread_local::run_dtors;
+use crate::sys::hermit::thread_local_dtor::run_dtors;
use crate::time::Duration;
pub type Tid = abi::Tid;
use crate::cmp;
use crate::io::{self, Initializer, IoSlice, IoSliceMut, Read};
use crate::mem;
+#[cfg(not(any(target_os = "redox", target_env = "newlib")))]
+use crate::sync::atomic::{AtomicUsize, Ordering};
use crate::sys::cvt;
use crate::sys_common::AsInner;
#[cfg(not(target_os = "macos"))]
const READ_LIMIT: usize = libc::ssize_t::MAX as usize;
+#[cfg(not(any(target_os = "redox", target_env = "newlib")))]
+fn max_iov() -> usize {
+ static LIM: AtomicUsize = AtomicUsize::new(0);
+
+ let mut lim = LIM.load(Ordering::Relaxed);
+ if lim == 0 {
+ let ret = unsafe { libc::sysconf(libc::_SC_IOV_MAX) };
+
+ // 16 is the minimum value required by POSIX.
+ lim = if ret > 0 { ret as usize } else { 16 };
+ LIM.store(lim, Ordering::Relaxed);
+ }
+
+ lim
+}
+
+#[cfg(any(target_os = "redox", target_env = "newlib"))]
+fn max_iov() -> usize {
+ 16 // The minimum value required by POSIX.
+}
+
impl FileDesc {
pub fn new(fd: c_int) -> FileDesc {
FileDesc { fd }
libc::readv(
self.fd,
bufs.as_ptr() as *const libc::iovec,
- cmp::min(bufs.len(), c_int::MAX as usize) as c_int,
+ cmp::min(bufs.len(), max_iov()) as c_int,
)
})?;
Ok(ret as usize)
libc::writev(
self.fd,
bufs.as_ptr() as *const libc::iovec,
- cmp::min(bufs.len(), c_int::MAX as usize) as c_int,
+ cmp::min(bufs.len(), max_iov()) as c_int,
)
})?;
Ok(ret as usize)
let _ = unsafe { libc::close(self.fd) };
}
}
+
+#[cfg(test)]
+mod tests {
+ use super::{FileDesc, IoSlice};
+ use core::mem::ManuallyDrop;
+
+ #[test]
+ fn limit_vector_count() {
+ let stdout = ManuallyDrop::new(FileDesc { fd: 1 });
+ let bufs = (0..1500).map(|_| IoSlice::new(&[])).collect::<Vec<_>>();
+ assert!(stdout.write_vectored(&bufs).is_ok());
+ }
+}
pub fn unrolled_find_u16s(needle: u16, haystack: &[u16]) -> Option<usize> {
let ptr = haystack.as_ptr();
- let mut len = haystack.len();
let mut start = &haystack[..];
// For performance reasons unfold the loop eight times.
- while len >= 8 {
- if start[0] == needle {
- return Some((start.as_ptr() as usize - ptr as usize) / 2);
- }
- if start[1] == needle {
- return Some((start[1..].as_ptr() as usize - ptr as usize) / 2);
- }
- if start[2] == needle {
- return Some((start[2..].as_ptr() as usize - ptr as usize) / 2);
- }
- if start[3] == needle {
- return Some((start[3..].as_ptr() as usize - ptr as usize) / 2);
- }
- if start[4] == needle {
- return Some((start[4..].as_ptr() as usize - ptr as usize) / 2);
- }
- if start[5] == needle {
- return Some((start[5..].as_ptr() as usize - ptr as usize) / 2);
- }
- if start[6] == needle {
- return Some((start[6..].as_ptr() as usize - ptr as usize) / 2);
- }
- if start[7] == needle {
- return Some((start[7..].as_ptr() as usize - ptr as usize) / 2);
+ while start.len() >= 8 {
+ macro_rules! if_return {
+ ($($n:literal,)+) => {
+ $(
+ if start[$n] == needle {
+ return Some((&start[$n] as *const u16 as usize - ptr as usize) / 2);
+ }
+ )+
+ }
}
+ if_return!(0, 1, 2, 3, 4, 5, 6, 7,);
+
start = &start[8..];
- len -= 8;
}
- for (i, c) in start.iter().enumerate() {
+ for c in start {
if *c == needle {
- return Some((start.as_ptr() as usize - ptr as usize) / 2 + i);
+ return Some((c as *const u16 as usize - ptr as usize) / 2);
}
}
None
bt_fmt.add_context()?;
let mut idx = 0;
let mut res = Ok(());
+ // Start immediately if we're not using a short backtrace.
+ let mut start = print_fmt != PrintFmt::Short;
backtrace_rs::trace_unsynchronized(|frame| {
if print_fmt == PrintFmt::Short && idx > MAX_NB_FRAMES {
return false;
stop = true;
return;
}
+ if sym.contains("__rust_end_short_backtrace") {
+ start = true;
+ return;
+ }
}
}
- res = bt_fmt.frame().symbol(frame, symbol);
+ if start {
+ res = bt_fmt.frame().symbol(frame, symbol);
+ }
});
if stop {
return false;
}
if !hit {
- res = bt_fmt.frame().print_raw(frame.ip(), None, None, None);
+ if start {
+ res = bt_fmt.frame().print_raw(frame.ip(), None, None, None);
+ }
}
idx += 1;
pub fn __rust_begin_short_backtrace<F, T>(f: F) -> T
where
F: FnOnce() -> T,
- F: Send,
- T: Send,
{
- f()
+ let result = f();
+
+ // prevent this frame from being tail-call optimised away
+ crate::hint::black_box(());
+
+ result
+}
+
+/// Fixed frame used to clean the backtrace with `RUST_BACKTRACE=1`. Note that
+/// this is only inline(never) when backtraces in libstd are enabled, otherwise
+/// it's fine to optimize away.
+#[cfg_attr(feature = "backtrace", inline(never))]
+pub fn __rust_end_short_backtrace<F, T>(f: F) -> T
+where
+ F: FnOnce() -> T,
+{
+ let result = f();
+
+ // prevent this frame from being tail-call optimised away
+ crate::hint::black_box(());
+
+ result
}
pub enum RustBacktrace {
#[allow(deprecated)]
pub fn get_concurrency() -> usize {
- return match env::var("RUST_TEST_THREADS") {
+ match env::var("RUST_TEST_THREADS") {
Ok(s) => {
let opt_n: Option<usize> = s.parse().ok();
match opt_n {
}
}
Err(..) => num_cpus(),
- };
+ }
}
cfg_if::cfg_if! {
/// Fixed frame used to clean the backtrace with `RUST_BACKTRACE=1`.
#[inline(never)]
fn __rust_begin_short_backtrace<F: FnOnce()>(f: F) {
- f()
+ f();
+
+ // prevent this frame from being tail-call optimised away
+ black_box(());
}
fn run_test_in_process(
} else if #[cfg(any(
unix,
windows,
+ target_os = "psp",
target_os = "cloudabi",
all(target_vendor = "fortanix", target_env = "sgx"),
))] {
// - os=uefi
// - os=cuda
// - nvptx64-nvidia-cuda
- // - mipsel-sony-psp
// - Any new targets not listed above.
}
}
<<: [*shared-ci-variables, *dummy-variables]
if: github.event_name == 'push' && github.ref == 'refs/heads/auto' && github.repository == 'rust-lang-ci/rust'
strategy:
+ fail-fast: false
matrix:
include:
#############################
-Subproject commit a914f2c7e5cdb771fa465de142381a51c53b580e
+Subproject commit 363293c1c5ce9e84ea3935a5e29ce8624801208a
-Subproject commit 94d9ea8460bcbbbfef1877b47cb930260b5849a7
+Subproject commit b5256448a2a4c1bec68b93c0847066f92f2ff5a9
-Subproject commit b329ce37424874ad4db94f829a55807c6e21d2cb
+Subproject commit c9b2736a059469043177e1e4ed41a55d7c63ac28
-Subproject commit 229c6945a26a53a751ffa4f9cb418388c00029d3
+Subproject commit 2e9271981adc32613365810f3428334c07095215
ctrl = table["ctrl"]["pointer"]
self.size = int(table["items"])
- self.data_ptr = table["data"]["pointer"]
- self.pair_type = self.data_ptr.dereference().type
+ self.pair_type = table.type.template_argument(0)
+
+ self.new_layout = not table.type.has_key("data")
+ if self.new_layout:
+ self.data_ptr = ctrl.cast(self.pair_type.pointer())
+ else:
+ self.data_ptr = table["data"]["pointer"]
self.valid_indices = []
for idx in range(capacity):
for index in range(self.size):
idx = self.valid_indices[index]
+ if self.new_layout:
+ idx = -(idx + 1)
element = (pairs_start + idx).dereference()
if self.show_values:
yield "key{}".format(index), element[ZERO_FIELD]
# type: (int) -> SBValue
pairs_start = self.data_ptr.GetValueAsUnsigned()
idx = self.valid_indices[index]
+ if self.new_layout:
+ idx = -(idx + 1)
address = pairs_start + idx * self.pair_type_size
element = self.data_ptr.CreateValueFromAddress("[%s]" % index, address, self.pair_type)
if self.show_values:
ctrl = table.GetChildMemberWithName("ctrl").GetChildAtIndex(0)
self.size = table.GetChildMemberWithName("items").GetValueAsUnsigned()
- self.data_ptr = table.GetChildMemberWithName("data").GetChildAtIndex(0)
- self.pair_type = self.data_ptr.Dereference().GetType()
+ self.pair_type = table.type.template_args[0]
self.pair_type_size = self.pair_type.GetByteSize()
+ self.new_layout = not table.GetChildMemberWithName("data").IsValid()
+ if self.new_layout:
+ self.data_ptr = ctrl.Cast(self.pair_type.GetPointerType())
+ else:
+ self.data_ptr = table.GetChildMemberWithName("data").GetChildAtIndex(0)
+
u8_type = self.valobj.GetTarget().GetBasicType(eBasicTypeUnsignedChar)
u8_type_size = self.valobj.GetTarget().GetBasicType(eBasicTypeUnsignedChar).GetByteSize()
<Expand>
<Item Name="[size]">base.table.items</Item>
<Item Name="[capacity]">base.table.items + base.table.growth_left</Item>
+ <Item Name="[state]">base.hash_builder</Item>
<CustomListItems>
<Variable Name="i" InitialValue="0" />
<If Condition="(base.table.ctrl.pointer[i] & 0x80) == 0">
<!-- Bucket is populated -->
<Exec>n--</Exec>
- <Item Name="{base.table.data.pointer[i].__0}">base.table.data.pointer[i].__1</Item>
+ <Item Name="{static_cast<tuple<$T1, $T2>*>(base.table.ctrl.pointer)[-(i + 1)].__0}">static_cast<tuple<$T1, $T2>*>(base.table.ctrl.pointer)[-(i + 1)].__1</Item>
</If>
<Exec>i++</Exec>
</Loop>
<Expand>
<Item Name="[size]">map.base.table.items</Item>
<Item Name="[capacity]">map.base.table.items + map.base.table.growth_left</Item>
+ <Item Name="[state]">map.base.hash_builder</Item>
<CustomListItems>
<Variable Name="i" InitialValue="0" />
<If Condition="(map.base.table.ctrl.pointer[i] & 0x80) == 0">
<!-- Bucket is populated -->
<Exec>n--</Exec>
- <Item>map.base.table.data.pointer[i].__0</Item>
- </If>
- <Exec>i++</Exec>
- </Loop>
- </CustomListItems>
- </Expand>
- </Type>
-
- <Type Name="hashbrown::raw::RawTable<*>">
- <!-- RawTable has a nice and simple layout.
- items Number of *populated* values in the RawTable (less than the size of ctrl.pointer / data.pointer)
- growth_left Remaining capacity before growth
- ctrl.pointer[i] & 0x80 Indicates the bucket is empty / should be skipped / doesn't count towards items.
- data.pointer[i] The (K,V) tuple, if not empty.
- -->
- <DisplayString>{{ size={items} }}</DisplayString>
- <Expand>
- <Item Name="[size]">items</Item>
- <Item Name="[capacity]">items + growth_left</Item>
-
- <CustomListItems>
- <Variable Name="i" InitialValue="0" />
- <Variable Name="n" InitialValue="items" />
- <Size>items</Size>
- <Loop>
- <Break Condition="n == 0" />
- <If Condition="(ctrl.pointer[i] & 0x80) == 0">
- <!-- Bucket is populated -->
- <Exec>n--</Exec>
- <Item>data.pointer[i]</Item>
+ <Item>static_cast<$T1*>(map.base.table.ctrl.pointer)[-(i + 1)]</Item>
</If>
<Exec>i++</Exec>
</Loop>
[dependencies]
rustc_serialize = { path = "../librustc_serialize" }
log = { package = "tracing", version = "0.1" }
-scoped-tls = "1.0"
rustc_span = { path = "../librustc_span" }
rustc_data_structures = { path = "../librustc_data_structures" }
rustc_index = { path = "../librustc_index" }
pub use UnsafeSource::*;
use crate::ptr::P;
-use crate::token::{self, DelimToken};
+use crate::token::{self, CommentKind, DelimToken};
use crate::tokenstream::{DelimSpan, TokenStream, TokenTree};
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
#[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
pub struct WhereClause {
/// `true` if we ate a `where` token: this can happen
- /// if we parsed no predicates (e.g. `struct Foo where {}
+ /// if we parsed no predicates (e.g. `struct Foo where {}`).
/// This allows us to accurately pretty-print
/// in `nt_to_tokenstream`
pub has_where_token: bool,
}
}
+ /// Is this expr either `N`, or `{ N }`.
+ ///
+ /// If this is not the case, name resolution does not resolve `N` when using
+ /// `feature(min_const_generics)` as more complex expressions are not supported.
+ pub fn is_potential_trivial_const_param(&self) -> bool {
+ let this = if let ExprKind::Block(ref block, None) = self.kind {
+ if block.stmts.len() == 1 {
+ if let StmtKind::Expr(ref expr) = block.stmts[0].kind { expr } else { self }
+ } else {
+ self
+ }
+ } else {
+ self
+ };
+
+ if let ExprKind::Path(None, ref path) = this.kind {
+ if path.segments.len() == 1 && path.segments[0].args.is_none() {
+ return true;
+ }
+ }
+
+ false
+ }
+
pub fn to_bound(&self) -> Option<GenericBound> {
match &self.kind {
ExprKind::Path(None, path) => Some(GenericBound::Trait(
/// A doc comment (e.g. `/// ...`, `//! ...`, `/** ... */`, `/*! ... */`).
/// Doc attributes (e.g. `#[doc="..."]`) are represented with the `Normal`
/// variant (which is much less compact and thus more expensive).
- DocComment(Symbol),
+ DocComment(CommentKind, Symbol),
}
/// `TraitRef`s appear in impls.
use crate::ast::{Path, PathSegment};
use crate::mut_visit::visit_clobber;
use crate::ptr::P;
-use crate::token::{self, Token};
+use crate::token::{self, CommentKind, Token};
use crate::tokenstream::{DelimSpan, TokenStream, TokenTree, TreeAndJoint};
-use rustc_data_structures::sync::Lock;
use rustc_index::bit_set::GrowableBitSet;
-use rustc_span::edition::{Edition, DEFAULT_EDITION};
use rustc_span::source_map::{BytePos, Spanned};
use rustc_span::symbol::{sym, Ident, Symbol};
use rustc_span::Span;
-use log::debug;
use std::iter;
use std::ops::DerefMut;
-// Per-session global variables: this struct is stored in thread-local storage
-// in such a way that it is accessible without any kind of handle to all
-// threads within the compilation session, but is not accessible outside the
-// session.
-pub struct SessionGlobals {
- used_attrs: Lock<GrowableBitSet<AttrId>>,
- known_attrs: Lock<GrowableBitSet<AttrId>>,
- span_session_globals: rustc_span::SessionGlobals,
-}
+pub struct MarkedAttrs(GrowableBitSet<AttrId>);
-impl SessionGlobals {
- fn new(edition: Edition) -> SessionGlobals {
- SessionGlobals {
- // We have no idea how many attributes there will be, so just
- // initiate the vectors with 0 bits. We'll grow them as necessary.
- used_attrs: Lock::new(GrowableBitSet::new_empty()),
- known_attrs: Lock::new(GrowableBitSet::new_empty()),
- span_session_globals: rustc_span::SessionGlobals::new(edition),
- }
+impl MarkedAttrs {
+ // We have no idea how many attributes there will be, so just
+ // initiate the vectors with 0 bits. We'll grow them as necessary.
+ pub fn new() -> Self {
+ MarkedAttrs(GrowableBitSet::new_empty())
}
-}
-
-pub fn with_session_globals<R>(edition: Edition, f: impl FnOnce() -> R) -> R {
- let ast_session_globals = SessionGlobals::new(edition);
- SESSION_GLOBALS.set(&ast_session_globals, || {
- rustc_span::SESSION_GLOBALS.set(&ast_session_globals.span_session_globals, f)
- })
-}
-
-pub fn with_default_session_globals<R>(f: impl FnOnce() -> R) -> R {
- with_session_globals(DEFAULT_EDITION, f)
-}
-scoped_tls::scoped_thread_local!(pub static SESSION_GLOBALS: SessionGlobals);
-
-pub fn mark_used(attr: &Attribute) {
- debug!("marking {:?} as used", attr);
- SESSION_GLOBALS.with(|session_globals| {
- session_globals.used_attrs.lock().insert(attr.id);
- });
-}
-
-pub fn is_used(attr: &Attribute) -> bool {
- SESSION_GLOBALS.with(|session_globals| session_globals.used_attrs.lock().contains(attr.id))
-}
-
-pub fn mark_known(attr: &Attribute) {
- debug!("marking {:?} as known", attr);
- SESSION_GLOBALS.with(|session_globals| {
- session_globals.known_attrs.lock().insert(attr.id);
- });
-}
+ pub fn mark(&mut self, attr: &Attribute) {
+ self.0.insert(attr.id);
+ }
-pub fn is_known(attr: &Attribute) -> bool {
- SESSION_GLOBALS.with(|session_globals| session_globals.known_attrs.lock().contains(attr.id))
+ pub fn is_marked(&self, attr: &Attribute) -> bool {
+ self.0.contains(attr.id)
+ }
}
pub fn is_known_lint_tool(m_item: Ident) -> bool {
pub fn has_name(&self, name: Symbol) -> bool {
match self.kind {
AttrKind::Normal(ref item) => item.path == name,
- AttrKind::DocComment(_) => false,
- }
- }
-
- /// Returns `true` if the attribute's path matches the argument.
- /// If it matches, then the attribute is marked as used.
- /// Should only be used by rustc, other tools can use `has_name` instead,
- /// because only rustc is supposed to report the `unused_attributes` lint.
- /// `MetaItem` and `NestedMetaItem` are produced by "lowering" an `Attribute`
- /// and don't have identity, so they only has the `has_name` method,
- /// and you need to mark the original `Attribute` as used when necessary.
- pub fn check_name(&self, name: Symbol) -> bool {
- let matches = self.has_name(name);
- if matches {
- mark_used(self);
+ AttrKind::DocComment(..) => false,
}
- matches
}
/// For a single-segment attribute, returns its name; otherwise, returns `None`.
None
}
}
- AttrKind::DocComment(_) => None,
+ AttrKind::DocComment(..) => None,
}
}
pub fn name_or_empty(&self) -> Symbol {
Some(MetaItem { kind: MetaItemKind::List(list), .. }) => Some(list),
_ => None,
},
- AttrKind::DocComment(_) => None,
+ AttrKind::DocComment(..) => None,
}
}
pub fn is_doc_comment(&self) -> bool {
match self.kind {
AttrKind::Normal(_) => false,
- AttrKind::DocComment(_) => true,
+ AttrKind::DocComment(..) => true,
}
}
pub fn doc_str(&self) -> Option<Symbol> {
match self.kind {
- AttrKind::DocComment(symbol) => Some(symbol),
+ AttrKind::DocComment(.., data) => Some(data),
AttrKind::Normal(ref item) if item.path == sym::doc => {
item.meta(self.span).and_then(|meta| meta.value_str())
}
pub fn get_normal_item(&self) -> &AttrItem {
match self.kind {
AttrKind::Normal(ref item) => item,
- AttrKind::DocComment(_) => panic!("unexpected doc comment"),
+ AttrKind::DocComment(..) => panic!("unexpected doc comment"),
}
}
pub fn unwrap_normal_item(self) -> AttrItem {
match self.kind {
AttrKind::Normal(item) => item,
- AttrKind::DocComment(_) => panic!("unexpected doc comment"),
+ AttrKind::DocComment(..) => panic!("unexpected doc comment"),
}
}
mk_attr(AttrStyle::Outer, item.path, item.kind.mac_args(item.span), item.span)
}
-pub fn mk_doc_comment(style: AttrStyle, comment: Symbol, span: Span) -> Attribute {
- Attribute { kind: AttrKind::DocComment(comment), id: mk_attr_id(), style, span }
+pub fn mk_doc_comment(
+ comment_kind: CommentKind,
+ style: AttrStyle,
+ data: Symbol,
+ span: Span,
+) -> Attribute {
+ Attribute { kind: AttrKind::DocComment(comment_kind, data), id: mk_attr_id(), style, span }
}
pub fn list_contains_name(items: &[NestedMetaItem], name: Symbol) -> bool {
items.iter().any(|item| item.has_name(name))
}
-pub fn contains_name(attrs: &[Attribute], name: Symbol) -> bool {
- attrs.iter().any(|item| item.check_name(name))
-}
-
-pub fn find_by_name(attrs: &[Attribute], name: Symbol) -> Option<&Attribute> {
- attrs.iter().find(|attr| attr.check_name(name))
-}
-
-pub fn filter_by_name(attrs: &[Attribute], name: Symbol) -> impl Iterator<Item = &Attribute> {
- attrs.iter().filter(move |attr| attr.check_name(name))
-}
-
-pub fn first_attr_value_str_by_name(attrs: &[Attribute], name: Symbol) -> Option<Symbol> {
- attrs.iter().find(|at| at.check_name(name)).and_then(|at| at.value_str())
-}
-
impl MetaItem {
fn token_trees_and_joints(&self) -> Vec<TreeAndJoint> {
let mut idents = vec![];
-use crate::ast::{Item, ItemKind};
-use crate::attr;
-use rustc_span::symbol::sym;
-
pub enum EntryPointType {
None,
MainNamed,
Start,
OtherMain, // Not an entry point, but some other function named main
}
-
-// Beware, this is duplicated in librustc_middle/middle/entry.rs, make sure to keep
-// them in sync.
-pub fn entry_point_type(item: &Item, depth: usize) -> EntryPointType {
- match item.kind {
- ItemKind::Fn(..) => {
- if attr::contains_name(&item.attrs, sym::start) {
- EntryPointType::Start
- } else if attr::contains_name(&item.attrs, sym::main) {
- EntryPointType::MainAttr
- } else if item.ident.name == sym::main {
- if depth == 1 {
- // This is a top-level function so can be 'main'
- EntryPointType::MainNamed
- } else {
- EntryPointType::OtherMain
- }
- } else {
- EntryPointType::None
- }
- }
- _ => EntryPointType::None,
- }
-}
-use crate::{ast, attr, visit};
use rustc_span::symbol::{sym, Symbol};
-use rustc_span::Span;
#[derive(Clone, Copy)]
pub enum AllocatorKind {
output: AllocatorTy::ResultPtr,
},
];
-
-pub fn global_allocator_spans(krate: &ast::Crate) -> Vec<Span> {
- struct Finder {
- name: Symbol,
- spans: Vec<Span>,
- }
- impl<'ast> visit::Visitor<'ast> for Finder {
- fn visit_item(&mut self, item: &'ast ast::Item) {
- if item.ident.name == self.name
- && attr::contains_name(&item.attrs, sym::rustc_std_internal_symbol)
- {
- self.spans.push(item.span);
- }
- visit::walk_item(self, item)
- }
- }
-
- let name = Symbol::intern(&AllocatorKind::Global.fn_name(sym::alloc));
- let mut f = Finder { name, spans: Vec::new() };
- visit::walk_crate(&mut f, krate);
- f.spans
-}
//! Definitions shared by macros / syntax extensions and e.g. librustc_middle.
-use crate::ast::Attribute;
-use rustc_span::symbol::sym;
-
pub mod allocator;
-
-pub fn is_proc_macro_attr(attr: &Attribute) -> bool {
- [sym::proc_macro, sym::proc_macro_attribute, sym::proc_macro_derive]
- .iter()
- .any(|kind| attr.check_name(*kind))
-}
pub mod ast;
pub mod attr;
-pub use attr::{with_default_session_globals, with_session_globals, SESSION_GLOBALS};
pub mod crate_disambiguator;
pub mod entry;
pub mod expand;
vis.visit_path(path);
visit_mac_args(args, vis);
}
- AttrKind::DocComment(_) => {}
+ AttrKind::DocComment(..) => {}
}
vis.visit_span(span);
}
use std::borrow::Cow;
use std::{fmt, mem};
+#[derive(Clone, Copy, PartialEq, RustcEncodable, RustcDecodable, Debug, HashStable_Generic)]
+pub enum CommentKind {
+ Line,
+ Block,
+}
+
#[derive(Clone, PartialEq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)]
#[derive(HashStable_Generic)]
pub enum BinOpToken {
Interpolated(Lrc<Nonterminal>),
- // Can be expanded into several tokens.
- /// A doc comment.
- DocComment(Symbol),
+ /// A doc comment token.
+ /// `Symbol` is the doc comment's data excluding its "quotes" (`///`, `/**`, etc)
+ /// similarly to symbols in string literal tokens.
+ DocComment(CommentKind, ast::AttrStyle, Symbol),
// Junk. These carry no data because we don't really care about the data
// they *would* carry, and don't really want to allocate a new ident for
-pub use CommentStyle::*;
-
-use crate::ast;
+use crate::ast::AttrStyle;
use rustc_span::source_map::SourceMap;
use rustc_span::{BytePos, CharPos, FileName, Pos, Symbol};
-use log::debug;
-
#[cfg(test)]
mod tests;
pub pos: BytePos,
}
-pub fn is_line_doc_comment(s: &str) -> bool {
- let res = (s.starts_with("///") && *s.as_bytes().get(3).unwrap_or(&b' ') != b'/')
- || s.starts_with("//!");
- debug!("is {:?} a doc comment? {}", s, res);
- res
-}
-
-pub fn is_block_doc_comment(s: &str) -> bool {
- // Prevent `/**/` from being parsed as a doc comment
- let res = ((s.starts_with("/**") && *s.as_bytes().get(3).unwrap_or(&b' ') != b'*')
- || s.starts_with("/*!"))
- && s.len() >= 5;
- debug!("is {:?} a doc comment? {}", s, res);
- res
-}
-
-// FIXME(#64197): Try to privatize this again.
-pub fn is_doc_comment(s: &str) -> bool {
- (s.starts_with("///") && is_line_doc_comment(s))
- || s.starts_with("//!")
- || (s.starts_with("/**") && is_block_doc_comment(s))
- || s.starts_with("/*!")
+/// For a full line comment string returns its doc comment style if it's a doc comment
+/// and returns `None` if it's a regular comment.
+pub fn line_doc_comment_style(line_comment: &str) -> Option<AttrStyle> {
+ let line_comment = line_comment.as_bytes();
+ assert!(line_comment.starts_with(b"//"));
+ match line_comment.get(2) {
+ // `//!` is an inner line doc comment.
+ Some(b'!') => Some(AttrStyle::Inner),
+ Some(b'/') => match line_comment.get(3) {
+ // `////` (more than 3 slashes) is not considered a doc comment.
+ Some(b'/') => None,
+ // Otherwise `///` is an outer line doc comment.
+ _ => Some(AttrStyle::Outer),
+ },
+ _ => None,
+ }
}
-pub fn doc_comment_style(comment: Symbol) -> ast::AttrStyle {
- let comment = &comment.as_str();
- assert!(is_doc_comment(comment));
- if comment.starts_with("//!") || comment.starts_with("/*!") {
- ast::AttrStyle::Inner
- } else {
- ast::AttrStyle::Outer
+/// For a full block comment string returns its doc comment style if it's a doc comment
+/// and returns `None` if it's a regular comment.
+pub fn block_doc_comment_style(block_comment: &str, terminated: bool) -> Option<AttrStyle> {
+ let block_comment = block_comment.as_bytes();
+ assert!(block_comment.starts_with(b"/*"));
+ assert!(!terminated || block_comment.ends_with(b"*/"));
+ match block_comment.get(2) {
+ // `/*!` is an inner block doc comment.
+ Some(b'!') => Some(AttrStyle::Inner),
+ Some(b'*') => match block_comment.get(3) {
+ // `/***` (more than 2 stars) is not considered a doc comment.
+ Some(b'*') => None,
+ // `/**/` is not considered a doc comment.
+ Some(b'/') if block_comment.len() == 4 => None,
+ // Otherwise `/**` is an outer block doc comment.
+ _ => Some(AttrStyle::Outer),
+ },
+ _ => None,
}
}
-pub fn strip_doc_comment_decoration(comment: Symbol) -> String {
- let comment = &comment.as_str();
-
+/// Makes a doc string more presentable to users.
+/// Used by rustdoc and perhaps other tools, but not by rustc.
+pub fn beautify_doc_string(data: Symbol) -> String {
/// remove whitespace-only lines from the start/end of lines
fn vertical_trim(lines: Vec<String>) -> Vec<String> {
let mut i = 0;
}
}
- // one-line comments lose their prefix
- const ONELINERS: &[&str] = &["///!", "///", "//!", "//"];
-
- for prefix in ONELINERS {
- if comment.starts_with(*prefix) {
- return (&comment[prefix.len()..]).to_string();
- }
- }
-
- if comment.starts_with("/*") {
- let lines =
- comment[3..comment.len() - 2].lines().map(|s| s.to_string()).collect::<Vec<String>>();
-
+ let data = data.as_str();
+ if data.contains('\n') {
+ let lines = data.lines().map(|s| s.to_string()).collect::<Vec<String>>();
let lines = vertical_trim(lines);
let lines = horizontal_trim(lines);
-
- return lines.join("\n");
+ lines.join("\n")
+ } else {
+ data.to_string()
}
-
- panic!("not a doc-comment: {}", comment);
}
/// Returns `None` if the first `col` chars of `s` contain a non-whitespace char.
if let Some(shebang_len) = rustc_lexer::strip_shebang(text) {
comments.push(Comment {
- style: Isolated,
+ style: CommentStyle::Isolated,
lines: vec![text[..shebang_len].to_string()],
pos: start_bpos,
});
while let Some(next_newline) = &token_text[idx + 1..].find('\n') {
idx = idx + 1 + next_newline;
comments.push(Comment {
- style: BlankLine,
+ style: CommentStyle::BlankLine,
lines: vec![],
pos: start_bpos + BytePos((pos + idx) as u32),
});
}
}
}
- rustc_lexer::TokenKind::BlockComment { terminated: _ } => {
- if !is_block_doc_comment(token_text) {
+ rustc_lexer::TokenKind::BlockComment { terminated } => {
+ if block_doc_comment_style(token_text, terminated).is_none() {
let code_to_the_right = match text[pos + token.len..].chars().next() {
Some('\r' | '\n') => false,
_ => true,
};
let style = match (code_to_the_left, code_to_the_right) {
- (_, true) => Mixed,
- (false, false) => Isolated,
- (true, false) => Trailing,
+ (_, true) => CommentStyle::Mixed,
+ (false, false) => CommentStyle::Isolated,
+ (true, false) => CommentStyle::Trailing,
};
// Count the number of chars since the start of the line by rescanning.
}
}
rustc_lexer::TokenKind::LineComment => {
- if !is_doc_comment(token_text) {
+ if line_doc_comment_style(token_text).is_none() {
comments.push(Comment {
- style: if code_to_the_left { Trailing } else { Isolated },
+ style: if code_to_the_left {
+ CommentStyle::Trailing
+ } else {
+ CommentStyle::Isolated
+ },
lines: vec![token_text.to_string()],
pos: start_bpos + BytePos(pos as u32),
})
use super::*;
-use crate::with_default_session_globals;
+use rustc_span::with_default_session_globals;
+
+#[test]
+fn line_doc_comments() {
+ assert!(line_doc_comment_style("///").is_some());
+ assert!(line_doc_comment_style("/// blah").is_some());
+ assert!(line_doc_comment_style("////").is_none());
+}
#[test]
fn test_block_doc_comment_1() {
with_default_session_globals(|| {
- let comment = "/**\n * Test \n ** Test\n * Test\n*/";
- let stripped = strip_doc_comment_decoration(Symbol::intern(comment));
+ let comment = "\n * Test \n ** Test\n * Test\n";
+ let stripped = beautify_doc_string(Symbol::intern(comment));
assert_eq!(stripped, " Test \n* Test\n Test");
})
}
#[test]
fn test_block_doc_comment_2() {
with_default_session_globals(|| {
- let comment = "/**\n * Test\n * Test\n*/";
- let stripped = strip_doc_comment_decoration(Symbol::intern(comment));
+ let comment = "\n * Test\n * Test\n";
+ let stripped = beautify_doc_string(Symbol::intern(comment));
assert_eq!(stripped, " Test\n Test");
})
}
#[test]
fn test_block_doc_comment_3() {
with_default_session_globals(|| {
- let comment = "/**\n let a: *i32;\n *a = 5;\n*/";
- let stripped = strip_doc_comment_decoration(Symbol::intern(comment));
+ let comment = "\n let a: *i32;\n *a = 5;\n";
+ let stripped = beautify_doc_string(Symbol::intern(comment));
assert_eq!(stripped, " let a: *i32;\n *a = 5;");
})
}
-#[test]
-fn test_block_doc_comment_4() {
- with_default_session_globals(|| {
- let comment = "/*******************\n test\n *********************/";
- let stripped = strip_doc_comment_decoration(Symbol::intern(comment));
- assert_eq!(stripped, " test");
- })
-}
-
#[test]
fn test_line_doc_comment() {
with_default_session_globals(|| {
- let stripped = strip_doc_comment_decoration(Symbol::intern("/// test"));
- assert_eq!(stripped, " test");
- let stripped = strip_doc_comment_decoration(Symbol::intern("///! test"));
- assert_eq!(stripped, " test");
- let stripped = strip_doc_comment_decoration(Symbol::intern("// test"));
+ let stripped = beautify_doc_string(Symbol::intern(" test"));
assert_eq!(stripped, " test");
- let stripped = strip_doc_comment_decoration(Symbol::intern("// test"));
- assert_eq!(stripped, " test");
- let stripped = strip_doc_comment_decoration(Symbol::intern("///test"));
- assert_eq!(stripped, "test");
- let stripped = strip_doc_comment_decoration(Symbol::intern("///!test"));
- assert_eq!(stripped, "test");
- let stripped = strip_doc_comment_decoration(Symbol::intern("//test"));
+ let stripped = beautify_doc_string(Symbol::intern("! test"));
+ assert_eq!(stripped, "! test");
+ let stripped = beautify_doc_string(Symbol::intern("test"));
assert_eq!(stripped, "test");
+ let stripped = beautify_doc_string(Symbol::intern("!test"));
+ assert_eq!(stripped, "!test");
})
}
#[test]
fn test_find_best_match_for_name() {
- use crate::with_default_session_globals;
+ use rustc_span::with_default_session_globals;
with_default_session_globals(|| {
let input = vec![Symbol::intern("aaab"), Symbol::intern("aaabc")];
assert_eq!(
pub fn walk_attribute<'a, V: Visitor<'a>>(visitor: &mut V, attr: &'a Attribute) {
match attr.kind {
AttrKind::Normal(ref item) => walk_mac_args(visitor, &item.args),
- AttrKind::DocComment(_) => {}
+ AttrKind::DocComment(..) => {}
}
}
.collect();
// Stop if there were any errors when lowering the register classes
- if operands.len() != asm.operands.len() {
+ if operands.len() != asm.operands.len() || sess.asm_arch.is_none() {
return hir::ExprKind::Err;
}
use crate::Arena;
use rustc_ast::ast::*;
-use rustc_ast::attr;
use rustc_ast::node_id::NodeMap;
use rustc_ast::ptr::P;
use rustc_ast::visit::{self, AssocCtxt, Visitor};
let attrs = self.lower_attrs(&i.attrs);
if let ItemKind::MacroDef(MacroDef { ref body, macro_rules }) = i.kind {
- if !macro_rules || attr::contains_name(&i.attrs, sym::macro_export) {
+ if !macro_rules || self.sess.contains_name(&i.attrs, sym::macro_export) {
let hir_id = self.lower_node_id(i.id);
let body = P(self.lower_mac_args(body));
self.exported_macros.push(hir::MacroDef {
use rustc_ast::ast;
use rustc_ast::ast::*;
-use rustc_ast::attr;
use rustc_ast::node_id::NodeMap;
use rustc_ast::token::{self, DelimToken, Nonterminal, Token};
use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree};
path: item.path.clone(),
args: self.lower_mac_args(&item.args),
}),
- AttrKind::DocComment(comment) => AttrKind::DocComment(comment),
+ AttrKind::DocComment(comment_kind, data) => AttrKind::DocComment(comment_kind, data),
};
Attribute { kind, id: attr.id, style: attr.style, span: attr.span }
synthetic: param
.attrs
.iter()
- .filter(|attr| attr.check_name(sym::rustc_synthetic))
+ .filter(|attr| self.sess.check_name(attr, sym::rustc_synthetic))
.map(|_| hir::SyntheticTyParamKind::ImplTrait)
.next(),
};
hir_id: self.lower_node_id(param.id),
name,
span: param.ident.span,
- pure_wrt_drop: attr::contains_name(¶m.attrs, sym::may_dangle),
+ pure_wrt_drop: self.sess.contains_name(¶m.attrs, sym::may_dangle),
attrs: self.lower_attrs(¶m.attrs),
bounds: self.arena.alloc_from_iter(bounds),
kind,
use itertools::{Either, Itertools};
use rustc_ast::ast::*;
-use rustc_ast::attr;
-use rustc_ast::expand::is_proc_macro_attr;
use rustc_ast::ptr::P;
use rustc_ast::visit::{self, AssocCtxt, FnCtxt, FnKind, Visitor};
use rustc_ast::walk_list;
span,
&format!(
"reorder the parameters: lifetimes, then types{}",
- if sess.features_untracked().const_generics { ", then consts" } else { "" },
+ if sess.features_untracked().const_generics
+ || sess.features_untracked().min_const_generics
+ {
+ ", then consts"
+ } else {
+ ""
+ },
),
ordered_params.clone(),
Applicability::MachineApplicable,
}
fn visit_item(&mut self, item: &'a Item) {
- if item.attrs.iter().any(|attr| is_proc_macro_attr(attr)) {
+ if item.attrs.iter().any(|attr| self.session.is_proc_macro_attr(attr)) {
self.has_proc_macro_decls = true;
}
- if attr::contains_name(&item.attrs, sym::no_mangle) {
+ if self.session.contains_name(&item.attrs, sym::no_mangle) {
self.check_nomangle_item_asciionly(item.ident, item.span);
}
}
ItemKind::Mod(Mod { inline, .. }) => {
// Ensure that `path` attributes on modules are recorded as used (cf. issue #35584).
- if !inline && !attr::contains_name(&item.attrs, sym::path) {
+ if !inline && !self.session.contains_name(&item.attrs, sym::path) {
self.check_mod_file_item_asciionly(item.ident);
}
}
use rustc_ast::ast::{self, AssocTyConstraint, AssocTyConstraintKind, NodeId};
use rustc_ast::ast::{GenericParam, GenericParamKind, PatKind, RangeEnd, VariantData};
-use rustc_ast::attr;
use rustc_ast::visit::{self, AssocCtxt, FnCtxt, FnKind, Visitor};
-use rustc_errors::{struct_span_err, Handler};
+use rustc_errors::struct_span_err;
use rustc_feature::{AttributeGate, BUILTIN_ATTRIBUTE_MAP};
-use rustc_feature::{Features, GateIssue, UnstableFeatures};
-use rustc_session::parse::{feature_err, feature_err_issue, ParseSess};
+use rustc_feature::{Features, GateIssue};
+use rustc_session::parse::{feature_err, feature_err_issue};
+use rustc_session::Session;
use rustc_span::source_map::Spanned;
use rustc_span::symbol::{sym, Symbol};
use rustc_span::Span;
use tracing::debug;
macro_rules! gate_feature_fn {
- ($cx: expr, $has_feature: expr, $span: expr, $name: expr, $explain: expr) => {{
- let (cx, has_feature, span, name, explain) = (&*$cx, $has_feature, $span, $name, $explain);
- let has_feature: bool = has_feature(&$cx.features);
+ ($visitor: expr, $has_feature: expr, $span: expr, $name: expr, $explain: expr) => {{
+ let (visitor, has_feature, span, name, explain) =
+ (&*$visitor, $has_feature, $span, $name, $explain);
+ let has_feature: bool = has_feature(visitor.features);
debug!("gate_feature(feature = {:?}, span = {:?}); has? {}", name, span, has_feature);
if !has_feature && !span.allows_unstable($name) {
- feature_err_issue(cx.parse_sess, name, span, GateIssue::Language, explain).emit();
+ feature_err_issue(&visitor.sess.parse_sess, name, span, GateIssue::Language, explain)
+ .emit();
}
}};
}
macro_rules! gate_feature_post {
- ($cx: expr, $feature: ident, $span: expr, $explain: expr) => {
- gate_feature_fn!($cx, |x: &Features| x.$feature, $span, sym::$feature, $explain)
+ ($visitor: expr, $feature: ident, $span: expr, $explain: expr) => {
+ gate_feature_fn!($visitor, |x: &Features| x.$feature, $span, sym::$feature, $explain)
};
}
-pub fn check_attribute(attr: &ast::Attribute, parse_sess: &ParseSess, features: &Features) {
- PostExpansionVisitor { parse_sess, features }.visit_attribute(attr)
+pub fn check_attribute(attr: &ast::Attribute, sess: &Session, features: &Features) {
+ PostExpansionVisitor { sess, features }.visit_attribute(attr)
}
struct PostExpansionVisitor<'a> {
- parse_sess: &'a ParseSess,
+ sess: &'a Session,
+
+ // `sess` contains a `Features`, but this might not be that one.
features: &'a Features,
}
);
}
abi => self
+ .sess
.parse_sess
.span_diagnostic
.delay_span_bug(span, &format!("unrecognized ABI not caught in lowering: {}", abi)),
if !discriminant_spans.is_empty() && has_fields {
let mut err = feature_err(
- self.parse_sess,
+ &self.sess.parse_sess,
sym::arbitrary_enum_discriminant,
discriminant_spans.clone(),
"custom discriminant values are not allowed in enums with tuple or struct variants",
gate_feature_fn!(self, has_feature, attr.span, name, descr);
}
// Check unstable flavors of the `#[doc]` attribute.
- if attr.check_name(sym::doc) {
+ if self.sess.check_name(attr, sym::doc) {
for nested_meta in attr.meta_item_list().unwrap_or_default() {
macro_rules! gate_doc { ($($name:ident => $feature:ident)*) => {
$(if nested_meta.has_name(sym::$name) {
gate_feature_post!(
&self,
non_ascii_idents,
- self.parse_sess.source_map().guess_head_span(sp),
+ self.sess.parse_sess.source_map().guess_head_span(sp),
"non-ascii idents are not fully supported"
);
}
}
ast::ItemKind::Fn(..) => {
- if attr::contains_name(&i.attrs[..], sym::plugin_registrar) {
+ if self.sess.contains_name(&i.attrs[..], sym::plugin_registrar) {
gate_feature_post!(
&self,
plugin_registrar,
"compiler plugins are experimental and possibly buggy"
);
}
- if attr::contains_name(&i.attrs[..], sym::start) {
+ if self.sess.contains_name(&i.attrs[..], sym::start) {
gate_feature_post!(
&self,
start,
over time"
);
}
- if attr::contains_name(&i.attrs[..], sym::main) {
+ if self.sess.contains_name(&i.attrs[..], sym::main) {
gate_feature_post!(
&self,
main,
}
ast::ItemKind::Struct(..) => {
- for attr in attr::filter_by_name(&i.attrs[..], sym::repr) {
+ for attr in self.sess.filter_by_name(&i.attrs[..], sym::repr) {
for item in attr.meta_item_list().unwrap_or_else(Vec::new) {
if item.has_name(sym::simd) {
gate_feature_post!(
fn visit_foreign_item(&mut self, i: &'a ast::ForeignItem) {
match i.kind {
ast::ForeignItemKind::Fn(..) | ast::ForeignItemKind::Static(..) => {
- let link_name = attr::first_attr_value_str_by_name(&i.attrs, sym::link_name);
+ let link_name = self.sess.first_attr_value_str_by_name(&i.attrs, sym::link_name);
let links_to_llvm = match link_name {
Some(val) => val.as_str().starts_with("llvm."),
_ => false,
ast::ExprKind::Type(..) => {
// To avoid noise about type ascription in common syntax errors, only emit if it
// is the *only* error.
- if self.parse_sess.span_diagnostic.err_count() == 0 {
+ if self.sess.parse_sess.span_diagnostic.err_count() == 0 {
gate_feature_post!(
&self,
type_ascription,
fn visit_generic_param(&mut self, param: &'a GenericParam) {
if let GenericParamKind::Const { .. } = param.kind {
- gate_feature_post!(
+ gate_feature_fn!(
&self,
- const_generics,
+ |x: &Features| x.const_generics || x.min_const_generics,
param.ident.span,
+ sym::min_const_generics,
"const generics are unstable"
- )
+ );
}
visit::walk_generic_param(self, param)
}
}
}
-pub fn check_crate(
- krate: &ast::Crate,
- parse_sess: &ParseSess,
- features: &Features,
- unstable: UnstableFeatures,
-) {
- maybe_stage_features(&parse_sess.span_diagnostic, krate, unstable);
- let mut visitor = PostExpansionVisitor { parse_sess, features };
+pub fn check_crate(krate: &ast::Crate, sess: &Session) {
+ maybe_stage_features(sess, krate);
+ let mut visitor = PostExpansionVisitor { sess, features: &sess.features_untracked() };
- let spans = parse_sess.gated_spans.spans.borrow();
+ let spans = sess.parse_sess.gated_spans.spans.borrow();
macro_rules! gate_all {
($gate:ident, $msg:literal) => {
for span in spans.get(&sym::$gate).unwrap_or(&vec![]) {
gate_all!(box_syntax, "box expression syntax is experimental; you can call `Box::new` instead");
// To avoid noise about type ascription in common syntax errors,
// only emit if it is the *only* error. (Also check it last.)
- if parse_sess.span_diagnostic.err_count() == 0 {
+ if sess.parse_sess.span_diagnostic.err_count() == 0 {
gate_all!(type_ascription, "type ascription is experimental");
}
visit::walk_crate(&mut visitor, krate);
}
-fn maybe_stage_features(span_handler: &Handler, krate: &ast::Crate, unstable: UnstableFeatures) {
- if !unstable.is_nightly_build() {
- for attr in krate.attrs.iter().filter(|attr| attr.check_name(sym::feature)) {
+fn maybe_stage_features(sess: &Session, krate: &ast::Crate) {
+ if !sess.opts.unstable_features.is_nightly_build() {
+ for attr in krate.attrs.iter().filter(|attr| sess.check_name(attr, sym::feature)) {
struct_span_err!(
- span_handler,
+ sess.parse_sess.span_diagnostic,
attr.span,
E0554,
"`#![feature]` may not be used on the {} release channel",
use rustc_ast::ast::{InlineAsmOptions, InlineAsmTemplatePiece};
use rustc_ast::attr;
use rustc_ast::ptr::P;
-use rustc_ast::token::{self, BinOpToken, DelimToken, Nonterminal, Token, TokenKind};
+use rustc_ast::token::{self, BinOpToken, CommentKind, DelimToken, Nonterminal, Token, TokenKind};
use rustc_ast::tokenstream::{TokenStream, TokenTree};
+use rustc_ast::util::classify;
+use rustc_ast::util::comments::{gather_comments, Comment, CommentStyle};
use rustc_ast::util::parser::{self, AssocOp, Fixity};
-use rustc_ast::util::{classify, comments};
use rustc_span::edition::Edition;
use rustc_span::source_map::{SourceMap, Spanned};
use rustc_span::symbol::{kw, sym, Ident, IdentPrinter, Symbol};
pub struct Comments<'a> {
sm: &'a SourceMap,
- comments: Vec<comments::Comment>,
+ comments: Vec<Comment>,
current: usize,
}
impl<'a> Comments<'a> {
pub fn new(sm: &'a SourceMap, filename: FileName, input: String) -> Comments<'a> {
- let comments = comments::gather_comments(sm, filename, input);
+ let comments = gather_comments(sm, filename, input);
Comments { sm, comments, current: 0 }
}
- pub fn next(&self) -> Option<comments::Comment> {
+ pub fn next(&self) -> Option<Comment> {
self.comments.get(self.current).cloned()
}
&mut self,
span: rustc_span::Span,
next_pos: Option<BytePos>,
- ) -> Option<comments::Comment> {
+ ) -> Option<Comment> {
if let Some(cmnt) = self.next() {
- if cmnt.style != comments::Trailing {
+ if cmnt.style != CommentStyle::Trailing {
return None;
}
let span_line = self.sm.lookup_char_pos(span.hi());
// and also addresses some specific regressions described in #63896 and #73345.
fn tt_prepend_space(tt: &TokenTree, prev: &TokenTree) -> bool {
if let TokenTree::Token(token) = prev {
- if let token::DocComment(s) = token.kind {
- return !s.as_str().starts_with("//");
+ if let token::DocComment(comment_kind, ..) = token.kind {
+ return comment_kind != CommentKind::Line;
}
}
match tt {
}
}
+fn doc_comment_to_string(
+ comment_kind: CommentKind,
+ attr_style: ast::AttrStyle,
+ data: Symbol,
+) -> String {
+ match (comment_kind, attr_style) {
+ (CommentKind::Line, ast::AttrStyle::Outer) => format!("///{}", data),
+ (CommentKind::Line, ast::AttrStyle::Inner) => format!("//!{}", data),
+ (CommentKind::Block, ast::AttrStyle::Outer) => format!("/**{}*/", data),
+ (CommentKind::Block, ast::AttrStyle::Inner) => format!("/*!{}*/", data),
+ }
+}
+
pub fn literal_to_string(lit: token::Lit) -> String {
let token::Lit { kind, symbol, suffix } = lit;
let mut out = match kind {
token::Lifetime(s) => s.to_string(),
/* Other */
- token::DocComment(s) => s.to_string(),
+ token::DocComment(comment_kind, attr_style, data) => {
+ doc_comment_to_string(comment_kind, attr_style, data)
+ }
token::Eof => "<eof>".to_string(),
token::Whitespace => " ".to_string(),
token::Comment => "/* */".to_string(),
}
}
- fn print_comment(&mut self, cmnt: &comments::Comment) {
+ fn print_comment(&mut self, cmnt: &Comment) {
match cmnt.style {
- comments::Mixed => {
+ CommentStyle::Mixed => {
if !self.is_beginning_of_line() {
self.zerobreak();
}
}
self.zerobreak()
}
- comments::Isolated => {
+ CommentStyle::Isolated => {
self.hardbreak_if_not_bol();
for line in &cmnt.lines {
// Don't print empty lines because they will end up as trailing
self.hardbreak();
}
}
- comments::Trailing => {
+ CommentStyle::Trailing => {
if !self.is_beginning_of_line() {
self.word(" ");
}
self.end();
}
}
- comments::BlankLine => {
+ CommentStyle::BlankLine => {
// We need to do at least one, possibly two hardbreaks.
let twice = match self.last_token() {
pp::Token::String(s) => ";" == s,
}
}
- fn next_comment(&mut self) -> Option<comments::Comment> {
+ fn next_comment(&mut self) -> Option<Comment> {
self.comments().as_mut().and_then(|c| c.next())
}
self.print_attr_item(&item, attr.span);
self.word("]");
}
- ast::AttrKind::DocComment(comment) => {
- self.word(comment.to_string());
+ ast::AttrKind::DocComment(comment_kind, data) => {
+ self.word(doc_comment_to_string(comment_kind, attr.style, data));
self.hardbreak()
}
}
use super::*;
use rustc_ast::ast;
-use rustc_ast::with_default_session_globals;
use rustc_span::source_map::respan;
use rustc_span::symbol::Ident;
+use rustc_span::with_default_session_globals;
fn fun_to_string(
decl: &ast::FnDecl,
//! Parsing and validation of builtin attributes
-use super::{find_by_name, mark_used};
-
use rustc_ast::ast::{self, Attribute, Lit, LitKind, MetaItem, MetaItemKind, NestedMetaItem};
use rustc_ast_pretty::pprust;
-use rustc_errors::{struct_span_err, Applicability, Handler};
+use rustc_errors::{struct_span_err, Applicability};
use rustc_feature::{find_gated_cfg, is_builtin_attr_name, Features, GatedCfg};
use rustc_macros::HashStable_Generic;
use rustc_session::parse::{feature_err, ParseSess};
+use rustc_session::Session;
use rustc_span::hygiene::Transparency;
use rustc_span::{symbol::sym, symbol::Symbol, Span};
use std::num::NonZeroU32;
}
/// Determine what `#[unwind]` attribute is present in `attrs`, if any.
-pub fn find_unwind_attr(diagnostic: Option<&Handler>, attrs: &[Attribute]) -> Option<UnwindAttr> {
+pub fn find_unwind_attr(sess: &Session, attrs: &[Attribute]) -> Option<UnwindAttr> {
attrs.iter().fold(None, |ia, attr| {
- if attr.check_name(sym::unwind) {
+ if sess.check_name(attr, sym::unwind) {
if let Some(meta) = attr.meta() {
if let MetaItemKind::List(items) = meta.kind {
if items.len() == 1 {
}
}
- if let Some(d) = diagnostic {
- struct_span_err!(d, attr.span, E0633, "malformed `unwind` attribute input")
- .span_label(attr.span, "invalid argument")
- .span_suggestions(
- attr.span,
- "the allowed arguments are `allowed` and `aborts`",
- (vec!["allowed", "aborts"])
- .into_iter()
- .map(|s| format!("#[unwind({})]", s)),
- Applicability::MachineApplicable,
- )
- .emit();
- };
+ struct_span_err!(
+ sess.diagnostic(),
+ attr.span,
+ E0633,
+ "malformed `unwind` attribute input"
+ )
+ .span_label(attr.span, "invalid argument")
+ .span_suggestions(
+ attr.span,
+ "the allowed arguments are `allowed` and `aborts`",
+ (vec!["allowed", "aborts"])
+ .into_iter()
+ .map(|s| format!("#[unwind({})]", s)),
+ Applicability::MachineApplicable,
+ )
+ .emit();
}
}
}
}
}
-/// Checks if `attrs` contains an attribute like `#![feature(feature_name)]`.
-/// This will not perform any "sanity checks" on the form of the attributes.
-pub fn contains_feature_attr(attrs: &[Attribute], feature_name: Symbol) -> bool {
- attrs.iter().any(|item| {
- item.check_name(sym::feature)
- && item
- .meta_item_list()
- .map(|list| list.iter().any(|mi| mi.is_word() && mi.has_name(feature_name)))
- .unwrap_or(false)
- })
-}
-
/// Collects stability info from all stability attributes in `attrs`.
/// Returns `None` if no stability attributes are found.
pub fn find_stability(
- sess: &ParseSess,
+ sess: &Session,
attrs: &[Attribute],
item_sp: Span,
) -> (Option<Stability>, Option<ConstStability>) {
}
fn find_stability_generic<'a, I>(
- sess: &ParseSess,
+ sess: &Session,
attrs_iter: I,
item_sp: Span,
) -> (Option<Stability>, Option<ConstStability>)
let mut const_stab: Option<ConstStability> = None;
let mut promotable = false;
let mut allow_const_fn_ptr = false;
- let diagnostic = &sess.span_diagnostic;
+ let diagnostic = &sess.parse_sess.span_diagnostic;
'outer: for attr in attrs_iter {
if ![
continue; // not a stability level
}
- mark_used(attr);
+ sess.mark_attr_used(attr);
let meta = attr.meta();
let get = |meta: &MetaItem, item: &mut Option<Symbol>| {
if item.is_some() {
handle_errors(
- sess,
+ &sess.parse_sess,
meta.span,
AttrError::MultipleItem(pprust::path_to_string(&meta.path)),
);
match meta_name {
sym::rustc_const_unstable | sym::unstable => {
if meta_name == sym::unstable && stab.is_some() {
- handle_errors(sess, attr.span, AttrError::MultipleStabilityLevels);
+ handle_errors(
+ &sess.parse_sess,
+ attr.span,
+ AttrError::MultipleStabilityLevels,
+ );
break;
} else if meta_name == sym::rustc_const_unstable && const_stab.is_some() {
- handle_errors(sess, attr.span, AttrError::MultipleStabilityLevels);
+ handle_errors(
+ &sess.parse_sess,
+ attr.span,
+ AttrError::MultipleStabilityLevels,
+ );
break;
}
sym::soft => {
if !mi.is_word() {
let msg = "`soft` should not have any arguments";
- sess.span_diagnostic.span_err(mi.span, msg);
+ sess.parse_sess.span_diagnostic.span_err(mi.span, msg);
}
is_soft = true;
}
_ => {
handle_errors(
- sess,
+ &sess.parse_sess,
meta.span(),
AttrError::UnknownMetaItem(
pprust::path_to_string(&mi.path),
}
} else {
handle_errors(
- sess,
+ &sess.parse_sess,
meta.span(),
AttrError::UnsupportedLiteral("unsupported literal", false),
);
}
}
(None, _, _) => {
- handle_errors(sess, attr.span, AttrError::MissingFeature);
+ handle_errors(&sess.parse_sess, attr.span, AttrError::MissingFeature);
continue;
}
_ => {
}
sym::rustc_const_stable | sym::stable => {
if meta_name == sym::stable && stab.is_some() {
- handle_errors(sess, attr.span, AttrError::MultipleStabilityLevels);
+ handle_errors(
+ &sess.parse_sess,
+ attr.span,
+ AttrError::MultipleStabilityLevels,
+ );
break;
} else if meta_name == sym::rustc_const_stable && const_stab.is_some() {
- handle_errors(sess, attr.span, AttrError::MultipleStabilityLevels);
+ handle_errors(
+ &sess.parse_sess,
+ attr.span,
+ AttrError::MultipleStabilityLevels,
+ );
break;
}
}
_ => {
handle_errors(
- sess,
+ &sess.parse_sess,
meta.span(),
AttrError::UnknownMetaItem(
pprust::path_to_string(&mi.path),
},
NestedMetaItem::Literal(lit) => {
handle_errors(
- sess,
+ &sess.parse_sess,
lit.span,
AttrError::UnsupportedLiteral("unsupported literal", false),
);
}
}
(None, _) => {
- handle_errors(sess, attr.span, AttrError::MissingFeature);
+ handle_errors(&sess.parse_sess, attr.span, AttrError::MissingFeature);
continue;
}
_ => {
- handle_errors(sess, attr.span, AttrError::MissingSince);
+ handle_errors(&sess.parse_sess, attr.span, AttrError::MissingSince);
continue;
}
}
(stab, const_stab)
}
-pub fn find_crate_name(attrs: &[Attribute]) -> Option<Symbol> {
- super::first_attr_value_str_by_name(attrs, sym::crate_name)
+pub fn find_crate_name(sess: &Session, attrs: &[Attribute]) -> Option<Symbol> {
+ sess.first_attr_value_str_by_name(attrs, sym::crate_name)
}
/// Tests if a cfg-pattern matches the cfg set
}
/// Finds the deprecation attribute. `None` if none exists.
-pub fn find_deprecation(
- sess: &ParseSess,
- attrs: &[Attribute],
- item_sp: Span,
-) -> Option<Deprecation> {
+pub fn find_deprecation(sess: &Session, attrs: &[Attribute], item_sp: Span) -> Option<Deprecation> {
find_deprecation_generic(sess, attrs.iter(), item_sp)
}
fn find_deprecation_generic<'a, I>(
- sess: &ParseSess,
+ sess: &Session,
attrs_iter: I,
item_sp: Span,
) -> Option<Deprecation>
I: Iterator<Item = &'a Attribute>,
{
let mut depr: Option<Deprecation> = None;
- let diagnostic = &sess.span_diagnostic;
+ let diagnostic = &sess.parse_sess.span_diagnostic;
'outer: for attr in attrs_iter {
- if !(attr.check_name(sym::deprecated) || attr.check_name(sym::rustc_deprecated)) {
+ if !(sess.check_name(attr, sym::deprecated) || sess.check_name(attr, sym::rustc_deprecated))
+ {
continue;
}
let get = |meta: &MetaItem, item: &mut Option<Symbol>| {
if item.is_some() {
handle_errors(
- sess,
+ &sess.parse_sess,
meta.span,
AttrError::MultipleItem(pprust::path_to_string(&meta.path)),
);
} else {
if let Some(lit) = meta.name_value_literal() {
handle_errors(
- sess,
+ &sess.parse_sess,
lit.span,
AttrError::UnsupportedLiteral(
"literal in `deprecated` \
continue 'outer;
}
}
- sym::note if attr.check_name(sym::deprecated) => {
+ sym::note if sess.check_name(attr, sym::deprecated) => {
if !get(mi, &mut note) {
continue 'outer;
}
}
- sym::reason if attr.check_name(sym::rustc_deprecated) => {
+ sym::reason if sess.check_name(attr, sym::rustc_deprecated) => {
if !get(mi, &mut note) {
continue 'outer;
}
}
- sym::suggestion if attr.check_name(sym::rustc_deprecated) => {
+ sym::suggestion if sess.check_name(attr, sym::rustc_deprecated) => {
if !get(mi, &mut suggestion) {
continue 'outer;
}
}
_ => {
handle_errors(
- sess,
+ &sess.parse_sess,
meta.span(),
AttrError::UnknownMetaItem(
pprust::path_to_string(&mi.path),
- if attr.check_name(sym::deprecated) {
+ if sess.check_name(attr, sym::deprecated) {
&["since", "note"]
} else {
&["since", "reason", "suggestion"]
},
NestedMetaItem::Literal(lit) => {
handle_errors(
- sess,
+ &sess.parse_sess,
lit.span,
AttrError::UnsupportedLiteral(
"item in `deprecated` must be a key/value pair",
}
}
- if suggestion.is_some() && attr.check_name(sym::deprecated) {
+ if suggestion.is_some() && sess.check_name(attr, sym::deprecated) {
unreachable!("only allowed on rustc_deprecated")
}
- if attr.check_name(sym::rustc_deprecated) {
+ if sess.check_name(attr, sym::rustc_deprecated) {
if since.is_none() {
- handle_errors(sess, attr.span, AttrError::MissingSince);
+ handle_errors(&sess.parse_sess, attr.span, AttrError::MissingSince);
continue;
}
}
}
- mark_used(&attr);
+ sess.mark_attr_used(&attr);
- let is_since_rustc_version = attr.check_name(sym::rustc_deprecated);
+ let is_since_rustc_version = sess.check_name(attr, sym::rustc_deprecated);
depr = Some(Deprecation { since, note, suggestion, is_since_rustc_version });
}
/// the same discriminant size that the corresponding C enum would or C
/// structure layout, `packed` to remove padding, and `transparent` to elegate representation
/// concerns to the only non-ZST field.
-pub fn find_repr_attrs(sess: &ParseSess, attr: &Attribute) -> Vec<ReprAttr> {
+pub fn find_repr_attrs(sess: &Session, attr: &Attribute) -> Vec<ReprAttr> {
use ReprAttr::*;
let mut acc = Vec::new();
- let diagnostic = &sess.span_diagnostic;
+ let diagnostic = &sess.parse_sess.span_diagnostic;
if attr.has_name(sym::repr) {
if let Some(items) = attr.meta_item_list() {
- mark_used(attr);
+ sess.mark_attr_used(attr);
for item in items {
if !item.is_meta_item() {
handle_errors(
- sess,
+ &sess.parse_sess,
item.span(),
AttrError::UnsupportedLiteral(
"meta item in `repr` must be an identifier",
}
pub fn find_transparency(
+ sess: &Session,
attrs: &[Attribute],
macro_rules: bool,
) -> (Transparency, Option<TransparencyError>) {
let mut transparency = None;
let mut error = None;
for attr in attrs {
- if attr.check_name(sym::rustc_macro_transparency) {
+ if sess.check_name(attr, sym::rustc_macro_transparency) {
if let Some((_, old_span)) = transparency {
error = Some(TransparencyError::MultipleTransparencyAttrs(old_span, attr.span));
break;
}
pub fn allow_internal_unstable<'a>(
+ sess: &'a Session,
attrs: &[Attribute],
- diag: &'a rustc_errors::Handler,
) -> Option<impl Iterator<Item = Symbol> + 'a> {
- let attr = find_by_name(attrs, sym::allow_internal_unstable)?;
+ let attr = sess.find_by_name(attrs, sym::allow_internal_unstable)?;
let list = attr.meta_item_list().or_else(|| {
- diag.span_err(attr.span, "allow_internal_unstable expects list of feature names");
+ sess.diagnostic()
+ .span_err(attr.span, "allow_internal_unstable expects list of feature names");
None
})?;
Some(list.into_iter().filter_map(move |it| {
let name = it.ident().map(|ident| ident.name);
if name.is_none() {
- diag.span_err(it.span(), "`allow_internal_unstable` expects feature names");
+ sess.diagnostic()
+ .span_err(it.span(), "`allow_internal_unstable` expects feature names");
}
name
}))
match parse_cfg(cx, sp, tts) {
Ok(cfg) => {
- let matches_cfg = attr::cfg_matches(&cfg, cx.parse_sess, cx.ecfg.features);
+ let matches_cfg = attr::cfg_matches(&cfg, &cx.sess.parse_sess, cx.ecfg.features);
MacEager::expr(cx.expr_bool(sp, matches_cfg))
}
Err(mut err) => {
) -> ExpandResult<Vec<Annotatable>, Annotatable> {
let template = AttributeTemplate { list: Some("path"), ..Default::default() };
let attr = &ecx.attribute(meta_item.clone());
- validate_attr::check_builtin_attribute(ecx.parse_sess, attr, sym::cfg_accessible, template);
+ validate_attr::check_builtin_attribute(
+ &ecx.sess.parse_sess,
+ attr,
+ sym::cfg_accessible,
+ template,
+ );
let path = match validate_input(ecx, meta_item) {
Some(path) => path,
},
StaticEnum(..) => {
struct_span_err!(
- cx.parse_sess.span_diagnostic,
+ &cx.sess.parse_sess.span_diagnostic,
trait_span,
E0665,
"`Default` cannot be derived for enums, only structs"
match *item {
Annotatable::Item(ref item) => {
let is_packed = item.attrs.iter().any(|attr| {
- for r in attr::find_repr_attrs(&cx.parse_sess, attr) {
+ for r in attr::find_repr_attrs(&cx.sess, attr) {
if let attr::ReprPacked(_) = r {
return true;
}
let attr = cx.attribute(cx.meta_word(self.span, sym::automatically_derived));
// Just mark it now since we know that it'll end up used downstream
- attr::mark_used(&attr);
+ cx.sess.mark_attr_used(&attr);
let opt_trait_ref = Some(trait_ref);
let unused_qual = {
let word = rustc_ast::attr::mk_nested_word_item(Ident::new(
let sp = cx.with_def_site_ctxt(sp);
let value = env::var(&var.as_str()).ok().as_deref().map(Symbol::intern);
- cx.parse_sess.env_depinfo.borrow_mut().insert((Symbol::intern(&var), value));
+ cx.sess.parse_sess.env_depinfo.borrow_mut().insert((Symbol::intern(&var), value));
let e = match value {
None => {
let lt = cx.lifetime(sp, Ident::new(kw::StaticLifetime, sp));
let sp = cx.with_def_site_ctxt(sp);
let value = env::var(&*var.as_str()).ok().as_deref().map(Symbol::intern);
- cx.parse_sess.env_depinfo.borrow_mut().insert((var, value));
+ cx.sess.parse_sess.env_depinfo.borrow_mut().insert((var, value));
let e = match value {
None => {
cx.span_err(sp, &msg.as_str());
check_builtin_macro_attribute(ecx, meta_item, sym::global_allocator);
let not_static = |item: Annotatable| {
- ecx.parse_sess.span_diagnostic.span_err(item.span(), "allocators must be statics");
+ ecx.sess.parse_sess.span_diagnostic.span_err(item.span(), "allocators must be statics");
vec![item]
};
let item = match item {
// If we already have a string with instructions,
// ending up in Asm state again is an error.
return Err(struct_span_err!(
- cx.parse_sess.span_diagnostic,
+ cx.sess.parse_sess.span_diagnostic,
sp,
E0660,
"malformed inline assembly"
Some('+') => Some(Symbol::intern(&format!("={}", ch.as_str()))),
_ => {
struct_span_err!(
- cx.parse_sess.span_diagnostic,
+ cx.sess.parse_sess.span_diagnostic,
span,
E0661,
"output operand constraint lacks '=' or '+'"
if constraint.as_str().starts_with('=') {
struct_span_err!(
- cx.parse_sess.span_diagnostic,
+ cx.sess.parse_sess.span_diagnostic,
p.prev_token.span,
E0662,
"input operand constraint contains '='"
.emit();
} else if constraint.as_str().starts_with('+') {
struct_span_err!(
- cx.parse_sess.span_diagnostic,
+ cx.sess.parse_sess.span_diagnostic,
p.prev_token.span,
E0663,
"input operand constraint contains '+'"
cx.span_warn(p.prev_token.span, "expected a clobber, found an option");
} else if s.as_str().starts_with('{') || s.as_str().ends_with('}') {
struct_span_err!(
- cx.parse_sess.span_diagnostic,
+ cx.sess.parse_sess.span_diagnostic,
p.prev_token.span,
E0664,
"clobber should not be surrounded by braces"
use rustc_ast::ast::{self, NodeId};
use rustc_ast::attr;
-use rustc_ast::expand::is_proc_macro_attr;
use rustc_ast::ptr::P;
use rustc_ast::visit::{self, Visitor};
use rustc_ast_pretty::pprust;
use rustc_expand::base::{ExtCtxt, ResolverExpand};
use rustc_expand::expand::{AstFragment, ExpansionConfig};
-use rustc_session::parse::ParseSess;
+use rustc_session::Session;
use rustc_span::hygiene::AstPass;
use rustc_span::source_map::SourceMap;
use rustc_span::symbol::{kw, sym, Ident, Symbol};
}
struct CollectProcMacros<'a> {
+ sess: &'a Session,
macros: Vec<ProcMacro>,
in_root: bool,
handler: &'a rustc_errors::Handler,
}
pub fn inject(
- sess: &ParseSess,
+ sess: &Session,
resolver: &mut dyn ResolverExpand,
mut krate: ast::Crate,
is_proc_macro_crate: bool,
let mut cx = ExtCtxt::new(sess, ecfg, resolver, None);
let mut collect = CollectProcMacros {
+ sess,
macros: Vec::new(),
in_root: true,
handler,
impl<'a> Visitor<'a> for CollectProcMacros<'a> {
fn visit_item(&mut self, item: &'a ast::Item) {
if let ast::ItemKind::MacroDef(..) = item.kind {
- if self.is_proc_macro_crate && attr::contains_name(&item.attrs, sym::macro_export) {
+ if self.is_proc_macro_crate && self.sess.contains_name(&item.attrs, sym::macro_export) {
let msg =
"cannot export macro_rules! macros from a `proc-macro` crate type currently";
self.handler.span_err(self.source_map.guess_head_span(item.span), msg);
let mut found_attr: Option<&'a ast::Attribute> = None;
for attr in &item.attrs {
- if is_proc_macro_attr(&attr) {
+ if self.sess.is_proc_macro_attr(&attr) {
if let Some(prev_attr) = found_attr {
let prev_item = prev_attr.get_normal_item();
let item = attr.get_normal_item();
return;
}
- if attr.check_name(sym::proc_macro_derive) {
+ if self.sess.check_name(attr, sym::proc_macro_derive) {
self.collect_custom_derive(item, attr);
- } else if attr.check_name(sym::proc_macro_attribute) {
+ } else if self.sess.check_name(attr, sym::proc_macro_attribute) {
self.collect_attr_proc_macro(item);
- } else if attr.check_name(sym::proc_macro) {
+ } else if self.sess.check_name(attr, sym::proc_macro) {
self.collect_bang_proc_macro(item);
};
+use rustc_ast::ast;
use rustc_ast::ptr::P;
-use rustc_ast::{ast, attr};
use rustc_expand::base::{ExtCtxt, ResolverExpand};
use rustc_expand::expand::ExpansionConfig;
-use rustc_session::parse::ParseSess;
+use rustc_session::Session;
use rustc_span::edition::Edition;
use rustc_span::hygiene::AstPass;
use rustc_span::symbol::{kw, sym, Ident, Symbol};
pub fn inject(
mut krate: ast::Crate,
resolver: &mut dyn ResolverExpand,
- sess: &ParseSess,
+ sess: &Session,
alt_std_name: Option<Symbol>,
) -> (ast::Crate, Option<Symbol>) {
- let rust_2018 = sess.edition >= Edition::Edition2018;
+ let rust_2018 = sess.parse_sess.edition >= Edition::Edition2018;
// the first name in this list is the crate name of the crate with the prelude
- let names: &[Symbol] = if attr::contains_name(&krate.attrs, sym::no_core) {
+ let names: &[Symbol] = if sess.contains_name(&krate.attrs, sym::no_core) {
return (krate, None);
- } else if attr::contains_name(&krate.attrs, sym::no_std) {
- if attr::contains_name(&krate.attrs, sym::compiler_builtins) {
+ } else if sess.contains_name(&krate.attrs, sym::no_std) {
+ if sess.contains_name(&krate.attrs, sym::compiler_builtins) {
&[sym::core]
} else {
&[sym::core, sym::compiler_builtins]
use rustc_ast::attr;
use rustc_ast_pretty::pprust;
use rustc_expand::base::*;
+use rustc_session::Session;
use rustc_span::source_map::respan;
use rustc_span::symbol::{sym, Ident, Symbol};
use rustc_span::Span;
};
if let ast::ItemKind::MacCall(_) = item.kind {
- cx.parse_sess.span_diagnostic.span_warn(
+ cx.sess.parse_sess.span_diagnostic.span_warn(
item.span,
"`#[test]` attribute should not be used on macros. Use `#[cfg(test)]` instead.",
);
),
),
// ignore: true | false
- field("ignore", cx.expr_bool(sp, should_ignore(&item))),
+ field(
+ "ignore",
+ cx.expr_bool(sp, should_ignore(&cx.sess, &item)),
+ ),
// allow_fail: true | false
- field("allow_fail", cx.expr_bool(sp, should_fail(&item))),
+ field(
+ "allow_fail",
+ cx.expr_bool(sp, should_fail(&cx.sess, &item)),
+ ),
// should_panic: ...
field(
"should_panic",
Yes(Option<Symbol>),
}
-fn should_ignore(i: &ast::Item) -> bool {
- attr::contains_name(&i.attrs, sym::ignore)
+fn should_ignore(sess: &Session, i: &ast::Item) -> bool {
+ sess.contains_name(&i.attrs, sym::ignore)
}
-fn should_fail(i: &ast::Item) -> bool {
- attr::contains_name(&i.attrs, sym::allow_fail)
+fn should_fail(sess: &Session, i: &ast::Item) -> bool {
+ sess.contains_name(&i.attrs, sym::allow_fail)
}
fn should_panic(cx: &ExtCtxt<'_>, i: &ast::Item) -> ShouldPanic {
- match attr::find_by_name(&i.attrs, sym::should_panic) {
+ match cx.sess.find_by_name(&i.attrs, sym::should_panic) {
Some(attr) => {
- let sd = &cx.parse_sess.span_diagnostic;
+ let sd = &cx.sess.parse_sess.span_diagnostic;
match attr.meta_item_list() {
// Handle #[should_panic(expected = "foo")]
}
fn has_test_signature(cx: &ExtCtxt<'_>, i: &ast::Item) -> bool {
- let has_should_panic_attr = attr::contains_name(&i.attrs, sym::should_panic);
- let sd = &cx.parse_sess.span_diagnostic;
+ let has_should_panic_attr = cx.sess.contains_name(&i.attrs, sym::should_panic);
+ let sd = &cx.sess.parse_sess.span_diagnostic;
if let ast::ItemKind::Fn(_, ref sig, ref generics, _) = i.kind {
if let ast::Unsafe::Yes(span) = sig.header.unsafety {
sd.struct_span_err(i.span, "unsafe functions cannot be used for tests")
};
if !has_sig {
- cx.parse_sess.span_diagnostic.span_err(
+ cx.sess.parse_sess.span_diagnostic.span_err(
i.span,
"functions used as benches must have \
signature `fn(&mut Bencher) -> impl Termination`",
use log::debug;
use rustc_ast::ast;
use rustc_ast::attr;
-use rustc_ast::entry::{self, EntryPointType};
+use rustc_ast::entry::EntryPointType;
use rustc_ast::mut_visit::{ExpectOne, *};
use rustc_ast::ptr::P;
use rustc_expand::base::{ExtCtxt, ResolverExpand};
use rustc_expand::expand::{AstFragment, ExpansionConfig};
use rustc_feature::Features;
-use rustc_session::parse::ParseSess;
+use rustc_session::Session;
use rustc_span::hygiene::{AstPass, SyntaxContext, Transparency};
use rustc_span::source_map::respan;
use rustc_span::symbol::{sym, Ident, Symbol};
// Traverse the crate, collecting all the test functions, eliding any
// existing main functions, and synthesizing a main test harness
-pub fn inject(
- sess: &ParseSess,
- resolver: &mut dyn ResolverExpand,
- should_test: bool,
- krate: &mut ast::Crate,
- span_diagnostic: &rustc_errors::Handler,
- features: &Features,
- panic_strategy: PanicStrategy,
- platform_panic_strategy: PanicStrategy,
- enable_panic_abort_tests: bool,
-) {
+pub fn inject(sess: &Session, resolver: &mut dyn ResolverExpand, krate: &mut ast::Crate) {
+ let span_diagnostic = sess.diagnostic();
+ let panic_strategy = sess.panic_strategy();
+ let platform_panic_strategy = sess.target.target.options.panic_strategy;
+
// Check for #![reexport_test_harness_main = "some_name"] which gives the
// main test function the name `some_name` without hygiene. This needs to be
// unconditional, so that the attribute is still marked as used in
// non-test builds.
let reexport_test_harness_main =
- attr::first_attr_value_str_by_name(&krate.attrs, sym::reexport_test_harness_main);
+ sess.first_attr_value_str_by_name(&krate.attrs, sym::reexport_test_harness_main);
// Do this here so that the test_runner crate attribute gets marked as used
// even in non-test builds
- let test_runner = get_test_runner(span_diagnostic, &krate);
+ let test_runner = get_test_runner(sess, span_diagnostic, &krate);
- if should_test {
- let panic_strategy = match (panic_strategy, enable_panic_abort_tests) {
+ if sess.opts.test {
+ let panic_strategy = match (panic_strategy, sess.opts.debugging_opts.panic_abort_tests) {
(PanicStrategy::Abort, true) => PanicStrategy::Abort,
- (PanicStrategy::Abort, false) if panic_strategy == platform_panic_strategy => {
- // Silently allow compiling with panic=abort on these platforms,
- // but with old behavior (abort if a test fails).
- PanicStrategy::Unwind
- }
(PanicStrategy::Abort, false) => {
- span_diagnostic.err(
- "building tests with panic=abort is not supported \
- without `-Zpanic_abort_tests`",
- );
+ if panic_strategy == platform_panic_strategy {
+ // Silently allow compiling with panic=abort on these platforms,
+ // but with old behavior (abort if a test fails).
+ } else {
+ span_diagnostic.err(
+ "building tests with panic=abort is not supported \
+ without `-Zpanic_abort_tests`",
+ );
+ }
PanicStrategy::Unwind
}
(PanicStrategy::Unwind, _) => PanicStrategy::Unwind,
resolver,
reexport_test_harness_main,
krate,
- features,
+ &sess.features_untracked(),
panic_strategy,
test_runner,
)
fn flat_map_item(&mut self, i: P<ast::Item>) -> SmallVec<[P<ast::Item>; 1]> {
let mut item = i.into_inner();
- if is_test_case(&item) {
+ if is_test_case(&self.cx.ext_cx.sess, &item) {
debug!("this is a test item");
let test = Test { span: item.span, ident: item.ident };
}
}
+// Beware, this is duplicated in librustc_passes/entry.rs (with
+// `rustc_hir::Item`), so make sure to keep them in sync.
+fn entry_point_type(sess: &Session, item: &ast::Item, depth: usize) -> EntryPointType {
+ match item.kind {
+ ast::ItemKind::Fn(..) => {
+ if sess.contains_name(&item.attrs, sym::start) {
+ EntryPointType::Start
+ } else if sess.contains_name(&item.attrs, sym::main) {
+ EntryPointType::MainAttr
+ } else if item.ident.name == sym::main {
+ if depth == 1 {
+ // This is a top-level function so can be 'main'
+ EntryPointType::MainNamed
+ } else {
+ EntryPointType::OtherMain
+ }
+ } else {
+ EntryPointType::None
+ }
+ }
+ _ => EntryPointType::None,
+ }
+}
/// A folder used to remove any entry points (like fn main) because the harness
/// generator will provide its own
-struct EntryPointCleaner {
+struct EntryPointCleaner<'a> {
// Current depth in the ast
+ sess: &'a Session,
depth: usize,
def_site: Span,
}
-impl MutVisitor for EntryPointCleaner {
+impl<'a> MutVisitor for EntryPointCleaner<'a> {
fn flat_map_item(&mut self, i: P<ast::Item>) -> SmallVec<[P<ast::Item>; 1]> {
self.depth += 1;
let item = noop_flat_map_item(i, self).expect_one("noop did something");
// Remove any #[main] or #[start] from the AST so it doesn't
// clash with the one we're going to add, but mark it as
// #[allow(dead_code)] to avoid printing warnings.
- let item = match entry::entry_point_type(&item, self.depth) {
+ let item = match entry_point_type(self.sess, &item, self.depth) {
EntryPointType::MainNamed | EntryPointType::MainAttr | EntryPointType::Start => item
.map(|ast::Item { id, ident, attrs, kind, vis, span, tokens }| {
let allow_ident = Ident::new(sym::allow, self.def_site);
let allow_dead_code = attr::mk_attr_outer(allow_dead_code_item);
let attrs = attrs
.into_iter()
- .filter(|attr| !attr.check_name(sym::main) && !attr.check_name(sym::start))
+ .filter(|attr| {
+ !self.sess.check_name(attr, sym::main)
+ && !self.sess.check_name(attr, sym::start)
+ })
.chain(iter::once(allow_dead_code))
.collect();
/// Crawl over the crate, inserting test reexports and the test main function
fn generate_test_harness(
- sess: &ParseSess,
+ sess: &Session,
resolver: &mut dyn ResolverExpand,
reexport_test_harness_main: Option<Symbol>,
krate: &mut ast::Crate,
let def_site = DUMMY_SP.with_def_site_ctxt(expn_id);
// Remove the entry points
- let mut cleaner = EntryPointCleaner { depth: 0, def_site };
+ let mut cleaner = EntryPointCleaner { sess, depth: 0, def_site };
cleaner.visit_crate(krate);
let cx = TestCtxt {
)
}
-fn is_test_case(i: &ast::Item) -> bool {
- attr::contains_name(&i.attrs, sym::rustc_test_marker)
+fn is_test_case(sess: &Session, i: &ast::Item) -> bool {
+ sess.contains_name(&i.attrs, sym::rustc_test_marker)
}
-fn get_test_runner(sd: &rustc_errors::Handler, krate: &ast::Crate) -> Option<ast::Path> {
- let test_attr = attr::find_by_name(&krate.attrs, sym::test_runner)?;
+fn get_test_runner(
+ sess: &Session,
+ sd: &rustc_errors::Handler,
+ krate: &ast::Crate,
+) -> Option<ast::Path> {
+ let test_attr = sess.find_by_name(&krate.attrs, sym::test_runner)?;
let meta_list = test_attr.meta_item_list()?;
let span = test_attr.span;
match &*meta_list {
// All the built-in macro attributes are "words" at the moment.
let template = AttributeTemplate { word: true, ..Default::default() };
let attr = ecx.attribute(meta_item.clone());
- validate_attr::check_builtin_attribute(ecx.parse_sess, &attr, name, template);
+ validate_attr::check_builtin_attribute(&ecx.sess.parse_sess, &attr, name, template);
}
debug!("get_static: sym={} attrs={:?}", sym, attrs);
for attr in attrs {
- if attr.check_name(sym::thread_local) {
+ if self.tcx.sess.check_name(attr, sym::thread_local) {
llvm::set_thread_local_mode(g, self.tls_model);
}
}
fn write_coverage_mappings(
&mut self,
expressions: Vec<CounterExpression>,
- counter_regions: impl Iterator<Item = (Counter, &'a Region)>,
+ counter_regions: impl Iterator<Item = (Counter, &'tcx Region<'tcx>)>,
coverage_mappings_buffer: &RustString,
) {
let mut counter_regions = counter_regions.collect::<Vec<_>>();
let mut virtual_file_mapping = Vec::new();
let mut mapping_regions = Vec::new();
- let mut current_file_path = None;
+ let mut current_file_name = None;
let mut current_file_id = 0;
// Convert the list of (Counter, Region) pairs to an array of `CounterMappingRegion`, sorted
// `filenames` array.
counter_regions.sort_unstable_by_key(|(_counter, region)| *region);
for (counter, region) in counter_regions {
- let (file_path, start_line, start_col, end_line, end_col) = region.file_start_and_end();
- let same_file = current_file_path.as_ref().map_or(false, |p| p == file_path);
+ let Region { file_name, start_line, start_col, end_line, end_col } = *region;
+ let same_file = current_file_name.as_ref().map_or(false, |p| p == file_name);
if !same_file {
- if current_file_path.is_some() {
+ if current_file_name.is_some() {
current_file_id += 1;
}
- current_file_path = Some(file_path.clone());
- let filename = CString::new(file_path.to_string_lossy().to_string())
- .expect("null error converting filename to C string");
- debug!(" file_id: {} = '{:?}'", current_file_id, filename);
- let filenames_index = match self.filename_to_index.get(&filename) {
+ current_file_name = Some(file_name.to_string());
+ let c_filename =
+ CString::new(file_name).expect("null error converting filename to C string");
+ debug!(" file_id: {} = '{:?}'", current_file_id, c_filename);
+ let filenames_index = match self.filename_to_index.get(&c_filename) {
Some(index) => *index,
None => {
let index = self.filenames.len() as u32;
- self.filenames.push(filename.clone());
- self.filename_to_index.insert(filename.clone(), index);
+ self.filenames.push(c_filename.clone());
+ self.filename_to_index.insert(c_filename.clone(), index);
index
}
};
use libc::c_uint;
use llvm::coverageinfo::CounterMappingRegion;
use log::debug;
-use rustc_codegen_ssa::coverageinfo::map::{CounterExpression, ExprKind, FunctionCoverage};
+use rustc_codegen_ssa::coverageinfo::map::{CounterExpression, ExprKind, FunctionCoverage, Region};
use rustc_codegen_ssa::traits::{
BaseTypeMethods, CoverageInfoBuilderMethods, CoverageInfoMethods, StaticMethods,
};
instance: Instance<'tcx>,
function_source_hash: u64,
id: u32,
- start_byte_pos: u32,
- end_byte_pos: u32,
+ region: Region<'tcx>,
) {
debug!(
"adding counter to coverage_regions: instance={:?}, function_source_hash={}, id={}, \
- byte range {}..{}",
- instance, function_source_hash, id, start_byte_pos, end_byte_pos,
+ at {:?}",
+ instance, function_source_hash, id, region,
);
let mut coverage_regions = self.coverage_context().function_coverage_map.borrow_mut();
coverage_regions
.entry(instance)
.or_insert_with(|| FunctionCoverage::new(self.tcx, instance))
- .add_counter(function_source_hash, id, start_byte_pos, end_byte_pos);
+ .add_counter(function_source_hash, id, region);
}
fn add_counter_expression_region(
lhs: u32,
op: ExprKind,
rhs: u32,
- start_byte_pos: u32,
- end_byte_pos: u32,
+ region: Region<'tcx>,
) {
debug!(
"adding counter expression to coverage_regions: instance={:?}, id={}, {} {:?} {}, \
- byte range {}..{}",
- instance, id_descending_from_max, lhs, op, rhs, start_byte_pos, end_byte_pos,
+ at {:?}",
+ instance, id_descending_from_max, lhs, op, rhs, region,
);
let mut coverage_regions = self.coverage_context().function_coverage_map.borrow_mut();
coverage_regions
.entry(instance)
.or_insert_with(|| FunctionCoverage::new(self.tcx, instance))
- .add_counter_expression(
- id_descending_from_max,
- lhs,
- op,
- rhs,
- start_byte_pos,
- end_byte_pos,
- );
+ .add_counter_expression(id_descending_from_max, lhs, op, rhs, region);
}
- fn add_unreachable_region(
- &mut self,
- instance: Instance<'tcx>,
- start_byte_pos: u32,
- end_byte_pos: u32,
- ) {
+ fn add_unreachable_region(&mut self, instance: Instance<'tcx>, region: Region<'tcx>) {
debug!(
- "adding unreachable code to coverage_regions: instance={:?}, byte range {}..{}",
- instance, start_byte_pos, end_byte_pos,
+ "adding unreachable code to coverage_regions: instance={:?}, at {:?}",
+ instance, region,
);
let mut coverage_regions = self.coverage_context().function_coverage_map.borrow_mut();
coverage_regions
.entry(instance)
.or_insert_with(|| FunctionCoverage::new(self.tcx, instance))
- .add_unreachable_region(start_byte_pos, end_byte_pos);
+ .add_unreachable_region(region);
}
}
use rustc_middle::bug;
use rustc_session::config::DebugInfo;
-use rustc_ast::attr;
use rustc_span::symbol::sym;
/// Inserts a side-effect free instruction sequence that makes sure that the
}
pub fn needs_gdb_debug_scripts_section(cx: &CodegenCx<'_, '_>) -> bool {
- let omit_gdb_pretty_printer_section =
- attr::contains_name(&cx.tcx.hir().krate_attrs(), sym::omit_gdb_pretty_printer_section);
+ let omit_gdb_pretty_printer_section = cx
+ .tcx
+ .sess
+ .contains_name(&cx.tcx.hir().krate_attrs(), sym::omit_gdb_pretty_printer_section);
!omit_gdb_pretty_printer_section
&& cx.sess().opts.debuginfo != DebugInfo::None
fn param_type_metadata(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>) -> &'ll DIType {
debug!("param_type_metadata: {:?}", t);
let name = format!("{:?}", t);
- return unsafe {
+ unsafe {
llvm::LLVMRustDIBuilderCreateBasicType(
DIB(cx),
name.as_ptr().cast(),
Size::ZERO.bits(),
DW_ATE_unsigned,
)
- };
+ }
}
pub fn compile_unit_metadata(
use rustc_codegen_ssa::base::{compare_simd_types, to_immediate, wants_msvc_seh};
use rustc_codegen_ssa::common::span_invalid_monomorphization_error;
use rustc_codegen_ssa::common::{IntPredicate, TypeKind};
-use rustc_codegen_ssa::coverageinfo::ExprKind;
+use rustc_codegen_ssa::coverageinfo;
use rustc_codegen_ssa::glue;
use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue};
use rustc_codegen_ssa::mir::place::PlaceRef;
let mut is_codegen_intrinsic = true;
// Set `is_codegen_intrinsic` to `false` to bypass `codegen_intrinsic_call()`.
- if self.tcx.sess.opts.debugging_opts.instrument_coverage {
- // If the intrinsic is from the local MIR, add the coverage information to the Codegen
- // context, to be encoded into the local crate's coverage map.
- if caller_instance.def_id().is_local() {
- // FIXME(richkadel): Make sure to add coverage analysis tests on a crate with
- // external crate dependencies, where:
- // 1. Both binary and dependent crates are compiled with `-Zinstrument-coverage`
- // 2. Only binary is compiled with `-Zinstrument-coverage`
- // 3. Only dependent crates are compiled with `-Zinstrument-coverage`
- match intrinsic {
- sym::count_code_region => {
- use coverage::count_code_region_args::*;
- self.add_counter_region(
- caller_instance,
- op_to_u64(&args[FUNCTION_SOURCE_HASH]),
- op_to_u32(&args[COUNTER_ID]),
- op_to_u32(&args[START_BYTE_POS]),
- op_to_u32(&args[END_BYTE_POS]),
- );
- }
- sym::coverage_counter_add | sym::coverage_counter_subtract => {
- use coverage::coverage_counter_expression_args::*;
- self.add_counter_expression_region(
- caller_instance,
- op_to_u32(&args[EXPRESSION_ID]),
- op_to_u32(&args[LEFT_ID]),
- if intrinsic == sym::coverage_counter_add {
- ExprKind::Add
- } else {
- ExprKind::Subtract
- },
- op_to_u32(&args[RIGHT_ID]),
- op_to_u32(&args[START_BYTE_POS]),
- op_to_u32(&args[END_BYTE_POS]),
- );
- }
- sym::coverage_unreachable => {
- use coverage::coverage_unreachable_args::*;
- self.add_unreachable_region(
- caller_instance,
- op_to_u32(&args[START_BYTE_POS]),
- op_to_u32(&args[END_BYTE_POS]),
- );
- }
- _ => {}
- }
+ // FIXME(richkadel): Make sure to add coverage analysis tests on a crate with
+ // external crate dependencies, where:
+ // 1. Both binary and dependent crates are compiled with `-Zinstrument-coverage`
+ // 2. Only binary is compiled with `-Zinstrument-coverage`
+ // 3. Only dependent crates are compiled with `-Zinstrument-coverage`
+ match intrinsic {
+ sym::count_code_region => {
+ use coverage::count_code_region_args::*;
+ self.add_counter_region(
+ caller_instance,
+ op_to_u64(&args[FUNCTION_SOURCE_HASH]),
+ op_to_u32(&args[COUNTER_ID]),
+ coverageinfo::Region::new(
+ op_to_str_slice(&args[FILE_NAME]),
+ op_to_u32(&args[START_LINE]),
+ op_to_u32(&args[START_COL]),
+ op_to_u32(&args[END_LINE]),
+ op_to_u32(&args[END_COL]),
+ ),
+ );
}
-
- // Only the `count_code_region` coverage intrinsic is translated into an actual LLVM
- // intrinsic call (local or not); otherwise, set `is_codegen_intrinsic` to `false`.
- match intrinsic {
- sym::coverage_counter_add
- | sym::coverage_counter_subtract
- | sym::coverage_unreachable => {
- is_codegen_intrinsic = false;
- }
- _ => {}
+ sym::coverage_counter_add | sym::coverage_counter_subtract => {
+ is_codegen_intrinsic = false;
+ use coverage::coverage_counter_expression_args::*;
+ self.add_counter_expression_region(
+ caller_instance,
+ op_to_u32(&args[EXPRESSION_ID]),
+ op_to_u32(&args[LEFT_ID]),
+ if intrinsic == sym::coverage_counter_add {
+ coverageinfo::ExprKind::Add
+ } else {
+ coverageinfo::ExprKind::Subtract
+ },
+ op_to_u32(&args[RIGHT_ID]),
+ coverageinfo::Region::new(
+ op_to_str_slice(&args[FILE_NAME]),
+ op_to_u32(&args[START_LINE]),
+ op_to_u32(&args[START_COL]),
+ op_to_u32(&args[END_LINE]),
+ op_to_u32(&args[END_COL]),
+ ),
+ );
}
+ sym::coverage_unreachable => {
+ is_codegen_intrinsic = false;
+ use coverage::coverage_unreachable_args::*;
+ self.add_unreachable_region(
+ caller_instance,
+ coverageinfo::Region::new(
+ op_to_str_slice(&args[FILE_NAME]),
+ op_to_u32(&args[START_LINE]),
+ op_to_u32(&args[START_COL]),
+ op_to_u32(&args[END_LINE]),
+ op_to_u32(&args[END_COL]),
+ ),
+ );
+ }
+ _ => {}
}
is_codegen_intrinsic
}
self.call(llfn, &[], None)
}
sym::count_code_region => {
- // FIXME(richkadel): The current implementation assumes the MIR for the given
- // caller_instance represents a single function. Validate and/or correct if inlining
- // and/or monomorphization invalidates these assumptions.
let coverageinfo = tcx.coverageinfo(caller_instance.def_id());
let mangled_fn = tcx.symbol_name(caller_instance);
let (mangled_fn_name, _len_val) = self.const_str(Symbol::intern(mangled_fn.name));
}
}
+fn op_to_str_slice<'tcx>(op: &Operand<'tcx>) -> &'tcx str {
+ Operand::value_from_const(op).try_to_str_slice().expect("Value is &str")
+}
+
fn op_to_u32<'tcx>(op: &Operand<'tcx>) -> u32 {
Operand::scalar_from_const(op).to_u32().expect("Scalar is u32")
}
use crate::traits::*;
use jobserver::{Acquired, Client};
-use rustc_ast::attr;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::profiling::SelfProfilerRef;
use rustc_data_structures::profiling::TimingGuard;
let crate_name = tcx.crate_name(LOCAL_CRATE);
let crate_hash = tcx.crate_hash(LOCAL_CRATE);
- let no_builtins = attr::contains_name(&tcx.hir().krate().item.attrs, sym::no_builtins);
+ let no_builtins = tcx.sess.contains_name(&tcx.hir().krate().item.attrs, sym::no_builtins);
let is_compiler_builtins =
- attr::contains_name(&tcx.hir().krate().item.attrs, sym::compiler_builtins);
- let subsystem =
- attr::first_attr_value_str_by_name(&tcx.hir().krate().item.attrs, sym::windows_subsystem);
+ tcx.sess.contains_name(&tcx.hir().krate().item.attrs, sym::compiler_builtins);
+ let subsystem = tcx
+ .sess
+ .first_attr_value_str_by_name(&tcx.hir().krate().item.attrs, sym::windows_subsystem);
let windows_subsystem = subsystem.map(|subsystem| {
if subsystem != sym::windows && subsystem != sym::console {
tcx.sess.fatal(&format!(
let _timer = sess.timer("copy_all_cgu_workproducts_to_incr_comp_cache_dir");
for module in compiled_modules.modules.iter().filter(|m| m.kind == ModuleKind::Regular) {
- let path = module.object.as_ref().map(|path| path.clone());
+ let path = module.object.as_ref().cloned();
if let Some((id, product)) =
copy_cgu_workproduct_to_incr_comp_cache_dir(sess, &module.name, &path)
use rustc_index::vec::IndexVec;
use rustc_middle::ty::Instance;
use rustc_middle::ty::TyCtxt;
-use rustc_span::source_map::{Pos, SourceMap};
-use rustc_span::{BytePos, FileName, Loc, RealFileName};
-use std::cmp::{Ord, Ordering};
-use std::fmt;
-use std::path::PathBuf;
+use std::cmp::Ord;
rustc_index::newtype_index! {
pub struct ExpressionOperandId {
}
}
-#[derive(Clone, Debug)]
-pub struct Region {
- start: Loc,
- end: Loc,
-}
-
-impl Ord for Region {
- fn cmp(&self, other: &Self) -> Ordering {
- (&self.start.file.name, &self.start.line, &self.start.col, &self.end.line, &self.end.col)
- .cmp(&(
- &other.start.file.name,
- &other.start.line,
- &other.start.col,
- &other.end.line,
- &other.end.col,
- ))
- }
-}
-
-impl PartialOrd for Region {
- fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
- Some(self.cmp(other))
- }
-}
-
-impl PartialEq for Region {
- fn eq(&self, other: &Self) -> bool {
- self.start.file.name == other.start.file.name
- && self.start.line == other.start.line
- && self.start.col == other.start.col
- && self.end.line == other.end.line
- && self.end.col == other.end.col
- }
-}
-
-impl Eq for Region {}
-
-impl fmt::Display for Region {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- let (file_path, start_line, start_col, end_line, end_col) = self.file_start_and_end();
- write!(f, "{:?}:{}:{} - {}:{}", file_path, start_line, start_col, end_line, end_col)
- }
+#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
+pub struct Region<'tcx> {
+ pub file_name: &'tcx str,
+ pub start_line: u32,
+ pub start_col: u32,
+ pub end_line: u32,
+ pub end_col: u32,
}
-impl Region {
- pub fn new(source_map: &SourceMap, start_byte_pos: u32, end_byte_pos: u32) -> Self {
- let start = source_map.lookup_char_pos(BytePos::from_u32(start_byte_pos));
- let end = source_map.lookup_char_pos(BytePos::from_u32(end_byte_pos));
- assert_eq!(
- start.file.name, end.file.name,
- "Region start ({} -> {:?}) and end ({} -> {:?}) don't come from the same source file!",
- start_byte_pos, start, end_byte_pos, end
- );
- Self { start, end }
- }
-
- pub fn file_start_and_end<'a>(&'a self) -> (&'a PathBuf, u32, u32, u32, u32) {
- let start = &self.start;
- let end = &self.end;
- match &start.file.name {
- FileName::Real(RealFileName::Named(path)) => (
- path,
- start.line as u32,
- start.col.to_u32() + 1,
- end.line as u32,
- end.col.to_u32() + 1,
- ),
- _ => {
- bug!("start.file.name should be a RealFileName, but it was: {:?}", start.file.name)
- }
- }
+impl<'tcx> Region<'tcx> {
+ pub fn new(
+ file_name: &'tcx str,
+ start_line: u32,
+ start_col: u32,
+ end_line: u32,
+ end_col: u32,
+ ) -> Self {
+ Self { file_name, start_line, start_col, end_line, end_col }
}
}
#[derive(Clone, Debug)]
-pub struct ExpressionRegion {
+pub struct ExpressionRegion<'tcx> {
lhs: ExpressionOperandId,
op: ExprKind,
rhs: ExpressionOperandId,
- region: Region,
+ region: Region<'tcx>,
}
-// FIXME(richkadel): There seems to be a problem computing the file location in
-// some cases. I need to investigate this more. When I generate and show coverage
-// for the example binary in the crates.io crate `json5format`, I had a couple of
-// notable problems:
-//
-// 1. I saw a lot of coverage spans in `llvm-cov show` highlighting regions in
-// various comments (not corresponding to rustdoc code), indicating a possible
-// problem with the byte_pos-to-source-map implementation.
-//
-// 2. And (perhaps not related) when I build the aforementioned example binary with:
-// `RUST_FLAGS="-Zinstrument-coverage" cargo build --example formatjson5`
-// and then run that binary with
-// `LLVM_PROFILE_FILE="formatjson5.profraw" ./target/debug/examples/formatjson5 \
-// some.json5` for some reason the binary generates *TWO* `.profraw` files. One
-// named `default.profraw` and the other named `formatjson5.profraw` (the expected
-// name, in this case).
-//
-// 3. I think that if I eliminate regions within a function, their region_ids,
-// referenced in expressions, will be wrong? I think the ids are implied by their
-// array position in the final coverage map output (IIRC).
-//
-// 4. I suspect a problem (if not the only problem) is the SourceMap is wrong for some
-// region start/end byte positions. Just like I couldn't get the function hash at
-// intrinsic codegen time for external crate functions, I think the SourceMap I
-// have here only applies to the local crate, and I know I have coverages that
-// reference external crates.
-//
-// I still don't know if I fixed the hash problem correctly. If external crates
-// implement the function, can't I use the coverage counters already compiled
-// into those external crates? (Maybe not for generics and/or maybe not for
-// macros... not sure. But I need to understand this better.)
-//
-// If the byte range conversion is wrong, fix it. But if it
-// is right, then it is possible for the start and end to be in different files.
-// Can I do something other than ignore coverages that span multiple files?
-//
-// If I can resolve this, remove the "Option<>" result type wrapper
-// `regions_in_file_order()` accordingly.
-
/// Collects all of the coverage regions associated with (a) injected counters, (b) counter
/// expressions (additions or subtraction), and (c) unreachable regions (always counted as zero),
/// for a given Function. Counters and counter expressions have non-overlapping `id`s because they
/// only whitespace or comments). According to LLVM Code Coverage Mapping documentation, "A count
/// for a gap area is only used as the line execution count if there are no other regions on a
/// line."
-pub struct FunctionCoverage<'a> {
- source_map: &'a SourceMap,
+pub struct FunctionCoverage<'tcx> {
source_hash: u64,
- counters: IndexVec<CounterValueReference, Option<Region>>,
- expressions: IndexVec<InjectedExpressionIndex, Option<ExpressionRegion>>,
- unreachable_regions: Vec<Region>,
+ counters: IndexVec<CounterValueReference, Option<Region<'tcx>>>,
+ expressions: IndexVec<InjectedExpressionIndex, Option<ExpressionRegion<'tcx>>>,
+ unreachable_regions: Vec<Region<'tcx>>,
}
-impl<'a> FunctionCoverage<'a> {
- pub fn new<'tcx: 'a>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> Self {
+impl<'tcx> FunctionCoverage<'tcx> {
+ pub fn new(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> Self {
let coverageinfo = tcx.coverageinfo(instance.def_id());
Self {
- source_map: tcx.sess.source_map(),
source_hash: 0, // will be set with the first `add_counter()`
counters: IndexVec::from_elem_n(None, coverageinfo.num_counters as usize),
expressions: IndexVec::from_elem_n(None, coverageinfo.num_expressions as usize),
/// Adds a code region to be counted by an injected counter intrinsic.
/// The source_hash (computed during coverage instrumentation) should also be provided, and
/// should be the same for all counters in a given function.
- pub fn add_counter(
- &mut self,
- source_hash: u64,
- id: u32,
- start_byte_pos: u32,
- end_byte_pos: u32,
- ) {
+ pub fn add_counter(&mut self, source_hash: u64, id: u32, region: Region<'tcx>) {
if self.source_hash == 0 {
self.source_hash = source_hash;
} else {
debug_assert_eq!(source_hash, self.source_hash);
}
self.counters[CounterValueReference::from(id)]
- .replace(Region::new(self.source_map, start_byte_pos, end_byte_pos))
+ .replace(region)
.expect_none("add_counter called with duplicate `id`");
}
lhs: u32,
op: ExprKind,
rhs: u32,
- start_byte_pos: u32,
- end_byte_pos: u32,
+ region: Region<'tcx>,
) {
let expression_id = ExpressionOperandId::from(id_descending_from_max);
let lhs = ExpressionOperandId::from(lhs);
let expression_index = self.expression_index(expression_id);
self.expressions[expression_index]
- .replace(ExpressionRegion {
- lhs,
- op,
- rhs,
- region: Region::new(self.source_map, start_byte_pos, end_byte_pos),
- })
+ .replace(ExpressionRegion { lhs, op, rhs, region })
.expect_none("add_counter_expression called with duplicate `id_descending_from_max`");
}
/// Add a region that will be marked as "unreachable", with a constant "zero counter".
- pub fn add_unreachable_region(&mut self, start_byte_pos: u32, end_byte_pos: u32) {
- self.unreachable_regions.push(Region::new(self.source_map, start_byte_pos, end_byte_pos));
+ pub fn add_unreachable_region(&mut self, region: Region<'tcx>) {
+ self.unreachable_regions.push(region)
}
/// Return the source hash, generated from the HIR node structure, and used to indicate whether
/// associated `Regions` (from which the LLVM-specific `CoverageMapGenerator` will create
/// `CounterMappingRegion`s.
pub fn get_expressions_and_counter_regions(
- &'a self,
- ) -> (Vec<CounterExpression>, impl Iterator<Item = (Counter, &'a Region)>) {
+ &'tcx self,
+ ) -> (Vec<CounterExpression>, impl Iterator<Item = (Counter, &'tcx Region<'tcx>)>) {
assert!(self.source_hash != 0);
let counter_regions = self.counter_regions();
(counter_expressions, counter_regions)
}
- fn counter_regions(&'a self) -> impl Iterator<Item = (Counter, &'a Region)> {
+ fn counter_regions(&'tcx self) -> impl Iterator<Item = (Counter, &'tcx Region<'tcx>)> {
self.counters.iter_enumerated().filter_map(|(index, entry)| {
// Option::map() will return None to filter out missing counters. This may happen
// if, for example, a MIR-instrumented counter is removed during an optimization.
}
fn expressions_with_regions(
- &'a self,
- ) -> (Vec<CounterExpression>, impl Iterator<Item = (Counter, &'a Region)>) {
+ &'tcx self,
+ ) -> (Vec<CounterExpression>, impl Iterator<Item = (Counter, &'tcx Region<'tcx>)>) {
let mut counter_expressions = Vec::with_capacity(self.expressions.len());
let mut expression_regions = Vec::with_capacity(self.expressions.len());
let mut new_indexes =
(counter_expressions, expression_regions.into_iter())
}
- fn unreachable_regions(&'a self) -> impl Iterator<Item = (Counter, &'a Region)> {
+ fn unreachable_regions(&'tcx self) -> impl Iterator<Item = (Counter, &'tcx Region<'tcx>)> {
self.unreachable_regions.iter().map(|region| (Counter::zero(), region))
}
pub mod map;
pub use map::ExprKind;
+pub use map::Region;
bx.sub(tag, bx.cx().const_uint_big(niche_llty, niche_start))
};
let relative_max = niche_variants.end().as_u32() - niche_variants.start().as_u32();
- let is_niche = {
- let relative_max = if relative_max == 0 {
- // Avoid calling `const_uint`, which wouldn't work for pointers.
- // FIXME(eddyb) check the actual primitive type here.
- bx.cx().const_null(niche_llty)
- } else {
- bx.cx().const_uint(niche_llty, relative_max as u64)
- };
+ let is_niche = if relative_max == 0 {
+ // Avoid calling `const_uint`, which wouldn't work for pointers.
+ // Also use canonical == 0 instead of non-canonical u<= 0.
+ // FIXME(eddyb) check the actual primitive type here.
+ bx.icmp(IntPredicate::IntEQ, relative_discr, bx.cx().const_null(niche_llty))
+ } else {
+ let relative_max = bx.cx().const_uint(niche_llty, relative_max as u64);
bx.icmp(IntPredicate::IntULE, relative_discr, relative_max)
};
use super::BackendTypes;
-use crate::coverageinfo::ExprKind;
+use crate::coverageinfo::{ExprKind, Region};
use rustc_middle::ty::Instance;
pub trait CoverageInfoMethods: BackendTypes {
instance: Instance<'tcx>,
function_source_hash: u64,
index: u32,
- start_byte_pos: u32,
- end_byte_pos: u32,
+ region: Region<'tcx>,
);
fn add_counter_expression_region(
lhs: u32,
op: ExprKind,
rhs: u32,
- start_byte_pos: u32,
- end_byte_pos: u32,
+ region: Region<'tcx>,
);
- fn add_unreachable_region(
- &mut self,
- instance: Instance<'tcx>,
- start_byte_pos: u32,
- end_byte_pos: u32,
- );
+ fn add_unreachable_region(&mut self, instance: Instance<'tcx>, region: Region<'tcx>);
}
let t_outputs = rustc_interface::util::build_output_filenames(
input, odir, ofile, attrs, sess,
);
- let id = rustc_session::output::find_crate_name(Some(sess), attrs, input);
+ let id = rustc_session::output::find_crate_name(sess, attrs, input);
if *req == PrintRequest::CrateName {
println!("{}", id);
continue;
```compile_fail,E0271
trait Trait { type AssociatedType; }
-fn foo<T>(t: T) where T: Trait<AssociatedType=u32> {
- println!("in foo");
-}
-
-impl Trait for i8 { type AssociatedType = &'static str; }
-
-foo(3_i8);
-```
-
-This is because of a type mismatch between the associated type of some
-trait (e.g., `T::Bar`, where `T` implements `trait Quux { type Bar; }`)
-and another type `U` that is required to be equal to `T::Bar`, but is not.
-Examples follow.
-
-Here is that same example again, with some explanatory comments:
-
-```compile_fail,E0271
-trait Trait { type AssociatedType; }
-
fn foo<T>(t: T) where T: Trait<AssociatedType=u32> {
// ~~~~~~~~ ~~~~~~~~~~~~~~~~~~
// | |
// therefore the type-checker complains with this error code.
```
-To avoid those issues, you have to make the types match correctly.
-So we can fix the previous examples like this:
-
+The issue can be resolved by changing the associated type:
+1) in the `foo` implementation:
```
-// Basic Example:
trait Trait { type AssociatedType; }
fn foo<T>(t: T) where T: Trait<AssociatedType = &'static str> {
impl Trait for i8 { type AssociatedType = &'static str; }
foo(3_i8);
+```
-// For-Loop Example:
-let vs = vec![1, 2, 3, 4];
-for v in &vs {
- match v {
- &1 => {}
- _ => {}
- }
+2) in the `Trait` implementation for `i8`:
+```
+trait Trait { type AssociatedType; }
+
+fn foo<T>(t: T) where T: Trait<AssociatedType = u32> {
+ println!("in foo");
}
+
+impl Trait for i8 { type AssociatedType = u32; }
+
+foo(3_i8);
```
```compile_fail,E0502
fn bar(x: &mut i32) {}
fn foo(a: &mut i32) {
- let ref y = a; // a is borrowed as immutable.
+ let y = &a; // a is borrowed as immutable.
bar(a); // error: cannot borrow `*a` as mutable because `a` is also borrowed
// as immutable
println!("{}", y);
fn bar(x: &mut i32) {}
fn foo(a: &mut i32) {
bar(a);
- let ref y = a; // ok!
+ let y = &a; // ok!
println!("{}", y);
}
```
-Cannot take address of temporary value.
+The address of temporary value was taken.
Erroneous code example:
```compile_fail,E0745
# #![feature(raw_ref_op)]
fn temp_address() {
- let ptr = &raw const 2; // ERROR
+ let ptr = &raw const 2; // error!
}
```
-To avoid the error, first bind the temporary to a named local variable.
+In this example, `2` is destroyed right after the assignment, which means that
+`ptr` now points to an unavailable location.
+
+To avoid this error, first bind the temporary to a named local variable:
```
# #![feature(raw_ref_op)]
fn temp_address() {
let val = 2;
- let ptr = &raw const val;
+ let ptr = &raw const val; // ok!
}
```
-Return types cannot be `dyn Trait`s as they must be `Sized`.
+An unboxed trait object was used as a return value.
Erroneous code example:
// Having the trait `T` as return type is invalid because
// unboxed trait objects do not have a statically known size:
-fn foo() -> dyn T {
+fn foo() -> dyn T { // error!
S(42)
}
```
+Return types cannot be `dyn Trait`s as they must be `Sized`.
+
To avoid the error there are a couple of options.
If there is a single type involved, you can use [`impl Trait`]:
# }
// The compiler will select `S(usize)` as the materialized return type of this
// function, but callers will only know that the return type implements `T`.
-fn foo() -> impl T {
+fn foo() -> impl T { // ok!
S(42)
}
```
// This now returns a "trait object" and callers are only be able to access
// associated items from `T`.
-fn foo(x: bool) -> Box<dyn T> {
+fn foo(x: bool) -> Box<dyn T> { // ok!
if x {
Box::new(S(42))
} else {
-Generic arguments must be provided in the same order as the corresponding
+Generic arguments were not provided in the same order as the corresponding
generic parameters are declared.
Erroneous code example:
```
The argument order should be changed to match the parameter declaration
-order, as in the following.
+order, as in the following:
```
struct S<'a, T>(&'a T);
-Negative impls cannot be default impls. A default impl supplies
-default values for the items within to be used by other impls, whereas
-a negative impl declares that there are no other impls. These don't
-make sense to combine.
+A negative impl was made default impl.
+
+Erroneous code example:
+
+```compile_fail,E0750
+# #![feature(negative_impls)]
+# #![feature(specialization)]
+trait MyTrait {
+ type Foo;
+}
+
+default impl !MyTrait for u32 {} // error!
+# fn main() {}
+```
+
+Negative impls cannot be default impls. A default impl supplies default values
+for the items within to be used by other impls, whereas a negative impl declares
+that there are no other impls. Combining it does not make sense.
format!(
"in this expansion of `{}`{}",
trace.kind.descr(),
- if macro_backtrace.len() > 2 {
+ if macro_backtrace.len() > 1 {
// if macro_backtrace.len() == 1 it'll be
// pointed at by "in this macro invocation"
format!(" (#{})", i + 1)
trace.call_site,
format!(
"in this macro invocation{}",
- if macro_backtrace.len() > 2 && always_backtrace {
+ if macro_backtrace.len() > 1 && always_backtrace {
// only specify order when the macro
// backtrace is multiple levels deep
format!(" (#{})", i + 1)
use rustc_data_structures::sync::{self, Lrc};
use rustc_errors::{DiagnosticBuilder, ErrorReported};
use rustc_parse::{self, nt_to_tokenstream, parser, MACRO_ARGUMENTS};
-use rustc_session::{parse::ParseSess, Limit};
+use rustc_session::{parse::ParseSess, Limit, Session};
use rustc_span::def_id::{DefId, LOCAL_CRATE};
use rustc_span::edition::Edition;
use rustc_span::hygiene::{AstPass, ExpnData, ExpnId, ExpnKind};
/// Constructs a syntax extension with the given properties
/// and other properties converted from attributes.
pub fn new(
- sess: &ParseSess,
+ sess: &Session,
kind: SyntaxExtensionKind,
span: Span,
helper_attrs: Vec<Symbol>,
name: Symbol,
attrs: &[ast::Attribute],
) -> SyntaxExtension {
- let allow_internal_unstable = attr::allow_internal_unstable(&attrs, &sess.span_diagnostic)
+ let allow_internal_unstable = attr::allow_internal_unstable(sess, &attrs)
.map(|features| features.collect::<Vec<Symbol>>().into());
let mut local_inner_macros = false;
- if let Some(macro_export) = attr::find_by_name(attrs, sym::macro_export) {
+ if let Some(macro_export) = sess.find_by_name(attrs, sym::macro_export) {
if let Some(l) = macro_export.meta_item_list() {
local_inner_macros = attr::list_contains_name(&l, sym::local_inner_macros);
}
}
- let is_builtin = attr::contains_name(attrs, sym::rustc_builtin_macro);
+ let is_builtin = sess.contains_name(attrs, sym::rustc_builtin_macro);
let (stability, const_stability) = attr::find_stability(&sess, attrs, span);
if const_stability.is_some() {
- sess.span_diagnostic.span_err(span, "macros cannot have const stability attributes");
+ sess.parse_sess
+ .span_diagnostic
+ .span_err(span, "macros cannot have const stability attributes");
}
SyntaxExtension {
kind,
span,
allow_internal_unstable,
- allow_internal_unsafe: attr::contains_name(attrs, sym::allow_internal_unsafe),
+ allow_internal_unsafe: sess.contains_name(attrs, sym::allow_internal_unsafe),
local_inner_macros,
stability,
deprecation: attr::find_deprecation(&sess, attrs, span),
/// when a macro expansion occurs, the resulting nodes have the `backtrace()
/// -> expn_data` of their expansion context stored into their span.
pub struct ExtCtxt<'a> {
- pub parse_sess: &'a ParseSess,
+ pub sess: &'a Session,
pub ecfg: expand::ExpansionConfig<'a>,
pub reduced_recursion_limit: Option<Limit>,
pub root_path: PathBuf,
impl<'a> ExtCtxt<'a> {
pub fn new(
- parse_sess: &'a ParseSess,
+ sess: &'a Session,
ecfg: expand::ExpansionConfig<'a>,
resolver: &'a mut dyn ResolverExpand,
extern_mod_loaded: Option<&'a dyn Fn(&ast::Crate)>,
) -> ExtCtxt<'a> {
ExtCtxt {
- parse_sess,
+ sess,
ecfg,
reduced_recursion_limit: None,
resolver,
expand::MacroExpander::new(self, true)
}
pub fn new_parser_from_tts(&self, stream: TokenStream) -> parser::Parser<'a> {
- rustc_parse::stream_to_parser(self.parse_sess, stream, MACRO_ARGUMENTS)
+ rustc_parse::stream_to_parser(&self.sess.parse_sess, stream, MACRO_ARGUMENTS)
}
pub fn source_map(&self) -> &'a SourceMap {
- self.parse_sess.source_map()
+ self.sess.parse_sess.source_map()
}
pub fn parse_sess(&self) -> &'a ParseSess {
- self.parse_sess
+ &self.sess.parse_sess
}
pub fn call_site(&self) -> Span {
self.current_expansion.id.expn_data().call_site
}
pub fn struct_span_err<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> DiagnosticBuilder<'a> {
- self.parse_sess.span_diagnostic.struct_span_err(sp, msg)
+ self.sess.parse_sess.span_diagnostic.struct_span_err(sp, msg)
}
/// Emit `msg` attached to `sp`, without immediately stopping
/// Compilation will be stopped in the near future (at the end of
/// the macro expansion phase).
pub fn span_err<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
- self.parse_sess.span_diagnostic.span_err(sp, msg);
+ self.sess.parse_sess.span_diagnostic.span_err(sp, msg);
}
pub fn span_warn<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
- self.parse_sess.span_diagnostic.span_warn(sp, msg);
+ self.sess.parse_sess.span_diagnostic.span_warn(sp, msg);
}
pub fn span_bug<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> ! {
- self.parse_sess.span_diagnostic.span_bug(sp, msg);
+ self.sess.parse_sess.span_diagnostic.span_bug(sp, msg);
}
pub fn trace_macros_diag(&mut self) {
for (sp, notes) in self.expansions.iter() {
- let mut db = self.parse_sess.span_diagnostic.span_note_diag(*sp, "trace_macro");
+ let mut db = self.sess.parse_sess.span_diagnostic.span_note_diag(*sp, "trace_macro");
for note in notes {
db.note(note);
}
self.expansions.clear();
}
pub fn bug(&self, msg: &str) -> ! {
- self.parse_sess.span_diagnostic.bug(msg);
+ self.sess.parse_sess.span_diagnostic.bug(msg);
}
pub fn trace_macros(&self) -> bool {
self.ecfg.trace_mac
ACCEPTED_FEATURES, ACTIVE_FEATURES, REMOVED_FEATURES, STABLE_REMOVED_FEATURES,
};
use rustc_parse::{parse_in, validate_attr};
-use rustc_session::parse::{feature_err, ParseSess};
+use rustc_session::parse::feature_err;
+use rustc_session::Session;
use rustc_span::edition::{Edition, ALL_EDITIONS};
use rustc_span::symbol::{sym, Symbol};
use rustc_span::{Span, DUMMY_SP};
/// A folder that strips out items that do not belong in the current configuration.
pub struct StripUnconfigured<'a> {
- pub sess: &'a ParseSess,
+ pub sess: &'a Session,
pub features: Option<&'a Features>,
}
fn get_features(
+ sess: &Session,
span_handler: &Handler,
krate_attrs: &[ast::Attribute],
- crate_edition: Edition,
- allow_features: &Option<Vec<String>>,
) -> Features {
fn feature_removed(span_handler: &Handler, span: Span, reason: Option<&str>) {
let mut err = struct_span_err!(span_handler, span, E0557, "feature has been removed");
let mut features = Features::default();
let mut edition_enabled_features = FxHashMap::default();
+ let crate_edition = sess.edition();
for &edition in ALL_EDITIONS {
if edition <= crate_edition {
// Process the edition umbrella feature-gates first, to ensure
// `edition_enabled_features` is completed before it's queried.
for attr in krate_attrs {
- if !attr.check_name(sym::feature) {
+ if !sess.check_name(attr, sym::feature) {
continue;
}
}
for attr in krate_attrs {
- if !attr.check_name(sym::feature) {
+ if !sess.check_name(attr, sym::feature) {
continue;
}
continue;
}
- if let Some(allowed) = allow_features.as_ref() {
+ if let Some(allowed) = sess.opts.debugging_opts.allow_features.as_ref() {
if allowed.iter().find(|&f| name.as_str() == *f).is_none() {
struct_span_err!(
span_handler,
}
// `cfg_attr`-process the crate's attributes and compute the crate's features.
-pub fn features(
- mut krate: ast::Crate,
- sess: &ParseSess,
- edition: Edition,
- allow_features: &Option<Vec<String>>,
-) -> (ast::Crate, Features) {
+pub fn features(sess: &Session, mut krate: ast::Crate) -> (ast::Crate, Features) {
let mut strip_unconfigured = StripUnconfigured { sess, features: None };
let unconfigured_attrs = krate.attrs.clone();
- let diag = &sess.span_diagnostic;
+ let diag = &sess.parse_sess.span_diagnostic;
let err_count = diag.err_count();
let features = match strip_unconfigured.configure(krate.attrs) {
None => {
}
Some(attrs) => {
krate.attrs = attrs;
- let features = get_features(diag, &krate.attrs, edition, allow_features);
+ let features = get_features(sess, diag, &krate.attrs);
if err_count == diag.err_count() {
// Avoid reconfiguring malformed `cfg_attr`s.
strip_unconfigured.features = Some(&features);
}
// At this point we know the attribute is considered used.
- attr::mark_used(&attr);
+ self.sess.mark_attr_used(&attr);
- if !attr::cfg_matches(&cfg_predicate, self.sess, self.features) {
+ if !attr::cfg_matches(&cfg_predicate, &self.sess.parse_sess, self.features) {
return vec![];
}
match attr.get_normal_item().args {
ast::MacArgs::Delimited(dspan, delim, ref tts) if !tts.is_empty() => {
let msg = "wrong `cfg_attr` delimiters";
- validate_attr::check_meta_bad_delim(self.sess, dspan, delim, msg);
- match parse_in(self.sess, tts.clone(), "`cfg_attr` input", |p| p.parse_cfg_attr()) {
+ validate_attr::check_meta_bad_delim(&self.sess.parse_sess, dspan, delim, msg);
+ match parse_in(&self.sess.parse_sess, tts.clone(), "`cfg_attr` input", |p| {
+ p.parse_cfg_attr()
+ }) {
Ok(r) => return Some(r),
Err(mut e) => {
e.help(&format!("the valid syntax is `{}`", CFG_ATTR_GRAMMAR_HELP))
fn error_malformed_cfg_attr_missing(&self, span: Span) {
self.sess
+ .parse_sess
.span_diagnostic
.struct_span_err(span, "malformed `cfg_attr` attribute input")
.span_suggestion(
/// Determines if a node with the given attributes should be included in this configuration.
pub fn in_cfg(&self, attrs: &[Attribute]) -> bool {
attrs.iter().all(|attr| {
- if !is_cfg(attr) {
+ if !is_cfg(self.sess, attr) {
return true;
}
- let meta_item = match validate_attr::parse_meta(self.sess, attr) {
+ let meta_item = match validate_attr::parse_meta(&self.sess.parse_sess, attr) {
Ok(meta_item) => meta_item,
Err(mut err) => {
err.emit();
}
};
let error = |span, msg, suggestion: &str| {
- let mut err = self.sess.span_diagnostic.struct_span_err(span, msg);
+ let mut err = self.sess.parse_sess.span_diagnostic.struct_span_err(span, msg);
if !suggestion.is_empty() {
err.span_suggestion(
span,
Some([]) => error(span, "`cfg` predicate is not specified", ""),
Some([_, .., l]) => error(l.span(), "multiple `cfg` predicates are specified", ""),
Some([single]) => match single.meta_item() {
- Some(meta_item) => attr::cfg_matches(meta_item, self.sess, self.features),
+ Some(meta_item) => {
+ attr::cfg_matches(meta_item, &self.sess.parse_sess, self.features)
+ }
None => error(single.span(), "`cfg` predicate key cannot be a literal", ""),
},
}
pub fn maybe_emit_expr_attr_err(&self, attr: &Attribute) {
if !self.features.map(|features| features.stmt_expr_attributes).unwrap_or(true) {
let mut err = feature_err(
- self.sess,
+ &self.sess.parse_sess,
sym::stmt_expr_attributes,
attr.span,
"attributes on expressions are experimental",
//
// N.B., this is intentionally not part of the visit_expr() function
// in order for filter_map_expr() to be able to avoid this check
- if let Some(attr) = expr.attrs().iter().find(|a| is_cfg(a)) {
+ if let Some(attr) = expr.attrs().iter().find(|a| is_cfg(self.sess, a)) {
let msg = "removing an expression is not supported in this position";
- self.sess.span_diagnostic.span_err(attr.span, msg);
+ self.sess.parse_sess.span_diagnostic.span_err(attr.span, msg);
}
self.process_cfg_attrs(expr)
}
}
-fn is_cfg(attr: &Attribute) -> bool {
- attr.check_name(sym::cfg)
+fn is_cfg(sess: &Session, attr: &Attribute) -> bool {
+ sess.check_name(attr, sym::cfg)
}
}
fn error_derive_forbidden_on_non_adt(&self, derives: &[Path], item: &Annotatable) {
- let attr = attr::find_by_name(item.attrs(), sym::derive);
+ let attr = self.cx.sess.find_by_name(item.attrs(), sym::derive);
let span = attr.map_or(item.span(), |attr| attr.span);
let mut err = self
.cx
let invocations = {
let mut collector = InvocationCollector {
- cfg: StripUnconfigured {
- sess: self.cx.parse_sess,
- features: self.cx.ecfg.features,
- },
+ cfg: StripUnconfigured { sess: &self.cx.sess, features: self.cx.ecfg.features },
cx: self.cx,
invocations: Vec::new(),
monotonic: self.monotonic,
}
fn fully_configure(&mut self, item: Annotatable) -> Annotatable {
- let mut cfg =
- StripUnconfigured { sess: self.cx.parse_sess, features: self.cx.ecfg.features };
+ let mut cfg = StripUnconfigured { sess: &self.cx.sess, features: self.cx.ecfg.features };
// Since the item itself has already been configured by the InvocationCollector,
// we know that fold result vector will contain exactly one element
match item {
SyntaxExtensionKind::Attr(expander) => {
self.gate_proc_macro_input(&item);
self.gate_proc_macro_attr_item(span, &item);
- let tokens = item.into_tokens(self.cx.parse_sess);
+ let tokens = item.into_tokens(&self.cx.sess.parse_sess);
let attr_item = attr.unwrap_normal_item();
if let MacArgs::Eq(..) = attr_item.args {
self.cx.span_err(span, "key-value macro attributes are not supported");
self.parse_ast_fragment(tok_result, fragment_kind, &attr_item.path, span)
}
SyntaxExtensionKind::LegacyAttr(expander) => {
- match validate_attr::parse_meta(self.cx.parse_sess, &attr) {
+ match validate_attr::parse_meta(&self.cx.sess.parse_sess, &attr) {
Ok(meta) => {
let items = match expander.expand(self.cx, span, &meta, item) {
ExpandResult::Ready(items) => items,
}
}
SyntaxExtensionKind::NonMacroAttr { mark_used } => {
- attr::mark_known(&attr);
+ self.cx.sess.mark_attr_known(&attr);
if *mark_used {
- attr::mark_used(&attr);
+ self.cx.sess.mark_attr_used(&attr);
}
item.visit_attrs(|attrs| attrs.push(attr));
fragment_kind.expect_from_annotatables(iter::once(item))
return;
}
feature_err(
- self.cx.parse_sess,
+ &self.cx.sess.parse_sess,
sym::proc_macro_hygiene,
span,
&format!("custom attributes cannot be applied to {}", kind),
}
if !self.cx.ecfg.proc_macro_hygiene() {
- annotatable.visit_with(&mut GateProcMacroInput { parse_sess: self.cx.parse_sess });
+ annotatable
+ .visit_with(&mut GateProcMacroInput { parse_sess: &self.cx.sess.parse_sess });
}
}
..ExpnData::default(
ExpnKind::Macro(MacroKind::Attr, sym::derive),
item.span(),
- self.cx.parse_sess.edition,
+ self.cx.sess.parse_sess.edition,
None,
)
}),
if a.has_name(sym::derive) {
*after_derive = true;
}
- !attr::is_known(a) && !is_builtin_attr(a)
+ !self.cx.sess.is_attr_known(a) && !is_builtin_attr(a)
})
.map(|i| attrs.remove(i));
if let Some(attr) = &attr {
&& !attr.has_name(sym::test)
{
feature_err(
- &self.cx.parse_sess,
+ &self.cx.sess.parse_sess,
sym::custom_inner_attributes,
attr.span,
"non-builtin inner attributes are unstable",
fn check_attributes(&mut self, attrs: &[ast::Attribute]) {
let features = self.cx.ecfg.features.unwrap();
for attr in attrs.iter() {
- rustc_ast_passes::feature_gate::check_attribute(attr, self.cx.parse_sess, features);
- validate_attr::check_meta(self.cx.parse_sess, attr);
+ rustc_ast_passes::feature_gate::check_attribute(attr, self.cx.sess, features);
+ validate_attr::check_meta(&self.cx.sess.parse_sess, attr);
// macros are expanded before any lint passes so this warning has to be hardcoded
if attr.has_name(sym::derive) {
}
if attr.doc_str().is_some() {
- self.cx.parse_sess.buffer_lint_with_diagnostic(
+ self.cx.sess.parse_sess.buffer_lint_with_diagnostic(
&UNUSED_DOC_COMMENTS,
attr.span,
ast::CRATE_NODE_ID,
})
}
ast::ItemKind::Mod(ref mut old_mod @ ast::Mod { .. }) if ident != Ident::invalid() => {
- let sess = self.cx.parse_sess;
+ let sess = &self.cx.sess.parse_sess;
let orig_ownership = self.cx.current_expansion.directory_ownership;
let mut module = (*self.cx.current_expansion.module).clone();
let Directory { ownership, path } = if old_mod.inline {
// Inline `mod foo { ... }`, but we still need to push directories.
item.attrs = attrs;
- push_directory(ident, &item.attrs, dir)
+ push_directory(&self.cx.sess, ident, &item.attrs, dir)
} else {
// We have an outline `mod foo;` so we need to parse the file.
let (new_mod, dir) =
- parse_external_mod(sess, ident, span, dir, &mut attrs, pushed);
+ parse_external_mod(&self.cx.sess, ident, span, dir, &mut attrs, pushed);
let krate = ast::Crate {
span: new_mod.inner,
fn visit_attribute(&mut self, at: &mut ast::Attribute) {
// turn `#[doc(include="filename")]` attributes into `#[doc(include(file="filename",
// contents="file contents")]` attributes
- if !at.check_name(sym::doc) {
+ if !self.cx.sess.check_name(at, sym::doc) {
return noop_visit_attribute(at, self);
}
}
if let Some(file) = it.value_str() {
- let err_count = self.cx.parse_sess.span_diagnostic.err_count();
+ let err_count = self.cx.sess.parse_sess.span_diagnostic.err_count();
self.check_attributes(slice::from_ref(at));
- if self.cx.parse_sess.span_diagnostic.err_count() > err_count {
+ if self.cx.sess.parse_sess.span_diagnostic.err_count() > err_count {
// avoid loading the file if they haven't enabled the feature
return noop_visit_attribute(at, self);
}
use rustc_feature::Features;
use rustc_parse::parser::Parser;
use rustc_session::parse::ParseSess;
+use rustc_session::Session;
use rustc_span::edition::Edition;
use rustc_span::hygiene::Transparency;
use rustc_span::symbol::{kw, sym, Ident, MacroRulesNormalizedIdent};
lhses: &[mbe::TokenTree],
rhses: &[mbe::TokenTree],
) -> Box<dyn MacResult + 'cx> {
- let sess = cx.parse_sess;
+ let sess = &cx.sess.parse_sess;
if cx.trace_macros() {
let msg = format!("expanding `{}! {{ {} }}`", name, pprust::tts_to_string(&arg));
/// Converts a macro item into a syntax extension.
pub fn compile_declarative_macro(
- sess: &ParseSess,
+ sess: &Session,
features: &Features,
def: &ast::Item,
edition: Edition,
)
};
- let diag = &sess.span_diagnostic;
+ let diag = &sess.parse_sess.span_diagnostic;
let lhs_nm = Ident::new(sym::lhs, def.span);
let rhs_nm = Ident::new(sym::rhs, def.span);
let tt_spec = Some(NonterminalKind::TT);
),
];
- let parser = Parser::new(sess, body, true, rustc_parse::MACRO_ARGUMENTS);
+ let parser = Parser::new(&sess.parse_sess, body, true, rustc_parse::MACRO_ARGUMENTS);
let argument_map = match parse_tt(&mut Cow::Borrowed(&parser), &argument_gram) {
Success(m) => m,
Failure(token, msg) => {
let s = parse_failure_msg(&token);
let sp = token.span.substitute_dummy(def.span);
- sess.span_diagnostic.struct_span_err(sp, &s).span_label(sp, msg).emit();
+ sess.parse_sess.span_diagnostic.struct_span_err(sp, &s).span_label(sp, msg).emit();
return mk_syn_ext(Box::new(macro_rules_dummy_expander));
}
Error(sp, msg) => {
- sess.span_diagnostic.struct_span_err(sp.substitute_dummy(def.span), &msg).emit();
+ sess.parse_sess
+ .span_diagnostic
+ .struct_span_err(sp.substitute_dummy(def.span), &msg)
+ .emit();
return mk_syn_ext(Box::new(macro_rules_dummy_expander));
}
ErrorReported => {
.map(|m| {
if let MatchedNonterminal(ref nt) = *m {
if let NtTT(ref tt) = **nt {
- let tt = mbe::quoted::parse(tt.clone().into(), true, sess, def.id)
- .pop()
- .unwrap();
- valid &= check_lhs_nt_follows(sess, features, &def.attrs, &tt);
+ let tt =
+ mbe::quoted::parse(tt.clone().into(), true, &sess.parse_sess, def.id)
+ .pop()
+ .unwrap();
+ valid &= check_lhs_nt_follows(&sess.parse_sess, features, &def.attrs, &tt);
return tt;
}
}
- sess.span_diagnostic.span_bug(def.span, "wrong-structured lhs")
+ sess.parse_sess.span_diagnostic.span_bug(def.span, "wrong-structured lhs")
})
.collect::<Vec<mbe::TokenTree>>(),
- _ => sess.span_diagnostic.span_bug(def.span, "wrong-structured lhs"),
+ _ => sess.parse_sess.span_diagnostic.span_bug(def.span, "wrong-structured lhs"),
};
let rhses = match argument_map[&MacroRulesNormalizedIdent::new(rhs_nm)] {
.map(|m| {
if let MatchedNonterminal(ref nt) = *m {
if let NtTT(ref tt) = **nt {
- return mbe::quoted::parse(tt.clone().into(), false, sess, def.id)
- .pop()
- .unwrap();
+ return mbe::quoted::parse(
+ tt.clone().into(),
+ false,
+ &sess.parse_sess,
+ def.id,
+ )
+ .pop()
+ .unwrap();
}
}
- sess.span_diagnostic.span_bug(def.span, "wrong-structured lhs")
+ sess.parse_sess.span_diagnostic.span_bug(def.span, "wrong-structured lhs")
})
.collect::<Vec<mbe::TokenTree>>(),
- _ => sess.span_diagnostic.span_bug(def.span, "wrong-structured rhs"),
+ _ => sess.parse_sess.span_diagnostic.span_bug(def.span, "wrong-structured rhs"),
};
for rhs in &rhses {
- valid &= check_rhs(sess, rhs);
+ valid &= check_rhs(&sess.parse_sess, rhs);
}
// don't abort iteration early, so that errors for multiple lhses can be reported
for lhs in &lhses {
- valid &= check_lhs_no_empty_seq(sess, slice::from_ref(lhs));
+ valid &= check_lhs_no_empty_seq(&sess.parse_sess, slice::from_ref(lhs));
}
- valid &= macro_check::check_meta_variables(sess, def.id, def.span, &lhses, &rhses);
+ valid &= macro_check::check_meta_variables(&sess.parse_sess, def.id, def.span, &lhses, &rhses);
- let (transparency, transparency_error) = attr::find_transparency(&def.attrs, macro_rules);
+ let (transparency, transparency_error) = attr::find_transparency(sess, &def.attrs, macro_rules);
match transparency_error {
Some(TransparencyError::UnknownTransparency(value, span)) => {
diag.span_err(span, &format!("unknown macro transparency: `{}`", value))
use rustc_ast::ast::{Attribute, Mod};
-use rustc_ast::{attr, token};
+use rustc_ast::token;
use rustc_errors::{struct_span_err, PResult};
use rustc_parse::new_parser_from_file;
use rustc_session::parse::ParseSess;
+use rustc_session::Session;
use rustc_span::source_map::{FileName, Span};
use rustc_span::symbol::{sym, Ident};
}
crate fn parse_external_mod(
- sess: &ParseSess,
+ sess: &Session,
id: Ident,
span: Span, // The span to blame on errors.
Directory { mut ownership, path }: Directory,
ownership = mp.ownership;
// Ensure file paths are acyclic.
- let mut included_mod_stack = sess.included_mod_stack.borrow_mut();
- error_on_circular_module(sess, span, &mp.path, &included_mod_stack)?;
+ let mut included_mod_stack = sess.parse_sess.included_mod_stack.borrow_mut();
+ error_on_circular_module(&sess.parse_sess, span, &mp.path, &included_mod_stack)?;
included_mod_stack.push(mp.path.clone());
*pop_mod_stack = true; // We have pushed, so notify caller.
drop(included_mod_stack);
// Actually parse the external file as a module.
- let mut module = new_parser_from_file(sess, &mp.path, Some(span)).parse_mod(&token::Eof)?;
+ let mut module =
+ new_parser_from_file(&sess.parse_sess, &mp.path, Some(span)).parse_mod(&token::Eof)?;
module.0.inline = false;
module
};
}
crate fn push_directory(
+ sess: &Session,
id: Ident,
attrs: &[Attribute],
Directory { mut ownership, mut path }: Directory,
) -> Directory {
- if let Some(filename) = attr::first_attr_value_str_by_name(attrs, sym::path) {
+ if let Some(filename) = sess.first_attr_value_str_by_name(attrs, sym::path) {
path.push(&*filename.as_str());
ownership = DirectoryOwnership::Owned { relative: None };
} else {
}
fn submod_path<'a>(
- sess: &'a ParseSess,
+ sess: &'a Session,
id: Ident,
span: Span,
attrs: &[Attribute],
ownership: DirectoryOwnership,
dir_path: &Path,
) -> PResult<'a, ModulePathSuccess> {
- if let Some(path) = submod_path_from_attr(attrs, dir_path) {
+ if let Some(path) = submod_path_from_attr(sess, attrs, dir_path) {
let ownership = match path.file_name().and_then(|s| s.to_str()) {
// All `#[path]` files are treated as though they are a `mod.rs` file.
// This means that `mod foo;` declarations inside `#[path]`-included
DirectoryOwnership::UnownedViaBlock | DirectoryOwnership::UnownedViaMod => None,
};
let ModulePath { path_exists, name, result } =
- default_submod_path(sess, id, span, relative, dir_path);
+ default_submod_path(&sess.parse_sess, id, span, relative, dir_path);
match ownership {
DirectoryOwnership::Owned { .. } => Ok(result?),
DirectoryOwnership::UnownedViaBlock => {
let _ = result.map_err(|mut err| err.cancel());
- error_decl_mod_in_block(sess, span, path_exists, &name)
+ error_decl_mod_in_block(&sess.parse_sess, span, path_exists, &name)
}
DirectoryOwnership::UnownedViaMod => {
let _ = result.map_err(|mut err| err.cancel());
- error_cannot_declare_mod_here(sess, span, path_exists, &name)
+ error_cannot_declare_mod_here(&sess.parse_sess, span, path_exists, &name)
}
}
}
/// Derive a submodule path from the first found `#[path = "path_string"]`.
/// The provided `dir_path` is joined with the `path_string`.
// Public for rustfmt usage.
-pub fn submod_path_from_attr(attrs: &[Attribute], dir_path: &Path) -> Option<PathBuf> {
+pub fn submod_path_from_attr(
+ sess: &Session,
+ attrs: &[Attribute],
+ dir_path: &Path,
+) -> Option<PathBuf> {
// Extract path string from first `#[path = "path_string"]` attribute.
- let path_string = attr::first_attr_value_str_by_name(attrs, sym::path)?;
+ let path_string = sess.first_attr_value_str_by_name(attrs, sym::path)?;
let path_string = path_string.as_str();
// On windows, the base path might have the form
use rustc_ast::ast;
use rustc_ast::mut_visit::{self, MutVisitor};
-use rustc_ast::with_default_session_globals;
use rustc_ast_pretty::pprust;
use rustc_span::symbol::Ident;
+use rustc_span::with_default_session_globals;
// This version doesn't care about getting comments or doc-strings in.
fn fake_print_crate(s: &mut pprust::State<'_>, krate: &ast::Crate) {
-use rustc_ast::token::{self, Token, TokenKind};
-use rustc_ast::util::comments::is_doc_comment;
-use rustc_ast::with_default_session_globals;
+use rustc_ast::ast::AttrStyle;
+use rustc_ast::token::{self, CommentKind, Token, TokenKind};
use rustc_data_structures::sync::Lrc;
use rustc_errors::{emitter::EmitterWriter, Handler};
use rustc_parse::lexer::StringReader;
use rustc_session::parse::ParseSess;
use rustc_span::source_map::{FilePathMapping, SourceMap};
use rustc_span::symbol::Symbol;
+use rustc_span::with_default_session_globals;
use rustc_span::{BytePos, Span};
use std::io;
})
}
-#[test]
-fn line_doc_comments() {
- assert!(is_doc_comment("///"));
- assert!(is_doc_comment("/// blah"));
- assert!(!is_doc_comment("////"));
-}
-
#[test]
fn nested_block_comments() {
with_default_session_globals(|| {
assert_eq!(comment.kind, token::Comment);
assert_eq!((comment.span.lo(), comment.span.hi()), (BytePos(0), BytePos(7)));
assert_eq!(lexer.next_token(), token::Whitespace);
- assert_eq!(lexer.next_token(), token::DocComment(Symbol::intern("/// test")));
+ assert_eq!(
+ lexer.next_token(),
+ token::DocComment(CommentKind::Line, AttrStyle::Outer, Symbol::intern(" test"))
+ );
})
}
use rustc_ast::token::{self, Token};
use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree};
use rustc_ast::visit;
-use rustc_ast::with_default_session_globals;
use rustc_ast_pretty::pprust::item_to_string;
use rustc_errors::PResult;
use rustc_parse::new_parser_from_source_str;
use rustc_session::parse::ParseSess;
use rustc_span::source_map::FilePathMapping;
use rustc_span::symbol::{kw, sym, Symbol};
+use rustc_span::with_default_session_globals;
use rustc_span::{BytePos, FileName, Pos, Span};
use std::path::PathBuf;
let source = "/// doc comment\r\nfn foo() {}".to_string();
let item = parse_item_from_source_str(name_1, source, &sess).unwrap().unwrap();
let doc = item.attrs.iter().filter_map(|at| at.doc_str()).next().unwrap();
- assert_eq!(doc.as_str(), "/// doc comment");
+ assert_eq!(doc.as_str(), " doc comment");
let name_2 = FileName::Custom("crlf_source_2".to_string());
let source = "/// doc comment\r\n/// line 2\r\nfn foo() {}".to_string();
let item = parse_item_from_source_str(name_2, source, &sess).unwrap().unwrap();
let docs = item.attrs.iter().filter_map(|at| at.doc_str()).collect::<Vec<_>>();
- let b: &[_] = &[Symbol::intern("/// doc comment"), Symbol::intern("/// line 2")];
+ let b: &[_] = &[Symbol::intern(" doc comment"), Symbol::intern(" line 2")];
assert_eq!(&docs[..], b);
let name_3 = FileName::Custom("clrf_source_3".to_string());
let source = "/** doc comment\r\n * with CRLF */\r\nfn foo() {}".to_string();
let item = parse_item_from_source_str(name_3, source, &sess).unwrap().unwrap();
let doc = item.attrs.iter().filter_map(|at| at.doc_str()).next().unwrap();
- assert_eq!(doc.as_str(), "/** doc comment\n * with CRLF */");
+ assert_eq!(doc.as_str(), " doc comment\n * with CRLF ");
});
}
let input = if item.pretty_printing_compatibility_hack() {
TokenTree::token(token::Interpolated(Lrc::new(item)), DUMMY_SP).into()
} else {
- nt_to_tokenstream(&item, ecx.parse_sess, DUMMY_SP)
+ nt_to_tokenstream(&item, &ecx.sess.parse_sess, DUMMY_SP)
};
let server = proc_macro_server::Rustc::new(ecx);
}
};
- let error_count_before = ecx.parse_sess.span_diagnostic.err_count();
+ let error_count_before = ecx.sess.parse_sess.span_diagnostic.err_count();
let mut parser =
- rustc_parse::stream_to_parser(ecx.parse_sess, stream, Some("proc-macro derive"));
+ rustc_parse::stream_to_parser(&ecx.sess.parse_sess, stream, Some("proc-macro derive"));
let mut items = vec![];
loop {
}
// fail if there have been errors emitted
- if ecx.parse_sess.span_diagnostic.err_count() > error_count_before {
+ if ecx.sess.parse_sess.span_diagnostic.err_count() > error_count_before {
ecx.struct_span_err(span, "proc-macro derive produced unparseable tokens").emit();
}
use rustc_ast::ast;
use rustc_ast::token;
use rustc_ast::tokenstream::{self, DelimSpan, IsJoint::*, TokenStream, TreeAndJoint};
-use rustc_ast::util::comments;
use rustc_ast_pretty::pprust;
use rustc_data_structures::sync::Lrc;
use rustc_errors::Diagnostic;
tt!(Punct::new('\'', true))
}
Literal(lit) => tt!(Literal { lit }),
- DocComment(c) => {
- let style = comments::doc_comment_style(c);
- let stripped = comments::strip_doc_comment_decoration(c);
+ DocComment(_, attr_style, data) => {
let mut escaped = String::new();
- for ch in stripped.chars() {
+ for ch in data.as_str().chars() {
escaped.extend(ch.escape_debug());
}
let stream = vec![
span: DelimSpan::from_single(span),
flatten: false,
}));
- if style == ast::AttrStyle::Inner {
+ if attr_style == ast::AttrStyle::Inner {
stack.push(tt!(Punct::new('!', false)));
}
tt!(Punct::new('#', false))
pub fn new(cx: &'a ExtCtxt<'_>) -> Self {
let expn_data = cx.current_expansion.id.expn_data();
Rustc {
- sess: cx.parse_sess,
+ sess: &cx.sess.parse_sess,
def_site: cx.with_def_site_ctxt(expn_data.def_site),
call_site: cx.with_call_site_ctxt(expn_data.call_site),
mixed_site: cx.with_mixed_site_ctxt(expn_data.call_site),
use rustc_ast::ast;
use rustc_ast::tokenstream::TokenStream;
-use rustc_ast::with_default_session_globals;
use rustc_parse::{new_parser_from_source_str, parser::Parser, source_file_to_stream};
use rustc_session::parse::ParseSess;
use rustc_span::source_map::{FilePathMapping, SourceMap};
+use rustc_span::with_default_session_globals;
use rustc_span::{BytePos, MultiSpan, Span};
use rustc_data_structures::sync::Lrc;
use rustc_ast::token;
use rustc_ast::tokenstream::{TokenStream, TokenStreamBuilder, TokenTree};
-use rustc_ast::with_default_session_globals;
+use rustc_span::with_default_session_globals;
use rustc_span::{BytePos, Span, Symbol};
use smallvec::smallvec;
/// Lazily evaluate constants. This allows constants to depend on type parameters.
(active, lazy_normalization_consts, "1.46.0", Some(72219), None),
- /// Alloc calling `transmute` in const fn
+ /// Allows calling `transmute` in const fn
(active, const_fn_transmute, "1.46.0", Some(53605), None),
+ /// The smallest useful subset of `const_generics`.
+ (active, min_const_generics, "1.47.0", Some(74878), None),
+
// -------------------------------------------------------------------------
// feature-group-end: actual feature gates
// -------------------------------------------------------------------------
}
}
- pub fn matches_ns(&self, ns: Namespace) -> bool {
+ pub fn ns(&self) -> Option<Namespace> {
match self {
DefKind::Mod
| DefKind::Struct
| DefKind::ForeignTy
| DefKind::TraitAlias
| DefKind::AssocTy
- | DefKind::TyParam => ns == Namespace::TypeNS,
+ | DefKind::TyParam => Some(Namespace::TypeNS),
DefKind::Fn
| DefKind::Const
| DefKind::Static
| DefKind::Ctor(..)
| DefKind::AssocFn
- | DefKind::AssocConst => ns == Namespace::ValueNS,
+ | DefKind::AssocConst => Some(Namespace::ValueNS),
- DefKind::Macro(..) => ns == Namespace::MacroNS,
+ DefKind::Macro(..) => Some(Namespace::MacroNS),
// Not namespaced.
DefKind::AnonConst
| DefKind::Use
| DefKind::ForeignMod
| DefKind::GlobalAsm
- | DefKind::Impl => false,
+ | DefKind::Impl => None,
}
}
}
pub fn matches_ns(&self, ns: Namespace) -> bool {
match self {
- Res::Def(kind, ..) => kind.matches_ns(ns),
+ Res::Def(kind, ..) => kind.ns() == Some(ns),
Res::PrimTy(..) | Res::SelfTy(..) | Res::ToolMod => ns == Namespace::TypeNS,
Res::SelfCtor(..) | Res::Local(..) => ns == Namespace::ValueNS,
Res::NonMacroAttr(..) => ns == Namespace::MacroNS,
/// Extracts the first `lang = "$name"` out of a list of attributes.
/// The attributes `#[panic_handler]` and `#[alloc_error_handler]`
/// are also extracted out when found.
-pub fn extract(attrs: &[ast::Attribute]) -> Option<(Symbol, Span)> {
+///
+/// About the `check_name` argument: passing in a `Session` would be simpler,
+/// because then we could call `Session::check_name` directly. But we want to
+/// avoid the need for `librustc_hir` to depend on `librustc_session`, so we
+/// use a closure instead.
+pub fn extract<'a, F>(check_name: F, attrs: &'a [ast::Attribute]) -> Option<(Symbol, Span)>
+where
+ F: Fn(&'a ast::Attribute, Symbol) -> bool,
+{
attrs.iter().find_map(|attr| {
Some(match attr {
- _ if attr.check_name(sym::lang) => (attr.value_str()?, attr.span),
- _ if attr.check_name(sym::panic_handler) => (sym::panic_impl, attr.span),
- _ if attr.check_name(sym::alloc_error_handler) => (sym::oom, attr.span),
+ _ if check_name(attr, sym::lang) => (attr.value_str()?, attr.span),
+ _ if check_name(attr, sym::panic_handler) => (sym::panic_impl, attr.span),
+ _ if check_name(attr, sym::alloc_error_handler) => (sym::oom, attr.span),
_ => return None,
})
})
};
}
-pub fn link_name(attrs: &[ast::Attribute]) -> Option<Symbol> {
- lang_items::extract(attrs).and_then(|(name, _)| {
+/// The `check_name` argument avoids the need for `librustc_hir` to depend on
+/// `librustc_session`.
+pub fn link_name<'a, F>(check_name: F, attrs: &'a [ast::Attribute]) -> Option<Symbol>
+where
+ F: Fn(&'a ast::Attribute, Symbol) -> bool
+{
+ lang_items::extract(check_name, attrs).and_then(|(name, _)| {
$(if name == sym::$name {
Some(sym::$sym)
} else)* {
let def_id = self.tcx.hir().local_def_id(hir_id);
let def_path_hash = self.tcx.def_path_hash(def_id.to_def_id());
for attr in attrs {
- if attr.check_name(sym::rustc_if_this_changed) {
+ if self.tcx.sess.check_name(attr, sym::rustc_if_this_changed) {
let dep_node_interned = self.argument(attr);
let dep_node = match dep_node_interned {
None => DepNode::from_def_path_hash(def_path_hash, DepKind::hir_owner),
},
};
self.if_this_changed.push((attr.span, def_id.to_def_id(), dep_node));
- } else if attr.check_name(sym::rustc_then_this_would_need) {
+ } else if self.tcx.sess.check_name(attr, sym::rustc_then_this_would_need) {
let dep_node_interned = self.argument(attr);
let dep_node = match dep_node_interned {
Some(n) => match DepNode::from_label_string(&n.as_str(), def_path_hash) {
.collect_and_partition_mono_items(LOCAL_CRATE)
.1
.iter()
- .map(|cgu| cgu.name())
- .collect::<BTreeSet<Symbol>>();
+ .map(|cgu| cgu.name().to_string())
+ .collect::<BTreeSet<String>>();
let ams = AssertModuleSource { tcx, available_cgus };
struct AssertModuleSource<'tcx> {
tcx: TyCtxt<'tcx>,
- available_cgus: BTreeSet<Symbol>,
+ available_cgus: BTreeSet<String>,
}
impl AssertModuleSource<'tcx> {
fn check_attr(&self, attr: &ast::Attribute) {
- let (expected_reuse, comp_kind) = if attr.check_name(sym::rustc_partition_reused) {
- (CguReuse::PreLto, ComparisonKind::AtLeast)
- } else if attr.check_name(sym::rustc_partition_codegened) {
- (CguReuse::No, ComparisonKind::Exact)
- } else if attr.check_name(sym::rustc_expected_cgu_reuse) {
- match self.field(attr, sym::kind) {
- sym::no => (CguReuse::No, ComparisonKind::Exact),
- sym::pre_dash_lto => (CguReuse::PreLto, ComparisonKind::Exact),
- sym::post_dash_lto => (CguReuse::PostLto, ComparisonKind::Exact),
- sym::any => (CguReuse::PreLto, ComparisonKind::AtLeast),
- other => {
- self.tcx.sess.span_fatal(
- attr.span,
- &format!("unknown cgu-reuse-kind `{}` specified", other),
- );
+ let (expected_reuse, comp_kind) =
+ if self.tcx.sess.check_name(attr, sym::rustc_partition_reused) {
+ (CguReuse::PreLto, ComparisonKind::AtLeast)
+ } else if self.tcx.sess.check_name(attr, sym::rustc_partition_codegened) {
+ (CguReuse::No, ComparisonKind::Exact)
+ } else if self.tcx.sess.check_name(attr, sym::rustc_expected_cgu_reuse) {
+ match self.field(attr, sym::kind) {
+ sym::no => (CguReuse::No, ComparisonKind::Exact),
+ sym::pre_dash_lto => (CguReuse::PreLto, ComparisonKind::Exact),
+ sym::post_dash_lto => (CguReuse::PostLto, ComparisonKind::Exact),
+ sym::any => (CguReuse::PreLto, ComparisonKind::AtLeast),
+ other => {
+ self.tcx.sess.span_fatal(
+ attr.span,
+ &format!("unknown cgu-reuse-kind `{}` specified", other),
+ );
+ }
}
- }
- } else {
- return;
- };
+ } else {
+ return;
+ };
if !self.tcx.sess.opts.debugging_opts.query_dep_graph {
self.tcx.sess.span_fatal(
debug!("mapping '{}' to cgu name '{}'", self.field(attr, sym::module), cgu_name);
- if !self.available_cgus.contains(&cgu_name) {
+ if !self.available_cgus.contains(&*cgu_name.as_str()) {
self.tcx.sess.span_err(
attr.span,
&format!(
- "no module named `{}` (mangled: {}). \
- Available modules: {}",
+ "no module named `{}` (mangled: {}). Available modules: {}",
user_path,
cgu_name,
self.available_cgus
impl DirtyCleanVisitor<'tcx> {
/// Possibly "deserialize" the attribute into a clean/dirty assertion
fn assertion_maybe(&mut self, item_id: hir::HirId, attr: &Attribute) -> Option<Assertion> {
- let is_clean = if attr.check_name(sym::rustc_dirty) {
+ let is_clean = if self.tcx.sess.check_name(attr, sym::rustc_dirty) {
false
- } else if attr.check_name(sym::rustc_clean) {
+ } else if self.tcx.sess.check_name(attr, sym::rustc_clean) {
true
} else {
// skip: not rustc_clean/dirty
impl FindAllAttrs<'tcx> {
fn is_active_attr(&mut self, attr: &Attribute) -> bool {
for attr_name in &self.attr_names {
- if attr.check_name(*attr_name) && check_config(self.tcx, attr) {
+ if self.tcx.sess.check_name(attr, *attr_name) && check_config(self.tcx, attr) {
return true;
}
}
fn checked_shr(self, rhs: u32) -> Option<Self>;
}
+impl FiniteBitSetTy for u32 {
+ const DOMAIN_SIZE: u32 = 32;
+
+ const FILLED: Self = Self::MAX;
+ const EMPTY: Self = Self::MIN;
+
+ const ONE: Self = 1u32;
+ const ZERO: Self = 0u32;
+
+ fn checked_shl(self, rhs: u32) -> Option<Self> {
+ self.checked_shl(rhs)
+ }
+
+ fn checked_shr(self, rhs: u32) -> Option<Self> {
+ self.checked_shr(rhs)
+ }
+}
+
+impl std::fmt::Debug for FiniteBitSet<u32> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "{:032b}", self.0)
+ }
+}
+
impl FiniteBitSetTy for u64 {
const DOMAIN_SIZE: u32 = 64;
debug!("try_report_named_anon_conflict: ret ty {:?}", ty);
if sub == &ty::ReStatic
- && v.0
- .into_iter()
- .filter(|t| t.span.desugaring_kind().is_none())
- .next()
- .is_some()
+ && v.0.into_iter().find(|t| t.span.desugaring_kind().is_none()).is_some()
{
// If the failure is due to a `'static` requirement coming from a `dyn` or
// `impl` Trait that *isn't* caused by `async fn` desugaring, handle this case
param.param_ty.to_string(),
Applicability::MaybeIncorrect,
);
- } else if let Some(_) = opaque
+ } else if opaque
.bounds
.iter()
.filter_map(|arg| match arg {
_ => None,
})
.next()
+ .is_some()
{
} else {
err.span_suggestion_verbose(
ty::Invariant => self.fields.equate(self.a_is_expected).relate(a, b),
ty::Covariant => self.relate(a, b),
// FIXME(#41044) -- not correct, need test
- ty::Bivariant => Ok(a.clone()),
+ ty::Bivariant => Ok(a),
ty::Contravariant => self.fields.lub(self.a_is_expected).relate(a, b),
}
}
// very challenging, switch to invariance. This is obviously
// overly conservative but works ok in practice.
self.relate_with_variance(ty::Variance::Invariant, a, b)?;
- Ok(a.clone())
+ Ok(a)
}
}
self.a_scopes.pop().unwrap();
}
- Ok(a.clone())
+ Ok(a)
}
}
) -> TypeError<'tcx> {
debug!("error: placeholder={:?}, other_region={:?}", placeholder, other_region);
if self.overly_polymorphic {
- return TypeError::RegionsOverlyPolymorphic(placeholder.name, other_region);
+ TypeError::RegionsOverlyPolymorphic(placeholder.name, other_region)
} else {
- return TypeError::RegionsInsufficientlyPolymorphic(placeholder.name, other_region);
+ TypeError::RegionsInsufficientlyPolymorphic(placeholder.name, other_region)
}
}
}
match variance {
ty::Invariant => self.fields.equate(self.a_is_expected).relate(a, b),
ty::Covariant => self.relate(a, b),
- ty::Bivariant => Ok(a.clone()),
+ ty::Bivariant => Ok(a),
ty::Contravariant => self.with_expected_switched(|this| this.relate(b, a)),
}
}
use rustc_session::lint;
use rustc_session::parse::{CrateConfig, ParseSess};
use rustc_session::{DiagnosticOutput, Session};
-use rustc_span::edition;
use rustc_span::source_map::{FileLoader, FileName};
use std::path::PathBuf;
use std::result;
/// Converts strings provided as `--cfg [cfgspec]` into a `crate_cfg`.
pub fn parse_cfgspecs(cfgspecs: Vec<String>) -> FxHashSet<(String, Option<String>)> {
- rustc_ast::with_default_session_globals(move || {
+ rustc_span::with_default_session_globals(move || {
let cfg = cfgspecs
.into_iter()
.map(|s| {
|| create_compiler_and_run(config, f),
)
}
-
-pub fn setup_callbacks_and_run_in_default_thread_pool_with_globals<R: Send>(
- edition: edition::Edition,
- f: impl FnOnce() -> R + Send,
-) -> R {
- // the 1 here is duplicating code in config.opts.debugging_opts.threads
- // which also defaults to 1; it ultimately doesn't matter as the default
- // isn't threaded, and just ignores this parameter
- util::setup_callbacks_and_run_in_thread_pool_with_globals(edition, 1, &None, f)
-}
)
});
- let (krate, features) = rustc_expand::config::features(
- krate,
- &sess.parse_sess,
- sess.edition(),
- &sess.opts.debugging_opts.allow_features,
- );
+ let (krate, features) = rustc_expand::config::features(sess, krate);
// these need to be set "early" so that expansion sees `quote` if enabled.
sess.init_features(features);
let (krate, name) = rustc_builtin_macros::standard_library_imports::inject(
krate,
&mut resolver,
- &sess.parse_sess,
+ &sess,
alt_std_name,
);
if let Some(name) = name {
krate
});
- util::check_attr_crate_type(&krate.attrs, &mut resolver.lint_buffer());
+ util::check_attr_crate_type(&sess, &krate.attrs, &mut resolver.lint_buffer());
// Expand all macros
krate = sess.time("macro_expand_crate", || {
};
let extern_mod_loaded = |k: &ast::Crate| pre_expansion_lint(sess, lint_store, k);
- let mut ecx = ExtCtxt::new(&sess.parse_sess, cfg, &mut resolver, Some(&extern_mod_loaded));
+ let mut ecx = ExtCtxt::new(&sess, cfg, &mut resolver, Some(&extern_mod_loaded));
// Expand macros now!
let krate = sess.time("expand_crate", || ecx.monotonic_expander().expand_crate(krate));
});
let mut missing_fragment_specifiers: Vec<_> = ecx
+ .sess
.parse_sess
.missing_fragment_specifiers
.borrow()
})?;
sess.time("maybe_building_test_harness", || {
- rustc_builtin_macros::test_harness::inject(
- &sess.parse_sess,
- &mut resolver,
- sess.opts.test,
- &mut krate,
- sess.diagnostic(),
- &sess.features_untracked(),
- sess.panic_strategy(),
- sess.target.target.options.panic_strategy,
- sess.opts.debugging_opts.panic_abort_tests,
- )
+ rustc_builtin_macros::test_harness::inject(&sess, &mut resolver, &mut krate)
});
if let Some(PpMode::PpmSource(PpSourceMode::PpmEveryBodyLoops)) = sess.opts.pretty {
let num_crate_types = crate_types.len();
let is_test_crate = sess.opts.test;
rustc_builtin_macros::proc_macro_harness::inject(
- &sess.parse_sess,
+ &sess,
&mut resolver,
krate,
is_proc_macro_crate,
// Needs to go *after* expansion to be able to check the results of macro expansion.
sess.time("complete_gated_feature_checking", || {
- rustc_ast_passes::feature_gate::check_crate(
- &krate,
- &sess.parse_sess,
- &sess.features_untracked(),
- sess.opts.unstable_features,
- );
+ rustc_ast_passes::feature_gate::check_crate(&krate, sess);
});
// Add all buffered lints from the `ParseSess` to the `Session`.
-use rustc_ast::attr;
use rustc_hir as hir;
use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE};
use rustc_hir::itemlikevisit::ItemLikeVisitor;
fn proc_macro_decls_static(tcx: TyCtxt<'_>, cnum: CrateNum) -> Option<DefId> {
assert_eq!(cnum, LOCAL_CRATE);
- let mut finder = Finder { decls: None };
+ let mut finder = Finder { tcx, decls: None };
tcx.hir().krate().visit_all_item_likes(&mut finder);
finder.decls.map(|id| tcx.hir().local_def_id(id).to_def_id())
}
-struct Finder {
+struct Finder<'tcx> {
+ tcx: TyCtxt<'tcx>,
decls: Option<hir::HirId>,
}
-impl<'v> ItemLikeVisitor<'v> for Finder {
+impl<'v> ItemLikeVisitor<'v> for Finder<'_> {
fn visit_item(&mut self, item: &hir::Item<'_>) {
- if attr::contains_name(&item.attrs, sym::rustc_proc_macro_decls) {
+ if self.tcx.sess.contains_name(&item.attrs, sym::rustc_proc_macro_decls) {
self.decls = Some(item.hir_id);
}
}
None => {
let parse_result = self.parse()?;
let krate = parse_result.peek();
- find_crate_name(Some(self.session()), &krate.attrs, &self.compiler.input)
+ find_crate_name(self.session(), &krate.attrs, &self.compiler.input)
}
})
})
};
let attrs = &*tcx.get_attrs(def_id.to_def_id());
- let attrs = attrs.iter().filter(|attr| attr.check_name(sym::rustc_error));
+ let attrs = attrs.iter().filter(|attr| tcx.sess.check_name(attr, sym::rustc_error));
for attr in attrs {
match attr.meta_item_list() {
// Check if there is a `#[rustc_error(delay_span_bug_from_inside_query)]`.
// When the user supplies --test we should implicitly supply --cfg test
#[test]
fn test_switch_implies_cfg_test() {
- rustc_ast::with_default_session_globals(|| {
+ rustc_span::with_default_session_globals(|| {
let matches = optgroups().parse(&["--test".to_string()]).unwrap();
let (sess, cfg) = mk_session(matches);
let cfg = build_configuration(&sess, to_crate_config(cfg));
// When the user supplies --test and --cfg test, don't implicitly add another --cfg test
#[test]
fn test_switch_implies_cfg_test_unless_cfg_test() {
- rustc_ast::with_default_session_globals(|| {
+ rustc_span::with_default_session_globals(|| {
let matches = optgroups().parse(&["--test".to_string(), "--cfg=test".to_string()]).unwrap();
let (sess, cfg) = mk_session(matches);
let cfg = build_configuration(&sess, to_crate_config(cfg));
#[test]
fn test_can_print_warnings() {
- rustc_ast::with_default_session_globals(|| {
+ rustc_span::with_default_session_globals(|| {
let matches = optgroups().parse(&["-Awarnings".to_string()]).unwrap();
let (sess, _) = mk_session(matches);
assert!(!sess.diagnostic().can_emit_warnings());
});
- rustc_ast::with_default_session_globals(|| {
+ rustc_span::with_default_session_globals(|| {
let matches =
optgroups().parse(&["-Awarnings".to_string(), "-Dwarnings".to_string()]).unwrap();
let (sess, _) = mk_session(matches);
assert!(sess.diagnostic().can_emit_warnings());
});
- rustc_ast::with_default_session_globals(|| {
+ rustc_span::with_default_session_globals(|| {
let matches = optgroups().parse(&["-Adead_code".to_string()]).unwrap();
let (sess, _) = mk_session(matches);
assert!(sess.diagnostic().can_emit_warnings());
crate::callbacks::setup_callbacks();
let main_handler = move || {
- rustc_ast::with_session_globals(edition, || {
+ rustc_span::with_session_globals(edition, || {
if let Some(stderr) = stderr {
io::set_panic(Some(box Sink(stderr.clone())));
}
let with_pool = move |pool: &rayon::ThreadPool| pool.install(move || f());
- rustc_ast::with_session_globals(edition, || {
- rustc_ast::SESSION_GLOBALS.with(|ast_session_globals| {
- rustc_span::SESSION_GLOBALS.with(|span_session_globals| {
- // The main handler runs for each Rayon worker thread and sets
- // up the thread local rustc uses. ast_session_globals and
- // span_session_globals are captured and set on the new
- // threads. ty::tls::with_thread_locals sets up thread local
- // callbacks from librustc_ast.
- let main_handler = move |thread: rayon::ThreadBuilder| {
- rustc_ast::SESSION_GLOBALS.set(ast_session_globals, || {
- rustc_span::SESSION_GLOBALS.set(span_session_globals, || {
- if let Some(stderr) = stderr {
- io::set_panic(Some(box Sink(stderr.clone())));
- }
- thread.run()
- })
- })
- };
+ rustc_span::with_session_globals(edition, || {
+ rustc_span::SESSION_GLOBALS.with(|session_globals| {
+ // The main handler runs for each Rayon worker thread and sets up
+ // the thread local rustc uses. `session_globals` is captured and set
+ // on the new threads.
+ let main_handler = move |thread: rayon::ThreadBuilder| {
+ rustc_span::SESSION_GLOBALS.set(session_globals, || {
+ if let Some(stderr) = stderr {
+ io::set_panic(Some(box Sink(stderr.clone())));
+ }
+ thread.run()
+ })
+ };
- config.build_scoped(main_handler, with_pool).unwrap()
- })
+ config.build_scoped(main_handler, with_pool).unwrap()
})
})
}
CrateDisambiguator::from(hasher.finish::<Fingerprint>())
}
-pub(crate) fn check_attr_crate_type(attrs: &[ast::Attribute], lint_buffer: &mut LintBuffer) {
+pub(crate) fn check_attr_crate_type(
+ sess: &Session,
+ attrs: &[ast::Attribute],
+ lint_buffer: &mut LintBuffer,
+) {
// Unconditionally collect crate types from attributes to make them used
for a in attrs.iter() {
- if a.check_name(sym::crate_type) {
+ if sess.check_name(a, sym::crate_type) {
if let Some(n) = a.value_str() {
if categorize_crate_type(n).is_some() {
return;
let attr_types: Vec<CrateType> = attrs
.iter()
.filter_map(|a| {
- if a.check_name(sym::crate_type) {
+ if session.check_name(a, sym::crate_type) {
match a.value_str() {
Some(s) => categorize_crate_type(s),
_ => None,
.opts
.crate_name
.clone()
- .or_else(|| rustc_attr::find_crate_name(attrs).map(|n| n.to_string()))
+ .or_else(|| rustc_attr::find_crate_name(&sess, attrs).map(|n| n.to_string()))
.unwrap_or_else(|| input.filestem().to_owned());
OutputFilenames::new(
use rustc_middle::ty::subst::{GenericArgKind, Subst};
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_session::lint::FutureIncompatibleInfo;
+use rustc_session::Session;
use rustc_span::edition::Edition;
use rustc_span::source_map::Spanned;
use rustc_span::symbol::{kw, sym, Ident, Symbol};
impl EarlyLintPass for UnsafeCode {
fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &ast::Attribute) {
- if attr.check_name(sym::allow_internal_unsafe) {
+ if cx.sess().check_name(attr, sym::allow_internal_unsafe) {
self.report_unsafe(cx, attr.span, |lint| {
lint.build(
"`allow_internal_unsafe` allows defining \
impl_lint_pass!(MissingDoc => [MISSING_DOCS]);
-fn has_doc(attr: &ast::Attribute) -> bool {
+fn has_doc(sess: &Session, attr: &ast::Attribute) -> bool {
if attr.is_doc_comment() {
return true;
}
- if !attr.check_name(sym::doc) {
+ if !sess.check_name(attr, sym::doc) {
return false;
}
}
}
- let has_doc = attrs.iter().any(|a| has_doc(a));
+ let has_doc = attrs.iter().any(|a| has_doc(cx.sess(), a));
if !has_doc {
cx.struct_span_lint(
MISSING_DOCS,
}
impl<'tcx> LateLintPass<'tcx> for MissingDoc {
- fn enter_lint_attrs(&mut self, _: &LateContext<'_>, attrs: &[ast::Attribute]) {
+ fn enter_lint_attrs(&mut self, cx: &LateContext<'_>, attrs: &[ast::Attribute]) {
let doc_hidden = self.doc_hidden()
|| attrs.iter().any(|attr| {
- attr.check_name(sym::doc)
+ cx.sess().check_name(attr, sym::doc)
&& match attr.meta_item_list() {
None => false,
Some(l) => attr::list_contains_name(&l, sym::hidden),
self.check_missing_docs_attrs(cx, None, &krate.item.attrs, krate.item.span, "the", "crate");
for macro_def in krate.exported_macros {
- let has_doc = macro_def.attrs.iter().any(|a| has_doc(a));
+ let has_doc = macro_def.attrs.iter().any(|a| has_doc(cx.sess(), a));
if !has_doc {
cx.struct_span_lint(
MISSING_DOCS,
return;
}
}
- if attr.check_name(sym::no_start) || attr.check_name(sym::crate_id) {
+ if cx.sess().check_name(attr, sym::no_start) || cx.sess().check_name(attr, sym::crate_id) {
let path_str = pprust::path_to_string(&attr.get_normal_item().path);
let msg = format!("use of deprecated attribute `{}`: no longer used.", path_str);
lint_deprecated_attr(cx, attr, &msg, None);
let span = sugared_span.take().unwrap_or_else(|| attr.span);
- if attr.is_doc_comment() || attr.check_name(sym::doc) {
+ if attr.is_doc_comment() || cx.sess().check_name(attr, sym::doc) {
cx.struct_span_lint(UNUSED_DOC_COMMENTS, span, |lint| {
let mut err = lint.build("unused doc comment");
err.span_label(
fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) {
match it.kind {
hir::ItemKind::Fn(.., ref generics, _) => {
- if let Some(no_mangle_attr) = attr::find_by_name(&it.attrs, sym::no_mangle) {
+ if let Some(no_mangle_attr) = cx.sess().find_by_name(&it.attrs, sym::no_mangle) {
for param in generics.params {
match param.kind {
GenericParamKind::Lifetime { .. } => {}
}
}
hir::ItemKind::Const(..) => {
- if attr::contains_name(&it.attrs, sym::no_mangle) {
+ if cx.sess().contains_name(&it.attrs, sym::no_mangle) {
// Const items do not refer to a particular location in memory, and therefore
// don't have anything to attach a symbol to
cx.struct_span_lint(NO_MANGLE_CONST_ITEMS, it.span, |lint| {
);
impl<'tcx> LateLintPass<'tcx> for UnstableFeatures {
- fn check_attribute(&mut self, ctx: &LateContext<'_>, attr: &ast::Attribute) {
- if attr.check_name(sym::feature) {
+ fn check_attribute(&mut self, cx: &LateContext<'_>, attr: &ast::Attribute) {
+ if cx.sess().check_name(attr, sym::feature) {
if let Some(items) = attr.meta_item_list() {
for item in items {
- ctx.struct_span_lint(UNSTABLE_FEATURES, item.span(), |lint| {
+ cx.struct_span_lint(UNSTABLE_FEATURES, item.span(), |lint| {
lint.build("unstable feature").emit()
});
}
return;
}
- if let Some(attr) = attr::find_by_name(&it.attrs, sym::rustc_test_marker) {
+ if let Some(attr) = cx.sess().find_by_name(&it.attrs, sym::rustc_test_marker) {
cx.struct_span_lint(UNNAMEABLE_TEST_ITEMS, attr.span, |lint| {
lint.build("cannot test inner items").emit()
});
overridden_link_name,
tcx.get_attrs(did.to_def_id())
.iter()
- .find(|at| at.check_name(sym::link_name))
+ .find(|at| tcx.sess.check_name(at, sym::link_name))
.unwrap()
.span,
)
};
let meta = unwrap_or!(attr.meta(), continue);
- attr::mark_used(attr);
+ self.sess.mark_attr_used(attr);
let mut metas = unwrap_or!(meta.meta_item_list(), continue);
let has_repr_c = it
.attrs
.iter()
- .any(|attr| attr::find_repr_attrs(&cx.sess.parse_sess, attr).contains(&attr::ReprC));
+ .any(|attr| attr::find_repr_attrs(&cx.sess, attr).contains(&attr::ReprC));
if has_repr_c {
return;
let crate_ident = if let Some(name) = &cx.tcx.sess.opts.crate_name {
Some(Ident::from_str(name))
} else {
- attr::find_by_name(&cx.tcx.hir().attrs(hir::CRATE_HIR_ID), sym::crate_name)
+ cx.sess()
+ .find_by_name(&cx.tcx.hir().attrs(hir::CRATE_HIR_ID), sym::crate_name)
.and_then(|attr| attr.meta())
.and_then(|meta| {
meta.name_value_literal().and_then(|lit| {
},
FnKind::ItemFn(ident, _, header, _, attrs) => {
// Skip foreign-ABI #[no_mangle] functions (Issue #31924)
- if header.abi != Abi::Rust && attr::contains_name(attrs, sym::no_mangle) {
+ if header.abi != Abi::Rust && cx.sess().contains_name(attrs, sym::no_mangle) {
return;
}
self.check_snake_case(cx, "function", ident);
impl<'tcx> LateLintPass<'tcx> for NonUpperCaseGlobals {
fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) {
match it.kind {
- hir::ItemKind::Static(..) if !attr::contains_name(&it.attrs, sym::no_mangle) => {
+ hir::ItemKind::Static(..) if !cx.sess().contains_name(&it.attrs, sym::no_mangle) => {
NonUpperCaseGlobals::check_upper_case(cx, "static variable", &it.ident);
}
hir::ItemKind::Const(..) => {
let guaranteed_nonnull_optimization = tcx
.get_attrs(def.did)
.iter()
- .any(|a| a.check_name(sym::rustc_nonnull_optimization_guaranteed));
+ .any(|a| tcx.sess.check_name(a, sym::rustc_nonnull_optimization_guaranteed));
if guaranteed_nonnull_optimization {
return true;
_ => false,
}
}
+
/// Given a non-null scalar (or transparent) type `ty`, return the nullable version of that type.
/// If the type passed in was not scalar, returns None.
fn get_nullable_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
}
// If `ty` is a `repr(transparent)` newtype, and the non-zero-sized type is a generic
// argument, which after substitution, is `()`, then this branch can be hit.
- FfiResult::FfiUnsafe { ty, .. } if is_return_type && ty.is_unit() => return,
+ FfiResult::FfiUnsafe { ty, .. } if is_return_type && ty.is_unit() => {}
FfiResult::FfiUnsafe { ty, reason, help } => {
self.emit_ffi_unsafe_type_lint(ty, sp, &reason, help.as_deref());
}
use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
use rustc_ast::ast;
use rustc_ast::ast::{ExprKind, StmtKind};
-use rustc_ast::attr;
use rustc_ast::util::parser;
use rustc_ast_pretty::pprust;
use rustc_data_structures::fx::FxHashMap;
descr_post_path: &str,
) -> bool {
for attr in cx.tcx.get_attrs(def_id).iter() {
- if attr.check_name(sym::must_use) {
+ if cx.sess().check_name(attr, sym::must_use) {
cx.struct_span_lint(UNUSED_MUST_USE, span, |lint| {
let msg = format!(
"unused {}`{}`{} that must be used",
}
}
- if !attr::is_used(attr) {
+ if !cx.sess().is_attr_used(attr) {
debug!("emitting warning for: {:?}", attr);
cx.struct_span_lint(UNUSED_ATTRIBUTES, attr.span, |lint| {
lint.build("unused attribute").emit()
use crate::locator::{CrateError, CrateLocator, CratePaths};
use crate::rmeta::{CrateDep, CrateMetadata, CrateNumMap, CrateRoot, MetadataBlob};
-use rustc_ast::expand::allocator::{global_allocator_spans, AllocatorKind};
-use rustc_ast::{ast, attr};
+use rustc_ast::expand::allocator::AllocatorKind;
+use rustc_ast::{ast, visit};
use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::svh::Svh;
use rustc_data_structures::sync::Lrc;
// compilation mode also comes into play.
let desired_strategy = self.sess.panic_strategy();
let mut runtime_found = false;
- let mut needs_panic_runtime = attr::contains_name(&krate.attrs, sym::needs_panic_runtime);
+ let mut needs_panic_runtime =
+ self.sess.contains_name(&krate.attrs, sym::needs_panic_runtime);
self.cstore.iter_crate_data(|cnum, data| {
needs_panic_runtime = needs_panic_runtime || data.needs_panic_runtime();
}
fn inject_allocator_crate(&mut self, krate: &ast::Crate) {
- self.cstore.has_global_allocator = match &*global_allocator_spans(krate) {
+ self.cstore.has_global_allocator = match &*global_allocator_spans(&self.sess, krate) {
[span1, span2, ..] => {
self.sess
.struct_span_err(*span2, "cannot define multiple global allocators")
// Check to see if we actually need an allocator. This desire comes
// about through the `#![needs_allocator]` attribute and is typically
// written down in liballoc.
- let mut needs_allocator = attr::contains_name(&krate.attrs, sym::needs_allocator);
+ let mut needs_allocator = self.sess.contains_name(&krate.attrs, sym::needs_allocator);
self.cstore.iter_crate_data(|_, data| {
needs_allocator = needs_allocator || data.needs_allocator();
});
// allocator. At this point our allocator request is typically fulfilled
// by the standard library, denoted by the `#![default_lib_allocator]`
// attribute.
- let mut has_default = attr::contains_name(&krate.attrs, sym::default_lib_allocator);
+ let mut has_default = self.sess.contains_name(&krate.attrs, sym::default_lib_allocator);
self.cstore.iter_crate_data(|_, data| {
if data.has_default_lib_allocator() {
has_default = true;
);
let name = match orig_name {
Some(orig_name) => {
- validate_crate_name(Some(self.sess), &orig_name.as_str(), Some(item.span));
+ validate_crate_name(self.sess, &orig_name.as_str(), Some(item.span));
orig_name
}
None => item.ident.name,
};
- let dep_kind = if attr::contains_name(&item.attrs, sym::no_link) {
+ let dep_kind = if self.sess.contains_name(&item.attrs, sym::no_link) {
CrateDepKind::MacrosOnly
} else {
CrateDepKind::Explicit
self.maybe_resolve_crate(name, CrateDepKind::Explicit, None).ok()
}
}
+
+fn global_allocator_spans(sess: &Session, krate: &ast::Crate) -> Vec<Span> {
+ struct Finder<'a> {
+ sess: &'a Session,
+ name: Symbol,
+ spans: Vec<Span>,
+ }
+ impl<'ast, 'a> visit::Visitor<'ast> for Finder<'a> {
+ fn visit_item(&mut self, item: &'ast ast::Item) {
+ if item.ident.name == self.name
+ && self.sess.contains_name(&item.attrs, sym::rustc_std_internal_symbol)
+ {
+ self.spans.push(item.span);
+ }
+ visit::walk_item(self, item)
+ }
+ }
+
+ let name = Symbol::intern(&AllocatorKind::Global.fn_name(sym::alloc));
+ let mut f = Finder { sess, name, spans: Vec::new() };
+ visit::walk_crate(&mut f, krate);
+ f.spans
+}
use rustc_target::spec::abi::Abi;
crate fn collect(tcx: TyCtxt<'_>) -> Vec<String> {
- let mut collector = Collector { args: Vec::new() };
+ let mut collector = Collector { tcx, args: Vec::new() };
tcx.hir().krate().visit_all_item_likes(&mut collector);
for attr in tcx.hir().krate().item.attrs.iter() {
collector.args
}
-struct Collector {
+struct Collector<'tcx> {
+ tcx: TyCtxt<'tcx>,
args: Vec<String>,
}
-impl<'tcx> ItemLikeVisitor<'tcx> for Collector {
+impl<'tcx> ItemLikeVisitor<'tcx> for Collector<'tcx> {
fn visit_item(&mut self, it: &'tcx hir::Item<'tcx>) {
let fm = match it.kind {
hir::ItemKind::ForeignMod(ref fm) => fm,
}
// First, add all of the custom #[link_args] attributes
- for m in it.attrs.iter().filter(|a| a.check_name(sym::link_args)) {
+ let sess = &self.tcx.sess;
+ for m in it.attrs.iter().filter(|a| sess.check_name(a, sym::link_args)) {
if let Some(linkarg) = m.value_str() {
self.add_link_args(linkarg);
}
fn visit_impl_item(&mut self, _it: &'tcx hir::ImplItem<'tcx>) {}
}
-impl Collector {
+impl<'tcx> Collector<'tcx> {
fn add_link_args(&mut self, args: Symbol) {
self.args.extend(args.as_str().split(' ').filter(|s| !s.is_empty()).map(|s| s.to_string()))
}
}
// Process all of the #[link(..)]-style arguments
- for m in it.attrs.iter().filter(|a| a.check_name(sym::link)) {
+ let sess = &self.tcx.sess;
+ for m in it.attrs.iter().filter(|a| sess.check_name(a, sym::link)) {
let items = match m.meta_item_list() {
Some(item) => item,
None => continue,
"framework" => NativeLibKind::Framework,
"raw-dylib" => NativeLibKind::RawDylib,
k => {
- struct_span_err!(
- self.tcx.sess,
- item.span(),
- E0458,
- "unknown kind: `{}`",
- k
- )
- .span_label(item.span(), "unknown kind")
- .span_label(m.span, "")
- .emit();
+ struct_span_err!(sess, item.span(), E0458, "unknown kind: `{}`", k)
+ .span_label(item.span(), "unknown kind")
+ .span_label(m.span, "")
+ .emit();
NativeLibKind::Unspecified
}
};
None => continue, // skip like historical compilers
};
if cfg.is_empty() {
- self.tcx.sess.span_err(item.span(), "`cfg()` must have an argument");
+ sess.span_err(item.span(), "`cfg()` must have an argument");
} else if let cfg @ Some(..) = cfg[0].meta_item() {
lib.cfg = cfg.cloned();
} else {
- self.tcx.sess.span_err(cfg[0].span(), "invalid argument for `cfg(..)`");
+ sess.span_err(cfg[0].span(), "invalid argument for `cfg(..)`");
}
} else if item.has_name(sym::wasm_import_module) {
match item.value_str() {
Some(s) => lib.wasm_import_module = Some(s),
None => {
let msg = "must be of the form `#[link(wasm_import_module = \"...\")]`";
- self.tcx.sess.span_err(item.span(), msg);
+ sess.span_err(item.span(), msg);
}
}
} else {
let requires_name = kind_specified || lib.wasm_import_module.is_none();
if lib.name.is_none() && requires_name {
struct_span_err!(
- self.tcx.sess,
+ sess,
m.span,
E0459,
"`#[link(...)]` specified without \
/// Trait impl data.
/// FIXME: Used only from queries and can use query cache,
/// so pre-decoding can probably be avoided.
- trait_impls: FxHashMap<(u32, DefIndex), Lazy<[DefIndex]>>,
+ trait_impls:
+ FxHashMap<(u32, DefIndex), Lazy<[(DefIndex, Option<ty::fast_reject::SimplifiedType>)]>>,
/// Proc macro descriptions for this crate, if it's a proc macro crate.
raw_proc_macros: Option<&'static [ProcMacro]>,
/// Source maps for code from the crate.
};
SyntaxExtension::new(
- &sess.parse_sess,
+ sess,
kind,
self.get_span(id, sess),
helper_attrs,
// for other constructors correct visibilities
// were already encoded in metadata.
let attrs = self.get_item_attrs(def_id.index, sess);
- if attr::contains_name(&attrs, sym::non_exhaustive) {
+ if sess.contains_name(&attrs, sym::non_exhaustive) {
let crate_def_id = self.local_def_id(CRATE_DEF_INDEX);
vis = ty::Visibility::Restricted(crate_def_id);
}
.decode((self, tcx))
}
- fn get_unused_generic_params(&self, id: DefIndex) -> FiniteBitSet<u64> {
+ fn get_unused_generic_params(&self, id: DefIndex) -> FiniteBitSet<u32> {
self.root
.tables
.unused_generic_params
&self,
tcx: TyCtxt<'tcx>,
filter: Option<DefId>,
- ) -> &'tcx [DefId] {
+ ) -> &'tcx [(DefId, Option<ty::fast_reject::SimplifiedType>)] {
if self.root.is_proc_macro_crate() {
// proc-macro crates export no trait impls.
return &[];
if let Some(filter) = filter {
if let Some(impls) = self.trait_impls.get(&filter) {
- tcx.arena.alloc_from_iter(impls.decode(self).map(|idx| self.local_def_id(idx)))
+ tcx.arena.alloc_from_iter(
+ impls.decode(self).map(|(idx, simplified_self_ty)| {
+ (self.local_def_id(idx), simplified_self_ty)
+ }),
+ )
} else {
&[]
}
} else {
- tcx.arena.alloc_from_iter(
- self.trait_impls
- .values()
- .flat_map(|impls| impls.decode(self).map(|idx| self.local_def_id(idx))),
- )
+ tcx.arena.alloc_from_iter(self.trait_impls.values().flat_map(|impls| {
+ impls
+ .decode(self)
+ .map(|(idx, simplified_self_ty)| (self.local_def_id(idx), simplified_self_ty))
+ }))
}
}
use crate::rmeta::{self, encoder};
use rustc_ast::ast;
-use rustc_ast::attr;
use rustc_ast::expand::allocator::AllocatorKind;
use rustc_data_structures::svh::Svh;
use rustc_hir as hir;
def_id_arg: ty::query::query_keys::$name<$lt>,
) -> ty::query::query_values::$name<$lt> {
let _prof_timer =
- $tcx.prof.generic_activity("metadata_decode_entry");
+ $tcx.prof.generic_activity(concat!("metadata_decode_entry_", stringify!($name)));
#[allow(unused_variables)]
let ($def_id, $other) = def_id_arg.into_args();
// Mark the attrs as used
let attrs = data.get_item_attrs(id.index, sess);
for attr in attrs.iter() {
- attr::mark_used(attr);
+ sess.mark_attr_used(attr);
}
let ident = data.item_ident(id.index, sess);
use log::{debug, trace};
use rustc_ast::ast;
-use rustc_ast::attr;
use rustc_data_structures::fingerprint::Fingerprint;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_data_structures::stable_hasher::StableHasher;
fn specialized_encode(&mut self, expn: &ExpnId) -> Result<(), Self::Error> {
rustc_span::hygiene::raw_encode_expn_id(
*expn,
- &mut self.hygiene_ctxt,
+ &self.hygiene_ctxt,
ExpnDataEncodeMode::Metadata,
self,
)
let source_map_bytes = self.position() - i;
let attrs = tcx.hir().krate_attrs();
- let has_default_lib_allocator = attr::contains_name(&attrs, sym::default_lib_allocator);
+ let has_default_lib_allocator = tcx.sess.contains_name(&attrs, sym::default_lib_allocator);
let root = self.lazy(CrateRoot {
name: tcx.crate_name(LOCAL_CRATE),
} else {
None
},
- compiler_builtins: attr::contains_name(&attrs, sym::compiler_builtins),
- needs_allocator: attr::contains_name(&attrs, sym::needs_allocator),
- needs_panic_runtime: attr::contains_name(&attrs, sym::needs_panic_runtime),
- no_builtins: attr::contains_name(&attrs, sym::no_builtins),
- panic_runtime: attr::contains_name(&attrs, sym::panic_runtime),
- profiler_runtime: attr::contains_name(&attrs, sym::profiler_runtime),
+ compiler_builtins: tcx.sess.contains_name(&attrs, sym::compiler_builtins),
+ needs_allocator: tcx.sess.contains_name(&attrs, sym::needs_allocator),
+ needs_panic_runtime: tcx.sess.contains_name(&attrs, sym::needs_panic_runtime),
+ no_builtins: tcx.sess.contains_name(&attrs, sym::no_builtins),
+ panic_runtime: tcx.sess.contains_name(&attrs, sym::panic_runtime),
+ profiler_runtime: tcx.sess.contains_name(&attrs, sym::profiler_runtime),
symbol_mangling_version: tcx.sess.opts.debugging_opts.symbol_mangling_version,
crate_deps,
debug!("EntryBuilder::encode_mir({:?})", def_id);
if self.tcx.mir_keys(LOCAL_CRATE).contains(&def_id) {
record!(self.tables.mir[def_id.to_def_id()] <- self.tcx.optimized_mir(def_id));
- record!(self.tables.unused_generic_params[def_id.to_def_id()] <-
- self.tcx.unused_generic_params(def_id));
+
+ let unused = self.tcx.unused_generic_params(def_id);
+ if !unused.is_empty() {
+ record!(self.tables.unused_generic_params[def_id.to_def_id()] <- unused);
+ }
}
}
.into_iter()
.map(|(trait_def_id, mut impls)| {
// Bring everything into deterministic order for hashing
- impls.sort_by_cached_key(|&index| {
+ impls.sort_by_cached_key(|&(index, _)| {
tcx.hir().definitions().def_path_hash(LocalDefId { local_def_index: index })
});
struct ImplVisitor<'tcx> {
tcx: TyCtxt<'tcx>,
- impls: FxHashMap<DefId, Vec<DefIndex>>,
+ impls: FxHashMap<DefId, Vec<(DefIndex, Option<ty::fast_reject::SimplifiedType>)>>,
}
impl<'tcx, 'v> ItemLikeVisitor<'v> for ImplVisitor<'tcx> {
if let hir::ItemKind::Impl { .. } = item.kind {
let impl_id = self.tcx.hir().local_def_id(item.hir_id);
if let Some(trait_ref) = self.tcx.impl_trait_ref(impl_id.to_def_id()) {
- self.impls.entry(trait_ref.def_id).or_default().push(impl_id.local_def_index);
+ let simplified_self_ty =
+ ty::fast_reject::simplify_type(self.tcx, trait_ref.self_ty(), false);
+
+ self.impls
+ .entry(trait_ref.def_id)
+ .or_default()
+ .push((impl_id.local_def_index, simplified_self_ty));
}
}
}
#[derive(RustcEncodable, RustcDecodable)]
crate struct TraitImpls {
trait_id: (u32, DefIndex),
- impls: Lazy<[DefIndex]>,
+ impls: Lazy<[(DefIndex, Option<ty::fast_reject::SimplifiedType>)]>,
}
/// Define `LazyTables` and `TableBuilders` at the same time.
super_predicates: Table<DefIndex, Lazy!(ty::GenericPredicates<'tcx>)>,
mir: Table<DefIndex, Lazy!(mir::Body<'tcx>)>,
promoted_mir: Table<DefIndex, Lazy!(IndexVec<mir::Promoted, mir::Body<'tcx>>)>,
- unused_generic_params: Table<DefIndex, Lazy<FiniteBitSet<u64>>>,
+ unused_generic_params: Table<DefIndex, Lazy<FiniteBitSet<u32>>>,
}
#[derive(Copy, Clone, RustcEncodable, RustcDecodable)]
default: usize,
) {
for attr in &krate.attrs {
- if !attr.check_name(name) {
+ if !sess.check_name(attr, name) {
continue;
}
pub mod count_code_region_args {
pub const FUNCTION_SOURCE_HASH: usize = 0;
pub const COUNTER_ID: usize = 1;
- pub const START_BYTE_POS: usize = 2;
- pub const END_BYTE_POS: usize = 3;
+ pub const FILE_NAME: usize = 2;
+ pub const START_LINE: usize = 3;
+ pub const START_COL: usize = 4;
+ pub const END_LINE: usize = 5;
+ pub const END_COL: usize = 6;
}
/// Positional arguments to `libcore::coverage_counter_add()` and
pub const EXPRESSION_ID: usize = 0;
pub const LEFT_ID: usize = 1;
pub const RIGHT_ID: usize = 2;
- pub const START_BYTE_POS: usize = 3;
- pub const END_BYTE_POS: usize = 4;
+ pub const FILE_NAME: usize = 3;
+ pub const START_LINE: usize = 4;
+ pub const START_COL: usize = 5;
+ pub const END_LINE: usize = 6;
+ pub const END_COL: usize = 7;
}
/// Positional arguments to `libcore::coverage_unreachable()`
pub mod coverage_unreachable_args {
- pub const START_BYTE_POS: usize = 0;
- pub const END_BYTE_POS: usize = 1;
+ pub const FILE_NAME: usize = 0;
+ pub const START_LINE: usize = 1;
+ pub const START_COL: usize = 2;
+ pub const END_LINE: usize = 3;
+ pub const END_COL: usize = 4;
}
}
}
+ pub fn try_to_str_slice(&self) -> Option<&'tcx str> {
+ if let ConstValue::Slice { data, start, end } = *self {
+ ::std::str::from_utf8(data.inspect_with_undef_and_ptr_outside_interpreter(start..end))
+ .ok()
+ } else {
+ None
+ }
+ }
+
pub fn try_to_bits(&self, size: Size) -> Option<u128> {
self.try_to_scalar()?.to_bits(size).ok()
}
//!
//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/mir/index.html
-use crate::mir::interpret::{GlobalAlloc, Scalar};
+use crate::mir::interpret::{Allocation, ConstValue, GlobalAlloc, Scalar};
use crate::mir::visit::MirVisitable;
use crate::ty::adjustment::PointerCast;
use crate::ty::fold::{TypeFoldable, TypeFolder, TypeVisitor};
}
}
+ /// Convenience helper to make a literal-like constant from a given `&str` slice.
+ /// Since this is used to synthesize MIR, assumes `user_ty` is None.
+ pub fn const_from_str(tcx: TyCtxt<'tcx>, val: &str, span: Span) -> Operand<'tcx> {
+ let tcx = tcx;
+ let allocation = Allocation::from_byte_aligned_bytes(val.as_bytes());
+ let allocation = tcx.intern_const_alloc(allocation);
+ let const_val = ConstValue::Slice { data: allocation, start: 0, end: val.len() };
+ let ty = tcx.mk_imm_ref(tcx.lifetimes.re_erased, tcx.types.str_);
+ Operand::Constant(box Constant {
+ span,
+ user_ty: None,
+ literal: ty::Const::from_value(tcx, const_val, ty),
+ })
+ }
+
+ /// Convenience helper to make a `ConstValue` from the given `Operand`, assuming that `Operand`
+ /// wraps a constant value (such as a `&str` slice). Panics if this is not the case.
+ pub fn value_from_const(operand: &Operand<'tcx>) -> ConstValue<'tcx> {
+ match operand {
+ Operand::Constant(constant) => match constant.literal.val.try_to_value() {
+ Some(const_value) => const_value,
+ _ => panic!("{:?}: ConstValue expected", constant.literal.val),
+ },
+ _ => panic!("{:?}: Constant expected", operand),
+ }
+ }
+
pub fn to_copy(&self) -> Self {
match *self {
Operand::Copy(_) | Operand::Constant(_) => self.clone(),
TypeChecking {
query implementations_of_trait(_: (CrateNum, DefId))
- -> &'tcx [DefId] {
+ -> &'tcx [(DefId, Option<ty::fast_reject::SimplifiedType>)] {
desc { "looking up implementations of a trait in a crate" }
}
query all_trait_implementations(_: CrateNum)
- -> &'tcx [DefId] {
+ -> &'tcx [(DefId, Option<ty::fast_reject::SimplifiedType>)] {
desc { "looking up all (?) trait implementations" }
}
}
query codegen_unit(_: Symbol) -> &'tcx CodegenUnit<'tcx> {
desc { "codegen_unit" }
}
- query unused_generic_params(key: DefId) -> FiniteBitSet<u64> {
+ query unused_generic_params(key: DefId) -> FiniteBitSet<u32> {
cache_on_disk_if { key.is_local() }
desc {
|tcx| "determining which generic parameters are unused by `{}`",
impl<'tcx> DropckOutlivesResult<'tcx> {
pub fn report_overflows(&self, tcx: TyCtxt<'tcx>, span: Span, ty: Ty<'tcx>) {
- if let Some(overflow_ty) = self.overflows.iter().next() {
+ if let Some(overflow_ty) = self.overflows.get(0) {
let mut err = struct_span_err!(
tcx.sess,
span,
pub fn layout_scalar_valid_range(self, def_id: DefId) -> (Bound<u128>, Bound<u128>) {
let attrs = self.get_attrs(def_id);
let get = |name| {
- let attr = match attrs.iter().find(|a| a.check_name(name)) {
+ let attr = match attrs.iter().find(|a| self.sess.check_name(a, name)) {
Some(attr) => attr,
None => return Bound::Unbounded,
};
/// we still evaluate them eagerly.
#[inline]
pub fn lazy_normalization(self) -> bool {
- self.features().const_generics || self.features().lazy_normalization_consts
+ let features = self.features();
+ // Note: We do not enable lazy normalization for `features.min_const_generics`.
+ features.const_generics || features.lazy_normalization_consts
}
#[inline]
};
providers.is_panic_runtime = |tcx, cnum| {
assert_eq!(cnum, LOCAL_CRATE);
- attr::contains_name(tcx.hir().krate_attrs(), sym::panic_runtime)
+ tcx.sess.contains_name(tcx.hir().krate_attrs(), sym::panic_runtime)
};
providers.is_compiler_builtins = |tcx, cnum| {
assert_eq!(cnum, LOCAL_CRATE);
- attr::contains_name(tcx.hir().krate_attrs(), sym::compiler_builtins)
+ tcx.sess.contains_name(tcx.hir().krate_attrs(), sym::compiler_builtins)
};
providers.has_panic_handler = |tcx, cnum| {
assert_eq!(cnum, LOCAL_CRATE);
}
&ty::Generator(_, ref substs, _) => {
+ self.add_flags(TypeFlags::MAY_POLYMORPHIZE);
+
let substs = substs.as_generator();
let should_remove_further_specializable =
!self.flags.contains(TypeFlags::STILL_FURTHER_SPECIALIZABLE);
}
&ty::Closure(_, substs) => {
+ self.add_flags(TypeFlags::MAY_POLYMORPHIZE);
+
let substs = substs.as_closure();
let should_remove_further_specializable =
!self.flags.contains(TypeFlags::STILL_FURTHER_SPECIALIZABLE);
}
&ty::FnDef(_, substs) => {
+ self.add_flags(TypeFlags::MAY_POLYMORPHIZE);
+
self.add_substs(substs);
}
self.has_type_flags(TypeFlags::STILL_FURTHER_SPECIALIZABLE)
}
+ /// Does this value contain closures, generators or functions such that it may require
+ /// polymorphization?
+ fn may_polymorphize(&self) -> bool {
+ self.has_type_flags(TypeFlags::MAY_POLYMORPHIZE)
+ }
+
/// A visitor that does not recurse into types, works like `fn walk_shallow` in `Ty`.
fn visit_tys_shallow(&self, visit: impl FnMut(Ty<'tcx>) -> bool) -> bool {
pub struct Visitor<F>(F);
}
if let InstanceDef::Item(def) = self.def {
- let unused = tcx.unused_generic_params(def.did);
-
- if unused.is_empty() {
- // Exit early if every parameter was used.
- return self;
- }
-
- debug!("polymorphize: unused={:?}", unused);
- let polymorphized_substs =
- InternalSubsts::for_item(tcx, def.did, |param, _| match param.kind {
- // If parameter is a const or type parameter..
- ty::GenericParamDefKind::Const | ty::GenericParamDefKind::Type { .. } if
- // ..and is within range and unused..
- unused.contains(param.index).unwrap_or(false) =>
- // ..then use the identity for this parameter.
- tcx.mk_param_from_def(param),
- // Otherwise, use the parameter as before.
- _ => self.substs[param.index as usize],
- });
-
+ let polymorphized_substs = polymorphize(tcx, def.did, self.substs);
debug!("polymorphize: self={:?} polymorphized_substs={:?}", self, polymorphized_substs);
Self { def: self.def, substs: polymorphized_substs }
} else {
}
}
+fn polymorphize<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ def_id: DefId,
+ substs: SubstsRef<'tcx>,
+) -> SubstsRef<'tcx> {
+ debug!("polymorphize({:?}, {:?})", def_id, substs);
+ let unused = tcx.unused_generic_params(def_id);
+ debug!("polymorphize: unused={:?}", unused);
+
+ struct PolymorphizationFolder<'tcx> {
+ tcx: TyCtxt<'tcx>,
+ };
+
+ impl ty::TypeFolder<'tcx> for PolymorphizationFolder<'tcx> {
+ fn tcx<'a>(&'a self) -> TyCtxt<'tcx> {
+ self.tcx
+ }
+
+ fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
+ debug!("fold_ty: ty={:?}", ty);
+ match ty.kind {
+ ty::Closure(def_id, substs) => {
+ let polymorphized_substs = polymorphize(self.tcx, def_id, substs);
+ if substs == polymorphized_substs {
+ ty
+ } else {
+ self.tcx.mk_closure(def_id, polymorphized_substs)
+ }
+ }
+ ty::FnDef(def_id, substs) => {
+ let polymorphized_substs = polymorphize(self.tcx, def_id, substs);
+ if substs == polymorphized_substs {
+ ty
+ } else {
+ self.tcx.mk_fn_def(def_id, polymorphized_substs)
+ }
+ }
+ ty::Generator(def_id, substs, movability) => {
+ let polymorphized_substs = polymorphize(self.tcx, def_id, substs);
+ if substs == polymorphized_substs {
+ ty
+ } else {
+ self.tcx.mk_generator(def_id, polymorphized_substs, movability)
+ }
+ }
+ _ => ty.super_fold_with(self),
+ }
+ }
+ }
+
+ InternalSubsts::for_item(tcx, def_id, |param, _| {
+ let is_unused = unused.contains(param.index).unwrap_or(false);
+ debug!("polymorphize: param={:?} is_unused={:?}", param, is_unused);
+ match param.kind {
+ // If parameter is a const or type parameter..
+ ty::GenericParamDefKind::Const | ty::GenericParamDefKind::Type { .. } if
+ // ..and is within range and unused..
+ unused.contains(param.index).unwrap_or(false) =>
+ // ..then use the identity for this parameter.
+ tcx.mk_param_from_def(param),
+
+ // If the parameter does not contain any closures or generators, then use the
+ // substitution directly.
+ _ if !substs.may_polymorphize() => substs[param.index as usize],
+
+ // Otherwise, use the substitution after polymorphizing.
+ _ => {
+ let arg = substs[param.index as usize];
+ let polymorphized_arg = arg.fold_with(&mut PolymorphizationFolder { tcx });
+ debug!("polymorphize: arg={:?} polymorphized_arg={:?}", arg, polymorphized_arg);
+ ty::GenericArg::from(polymorphized_arg)
+ }
+ }
+ })
+}
+
fn needs_fn_once_adapter_shim(
actual_closure_kind: ty::ClosureKind,
trait_closure_kind: ty::ClosureKind,
/// Does this value have parameters/placeholders/inference variables which could be
/// replaced later, in a way that would change the results of `impl` specialization?
const STILL_FURTHER_SPECIALIZABLE = 1 << 17;
+
+ /// Does this value contain closures, generators or functions such that it may require
+ /// polymorphization?
+ const MAY_POLYMORPHIZE = 1 << 18;
}
}
let mut max_align: Option<Align> = None;
let mut min_pack: Option<Align> = None;
for attr in tcx.get_attrs(did).iter() {
- for r in attr::find_repr_attrs(&tcx.sess.parse_sess, attr) {
+ for r in attr::find_repr_attrs(&tcx.sess, attr) {
flags.insert(match r {
attr::ReprC => ReprFlags::IS_C,
attr::ReprPacked(pack) => {
}
let attrs = tcx.get_attrs(did);
- if attr::contains_name(&attrs, sym::fundamental) {
+ if tcx.sess.contains_name(&attrs, sym::fundamental) {
flags |= AdtFlags::IS_FUNDAMENTAL;
}
if Some(did) == tcx.lang_items().phantom_data() {
/// Determines whether an item is annotated with an attribute.
pub fn has_attr(self, did: DefId, attr: Symbol) -> bool {
- attr::contains_name(&self.get_attrs(did), attr)
+ self.sess.contains_name(&self.get_attrs(did), attr)
}
/// Returns `true` if this is an `auto trait`.
rustc_data_structures::sync::assert_sync::<tls::ImplicitCtxt<'_, '_>>();
let icx: &tls::ImplicitCtxt<'_, '_> = &*(context as *const tls::ImplicitCtxt<'_, '_>);
- let span_session_globals = rustc_span::SESSION_GLOBALS.with(|ssg| ssg as *const _);
- let span_session_globals = &*span_session_globals;
- let ast_session_globals = rustc_ast::attr::SESSION_GLOBALS.with(|asg| asg as *const _);
- let ast_session_globals = &*ast_session_globals;
+ let session_globals = rustc_span::SESSION_GLOBALS.with(|sg| sg as *const _);
+ let session_globals = &*session_globals;
thread::spawn(move || {
tls::enter_context(icx, |_| {
- rustc_ast::attr::SESSION_GLOBALS.set(ast_session_globals, || {
- rustc_span::SESSION_GLOBALS
- .set(span_session_globals, || tls::with(|tcx| deadlock(tcx, ®istry)))
- });
+ rustc_span::SESSION_GLOBALS
+ .set(session_globals, || tls::with(|tcx| deadlock(tcx, ®istry)))
})
});
}
substs: &[GenericArg<'tcx>],
span: Option<Span>,
) -> T {
- let mut folder =
- SubstFolder { tcx, substs, span, root_ty: None, ty_stack_depth: 0, binders_passed: 0 };
+ let mut folder = SubstFolder { tcx, substs, span, binders_passed: 0 };
(*self).fold_with(&mut folder)
}
}
/// The location for which the substitution is performed, if available.
span: Option<Span>,
- /// The root type that is being substituted, if available.
- root_ty: Option<Ty<'tcx>>,
-
- /// Depth of type stack
- ty_stack_depth: usize,
-
/// Number of region binders we have passed through while doing the substitution
binders_passed: u32,
}
let span = self.span.unwrap_or(DUMMY_SP);
let msg = format!(
"Region parameter out of range \
- when substituting in region {} (root type={:?}) \
- (index={})",
- data.name, self.root_ty, data.index
+ when substituting in region {} (index={})",
+ data.name, data.index
);
span_bug!(span, "{}", msg);
}
return t;
}
- // track the root type we were asked to substitute
- let depth = self.ty_stack_depth;
- if depth == 0 {
- self.root_ty = Some(t);
- }
- self.ty_stack_depth += 1;
-
- let t1 = match t.kind {
+ match t.kind {
ty::Param(p) => self.ty_for_param(p, t),
_ => t.super_fold_with(self),
- };
-
- assert_eq!(depth + 1, self.ty_stack_depth);
- self.ty_stack_depth -= 1;
- if depth == 0 {
- self.root_ty = None;
}
-
- t1
}
fn fold_const(&mut self, c: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> {
span_bug!(
span,
"expected type for `{:?}` ({:?}/{}) but found {:?} \
- when substituting (root type={:?}) substs={:?}",
+ when substituting, substs={:?}",
p,
source_ty,
p.index,
kind,
- self.root_ty,
self.substs,
);
}
span_bug!(
span,
"type parameter `{:?}` ({:?}/{}) out of range \
- when substituting (root type={:?}) substs={:?}",
+ when substituting, substs={:?}",
p,
source_ty,
p.index,
- self.root_ty,
self.substs,
);
}
pub(super) fn trait_impls_of_provider(tcx: TyCtxt<'_>, trait_id: DefId) -> TraitImpls {
let mut impls = TraitImpls::default();
- {
- let mut add_impl = |impl_def_id: DefId| {
- let impl_self_ty = tcx.type_of(impl_def_id);
- if impl_def_id.is_local() && impl_self_ty.references_error() {
- return;
- }
-
- if let Some(simplified_self_ty) = fast_reject::simplify_type(tcx, impl_self_ty, false) {
- impls.non_blanket_impls.entry(simplified_self_ty).or_default().push(impl_def_id);
- } else {
- impls.blanket_impls.push(impl_def_id);
- }
- };
-
- // Traits defined in the current crate can't have impls in upstream
- // crates, so we don't bother querying the cstore.
- if !trait_id.is_local() {
- for &cnum in tcx.crates().iter() {
- for &def_id in tcx.implementations_of_trait((cnum, trait_id)).iter() {
- add_impl(def_id);
+ // Traits defined in the current crate can't have impls in upstream
+ // crates, so we don't bother querying the cstore.
+ if !trait_id.is_local() {
+ for &cnum in tcx.crates().iter() {
+ for &(impl_def_id, simplified_self_ty) in
+ tcx.implementations_of_trait((cnum, trait_id)).iter()
+ {
+ if let Some(simplified_self_ty) = simplified_self_ty {
+ impls
+ .non_blanket_impls
+ .entry(simplified_self_ty)
+ .or_default()
+ .push(impl_def_id);
+ } else {
+ impls.blanket_impls.push(impl_def_id);
}
}
}
+ }
+
+ for &hir_id in tcx.hir().trait_impls(trait_id) {
+ let impl_def_id = tcx.hir().local_def_id(hir_id).to_def_id();
+
+ let impl_self_ty = tcx.type_of(impl_def_id);
+ if impl_self_ty.references_error() {
+ continue;
+ }
- for &hir_id in tcx.hir().trait_impls(trait_id) {
- add_impl(tcx.hir().local_def_id(hir_id).to_def_id());
+ if let Some(simplified_self_ty) = fast_reject::simplify_type(tcx, impl_self_ty, false) {
+ impls.non_blanket_impls.entry(simplified_self_ty).or_default().push(impl_def_id);
+ } else {
+ impls.blanket_impls.push(impl_def_id);
}
}
}
}
}
- return normal_ret;
+ normal_ret
}
/// Finds the span of arguments of a closure (within `maybe_closure_span`)
let rustc_mir_attrs = attrs
.iter()
- .filter(|attr| attr.check_name(sym::rustc_mir))
+ .filter(|attr| tcx.sess.check_name(attr, sym::rustc_mir))
.flat_map(|attr| attr.meta_item_list().into_iter().flat_map(|v| v.into_iter()));
for attr in rustc_mir_attrs {
use rustc_ast::ast::{self, MetaItem};
use rustc_middle::ty;
+use rustc_session::Session;
use rustc_span::symbol::{sym, Symbol};
pub(crate) use self::drop_flag_effects::*;
pub(crate) param_env: ty::ParamEnv<'tcx>,
}
-pub(crate) fn has_rustc_mir_with(attrs: &[ast::Attribute], name: Symbol) -> Option<MetaItem> {
+pub(crate) fn has_rustc_mir_with(
+ sess: &Session,
+ attrs: &[ast::Attribute],
+ name: Symbol,
+) -> Option<MetaItem> {
for attr in attrs {
- if attr.check_name(sym::rustc_mir) {
+ if sess.check_name(attr, sym::rustc_mir) {
let items = attr.meta_item_list();
for item in items.iter().flat_map(|l| l.iter()) {
match item.meta_item() {
if compressed.no_bytes_init() {
// Fast path: If all bytes are `uninit` then there is nothing to copy. The target range
- // is marked as unititialized but we otherwise omit changing the byte representation which may
+ // is marked as uninitialized but we otherwise omit changing the byte representation which may
// be arbitrary for uninitialized bytes.
// This also avoids writing to the target bytes so that the backing allocation is never
// touched if the bytes stay uninitialized for the whole interpreter execution. On contemporary
unused_params.contains(index).map(|unused| !unused).unwrap_or(true);
// Only recurse when generic parameters in fns, closures and generators
// are used and require substitution.
- if is_used && subst.needs_subst() {
+ match (is_used, subst.needs_subst()) {
// Just in case there are closures or generators within this subst,
// recurse.
- if subst.super_visit_with(self) {
+ (true, true) if subst.super_visit_with(self) => {
// Only return when we find a parameter so the remaining substs
// are not skipped.
return true;
}
+ // Confirm that polymorphization replaced the parameter with
+ // `ty::Param`/`ty::ConstKind::Param`.
+ (false, true) if cfg!(debug_assertions) => match subst.unpack() {
+ ty::subst::GenericArgKind::Type(ty) => {
+ assert!(matches!(ty.kind, ty::Param(_)))
+ }
+ ty::subst::GenericArgKind::Const(ct) => {
+ assert!(matches!(ct.val, ty::ConstKind::Param(_)))
+ }
+ ty::subst::GenericArgKind::Lifetime(..) => (),
+ },
+ _ => {}
}
}
false
self,
fold::{TypeFoldable, TypeVisitor},
query::Providers,
+ subst::SubstsRef,
Const, Ty, TyCtxt,
};
use rustc_span::symbol::sym;
/// Determine which generic parameters are used by the function/method/closure represented by
/// `def_id`. Returns a bitset where bits representing unused parameters are set (`is_empty`
/// indicates all parameters are used).
-fn unused_generic_params(tcx: TyCtxt<'_>, def_id: DefId) -> FiniteBitSet<u64> {
+fn unused_generic_params(tcx: TyCtxt<'_>, def_id: DefId) -> FiniteBitSet<u32> {
debug!("unused_generic_params({:?})", def_id);
if !tcx.sess.opts.debugging_opts.polymorphize {
return FiniteBitSet::new_empty();
}
+ // Polymorphization results are stored in cross-crate metadata only when there are unused
+ // parameters, so assume that non-local items must have only used parameters (else this query
+ // would not be invoked, and the cross-crate metadata used instead).
+ if !def_id.is_local() {
+ return FiniteBitSet::new_empty();
+ }
+
let generics = tcx.generics_of(def_id);
debug!("unused_generic_params: generics={:?}", generics);
// Create a bitset with N rightmost ones for each parameter.
let generics_count: u32 =
generics.count().try_into().expect("more generic parameters than can fit into a `u32`");
- let mut unused_parameters = FiniteBitSet::<u64>::new_empty();
+ let mut unused_parameters = FiniteBitSet::<u32>::new_empty();
unused_parameters.set_range(0..generics_count);
debug!("unused_generic_params: (start) unused_parameters={:?}", unused_parameters);
mark_used_by_default_parameters(tcx, def_id, generics, &mut unused_parameters);
tcx: TyCtxt<'tcx>,
def_id: DefId,
generics: &'tcx ty::Generics,
- unused_parameters: &mut FiniteBitSet<u64>,
+ unused_parameters: &mut FiniteBitSet<u32>,
) {
if !tcx.is_trait(def_id) && (tcx.is_closure(def_id) || tcx.type_of(def_id).is_generator()) {
for param in &generics.params {
fn mark_used_by_predicates<'tcx>(
tcx: TyCtxt<'tcx>,
def_id: DefId,
- unused_parameters: &mut FiniteBitSet<u64>,
+ unused_parameters: &mut FiniteBitSet<u32>,
) {
let def_id = tcx.closure_base_def_id(def_id);
- let is_self_ty_used = |unused_parameters: &mut FiniteBitSet<u64>, self_ty: Ty<'tcx>| {
+ let is_self_ty_used = |unused_parameters: &mut FiniteBitSet<u32>, self_ty: Ty<'tcx>| {
debug!("unused_generic_params: self_ty={:?}", self_ty);
if let ty::Param(param) = self_ty.kind {
!unused_parameters.contains(param.index).unwrap_or(false)
}
};
- let mark_ty = |unused_parameters: &mut FiniteBitSet<u64>, ty: Ty<'tcx>| {
+ let mark_ty = |unused_parameters: &mut FiniteBitSet<u32>, ty: Ty<'tcx>| {
let mut vis = UsedGenericParametersVisitor { tcx, def_id, unused_parameters };
ty.visit_with(&mut vis);
};
tcx: TyCtxt<'tcx>,
def_id: DefId,
generics: &'tcx ty::Generics,
- unused_parameters: &FiniteBitSet<u64>,
+ unused_parameters: &FiniteBitSet<u32>,
) {
debug!("emit_unused_generic_params_error: def_id={:?}", def_id);
let base_def_id = tcx.closure_base_def_id(def_id);
- if !tcx.get_attrs(base_def_id).iter().any(|a| a.check_name(sym::rustc_polymorphize_error)) {
+ if !tcx
+ .get_attrs(base_def_id)
+ .iter()
+ .any(|a| tcx.sess.check_name(a, sym::rustc_polymorphize_error))
+ {
return;
}
struct UsedGenericParametersVisitor<'a, 'tcx> {
tcx: TyCtxt<'tcx>,
def_id: DefId,
- unused_parameters: &'a mut FiniteBitSet<u64>,
+ unused_parameters: &'a mut FiniteBitSet<u32>,
+}
+
+impl<'a, 'tcx> UsedGenericParametersVisitor<'a, 'tcx> {
+ /// Invoke `unused_generic_params` on a body contained within the current item (e.g.
+ /// a closure, generator or constant).
+ fn visit_child_body(&mut self, def_id: DefId, substs: SubstsRef<'tcx>) {
+ let unused = self.tcx.unused_generic_params(def_id);
+ debug!(
+ "visit_child_body: unused_parameters={:?} unused={:?}",
+ self.unused_parameters, unused
+ );
+ for (i, arg) in substs.iter().enumerate() {
+ let i = i.try_into().unwrap();
+ if !unused.contains(i).unwrap_or(false) {
+ arg.visit_with(self);
+ }
+ }
+ debug!("visit_child_body: unused_parameters={:?}", self.unused_parameters);
+ }
}
impl<'a, 'tcx> Visitor<'tcx> for UsedGenericParametersVisitor<'a, 'tcx> {
self.unused_parameters.clear(param.index);
false
}
+ ty::ConstKind::Unevaluated(_, _, Some(p)) => {
+ // If there is a promoted, don't look at the substs - since it will always contain
+ // the generic parameters, instead, traverse the promoted MIR.
+ let promoted = self.tcx.promoted_mir(self.def_id);
+ self.visit_body(&promoted[p]);
+ false
+ }
+ ty::ConstKind::Unevaluated(def_id, unevaluated_substs, None) => {
+ self.visit_child_body(def_id.did, unevaluated_substs);
+ false
+ }
_ => c.super_visit_with(self),
}
}
// Consider any generic parameters used by any closures/generators as used in the
// parent.
- let unused = self.tcx.unused_generic_params(def_id);
- debug!(
- "visit_ty: unused_parameters={:?} unused={:?}",
- self.unused_parameters, unused
- );
- for (i, arg) in substs.iter().enumerate() {
- let i = i.try_into().unwrap();
- if !unused.contains(i).unwrap_or(false) {
- arg.visit_with(self);
- }
- }
- debug!("visit_ty: unused_parameters={:?}", self.unused_parameters);
-
+ self.visit_child_body(def_id, substs);
false
}
ty::Param(param) => {
};
use crate::transform::{MirPass, MirSource};
-/// The maximum number of bytes that we'll allocate space for a return value.
+/// The maximum number of bytes that we'll allocate space for a local or the return value.
+/// Needed for #66397, because otherwise we eval into large places and that can cause OOM or just
+/// Severely regress performance.
const MAX_ALLOC_LIMIT: u64 = 1024;
/// Macro for machine-specific `InterpError` without allocation.
written_only_inside_own_block_locals: FxHashSet<Local>,
/// Locals that need to be cleared after every block terminates.
only_propagate_inside_block_locals: BitSet<Local>,
+ can_const_prop: IndexVec<Local, ConstPropMode>,
}
impl<'mir, 'tcx> ConstPropMachine<'mir, 'tcx> {
- fn new(only_propagate_inside_block_locals: BitSet<Local>) -> Self {
+ fn new(
+ only_propagate_inside_block_locals: BitSet<Local>,
+ can_const_prop: IndexVec<Local, ConstPropMode>,
+ ) -> Self {
Self {
stack: Vec::new(),
written_only_inside_own_block_locals: Default::default(),
only_propagate_inside_block_locals,
+ can_const_prop,
}
}
}
local: Local,
) -> InterpResult<'tcx, Result<&'a mut LocalValue<Self::PointerTag>, MemPlace<Self::PointerTag>>>
{
+ if ecx.machine.can_const_prop[local] == ConstPropMode::NoPropagation {
+ throw_machine_stop_str!("tried to write to a local that is marked as not propagatable")
+ }
if frame == 0 && ecx.machine.only_propagate_inside_block_locals.contains(local) {
ecx.machine.written_only_inside_own_block_locals.insert(local);
}
struct ConstPropagator<'mir, 'tcx> {
ecx: InterpCx<'mir, 'tcx, ConstPropMachine<'mir, 'tcx>>,
tcx: TyCtxt<'tcx>,
- can_const_prop: IndexVec<Local, ConstPropMode>,
param_env: ParamEnv<'tcx>,
// FIXME(eddyb) avoid cloning these two fields more than once,
// by accessing them through `ecx` instead.
let param_env = tcx.param_env_reveal_all_normalized(def_id);
let span = tcx.def_span(def_id);
- let can_const_prop = CanConstProp::check(body);
+ // FIXME: `CanConstProp::check` computes the layout of all locals, return those layouts
+ // so we can write them to `ecx.frame_mut().locals.layout, reducing the duplication in
+ // `layout_of` query invocations.
+ let can_const_prop = CanConstProp::check(tcx, param_env, body);
let mut only_propagate_inside_block_locals = BitSet::new_empty(can_const_prop.len());
for (l, mode) in can_const_prop.iter_enumerated() {
if *mode == ConstPropMode::OnlyInsideOwnBlock {
tcx,
span,
param_env,
- ConstPropMachine::new(only_propagate_inside_block_locals),
+ ConstPropMachine::new(only_propagate_inside_block_locals, can_const_prop),
(),
);
ecx,
tcx,
param_env,
- can_const_prop,
// FIXME(eddyb) avoid cloning these two fields more than once,
// by accessing them through `ecx` instead.
source_scopes: body.source_scopes.clone(),
fn const_prop(
&mut self,
rvalue: &Rvalue<'tcx>,
- place_layout: TyAndLayout<'tcx>,
source_info: SourceInfo,
place: Place<'tcx>,
) -> Option<()> {
- // #66397: Don't try to eval into large places as that can cause an OOM
- if place_layout.size >= Size::from_bytes(MAX_ALLOC_LIMIT) {
- return None;
- }
-
// Perform any special handling for specific Rvalue types.
// Generally, checks here fall into one of two categories:
// 1. Additional checking to provide useful lints to the user
impl CanConstProp {
/// Returns true if `local` can be propagated
- fn check(body: &Body<'_>) -> IndexVec<Local, ConstPropMode> {
+ fn check(
+ tcx: TyCtxt<'tcx>,
+ param_env: ParamEnv<'tcx>,
+ body: &Body<'tcx>,
+ ) -> IndexVec<Local, ConstPropMode> {
let mut cpv = CanConstProp {
can_const_prop: IndexVec::from_elem(ConstPropMode::FullConstProp, &body.local_decls),
found_assignment: BitSet::new_empty(body.local_decls.len()),
),
};
for (local, val) in cpv.can_const_prop.iter_enumerated_mut() {
+ let ty = body.local_decls[local].ty;
+ match tcx.layout_of(param_env.and(ty)) {
+ Ok(layout) if layout.size < Size::from_bytes(MAX_ALLOC_LIMIT) => {}
+ // Either the layout fails to compute, then we can't use this local anyway
+ // or the local is too large, then we don't want to.
+ _ => {
+ *val = ConstPropMode::NoPropagation;
+ continue;
+ }
+ }
// Cannot use args at all
// Cannot use locals because if x < y { y - x } else { x - y } would
// lint for x != y
let source_info = statement.source_info;
self.source_info = Some(source_info);
if let StatementKind::Assign(box (place, ref mut rval)) = statement.kind {
- let place_ty: Ty<'tcx> = place.ty(&self.local_decls, self.tcx).ty;
- if let Ok(place_layout) = self.tcx.layout_of(self.param_env.and(place_ty)) {
- let can_const_prop = self.can_const_prop[place.local];
- if let Some(()) = self.const_prop(rval, place_layout, source_info, place) {
- // This will return None if the above `const_prop` invocation only "wrote" a
- // type whose creation requires no write. E.g. a generator whose initial state
- // consists solely of uninitialized memory (so it doesn't capture any locals).
- if let Some(value) = self.get_const(place) {
- if self.should_const_prop(value) {
- trace!("replacing {:?} with {:?}", rval, value);
- self.replace_with_const(rval, value, source_info);
- if can_const_prop == ConstPropMode::FullConstProp
- || can_const_prop == ConstPropMode::OnlyInsideOwnBlock
- {
- trace!("propagated into {:?}", place);
- }
+ let can_const_prop = self.ecx.machine.can_const_prop[place.local];
+ if let Some(()) = self.const_prop(rval, source_info, place) {
+ // This will return None if the above `const_prop` invocation only "wrote" a
+ // type whose creation requires no write. E.g. a generator whose initial state
+ // consists solely of uninitialized memory (so it doesn't capture any locals).
+ if let Some(value) = self.get_const(place) {
+ if self.should_const_prop(value) {
+ trace!("replacing {:?} with {:?}", rval, value);
+ self.replace_with_const(rval, value, source_info);
+ if can_const_prop == ConstPropMode::FullConstProp
+ || can_const_prop == ConstPropMode::OnlyInsideOwnBlock
+ {
+ trace!("propagated into {:?}", place);
}
}
- match can_const_prop {
- ConstPropMode::OnlyInsideOwnBlock => {
- trace!(
- "found local restricted to its block. \
+ }
+ match can_const_prop {
+ ConstPropMode::OnlyInsideOwnBlock => {
+ trace!(
+ "found local restricted to its block. \
Will remove it from const-prop after block is finished. Local: {:?}",
- place.local
- );
- }
- ConstPropMode::OnlyPropagateInto | ConstPropMode::NoPropagation => {
- trace!("can't propagate into {:?}", place);
- if place.local != RETURN_PLACE {
- Self::remove_const(&mut self.ecx, place.local);
- }
+ place.local
+ );
+ }
+ ConstPropMode::OnlyPropagateInto | ConstPropMode::NoPropagation => {
+ trace!("can't propagate into {:?}", place);
+ if place.local != RETURN_PLACE {
+ Self::remove_const(&mut self.ecx, place.local);
}
- ConstPropMode::FullConstProp => {}
}
- } else {
- // Const prop failed, so erase the destination, ensuring that whatever happens
- // from here on, does not know about the previous value.
- // This is important in case we have
- // ```rust
- // let mut x = 42;
- // x = SOME_MUTABLE_STATIC;
- // // x must now be undefined
- // ```
- // FIXME: we overzealously erase the entire local, because that's easier to
- // implement.
- trace!(
- "propagation into {:?} failed.
- Nuking the entire site from orbit, it's the only way to be sure",
- place,
- );
- Self::remove_const(&mut self.ecx, place.local);
+ ConstPropMode::FullConstProp => {}
}
} else {
+ // Const prop failed, so erase the destination, ensuring that whatever happens
+ // from here on, does not know about the previous value.
+ // This is important in case we have
+ // ```rust
+ // let mut x = 42;
+ // x = SOME_MUTABLE_STATIC;
+ // // x must now be undefined
+ // ```
+ // FIXME: we overzealously erase the entire local, because that's easier to
+ // implement.
trace!(
- "cannot propagate into {:?}, because the type of the local is generic.",
+ "propagation into {:?} failed.
+ Nuking the entire site from orbit, it's the only way to be sure",
place,
);
Self::remove_const(&mut self.ecx, place.local);
use rustc_hir::lang_items;
use rustc_middle::hir;
use rustc_middle::ich::StableHashingContext;
+use rustc_middle::mir;
use rustc_middle::mir::coverage::*;
use rustc_middle::mir::interpret::Scalar;
+use rustc_middle::mir::traversal;
use rustc_middle::mir::{
- self, traversal, BasicBlock, BasicBlockData, CoverageInfo, Operand, Place, SourceInfo,
- SourceScope, StatementKind, Terminator, TerminatorKind,
+ BasicBlock, BasicBlockData, CoverageInfo, Operand, Place, SourceInfo, SourceScope,
+ StatementKind, Terminator, TerminatorKind,
};
use rustc_middle::ty;
use rustc_middle::ty::query::Providers;
-use rustc_middle::ty::FnDef;
-use rustc_middle::ty::TyCtxt;
+use rustc_middle::ty::{FnDef, TyCtxt};
use rustc_span::def_id::DefId;
-use rustc_span::{Pos, Span};
+use rustc_span::{FileName, Pos, RealFileName, Span};
/// Inserts call to count_code_region() as a placeholder to be replaced during code generation with
/// the intrinsic llvm.instrprof.increment.
struct InjectedCall<'tcx> {
func: Operand<'tcx>,
args: Vec<Operand<'tcx>>,
+ span: Span,
inject_at: Span,
}
let _ignore = mir_body;
let id = self.next_counter();
let function_source_hash = self.function_source_hash();
- let code_region = body_span;
let scope = rustc_middle::mir::OUTERMOST_SOURCE_SCOPE;
let is_cleanup = false;
let next_block = rustc_middle::mir::START_BLOCK;
self.inject_call(
- self.make_counter(id, function_source_hash, code_region),
+ self.make_counter(id, function_source_hash, body_span),
scope,
is_cleanup,
next_block,
let op = if add { Op::Add } else { Op::Subtract };
let rhs = 2;
- let code_region = body_span;
let scope = rustc_middle::mir::OUTERMOST_SOURCE_SCOPE;
let is_cleanup = false;
let next_block = rustc_middle::mir::START_BLOCK;
let id = self.next_expression();
self.inject_call(
- self.make_expression(id, code_region, lhs, op, rhs),
+ self.make_expression(id, body_span, lhs, op, rhs),
scope,
is_cleanup,
next_block,
}
}
- fn make_counter(
- &self,
- id: u32,
- function_source_hash: u64,
- code_region: Span,
- ) -> InjectedCall<'tcx> {
- let inject_at = code_region.shrink_to_lo();
+ fn make_counter(&self, id: u32, function_source_hash: u64, span: Span) -> InjectedCall<'tcx> {
+ let inject_at = span.shrink_to_lo();
let func = function_handle(
self.tcx,
debug_assert_eq!(COUNTER_ID, args.len());
args.push(self.const_u32(id, inject_at));
- debug_assert_eq!(START_BYTE_POS, args.len());
- args.push(self.const_u32(code_region.lo().to_u32(), inject_at));
-
- debug_assert_eq!(END_BYTE_POS, args.len());
- args.push(self.const_u32(code_region.hi().to_u32(), inject_at));
-
- InjectedCall { func, args, inject_at }
+ InjectedCall { func, args, span, inject_at }
}
fn make_expression(
&self,
id: u32,
- code_region: Span,
+ span: Span,
lhs: u32,
op: Op,
rhs: u32,
) -> InjectedCall<'tcx> {
- let inject_at = code_region.shrink_to_lo();
+ let inject_at = span.shrink_to_lo();
let func = function_handle(
self.tcx,
debug_assert_eq!(RIGHT_ID, args.len());
args.push(self.const_u32(rhs, inject_at));
- debug_assert_eq!(START_BYTE_POS, args.len());
- args.push(self.const_u32(code_region.lo().to_u32(), inject_at));
-
- debug_assert_eq!(END_BYTE_POS, args.len());
- args.push(self.const_u32(code_region.hi().to_u32(), inject_at));
-
- InjectedCall { func, args, inject_at }
+ InjectedCall { func, args, span, inject_at }
}
fn inject_call(
is_cleanup: bool,
next_block: BasicBlock,
) {
- let InjectedCall { func, args, inject_at } = call;
+ let InjectedCall { func, mut args, span, inject_at } = call;
debug!(
" injecting {}call to {:?}({:?}) at: {:?}, scope: {:?}",
if is_cleanup { "cleanup " } else { "" },
let mut patch = MirPatch::new(self.mir_body);
+ let (file_name, start_line, start_col, end_line, end_col) = self.code_region(&span);
+
+ args.push(self.const_str(&file_name, inject_at));
+ args.push(self.const_u32(start_line, inject_at));
+ args.push(self.const_u32(start_col, inject_at));
+ args.push(self.const_u32(end_line, inject_at));
+ args.push(self.const_u32(end_col, inject_at));
+
let temp = patch.new_temp(self.tcx.mk_unit(), inject_at);
let new_block = patch.new_block(placeholder_block(inject_at, scope, is_cleanup));
patch.patch_terminator(
self.mir_body.basic_blocks_mut().swap(next_block, new_block);
}
+ /// Convert the Span into its file name, start line and column, and end line and column
+ fn code_region(&self, span: &Span) -> (String, u32, u32, u32, u32) {
+ let source_map = self.tcx.sess.source_map();
+ let start = source_map.lookup_char_pos(span.lo());
+ let end = if span.hi() == span.lo() {
+ start.clone()
+ } else {
+ let end = source_map.lookup_char_pos(span.hi());
+ debug_assert_eq!(
+ start.file.name,
+ end.file.name,
+ "Region start ({:?} -> {:?}) and end ({:?} -> {:?}) don't come from the same source file!",
+ span.lo(),
+ start,
+ span.hi(),
+ end
+ );
+ end
+ };
+ match &start.file.name {
+ FileName::Real(RealFileName::Named(path)) => (
+ path.to_string_lossy().to_string(),
+ start.line as u32,
+ start.col.to_u32() + 1,
+ end.line as u32,
+ end.col.to_u32() + 1,
+ ),
+ _ => {
+ bug!("start.file.name should be a RealFileName, but it was: {:?}", start.file.name)
+ }
+ }
+ }
+
+ fn const_str(&self, value: &str, span: Span) -> Operand<'tcx> {
+ Operand::const_from_str(self.tcx, value, span)
+ }
+
fn const_u32(&self, value: u32, span: Span) -> Operand<'tcx> {
Operand::const_from_scalar(self.tcx, self.tcx.types.u32, Scalar::from_u32(value), span)
}
fn args_required_const(tcx: TyCtxt<'_>, def_id: DefId) -> Option<Vec<usize>> {
let attrs = tcx.get_attrs(def_id);
- let attr = attrs.iter().find(|a| a.check_name(sym::rustc_args_required_const))?;
+ let attr = attrs.iter().find(|a| tcx.sess.check_name(a, sym::rustc_args_required_const))?;
let mut ret = vec![];
for meta in attr.meta_item_list()? {
match meta.literal()?.kind {
_,
) => Err((span, "function pointer casts are not allowed in const fn".into())),
Rvalue::Cast(CastKind::Pointer(PointerCast::Unsize), op, cast_ty) => {
- let pointee_ty = cast_ty.builtin_deref(true).unwrap().ty;
+ let pointee_ty = if let Some(deref_ty) = cast_ty.builtin_deref(true) {
+ deref_ty.ty
+ } else {
+ // We cannot allow this for now.
+ return Err((
+ span,
+ "unsizing casts are only allowed for references right now".into(),
+ ));
+ };
let unsized_ty = tcx.struct_tail_erasing_lifetimes(pointee_ty, tcx.param_env(def_id));
if let ty::Slice(_) | ty::Str = unsized_ty.kind {
check_operand(tcx, op, span, def_id, body)?;
// However, we cannot allow stable `const fn`s to use unstable features without an explicit
// opt-in via `allow_internal_unstable`.
- attr::allow_internal_unstable(&tcx.get_attrs(def_id), &tcx.sess.diagnostic())
+ attr::allow_internal_unstable(&tcx.sess, &tcx.get_attrs(def_id))
.map_or(false, |mut features| features.any(|name| name == feature_gate))
}
// However, we cannot allow stable `const fn`s to use unstable features without an explicit
// opt-in via `allow_internal_unstable`.
- attr::allow_internal_unstable(&tcx.get_attrs(def_id), &tcx.sess.diagnostic())
+ attr::allow_internal_unstable(&tcx.sess, &tcx.get_attrs(def_id))
.map_or(false, |mut features| features.any(|name| name == feature_gate))
}
let param_env = tcx.param_env(def_id);
let move_data = MoveData::gather_moves(body, tcx, param_env).unwrap();
let mdpe = MoveDataParamEnv { move_data, param_env };
+ let sess = &tcx.sess;
- if has_rustc_mir_with(&attributes, sym::rustc_peek_maybe_init).is_some() {
+ if has_rustc_mir_with(sess, &attributes, sym::rustc_peek_maybe_init).is_some() {
let flow_inits = MaybeInitializedPlaces::new(tcx, body, &mdpe)
.into_engine(tcx, body, def_id)
.iterate_to_fixpoint();
sanity_check_via_rustc_peek(tcx, body, def_id, &attributes, &flow_inits);
}
- if has_rustc_mir_with(&attributes, sym::rustc_peek_maybe_uninit).is_some() {
+ if has_rustc_mir_with(sess, &attributes, sym::rustc_peek_maybe_uninit).is_some() {
let flow_uninits = MaybeUninitializedPlaces::new(tcx, body, &mdpe)
.into_engine(tcx, body, def_id)
.iterate_to_fixpoint();
sanity_check_via_rustc_peek(tcx, body, def_id, &attributes, &flow_uninits);
}
- if has_rustc_mir_with(&attributes, sym::rustc_peek_definite_init).is_some() {
+ if has_rustc_mir_with(sess, &attributes, sym::rustc_peek_definite_init).is_some() {
let flow_def_inits = DefinitelyInitializedPlaces::new(tcx, body, &mdpe)
.into_engine(tcx, body, def_id)
.iterate_to_fixpoint();
sanity_check_via_rustc_peek(tcx, body, def_id, &attributes, &flow_def_inits);
}
- if has_rustc_mir_with(&attributes, sym::rustc_peek_indirectly_mutable).is_some() {
+ if has_rustc_mir_with(sess, &attributes, sym::rustc_peek_indirectly_mutable).is_some() {
let flow_mut_borrowed = MaybeMutBorrowedLocals::mut_borrows_only(tcx, body, param_env)
.into_engine(tcx, body, def_id)
.iterate_to_fixpoint();
sanity_check_via_rustc_peek(tcx, body, def_id, &attributes, &flow_mut_borrowed);
}
- if has_rustc_mir_with(&attributes, sym::rustc_peek_liveness).is_some() {
+ if has_rustc_mir_with(sess, &attributes, sym::rustc_peek_liveness).is_some() {
let flow_liveness =
MaybeLiveLocals.into_engine(tcx, body, def_id).iterate_to_fixpoint();
sanity_check_via_rustc_peek(tcx, body, def_id, &attributes, &flow_liveness);
}
- if has_rustc_mir_with(&attributes, sym::stop_after_dataflow).is_some() {
+ if has_rustc_mir_with(sess, &attributes, sym::stop_after_dataflow).is_some() {
tcx.sess.fatal("stop_after_dataflow ended compilation");
}
}
}
trace!("SUCCESS: optimization applies!");
- return true;
+ true
}
impl<'tcx> MirPass<'tcx> for SimplifyArmIdentity {
T: Relate<'tcx>,
{
self.relate(a.skip_binder(), b.skip_binder())?;
- Ok(a.clone())
+ Ok(a)
}
}
fn should_abort_on_panic(tcx: TyCtxt<'_>, fn_def_id: LocalDefId, _abi: Abi) -> bool {
// Validate `#[unwind]` syntax regardless of platform-specific panic strategy.
let attrs = &tcx.get_attrs(fn_def_id.to_def_id());
- let unwind_attr = attr::find_unwind_attr(Some(tcx.sess.diagnostic()), attrs);
+ let unwind_attr = attr::find_unwind_attr(&tcx.sess, attrs);
// We never unwind, so it's not relevant to stop an unwind.
if tcx.sess.panic_strategy() != PanicStrategy::Unwind {
use crate::thir::*;
use rustc_ast::ast;
-use rustc_ast::attr;
use rustc_hir as hir;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::Node;
// Some functions always have overflow checks enabled,
// however, they may not get codegen'd, depending on
// the settings for the crate they are codegened in.
- let mut check_overflow = attr::contains_name(attrs, sym::rustc_inherit_overflow_checks);
+ let mut check_overflow = tcx.sess.contains_name(attrs, sym::rustc_inherit_overflow_checks);
// Respect -C overflow-checks.
check_overflow |= tcx.sess.overflow_checks();
-use rustc_ast::token::{self, Token, TokenKind};
+use rustc_ast::token::{self, CommentKind, Token, TokenKind};
use rustc_ast::util::comments;
use rustc_data_structures::sync::Lrc;
use rustc_errors::{error_code, Applicability, DiagnosticBuilder, FatalError};
match token {
rustc_lexer::TokenKind::LineComment => {
let string = self.str_from(start);
- // comments with only more "/"s are not doc comments
- if comments::is_line_doc_comment(string) {
+ if let Some(attr_style) = comments::line_doc_comment_style(string) {
self.forbid_bare_cr(start, string, "bare CR not allowed in doc-comment");
- token::DocComment(Symbol::intern(string))
+ // Opening delimiter of the length 3 is not included into the symbol.
+ token::DocComment(CommentKind::Line, attr_style, Symbol::intern(&string[3..]))
} else {
token::Comment
}
}
rustc_lexer::TokenKind::BlockComment { terminated } => {
let string = self.str_from(start);
- // block comments starting with "/**" or "/*!" are doc-comments
- // but comments with only "*"s between two "/"s are not
- let is_doc_comment = comments::is_block_doc_comment(string);
+ let attr_style = comments::block_doc_comment_style(string, terminated);
if !terminated {
- let msg = if is_doc_comment {
+ let msg = if attr_style.is_some() {
"unterminated block doc-comment"
} else {
"unterminated block comment"
FatalError.raise();
}
- if is_doc_comment {
+ if let Some(attr_style) = attr_style {
self.forbid_bare_cr(start, string, "bare CR not allowed in block doc-comment");
- token::DocComment(Symbol::intern(string))
+ // Opening delimiter of the length 3 and closing delimiter of the length 2
+ // are not included into the symbol.
+ token::DocComment(
+ CommentKind::Block,
+ attr_style,
+ Symbol::intern(&string[3..string.len() - if terminated { 2 } else { 0 }]),
+ )
} else {
token::Comment
}
(&OpenDelim(a), &OpenDelim(b)) | (&CloseDelim(a), &CloseDelim(b)) => a == b,
- (&DocComment(a), &DocComment(b)) | (&Shebang(a), &Shebang(b)) => a == b,
+ (&DocComment(a1, a2, a3), &DocComment(b1, b2, b3)) => a1 == b1 && a2 == b2 && a3 == b3,
+
+ (&Shebang(a), &Shebang(b)) => a == b,
(&Literal(a), &Literal(b)) => a == b,
let item = match attr.kind {
ast::AttrKind::Normal(ref item) => item,
- ast::AttrKind::DocComment(_) => {
+ ast::AttrKind::DocComment(..) => {
let stream = parse_stream_from_source_str(macro_filename, source, sess, Some(span));
builder.push(stream);
continue;
use rustc_ast::ast;
use rustc_ast::attr;
use rustc_ast::token::{self, Nonterminal};
-use rustc_ast::util::comments;
use rustc_ast_pretty::pprust;
use rustc_errors::{error_code, PResult};
-use rustc_span::{Span, Symbol};
+use rustc_span::Span;
use log::debug;
let attr = self.parse_attribute_with_inner_parse_policy(inner_parse_policy)?;
attrs.push(attr);
just_parsed_doc_comment = false;
- } else if let token::DocComment(s) = self.token.kind {
- let attr = self.mk_doc_comment(s);
+ } else if let token::DocComment(comment_kind, attr_style, data) = self.token.kind {
+ let attr = attr::mk_doc_comment(comment_kind, attr_style, data, self.token.span);
if attr.style != ast::AttrStyle::Outer {
self.sess
.span_diagnostic
Ok(attrs)
}
- fn mk_doc_comment(&self, s: Symbol) -> ast::Attribute {
- attr::mk_doc_comment(comments::doc_comment_style(s), s, self.token.span)
- }
-
/// Matches `attribute = # ! [ meta_item ]`.
///
/// If `permit_inner` is `true`, then a leading `!` indicates an inner
let attr = self.parse_attribute(true)?;
assert_eq!(attr.style, ast::AttrStyle::Inner);
attrs.push(attr);
- } else if let token::DocComment(s) = self.token.kind {
+ } else if let token::DocComment(comment_kind, attr_style, data) = self.token.kind {
// We need to get the position of this token before we bump.
- let attr = self.mk_doc_comment(s);
+ let attr = attr::mk_doc_comment(comment_kind, attr_style, data, self.token.span);
if attr.style == ast::AttrStyle::Inner {
attrs.push(attr);
self.bump();
}
pub(super) fn eat_incorrect_doc_comment_for_param_type(&mut self) {
- if let token::DocComment(_) = self.token.kind {
+ if let token::DocComment(..) = self.token.kind {
self.struct_span_err(
self.token.span,
"documentation comments cannot be applied to a function parameter's type",
self.expect(&token::Colon)?;
let ty = self.parse_ty()?;
- self.sess.gated_spans.gate(sym::const_generics, const_span.to(self.prev_token.span));
+ self.sess.gated_spans.gate(sym::min_const_generics, const_span.to(self.prev_token.span));
Ok(GenericParam {
ident,
/// Recover on a doc comment before `}`.
fn recover_doc_comment_before_brace(&mut self) -> bool {
- if let token::DocComment(_) = self.token.kind {
+ if let token::DocComment(..) = self.token.kind {
if self.look_ahead(1, |tok| tok == &token::CloseDelim(token::Brace)) {
struct_span_err!(
self.diagnostic(),
self.bump();
}
token::CloseDelim(token::Brace) => {}
- token::DocComment(_) => {
+ token::DocComment(..) => {
let previous_span = self.prev_token.span;
let mut err = self.span_fatal_err(self.token.span, Error::UselessDocComment);
self.bump(); // consume the doc comment
use rustc_ast::ptr::P;
use rustc_ast::token::{self, DelimToken, Token, TokenKind};
use rustc_ast::tokenstream::{self, DelimSpan, TokenStream, TokenTree, TreeAndJoint};
-use rustc_ast::util::comments::{doc_comment_style, strip_doc_comment_decoration};
use rustc_ast_pretty::pprust;
use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, FatalError, PResult};
use rustc_session::parse::ParseSess;
/// error.
pub(super) unclosed_delims: Vec<UnmatchedBrace>,
last_unexpected_token_span: Option<Span>,
+ /// Span pointing at the `:` for the last type ascription the parser has seen, and whether it
+ /// looked like it could have been a mistyped path or literal `Option:Some(42)`).
pub last_type_ascription: Option<(Span, bool /* likely path typo */)>,
/// If present, this `Parser` is not parsing Rust code but rather a macro call.
subparser_name: Option<&'static str>,
}
fn next_desugared(&mut self) -> Token {
- let (name, sp) = match self.next() {
- Token { kind: token::DocComment(name), span } => (name, span),
+ let (data, attr_style, sp) = match self.next() {
+ Token { kind: token::DocComment(_, attr_style, data), span } => {
+ (data, attr_style, span)
+ }
tok => return tok,
};
- let stripped = strip_doc_comment_decoration(name);
-
// Searches for the occurrences of `"#*` and returns the minimum number of `#`s
// required to wrap the text.
let mut num_of_hashes = 0;
let mut count = 0;
- for ch in stripped.chars() {
+ for ch in data.as_str().chars() {
count = match ch {
'"' => 1,
'#' if count > 0 => count + 1,
[
TokenTree::token(token::Ident(sym::doc, false), sp),
TokenTree::token(token::Eq, sp),
- TokenTree::token(
- TokenKind::lit(token::StrRaw(num_of_hashes), Symbol::intern(&stripped), None),
- sp,
- ),
+ TokenTree::token(TokenKind::lit(token::StrRaw(num_of_hashes), data, None), sp),
]
.iter()
.cloned()
TokenCursorFrame::new(
delim_span,
token::NoDelim,
- &if doc_comment_style(name) == AttrStyle::Inner {
+ &if attr_style == AttrStyle::Inner {
[TokenTree::token(token::Pound, sp), TokenTree::token(token::Not, sp), body]
.iter()
.cloned()
}
let r_start = str_style.map(|r| r + 1).unwrap_or(0);
- let r_end = str_style.map(|r| r).unwrap_or(0);
+ let r_end = str_style.unwrap_or(0);
let s = &snippet[r_start + 1..snippet.len() - r_end - 1];
(find_skips(s, str_style.is_some()), true)
}
use rustc_middle::ty::TyCtxt;
use rustc_ast::ast::{Attribute, NestedMetaItem};
-use rustc_ast::attr;
use rustc_errors::struct_span_err;
use rustc_hir as hir;
use rustc_hir::def_id::LocalDefId;
) {
let mut is_valid = true;
for attr in attrs {
- is_valid &= if attr.check_name(sym::inline) {
+ is_valid &= if self.tcx.sess.check_name(attr, sym::inline) {
self.check_inline(hir_id, attr, span, target)
- } else if attr.check_name(sym::non_exhaustive) {
+ } else if self.tcx.sess.check_name(attr, sym::non_exhaustive) {
self.check_non_exhaustive(attr, span, target)
- } else if attr.check_name(sym::marker) {
+ } else if self.tcx.sess.check_name(attr, sym::marker) {
self.check_marker(attr, span, target)
- } else if attr.check_name(sym::target_feature) {
+ } else if self.tcx.sess.check_name(attr, sym::target_feature) {
self.check_target_feature(attr, span, target)
- } else if attr.check_name(sym::track_caller) {
+ } else if self.tcx.sess.check_name(attr, sym::track_caller) {
self.check_track_caller(&attr.span, attrs, span, target)
- } else if attr.check_name(sym::doc) {
+ } else if self.tcx.sess.check_name(attr, sym::doc) {
self.check_doc_alias(attr)
} else {
true
target: Target,
) -> bool {
match target {
- _ if attr::contains_name(attrs, sym::naked) => {
+ _ if self.tcx.sess.contains_name(attrs, sym::naked) => {
struct_span_err!(
self.tcx.sess,
*attr_span,
// ```
let hints: Vec<_> = attrs
.iter()
- .filter(|attr| attr.check_name(sym::repr))
+ .filter(|attr| self.tcx.sess.check_name(attr, sym::repr))
.filter_map(|attr| attr.meta_item_list())
.flatten()
.collect();
// When checking statements ignore expressions, they will be checked later
if let hir::StmtKind::Local(ref l) = stmt.kind {
for attr in l.attrs.iter() {
- if attr.check_name(sym::inline) {
+ if self.tcx.sess.check_name(attr, sym::inline) {
self.check_inline(l.hir_id, attr, &stmt.span, Target::Statement);
}
- if attr.check_name(sym::repr) {
+ if self.tcx.sess.check_name(attr, sym::repr) {
self.emit_repr_error(
attr.span,
stmt.span,
_ => Target::Expression,
};
for attr in expr.attrs.iter() {
- if attr.check_name(sym::inline) {
+ if self.tcx.sess.check_name(attr, sym::inline) {
self.check_inline(expr.hir_id, attr, &expr.span, target);
}
- if attr.check_name(sym::repr) {
+ if self.tcx.sess.check_name(attr, sym::repr) {
self.emit_repr_error(
attr.span,
expr.span,
fn check_used(&self, attrs: &'hir [Attribute], target: Target) {
for attr in attrs {
- if attr.check_name(sym::used) && target != Target::Static {
+ if self.tcx.sess.check_name(attr, sym::used) && target != Target::Static {
self.tcx
.sess
.span_err(attr.span, "attribute must be applied to a `static` variable");
// However, we cannot allow stable `const fn`s to use unstable features without an explicit
// opt-in via `allow_internal_unstable`.
- attr::allow_internal_unstable(&tcx.get_attrs(def_id), &tcx.sess.diagnostic())
+ attr::allow_internal_unstable(&tcx.sess, &tcx.get_attrs(def_id))
.map_or(false, |mut features| features.any(|name| name == feature_gate))
};
use rustc_middle::ty::{self, DefIdTree, TyCtxt};
use rustc_session::lint;
-use rustc_ast::{ast, attr};
+use rustc_ast::ast;
use rustc_span::symbol::{sym, Symbol};
// Any local node that may call something in its body block should be
id: hir::HirId,
attrs: &[ast::Attribute],
) -> bool {
- if attr::contains_name(attrs, sym::lang) {
+ if tcx.sess.contains_name(attrs, sym::lang) {
return true;
}
// Stable attribute for #[lang = "panic_impl"]
- if attr::contains_name(attrs, sym::panic_handler) {
+ if tcx.sess.contains_name(attrs, sym::panic_handler) {
return true;
}
// (To be) stable attribute for #[lang = "oom"]
- if attr::contains_name(attrs, sym::alloc_error_handler) {
+ if tcx.sess.contains_name(attrs, sym::alloc_error_handler) {
return true;
}
use rustc_hir::itemlikevisit::ItemLikeVisitor;
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::TyCtxt;
+use rustc_session::Session;
use rustc_span::symbol::{sym, Symbol};
struct DiagnosticItemCollector<'tcx> {
}
fn observe_item(&mut self, attrs: &[ast::Attribute], hir_id: hir::HirId) {
- if let Some(name) = extract(attrs) {
+ if let Some(name) = extract(&self.tcx.sess, attrs) {
let def_id = self.tcx.hir().local_def_id(hir_id);
// insert into our table
collect_item(self.tcx, &mut self.items, name, def_id.to_def_id());
}
/// Extract the first `rustc_diagnostic_item = "$name"` out of a list of attributes.
-fn extract(attrs: &[ast::Attribute]) -> Option<Symbol> {
+fn extract(sess: &Session, attrs: &[ast::Attribute]) -> Option<Symbol> {
attrs.iter().find_map(|attr| {
- if attr.check_name(sym::rustc_diagnostic_item) { attr.value_str() } else { None }
+ if sess.check_name(attr, sym::rustc_diagnostic_item) { attr.value_str() } else { None }
})
}
-use rustc_ast::attr;
use rustc_ast::entry::EntryPointType;
use rustc_errors::struct_span_err;
use rustc_hir::def_id::{CrateNum, LocalDefId, CRATE_DEF_INDEX, LOCAL_CRATE};
}
// If the user wants no main function at all, then stop here.
- if attr::contains_name(&tcx.hir().krate().item.attrs, sym::no_main) {
+ if tcx.sess.contains_name(&tcx.hir().krate().item.attrs, sym::no_main) {
return None;
}
configure_main(tcx, &ctxt)
}
-// Beware, this is duplicated in `librustc_ast/entry.rs`, so make sure to keep
-// them in sync.
-fn entry_point_type(item: &Item<'_>, at_root: bool) -> EntryPointType {
+// Beware, this is duplicated in `librustc_builtin_macros/test_harness.rs`
+// (with `ast::Item`), so make sure to keep them in sync.
+fn entry_point_type(sess: &Session, item: &Item<'_>, at_root: bool) -> EntryPointType {
match item.kind {
ItemKind::Fn(..) => {
- if attr::contains_name(&item.attrs, sym::start) {
+ if sess.contains_name(&item.attrs, sym::start) {
EntryPointType::Start
- } else if attr::contains_name(&item.attrs, sym::main) {
+ } else if sess.contains_name(&item.attrs, sym::main) {
EntryPointType::MainAttr
} else if item.ident.name == sym::main {
if at_root {
}
fn find_item(item: &Item<'_>, ctxt: &mut EntryContext<'_, '_>, at_root: bool) {
- match entry_point_type(item, at_root) {
+ match entry_point_type(&ctxt.session, item, at_root) {
EntryPointType::MainNamed => {
if ctxt.main_fn.is_none() {
ctxt.main_fn = Some((item.hir_id, item.span));
}
fn check_for_lang(&mut self, actual_target: Target, hir_id: HirId, attrs: &[Attribute]) {
- if let Some((value, span)) = extract(&attrs) {
+ let check_name = |attr, sym| self.tcx.sess.check_name(attr, sym);
+ if let Some((value, span)) = extract(check_name, &attrs) {
match ITEM_REFS.get(&value).cloned() {
// Known lang item with attribute on correct target.
Some((item_index, expected_target)) if actual_target == expected_target => {
| ItemKind::Struct(..)
| ItemKind::Union(..) => {
for attr in self.tcx.get_attrs(item_def_id.to_def_id()).iter() {
- if attr.check_name(sym::rustc_layout) {
+ if self.tcx.sess.check_name(attr, sym::rustc_layout) {
self.dump_layout_of(item_def_id, item, attr);
}
}
// Find a stability attribute (i.e., `#[stable (..)]`, `#[unstable (..)]`,
// `#[rustc_const_unstable (..)]`).
- if let Some(stab_attr) = stab_attrs.iter().find(|stab_attr| attr.check_name(**stab_attr)) {
+ if let Some(stab_attr) =
+ stab_attrs.iter().find(|stab_attr| self.tcx.sess.check_name(attr, **stab_attr))
+ {
let meta_item = attr.meta();
if let Some(MetaItem { kind: MetaItemKind::List(ref metas), .. }) = meta_item {
let mut feature = None;
if let FnKind::Method(..) = fk {
let parent = ir.tcx.hir().get_parent_item(id);
if let Some(Node::Item(i)) = ir.tcx.hir().find(parent) {
- if i.attrs.iter().any(|a| a.check_name(sym::automatically_derived)) {
+ if i.attrs.iter().any(|a| ir.tcx.sess.check_name(a, sym::automatically_derived)) {
return;
}
}
did_error = self.forbid_staged_api_attrs(hir_id, attrs);
}
- let depr = if did_error {
- None
- } else {
- attr::find_deprecation(&self.tcx.sess.parse_sess, attrs, item_sp)
- };
+ let depr =
+ if did_error { None } else { attr::find_deprecation(&self.tcx.sess, attrs, item_sp) };
let mut is_deprecated = false;
if let Some(depr) = &depr {
is_deprecated = true;
}
if self.tcx.features().staged_api {
- if let Some(..) = attrs.iter().find(|a| a.check_name(sym::deprecated)) {
+ if let Some(..) = attrs.iter().find(|a| self.tcx.sess.check_name(a, sym::deprecated)) {
self.tcx.sess.span_err(
item_sp,
"`#[deprecated]` cannot be used in staged API; \
return;
}
- let (stab, const_stab) = attr::find_stability(&self.tcx.sess.parse_sess, attrs, item_sp);
+ let (stab, const_stab) = attr::find_stability(&self.tcx.sess, attrs, item_sp);
let const_stab = const_stab.map(|const_stab| {
let const_stab = self.tcx.intern_const_stability(const_stab);
for attr in attrs {
let name = attr.name_or_empty();
if unstable_attrs.contains(&name) {
- attr::mark_used(attr);
+ self.tcx.sess.mark_attr_used(attr);
struct_span_err!(
self.tcx.sess,
attr.span,
}
fn visit_foreign_item(&mut self, i: &hir::ForeignItem<'_>) {
- if let Some((lang_item, _)) = hir::lang_items::extract(&i.attrs) {
+ let check_name = |attr, sym| self.tcx.sess.check_name(attr, sym);
+ if let Some((lang_item, _)) = hir::lang_items::extract(check_name, &i.attrs) {
self.register(lang_item, i.span, i.hir_id);
}
intravisit::walk_foreign_item(self, i)
//! Used by `rustc` when compiling a plugin crate.
-use rustc_ast::attr;
use rustc_hir as hir;
use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE};
use rustc_hir::itemlikevisit::ItemLikeVisitor;
use rustc_span::symbol::sym;
use rustc_span::Span;
-struct RegistrarFinder {
+struct RegistrarFinder<'tcx> {
+ tcx: TyCtxt<'tcx>,
registrars: Vec<(hir::HirId, Span)>,
}
-impl<'v> ItemLikeVisitor<'v> for RegistrarFinder {
+impl<'v, 'tcx> ItemLikeVisitor<'v> for RegistrarFinder<'tcx> {
fn visit_item(&mut self, item: &hir::Item<'_>) {
if let hir::ItemKind::Fn(..) = item.kind {
- if attr::contains_name(&item.attrs, sym::plugin_registrar) {
+ if self.tcx.sess.contains_name(&item.attrs, sym::plugin_registrar) {
self.registrars.push((item.hir_id, item.span));
}
}
fn plugin_registrar_fn(tcx: TyCtxt<'_>, cnum: CrateNum) -> Option<DefId> {
assert_eq!(cnum, LOCAL_CRATE);
- let mut finder = RegistrarFinder { registrars: Vec::new() };
+ let mut finder = RegistrarFinder { tcx, registrars: Vec::new() };
tcx.hir().krate().visit_all_item_likes(&mut finder);
match finder.registrars.len() {
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")]
#![feature(nll)]
+#![recursion_limit = "256"]
use rustc_lint::LintStore;
let mut plugins = Vec::new();
for attr in &krate.attrs {
- if !attr.check_name(sym::plugin) {
+ if !sess.check_name(attr, sym::plugin) {
continue;
}
Node::Item(item) => &item.vis,
Node::ForeignItem(foreign_item) => &foreign_item.vis,
Node::MacroDef(macro_def) => {
- if attr::contains_name(¯o_def.attrs, sym::macro_export) {
+ if tcx.sess.contains_name(¯o_def.attrs, sym::macro_export) {
return (ty::Visibility::Public, macro_def.span, "public");
} else {
¯o_def.vis
ctor_vis =
ty::Visibility::Restricted(DefId::local(CRATE_DEF_INDEX));
let attrs = tcx.get_attrs(variant.def_id);
- span =
- attr::find_by_name(&attrs, sym::non_exhaustive).unwrap().span;
+ span = tcx
+ .sess
+ .find_by_name(&attrs, sym::non_exhaustive)
+ .unwrap()
+ .span;
descr = "crate-visible";
}
if adt_def.non_enum_variant().is_field_list_non_exhaustive() {
ctor_vis =
ty::Visibility::Restricted(DefId::local(CRATE_DEF_INDEX));
- span = attr::find_by_name(&item.attrs, sym::non_exhaustive)
+ span = tcx
+ .sess
+ .find_by_name(&item.attrs, sym::non_exhaustive)
.unwrap()
.span;
descr = "crate-visible";
}
fn visit_macro_def(&mut self, md: &'tcx hir::MacroDef<'tcx>) {
- if attr::find_transparency(&md.attrs, md.ast.macro_rules).0 != Transparency::Opaque {
+ if attr::find_transparency(&self.tcx.sess, &md.attrs, md.ast.macro_rules).0
+ != Transparency::Opaque
+ {
self.update(md.hir_id, Some(AccessLevel::Public));
return;
}
}
ast::UseTreeKind::Glob => {
let kind = ImportKind::Glob {
- is_prelude: attr::contains_name(&item.attrs, sym::prelude_import),
+ is_prelude: self.r.session.contains_name(&item.attrs, sym::prelude_import),
max_vis: Cell::new(ty::Visibility::Invisible),
};
self.add_import(prefix, kind, use_tree.span, id, item, root_span, item.id, vis);
let module_kind = ModuleKind::Def(DefKind::Mod, def_id.to_def_id(), ident.name);
let module = self.r.arenas.alloc_module(ModuleData {
no_implicit_prelude: parent.no_implicit_prelude || {
- attr::contains_name(&item.attrs, sym::no_implicit_prelude)
+ self.r.session.contains_name(&item.attrs, sym::no_implicit_prelude)
},
..ModuleData::new(
Some(parent),
// If the structure is marked as non_exhaustive then lower the visibility
// to within the crate.
let mut ctor_vis = if vis == ty::Visibility::Public
- && attr::contains_name(&item.attrs, sym::non_exhaustive)
+ && self.r.session.contains_name(&item.attrs, sym::non_exhaustive)
{
ty::Visibility::Restricted(DefId::local(CRATE_DEF_INDEX))
} else {
let mut import_all = None;
let mut single_imports = Vec::new();
for attr in &item.attrs {
- if attr.check_name(sym::macro_use) {
+ if self.r.session.check_name(attr, sym::macro_use) {
if self.parent_scope.module.parent.is_some() {
struct_span_err!(
self.r.session,
/// Returns `true` if this attribute list contains `macro_use`.
fn contains_macro_use(&mut self, attrs: &[ast::Attribute]) -> bool {
for attr in attrs {
- if attr.check_name(sym::macro_escape) {
+ if self.r.session.check_name(attr, sym::macro_escape) {
let msg = "`#[macro_escape]` is a deprecated synonym for `#[macro_use]`";
let mut err = self.r.session.struct_span_warn(attr.span, msg);
if let ast::AttrStyle::Inner = attr.style {
} else {
err.emit();
}
- } else if !attr.check_name(sym::macro_use) {
+ } else if !self.r.session.check_name(attr, sym::macro_use) {
continue;
}
MacroRulesScope::Invocation(invoc_id)
}
- fn proc_macro_stub(item: &ast::Item) -> Option<(MacroKind, Ident, Span)> {
- if attr::contains_name(&item.attrs, sym::proc_macro) {
+ fn proc_macro_stub(&self, item: &ast::Item) -> Option<(MacroKind, Ident, Span)> {
+ if self.r.session.contains_name(&item.attrs, sym::proc_macro) {
return Some((MacroKind::Bang, item.ident, item.span));
- } else if attr::contains_name(&item.attrs, sym::proc_macro_attribute) {
+ } else if self.r.session.contains_name(&item.attrs, sym::proc_macro_attribute) {
return Some((MacroKind::Attr, item.ident, item.span));
- } else if let Some(attr) = attr::find_by_name(&item.attrs, sym::proc_macro_derive) {
+ } else if let Some(attr) = self.r.session.find_by_name(&item.attrs, sym::proc_macro_derive)
+ {
if let Some(nested_meta) = attr.meta_item_list().and_then(|list| list.get(0).cloned()) {
if let Some(ident) = nested_meta.ident() {
return Some((MacroKind::Derive, ident, ident.span));
let ext = Lrc::new(self.r.compile_macro(item, self.r.session.edition()));
(ext, item.ident, item.span, def.macro_rules)
}
- ItemKind::Fn(..) => match Self::proc_macro_stub(item) {
+ ItemKind::Fn(..) => match self.proc_macro_stub(item) {
Some((macro_kind, ident, span)) => {
self.r.proc_macro_stubs.insert(def_id);
(self.r.dummy_ext(macro_kind), ident, span, false)
if macro_rules {
let ident = ident.normalize_to_macros_2_0();
self.r.macro_names.insert(ident);
- let is_macro_export = attr::contains_name(&item.attrs, sym::macro_export);
+ let is_macro_export = self.r.session.contains_name(&item.attrs, sym::macro_export);
let vis = if is_macro_export {
ty::Visibility::Public
} else {
// If the variant is marked as non_exhaustive then lower the visibility to within the
// crate.
let mut ctor_vis = vis;
- let has_non_exhaustive = attr::contains_name(&variant.attrs, sym::non_exhaustive);
+ let has_non_exhaustive = self.r.session.contains_name(&variant.attrs, sym::non_exhaustive);
if has_non_exhaustive && vis == ty::Visibility::Public {
ctor_vis = ty::Visibility::Restricted(DefId::local(CRATE_DEF_INDEX));
}
);
err
}
+ ResolutionError::ParamInNonTrivialAnonConst(name) => {
+ let mut err = self.session.struct_span_err(
+ span,
+ "generic parameters must not be used inside of non trivial constant values",
+ );
+ err.span_label(
+ span,
+ &format!(
+ "non-trivial anonymous constants must not depend on the parameter `{}`",
+ name
+ ),
+ );
+ err.help(
+ &format!("it is currently only allowed to use either `{0}` or `{{ {0} }}` as generic constants", name)
+ );
+ err
+ }
ResolutionError::SelfInTyParamDefault => {
let mut err = struct_span_err!(
self.session,
) = binding.kind
{
let def_id = (&*self).parent(ctor_def_id).expect("no parent for a constructor");
- if let Some(fields) = self.field_names.get(&def_id) {
- let first_field = fields.first().expect("empty field list in the map");
- return Some(fields.iter().fold(first_field.span, |acc, field| acc.to(field.span)));
- }
+ let fields = self.field_names.get(&def_id)?;
+ let first_field = fields.first()?; // Handle `struct Foo()`
+ return Some(fields.iter().fold(first_field.span, |acc, field| acc.to(field.span)));
}
None
}
ItemRibKind(HasGenericParams),
/// We're in a constant item. Can't refer to dynamic stuff.
- ConstantItemRibKind,
+ ConstantItemRibKind(bool),
/// We passed through a module.
ModuleRibKind(Module<'a>),
NormalRibKind
| ClosureOrAsyncRibKind
| FnItemRibKind
- | ConstantItemRibKind
+ | ConstantItemRibKind(_)
| ModuleRibKind(_)
| MacroDefinition(_)
| ConstParamTyRibKind => false,
ValueNS => "method or associated constant",
MacroNS => bug!("associated macro"),
},
- PathSource::Expr(parent) => match &parent.as_ref().map(|p| &p.kind) {
+ PathSource::Expr(parent) => match parent.as_ref().map(|p| &p.kind) {
// "function" here means "anything callable" rather than `DefKind::Fn`,
// this is not precise but usually more helpful than just "value".
Some(ExprKind::Call(call_expr, _)) => match &call_expr.kind {
}
fn visit_anon_const(&mut self, constant: &'ast AnonConst) {
debug!("visit_anon_const {:?}", constant);
- self.with_constant_rib(|this| {
+ self.with_constant_rib(constant.value.is_potential_trivial_const_param(), |this| {
visit::walk_anon_const(this, constant);
});
}
if !check_ns(TypeNS) && check_ns(ValueNS) {
// This must be equivalent to `visit_anon_const`, but we cannot call it
// directly due to visitor lifetimes so we have to copy-paste some code.
- self.with_constant_rib(|this| {
+ self.with_constant_rib(true, |this| {
this.smart_resolve_path(
ty.id,
qself.as_ref(),
| ClosureOrAsyncRibKind
| FnItemRibKind
| ItemRibKind(..)
- | ConstantItemRibKind
+ | ConstantItemRibKind(_)
| ModuleRibKind(..)
| ForwardTyParamBanRibKind
| ConstParamTyRibKind => {
// Only impose the restrictions of `ConstRibKind` for an
// actual constant expression in a provided default.
if let Some(expr) = default {
- this.with_constant_rib(|this| this.visit_expr(expr));
+ // We allow arbitrary const expressions inside of associated consts,
+ // even if they are potentially not const evaluatable.
+ //
+ // Type parameters can already be used and as associated consts are
+ // not used as part of the type system, this is far less surprising.
+ this.with_constant_rib(true, |this| {
+ this.visit_expr(expr)
+ });
}
}
AssocItemKind::Fn(_, _, generics, _) => {
self.with_item_rib(HasGenericParams::No, |this| {
this.visit_ty(ty);
if let Some(expr) = expr {
- this.with_constant_rib(|this| this.visit_expr(expr));
+ this.with_constant_rib(expr.is_potential_trivial_const_param(), |this| {
+ this.visit_expr(expr)
+ });
}
});
}
self.with_rib(ValueNS, kind, |this| this.with_rib(TypeNS, kind, f))
}
- fn with_constant_rib(&mut self, f: impl FnOnce(&mut Self)) {
+ fn with_constant_rib(&mut self, trivial: bool, f: impl FnOnce(&mut Self)) {
debug!("with_constant_rib");
- self.with_rib(ValueNS, ConstantItemRibKind, |this| {
- this.with_rib(TypeNS, ConstantItemRibKind, |this| {
- this.with_label_rib(ConstantItemRibKind, f);
+ self.with_rib(ValueNS, ConstantItemRibKind(trivial), |this| {
+ this.with_rib(TypeNS, ConstantItemRibKind(trivial), |this| {
+ this.with_label_rib(ConstantItemRibKind(trivial), f);
})
});
}
for item in impl_items {
use crate::ResolutionError::*;
match &item.kind {
- AssocItemKind::Const(..) => {
+ AssocItemKind::Const(_default, _ty, _expr) => {
debug!("resolve_implementation AssocItemKind::Const",);
// If this is a trait impl, ensure the const
// exists in trait
|n, s| ConstNotMemberOfTrait(n, s),
);
- this.with_constant_rib(|this| {
+ // We allow arbitrary const expressions inside of associated consts,
+ // even if they are potentially not const evaluatable.
+ //
+ // Type parameters can already be used and as associated consts are
+ // not used as part of the type system, this is far less surprising.
+ this.with_constant_rib(true, |this| {
visit::walk_assoc_item(this, item, AssocCtxt::Impl)
});
}
//! way. Therefore, we break lifetime name resolution into a separate pass.
use crate::late::diagnostics::{ForLifetimeSpanType, MissingLifetimeSpot};
-use rustc_ast::attr;
use rustc_ast::walk_list;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder};
let result = object_lifetime_defaults_for_item(tcx, generics);
// Debugging aid.
- if attr::contains_name(&item.attrs, sym::rustc_object_lifetime_default) {
+ if tcx.sess.contains_name(&item.attrs, sym::rustc_object_lifetime_default) {
let object_lifetime_default_reprs: String = result
.iter()
.map(|set| match *set {
if let Some(def_id) = parent_def_id.as_local() {
let parent_hir_id = self.tcx.hir().as_local_hir_id(def_id);
// lifetimes in `derive` expansions don't count (Issue #53738)
- if self
- .tcx
- .hir()
- .attrs(parent_hir_id)
- .iter()
- .any(|attr| attr.check_name(sym::automatically_derived))
- {
+ if self.tcx.hir().attrs(parent_hir_id).iter().any(|attr| {
+ self.tcx.sess.check_name(attr, sym::automatically_derived)
+ }) {
continue;
}
}
use rustc_ast::ast::{self, FloatTy, IntTy, NodeId, UintTy};
use rustc_ast::ast::{Crate, CRATE_NODE_ID};
use rustc_ast::ast::{ItemKind, Path};
-use rustc_ast::attr;
use rustc_ast::node_id::NodeMap;
use rustc_ast::unwrap_or;
use rustc_ast::visit::{self, Visitor};
ParamInTyOfConstParam(Symbol),
/// constant values inside of type parameter defaults must not depend on generic parameters.
ParamInAnonConstInTyDefault(Symbol),
+ /// generic parameters must not be used inside of non trivial constant values.
+ ///
+ /// This error is only emitted when using `min_const_generics`.
+ ParamInNonTrivialAnonConst(Symbol),
/// Error E0735: type parameters with a default cannot use `Self`
SelfInTyParamDefault,
/// Error E0767: use of unreachable label
let root_def_id = DefId::local(CRATE_DEF_INDEX);
let root_module_kind = ModuleKind::Def(DefKind::Mod, root_def_id, kw::Invalid);
let graph_root = arenas.alloc_module(ModuleData {
- no_implicit_prelude: attr::contains_name(&krate.attrs, sym::no_implicit_prelude),
+ no_implicit_prelude: session.contains_name(&krate.attrs, sym::no_implicit_prelude),
..ModuleData::new(None, root_module_kind, root_def_id, ExpnId::root(), krate.span)
});
let empty_module_kind = ModuleKind::Def(DefKind::Mod, root_def_id, kw::Invalid);
.map(|(name, _)| (Ident::from_str(name), Default::default()))
.collect();
- if !attr::contains_name(&krate.attrs, sym::no_core) {
+ if !session.contains_name(&krate.attrs, sym::no_core) {
extern_prelude.insert(Ident::with_dummy_span(sym::core), Default::default());
- if !attr::contains_name(&krate.attrs, sym::no_std) {
+ if !session.contains_name(&krate.attrs, sym::no_std) {
extern_prelude.insert(Ident::with_dummy_span(sym::std), Default::default());
if session.rust_2018() {
extern_prelude.insert(Ident::with_dummy_span(sym::meta), Default::default());
res_err = Some(CannotCaptureDynamicEnvironmentInFnItem);
}
}
- ConstantItemRibKind => {
+ ConstantItemRibKind(_) => {
// Still doesn't deal with upvars
if record_used {
self.report_error(span, AttemptToUseNonConstantValueInConstant);
in_ty_param_default = true;
continue;
}
- ConstantItemRibKind => {
+ ConstantItemRibKind(trivial) => {
+ // HACK(min_const_generics): We currently only allow `N` or `{ N }`.
+ if !trivial && self.session.features_untracked().min_const_generics {
+ if record_used {
+ self.report_error(
+ span,
+ ResolutionError::ParamInNonTrivialAnonConst(rib_ident.name),
+ );
+ }
+ return Res::Err;
+ }
+
if in_ty_param_default {
if record_used {
self.report_error(
in_ty_param_default = true;
continue;
}
- ConstantItemRibKind => {
+ ConstantItemRibKind(trivial) => {
+ // HACK(min_const_generics): We currently only allow `N` or `{ N }`.
+ if !trivial && self.session.features_untracked().min_const_generics {
+ if record_used {
+ self.report_error(
+ span,
+ ResolutionError::ParamInNonTrivialAnonConst(rib_ident.name),
+ );
+ }
+ return Res::Err;
+ }
+
if in_ty_param_default {
if record_used {
self.report_error(
use rustc_ast::ast::{self, NodeId};
use rustc_ast_lowering::ResolverAstLowering;
use rustc_ast_pretty::pprust;
-use rustc_attr::{self as attr, StabilityLevel};
+use rustc_attr::StabilityLevel;
use rustc_data_structures::fx::FxHashSet;
use rustc_expand::base::{Indeterminate, InvocationRes, ResolverExpand, SyntaxExtension};
use rustc_expand::compile_declarative_macro;
descr: &str,
) -> FxHashSet<Ident> {
let mut registered = FxHashSet::default();
- for attr in attr::filter_by_name(attrs, attr_name) {
+ for attr in sess.filter_by_name(attrs, attr_name) {
for nested_meta in attr.meta_item_list().unwrap_or_default() {
match nested_meta.ident() {
Some(ident) => {
/// its expander to a pre-defined one for built-in macros.
crate fn compile_macro(&mut self, item: &ast::Item, edition: Edition) -> SyntaxExtension {
let mut result = compile_declarative_macro(
- &self.session.parse_sess,
+ &self.session,
self.session.features_untracked(),
item,
edition,
mod sig;
use rustc_ast::ast::{self};
-use rustc_ast::util::comments::strip_doc_comment_decoration;
+use rustc_ast::util::comments::beautify_doc_string;
use rustc_ast_pretty::pprust::attribute_to_string;
use rustc_hir as hir;
use rustc_hir::def::{DefKind as HirDefKind, Res};
Res::Def(HirDefKind::ConstParam, def_id) => {
Some(Ref { kind: RefKind::Variable, span, ref_id: id_from_def_id(def_id) })
}
- Res::Def(HirDefKind::Ctor(_, ..), def_id) => {
+ Res::Def(HirDefKind::Ctor(..), def_id) => {
// This is a reference to a tuple struct or an enum variant where the def_id points
// to an invisible constructor function. That is not a very useful
// def, so adjust to point to the tuple struct or enum variant itself.
for attr in attrs {
if let Some(val) = attr.doc_str() {
- if attr.is_doc_comment() {
- result.push_str(&strip_doc_comment_decoration(val));
- } else {
- result.push_str(&val.as_str());
- }
+ // FIXME: Should save-analysis beautify doc strings itself or leave it to users?
+ result.push_str(&beautify_doc_string(val));
result.push('\n');
- } else if attr.check_name(sym::doc) {
+ } else if self.tcx.sess.check_name(attr, sym::doc) {
if let Some(meta_list) = attr.meta_item_list() {
meta_list
.into_iter()
p.push(RUST_LIB_DIR);
p.push(&self.triple);
p.push("bin");
- if self_contained { vec![p.clone(), p.join("self-contained")] } else { vec![p.clone()] }
+ if self_contained { vec![p.clone(), p.join("self-contained")] } else { vec![p] }
}
}
//! Related to out filenames of compilation (e.g. save analysis, binaries).
use crate::config::{CrateType, Input, OutputFilenames, OutputType};
use crate::Session;
-use rustc_ast::{ast, attr};
+use rustc_ast::ast;
use rustc_span::symbol::sym;
use rustc_span::Span;
use std::path::{Path, PathBuf};
}
}
-pub fn find_crate_name(sess: Option<&Session>, attrs: &[ast::Attribute], input: &Input) -> String {
+pub fn find_crate_name(sess: &Session, attrs: &[ast::Attribute], input: &Input) -> String {
let validate = |s: String, span: Option<Span>| {
validate_crate_name(sess, &s, span);
s
// the command line over one found in the #[crate_name] attribute. If we
// find both we ensure that they're the same later on as well.
let attr_crate_name =
- attr::find_by_name(attrs, sym::crate_name).and_then(|at| at.value_str().map(|s| (at, s)));
-
- if let Some(sess) = sess {
- if let Some(ref s) = sess.opts.crate_name {
- if let Some((attr, name)) = attr_crate_name {
- if name.as_str() != *s {
- let msg = format!(
- "`--crate-name` and `#[crate_name]` are \
- required to match, but `{}` != `{}`",
- s, name
- );
- sess.span_err(attr.span, &msg);
- }
+ sess.find_by_name(attrs, sym::crate_name).and_then(|at| at.value_str().map(|s| (at, s)));
+
+ if let Some(ref s) = sess.opts.crate_name {
+ if let Some((attr, name)) = attr_crate_name {
+ if name.as_str() != *s {
+ let msg = format!(
+ "`--crate-name` and `#[crate_name]` are \
+ required to match, but `{}` != `{}`",
+ s, name
+ );
+ sess.span_err(attr.span, &msg);
}
- return validate(s.clone(), None);
}
+ return validate(s.clone(), None);
}
if let Some((attr, s)) = attr_crate_name {
`{}` has a leading hyphen",
s
);
- if let Some(sess) = sess {
- sess.err(&msg);
- }
+ sess.err(&msg);
} else {
return validate(s.replace("-", "_"), None);
}
"rust_out".to_string()
}
-pub fn validate_crate_name(sess: Option<&Session>, s: &str, sp: Option<Span>) {
+pub fn validate_crate_name(sess: &Session, s: &str, sp: Option<Span>) {
let mut err_count = 0;
{
let mut say = |s: &str| {
- match (sp, sess) {
- (_, None) => panic!("{}", s),
- (Some(sp), Some(sess)) => sess.span_err(sp, s),
- (None, Some(sess)) => sess.err(s),
+ match sp {
+ Some(sp) => sess.span_err(sp, s),
+ None => sess.err(s),
}
err_count += 1;
};
}
if err_count > 0 {
- sess.unwrap().abort_if_errors();
+ sess.abort_if_errors();
}
}
use crate::parse::ParseSess;
use crate::search_paths::{PathKind, SearchPath};
+pub use rustc_ast::ast::Attribute;
+pub use rustc_ast::attr::MarkedAttrs;
pub use rustc_ast::crate_disambiguator::CrateDisambiguator;
use rustc_data_structures::flock;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticId, ErrorReported};
use rustc_span::edition::Edition;
use rustc_span::source_map::{FileLoader, MultiSpan, RealFileLoader, SourceMap, Span};
-use rustc_span::{SourceFileHashAlgorithm, Symbol};
+use rustc_span::{sym, SourceFileHashAlgorithm, Symbol};
use rustc_target::asm::InlineAsmArch;
use rustc_target::spec::{CodeModel, PanicStrategy, RelocModel, RelroLevel};
use rustc_target::spec::{Target, TargetTriple, TlsModel};
/// Set of enabled features for the current target.
pub target_features: FxHashSet<Symbol>,
+
+ known_attrs: Lock<MarkedAttrs>,
+ used_attrs: Lock<MarkedAttrs>,
}
pub struct PerfStats {
// MemorySanitizer uses lifetimes to detect use of uninitialized stack variables.
|| self.opts.debugging_opts.sanitizer.intersects(SanitizerSet::ADDRESS | SanitizerSet::MEMORY)
}
+
+ pub fn mark_attr_known(&self, attr: &Attribute) {
+ self.known_attrs.lock().mark(attr)
+ }
+
+ pub fn is_attr_known(&self, attr: &Attribute) -> bool {
+ self.known_attrs.lock().is_marked(attr)
+ }
+
+ pub fn mark_attr_used(&self, attr: &Attribute) {
+ self.used_attrs.lock().mark(attr)
+ }
+
+ pub fn is_attr_used(&self, attr: &Attribute) -> bool {
+ self.used_attrs.lock().is_marked(attr)
+ }
+
+ /// Returns `true` if the attribute's path matches the argument. If it matches, then the
+ /// attribute is marked as used.
+
+ /// Returns `true` if the attribute's path matches the argument. If it
+ /// matches, then the attribute is marked as used.
+ ///
+ /// This method should only be used by rustc, other tools can use
+ /// `Attribute::has_name` instead, because only rustc is supposed to report
+ /// the `unused_attributes` lint. (`MetaItem` and `NestedMetaItem` are
+ /// produced by lowering an `Attribute` and don't have identity, so they
+ /// only have the `has_name` method, and you need to mark the original
+ /// `Attribute` as used when necessary.)
+ pub fn check_name(&self, attr: &Attribute, name: Symbol) -> bool {
+ let matches = attr.has_name(name);
+ if matches {
+ self.mark_attr_used(attr);
+ }
+ matches
+ }
+
+ pub fn is_proc_macro_attr(&self, attr: &Attribute) -> bool {
+ [sym::proc_macro, sym::proc_macro_attribute, sym::proc_macro_derive]
+ .iter()
+ .any(|kind| self.check_name(attr, *kind))
+ }
+
+ pub fn contains_name(&self, attrs: &[Attribute], name: Symbol) -> bool {
+ attrs.iter().any(|item| self.check_name(item, name))
+ }
+
+ pub fn find_by_name<'a>(
+ &'a self,
+ attrs: &'a [Attribute],
+ name: Symbol,
+ ) -> Option<&'a Attribute> {
+ attrs.iter().find(|attr| self.check_name(attr, name))
+ }
+
+ pub fn filter_by_name<'a>(
+ &'a self,
+ attrs: &'a [Attribute],
+ name: Symbol,
+ ) -> impl Iterator<Item = &'a Attribute> {
+ attrs.iter().filter(move |attr| self.check_name(attr, name))
+ }
+
+ pub fn first_attr_value_str_by_name(
+ &self,
+ attrs: &[Attribute],
+ name: Symbol,
+ ) -> Option<Symbol> {
+ attrs.iter().find(|at| self.check_name(at, name)).and_then(|at| at.value_str())
+ }
}
fn default_emitter(
real_rust_source_base_dir,
asm_arch,
target_features: FxHashSet::default(),
+ known_attrs: Lock::new(MarkedAttrs::new()),
+ used_attrs: Lock::new(MarkedAttrs::new()),
};
validate_commandline_args_with_session_available(&sess);
Opaque,
}
-pub(crate) const NUM_TRANSPARENCIES: usize = 3;
-
impl ExpnId {
pub fn fresh(expn_data: Option<ExpnData>) -> Self {
HygieneData::with(|data| data.fresh_expn(expn_data))
HygieneData::with(|data| data.expn_data(data.outer_expn(self)).clone())
}
+ #[inline]
+ pub fn outer_mark(self) -> (ExpnId, Transparency) {
+ HygieneData::with(|data| data.outer_mark(self))
+ }
+
#[inline]
pub fn outer_mark_with_data(self) -> (ExpnId, Transparency, ExpnData) {
HygieneData::with(|data| {
/// The kind of this expansion - macro or compiler desugaring.
pub kind: ExpnKind,
/// The expansion that produced this expansion.
- #[stable_hasher(ignore)]
pub parent: ExpnId,
/// The location of the actual macro invocation or syntax sugar , e.g.
/// `let x = foo!();` or `if let Some(y) = x {}`
drop(expns);
expn_id
});
- return Ok(expn_id);
+ Ok(expn_id)
}
// Decodes `SyntaxContext`, using the provided `HygieneDecodeContext`
assert_eq!(dummy.dollar_crate_name, kw::Invalid);
});
- return Ok(new_ctxt);
+ Ok(new_ctxt)
}
pub fn num_syntax_ctxts() -> usize {
use edition::Edition;
pub mod hygiene;
pub use hygiene::SyntaxContext;
+use hygiene::Transparency;
pub use hygiene::{DesugaringKind, ExpnData, ExpnId, ExpnKind, ForLoopLoc, MacroKind};
-use hygiene::{Transparency, NUM_TRANSPARENCIES};
pub mod def_id;
use def_id::{CrateNum, DefId, LOCAL_CRATE};
mod span_encoding;
}
}
+pub fn with_session_globals<R>(edition: Edition, f: impl FnOnce() -> R) -> R {
+ let session_globals = SessionGlobals::new(edition);
+ SESSION_GLOBALS.set(&session_globals, f)
+}
+
+pub fn with_default_session_globals<R>(f: impl FnOnce() -> R) -> R {
+ with_session_globals(edition::DEFAULT_EDITION, f)
+}
+
// If this ever becomes non thread-local, `decode_syntax_context`
// and `decode_expn_id` will need to be updated to handle concurrent
// deserialization.
TAG_NO_EXPANSION.hash_stable(ctx, hasher);
} else {
TAG_EXPANSION.hash_stable(ctx, hasher);
+ let (expn_id, transparency) = self.outer_mark();
+ expn_id.hash_stable(ctx, hasher);
+ transparency.hash_stable(ctx, hasher);
+ }
+ }
+}
- // Since the same expansion context is usually referenced many
- // times, we cache a stable hash of it and hash that instead of
- // recursing every time.
- thread_local! {
- static CACHE: RefCell<Vec<Option<[Option<u64>; NUM_TRANSPARENCIES]>>> = Default::default();
- }
+impl<CTX: HashStableContext> HashStable<CTX> for ExpnId {
+ fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
+ // Since the same expansion context is usually referenced many
+ // times, we cache a stable hash of it and hash that instead of
+ // recursing every time.
+ thread_local! {
+ static CACHE: RefCell<Vec<Option<Fingerprint>>> = Default::default();
+ }
- let sub_hash: u64 = CACHE.with(|cache| {
- let (expn_id, transparency, _) = self.outer_mark_with_data();
- let index = expn_id.as_u32() as usize;
+ const TAG_ROOT: u8 = 0;
+ const TAG_NOT_ROOT: u8 = 1;
- if let Some(sub_hash_cache) = cache.borrow().get(index).copied().flatten() {
- if let Some(sub_hash) = sub_hash_cache[transparency as usize] {
- return sub_hash;
- }
- }
+ if *self == ExpnId::root() {
+ TAG_ROOT.hash_stable(ctx, hasher);
+ return;
+ }
- let new_len = index + 1;
+ TAG_NOT_ROOT.hash_stable(ctx, hasher);
+ let index = self.as_u32() as usize;
- let mut hasher = StableHasher::new();
- expn_id.expn_data().hash_stable(ctx, &mut hasher);
- transparency.hash_stable(ctx, &mut hasher);
+ let res = CACHE.with(|cache| cache.borrow().get(index).copied().flatten());
- let sub_hash: Fingerprint = hasher.finish();
- let sub_hash = sub_hash.to_smaller_hash();
+ if let Some(res) = res {
+ res.hash_stable(ctx, hasher);
+ } else {
+ let new_len = index + 1;
+ let mut sub_hasher = StableHasher::new();
+ self.expn_data().hash_stable(ctx, &mut sub_hasher);
+ let sub_hash: Fingerprint = sub_hasher.finish();
+
+ CACHE.with(|cache| {
let mut cache = cache.borrow_mut();
if cache.len() < new_len {
cache.resize(new_len, None);
}
- if let Some(mut sub_hash_cache) = cache[index] {
- sub_hash_cache[transparency as usize] = Some(sub_hash);
- } else {
- let mut sub_hash_cache = [None; NUM_TRANSPARENCIES];
- sub_hash_cache[transparency as usize] = Some(sub_hash);
- cache[index] = Some(sub_hash_cache);
- }
- sub_hash
+ cache[index].replace(sub_hash).expect_none("Cache slot was filled");
});
-
sub_hash.hash_stable(ctx, hasher);
}
}
min_align_of,
min_align_of_val,
min_const_fn,
+ min_const_generics,
min_const_unsafe_fn,
min_specialization,
minnumf32,
let tcx = self.tcx;
let def_id = tcx.hir().local_def_id(hir_id);
for attr in tcx.get_attrs(def_id.to_def_id()).iter() {
- if attr.check_name(SYMBOL_NAME) {
+ if tcx.sess.check_name(attr, SYMBOL_NAME) {
// for now, can only use on monomorphic names
let instance = Instance::mono(tcx, def_id.to_def_id());
- let mangled = self.tcx.symbol_name(instance);
+ let mangled = tcx.symbol_name(instance);
tcx.sess.span_err(attr.span, &format!("symbol-name({})", mangled));
if let Ok(demangling) = rustc_demangle::try_demangle(mangled.name) {
tcx.sess.span_err(attr.span, &format!("demangling({})", demangling));
tcx.sess.span_err(attr.span, &format!("demangling-alt({:#})", demangling));
}
- } else if attr.check_name(DEF_PATH) {
+ } else if tcx.sess.check_name(attr, DEF_PATH) {
let path = tcx.def_path_str(def_id.to_def_id());
tcx.sess.span_err(attr.span, &format!("def-path({})", path));
}
clang_args.push(format!("-Wl,{}", arg));
};
- // There have been reports in the wild (rustwasm/wasm-bindgen#119) of
- // using threads causing weird hangs and bugs. Disable it entirely as
- // this isn't yet the bottleneck of compilation at all anyway.
- //
- // FIXME: we should file an upstream issue with LLD about this
- arg("--no-threads");
-
// By default LLD only gives us one page of stack (64k) which is a
// little small. Default to a larger stack closer to other PC platforms
// (1MB) and users can always inject their own link-args to override this.
}
pub fn span(&self) -> Span {
- self.span.clone()
+ self.span
}
pub fn reached_recursion_limit(&self) -> bool {
if let Ok(src) = self.tcx.sess.source_map().span_to_snippet(span) {
// Don't care about `&mut` because `DerefMut` is used less
// often and user will not expect autoderef happens.
- if src.starts_with("&") && !src.starts_with("&mut ") {
+ if src.starts_with('&') && !src.starts_with("&mut ") {
let derefs = "*".repeat(steps);
err.span_suggestion(
span,
) -> Result<Option<Self>, ErrorReported> {
let attrs = tcx.get_attrs(impl_def_id);
- let attr = if let Some(item) = attr::find_by_name(&attrs, sym::rustc_on_unimplemented) {
+ let attr = if let Some(item) = tcx.sess.find_by_name(&attrs, sym::rustc_on_unimplemented) {
item
} else {
return Ok(None);
use crate::infer::{InferCtxt, InferOk, TypeFreshener};
use crate::traits::error_reporting::InferCtxtExt;
use crate::traits::project::ProjectionCacheKeyExt;
-use rustc_ast::attr;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_errors::ErrorReported;
obligation
);
- // `previous_stack` stores a `TraitObligatiom`, while `obligation` is
+ // `previous_stack` stores a `TraitObligation`, while `obligation` is
// a `PredicateObligation`. These are distinct types, so we can't
// use any `Option` combinator method that would force them to be
// the same.
&mut self.intercrate_ambiguity_causes
{
let attrs = tcx.get_attrs(def_id);
- let attr = attr::find_by_name(&attrs, sym::rustc_reservation_impl);
+ let attr = tcx.sess.find_by_name(&attrs, sym::rustc_reservation_impl);
let value = attr.and_then(|a| a.value_str());
if let Some(value) = value {
debug!(
let predicates = self.tcx.predicates_of(adt_def.did).predicates;
let where_clauses: Vec<_> = predicates
- .into_iter()
+ .iter()
.map(|(wc, _)| wc.subst(self.tcx, bound_vars))
.filter_map(|wc| LowerInto::<Option<chalk_ir::QuantifiedWhereClause<RustInterner<'tcx>>>>::lower_into(wc, &self.interner))
.collect();
phantom_data: adt_def.is_phantom_data(),
},
});
- return struct_datum;
+ struct_datum
}
fn fn_def_datum(
let predicates = self.tcx.predicates_defined_on(def_id).predicates;
let where_clauses: Vec<_> = predicates
- .into_iter()
+ .iter()
.map(|(wc, _)| wc.subst(self.tcx, &bound_vars))
.filter_map(|wc| LowerInto::<Option<chalk_ir::QuantifiedWhereClause<RustInterner<'tcx>>>>::lower_into(wc, &self.interner)).collect();
parameters[0].assert_ty_ref(&self.interner).could_match(&self.interner, &lowered_ty)
});
- let impls = matched_impls.map(|matched_impl| chalk_ir::ImplId(matched_impl)).collect();
+ let impls = matched_impls.map(chalk_ir::ImplId).collect();
impls
}
ty::AdtKind::Struct | ty::AdtKind::Union => None,
ty::AdtKind::Enum => {
let constraint = self.tcx.adt_sized_constraint(adt_def.did);
- if constraint.0.len() > 0 { unimplemented!() } else { Some(true) }
+ if !constraint.0.is_empty() { unimplemented!() } else { Some(true) }
}
},
_ => None,
ty::AdtKind::Struct | ty::AdtKind::Union => None,
ty::AdtKind::Enum => {
let constraint = self.tcx.adt_sized_constraint(adt_def.did);
- if constraint.0.len() > 0 { unimplemented!() } else { Some(true) }
+ if !constraint.0.is_empty() { unimplemented!() } else { Some(true) }
}
},
_ => None,
FnOnce => self.tcx.lang_items().fn_once_trait(),
Unsize => self.tcx.lang_items().unsize_trait(),
};
- def_id.map(|t| chalk_ir::TraitId(t))
+ def_id.map(chalk_ir::TraitId)
}
fn is_object_safe(&self, trait_id: chalk_ir::TraitId<RustInterner<'tcx>>) -> bool {
let bounds = tcx.predicates_of(def_id);
let predicates =
- util::elaborate_predicates(tcx, bounds.predicates.into_iter().map(|&(pred, _)| pred));
+ util::elaborate_predicates(tcx, bounds.predicates.iter().map(|&(pred, _)| pred));
let filtered_predicates = predicates.filter_map(|obligation| {
let pred = obligation.predicate;
//
// FIXME? Other potential candidate methods: `as_ref` and
// `as_mut`?
- .any(|a| a.check_name(sym::rustc_conversion_suggestion))
+ .any(|a| self.sess().check_name(a, sym::rustc_conversion_suggestion))
});
methods
let anon_b = self.tcx.anonymize_late_bound_regions(&b);
self.relate(anon_a.skip_binder(), anon_b.skip_binder())?;
- Ok(a.clone())
+ Ok(a)
}
}
sym::nontemporal_store => (1, vec![tcx.mk_mut_ptr(param(0)), param(0)], tcx.mk_unit()),
- sym::count_code_region => {
- (0, vec![tcx.types.u64, tcx.types.u32, tcx.types.u32, tcx.types.u32], tcx.mk_unit())
- }
+ sym::count_code_region => (
+ 0,
+ vec![
+ tcx.types.u64,
+ tcx.types.u32,
+ tcx.mk_static_str(),
+ tcx.types.u32,
+ tcx.types.u32,
+ tcx.types.u32,
+ tcx.types.u32,
+ ],
+ tcx.mk_unit(),
+ ),
sym::coverage_counter_add | sym::coverage_counter_subtract => (
0,
- vec![tcx.types.u32, tcx.types.u32, tcx.types.u32, tcx.types.u32, tcx.types.u32],
+ vec![
+ tcx.types.u32,
+ tcx.types.u32,
+ tcx.types.u32,
+ tcx.mk_static_str(),
+ tcx.types.u32,
+ tcx.types.u32,
+ tcx.types.u32,
+ tcx.types.u32,
+ ],
tcx.mk_unit(),
),
- sym::coverage_unreachable => (0, vec![tcx.types.u32, tcx.types.u32], tcx.mk_unit()),
+ sym::coverage_unreachable => (
+ 0,
+ vec![
+ tcx.mk_static_str(),
+ tcx.types.u32,
+ tcx.types.u32,
+ tcx.types.u32,
+ tcx.types.u32,
+ ],
+ tcx.mk_unit(),
+ ),
other => {
struct_span_err!(
let repr = def.repr;
if repr.packed() {
for attr in tcx.get_attrs(def.did).iter() {
- for r in attr::find_repr_attrs(&tcx.sess.parse_sess, attr) {
+ for r in attr::find_repr_attrs(&tcx.sess, attr) {
if let attr::ReprPacked(pack) = r {
if let Some(repr_pack) = repr.pack {
if pack as u64 != repr_pack.bytes() {
if vs.is_empty() {
let attributes = tcx.get_attrs(def_id.to_def_id());
- if let Some(attr) = attr::find_by_name(&attributes, sym::repr) {
+ if let Some(attr) = tcx.sess.find_by_name(&attributes, sym::repr) {
struct_span_err!(
tcx.sess,
attr.span,
tcx.sess.struct_span_err(pat.span, "`..` cannot be used in union patterns").emit();
}
} else if !etc && !unmentioned_fields.is_empty() {
- unmentioned_err = Some(self.error_unmentioned_fields(pat.span, &unmentioned_fields));
+ unmentioned_err = Some(self.error_unmentioned_fields(pat, &unmentioned_fields));
}
match (inexistent_fields_err, unmentioned_err) {
(Some(mut i), Some(mut u)) => {
if tcx.sess.teach(&err.get_code().unwrap()) {
err.note(
"This error indicates that a struct pattern attempted to \
- extract a non-existent field from a struct. Struct fields \
- are identified by the name used before the colon : so struct \
- patterns should resemble the declaration of the struct type \
- being matched.\n\n\
- If you are using shorthand field patterns but want to refer \
- to the struct field by a different name, you should rename \
- it explicitly.",
+ extract a non-existent field from a struct. Struct fields \
+ are identified by the name used before the colon : so struct \
+ patterns should resemble the declaration of the struct type \
+ being matched.\n\n\
+ If you are using shorthand field patterns but want to refer \
+ to the struct field by a different name, you should rename \
+ it explicitly.",
);
}
err
fn error_unmentioned_fields(
&self,
- span: Span,
+ pat: &Pat<'_>,
unmentioned_fields: &[Ident],
) -> DiagnosticBuilder<'tcx> {
let field_names = if unmentioned_fields.len() == 1 {
.join(", ");
format!("fields {}", fields)
};
- let mut diag = struct_span_err!(
+ let mut err = struct_span_err!(
self.tcx.sess,
- span,
+ pat.span,
E0027,
"pattern does not mention {}",
field_names
);
- diag.span_label(span, format!("missing {}", field_names));
- if self.tcx.sess.teach(&diag.get_code().unwrap()) {
- diag.note(
+ err.span_label(pat.span, format!("missing {}", field_names));
+ if self.tcx.sess.teach(&err.get_code().unwrap()) {
+ err.note(
"This error indicates that a pattern for a struct fails to specify a \
- sub-pattern for every one of the struct's fields. Ensure that each field \
- from the struct's definition is mentioned in the pattern, or use `..` to \
- ignore unwanted fields.",
+ sub-pattern for every one of the struct's fields. Ensure that each field \
+ from the struct's definition is mentioned in the pattern, or use `..` to \
+ ignore unwanted fields.",
);
}
- diag
+ err
}
fn check_pat_box(
// Gather up expressions we want to munge.
let mut exprs = vec![expr];
- loop {
- match exprs.last().unwrap().kind {
- hir::ExprKind::Field(ref expr, _)
- | hir::ExprKind::Index(ref expr, _)
- | hir::ExprKind::Unary(hir::UnOp::UnDeref, ref expr) => exprs.push(&expr),
- _ => break,
- }
+ while let hir::ExprKind::Field(ref expr, _)
+ | hir::ExprKind::Index(ref expr, _)
+ | hir::ExprKind::Unary(hir::UnOp::UnDeref, ref expr) = exprs.last().unwrap().kind
+ {
+ exprs.push(&expr);
}
debug!("convert_place_derefs_to_mutable: exprs={:?}", exprs);
use crate::middle::resolve_lifetime as rl;
use rustc_ast::ast;
use rustc_ast::ast::MetaItemKind;
-use rustc_attr::{list_contains_name, mark_used, InlineAttr, OptimizeAttr};
+use rustc_attr::{list_contains_name, InlineAttr, OptimizeAttr};
use rustc_data_structures::captures::Captures;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_errors::{struct_span_err, Applicability};
// HACK(eddyb) this provides the correct generics when
// `feature(const_generics)` is enabled, so that const expressions
// used with const generics, e.g. `Foo<{N+1}>`, can work at all.
+ //
+ // Note that we do not supply the parent generics when using
+ // `feature(min_const_generics)`.
Some(parent_def_id.to_def_id())
} else {
let parent_node = tcx.hir().get(tcx.hir().get_parent_node(hir_id));
let mut link_ordinal_span = None;
let mut no_sanitize_span = None;
for attr in attrs.iter() {
- if attr.check_name(sym::cold) {
+ if tcx.sess.check_name(attr, sym::cold) {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::COLD;
- } else if attr.check_name(sym::rustc_allocator) {
+ } else if tcx.sess.check_name(attr, sym::rustc_allocator) {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR;
- } else if attr.check_name(sym::unwind) {
+ } else if tcx.sess.check_name(attr, sym::unwind) {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::UNWIND;
- } else if attr.check_name(sym::ffi_returns_twice) {
+ } else if tcx.sess.check_name(attr, sym::ffi_returns_twice) {
if tcx.is_foreign_item(id) {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_RETURNS_TWICE;
} else {
)
.emit();
}
- } else if attr.check_name(sym::ffi_pure) {
+ } else if tcx.sess.check_name(attr, sym::ffi_pure) {
if tcx.is_foreign_item(id) {
- if attrs.iter().any(|a| a.check_name(sym::ffi_const)) {
+ if attrs.iter().any(|a| tcx.sess.check_name(a, sym::ffi_const)) {
// `#[ffi_const]` functions cannot be `#[ffi_pure]`
struct_span_err!(
tcx.sess,
)
.emit();
}
- } else if attr.check_name(sym::ffi_const) {
+ } else if tcx.sess.check_name(attr, sym::ffi_const) {
if tcx.is_foreign_item(id) {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_CONST;
} else {
)
.emit();
}
- } else if attr.check_name(sym::rustc_allocator_nounwind) {
+ } else if tcx.sess.check_name(attr, sym::rustc_allocator_nounwind) {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_ALLOCATOR_NOUNWIND;
- } else if attr.check_name(sym::naked) {
+ } else if tcx.sess.check_name(attr, sym::naked) {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED;
- } else if attr.check_name(sym::no_mangle) {
+ } else if tcx.sess.check_name(attr, sym::no_mangle) {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE;
- } else if attr.check_name(sym::rustc_std_internal_symbol) {
+ } else if tcx.sess.check_name(attr, sym::rustc_std_internal_symbol) {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL;
- } else if attr.check_name(sym::used) {
+ } else if tcx.sess.check_name(attr, sym::used) {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED;
- } else if attr.check_name(sym::thread_local) {
+ } else if tcx.sess.check_name(attr, sym::thread_local) {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::THREAD_LOCAL;
- } else if attr.check_name(sym::track_caller) {
+ } else if tcx.sess.check_name(attr, sym::track_caller) {
if tcx.is_closure(id) || tcx.fn_sig(id).abi() != abi::Abi::Rust {
struct_span_err!(tcx.sess, attr.span, E0737, "`#[track_caller]` requires Rust ABI")
.emit();
}
codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER;
- } else if attr.check_name(sym::export_name) {
+ } else if tcx.sess.check_name(attr, sym::export_name) {
if let Some(s) = attr.value_str() {
if s.as_str().contains('\0') {
// `#[export_name = ...]` will be converted to a null-terminated string,
}
codegen_fn_attrs.export_name = Some(s);
}
- } else if attr.check_name(sym::target_feature) {
+ } else if tcx.sess.check_name(attr, sym::target_feature) {
if !tcx.features().target_feature_11 {
check_target_feature_safe_fn(tcx, id, attr.span);
} else if let Some(local_id) = id.as_local() {
&supported_target_features,
&mut codegen_fn_attrs.target_features,
);
- } else if attr.check_name(sym::linkage) {
+ } else if tcx.sess.check_name(attr, sym::linkage) {
if let Some(val) = attr.value_str() {
codegen_fn_attrs.linkage = Some(linkage_by_name(tcx, id, &val.as_str()));
}
- } else if attr.check_name(sym::link_section) {
+ } else if tcx.sess.check_name(attr, sym::link_section) {
if let Some(val) = attr.value_str() {
if val.as_str().bytes().any(|b| b == 0) {
let msg = format!(
codegen_fn_attrs.link_section = Some(val);
}
}
- } else if attr.check_name(sym::link_name) {
+ } else if tcx.sess.check_name(attr, sym::link_name) {
codegen_fn_attrs.link_name = attr.value_str();
- } else if attr.check_name(sym::link_ordinal) {
+ } else if tcx.sess.check_name(attr, sym::link_ordinal) {
link_ordinal_span = Some(attr.span);
if let ordinal @ Some(_) = check_link_ordinal(tcx, attr) {
codegen_fn_attrs.link_ordinal = ordinal;
}
- } else if attr.check_name(sym::no_sanitize) {
+ } else if tcx.sess.check_name(attr, sym::no_sanitize) {
no_sanitize_span = Some(attr.span);
if let Some(list) = attr.meta_item_list() {
for item in list.iter() {
}
match attr.meta().map(|i| i.kind) {
Some(MetaItemKind::Word) => {
- mark_used(attr);
+ tcx.sess.mark_attr_used(attr);
InlineAttr::Hint
}
Some(MetaItemKind::List(ref items)) => {
- mark_used(attr);
+ tcx.sess.mark_attr_used(attr);
inline_span = Some(attr.span);
if items.len() != 1 {
struct_span_err!(
ia
}
Some(MetaItemKind::List(ref items)) => {
- mark_used(attr);
+ tcx.sess.mark_attr_used(attr);
inline_span = Some(attr.span);
if items.len() != 1 {
err(attr.span, "expected one argument");
if tcx.is_weak_lang_item(id) {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL;
}
- if let Some(name) = weak_lang_items::link_name(&attrs) {
+ let check_name = |attr, sym| tcx.sess.check_name(attr, sym);
+ if let Some(name) = weak_lang_items::link_name(check_name, &attrs) {
codegen_fn_attrs.export_name = Some(name);
codegen_fn_attrs.link_name = Some(name);
}
GenericParamKind::Type { default: Some(ref ty), .. } => icx.to_ty(ty),
GenericParamKind::Const { ty: ref hir_ty, .. } => {
let ty = icx.to_ty(hir_ty);
- let err = match ty.peel_refs().kind {
- ty::FnPtr(_) => Some("function pointers"),
- ty::RawPtr(_) => Some("raw pointers"),
- _ => None,
+ let err_ty_str;
+ let err = if tcx.features().min_const_generics {
+ match ty.kind {
+ ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Error(_) => None,
+ ty::FnPtr(_) => Some("function pointers"),
+ ty::RawPtr(_) => Some("raw pointers"),
+ _ => {
+ err_ty_str = format!("`{}`", ty);
+ Some(err_ty_str.as_str())
+ }
+ }
+ } else {
+ match ty.peel_refs().kind {
+ ty::FnPtr(_) => Some("function pointers"),
+ ty::RawPtr(_) => Some("raw pointers"),
+ _ => None,
+ }
};
if let Some(unsupported_type) = err {
- tcx.sess
- .struct_span_err(
- hir_ty.span,
- &format!(
- "using {} as const generic parameters is forbidden",
- unsupported_type
- ),
- )
- .emit();
+ let mut err = tcx.sess.struct_span_err(
+ hir_ty.span,
+ &format!(
+ "using {} as const generic parameters is forbidden",
+ unsupported_type
+ ),
+ );
+
+ if tcx.features().min_const_generics {
+ err.note("the only supported types are integers, `bool` and `char`")
+ .note("more complex types are supported with `#[feature(const_generics)]`").emit()
+ } else {
+ err.emit();
+ }
};
if traits::search_for_structural_match_violation(param.hir_id, param.span, tcx, ty)
.is_some()
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_middle::util;
use rustc_session::config::EntryFnType;
-use rustc_span::{Span, DUMMY_SP};
+use rustc_span::{symbol::sym, Span, DUMMY_SP};
use rustc_target::spec::abi::Abi;
use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _;
use rustc_trait_selection::traits::{
.emit();
error = true;
}
+
+ for attr in it.attrs {
+ if tcx.sess.check_name(attr, sym::track_caller) {
+ tcx.sess
+ .struct_span_err(
+ attr.span,
+ "`main` function is not allowed to be `#[track_caller]`",
+ )
+ .span_label(
+ main_span,
+ "`main` function is not allowed to be `#[track_caller]`",
+ )
+ .emit();
+ error = true;
+ }
+ }
+
if error {
return;
}
tcx.sess,
span,
E0752,
- "start is not allowed to be `async`"
+ "`start` is not allowed to be `async`"
)
- .span_label(span, "start is not allowed to be `async`")
+ .span_label(span, "`start` is not allowed to be `async`")
.emit();
error = true;
}
+
+ for attr in it.attrs {
+ if tcx.sess.check_name(attr, sym::track_caller) {
+ tcx.sess
+ .struct_span_err(
+ attr.span,
+ "`start` is not allowed to be `#[track_caller]`",
+ )
+ .span_label(
+ start_span,
+ "`start` is not allowed to be `#[track_caller]`",
+ )
+ .emit();
+ error = true;
+ }
+ }
+
if error {
return;
}
self.tcx()
.sess
.delay_span_bug(span, "struct or tuple struct pattern not applied to an ADT");
- return Err(());
+ Err(())
}
}
}
ty::Tuple(substs) => Ok(substs.len()),
_ => {
self.tcx().sess.delay_span_bug(span, "tuple pattern not applied to a tuple");
- return Err(());
+ Err(())
}
}
}
use rustc_ast::ast::*;
use rustc_ast::attr;
-use rustc_ast::with_default_session_globals;
use rustc_span::symbol::{Ident, Symbol};
+use rustc_span::with_default_session_globals;
use rustc_span::DUMMY_SP;
fn word_cfg(s: &str) -> Cfg {
_ => None,
});
if let Some(lt) = lifetime.cloned() {
- if !lt.is_elided() {
- let lt_def_id = cx.tcx.hir().local_def_id(param.hir_id);
- lt_substs.insert(lt_def_id.to_def_id(), lt.clean(cx));
- }
+ let lt_def_id = cx.tcx.hir().local_def_id(param.hir_id);
+ let cleaned = if !lt.is_elided() {
+ lt.clean(cx)
+ } else {
+ self::types::Lifetime::elided()
+ };
+ lt_substs.insert(lt_def_id.to_def_id(), cleaned);
}
indices.lifetimes += 1;
}
output: if output != Type::Tuple(Vec::new()) { Some(output) } else { None },
}
} else {
- let elide_lifetimes = self.args.iter().all(|arg| match arg {
- hir::GenericArg::Lifetime(lt) => lt.is_elided(),
- _ => true,
- });
GenericArgs::AngleBracketed {
args: self
.args
.iter()
- .filter_map(|arg| match arg {
- hir::GenericArg::Lifetime(lt) if !elide_lifetimes => {
- Some(GenericArg::Lifetime(lt.clean(cx)))
+ .map(|arg| match arg {
+ hir::GenericArg::Lifetime(lt) if !lt.is_elided() => {
+ GenericArg::Lifetime(lt.clean(cx))
}
- hir::GenericArg::Lifetime(_) => None,
- hir::GenericArg::Type(ty) => Some(GenericArg::Type(ty.clean(cx))),
- hir::GenericArg::Const(ct) => Some(GenericArg::Const(ct.clean(cx))),
+ hir::GenericArg::Lifetime(_) => GenericArg::Lifetime(Lifetime::elided()),
+ hir::GenericArg::Type(ty) => GenericArg::Type(ty.clean(cx)),
+ hir::GenericArg::Const(ct) => GenericArg::Const(ct.clean(cx)),
})
.collect(),
bindings: self.bindings.clean(cx),
use rustc_ast::ast::{self, AttrStyle};
use rustc_ast::attr;
-use rustc_ast::util::comments::strip_doc_comment_decoration;
+use rustc_ast::util::comments::beautify_doc_string;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir as hir;
use rustc_hir::def::Res;
.iter()
.filter_map(|attr| {
if let Some(value) = attr.doc_str() {
- let (value, mk_fragment): (_, fn(_, _, _) -> _) = if attr.is_doc_comment() {
- (strip_doc_comment_decoration(value), DocFragment::SugaredDoc)
+ let value = beautify_doc_string(value);
+ let mk_fragment: fn(_, _, _) -> _ = if attr.is_doc_comment() {
+ DocFragment::SugaredDoc
} else {
- (value.to_string(), DocFragment::RawDoc)
+ DocFragment::RawDoc
};
let line = doc_line;
pub fn statik() -> Lifetime {
Lifetime("'static".to_string())
}
+
+ pub fn elided() -> Lifetime {
+ Lifetime("'_".to_string())
+ }
}
#[derive(Clone, Debug)]
use crate::clean::blanket_impl::BlanketImplFinder;
use crate::clean::{
inline, Clean, Crate, Deprecation, ExternalCrate, FnDecl, FnRetTy, Generic, GenericArg,
- GenericArgs, GenericBound, Generics, GetDefId, ImportSource, Item, ItemEnum, MacroKind, Path,
- PathSegment, Primitive, PrimitiveType, ResolvedPath, Span, Stability, Type, TypeBinding,
- TypeKind, Visibility, WherePredicate,
+ GenericArgs, GenericBound, Generics, GetDefId, ImportSource, Item, ItemEnum, Lifetime,
+ MacroKind, Path, PathSegment, Primitive, PrimitiveType, ResolvedPath, Span, Stability, Type,
+ TypeBinding, TypeKind, Visibility, WherePredicate,
};
use crate::core::DocContext;
let args: Vec<_> = substs
.iter()
.filter_map(|kind| match kind.unpack() {
- GenericArgKind::Lifetime(lt) => lt.clean(cx).map(GenericArg::Lifetime),
+ GenericArgKind::Lifetime(lt) => match lt {
+ ty::ReLateBound(_, ty::BrAnon(_)) => Some(GenericArg::Lifetime(Lifetime::elided())),
+ _ => lt.clean(cx).map(GenericArg::Lifetime),
+ },
GenericArgKind::Type(_) if skip_self => {
skip_self = false;
None
Res::Def(DefKind::TyAlias, i) => (i, TypeKind::Typedef),
Res::Def(DefKind::Enum, i) => (i, TypeKind::Enum),
Res::Def(DefKind::Trait, i) => (i, TypeKind::Trait),
+ Res::Def(DefKind::AssocTy | DefKind::AssocFn | DefKind::AssocConst, i) => {
+ (cx.tcx.parent(i).unwrap(), TypeKind::Trait)
+ }
Res::Def(DefKind::Struct, i) => (i, TypeKind::Struct),
Res::Def(DefKind::Union, i) => (i, TypeKind::Union),
Res::Def(DefKind::Mod, i) => (i, TypeKind::Module),
let sender = self.errors.clone().expect("can't write after closing");
rayon::spawn(move || {
fs::write(&path, contents).unwrap_or_else(|e| {
- sender
- .send(format!("\"{}\": {}", path.display(), e))
- .expect(&format!("failed to send error on \"{}\"", path.display()));
+ sender.send(format!("\"{}\": {}", path.display(), e)).unwrap_or_else(|_| {
+ panic!("failed to send error on \"{}\"", path.display())
+ })
});
});
- Ok(())
} else {
- Ok(try_err!(fs::write(&path, contents), path))
+ try_err!(fs::write(&path, contents), path);
}
+ Ok(())
}
}
-use rustc_ast::attr::with_session_globals;
use rustc_session::parse::ParseSess;
use rustc_span::edition::Edition;
+use rustc_span::with_session_globals;
use rustc_span::FileName;
use super::Classifier;
extern crate rustc_typeck;
extern crate test as testing;
#[macro_use]
-extern crate tracing as log;
+extern crate tracing;
use std::default::Default;
use std::env;
-use std::panic;
use std::process;
+use rustc_errors::ErrorReported;
use rustc_session::config::{make_crate_type_option, ErrorOutputType, RustcOptGroup};
use rustc_session::getopts;
use rustc_session::{early_error, early_warn};
}
pub fn main() {
- let thread_stack_size: usize = if cfg!(target_os = "haiku") {
- 16_000_000 // 16MB on Haiku
- } else {
- 32_000_000 // 32MB on other platforms
- };
rustc_driver::set_sigpipe_handler();
rustc_driver::install_ice_hook();
rustc_driver::init_env_logger("RUSTDOC_LOG");
-
- let res = std::thread::Builder::new()
- .stack_size(thread_stack_size)
- .spawn(move || get_args().map(|args| main_args(&args)).unwrap_or(1))
- .unwrap()
- .join()
- .unwrap_or(rustc_driver::EXIT_FAILURE);
- process::exit(res);
+ let exit_code = rustc_driver::catch_with_exit_code(|| match get_args() {
+ Some(args) => main_args(&args),
+ _ => Err(ErrorReported),
+ });
+ process::exit(exit_code);
}
fn get_args() -> Option<Vec<String>> {
println!("{}", options.usage(&format!("{} [options] <input>", argv0)));
}
-fn main_args(args: &[String]) -> i32 {
+/// A result type used by several functions under `main()`.
+type MainResult = Result<(), ErrorReported>;
+
+fn main_args(args: &[String]) -> MainResult {
let mut options = getopts::Options::new();
for option in opts() {
(option.apply)(&mut options);
early_error(ErrorOutputType::default(), &err.to_string());
}
};
+
+ // Note that we discard any distinction between different non-zero exit
+ // codes from `from_matches` here.
let options = match config::Options::from_matches(&matches) {
Ok(opts) => opts,
- Err(code) => return code,
+ Err(code) => return if code == 0 { Ok(()) } else { Err(ErrorReported) },
};
- rustc_interface::interface::setup_callbacks_and_run_in_default_thread_pool_with_globals(
+ rustc_interface::util::setup_callbacks_and_run_in_thread_pool_with_globals(
options.edition,
+ 1, // this runs single-threaded, even in a parallel compiler
+ &None,
move || main_options(options),
)
}
-fn wrap_return(diag: &rustc_errors::Handler, res: Result<(), String>) -> i32 {
+fn wrap_return(diag: &rustc_errors::Handler, res: Result<(), String>) -> MainResult {
match res {
- Ok(()) => 0,
+ Ok(()) => Ok(()),
Err(err) => {
- if !err.is_empty() {
- diag.struct_err(&err).emit();
- }
- 1
+ diag.struct_err(&err).emit();
+ Err(ErrorReported)
}
}
}
render_info: config::RenderInfo,
diag: &rustc_errors::Handler,
edition: rustc_span::edition::Edition,
-) -> i32 {
+) -> MainResult {
match formats::run_format::<T>(krate, renderopts, render_info, &diag, edition) {
- Ok(_) => rustc_driver::EXIT_SUCCESS,
+ Ok(_) => Ok(()),
Err(e) => {
let mut msg = diag.struct_err(&format!("couldn't generate documentation: {}", e.error));
let file = e.file.display().to_string();
} else {
msg.note(&format!("failed to create or modify \"{}\"", file)).emit()
}
- rustc_driver::EXIT_FAILURE
+ Err(ErrorReported)
}
}
}
-fn main_options(options: config::Options) -> i32 {
+fn main_options(options: config::Options) -> MainResult {
let diag = core::new_handler(options.error_format, None, &options.debugging_options);
match (options.should_test, options.markdown_input()) {
(true, true) => return wrap_return(&diag, markdown::test(options)),
- (true, false) => return wrap_return(&diag, test::run(options)),
+ (true, false) => return test::run(options),
(false, true) => {
return wrap_return(
&diag,
// compiler all the way through the analysis passes. The rustdoc output is
// then generated from the cleaned AST of the crate. This runs all the
// plug/cleaning passes.
- let result = rustc_driver::catch_fatal_errors(move || {
- let crate_name = options.crate_name.clone();
- let crate_version = options.crate_version.clone();
- let output_format = options.output_format;
- let (mut krate, renderinfo, renderopts) = core::run_core(options);
+ let crate_name = options.crate_name.clone();
+ let crate_version = options.crate_version.clone();
+ let output_format = options.output_format;
+ let (mut krate, renderinfo, renderopts) = core::run_core(options);
- info!("finished with rustc");
+ info!("finished with rustc");
- if let Some(name) = crate_name {
- krate.name = name
- }
+ if let Some(name) = crate_name {
+ krate.name = name
+ }
- krate.version = crate_version;
+ krate.version = crate_version;
- let out = Output { krate, renderinfo, renderopts };
+ let out = Output { krate, renderinfo, renderopts };
- if show_coverage {
- // if we ran coverage, bail early, we don't need to also generate docs at this point
- // (also we didn't load in any of the useful passes)
- return rustc_driver::EXIT_SUCCESS;
- }
+ if show_coverage {
+ // if we ran coverage, bail early, we don't need to also generate docs at this point
+ // (also we didn't load in any of the useful passes)
+ return Ok(());
+ }
- let Output { krate, renderinfo, renderopts } = out;
- info!("going to format");
- let (error_format, edition, debugging_options) = diag_opts;
- let diag = core::new_handler(error_format, None, &debugging_options);
- match output_format {
- None | Some(config::OutputFormat::Html) => {
- run_renderer::<html::render::Context>(krate, renderopts, renderinfo, &diag, edition)
- }
- Some(config::OutputFormat::Json) => {
- run_renderer::<json::JsonRenderer>(krate, renderopts, renderinfo, &diag, edition)
- }
+ let Output { krate, renderinfo, renderopts } = out;
+ info!("going to format");
+ let (error_format, edition, debugging_options) = diag_opts;
+ let diag = core::new_handler(error_format, None, &debugging_options);
+ match output_format {
+ None | Some(config::OutputFormat::Html) => {
+ run_renderer::<html::render::Context>(krate, renderopts, renderinfo, &diag, edition)
+ }
+ Some(config::OutputFormat::Json) => {
+ run_renderer::<json::JsonRenderer>(krate, renderopts, renderinfo, &diag, edition)
}
- });
-
- match result {
- Ok(output) => output,
- Err(_) => panic::resume_unwind(Box::new(rustc_errors::FatalErrorMarker)),
}
}
use crate::fold::{self, DocFolder};
use crate::passes::Pass;
-use rustc_ast::attr;
use rustc_span::symbol::sym;
use rustc_span::FileName;
use serde::Serialize;
return Some(i);
}
clean::ImplItem(ref impl_)
- if attr::contains_name(&i.attrs.other_attrs, sym::automatically_derived)
+ if i.attrs
+ .other_attrs
+ .iter()
+ .any(|item| item.has_name(sym::automatically_derived))
|| impl_.synthetic
|| impl_.blanket_impl.is_some() =>
{
use rustc_span::symbol::Symbol;
use rustc_span::DUMMY_SP;
+use std::cell::Cell;
use std::ops::Range;
use crate::clean::*;
cx: &'a DocContext<'tcx>,
// NOTE: this may not necessarily be a module in the current crate
mod_ids: Vec<DefId>,
+ /// This is used to store the kind of associated items,
+ /// because `clean` and the disambiguator code expect them to be different.
+ /// See the code for associated items on inherent impls for details.
+ kind_side_channel: Cell<Option<DefKind>>,
}
impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
fn new(cx: &'a DocContext<'tcx>) -> Self {
- LinkCollector { cx, mod_ids: Vec::new() }
+ LinkCollector { cx, mod_ids: Vec::new(), kind_side_channel: Cell::new(None) }
}
fn variant_field(
fn resolve(
&self,
path_str: &str,
- disambiguator: Option<&str>,
+ disambiguator: Option<Disambiguator>,
ns: Namespace,
current_item: &Option<String>,
parent_id: Option<DefId>,
Res::Def(DefKind::Mod, _) => {
// This resolved to a module, but if we were passed `type@`,
// we want primitive types to take precedence instead.
- if disambiguator == Some("type") {
+ if disambiguator == Some(Disambiguator::Namespace(Namespace::TypeNS)) {
if let Some(prim) = is_primitive(path_str, ns) {
if extra_fragment.is_some() {
return Err(ErrorKind::AnchorFailure(AnchorFailure::Primitive));
AnchorFailure::AssocConstant
}))
} else {
+ // HACK(jynelson): `clean` expects the type, not the associated item.
+ // but the disambiguator logic expects the associated item.
+ // Store the kind in a side channel so that only the disambiguator logic looks at it.
+ self.kind_side_channel.replace(Some(item.kind.as_def_kind()));
Ok((ty_res, Some(format!("{}.{}", out, item_name))))
}
} else {
AnchorFailure::Method
}))
} else {
- Ok((ty_res, Some(format!("{}.{}", kind, item_name))))
+ let res = Res::Def(item.kind.as_def_kind(), item.def_id);
+ Ok((res, Some(format!("{}.{}", kind, item_name))))
}
} else {
self.variant_field(path_str, current_item, module_id)
};
let resolved_self;
let mut path_str;
+ let disambiguator;
let (res, fragment) = {
- let mut kind = None;
- let mut disambiguator = None;
- path_str = if let Some(prefix) =
- ["struct@", "enum@", "type@", "trait@", "union@", "module@", "mod@"]
- .iter()
- .find(|p| link.starts_with(**p))
- {
- kind = Some(TypeNS);
- disambiguator = Some(&prefix[..prefix.len() - 1]);
- link.trim_start_matches(prefix)
- } else if let Some(prefix) =
- ["const@", "static@", "value@", "function@", "fn@", "method@"]
- .iter()
- .find(|p| link.starts_with(**p))
- {
- kind = Some(ValueNS);
- disambiguator = Some(&prefix[..prefix.len() - 1]);
- link.trim_start_matches(prefix)
- } else if link.ends_with("!()") {
- kind = Some(MacroNS);
- link.trim_end_matches("!()")
- } else if link.ends_with("()") {
- kind = Some(ValueNS);
- disambiguator = Some("fn");
- link.trim_end_matches("()")
- } else if link.starts_with("macro@") {
- kind = Some(MacroNS);
- disambiguator = Some("macro");
- link.trim_start_matches("macro@")
- } else if link.starts_with("derive@") {
- kind = Some(MacroNS);
- disambiguator = Some("derive");
- link.trim_start_matches("derive@")
- } else if link.ends_with('!') {
- kind = Some(MacroNS);
- disambiguator = Some("macro");
- link.trim_end_matches('!')
+ path_str = if let Ok((d, path)) = Disambiguator::from_str(&link) {
+ disambiguator = Some(d);
+ path
} else {
- &link[..]
+ disambiguator = None;
+ &link
}
.trim();
}
}
- match kind {
+ match disambiguator.map(Disambiguator::ns) {
Some(ns @ ValueNS) => {
match self.resolve(
path_str,
} else {
debug!("intra-doc link to {} resolved to {:?}", path_str, res);
+ // Disallow e.g. linking to enums with `struct@`
+ if let Res::Def(kind, id) = res {
+ debug!("saw kind {:?} with disambiguator {:?}", kind, disambiguator);
+ match (self.kind_side_channel.take().unwrap_or(kind), disambiguator) {
+ | (DefKind::Const | DefKind::ConstParam | DefKind::AssocConst | DefKind::AnonConst, Some(Disambiguator::Kind(DefKind::Const)))
+ // NOTE: this allows 'method' to mean both normal functions and associated functions
+ // This can't cause ambiguity because both are in the same namespace.
+ | (DefKind::Fn | DefKind::AssocFn, Some(Disambiguator::Kind(DefKind::Fn)))
+ // These are namespaces; allow anything in the namespace to match
+ | (_, Some(Disambiguator::Namespace(_)))
+ // If no disambiguator given, allow anything
+ | (_, None)
+ // All of these are valid, so do nothing
+ => {}
+ (actual, Some(Disambiguator::Kind(expected))) if actual == expected => {}
+ (_, Some(Disambiguator::Kind(expected))) => {
+ // The resolved item did not match the disambiguator; give a better error than 'not found'
+ let msg = format!("incompatible link kind for `{}`", path_str);
+ report_diagnostic(cx, &msg, &item, &dox, link_range, |diag, sp| {
+ // HACK(jynelson): by looking at the source I saw the DefId we pass
+ // for `expected.descr()` doesn't matter, since it's not a crate
+ let note = format!("this link resolved to {} {}, which is not {} {}", kind.article(), kind.descr(id), expected.article(), expected.descr(id));
+ let suggestion = Disambiguator::display_for(kind, path_str);
+ let help_msg = format!("to link to the {}, use its disambiguator", kind.descr(id));
+ diag.note(¬e);
+ if let Some(sp) = sp {
+ diag.span_suggestion(sp, &help_msg, suggestion, Applicability::MaybeIncorrect);
+ } else {
+ diag.help(&format!("{}: {}", help_msg, suggestion));
+ }
+ });
+ continue;
+ }
+ }
+ }
+
// item can be non-local e.g. when using #[doc(primitive = "pointer")]
if let Some((src_id, dst_id)) = res
.opt_def_id()
}
}
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+enum Disambiguator {
+ Kind(DefKind),
+ Namespace(Namespace),
+}
+
+impl Disambiguator {
+ /// (disambiguator, path_str)
+ fn from_str(link: &str) -> Result<(Self, &str), ()> {
+ use Disambiguator::{Kind, Namespace as NS};
+
+ let find_suffix = || {
+ let suffixes = [
+ ("!()", DefKind::Macro(MacroKind::Bang)),
+ ("()", DefKind::Fn),
+ ("!", DefKind::Macro(MacroKind::Bang)),
+ ];
+ for &(suffix, kind) in &suffixes {
+ if link.ends_with(suffix) {
+ return Ok((Kind(kind), link.trim_end_matches(suffix)));
+ }
+ }
+ Err(())
+ };
+
+ if let Some(idx) = link.find('@') {
+ let (prefix, rest) = link.split_at(idx);
+ let d = match prefix {
+ "struct" => Kind(DefKind::Struct),
+ "enum" => Kind(DefKind::Enum),
+ "trait" => Kind(DefKind::Trait),
+ "union" => Kind(DefKind::Union),
+ "module" | "mod" => Kind(DefKind::Mod),
+ "const" | "constant" => Kind(DefKind::Const),
+ "static" => Kind(DefKind::Static),
+ "function" | "fn" | "method" => Kind(DefKind::Fn),
+ "derive" => Kind(DefKind::Macro(MacroKind::Derive)),
+ "type" => NS(Namespace::TypeNS),
+ "value" => NS(Namespace::ValueNS),
+ "macro" => NS(Namespace::MacroNS),
+ _ => return find_suffix(),
+ };
+ Ok((d, &rest[1..]))
+ } else {
+ find_suffix()
+ }
+ }
+
+ fn display_for(kind: DefKind, path_str: &str) -> String {
+ if kind == DefKind::Macro(MacroKind::Bang) {
+ return format!("{}!", path_str);
+ } else if kind == DefKind::Fn || kind == DefKind::AssocFn {
+ return format!("{}()", path_str);
+ }
+ let prefix = match kind {
+ DefKind::Struct => "struct",
+ DefKind::Enum => "enum",
+ DefKind::Trait => "trait",
+ DefKind::Union => "union",
+ DefKind::Mod => "mod",
+ DefKind::Const | DefKind::ConstParam | DefKind::AssocConst | DefKind::AnonConst => {
+ "const"
+ }
+ DefKind::Static => "static",
+ DefKind::Macro(MacroKind::Derive) => "derive",
+ // Now handle things that don't have a specific disambiguator
+ _ => match kind
+ .ns()
+ .expect("tried to calculate a disambiguator for a def without a namespace?")
+ {
+ Namespace::TypeNS => "type",
+ Namespace::ValueNS => "value",
+ Namespace::MacroNS => "macro",
+ },
+ };
+ format!("{}@{}", prefix, path_str)
+ }
+
+ fn ns(self) -> Namespace {
+ match self {
+ Self::Namespace(n) => n,
+ Self::Kind(k) => {
+ k.ns().expect("only DefKinds with a valid namespace can be disambiguators")
+ }
+ }
+ }
+}
+
/// Reports a diagnostic for an intra-doc link.
///
/// If no link range is provided, or the source span of the link cannot be determined, the span of
let mut new_items = Vec::new();
for &cnum in cx.tcx.crates().iter() {
- for &did in cx.tcx.all_trait_implementations(cnum).iter() {
+ for &(did, _) in cx.tcx.all_trait_implementations(cnum).iter() {
inline::build_impl(cx, did, None, &mut new_items);
}
}
pub attrs: Vec<String>,
}
-pub fn run(options: Options) -> Result<(), String> {
+pub fn run(options: Options) -> Result<(), ErrorReported> {
let input = config::Input::File(options.input.clone());
let invalid_codeblock_attributes_name = rustc_lint::builtin::INVALID_CODEBLOCK_ATTRIBUTES.name;
});
let tests = match tests {
Ok(tests) => tests,
- Err(ErrorReported) => return Err(String::new()),
+ Err(ErrorReported) => return Err(ErrorReported),
};
test_args.insert(0, "rustdoctest".to_string());
// Uses librustc_ast to parse the doctest and find if there's a main fn and the extern
// crate already is included.
let result = rustc_driver::catch_fatal_errors(|| {
- rustc_ast::with_session_globals(edition, || {
+ rustc_span::with_session_globals(edition, || {
use rustc_errors::emitter::EmitterWriter;
use rustc_errors::Handler;
use rustc_parse::maybe_new_parser_from_source_str;
// The collapse-docs pass won't combine sugared/raw doc attributes, or included files with
// anything else, this will combine them for us.
if let Some(doc) = attrs.collapsed_doc_value() {
- self.collector.set_position(attrs.span.unwrap_or(DUMMY_SP));
+ // Use the outermost invocation, so that doctest names come from where the docs were written.
+ let span = attrs
+ .span
+ .map(|span| span.ctxt().outer_expn().expansion_cause().unwrap_or(span))
+ .unwrap_or(DUMMY_SP);
+ self.collector.set_position(span);
markdown::find_testable_code(
&doc,
self.collector,
--- /dev/null
+// compile-flags:-Zpolymorphize=on -Zprint-mono-items=lazy -Copt-level=1
+// ignore-tidy-linelength
+
+#![crate_type = "rlib"]
+
+// Test that only one copy of `Iter::map` and `iter::repeat` are generated.
+
+fn unused<T>() -> u64 {
+ 42
+}
+
+fn foo<T>() {
+ let x = [1, 2, 3, std::mem::size_of::<T>()];
+ x.iter().map(|_| ());
+}
+
+//~ MONO_ITEM fn core::iter[0]::adapters[0]::{{impl}}[29]::new[0]<core::slice[0]::Iter[0]<usize>, pr_75255::foo[0]::{{closure}}[0]<T>> @@ pr_75255-cgu.0[External]
+//~ MONO_ITEM fn core::iter[0]::traits[0]::iterator[0]::Iterator[0]::map[0]<core::slice[0]::Iter[0]<usize>, (), pr_75255::foo[0]::{{closure}}[0]<T>> @@ pr_75255-cgu.1[Internal]
+
+fn bar<T>() {
+ std::iter::repeat(unused::<T>);
+}
+
+//~ MONO_ITEM fn core::iter[0]::sources[0]::repeat[0]<fn() -> u64> @@ pr_75255-cgu.1[Internal]
+
+pub fn dispatch() {
+ foo::<String>();
+ foo::<Vec<String>>();
+
+ bar::<String>();
+ bar::<Vec<String>>();
+}
+
+// These are all the items that aren't relevant to the test.
+//~ MONO_ITEM fn core::mem[0]::size_of[0]<alloc::string[0]::String[0]> @@ pr_75255-cgu.1[Internal]
+//~ MONO_ITEM fn core::mem[0]::size_of[0]<alloc::vec[0]::Vec[0]<alloc::string[0]::String[0]>> @@ pr_75255-cgu.1[Internal]
+//~ MONO_ITEM fn core::mem[0]::size_of[0]<usize> @@ pr_75255-cgu.1[Internal]
+//~ MONO_ITEM fn core::ptr[0]::const_ptr[0]::{{impl}}[0]::add[0]<usize> @@ pr_75255-cgu.1[Internal]
+//~ MONO_ITEM fn core::ptr[0]::const_ptr[0]::{{impl}}[0]::is_null[0]<usize> @@ pr_75255-cgu.1[Internal]
+//~ MONO_ITEM fn core::ptr[0]::const_ptr[0]::{{impl}}[0]::offset[0]<usize> @@ pr_75255-cgu.1[Internal]
+//~ MONO_ITEM fn core::ptr[0]::const_ptr[0]::{{impl}}[0]::wrapping_add[0]<u8> @@ pr_75255-cgu.1[Internal]
+//~ MONO_ITEM fn core::ptr[0]::const_ptr[0]::{{impl}}[0]::wrapping_offset[0]<u8> @@ pr_75255-cgu.1[Internal]
+//~ MONO_ITEM fn core::ptr[0]::non_null[0]::{{impl}}[3]::new_unchecked[0]<usize> @@ pr_75255-cgu.1[Internal]
+//~ MONO_ITEM fn core::ptr[0]::null[0]<u8> @@ pr_75255-cgu.1[Internal]
+//~ MONO_ITEM fn core::slice[0]::{{impl}}[0]::as_ptr[0]<usize> @@ pr_75255-cgu.1[Internal]
+//~ MONO_ITEM fn core::slice[0]::{{impl}}[0]::iter[0]<usize> @@ pr_75255-cgu.1[Internal]
+//~ MONO_ITEM fn core::slice[0]::{{impl}}[0]::len[0]<usize> @@ pr_75255-cgu.1[Internal]
+//~ MONO_ITEM fn pr_75255::dispatch[0] @@ pr_75255-cgu.1[External]
+//~ MONO_ITEM fn pr_75255::foo[0]<alloc::string[0]::String[0]> @@ pr_75255-cgu.1[Internal]
+//~ MONO_ITEM fn pr_75255::foo[0]<alloc::vec[0]::Vec[0]<alloc::string[0]::String[0]>> @@ pr_75255-cgu.1[Internal]
+//~ MONO_ITEM fn pr_75255::bar[0]<alloc::string[0]::String[0]> @@ pr_75255-cgu.1[Internal]
+//~ MONO_ITEM fn pr_75255::bar[0]<alloc::vec[0]::Vec[0]<alloc::string[0]::String[0]>> @@ pr_75255-cgu.1[Internal]
--- /dev/null
+// compile-flags: -O
+
+#![crate_type = "lib"]
+
+// CHECK-LABEL: @test
+// CHECK-NEXT: start:
+// CHECK-NEXT: tail call void @ext_fn0()
+#[no_mangle]
+pub fn test() {
+ test_inner(Some(inner0));
+}
+
+fn test_inner(f_maybe: Option<fn()>) {
+ if let Some(f) = f_maybe {
+ f();
+ }
+}
+
+fn inner0() {
+ unsafe { ext_fn0() };
+}
+
+extern "C" {
+ fn ext_fn0();
+}
// We have to ignore android because of this issue:
// https://github.com/rust-lang/rust/issues/74847
// ignore-android
+//
+// We need to use inline assembly, so just use one platform
+// only-x86_64
// compile-flags:-g
// lldb-command:continue
+#![feature(asm)]
#![feature(naked_functions)]
#![feature(omit_gdb_pretty_printer_section)]
#![omit_gdb_pretty_printer_section]
}
#[naked]
-fn naked(x: usize, y: usize) {
- zzz(); // #break
+extern "C" fn naked(x: usize, y: usize) {
+ unsafe { asm!("ret"); } // #break
}
-
-fn zzz() { () }
// cdb-check:hash_set,d [...] : { size=15 } [Type: [...]::HashSet<u64, [...]>]
// cdb-check: [size] : 15 [Type: [...]]
// cdb-check: [capacity] : [...]
-// cdb-check: [[...]] [...] : 0 [Type: unsigned __int64]
+// cdb-check: [[...]] [...] : 0 [Type: u64]
// cdb-command: dx hash_set,d
-// cdb-check: [[...]] [...] : 1 [Type: unsigned __int64]
+// cdb-check: [[...]] [...] : 1 [Type: u64]
// cdb-command: dx hash_set,d
-// cdb-check: [[...]] [...] : 2 [Type: unsigned __int64]
+// cdb-check: [[...]] [...] : 2 [Type: u64]
// cdb-command: dx hash_set,d
-// cdb-check: [[...]] [...] : 3 [Type: unsigned __int64]
+// cdb-check: [[...]] [...] : 3 [Type: u64]
// cdb-command: dx hash_set,d
-// cdb-check: [[...]] [...] : 4 [Type: unsigned __int64]
+// cdb-check: [[...]] [...] : 4 [Type: u64]
// cdb-command: dx hash_set,d
-// cdb-check: [[...]] [...] : 5 [Type: unsigned __int64]
+// cdb-check: [[...]] [...] : 5 [Type: u64]
// cdb-command: dx hash_set,d
-// cdb-check: [[...]] [...] : 6 [Type: unsigned __int64]
+// cdb-check: [[...]] [...] : 6 [Type: u64]
// cdb-command: dx hash_set,d
-// cdb-check: [[...]] [...] : 7 [Type: unsigned __int64]
+// cdb-check: [[...]] [...] : 7 [Type: u64]
// cdb-command: dx hash_set,d
-// cdb-check: [[...]] [...] : 8 [Type: unsigned __int64]
+// cdb-check: [[...]] [...] : 8 [Type: u64]
// cdb-command: dx hash_set,d
-// cdb-check: [[...]] [...] : 9 [Type: unsigned __int64]
+// cdb-check: [[...]] [...] : 9 [Type: u64]
// cdb-command: dx hash_set,d
-// cdb-check: [[...]] [...] : 10 [Type: unsigned __int64]
+// cdb-check: [[...]] [...] : 10 [Type: u64]
// cdb-command: dx hash_set,d
-// cdb-check: [[...]] [...] : 11 [Type: unsigned __int64]
+// cdb-check: [[...]] [...] : 11 [Type: u64]
// cdb-command: dx hash_set,d
-// cdb-check: [[...]] [...] : 12 [Type: unsigned __int64]
+// cdb-check: [[...]] [...] : 12 [Type: u64]
// cdb-command: dx hash_set,d
-// cdb-check: [[...]] [...] : 13 [Type: unsigned __int64]
+// cdb-check: [[...]] [...] : 13 [Type: u64]
// cdb-command: dx hash_set,d
-// cdb-check: [[...]] [...] : 14 [Type: unsigned __int64]
+// cdb-check: [[...]] [...] : 14 [Type: u64]
// cdb-command: dx hash_map,d
// cdb-check:hash_map,d [...] : { size=15 } [Type: [...]::HashMap<u64, u64, [...]>]
+// ignore-endian-big
extern "C" {
static X: i32;
}
-
static Y: i32 = 42;
// EMIT_MIR const_promotion_extern_static.BAR.PromoteTemps.diff
+// ignore-endian-big
// EMIT_MIR_FOR_EACH_BIT_WIDTH
-
static FOO: &[(Option<i32>, &[&str])] =
&[(None, &[]), (None, &["foo", "bar"]), (Some(42), &["meh", "mop", "möp"])];
+// ignore-endian-big
// EMIT_MIR_FOR_EACH_BIT_WIDTH
-
// EMIT_MIR const_allocation2.main.ConstProp.after.mir
fn main() {
FOO;
+// ignore-endian-big
// EMIT_MIR_FOR_EACH_BIT_WIDTH
-
// EMIT_MIR const_allocation3.main.ConstProp.after.mir
fn main() {
FOO;
--- /dev/null
+- // MIR for `main` before ConstProp
++ // MIR for `main` after ConstProp
+
+ fn main() -> () {
+ let mut _0: (); // return place in scope 0 at $DIR/large_array_index.rs:4:11: 4:11
+ let _1: u8; // in scope 0 at $DIR/large_array_index.rs:6:9: 6:10
+ let mut _2: [u8; 5000]; // in scope 0 at $DIR/large_array_index.rs:6:17: 6:29
+ let _3: usize; // in scope 0 at $DIR/large_array_index.rs:6:30: 6:31
+ let mut _4: usize; // in scope 0 at $DIR/large_array_index.rs:6:17: 6:32
+ let mut _5: bool; // in scope 0 at $DIR/large_array_index.rs:6:17: 6:32
+ scope 1 {
+ debug x => _1; // in scope 1 at $DIR/large_array_index.rs:6:9: 6:10
+ }
+
+ bb0: {
+ StorageLive(_1); // scope 0 at $DIR/large_array_index.rs:6:9: 6:10
+ StorageLive(_2); // scope 0 at $DIR/large_array_index.rs:6:17: 6:29
+ _2 = [const 0_u8; 5000]; // scope 0 at $DIR/large_array_index.rs:6:17: 6:29
+ // ty::Const
+ // + ty: u8
+ // + val: Value(Scalar(0x00))
+ // mir::Constant
+ // + span: $DIR/large_array_index.rs:6:18: 6:22
+ // + literal: Const { ty: u8, val: Value(Scalar(0x00)) }
+ StorageLive(_3); // scope 0 at $DIR/large_array_index.rs:6:30: 6:31
+ _3 = const 2_usize; // scope 0 at $DIR/large_array_index.rs:6:30: 6:31
+ // ty::Const
+ // + ty: usize
+ // + val: Value(Scalar(0x00000002))
+ // mir::Constant
+ // + span: $DIR/large_array_index.rs:6:30: 6:31
+ // + literal: Const { ty: usize, val: Value(Scalar(0x00000002)) }
+ _4 = const 5000_usize; // scope 0 at $DIR/large_array_index.rs:6:17: 6:32
+ // ty::Const
+ // + ty: usize
+ // + val: Value(Scalar(0x00001388))
+ // mir::Constant
+ // + span: $DIR/large_array_index.rs:6:17: 6:32
+ // + literal: Const { ty: usize, val: Value(Scalar(0x00001388)) }
+- _5 = Lt(_3, _4); // scope 0 at $DIR/large_array_index.rs:6:17: 6:32
+- assert(move _5, "index out of bounds: the len is {} but the index is {}", move _4, _3) -> bb1; // scope 0 at $DIR/large_array_index.rs:6:17: 6:32
++ _5 = const true; // scope 0 at $DIR/large_array_index.rs:6:17: 6:32
++ // ty::Const
++ // + ty: bool
++ // + val: Value(Scalar(0x01))
++ // mir::Constant
++ // + span: $DIR/large_array_index.rs:6:17: 6:32
++ // + literal: Const { ty: bool, val: Value(Scalar(0x01)) }
++ assert(const true, "index out of bounds: the len is {} but the index is {}", const 5000_usize, const 2_usize) -> bb1; // scope 0 at $DIR/large_array_index.rs:6:17: 6:32
++ // ty::Const
++ // + ty: bool
++ // + val: Value(Scalar(0x01))
++ // mir::Constant
++ // + span: $DIR/large_array_index.rs:6:17: 6:32
++ // + literal: Const { ty: bool, val: Value(Scalar(0x01)) }
++ // ty::Const
++ // + ty: usize
++ // + val: Value(Scalar(0x00001388))
++ // mir::Constant
++ // + span: $DIR/large_array_index.rs:6:17: 6:32
++ // + literal: Const { ty: usize, val: Value(Scalar(0x00001388)) }
++ // ty::Const
++ // + ty: usize
++ // + val: Value(Scalar(0x00000002))
++ // mir::Constant
++ // + span: $DIR/large_array_index.rs:6:17: 6:32
++ // + literal: Const { ty: usize, val: Value(Scalar(0x00000002)) }
+ }
+
+ bb1: {
+ _1 = _2[_3]; // scope 0 at $DIR/large_array_index.rs:6:17: 6:32
+ StorageDead(_3); // scope 0 at $DIR/large_array_index.rs:6:32: 6:33
+ StorageDead(_2); // scope 0 at $DIR/large_array_index.rs:6:32: 6:33
+ _0 = const (); // scope 0 at $DIR/large_array_index.rs:4:11: 7:2
+ // ty::Const
+ // + ty: ()
+ // + val: Value(Scalar(<ZST>))
+ // mir::Constant
+ // + span: $DIR/large_array_index.rs:4:11: 7:2
+ // + literal: Const { ty: (), val: Value(Scalar(<ZST>)) }
+ StorageDead(_1); // scope 0 at $DIR/large_array_index.rs:7:1: 7:2
+ return; // scope 0 at $DIR/large_array_index.rs:7:2: 7:2
+ }
+ }
+
--- /dev/null
+- // MIR for `main` before ConstProp
++ // MIR for `main` after ConstProp
+
+ fn main() -> () {
+ let mut _0: (); // return place in scope 0 at $DIR/large_array_index.rs:4:11: 4:11
+ let _1: u8; // in scope 0 at $DIR/large_array_index.rs:6:9: 6:10
+ let mut _2: [u8; 5000]; // in scope 0 at $DIR/large_array_index.rs:6:17: 6:29
+ let _3: usize; // in scope 0 at $DIR/large_array_index.rs:6:30: 6:31
+ let mut _4: usize; // in scope 0 at $DIR/large_array_index.rs:6:17: 6:32
+ let mut _5: bool; // in scope 0 at $DIR/large_array_index.rs:6:17: 6:32
+ scope 1 {
+ debug x => _1; // in scope 1 at $DIR/large_array_index.rs:6:9: 6:10
+ }
+
+ bb0: {
+ StorageLive(_1); // scope 0 at $DIR/large_array_index.rs:6:9: 6:10
+ StorageLive(_2); // scope 0 at $DIR/large_array_index.rs:6:17: 6:29
+ _2 = [const 0_u8; 5000]; // scope 0 at $DIR/large_array_index.rs:6:17: 6:29
+ // ty::Const
+ // + ty: u8
+ // + val: Value(Scalar(0x00))
+ // mir::Constant
+ // + span: $DIR/large_array_index.rs:6:18: 6:22
+ // + literal: Const { ty: u8, val: Value(Scalar(0x00)) }
+ StorageLive(_3); // scope 0 at $DIR/large_array_index.rs:6:30: 6:31
+ _3 = const 2_usize; // scope 0 at $DIR/large_array_index.rs:6:30: 6:31
+ // ty::Const
+ // + ty: usize
+ // + val: Value(Scalar(0x0000000000000002))
+ // mir::Constant
+ // + span: $DIR/large_array_index.rs:6:30: 6:31
+ // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000002)) }
+ _4 = const 5000_usize; // scope 0 at $DIR/large_array_index.rs:6:17: 6:32
+ // ty::Const
+ // + ty: usize
+ // + val: Value(Scalar(0x0000000000001388))
+ // mir::Constant
+ // + span: $DIR/large_array_index.rs:6:17: 6:32
+ // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000001388)) }
+- _5 = Lt(_3, _4); // scope 0 at $DIR/large_array_index.rs:6:17: 6:32
+- assert(move _5, "index out of bounds: the len is {} but the index is {}", move _4, _3) -> bb1; // scope 0 at $DIR/large_array_index.rs:6:17: 6:32
++ _5 = const true; // scope 0 at $DIR/large_array_index.rs:6:17: 6:32
++ // ty::Const
++ // + ty: bool
++ // + val: Value(Scalar(0x01))
++ // mir::Constant
++ // + span: $DIR/large_array_index.rs:6:17: 6:32
++ // + literal: Const { ty: bool, val: Value(Scalar(0x01)) }
++ assert(const true, "index out of bounds: the len is {} but the index is {}", const 5000_usize, const 2_usize) -> bb1; // scope 0 at $DIR/large_array_index.rs:6:17: 6:32
++ // ty::Const
++ // + ty: bool
++ // + val: Value(Scalar(0x01))
++ // mir::Constant
++ // + span: $DIR/large_array_index.rs:6:17: 6:32
++ // + literal: Const { ty: bool, val: Value(Scalar(0x01)) }
++ // ty::Const
++ // + ty: usize
++ // + val: Value(Scalar(0x0000000000001388))
++ // mir::Constant
++ // + span: $DIR/large_array_index.rs:6:17: 6:32
++ // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000001388)) }
++ // ty::Const
++ // + ty: usize
++ // + val: Value(Scalar(0x0000000000000002))
++ // mir::Constant
++ // + span: $DIR/large_array_index.rs:6:17: 6:32
++ // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000002)) }
+ }
+
+ bb1: {
+ _1 = _2[_3]; // scope 0 at $DIR/large_array_index.rs:6:17: 6:32
+ StorageDead(_3); // scope 0 at $DIR/large_array_index.rs:6:32: 6:33
+ StorageDead(_2); // scope 0 at $DIR/large_array_index.rs:6:32: 6:33
+ _0 = const (); // scope 0 at $DIR/large_array_index.rs:4:11: 7:2
+ // ty::Const
+ // + ty: ()
+ // + val: Value(Scalar(<ZST>))
+ // mir::Constant
+ // + span: $DIR/large_array_index.rs:4:11: 7:2
+ // + literal: Const { ty: (), val: Value(Scalar(<ZST>)) }
+ StorageDead(_1); // scope 0 at $DIR/large_array_index.rs:7:1: 7:2
+ return; // scope 0 at $DIR/large_array_index.rs:7:2: 7:2
+ }
+ }
+
--- /dev/null
+// EMIT_MIR_FOR_EACH_BIT_WIDTH
+
+// EMIT_MIR large_array_index.main.ConstProp.diff
+fn main() {
+ // check that we don't propagate this, because it's too large
+ let x: u8 = [0_u8; 5000][2];
+}
// + ty: i32
// + val: Value(Scalar(0x0000002a))
// mir::Constant
-- // + span: $DIR/mutable_variable_aggregate_mut_ref.rs:5:18: 5:20
-+ // + span: $DIR/mutable_variable_aggregate_mut_ref.rs:5:17: 5:25
+ // + span: $DIR/mutable_variable_aggregate_mut_ref.rs:5:18: 5:20
// + literal: Const { ty: i32, val: Value(Scalar(0x0000002a)) }
// ty::Const
// + ty: i32
// + val: Value(Scalar(0x0000002b))
// mir::Constant
-- // + span: $DIR/mutable_variable_aggregate_mut_ref.rs:5:22: 5:24
-+ // + span: $DIR/mutable_variable_aggregate_mut_ref.rs:5:17: 5:25
+ // + span: $DIR/mutable_variable_aggregate_mut_ref.rs:5:22: 5:24
// + literal: Const { ty: i32, val: Value(Scalar(0x0000002b)) }
StorageLive(_2); // scope 1 at $DIR/mutable_variable_aggregate_mut_ref.rs:6:9: 6:10
_2 = &mut _1; // scope 1 at $DIR/mutable_variable_aggregate_mut_ref.rs:6:13: 6:19
+// ignore-endian-big
// ignore-wasm32-bare compiled with panic=abort by default
// compile-flags: -Z mir-opt-level=3
// EMIT_MIR_FOR_EACH_BIT_WIDTH
#![feature(box_syntax)]
-
// EMIT_MIR inline_into_box_place.main.Inline.diff
fn main() {
let _x: Box<Vec<u32>> = box Vec::new();
+ // MIR for `bar` after InstrumentCoverage
fn bar() -> bool {
- let mut _0: bool; // return place in scope 0 at $DIR/instrument_coverage.rs:18:13: 18:17
-+ let mut _1: (); // in scope 0 at $DIR/instrument_coverage.rs:18:18: 18:18
+ let mut _0: bool; // return place in scope 0 at /the/src/instrument_coverage.rs:19:13: 19:17
++ let mut _1: (); // in scope 0 at /the/src/instrument_coverage.rs:19:18: 19:18
bb0: {
-+ StorageLive(_1); // scope 0 at $DIR/instrument_coverage.rs:18:18: 18:18
-+ _1 = const std::intrinsics::count_code_region(const 10208505205182607101_u64, const 0_u32, const 529_u32, const 541_u32) -> bb2; // scope 0 at $DIR/instrument_coverage.rs:18:18: 18:18
++ StorageLive(_1); // scope 0 at /the/src/instrument_coverage.rs:19:18: 19:18
++ _1 = const std::intrinsics::count_code_region(const 10208505205182607101_u64, const 0_u32, const "/the/src/instrument_coverage.rs", const 19_u32, const 18_u32, const 21_u32, const 2_u32) -> bb2; // scope 0 at /the/src/instrument_coverage.rs:19:18: 19:18
+ // ty::Const
-+ // + ty: unsafe extern "rust-intrinsic" fn(u64, u32, u32, u32) {std::intrinsics::count_code_region}
++ // + ty: unsafe extern "rust-intrinsic" fn(u64, u32, &'static str, u32, u32, u32, u32) {std::intrinsics::count_code_region}
+ // + val: Value(Scalar(<ZST>))
+ // mir::Constant
-+ // + span: $DIR/instrument_coverage.rs:18:18: 18:18
-+ // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(u64, u32, u32, u32) {std::intrinsics::count_code_region}, val: Value(Scalar(<ZST>)) }
++ // + span: /the/src/instrument_coverage.rs:19:18: 19:18
++ // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(u64, u32, &'static str, u32, u32, u32, u32) {std::intrinsics::count_code_region}, val: Value(Scalar(<ZST>)) }
+ // ty::Const
+ // + ty: u64
+ // + val: Value(Scalar(0x8dabe565aaa2aefd))
+ // mir::Constant
-+ // + span: $DIR/instrument_coverage.rs:18:18: 18:18
++ // + span: /the/src/instrument_coverage.rs:19:18: 19:18
+ // + literal: Const { ty: u64, val: Value(Scalar(0x8dabe565aaa2aefd)) }
+ // ty::Const
+ // + ty: u32
+ // + val: Value(Scalar(0x00000000))
+ // mir::Constant
-+ // + span: $DIR/instrument_coverage.rs:18:18: 18:18
++ // + span: /the/src/instrument_coverage.rs:19:18: 19:18
+ // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) }
+ // ty::Const
++ // + ty: &str
++ // + val: Value(Slice { data: Allocation { bytes: [47, 116, 104, 101, 47, 115, 114, 99, 47, 105, 110, 115, 116, 114, 117, 109, 101, 110, 116, 95, 99, 111, 118, 101, 114, 97, 103, 101, 46, 114, 115], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [2147483647], len: Size { raw: 31 } }, size: Size { raw: 31 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 31 })
++ // mir::Constant
++ // + span: /the/src/instrument_coverage.rs:19:18: 19:18
++ // + literal: Const { ty: &str, val: Value(Slice { data: Allocation { bytes: [47, 116, 104, 101, 47, 115, 114, 99, 47, 105, 110, 115, 116, 114, 117, 109, 101, 110, 116, 95, 99, 111, 118, 101, 114, 97, 103, 101, 46, 114, 115], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [2147483647], len: Size { raw: 31 } }, size: Size { raw: 31 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 31 }) }
++ // ty::Const
++ // + ty: u32
++ // + val: Value(Scalar(0x00000013))
++ // mir::Constant
++ // + span: /the/src/instrument_coverage.rs:19:18: 19:18
++ // + literal: Const { ty: u32, val: Value(Scalar(0x00000013)) }
++ // ty::Const
++ // + ty: u32
++ // + val: Value(Scalar(0x00000012))
++ // mir::Constant
++ // + span: /the/src/instrument_coverage.rs:19:18: 19:18
++ // + literal: Const { ty: u32, val: Value(Scalar(0x00000012)) }
++ // ty::Const
+ // + ty: u32
-+ // + val: Value(Scalar(0x00000211))
++ // + val: Value(Scalar(0x00000015))
+ // mir::Constant
-+ // + span: $DIR/instrument_coverage.rs:18:18: 18:18
-+ // + literal: Const { ty: u32, val: Value(Scalar(0x00000211)) }
++ // + span: /the/src/instrument_coverage.rs:19:18: 19:18
++ // + literal: Const { ty: u32, val: Value(Scalar(0x00000015)) }
+ // ty::Const
+ // + ty: u32
-+ // + val: Value(Scalar(0x0000021d))
++ // + val: Value(Scalar(0x00000002))
+ // mir::Constant
-+ // + span: $DIR/instrument_coverage.rs:18:18: 18:18
-+ // + literal: Const { ty: u32, val: Value(Scalar(0x0000021d)) }
++ // + span: /the/src/instrument_coverage.rs:19:18: 19:18
++ // + literal: Const { ty: u32, val: Value(Scalar(0x00000002)) }
+ }
+
+ bb1 (cleanup): {
-+ resume; // scope 0 at $DIR/instrument_coverage.rs:18:1: 20:2
++ resume; // scope 0 at /the/src/instrument_coverage.rs:19:1: 21:2
+ }
+
+ bb2: {
-+ StorageDead(_1); // scope 0 at $DIR/instrument_coverage.rs:19:5: 19:9
- _0 = const true; // scope 0 at $DIR/instrument_coverage.rs:19:5: 19:9
++ StorageDead(_1); // scope 0 at /the/src/instrument_coverage.rs:20:5: 20:9
+ _0 = const true; // scope 0 at /the/src/instrument_coverage.rs:20:5: 20:9
// ty::Const
// + ty: bool
// + val: Value(Scalar(0x01))
// mir::Constant
- // + span: $DIR/instrument_coverage.rs:19:5: 19:9
+ // + span: /the/src/instrument_coverage.rs:20:5: 20:9
// + literal: Const { ty: bool, val: Value(Scalar(0x01)) }
- return; // scope 0 at $DIR/instrument_coverage.rs:20:2: 20:2
+ return; // scope 0 at /the/src/instrument_coverage.rs:21:2: 21:2
}
}
+ // MIR for `main` after InstrumentCoverage
fn main() -> () {
- let mut _0: (); // return place in scope 0 at $DIR/instrument_coverage.rs:9:11: 9:11
- let mut _1: (); // in scope 0 at $DIR/instrument_coverage.rs:9:1: 15:2
- let mut _2: bool; // in scope 0 at $DIR/instrument_coverage.rs:11:12: 11:17
- let mut _3: !; // in scope 0 at $DIR/instrument_coverage.rs:11:18: 13:10
-+ let mut _4: (); // in scope 0 at $DIR/instrument_coverage.rs:9:11: 9:11
+ let mut _0: (); // return place in scope 0 at /the/src/instrument_coverage.rs:10:11: 10:11
+ let mut _1: (); // in scope 0 at /the/src/instrument_coverage.rs:10:1: 16:2
+ let mut _2: bool; // in scope 0 at /the/src/instrument_coverage.rs:12:12: 12:17
+ let mut _3: !; // in scope 0 at /the/src/instrument_coverage.rs:12:18: 14:10
++ let mut _4: (); // in scope 0 at /the/src/instrument_coverage.rs:10:11: 10:11
bb0: {
-- falseUnwind -> [real: bb1, cleanup: bb2]; // scope 0 at $DIR/instrument_coverage.rs:10:5: 14:6
-+ StorageLive(_4); // scope 0 at $DIR/instrument_coverage.rs:9:11: 9:11
-+ _4 = const std::intrinsics::count_code_region(const 16004455475339839479_u64, const 0_u32, const 425_u32, const 493_u32) -> bb7; // scope 0 at $DIR/instrument_coverage.rs:9:11: 9:11
+- falseUnwind -> [real: bb1, cleanup: bb2]; // scope 0 at /the/src/instrument_coverage.rs:11:5: 15:6
++ StorageLive(_4); // scope 0 at /the/src/instrument_coverage.rs:10:11: 10:11
++ _4 = const std::intrinsics::count_code_region(const 16004455475339839479_u64, const 0_u32, const "/the/src/instrument_coverage.rs", const 10_u32, const 11_u32, const 16_u32, const 2_u32) -> bb7; // scope 0 at /the/src/instrument_coverage.rs:10:11: 10:11
+ // ty::Const
-+ // + ty: unsafe extern "rust-intrinsic" fn(u64, u32, u32, u32) {std::intrinsics::count_code_region}
++ // + ty: unsafe extern "rust-intrinsic" fn(u64, u32, &'static str, u32, u32, u32, u32) {std::intrinsics::count_code_region}
+ // + val: Value(Scalar(<ZST>))
+ // mir::Constant
-+ // + span: $DIR/instrument_coverage.rs:9:11: 9:11
-+ // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(u64, u32, u32, u32) {std::intrinsics::count_code_region}, val: Value(Scalar(<ZST>)) }
++ // + span: /the/src/instrument_coverage.rs:10:11: 10:11
++ // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(u64, u32, &'static str, u32, u32, u32, u32) {std::intrinsics::count_code_region}, val: Value(Scalar(<ZST>)) }
+ // ty::Const
+ // + ty: u64
+ // + val: Value(Scalar(0xde1b3f75a72fc7f7))
+ // mir::Constant
-+ // + span: $DIR/instrument_coverage.rs:9:11: 9:11
++ // + span: /the/src/instrument_coverage.rs:10:11: 10:11
+ // + literal: Const { ty: u64, val: Value(Scalar(0xde1b3f75a72fc7f7)) }
+ // ty::Const
+ // + ty: u32
+ // + val: Value(Scalar(0x00000000))
+ // mir::Constant
-+ // + span: $DIR/instrument_coverage.rs:9:11: 9:11
++ // + span: /the/src/instrument_coverage.rs:10:11: 10:11
+ // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) }
+ // ty::Const
++ // + ty: &str
++ // + val: Value(Slice { data: Allocation { bytes: [47, 116, 104, 101, 47, 115, 114, 99, 47, 105, 110, 115, 116, 114, 117, 109, 101, 110, 116, 95, 99, 111, 118, 101, 114, 97, 103, 101, 46, 114, 115], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [2147483647], len: Size { raw: 31 } }, size: Size { raw: 31 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 31 })
++ // mir::Constant
++ // + span: /the/src/instrument_coverage.rs:10:11: 10:11
++ // + literal: Const { ty: &str, val: Value(Slice { data: Allocation { bytes: [47, 116, 104, 101, 47, 115, 114, 99, 47, 105, 110, 115, 116, 114, 117, 109, 101, 110, 116, 95, 99, 111, 118, 101, 114, 97, 103, 101, 46, 114, 115], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [2147483647], len: Size { raw: 31 } }, size: Size { raw: 31 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 31 }) }
++ // ty::Const
++ // + ty: u32
++ // + val: Value(Scalar(0x0000000a))
++ // mir::Constant
++ // + span: /the/src/instrument_coverage.rs:10:11: 10:11
++ // + literal: Const { ty: u32, val: Value(Scalar(0x0000000a)) }
++ // ty::Const
++ // + ty: u32
++ // + val: Value(Scalar(0x0000000b))
++ // mir::Constant
++ // + span: /the/src/instrument_coverage.rs:10:11: 10:11
++ // + literal: Const { ty: u32, val: Value(Scalar(0x0000000b)) }
++ // ty::Const
+ // + ty: u32
-+ // + val: Value(Scalar(0x000001a9))
++ // + val: Value(Scalar(0x00000010))
+ // mir::Constant
-+ // + span: $DIR/instrument_coverage.rs:9:11: 9:11
-+ // + literal: Const { ty: u32, val: Value(Scalar(0x000001a9)) }
++ // + span: /the/src/instrument_coverage.rs:10:11: 10:11
++ // + literal: Const { ty: u32, val: Value(Scalar(0x00000010)) }
+ // ty::Const
+ // + ty: u32
-+ // + val: Value(Scalar(0x000001ed))
++ // + val: Value(Scalar(0x00000002))
+ // mir::Constant
-+ // + span: $DIR/instrument_coverage.rs:9:11: 9:11
-+ // + literal: Const { ty: u32, val: Value(Scalar(0x000001ed)) }
++ // + span: /the/src/instrument_coverage.rs:10:11: 10:11
++ // + literal: Const { ty: u32, val: Value(Scalar(0x00000002)) }
}
bb1: {
- StorageLive(_2); // scope 0 at $DIR/instrument_coverage.rs:11:12: 11:17
- _2 = const bar() -> [return: bb3, unwind: bb2]; // scope 0 at $DIR/instrument_coverage.rs:11:12: 11:17
+ StorageLive(_2); // scope 0 at /the/src/instrument_coverage.rs:12:12: 12:17
+ _2 = const bar() -> [return: bb3, unwind: bb2]; // scope 0 at /the/src/instrument_coverage.rs:12:12: 12:17
// ty::Const
// + ty: fn() -> bool {bar}
// + val: Value(Scalar(<ZST>))
// mir::Constant
- // + span: $DIR/instrument_coverage.rs:11:12: 11:15
+ // + span: /the/src/instrument_coverage.rs:12:12: 12:15
// + literal: Const { ty: fn() -> bool {bar}, val: Value(Scalar(<ZST>)) }
}
bb2 (cleanup): {
- resume; // scope 0 at $DIR/instrument_coverage.rs:9:1: 15:2
+ resume; // scope 0 at /the/src/instrument_coverage.rs:10:1: 16:2
}
bb3: {
- FakeRead(ForMatchedPlace, _2); // scope 0 at $DIR/instrument_coverage.rs:11:12: 11:17
- switchInt(_2) -> [false: bb5, otherwise: bb4]; // scope 0 at $DIR/instrument_coverage.rs:11:9: 13:10
+ FakeRead(ForMatchedPlace, _2); // scope 0 at /the/src/instrument_coverage.rs:12:12: 12:17
+ switchInt(_2) -> [false: bb5, otherwise: bb4]; // scope 0 at /the/src/instrument_coverage.rs:12:9: 14:10
}
bb4: {
- falseEdge -> [real: bb6, imaginary: bb5]; // scope 0 at $DIR/instrument_coverage.rs:11:9: 13:10
+ falseEdge -> [real: bb6, imaginary: bb5]; // scope 0 at /the/src/instrument_coverage.rs:12:9: 14:10
}
bb5: {
- _1 = const (); // scope 0 at $DIR/instrument_coverage.rs:11:9: 13:10
+ _1 = const (); // scope 0 at /the/src/instrument_coverage.rs:12:9: 14:10
// ty::Const
// + ty: ()
// + val: Value(Scalar(<ZST>))
// mir::Constant
- // + span: $DIR/instrument_coverage.rs:11:9: 13:10
+ // + span: /the/src/instrument_coverage.rs:12:9: 14:10
// + literal: Const { ty: (), val: Value(Scalar(<ZST>)) }
- StorageDead(_2); // scope 0 at $DIR/instrument_coverage.rs:14:5: 14:6
- goto -> bb0; // scope 0 at $DIR/instrument_coverage.rs:10:5: 14:6
+ StorageDead(_2); // scope 0 at /the/src/instrument_coverage.rs:15:5: 15:6
+ goto -> bb0; // scope 0 at /the/src/instrument_coverage.rs:11:5: 15:6
}
bb6: {
- _0 = const (); // scope 0 at $DIR/instrument_coverage.rs:12:13: 12:18
+ _0 = const (); // scope 0 at /the/src/instrument_coverage.rs:13:13: 13:18
// ty::Const
// + ty: ()
// + val: Value(Scalar(<ZST>))
// mir::Constant
- // + span: $DIR/instrument_coverage.rs:12:13: 12:18
+ // + span: /the/src/instrument_coverage.rs:13:13: 13:18
// + literal: Const { ty: (), val: Value(Scalar(<ZST>)) }
- StorageDead(_2); // scope 0 at $DIR/instrument_coverage.rs:14:5: 14:6
- return; // scope 0 at $DIR/instrument_coverage.rs:15:2: 15:2
+ StorageDead(_2); // scope 0 at /the/src/instrument_coverage.rs:15:5: 15:6
+ return; // scope 0 at /the/src/instrument_coverage.rs:16:2: 16:2
+ }
+
+ bb7: {
-+ StorageDead(_4); // scope 0 at $DIR/instrument_coverage.rs:10:5: 14:6
-+ falseUnwind -> [real: bb1, cleanup: bb2]; // scope 0 at $DIR/instrument_coverage.rs:10:5: 14:6
++ StorageDead(_4); // scope 0 at /the/src/instrument_coverage.rs:11:5: 15:6
++ falseUnwind -> [real: bb1, cleanup: bb2]; // scope 0 at /the/src/instrument_coverage.rs:11:5: 15:6
}
}
// intrinsics, during codegen.
// needs-profiler-support
-// compile-flags: -Zinstrument-coverage
+// ignore-windows
+// compile-flags: -Zinstrument-coverage --remap-path-prefix={{src-base}}=/the/src
// EMIT_MIR instrument_coverage.main.InstrumentCoverage.diff
// EMIT_MIR instrument_coverage.bar.InstrumentCoverage.diff
fn main() {
fn bar() -> bool {
true
}
+
+// Note that the MIR with injected coverage intrinsics includes references to source locations,
+// including the source file absolute path. Typically, MIR pretty print output with file
+// references are safe because the file prefixes are substituted with `$DIR`, but in this case
+// the file references are encoded as function arguments, with an `Operand` type representation
+// (`Slice` `Allocation` interned byte array) that cannot be normalized by simple substitution.
+//
+// The first workaround is to use the `SourceMap`-supported `--remap-path-prefix` option; however,
+// the implementation of the `--remap-path-prefix` option currently joins the new prefix and the
+// remaining source path with an OS-specific path separator (`\` on Windows). This difference still
+// shows up in the byte array representation of the path, causing Windows tests to fail to match
+// blessed results baselined with a `/` path separator.
+//
+// Since this `mir-opt` test does not have any significant platform dependencies, other than the
+// path separator differences, the final workaround is to disable testing on Windows.
--- /dev/null
+#[macro_export]
+macro_rules! attrs_on_struct {
+ ( $( #[$attr:meta] )* ) => {
+ $( #[$attr] )*
+ pub struct ExpandedStruct;
+ }
+}
+// edition:2018
+// aux-build:extern_macros.rs
// compile-flags:--test --test-args=--test-threads=1
// normalize-stdout-test: "src/test/rustdoc-ui" -> "$$DIR"
// check-pass
//! assert_eq!(1 + 1, 2);
//! ```
+extern crate extern_macros as macros;
+
+use macros::attrs_on_struct;
+
pub mod foo {
/// ```
/// ```
pub fn bar() {}
}
+
+attrs_on_struct! {
+ /// ```
+ /// assert!(true);
+ /// ```
+}
-running 2 tests
-test $DIR/doctest-output.rs - (line 5) ... ok
-test $DIR/doctest-output.rs - foo::bar (line 11) ... ok
+running 3 tests
+test $DIR/doctest-output.rs - (line 7) ... ok
+test $DIR/doctest-output.rs - ExpandedStruct (line 23) ... ok
+test $DIR/doctest-output.rs - foo::bar (line 17) ... ok
-test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
+test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
--- /dev/null
+#![deny(broken_intra_doc_links)]
+//~^ NOTE lint level is defined
+pub enum S {}
+
+macro_rules! m {
+ () => {};
+}
+
+static s: usize = 0;
+const c: usize = 0;
+
+trait T {}
+
+/// Link to [struct@S]
+//~^ ERROR incompatible link kind for `S`
+//~| NOTE this link resolved
+//~| HELP use its disambiguator
+
+/// Link to [mod@S]
+//~^ ERROR incompatible link kind for `S`
+//~| NOTE this link resolved
+//~| HELP use its disambiguator
+
+/// Link to [union@S]
+//~^ ERROR incompatible link kind for `S`
+//~| NOTE this link resolved
+//~| HELP use its disambiguator
+
+/// Link to [trait@S]
+//~^ ERROR incompatible link kind for `S`
+//~| NOTE this link resolved
+//~| HELP use its disambiguator
+
+/// Link to [struct@T]
+//~^ ERROR incompatible link kind for `T`
+//~| NOTE this link resolved
+//~| HELP use its disambiguator
+
+/// Link to [derive@m]
+//~^ ERROR incompatible link kind for `m`
+//~| NOTE this link resolved
+//~| HELP use its disambiguator
+
+/// Link to [const@s]
+//~^ ERROR incompatible link kind for `s`
+//~| NOTE this link resolved
+//~| HELP use its disambiguator
+
+/// Link to [static@c]
+//~^ ERROR incompatible link kind for `c`
+//~| NOTE this link resolved
+//~| HELP use its disambiguator
+
+/// Link to [fn@c]
+//~^ ERROR incompatible link kind for `c`
+//~| NOTE this link resolved
+//~| HELP use its disambiguator
+
+/// Link to [c()]
+//~^ ERROR incompatible link kind for `c`
+//~| NOTE this link resolved
+//~| HELP use its disambiguator
+
+/// Link to [const@f]
+//~^ ERROR incompatible link kind for `f`
+//~| NOTE this link resolved
+//~| HELP use its disambiguator
+pub fn f() {}
--- /dev/null
+error: incompatible link kind for `S`
+ --> $DIR/intra-links-disambiguator-mismatch.rs:14:14
+ |
+LL | /// Link to [struct@S]
+ | ^^^^^^^^ help: to link to the enum, use its disambiguator: `enum@S`
+ |
+note: the lint level is defined here
+ --> $DIR/intra-links-disambiguator-mismatch.rs:1:9
+ |
+LL | #![deny(broken_intra_doc_links)]
+ | ^^^^^^^^^^^^^^^^^^^^^^
+ = note: this link resolved to an enum, which is not a struct
+
+error: incompatible link kind for `S`
+ --> $DIR/intra-links-disambiguator-mismatch.rs:19:14
+ |
+LL | /// Link to [mod@S]
+ | ^^^^^ help: to link to the enum, use its disambiguator: `enum@S`
+ |
+ = note: this link resolved to an enum, which is not a module
+
+error: incompatible link kind for `S`
+ --> $DIR/intra-links-disambiguator-mismatch.rs:24:14
+ |
+LL | /// Link to [union@S]
+ | ^^^^^^^ help: to link to the enum, use its disambiguator: `enum@S`
+ |
+ = note: this link resolved to an enum, which is not a union
+
+error: incompatible link kind for `S`
+ --> $DIR/intra-links-disambiguator-mismatch.rs:29:14
+ |
+LL | /// Link to [trait@S]
+ | ^^^^^^^ help: to link to the enum, use its disambiguator: `enum@S`
+ |
+ = note: this link resolved to an enum, which is not a trait
+
+error: incompatible link kind for `T`
+ --> $DIR/intra-links-disambiguator-mismatch.rs:34:14
+ |
+LL | /// Link to [struct@T]
+ | ^^^^^^^^ help: to link to the trait, use its disambiguator: `trait@T`
+ |
+ = note: this link resolved to a trait, which is not a struct
+
+error: incompatible link kind for `m`
+ --> $DIR/intra-links-disambiguator-mismatch.rs:39:14
+ |
+LL | /// Link to [derive@m]
+ | ^^^^^^^^ help: to link to the macro, use its disambiguator: `m!`
+ |
+ = note: this link resolved to a macro, which is not a derive macro
+
+error: incompatible link kind for `s`
+ --> $DIR/intra-links-disambiguator-mismatch.rs:44:14
+ |
+LL | /// Link to [const@s]
+ | ^^^^^^^ help: to link to the static, use its disambiguator: `static@s`
+ |
+ = note: this link resolved to a static, which is not a constant
+
+error: incompatible link kind for `c`
+ --> $DIR/intra-links-disambiguator-mismatch.rs:49:14
+ |
+LL | /// Link to [static@c]
+ | ^^^^^^^^ help: to link to the constant, use its disambiguator: `const@c`
+ |
+ = note: this link resolved to a constant, which is not a static
+
+error: incompatible link kind for `c`
+ --> $DIR/intra-links-disambiguator-mismatch.rs:54:14
+ |
+LL | /// Link to [fn@c]
+ | ^^^^ help: to link to the constant, use its disambiguator: `const@c`
+ |
+ = note: this link resolved to a constant, which is not a function
+
+error: incompatible link kind for `c`
+ --> $DIR/intra-links-disambiguator-mismatch.rs:59:14
+ |
+LL | /// Link to [c()]
+ | ^^^ help: to link to the constant, use its disambiguator: `const@c`
+ |
+ = note: this link resolved to a constant, which is not a function
+
+error: incompatible link kind for `f`
+ --> $DIR/intra-links-disambiguator-mismatch.rs:64:14
+ |
+LL | /// Link to [const@f]
+ | ^^^^^^^ help: to link to the function, use its disambiguator: `f()`
+ |
+ = note: this link resolved to a function, which is not a constant
+
+error: aborting due to 11 previous errors
+
--- /dev/null
+#![crate_name = "bar"]
+
+pub struct Ref<'a>(&'a u32);
+
+pub fn test5(a: &u32) -> Ref {
+ Ref(a)
+}
+
+pub fn test6(a: &u32) -> Ref<'_> {
+ Ref(a)
+}
--- /dev/null
+// aux-build:elided-lifetime.rs
+//
+// rust-lang/rust#75225
+//
+// Since Rust 2018 we encourage writing out <'_> explicitly to make it clear
+// that borrowing is occuring. Make sure rustdoc is following the same idiom.
+
+#![crate_name = "foo"]
+
+pub struct Ref<'a>(&'a u32);
+type ARef<'a> = Ref<'a>;
+
+// @has foo/fn.test1.html
+// @matches - "Ref</a><'_>"
+pub fn test1(a: &u32) -> Ref {
+ Ref(a)
+}
+
+// @has foo/fn.test2.html
+// @matches - "Ref</a><'_>"
+pub fn test2(a: &u32) -> Ref<'_> {
+ Ref(a)
+}
+
+// @has foo/fn.test3.html
+// @matches - "Ref</a><'_>"
+pub fn test3(a: &u32) -> ARef {
+ Ref(a)
+}
+
+// @has foo/fn.test4.html
+// @matches - "Ref</a><'_>"
+pub fn test4(a: &u32) -> ARef<'_> {
+ Ref(a)
+}
+
+// Ensure external paths in inlined docs also display elided lifetime
+// @has foo/bar/fn.test5.html
+// @matches - "Ref</a><'_>"
+// @has foo/bar/fn.test6.html
+// @matches - "Ref</a><'_>"
+#[doc(inline)]
+pub extern crate bar;
--- /dev/null
+// ignore-tidy-linelength
+#![deny(broken_intra_doc_links)]
+
+/// Link to [S::assoc_fn()]
+/// Link to [Default::default()]
+// @has intra_link_trait_item/struct.S.html '//*[@href="../intra_link_trait_item/struct.S.html#method.assoc_fn"]' 'S::assoc_fn()'
+// @has - '//*[@href="https://doc.rust-lang.org/nightly/core/default/trait.Default.html#tymethod.default"]' 'Default::default()'
+pub struct S;
+
+impl S {
+ pub fn assoc_fn() {}
+}
impl LateLintPass<'_> for $struct {
fn check_crate(&mut self, cx: &LateContext, krate: &rustc_hir::Crate) {
$(
- if !attr::contains_name(&krate.item.attrs, $attr) {
+ if !cx.sess().contains_name(&krate.item.attrs, $attr) {
cx.lint(CRATE_NOT_OKAY, |lint| {
let msg = format!("crate is not marked with #![{}]", $attr);
lint.build(&msg).set_span(krate.item.span).emit()
impl<'tcx> LateLintPass<'tcx> for Pass {
fn check_crate(&mut self, cx: &LateContext, krate: &rustc_hir::Crate) {
- if !attr::contains_name(&krate.item.attrs, Symbol::intern("crate_okay")) {
+ if !cx.sess().contains_name(&krate.item.attrs, Symbol::intern("crate_okay")) {
cx.lint(CRATE_NOT_OKAY, |lint| {
lint.build("crate is not marked with #![crate_okay]")
.set_span(krate.item.span)
mod gravy;
pub fn main() {
- rustc_ast::with_default_session_globals(|| parse());
+ rustc_span::with_default_session_globals(|| parse());
assert_eq!(gravy::foo(), 10);
}
}
fn main() {
- rustc_ast::with_default_session_globals(|| run());
+ rustc_span::with_default_session_globals(|| run());
}
fn run() {
// no-prefer-dynamic
#![feature(allocator_api)]
+#![feature(slice_ptr_get)]
extern crate helper;
-use std::alloc::{self, AllocInit, AllocRef, Global, Layout, System};
+use std::alloc::{self, AllocRef, Global, Layout, System};
use std::sync::atomic::{AtomicUsize, Ordering};
static HITS: AtomicUsize = AtomicUsize::new(0);
unsafe {
let layout = Layout::from_size_align(4, 2).unwrap();
- let memory = Global.alloc(layout.clone(), AllocInit::Uninitialized).unwrap();
- helper::work_with(&memory.ptr);
+ let memory = Global.alloc(layout.clone()).unwrap();
+ helper::work_with(&memory);
assert_eq!(HITS.load(Ordering::SeqCst), n + 1);
- Global.dealloc(memory.ptr, layout);
+ Global.dealloc(memory.as_non_null_ptr(), layout);
assert_eq!(HITS.load(Ordering::SeqCst), n + 2);
let s = String::with_capacity(10);
drop(s);
assert_eq!(HITS.load(Ordering::SeqCst), n + 4);
- let memory = System.alloc(layout.clone(), AllocInit::Uninitialized).unwrap();
+ let memory = System.alloc(layout.clone()).unwrap();
assert_eq!(HITS.load(Ordering::SeqCst), n + 4);
- helper::work_with(&memory.ptr);
- System.dealloc(memory.ptr, layout);
+ helper::work_with(&memory);
+ System.dealloc(memory.as_non_null_ptr(), layout);
assert_eq!(HITS.load(Ordering::SeqCst), n + 4);
}
}
// no-prefer-dynamic
#![feature(allocator_api)]
+#![feature(slice_ptr_get)]
extern crate custom;
extern crate helper;
-use std::alloc::{AllocInit, AllocRef, Global, Layout, System};
+use std::alloc::{AllocRef, Global, Layout, System};
use std::sync::atomic::{AtomicUsize, Ordering};
#[global_allocator]
let n = GLOBAL.0.load(Ordering::SeqCst);
let layout = Layout::from_size_align(4, 2).unwrap();
- let memory = Global.alloc(layout.clone(), AllocInit::Uninitialized).unwrap();
- helper::work_with(&memory.ptr);
+ let memory = Global.alloc(layout.clone()).unwrap();
+ helper::work_with(&memory);
assert_eq!(GLOBAL.0.load(Ordering::SeqCst), n + 1);
- Global.dealloc(memory.ptr, layout);
+ Global.dealloc(memory.as_non_null_ptr(), layout);
assert_eq!(GLOBAL.0.load(Ordering::SeqCst), n + 2);
- let memory = System.alloc(layout.clone(), AllocInit::Uninitialized).unwrap();
+ let memory = System.alloc(layout.clone()).unwrap();
assert_eq!(GLOBAL.0.load(Ordering::SeqCst), n + 2);
- helper::work_with(&memory.ptr);
- System.dealloc(memory.ptr, layout);
+ helper::work_with(&memory);
+ System.dealloc(memory.as_non_null_ptr(), layout);
assert_eq!(GLOBAL.0.load(Ordering::SeqCst), n + 2);
}
}
--- /dev/null
+// compile-flags: --target wasm32-unknown-unknown
+// needs-llvm-components: webassembly
+
+#![feature(no_core, lang_items, rustc_attrs)]
+#![no_core]
+
+#[rustc_builtin_macro]
+macro_rules! asm {
+ () => {};
+}
+#[lang = "sized"]
+trait Sized {}
+
+fn main() {
+ unsafe {
+ asm!("");
+ //~^ ERROR asm! is unsupported on this target
+ }
+}
--- /dev/null
+error[E0472]: asm! is unsupported on this target
+ --> $DIR/bad-arch.rs:16:9
+ |
+LL | asm!("");
+ | ^^^^^^^^^
+
+error: aborting due to previous error
+
#[start]
pub async fn start(_: isize, _: *const *const u8) -> isize {
-//~^ ERROR start is not allowed to be `async`
+//~^ ERROR `start` is not allowed to be `async`
0
}
-error[E0752]: start is not allowed to be `async`
+error[E0752]: `start` is not allowed to be `async`
--> $DIR/issue-68523-start.rs:6:1
|
LL | pub async fn start(_: isize, _: *const *const u8) -> isize {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ start is not allowed to be `async`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `start` is not allowed to be `async`
error: aborting due to previous error
--- /dev/null
+// check-pass
+
+use std::collections::{BTreeMap, HashMap};
+
+trait Map
+where
+ for<'a> &'a Self: IntoIterator<Item = (&'a Self::Key, &'a Self::Value)>,
+{
+ type Key;
+ type Value;
+}
+
+impl<K, V> Map for HashMap<K, V> {
+ type Key = K;
+ type Value = V;
+}
+
+impl<K, V> Map for BTreeMap<K, V> {
+ type Key = K;
+ type Value = V;
+}
+
+fn main() {}
LL | trait Trait<const T: ()> {}
| ^
|
- = note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information
- = help: add `#![feature(const_generics)]` to the crate attributes to enable
+ = note: see issue #74878 <https://github.com/rust-lang/rust/issues/74878> for more information
+ = help: add `#![feature(min_const_generics)]` to the crate attributes to enable
error: aborting due to previous error
LL | struct B<T, const N: T>(PhantomData<[T; N]>);
| ^
|
- = note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information
- = help: add `#![feature(const_generics)]` to the crate attributes to enable
+ = note: see issue #74878 <https://github.com/rust-lang/rust/issues/74878> for more information
+ = help: add `#![feature(min_const_generics)]` to the crate attributes to enable
error: aborting due to 2 previous errors
--- /dev/null
+error: type parameters with a default must be trailing
+ --> $DIR/wrong-order.rs:5:10
+ |
+LL | struct A<T = u32, const N: usize> {
+ | ^
+ |
+ = note: using type defaults and const parameters in the same parameter list is currently not permitted
+
+warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes
+ --> $DIR/wrong-order.rs:2:27
+ |
+LL | #![cfg_attr(full, feature(const_generics))]
+ | ^^^^^^^^^^^^^^
+ |
+ = note: `#[warn(incomplete_features)]` on by default
+ = note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information
+
+error: aborting due to previous error; 1 warning emitted
+
--- /dev/null
+error: type parameters with a default must be trailing
+ --> $DIR/wrong-order.rs:5:10
+ |
+LL | struct A<T = u32, const N: usize> {
+ | ^
+ |
+ = note: using type defaults and const parameters in the same parameter list is currently not permitted
+
+error: aborting due to previous error
+
-#![feature(const_generics)] //~ WARN the feature `const_generics` is incomplete
+// revisions: full min
+#![cfg_attr(full, feature(const_generics))] //[full]~WARN the feature `const_generics` is incomplete
+#![cfg_attr(min, feature(min_const_generics))]
struct A<T = u32, const N: usize> {
//~^ ERROR type parameters with a default must be trailing
+++ /dev/null
-error: type parameters with a default must be trailing
- --> $DIR/wrong-order.rs:3:10
- |
-LL | struct A<T = u32, const N: usize> {
- | ^
- |
- = note: using type defaults and const parameters in the same parameter list is currently not permitted
-
-warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes
- --> $DIR/wrong-order.rs:1:12
- |
-LL | #![feature(const_generics)]
- | ^^^^^^^^^^^^^^
- |
- = note: `#[warn(incomplete_features)]` on by default
- = note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information
-
-error: aborting due to previous error; 1 warning emitted
-
--- /dev/null
+warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes
+ --> $DIR/issue-56445.rs:3:27
+ |
+LL | #![cfg_attr(full, feature(const_generics))]
+ | ^^^^^^^^^^^^^^
+ |
+ = note: `#[warn(incomplete_features)]` on by default
+ = note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information
+
+error[E0771]: use of non-static lifetime `'a` in const generic
+ --> $DIR/issue-56445.rs:9:26
+ |
+LL | struct Bug<'a, const S: &'a str>(PhantomData<&'a ()>);
+ | ^^
+ |
+ = note: for more information, see issue #74052 <https://github.com/rust-lang/rust/issues/74052>
+
+error: aborting due to previous error; 1 warning emitted
+
+For more information about this error, try `rustc --explain E0771`.
--- /dev/null
+error[E0771]: use of non-static lifetime `'a` in const generic
+ --> $DIR/issue-56445.rs:9:26
+ |
+LL | struct Bug<'a, const S: &'a str>(PhantomData<&'a ()>);
+ | ^^
+ |
+ = note: for more information, see issue #74052 <https://github.com/rust-lang/rust/issues/74052>
+
+error: using `&'static str` as const generic parameters is forbidden
+ --> $DIR/issue-56445.rs:9:25
+ |
+LL | struct Bug<'a, const S: &'a str>(PhantomData<&'a ()>);
+ | ^^^^^^^
+ |
+ = note: the only supported types are integers, `bool` and `char`
+ = note: more complex types are supported with `#[feature(const_generics)]`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0771`.
// Regression test for https://github.com/rust-lang/rust/issues/56445#issuecomment-518402995.
-
-#![feature(const_generics)]
-//~^ WARN: the feature `const_generics` is incomplete
+// revisions: full min
+#![cfg_attr(full, feature(const_generics))] //[full]~WARN the feature `const_generics` is incomplete
+#![cfg_attr(min, feature(min_const_generics))]
#![crate_type = "lib"]
use std::marker::PhantomData;
struct Bug<'a, const S: &'a str>(PhantomData<&'a ()>);
//~^ ERROR: use of non-static lifetime `'a` in const generic
+//[min]~| ERROR: using `&'static str` as const
impl Bug<'_, ""> {}
+++ /dev/null
-warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes
- --> $DIR/issue-56445.rs:3:12
- |
-LL | #![feature(const_generics)]
- | ^^^^^^^^^^^^^^
- |
- = note: `#[warn(incomplete_features)]` on by default
- = note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information
-
-error[E0771]: use of non-static lifetime `'a` in const generic
- --> $DIR/issue-56445.rs:9:26
- |
-LL | struct Bug<'a, const S: &'a str>(PhantomData<&'a ()>);
- | ^^
- |
- = note: for more information, see issue #74052 <https://github.com/rust-lang/rust/issues/74052>
-
-error: aborting due to previous error; 1 warning emitted
-
-For more information about this error, try `rustc --explain E0771`.
LL | struct B<const I: u8>;
| ^
|
- = note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information
- = help: add `#![feature(const_generics)]` to the crate attributes to enable
+ = note: see issue #74878 <https://github.com/rust-lang/rust/issues/74878> for more information
+ = help: add `#![feature(min_const_generics)]` to the crate attributes to enable
error: aborting due to previous error
--- /dev/null
+warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes
+ --> $DIR/issue-60818-struct-constructors.rs:3:27
+ |
+LL | #![cfg_attr(full, feature(const_generics))]
+ | ^^^^^^^^^^^^^^
+ |
+ = note: `#[warn(incomplete_features)]` on by default
+ = note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information
+
+warning: 1 warning emitted
+
// check-pass
-
-#![feature(const_generics)]
-//~^ WARN the feature `const_generics` is incomplete
+// revisions: full min
+#![cfg_attr(full, feature(const_generics))] //[full]~WARN the feature `const_generics` is incomplete
+#![cfg_attr(min, feature(min_const_generics))]
struct Generic<const V: usize>;
+++ /dev/null
-warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes
- --> $DIR/issue-60818-struct-constructors.rs:3:12
- |
-LL | #![feature(const_generics)]
- | ^^^^^^^^^^^^^^
- |
- = note: `#[warn(incomplete_features)]` on by default
- = note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information
-
-warning: 1 warning emitted
-
--- /dev/null
+warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes
+ --> $DIR/issue-61336-1.rs:3:27
+ |
+LL | #![cfg_attr(full, feature(const_generics))]
+ | ^^^^^^^^^^^^^^
+ |
+ = note: `#[warn(incomplete_features)]` on by default
+ = note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information
+
+warning: 1 warning emitted
+
-#![feature(const_generics)]
-//~^ WARN the feature `const_generics` is incomplete
-
// build-pass
+// revisions: full min
+#![cfg_attr(full, feature(const_generics))] //[full]~WARN the feature `const_generics` is incomplete
+#![cfg_attr(min, feature(min_const_generics))]
fn f<T: Copy, const N: usize>(x: T) -> [T; N] {
[x; N]
+++ /dev/null
-warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes
- --> $DIR/issue-61336-1.rs:1:12
- |
-LL | #![feature(const_generics)]
- | ^^^^^^^^^^^^^^
- |
- = note: `#[warn(incomplete_features)]` on by default
- = note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information
-
-warning: 1 warning emitted
-
--- /dev/null
+warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes
+ --> $DIR/issue-61336-2.rs:2:27
+ |
+LL | #![cfg_attr(full, feature(const_generics))]
+ | ^^^^^^^^^^^^^^
+ |
+ = note: `#[warn(incomplete_features)]` on by default
+ = note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information
+
+error[E0277]: the trait bound `T: std::marker::Copy` is not satisfied
+ --> $DIR/issue-61336-2.rs:10:5
+ |
+LL | [x; { N }]
+ | ^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `T`
+ |
+ = note: the `Copy` trait is required because the repeated element will be copied
+help: consider restricting type parameter `T`
+ |
+LL | fn g<T: std::marker::Copy, const N: usize>(x: T) -> [T; N] {
+ | ^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error; 1 warning emitted
+
+For more information about this error, try `rustc --explain E0277`.
--- /dev/null
+error[E0277]: the trait bound `T: std::marker::Copy` is not satisfied
+ --> $DIR/issue-61336-2.rs:10:5
+ |
+LL | [x; { N }]
+ | ^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `T`
+ |
+ = note: the `Copy` trait is required because the repeated element will be copied
+help: consider restricting type parameter `T`
+ |
+LL | fn g<T: std::marker::Copy, const N: usize>(x: T) -> [T; N] {
+ | ^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
-#![feature(const_generics)]
-//~^ WARN the feature `const_generics` is incomplete
+// revisions: full min
+#![cfg_attr(full, feature(const_generics))] //[full]~WARN the feature `const_generics` is incomplete
+#![cfg_attr(min, feature(min_const_generics))]
fn f<T: Copy, const N: usize>(x: T) -> [T; N] {
[x; { N }]
+++ /dev/null
-warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes
- --> $DIR/issue-61336-2.rs:1:12
- |
-LL | #![feature(const_generics)]
- | ^^^^^^^^^^^^^^
- |
- = note: `#[warn(incomplete_features)]` on by default
- = note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information
-
-error[E0277]: the trait bound `T: std::marker::Copy` is not satisfied
- --> $DIR/issue-61336-2.rs:9:5
- |
-LL | [x; { N }]
- | ^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `T`
- |
- = note: the `Copy` trait is required because the repeated element will be copied
-help: consider restricting type parameter `T`
- |
-LL | fn g<T: std::marker::Copy, const N: usize>(x: T) -> [T; N] {
- | ^^^^^^^^^^^^^^^^^^^
-
-error: aborting due to previous error; 1 warning emitted
-
-For more information about this error, try `rustc --explain E0277`.
--- /dev/null
+warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes
+ --> $DIR/issue-61336.rs:2:27
+ |
+LL | #![cfg_attr(full, feature(const_generics))]
+ | ^^^^^^^^^^^^^^
+ |
+ = note: `#[warn(incomplete_features)]` on by default
+ = note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information
+
+error[E0277]: the trait bound `T: std::marker::Copy` is not satisfied
+ --> $DIR/issue-61336.rs:10:5
+ |
+LL | [x; N]
+ | ^^^^^^ the trait `std::marker::Copy` is not implemented for `T`
+ |
+ = note: the `Copy` trait is required because the repeated element will be copied
+help: consider restricting type parameter `T`
+ |
+LL | fn g<T: std::marker::Copy, const N: usize>(x: T) -> [T; N] {
+ | ^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error; 1 warning emitted
+
+For more information about this error, try `rustc --explain E0277`.
--- /dev/null
+error[E0277]: the trait bound `T: std::marker::Copy` is not satisfied
+ --> $DIR/issue-61336.rs:10:5
+ |
+LL | [x; N]
+ | ^^^^^^ the trait `std::marker::Copy` is not implemented for `T`
+ |
+ = note: the `Copy` trait is required because the repeated element will be copied
+help: consider restricting type parameter `T`
+ |
+LL | fn g<T: std::marker::Copy, const N: usize>(x: T) -> [T; N] {
+ | ^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
-#![feature(const_generics)]
-//~^ WARN the feature `const_generics` is incomplete
+// revisions: full min
+#![cfg_attr(full, feature(const_generics))] //[full]~WARN the feature `const_generics` is incomplete
+#![cfg_attr(min, feature(min_const_generics))]
fn f<T: Copy, const N: usize>(x: T) -> [T; N] {
[x; N]
--- /dev/null
+warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes
+ --> $DIR/issue-61422.rs:3:27
+ |
+LL | #![cfg_attr(full, feature(const_generics))]
+ | ^^^^^^^^^^^^^^
+ |
+ = note: `#[warn(incomplete_features)]` on by default
+ = note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information
+
+warning: 1 warning emitted
+
// check-pass
-
-#![feature(const_generics)]
-//~^ WARN the feature `const_generics` is incomplete
+// revisions: full min
+#![cfg_attr(full, feature(const_generics))] //[full]~WARN the feature `const_generics` is incomplete
+#![cfg_attr(min, feature(min_const_generics))]
use std::mem;
+++ /dev/null
-warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes
- --> $DIR/issue-61422.rs:3:12
- |
-LL | #![feature(const_generics)]
- | ^^^^^^^^^^^^^^
- |
- = note: `#[warn(incomplete_features)]` on by default
- = note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information
-
-warning: 1 warning emitted
-
--- /dev/null
+warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes
+ --> $DIR/issue-61432.rs:3:27
+ |
+LL | #![cfg_attr(full, feature(const_generics))]
+ | ^^^^^^^^^^^^^^
+ |
+ = note: `#[warn(incomplete_features)]` on by default
+ = note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information
+
+warning: 1 warning emitted
+
// run-pass
-
-#![feature(const_generics)]
-//~^ WARN the feature `const_generics` is incomplete
+// revisions: full min
+#![cfg_attr(full, feature(const_generics))] //[full]~WARN the feature `const_generics` is incomplete
+#![cfg_attr(min, feature(min_const_generics))]
fn promote<const N: i32>() {
// works:
+++ /dev/null
-warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes
- --> $DIR/issue-61432.rs:3:12
- |
-LL | #![feature(const_generics)]
- | ^^^^^^^^^^^^^^
- |
- = note: `#[warn(incomplete_features)]` on by default
- = note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information
-
-warning: 1 warning emitted
-
--- /dev/null
+warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes
+ --> $DIR/issue-61747.rs:2:27
+ |
+LL | #![cfg_attr(full, feature(const_generics))]
+ | ^^^^^^^^^^^^^^
+ |
+ = note: `#[warn(incomplete_features)]` on by default
+ = note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information
+
+error: constant expression depends on a generic parameter
+ --> $DIR/issue-61747.rs:8:23
+ |
+LL | fn successor() -> Const<{C + 1}> {
+ | ^^^^^^^^^^^^^^
+ |
+ = note: this may fail depending on what value the parameter takes
+
+error: aborting due to previous error; 1 warning emitted
+
--- /dev/null
+error: generic parameters must not be used inside of non trivial constant values
+ --> $DIR/issue-61747.rs:8:30
+ |
+LL | fn successor() -> Const<{C + 1}> {
+ | ^ non-trivial anonymous constants must not depend on the parameter `C`
+ |
+ = help: it is currently only allowed to use either `C` or `{ C }` as generic constants
+
+error: aborting due to previous error
+
-#![feature(const_generics)]
-//~^ WARN the feature `const_generics` is incomplete
+// revisions: full min
+#![cfg_attr(full, feature(const_generics))] //[full]~WARN the feature `const_generics` is incomplete
+#![cfg_attr(min, feature(min_const_generics))]
struct Const<const N: usize>;
impl<const C: usize> Const<{C}> {
fn successor() -> Const<{C + 1}> {
- //~^ ERROR constant expression depends on a generic parameter
+ //[full]~^ ERROR constant expression depends on a generic parameter
+ //[min]~^^ ERROR generic parameters must not be used
Const
}
}
+++ /dev/null
-warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes
- --> $DIR/issue-61747.rs:1:12
- |
-LL | #![feature(const_generics)]
- | ^^^^^^^^^^^^^^
- |
- = note: `#[warn(incomplete_features)]` on by default
- = note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information
-
-error: constant expression depends on a generic parameter
- --> $DIR/issue-61747.rs:7:23
- |
-LL | fn successor() -> Const<{C + 1}> {
- | ^^^^^^^^^^^^^^
- |
- = note: this may fail depending on what value the parameter takes
-
-error: aborting due to previous error; 1 warning emitted
-
--- /dev/null
+#![feature(const_generics)]
+#![allow(incomplete_features)]
+
+trait Foo {
+ const VAL: usize;
+}
+
+trait MyTrait {}
+
+trait True {}
+struct Is<const T: bool>;
+impl True for Is<{true}> {}
+
+impl<T: Foo> MyTrait for T where Is<{T::VAL == 5}>: True {}
+//~^ ERROR constant expression depends on a generic parameter
+impl<T: Foo> MyTrait for T where Is<{T::VAL == 6}>: True {}
+//~^ ERROR constant expression depends on a generic parameter
+
+fn main() {}
--- /dev/null
+error: constant expression depends on a generic parameter
+ --> $DIR/issue-64494.rs:14:53
+ |
+LL | impl<T: Foo> MyTrait for T where Is<{T::VAL == 5}>: True {}
+ | ^^^^
+ |
+ = note: this may fail depending on what value the parameter takes
+
+error: constant expression depends on a generic parameter
+ --> $DIR/issue-64494.rs:16:53
+ |
+LL | impl<T: Foo> MyTrait for T where Is<{T::VAL == 6}>: True {}
+ | ^^^^
+ |
+ = note: this may fail depending on what value the parameter takes
+
+error: aborting due to 2 previous errors
+
--- /dev/null
+#![feature(const_generics)]
+#![allow(incomplete_features)]
+
+pub struct IsLessOrEqual<const LHS: u32, const RHS: u32>;
+pub struct Condition<const CONDITION: bool>;
+pub trait True {}
+
+impl<const LHS: u32, const RHS: u32> True for IsLessOrEqual<LHS, RHS> where
+ Condition<{ LHS <= RHS }>: True
+//~^ Error constant expression depends on a generic parameter
+{
+}
+impl True for Condition<true> {}
+
+struct S<const I: u32, const J: u32>;
+impl<const I: u32, const J: u32> S<I, J>
+where
+ IsLessOrEqual<I, 8>: True,
+ IsLessOrEqual<J, 8>: True,
+ IsLessOrEqual<{ 8 - I }, { 8 - J }>: True,
+//~^ Error constant expression depends on a generic parameter
+//~| Error constant expression depends on a generic parameter
+//~| Error constant expression depends on a generic parameter
+//~| Error constant expression depends on a generic parameter
+ // Condition<{ 8 - I <= 8 - J }>: True,
+{
+ fn print() {
+ println!("I {} J {}", I, J);
+ }
+}
+
+fn main() {}
--- /dev/null
+error: constant expression depends on a generic parameter
+ --> $DIR/issue-72787.rs:9:32
+ |
+LL | Condition<{ LHS <= RHS }>: True
+ | ^^^^
+ |
+ = note: this may fail depending on what value the parameter takes
+
+error: constant expression depends on a generic parameter
+ --> $DIR/issue-72787.rs:20:42
+ |
+LL | IsLessOrEqual<{ 8 - I }, { 8 - J }>: True,
+ | ^^^^
+ |
+ = note: this may fail depending on what value the parameter takes
+
+error: constant expression depends on a generic parameter
+ --> $DIR/issue-72787.rs:20:42
+ |
+LL | IsLessOrEqual<{ 8 - I }, { 8 - J }>: True,
+ | ^^^^
+ |
+ = note: this may fail depending on what value the parameter takes
+
+error: constant expression depends on a generic parameter
+ --> $DIR/issue-72787.rs:20:42
+ |
+LL | IsLessOrEqual<{ 8 - I }, { 8 - J }>: True,
+ | ^^^^
+ |
+ = note: this may fail depending on what value the parameter takes
+
+error: constant expression depends on a generic parameter
+ --> $DIR/issue-72787.rs:20:42
+ |
+LL | IsLessOrEqual<{ 8 - I }, { 8 - J }>: True,
+ | ^^^^
+ |
+ = note: this may fail depending on what value the parameter takes
+
+error: aborting due to 5 previous errors
+
--- /dev/null
+// check-pass
+#![feature(min_const_generics)]
+
+struct Foo<const N: usize>;
+
+impl<const N: usize> Foo<N> {
+ const VALUE: usize = N * 2;
+}
+
+trait Bar {
+ const ASSOC: usize;
+}
+
+impl<const N: usize> Bar for Foo<N> {
+ const ASSOC: usize = N * 3;
+}
+
+fn main() {}
--- /dev/null
+#![feature(min_const_generics)]
+
+fn test<const N: usize>() {}
+
+fn ok<const M: usize>() -> [u8; M] {
+ [0; { M }]
+}
+
+struct Break0<const N: usize>([u8; { N + 1 }]);
+//~^ ERROR generic parameters must not be used inside of non trivial constant values
+
+struct Break1<const N: usize>([u8; { { N } }]);
+//~^ ERROR generic parameters must not be used inside of non trivial constant values
+
+fn break2<const N: usize>() {
+ let _: [u8; N + 1];
+ //~^ ERROR generic parameters must not be used inside of non trivial constant values
+}
+
+fn break3<const N: usize>() {
+ let _ = [0; N + 1];
+ //~^ ERROR generic parameters must not be used inside of non trivial constant values
+}
+
+trait Foo {
+ const ASSOC: usize;
+}
+
+fn main() {}
--- /dev/null
+error: generic parameters must not be used inside of non trivial constant values
+ --> $DIR/complex-expression.rs:9:38
+ |
+LL | struct Break0<const N: usize>([u8; { N + 1 }]);
+ | ^ non-trivial anonymous constants must not depend on the parameter `N`
+ |
+ = help: it is currently only allowed to use either `N` or `{ N }` as generic constants
+
+error: generic parameters must not be used inside of non trivial constant values
+ --> $DIR/complex-expression.rs:12:40
+ |
+LL | struct Break1<const N: usize>([u8; { { N } }]);
+ | ^ non-trivial anonymous constants must not depend on the parameter `N`
+ |
+ = help: it is currently only allowed to use either `N` or `{ N }` as generic constants
+
+error: generic parameters must not be used inside of non trivial constant values
+ --> $DIR/complex-expression.rs:16:17
+ |
+LL | let _: [u8; N + 1];
+ | ^ non-trivial anonymous constants must not depend on the parameter `N`
+ |
+ = help: it is currently only allowed to use either `N` or `{ N }` as generic constants
+
+error: generic parameters must not be used inside of non trivial constant values
+ --> $DIR/complex-expression.rs:21:17
+ |
+LL | let _ = [0; N + 1];
+ | ^ non-trivial anonymous constants must not depend on the parameter `N`
+ |
+ = help: it is currently only allowed to use either `N` or `{ N }` as generic constants
+
+error: aborting due to 4 previous errors
+
--- /dev/null
+#![feature(min_const_generics)]
+
+struct Foo<const N: [u8; 0]>;
+//~^ ERROR using `[u8; 0]` as const generic parameters is forbidden
+
+struct Bar<const N: ()>;
+//~^ ERROR using `()` as const generic parameters is forbidden
+
+#[derive(PartialEq, Eq)]
+struct No;
+
+struct Fez<const N: No>;
+//~^ ERROR using `No` as const generic parameters is forbidden
+
+struct Faz<const N: &'static u8>;
+//~^ ERROR using `&'static u8` as const generic parameters is forbidden
+
+fn main() {}
--- /dev/null
+error: using `[u8; 0]` as const generic parameters is forbidden
+ --> $DIR/complex-types.rs:3:21
+ |
+LL | struct Foo<const N: [u8; 0]>;
+ | ^^^^^^^
+ |
+ = note: the only supported types are integers, `bool` and `char`
+ = note: more complex types are supported with `#[feature(const_generics)]`
+
+error: using `()` as const generic parameters is forbidden
+ --> $DIR/complex-types.rs:6:21
+ |
+LL | struct Bar<const N: ()>;
+ | ^^
+ |
+ = note: the only supported types are integers, `bool` and `char`
+ = note: more complex types are supported with `#[feature(const_generics)]`
+
+error: using `No` as const generic parameters is forbidden
+ --> $DIR/complex-types.rs:12:21
+ |
+LL | struct Fez<const N: No>;
+ | ^^
+ |
+ = note: the only supported types are integers, `bool` and `char`
+ = note: more complex types are supported with `#[feature(const_generics)]`
+
+error: using `&'static u8` as const generic parameters is forbidden
+ --> $DIR/complex-types.rs:15:21
+ |
+LL | struct Faz<const N: &'static u8>;
+ | ^^^^^^^^^^^
+ |
+ = note: the only supported types are integers, `bool` and `char`
+ = note: more complex types are supported with `#[feature(const_generics)]`
+
+error: aborting due to 4 previous errors
+
--- /dev/null
+fn test<const N: usize>() {}
+//~^ ERROR const generics are unstable
+
+fn main() {}
--- /dev/null
+error[E0658]: const generics are unstable
+ --> $DIR/feature-gate-min_const_generics.rs:1:15
+ |
+LL | fn test<const N: usize>() {}
+ | ^
+ |
+ = note: see issue #74878 <https://github.com/rust-lang/rust/issues/74878> for more information
+ = help: add `#![feature(min_const_generics)]` to the crate attributes to enable
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0658`.
// run-pass
-#![feature(const_generics)]
+// revisions: full min
+#![cfg_attr(full, feature(const_generics))]
+#![cfg_attr(min, feature(min_const_generics))]
#![allow(incomplete_features)]
-#![feature(const_fn)]
struct Foo;
--- /dev/null
+// Regression test for #75118.
+
+use std::ptr::NonNull;
+
+pub const fn dangling_slice<T>() -> NonNull<[T]> {
+ NonNull::<[T; 0]>::dangling()
+ //~^ ERROR: unsizing casts are only allowed for references right now
+}
+
+fn main() {}
--- /dev/null
+error[E0723]: unsizing casts are only allowed for references right now
+ --> $DIR/unsizing-cast-non-null.rs:6:5
+ |
+LL | NonNull::<[T; 0]>::dangling()
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information
+ = help: add `#![feature(const_fn)]` to the crate attributes to enable
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0723`.
-// ignore-emscripten
+// only-x86_64
fn main() {
unsafe {
-// ignore-emscripten
+// only-x86_64
fn main() {
unsafe {
LL | struct ConstFn<const F: fn()>;
| ^
|
- = note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information
- = help: add `#![feature(const_generics)]` to the crate attributes to enable
+ = note: see issue #74878 <https://github.com/rust-lang/rust/issues/74878> for more information
+ = help: add `#![feature(min_const_generics)]` to the crate attributes to enable
error[E0658]: const generics are unstable
--> $DIR/feature-gate-const_generics-ptr.rs:5:23
LL | struct ConstPtr<const P: *const u32>;
| ^
|
- = note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information
- = help: add `#![feature(const_generics)]` to the crate attributes to enable
+ = note: see issue #74878 <https://github.com/rust-lang/rust/issues/74878> for more information
+ = help: add `#![feature(min_const_generics)]` to the crate attributes to enable
error: using function pointers as const generic parameters is forbidden
--> $DIR/feature-gate-const_generics-ptr.rs:1:25
LL | fn foo<const X: ()>() {}
| ^
|
- = note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information
- = help: add `#![feature(const_generics)]` to the crate attributes to enable
+ = note: see issue #74878 <https://github.com/rust-lang/rust/issues/74878> for more information
+ = help: add `#![feature(min_const_generics)]` to the crate attributes to enable
error[E0658]: const generics are unstable
--> $DIR/feature-gate-const_generics.rs:3:18
LL | struct Foo<const X: usize>([(); X]);
| ^
|
- = note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information
- = help: add `#![feature(const_generics)]` to the crate attributes to enable
+ = note: see issue #74878 <https://github.com/rust-lang/rust/issues/74878> for more information
+ = help: add `#![feature(min_const_generics)]` to the crate attributes to enable
error: aborting due to 2 previous errors
--- /dev/null
+// Regression test for #59311. The test is taken from
+// rust-lang/rust/issues/71546#issuecomment-620638437
+// as they seem to have the same cause.
+
+// FIXME: It's not clear that this code ought to report
+// an error, but the regression test is here to ensure
+// that it does not ICE. See discussion on #74889 for details.
+
+pub trait T {
+ fn t<F: Fn()>(&self, _: F) {}
+}
+
+pub fn crash<V>(v: &V)
+where
+ for<'a> &'a V: T + 'static,
+{
+ v.t(|| {}); //~ ERROR: higher-ranked subtype error
+}
+
+fn main() {}
--- /dev/null
+error: higher-ranked subtype error
+ --> $DIR/issue-59311.rs:17:9
+ |
+LL | v.t(|| {});
+ | ^^^^^
+
+error: aborting due to previous error
+
LL | | () => { syntax error };
| | ^^^^^ expected one of 8 possible tokens
LL | | }
- | |__- in this expansion of `pong!`
+ | |__- in this expansion of `pong!` (#2)
...
LL | ping!();
- | -------- in this macro invocation
+ | -------- in this macro invocation (#1)
|
::: $DIR/auxiliary/ping.rs:5:1
|
LL | / macro_rules! ping {
LL | | () => {
LL | | pong!();
- | | -------- in this macro invocation
+ | | -------- in this macro invocation (#2)
LL | | }
LL | | }
- | |_- in this expansion of `ping!`
+ | |_- in this expansion of `ping!` (#1)
error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, `}`, or an operator, found `error`
--> $DIR/main.rs:10:20
--- /dev/null
+// Regression test for #47429: short backtraces were not terminating correctly
+
+// compile-flags: -O
+// run-fail
+// check-run-results
+// exec-env:RUST_BACKTRACE=1
+
+// ignore-msvc see #62897 and `backtrace-debuginfo.rs` test
+// ignore-android FIXME #17520
+// ignore-cloudabi spawning processes is not supported
+// ignore-openbsd no support for libbacktrace without filename
+// ignore-wasm no panic or subprocess support
+// ignore-emscripten no panic or subprocess support
+// ignore-sgx no subprocess support
+
+fn main() {
+ panic!()
+}
--- /dev/null
+thread 'main' panicked at 'explicit panic', $DIR/issue-47429-short-backtraces.rs:17:5
+stack backtrace:
+ 0: std::panicking::begin_panic
+ 1: issue_47429_short_backtraces::main
+note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
--- /dev/null
+// build-pass
+// compile-flags:-Zpolymorphize=on -Zsymbol-mangling-version=v0
+
+fn foo(f: impl Fn()) {
+ let x = |_: ()| ();
+
+ // Don't use `f` in `y`, but refer to `x` so that the closure substs contain a reference to
+ // `x` that will differ for each instantiation despite polymorphisation of the varying
+ // argument.
+ let y = || x(());
+
+ // Consider `f` used in `foo`.
+ f();
+ // Use `y` so that it is visited in monomorphisation collection.
+ y();
+}
+
+fn entry_a() {
+ foo(|| ());
+}
+
+fn entry_b() {
+ foo(|| ());
+}
+
+fn main() {
+ entry_a();
+ entry_b();
+}
--- /dev/null
+// build-pass
+// compile-flags:-Zpolymorphize=on -Zsymbol-mangling-version=v0
+
+fn foo(f: impl Fn()) {
+ // Mutate an upvar from `x` so that it implements `FnMut`.
+ let mut outer = 3;
+ let mut x = |_: ()| {
+ outer = 4;
+ ()
+ };
+
+ // Don't use `f` in `y`, but refer to `x` so that the closure substs contain a reference to
+ // `x` that will differ for each instantiation despite polymorphisation of the varying
+ // argument.
+ let mut y = || x(());
+
+ // Consider `f` used in `foo`.
+ f();
+ // Use `y` so that it is visited in monomorphisation collection.
+ y();
+}
+
+fn entry_a() {
+ foo(|| ());
+}
+
+fn entry_b() {
+ foo(|| ());
+}
+
+fn main() {
+ entry_a();
+ entry_b();
+}
--- /dev/null
+// build-pass
+// compile-flags:-Zpolymorphize=on -Zsymbol-mangling-version=v0
+
+fn foo(f: impl Fn()) {
+ // Move a non-copy type into `x` so that it implements `FnOnce`.
+ let outer = Vec::<u32>::new();
+ let x = move |_: ()| {
+ let inner = outer;
+ ()
+ };
+
+ // Don't use `f` in `y`, but refer to `x` so that the closure substs contain a reference to
+ // `x` that will differ for each instantiation despite polymorphisation of the varying
+ // argument.
+ let y = || x(());
+
+ // Consider `f` used in `foo`.
+ f();
+ // Use `y` so that it is visited in monomorphisation collection.
+ y();
+}
+
+fn entry_a() {
+ foo(|| ());
+}
+
+fn entry_b() {
+ foo(|| ());
+}
+
+fn main() {
+ entry_a();
+ entry_b();
+}
--- /dev/null
+// build-pass
+// compile-flags:-Zpolymorphize=on -Zsymbol-mangling-version=v0
+
+fn y_uses_f(f: impl Fn()) {
+ let x = |_: ()| ();
+
+ let y = || {
+ f();
+ x(());
+ };
+
+ f();
+ y();
+}
+
+fn x_uses_f(f: impl Fn()) {
+ let x = |_: ()| { f(); };
+
+ let y = || x(());
+
+ f();
+ y();
+}
+
+fn entry_a() {
+ x_uses_f(|| ());
+ y_uses_f(|| ());
+}
+
+fn entry_b() {
+ x_uses_f(|| ());
+ y_uses_f(|| ());
+}
+
+fn main() {
+ entry_a();
+ entry_b();
+}
--- /dev/null
+// build-fail
+// compile-flags: -Zpolymorphize=on
+#![crate_type = "lib"]
+#![feature(rustc_attrs)]
+
+fn foo<'a>(_: &'a ()) {}
+
+#[rustc_polymorphize_error]
+pub fn test<T>() {
+ //~^ ERROR item has unused generic parameters
+ foo(&());
+}
--- /dev/null
+error: item has unused generic parameters
+ --> $DIR/promoted-function-1.rs:9:8
+ |
+LL | pub fn test<T>() {
+ | ^^^^ - generic parameter `T` is unused
+
+error: aborting due to previous error
+
--- /dev/null
+// build-fail
+// compile-flags:-Zpolymorphize=on
+#![crate_type = "lib"]
+#![feature(lazy_normalization_consts, rustc_attrs)]
+//~^ WARN the feature `lazy_normalization_consts` is incomplete
+
+#[rustc_polymorphize_error]
+fn test<T>() {
+ //~^ ERROR item has unused generic parameters
+ let x = [0; 3 + 4];
+}
+
+pub fn caller() {
+ test::<String>();
+ test::<Vec<String>>();
+}
--- /dev/null
+warning: the feature `lazy_normalization_consts` is incomplete and may not be safe to use and/or cause compiler crashes
+ --> $DIR/promoted-function-2.rs:4:12
+ |
+LL | #![feature(lazy_normalization_consts, rustc_attrs)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `#[warn(incomplete_features)]` on by default
+ = note: see issue #72219 <https://github.com/rust-lang/rust/issues/72219> for more information
+
+error: item has unused generic parameters
+ --> $DIR/promoted-function-2.rs:8:4
+ |
+LL | fn test<T>() {
+ | ^^^^ - generic parameter `T` is unused
+
+error: aborting due to previous error; 1 warning emitted
+
// run-pass
+// compile-flags:-Zpolymorphize=on
+
fn fop<T>() {}
fn bar<T>() -> &'static fn() {
fn foo2<T: Default>() {
let _: T = Default::default();
(|| {
+ //~^ ERROR item has unused generic parameters
let call: extern "rust-call" fn(_, _) = Fn::call;
call(&|| {}, ());
//~^ ERROR item has unused generic parameters
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: item has unused generic parameters
- --> $DIR/unsized_cast.rs:21:15
+ --> $DIR/unsized_cast.rs:22:15
|
LL | fn foo2<T: Default>() {
| - generic parameter `T` is unused
LL | call(&|| {}, ());
| ^^^^^
-error: aborting due to 3 previous errors
+error: item has unused generic parameters
+ --> $DIR/unsized_cast.rs:19:5
+ |
+LL | fn foo2<T: Default>() {
+ | - generic parameter `T` is unused
+LL | let _: T = Default::default();
+LL | / (|| {
+LL | |
+LL | | let call: extern "rust-call" fn(_, _) = Fn::call;
+LL | | call(&|| {}, ());
+LL | |
+LL | | })();
+ | |______^
+
+error: aborting due to 4 previous errors
--- /dev/null
+// Regression test for issue #75062
+// Tests that we don't ICE on a privacy error for a fieldless tuple struct.
+
+mod foo {
+ struct Bar();
+}
+
+fn main() {
+ foo::Bar(); //~ ERROR tuple struct
+}
--- /dev/null
+error[E0603]: tuple struct `Bar` is private
+ --> $DIR/issue-75062-fieldless-tuple-struct.rs:9:10
+ |
+LL | foo::Bar();
+ | ^^^ private tuple struct
+ |
+note: the tuple struct `Bar` is defined here
+ --> $DIR/issue-75062-fieldless-tuple-struct.rs:5:5
+ |
+LL | struct Bar();
+ | ^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0603`.
--- /dev/null
+// check-pass
+// aux-build:test-macros.rs
+
+// Anonymize unstable non-dummy spans while still showing dummy spans `0..0`.
+// normalize-stdout-test "bytes\([^0]\w*\.\.(\w+)\)" -> "bytes(LO..$1)"
+// normalize-stdout-test "bytes\((\w+)\.\.[^0]\w*\)" -> "bytes($1..HI)"
+
+#[macro_use]
+extern crate test_macros;
+
+print_bang! {
+
+/**
+*******
+* DOC *
+* DOC *
+* DOC *
+*******
+*/
+pub struct S;
+
+}
+
+fn main() {}
--- /dev/null
+PRINT-BANG INPUT (DISPLAY): /**
+*******
+* DOC *
+* DOC *
+* DOC *
+*******
+*/
+ pub struct S ;
+PRINT-BANG RE-COLLECTED (DISPLAY): #[doc = "\n*******\n* DOC *\n* DOC *\n* DOC *\n*******\n"] pub struct S ;
+PRINT-BANG INPUT (DEBUG): TokenStream [
+ Punct {
+ ch: '#',
+ spacing: Alone,
+ span: #0 bytes(LO..HI),
+ },
+ Group {
+ delimiter: Bracket,
+ stream: TokenStream [
+ Ident {
+ ident: "doc",
+ span: #0 bytes(LO..HI),
+ },
+ Punct {
+ ch: '=',
+ spacing: Alone,
+ span: #0 bytes(LO..HI),
+ },
+ Literal {
+ kind: Str,
+ symbol: "\n*******\n* DOC *\n* DOC *\n* DOC *\n*******\n",
+ suffix: None,
+ span: #0 bytes(LO..HI),
+ },
+ ],
+ span: #0 bytes(LO..HI),
+ },
+ Ident {
+ ident: "pub",
+ span: #0 bytes(LO..HI),
+ },
+ Ident {
+ ident: "struct",
+ span: #0 bytes(LO..HI),
+ },
+ Ident {
+ ident: "S",
+ span: #0 bytes(LO..HI),
+ },
+ Punct {
+ ch: ';',
+ spacing: Alone,
+ span: #0 bytes(LO..HI),
+ },
+]
// well enough to reproduce (and illustrate) the bug from #16687.
#![feature(allocator_api)]
+#![feature(slice_ptr_get)]
-use std::alloc::{handle_alloc_error, AllocInit, AllocRef, Global, Layout, ReallocPlacement};
+use std::alloc::{handle_alloc_error, AllocRef, Global, Layout};
use std::ptr::{self, NonNull};
fn main() {
println!("allocate({:?})", layout);
}
- let memory = Global
- .alloc(layout, AllocInit::Uninitialized)
- .unwrap_or_else(|_| handle_alloc_error(layout));
+ let ptr = Global.alloc(layout).unwrap_or_else(|_| handle_alloc_error(layout));
if PRINT {
- println!("allocate({:?}) = {:?}", layout, memory.ptr);
+ println!("allocate({:?}) = {:?}", layout, ptr);
}
- memory.ptr.cast().as_ptr()
+ ptr.as_non_null_ptr().as_ptr()
}
unsafe fn deallocate(ptr: *mut u8, layout: Layout) {
NonNull::new_unchecked(ptr),
old,
new.size(),
- ReallocPlacement::MayMove,
- AllocInit::Uninitialized,
)
} else {
- Global.shrink(NonNull::new_unchecked(ptr), old, new.size(), ReallocPlacement::MayMove)
+ Global.shrink(NonNull::new_unchecked(ptr), old, new.size())
};
- let memory = memory.unwrap_or_else(|_| {
+ let ptr = memory.unwrap_or_else(|_| {
handle_alloc_error(Layout::from_size_align_unchecked(new.size(), old.align()))
});
if PRINT {
- println!("reallocate({:?}, old={:?}, new={:?}) = {:?}", ptr, old, new, memory.ptr);
+ println!("reallocate({:?}, old={:?}, new={:?}) = {:?}", ptr, old, new, ptr);
}
- memory.ptr.cast().as_ptr()
+ ptr.as_non_null_ptr().as_ptr()
}
fn idx_to_size(i: usize) -> usize {
-error[E0391]: cycle detected when computing layout of `std::option::Option<S>`
+error[E0391]: cycle detected when computing layout of `S`
|
- = note: ...which requires computing layout of `S`...
- = note: ...which again requires computing layout of `std::option::Option<S>`, completing the cycle
+ = note: ...which requires computing layout of `std::option::Option<S>`...
+ = note: ...which again requires computing layout of `S`, completing the cycle
note: cycle used when optimizing MIR for `main`
--> $DIR/issue-26548-recursion-via-normalize.rs:15:1
|
// pretty-expanded FIXME #23616
#![feature(allocator_api)]
-use std::alloc::{handle_alloc_error, AllocInit, AllocRef, Global, Layout};
+use std::alloc::{handle_alloc_error, AllocRef, Global, Layout};
use std::ptr::NonNull;
struct arena(());
fn alloc(_bcx: &arena) -> &Bcx<'_> {
unsafe {
let layout = Layout::new::<Bcx>();
- let memory = Global
- .alloc(layout, AllocInit::Uninitialized)
- .unwrap_or_else(|_| handle_alloc_error(layout));
- &*(memory.ptr.as_ptr() as *const _)
+ let ptr = Global.alloc(layout).unwrap_or_else(|_| handle_alloc_error(layout));
+ &*(ptr.as_ptr() as *const _)
}
}
--- /dev/null
+#[track_caller] //~ ERROR `main` function is not allowed to be
+fn main() {
+ panic!("{}: oh no", std::panic::Location::caller());
+}
--- /dev/null
+error: `main` function is not allowed to be `#[track_caller]`
+ --> $DIR/error-with-main.rs:1:1
+ |
+LL | #[track_caller]
+ | ^^^^^^^^^^^^^^^
+LL | / fn main() {
+LL | | panic!("{}: oh no", std::panic::Location::caller());
+LL | | }
+ | |_- `main` function is not allowed to be `#[track_caller]`
+
+error: aborting due to previous error
+
--- /dev/null
+#![feature(start)]
+
+#[start]
+#[track_caller] //~ ERROR `start` is not allowed to be `#[track_caller]`
+fn start(_argc: isize, _argv: *const *const u8) -> isize {
+ panic!("{}: oh no", std::panic::Location::caller());
+}
--- /dev/null
+error: `start` is not allowed to be `#[track_caller]`
+ --> $DIR/error-with-start.rs:4:1
+ |
+LL | #[track_caller]
+ | ^^^^^^^^^^^^^^^
+LL | / fn start(_argc: isize, _argv: *const *const u8) -> isize {
+LL | | panic!("{}: oh no", std::panic::Location::caller());
+LL | | }
+ | |_- `start` is not allowed to be `#[track_caller]`
+
+error: aborting due to previous error
+
#![allow(non_camel_case_types)]
// ignore-emscripten
+// ignore-endian-big behavior of simd_bitmask is endian-specific
// Test that the simd_bitmask intrinsic produces correct results.
#![allow(non_camel_case_types)]
// ignore-emscripten
-// ignore-mips behavior of simd_select_bitmask is endian-specific
-// ignore-mips64 behavior of simd_select_bitmask is endian-specific
-// ignore-powerpc behavior of simd_select_bitmask is endian-specific
-// ignore-powerpc64 behavior of simd_select_bitmask is endian-specific
+// ignore-endian-big behavior of simd_select_bitmask is endian-specific
// Test that the simd_select intrinsics produces correct results.
-Subproject commit 2d5c2381e4e50484bf281fc1bfe19743aa9eb37a
+Subproject commit 1653f354644834073d6d2541e27fae94588e685e
use if_chain::if_chain;
use itertools::Itertools;
use rustc_ast::ast::{AttrKind, Attribute};
+use rustc_ast::token::CommentKind;
use rustc_data_structures::fx::FxHashSet;
use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass};
}
}
-/// Cleanup documentation decoration (`///` and such).
+/// Cleanup documentation decoration.
///
/// We can't use `rustc_ast::attr::AttributeMethods::with_desugared_doc` or
/// `rustc_ast::parse::lexer::comments::strip_doc_comment_decoration` because we
/// the spans but this function is inspired from the later.
#[allow(clippy::cast_possible_truncation)]
#[must_use]
-pub fn strip_doc_comment_decoration(comment: &str, span: Span) -> (String, Vec<(usize, Span)>) {
+pub fn strip_doc_comment_decoration(doc: &str, comment_kind: CommentKind, span: Span) -> (String, Vec<(usize, Span)>) {
// one-line comments lose their prefix
- const ONELINERS: &[&str] = &["///!", "///", "//!", "//"];
- for prefix in ONELINERS {
- if comment.starts_with(*prefix) {
- let doc = &comment[prefix.len()..];
- let mut doc = doc.to_owned();
- doc.push('\n');
- return (
- doc.to_owned(),
- vec![(doc.len(), span.with_lo(span.lo() + BytePos(prefix.len() as u32)))],
- );
- }
+ if comment_kind == CommentKind::Line {
+ let mut doc = doc.to_owned();
+ doc.push('\n');
+ let len = doc.len();
+ // +3 skips the opening delimiter
+ return (doc, vec![(len, span.with_lo(span.lo() + BytePos(3)))]);
}
- if comment.starts_with("/*") {
- let doc = &comment[3..comment.len() - 2];
- let mut sizes = vec![];
- let mut contains_initial_stars = false;
- for line in doc.lines() {
- let offset = line.as_ptr() as usize - comment.as_ptr() as usize;
- debug_assert_eq!(offset as u32 as usize, offset);
- contains_initial_stars |= line.trim_start().starts_with('*');
- // +1 for the newline
- sizes.push((line.len() + 1, span.with_lo(span.lo() + BytePos(offset as u32))));
- }
- if !contains_initial_stars {
- return (doc.to_string(), sizes);
- }
- // remove the initial '*'s if any
- let mut no_stars = String::with_capacity(doc.len());
- for line in doc.lines() {
- let mut chars = line.chars();
- while let Some(c) = chars.next() {
- if c.is_whitespace() {
- no_stars.push(c);
- } else {
- no_stars.push(if c == '*' { ' ' } else { c });
- break;
- }
+ let mut sizes = vec![];
+ let mut contains_initial_stars = false;
+ for line in doc.lines() {
+ let offset = line.as_ptr() as usize - doc.as_ptr() as usize;
+ debug_assert_eq!(offset as u32 as usize, offset);
+ contains_initial_stars |= line.trim_start().starts_with('*');
+ // +1 adds the newline, +3 skips the opening delimiter
+ sizes.push((line.len() + 1, span.with_lo(span.lo() + BytePos(3 + offset as u32))));
+ }
+ if !contains_initial_stars {
+ return (doc.to_string(), sizes);
+ }
+ // remove the initial '*'s if any
+ let mut no_stars = String::with_capacity(doc.len());
+ for line in doc.lines() {
+ let mut chars = line.chars();
+ while let Some(c) = chars.next() {
+ if c.is_whitespace() {
+ no_stars.push(c);
+ } else {
+ no_stars.push(if c == '*' { ' ' } else { c });
+ break;
}
- no_stars.push_str(chars.as_str());
- no_stars.push('\n');
}
- return (no_stars, sizes);
+ no_stars.push_str(chars.as_str());
+ no_stars.push('\n');
}
- panic!("not a doc-comment: {}", comment);
+ (no_stars, sizes)
}
#[derive(Copy, Clone)]
let mut spans = vec![];
for attr in attrs {
- if let AttrKind::DocComment(ref comment) = attr.kind {
- let comment = comment.to_string();
- let (comment, current_spans) = strip_doc_comment_decoration(&comment, attr.span);
+ if let AttrKind::DocComment(comment_kind, comment) = attr.kind {
+ let (comment, current_spans) = strip_doc_comment_decoration(&comment.as_str(), comment_kind, attr.span);
spans.extend_from_slice(¤t_spans);
doc.push_str(&comment);
} else if attr.has_name(sym!(doc)) {
return;
}
if cx.access_levels.is_exported(item.hir_id)
- && !is_proc_macro(&item.attrs)
+ && !is_proc_macro(cx.sess(), &item.attrs)
&& attr_by_name(&item.attrs, "no_mangle").is_none()
{
check_must_use_candidate(
let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
check_needless_must_use(cx, &sig.decl, item.hir_id, item.span, fn_header_span, attr);
} else if cx.access_levels.is_exported(item.hir_id)
- && !is_proc_macro(&item.attrs)
+ && !is_proc_macro(cx.sess(), &item.attrs)
&& trait_ref_of_method(cx, item.hir_id).is_none()
{
check_must_use_candidate(
let body = cx.tcx.hir().body(eid);
Self::check_raw_ptr(cx, sig.header.unsafety, &sig.decl, body, item.hir_id);
- if attr.is_none() && cx.access_levels.is_exported(item.hir_id) && !is_proc_macro(&item.attrs) {
+ if attr.is_none() && cx.access_levels.is_exported(item.hir_id) && !is_proc_macro(cx.sess(), &item.attrs) {
check_must_use_candidate(
cx,
&sig.decl,
"this seems like a manual implementation of the non-exhaustive pattern",
|diag| {
if_chain! {
- if !attr::contains_name(&item.attrs, sym!(non_exhaustive));
+ if !item.attrs.iter().any(|attr| attr.has_name(sym!(non_exhaustive)));
let header_span = cx.sess.source_map().span_until_char(item.span, '{');
if let Some(snippet) = snippet_opt(cx, header_span);
then {
"this seems like a manual implementation of the non-exhaustive pattern",
|diag| {
if_chain! {
- if !attr::contains_name(&item.attrs, sym!(non_exhaustive));
+ if !item.attrs.iter().any(|attr| attr.has_name(sym!(non_exhaustive)));
let header_span = find_header_span(cx, item, data);
if let Some(snippet) = snippet_opt(cx, header_span);
then {
use rustc_ast::ast::{
Arm, AssocItem, AssocItemKind, Attribute, Block, FnDecl, Item, ItemKind, Local, MacCall, Pat, PatKind,
};
-use rustc_ast::attr;
use rustc_ast::visit::{walk_block, walk_expr, walk_pat, Visitor};
use rustc_lint::{EarlyContext, EarlyLintPass};
use rustc_middle::lint::in_external_macro;
}
fn do_check(lint: &mut NonExpressiveNames, cx: &EarlyContext<'_>, attrs: &[Attribute], decl: &FnDecl, blk: &Block) {
- if !attr::contains_name(attrs, sym!(test)) {
+ if !attrs.iter().any(|attr| attr.has_name(sym!(test))) {
let mut visitor = SimilarNamesLocalVisitor {
names: Vec::new(),
cx,
impl TabsInDocComments {
fn warn_if_tabs_in_doc(cx: &EarlyContext<'_>, attr: &ast::Attribute) {
- if let ast::AttrKind::DocComment(comment) = attr.kind {
+ if let ast::AttrKind::DocComment(_, comment) = attr.kind {
let comment = comment.as_str();
for (lo, hi) in get_chunks_of_tabs(&comment) {
+ // +3 skips the opening delimiter
let new_span = Span::new(
- attr.span.lo() + BytePos(lo),
- attr.span.lo() + BytePos(hi),
+ attr.span.lo() + BytePos(3 + lo),
+ attr.span.lo() + BytePos(3 + hi),
attr.span.ctxt(),
);
span_lint_and_sugg(
use AttrKind::*;
l.style == r.style
&& match (&l.kind, &r.kind) {
- (DocComment(l), DocComment(r)) => l == r,
+ (DocComment(l1, l2), DocComment(r1, r2)) => l1 == r1 && l2 == r2,
(Normal(l), Normal(r)) => eq_path(&l.path, &r.path) && eq_mac_args(&l.args, &r.args),
_ => false,
}
use rustc_ast::ast;
-use rustc_ast::expand::is_proc_macro_attr;
use rustc_errors::Applicability;
use rustc_session::Session;
use std::str::FromStr;
/// Return true if the attributes contain any of `proc_macro`,
/// `proc_macro_derive` or `proc_macro_attribute`, false otherwise
-pub fn is_proc_macro(attrs: &[ast::Attribute]) -> bool {
- attrs.iter().any(is_proc_macro_attr)
+pub fn is_proc_macro(sess: &Session, attrs: &[ast::Attribute]) -> bool {
+ attrs.iter().any(|attr| sess.is_proc_macro_attr(attr))
}
/// Checks for the `#[automatically_derived]` attribute all `#[derive]`d
/// implementations have.
pub fn is_automatically_derived(attrs: &[ast::Attribute]) -> bool {
- attr::contains_name(attrs, sym!(automatically_derived))
+ attrs.iter().any(|attr| attr.has_name(sym!(automatically_derived)))
}
/// Remove blocks around an expression.
-error: this operation will panic at runtime
- --> $DIR/indexing_slicing_index.rs:11:5
- |
-LL | x[4]; // Ok, let rustc's `const_err` lint handle `usize` indexing on arrays.
- | ^^^^ index out of bounds: the len is 4 but the index is 4
- |
- = note: `#[deny(unconditional_panic)]` on by default
-
-error: this operation will panic at runtime
- --> $DIR/indexing_slicing_index.rs:12:5
- |
-LL | x[1 << 3]; // Ok, let rustc's `const_err` lint handle `usize` indexing on arrays.
- | ^^^^^^^^^ index out of bounds: the len is 4 but the index is 8
-
-error: this operation will panic at runtime
- --> $DIR/indexing_slicing_index.rs:27:5
- |
-LL | x[N]; // Ok, let rustc's `const_err` lint handle `usize` indexing on arrays.
- | ^^^^ index out of bounds: the len is 4 but the index is 15
-
error: indexing may panic.
--> $DIR/indexing_slicing_index.rs:10:5
|
|
= help: Consider using `.get(n)` or `.get_mut(n)` instead
-error: aborting due to 10 previous errors
+error: aborting due to 7 previous errors
}
/// Configuration for compiletest
-#[derive(Clone)]
+#[derive(Debug, Clone)]
pub struct Config {
/// `true` to to overwrite stderr/stdout files instead of complaining about changes in output.
pub bless: bool,
name == util::get_pointer_width(&self.target) || // pointer width
name == self.stage_id.split('-').next().unwrap() || // stage
(self.target != self.host && name == "cross-compile") ||
+ (name == "endian-big" && util::is_big_endian(&self.target)) ||
(self.remote_test_client.is_some() && name == "remote") ||
match self.compare_mode {
Some(CompareMode::Nll) => name == "compare-mode-nll",
emit_metadata: EmitMetadata,
allow_unused: AllowUnused,
) -> Command {
- let is_rustdoc = self.is_rustdoc();
+ let is_aux = input_file.components().map(|c| c.as_os_str()).any(|c| c == "auxiliary");
+ let is_rustdoc = self.is_rustdoc() && !is_aux;
let mut rustc = if !is_rustdoc {
Command::new(&self.config.rustc_path)
} else {
}
fn diff_mir_files(&self, before: PathBuf, after: PathBuf) -> String {
- let before = self.get_mir_dump_dir().join(before);
- let after = self.get_mir_dump_dir().join(after);
+ let to_full_path = |path: PathBuf| {
+ let full = self.get_mir_dump_dir().join(&path);
+ if !full.exists() {
+ panic!(
+ "the mir dump file for {} does not exist (requested in {})",
+ path.display(),
+ self.testpaths.file.display(),
+ );
+ }
+ full
+ };
+ let before = to_full_path(before);
+ let after = to_full_path(after);
debug!("comparing the contents of: {} with {}", before.display(), after.display());
let before = fs::read_to_string(before).unwrap();
let after = fs::read_to_string(after).unwrap();
}
}
+#[derive(Debug)]
enum TargetLocation {
ThisFile(PathBuf),
ThisDirectory(PathBuf),
pub const TSAN_SUPPORTED_TARGETS: &'static [&'static str] =
&["aarch64-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-unknown-linux-gnu"];
+const BIG_ENDIAN: &'static [&'static str] = &[
+ "armebv7r",
+ "mips",
+ "mips64",
+ "mipsisa32r6",
+ "mipsisa64r6",
+ "powerpc",
+ "powerpc64",
+ "s390x",
+ "sparc",
+ "sparc64",
+ "sparcv9",
+];
+
pub fn matches_os(triple: &str, name: &str) -> bool {
// For the wasm32 bare target we ignore anything also ignored on emscripten
// and then we also recognize `wasm32-bare` as the os for the target
panic!("Cannot determine Architecture from triple");
}
+/// Determine the endianness from `triple`
+pub fn is_big_endian(triple: &str) -> bool {
+ let triple_arch = triple.split('-').next().unwrap();
+ BIG_ENDIAN.contains(&triple_arch)
+}
+
pub fn matches_env(triple: &str, name: &str) -> bool {
if let Some(env) = triple.split('-').nth(3) { env.starts_with(name) } else { false }
}
#![feature(rustc_private)]
-extern crate rustc_ast;
extern crate rustc_driver;
extern crate rustc_span;
fn main() {
rustc_driver::init_env_logger("RUST_LOG");
let (format, dst) = parse_args();
- let result = rustc_ast::with_default_session_globals(move || main_with_result(format, &dst));
+ let result = rustc_span::with_default_session_globals(move || main_with_result(format, &dst));
if let Err(e) = result {
panic!("{}", e.to_string());
}
-Subproject commit 55bdb3174653039f47362742f8dc941bfc086e8f
+Subproject commit cf633d0e897c065381b7b7d14984830176caf8b2
LABELS = {
'miri': ['A-miri', 'C-bug'],
'rls': ['A-rls', 'C-bug'],
- 'rustfmt': ['C-bug'],
+ 'rustfmt': ['A-rustfmt', 'C-bug'],
'book': ['C-bug'],
'nomicon': ['C-bug'],
'reference': ['C-bug'],
];
// Some error codes don't have any tests apparently...
-const IGNORE_EXPLANATION_CHECK: &[&str] =
- &["E0570", "E0601", "E0602", "E0639", "E0729", "E0749", "E0750"];
+const IGNORE_EXPLANATION_CHECK: &[&str] = &["E0570", "E0601", "E0602", "E0639", "E0729", "E0749"];
fn check_error_code_explanation(
f: &str,
//! We have two separate encoding schemes: a skiplist-like approach, and a
//! compressed bitset. The datasets we consider mostly use the skiplist (it's
//! smaller) but the lowercase and uppercase sets are sufficiently sparse for
-//! the bitset to be worthwhile -- for those sets the biset is a 2x size win.
+//! the bitset to be worthwhile -- for those sets the bitset is a 2x size win.
//! Since the bitset is also faster, this seems an obvious choice. (As a
//! historical note, the bitset was also the prior implementation, so its
//! relative complexity had already been paid).