From d5d56d00e7b940e7abc997e9c4abbf00dd3198a9 Mon Sep 17 00:00:00 2001 From: Slanterns Date: Tue, 16 Apr 2024 20:58:45 +0800 Subject: [PATCH 01/92] Stabilize `BinaryHeap::as_slice` --- library/alloc/src/collections/binary_heap/mod.rs | 3 +-- library/alloc/tests/lib.rs | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/library/alloc/src/collections/binary_heap/mod.rs b/library/alloc/src/collections/binary_heap/mod.rs index 83b2678f7f52..3c024649d3f5 100644 --- a/library/alloc/src/collections/binary_heap/mod.rs +++ b/library/alloc/src/collections/binary_heap/mod.rs @@ -1207,7 +1207,6 @@ impl BinaryHeap { /// Basic usage: /// /// ``` - /// #![feature(binary_heap_as_slice)] /// use std::collections::BinaryHeap; /// use std::io::{self, Write}; /// @@ -1216,7 +1215,7 @@ impl BinaryHeap { /// io::sink().write(heap.as_slice()).unwrap(); /// ``` #[must_use] - #[unstable(feature = "binary_heap_as_slice", issue = "83659")] + #[stable(feature = "binary_heap_as_slice", since = "CURRENT_RUSTC_VERSION")] pub fn as_slice(&self) -> &[T] { self.data.as_slice() } diff --git a/library/alloc/tests/lib.rs b/library/alloc/tests/lib.rs index a34bce66496d..37a02828901d 100644 --- a/library/alloc/tests/lib.rs +++ b/library/alloc/tests/lib.rs @@ -26,7 +26,6 @@ #![feature(binary_heap_into_iter_sorted)] #![feature(binary_heap_drain_sorted)] #![feature(slice_ptr_get)] -#![feature(binary_heap_as_slice)] #![feature(inplace_iteration)] #![feature(iter_advance_by)] #![feature(iter_next_chunk)] From d32d1c1a2ef2451f2fd7a061bba933173a195938 Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Mon, 27 May 2024 11:08:21 +0200 Subject: [PATCH 02/92] Size optimize int formatting --- library/core/src/fmt/num.rs | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/library/core/src/fmt/num.rs b/library/core/src/fmt/num.rs index ab2158394bf1..3a5a5af8bf5d 100644 --- a/library/core/src/fmt/num.rs +++ b/library/core/src/fmt/num.rs @@ -212,6 +212,7 @@ static DEC_DIGITS_LUT: &[u8; 200] = b"0001020304050607080910111213141516171819\ macro_rules! impl_Display { ($($t:ident),* as $u:ident via $conv_fn:ident named $name:ident) => { + #[cfg(not(feature = "optimize_for_size"))] fn $name(mut n: $u, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result { // 2^128 is about 3*10^38, so 39 gives an extra byte of space let mut buf = [MaybeUninit::::uninit(); 39]; @@ -277,6 +278,38 @@ macro_rules! impl_Display { f.pad_integral(is_nonnegative, "", buf_slice) } + #[cfg(feature = "optimize_for_size")] + fn $name(mut n: $u, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // 2^128 is about 3*10^38, so 39 gives an extra byte of space + let mut buf = [MaybeUninit::::uninit(); 39]; + let mut curr = buf.len(); + let buf_ptr = MaybeUninit::slice_as_mut_ptr(&mut buf); + + // SAFETY: To show that it's OK to copy into `buf_ptr`, notice that at the beginning + // `curr == buf.len() == 39 > log(n)` since `n < 2^128 < 10^39`, and at + // each step this is kept the same as `n` is divided. Since `n` is always + // non-negative, this means that `curr > 0` so `buf_ptr[curr..curr + 1]` + // is safe to access. + unsafe { + loop { + curr -= 1; + buf_ptr.add(curr).write((n % 10) as u8 + b'0'); + n /= 10; + + if n == 0 { + break; + } + } + } + + // SAFETY: `curr` > 0 (since we made `buf` large enough), and all the chars are valid UTF-8 + let buf_slice = unsafe { + str::from_utf8_unchecked( + slice::from_raw_parts(buf_ptr.add(curr), buf.len() - curr)) + }; + f.pad_integral(is_nonnegative, "", buf_slice) + } + $(#[stable(feature = "rust1", since = "1.0.0")] impl fmt::Display for $t { #[allow(unused_comparisons)] From 6cfdc571d9d69ca4df1e833998b7ddb331e36d08 Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Mon, 3 Jun 2024 14:36:55 +0200 Subject: [PATCH 03/92] Stabilize order of MonoItems in CGUs and disallow query_instability lint for rustc_monomorphize --- compiler/rustc_middle/src/mir/mono.rs | 49 ++++++-------- compiler/rustc_monomorphize/src/collector.rs | 34 ++++++---- compiler/rustc_monomorphize/src/lib.rs | 1 - .../rustc_monomorphize/src/partitioning.rs | 64 ++++++++++--------- 4 files changed, 74 insertions(+), 74 deletions(-) diff --git a/compiler/rustc_middle/src/mir/mono.rs b/compiler/rustc_middle/src/mir/mono.rs index a3d2140eb1b1..3d79ec0092f8 100644 --- a/compiler/rustc_middle/src/mir/mono.rs +++ b/compiler/rustc_middle/src/mir/mono.rs @@ -5,9 +5,9 @@ use rustc_data_structures::base_n::BaseNString; use rustc_data_structures::base_n::ToBaseN; use rustc_data_structures::base_n::CASE_INSENSITIVE; use rustc_data_structures::fingerprint::Fingerprint; -use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::fx::FxIndexMap; -use rustc_data_structures::stable_hasher::{Hash128, HashStable, StableHasher}; +use rustc_data_structures::stable_hasher::{Hash128, HashStable, StableHasher, ToStableHashKey}; +use rustc_data_structures::unord::UnordMap; use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; use rustc_hir::ItemId; use rustc_index::Idx; @@ -241,7 +241,17 @@ impl<'tcx> fmt::Display for MonoItem<'tcx> { } } -#[derive(Debug)] +impl ToStableHashKey> for MonoItem<'_> { + type KeyType = Fingerprint; + + fn to_stable_hash_key(&self, hcx: &StableHashingContext<'_>) -> Self::KeyType { + let mut hasher = StableHasher::new(); + self.hash_stable(&mut hcx.clone(), &mut hasher); + hasher.finish() + } +} + +#[derive(Debug, HashStable)] pub struct CodegenUnit<'tcx> { /// A name for this CGU. Incremental compilation requires that /// name be unique amongst **all** crates. Therefore, it should @@ -430,38 +440,19 @@ impl<'tcx> CodegenUnit<'tcx> { } } -impl<'a, 'tcx> HashStable> for CodegenUnit<'tcx> { - fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { - let CodegenUnit { - ref items, - name, - // The size estimate is not relevant to the hash - size_estimate: _, - primary: _, - is_code_coverage_dead_code_cgu, - } = *self; +impl ToStableHashKey> for CodegenUnit<'_> { + type KeyType = String; - name.hash_stable(hcx, hasher); - is_code_coverage_dead_code_cgu.hash_stable(hcx, hasher); - - let mut items: Vec<(Fingerprint, _)> = items - .iter() - .map(|(mono_item, &attrs)| { - let mut hasher = StableHasher::new(); - mono_item.hash_stable(hcx, &mut hasher); - let mono_item_fingerprint = hasher.finish(); - (mono_item_fingerprint, attrs) - }) - .collect(); - - items.sort_unstable_by_key(|i| i.0); - items.hash_stable(hcx, hasher); + fn to_stable_hash_key(&self, _: &StableHashingContext<'_>) -> Self::KeyType { + // Codegen unit names are conceptually required to be stable across + // compilation session so that object file names match up. + self.name.to_string() } } pub struct CodegenUnitNameBuilder<'tcx> { tcx: TyCtxt<'tcx>, - cache: FxHashMap, + cache: UnordMap, } impl<'tcx> CodegenUnitNameBuilder<'tcx> { diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 9487692662bb..61680dbfaf52 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -207,8 +207,8 @@ mod move_check; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::sync::{par_for_each_in, LRef, MTLock}; +use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId}; @@ -251,10 +251,10 @@ pub enum MonoItemCollectionStrategy { pub struct UsageMap<'tcx> { // Maps every mono item to the mono items used by it. - used_map: FxHashMap, Vec>>, + used_map: UnordMap, Vec>>, // Maps every mono item to the mono items that use it. - user_map: FxHashMap, Vec>>, + user_map: UnordMap, Vec>>, } type MonoItems<'tcx> = Vec>>; @@ -262,10 +262,10 @@ type MonoItems<'tcx> = Vec>>; /// The state that is shared across the concurrent threads that are doing collection. struct SharedState<'tcx> { /// Items that have been or are currently being recursively collected. - visited: MTLock>>, + visited: MTLock>>, /// Items that have been or are currently being recursively treated as "mentioned", i.e., their /// consts are evaluated but nothing is added to the collection. - mentioned: MTLock>>, + mentioned: MTLock>>, /// Which items are being used where, for better errors. usage_map: MTLock>, } @@ -290,7 +290,7 @@ enum CollectionMode { impl<'tcx> UsageMap<'tcx> { fn new() -> UsageMap<'tcx> { - UsageMap { used_map: FxHashMap::default(), user_map: FxHashMap::default() } + UsageMap { used_map: Default::default(), user_map: Default::default() } } fn record_used<'a>( @@ -668,7 +668,7 @@ struct MirUsedCollector<'a, 'tcx> { used_items: &'a mut MonoItems<'tcx>, /// See the comment in `collect_items_of_instance` for the purpose of this set. /// Note that this contains *not-monomorphized* items! - used_mentioned_items: &'a mut FxHashSet>, + used_mentioned_items: &'a mut UnordSet>, instance: Instance<'tcx>, visiting_call_terminator: bool, move_check: move_check::MoveCheckState, @@ -1272,7 +1272,7 @@ fn collect_items_of_instance<'tcx>( // mentioned item. So instead we collect all pre-monomorphized `MentionedItem` that were already // added to `used_items` in a hash set, which can efficiently query in the // `body.mentioned_items` loop below without even having to monomorphize the item. - let mut used_mentioned_items = FxHashSet::>::default(); + let mut used_mentioned_items = Default::default(); let mut collector = MirUsedCollector { tcx, body, @@ -1628,10 +1628,10 @@ fn create_mono_items_for_default_impls<'tcx>( //=----------------------------------------------------------------------------- #[instrument(skip(tcx, strategy), level = "debug")] -pub fn collect_crate_mono_items( - tcx: TyCtxt<'_>, +pub(crate) fn collect_crate_mono_items<'tcx>( + tcx: TyCtxt<'tcx>, strategy: MonoItemCollectionStrategy, -) -> (FxHashSet>, UsageMap<'_>) { +) -> (Vec>, UsageMap<'tcx>) { let _prof_timer = tcx.prof.generic_activity("monomorphization_collector"); let roots = tcx @@ -1641,8 +1641,8 @@ pub fn collect_crate_mono_items( debug!("building mono item graph, beginning at roots"); let mut state = SharedState { - visited: MTLock::new(FxHashSet::default()), - mentioned: MTLock::new(FxHashSet::default()), + visited: MTLock::new(UnordSet::default()), + mentioned: MTLock::new(UnordSet::default()), usage_map: MTLock::new(UsageMap::new()), }; let recursion_limit = tcx.recursion_limit(); @@ -1665,5 +1665,11 @@ pub fn collect_crate_mono_items( }); } - (state.visited.into_inner(), state.usage_map.into_inner()) + // The set of MonoItems was created in an inherently indeterministic order because + // of parallelism. We sort it here to ensure that the output is deterministic. + let mono_items = tcx.with_stable_hashing_context(move |ref hcx| { + state.visited.into_inner().into_sorted(hcx, true) + }); + + (mono_items, state.usage_map.into_inner()) } diff --git a/compiler/rustc_monomorphize/src/lib.rs b/compiler/rustc_monomorphize/src/lib.rs index b298fe5813f8..eb5f8d92603a 100644 --- a/compiler/rustc_monomorphize/src/lib.rs +++ b/compiler/rustc_monomorphize/src/lib.rs @@ -1,6 +1,5 @@ #![feature(array_windows)] #![feature(is_sorted)] -#![allow(rustc::potential_query_instability)] use rustc_hir::lang_items::LangItem; use rustc_middle::bug; diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index 21d52004728a..336341f4e746 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -98,8 +98,9 @@ use std::fs::{self, File}; use std::io::{BufWriter, Write}; use std::path::{Path, PathBuf}; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::sync; +use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, DefIdSet, LOCAL_CRATE}; use rustc_hir::definitions::DefPathDataName; @@ -131,7 +132,7 @@ struct PlacedMonoItems<'tcx> { /// The codegen units, sorted by name to make things deterministic. codegen_units: Vec>, - internalization_candidates: FxHashSet>, + internalization_candidates: UnordSet>, } // The output CGUs are sorted by name. @@ -197,9 +198,9 @@ fn place_mono_items<'tcx, I>(cx: &PartitioningCx<'_, 'tcx>, mono_items: I) -> Pl where I: Iterator>, { - let mut codegen_units = FxHashMap::default(); + let mut codegen_units = UnordMap::default(); let is_incremental_build = cx.tcx.sess.opts.incremental.is_some(); - let mut internalization_candidates = FxHashSet::default(); + let mut internalization_candidates = UnordSet::default(); // Determine if monomorphizations instantiated in this crate will be made // available to downstream crates. This depends on whether we are in @@ -209,7 +210,7 @@ where cx.tcx.sess.opts.share_generics() && cx.tcx.local_crate_exports_generics(); let cgu_name_builder = &mut CodegenUnitNameBuilder::new(cx.tcx); - let cgu_name_cache = &mut FxHashMap::default(); + let cgu_name_cache = &mut UnordMap::default(); for mono_item in mono_items { // Handle only root (GloballyShared) items directly here. Inlined (LocalCopy) items @@ -260,7 +261,7 @@ where // going via another root item. This includes drop-glue, functions from // external crates, and local functions the definition of which is // marked with `#[inline]`. - let mut reachable_inlined_items = FxHashSet::default(); + let mut reachable_inlined_items = FxIndexSet::default(); get_reachable_inlined_items(cx.tcx, mono_item, cx.usage_map, &mut reachable_inlined_items); // Add those inlined items. It's possible an inlined item is reachable @@ -284,8 +285,9 @@ where codegen_units.insert(cgu_name, CodegenUnit::new(cgu_name)); } - let mut codegen_units: Vec<_> = codegen_units.into_values().collect(); - codegen_units.sort_by(|a, b| a.name().as_str().cmp(b.name().as_str())); + let mut codegen_units: Vec<_> = cx.tcx.with_stable_hashing_context(|ref hcx| { + codegen_units.into_items().map(|(_, cgu)| cgu).collect_sorted(hcx, true) + }); for cgu in codegen_units.iter_mut() { cgu.compute_size_estimate(); @@ -297,7 +299,7 @@ where tcx: TyCtxt<'tcx>, item: MonoItem<'tcx>, usage_map: &UsageMap<'tcx>, - visited: &mut FxHashSet>, + visited: &mut FxIndexSet>, ) { usage_map.for_each_inlined_used_item(tcx, item, |inlined_item| { let is_new = visited.insert(inlined_item); @@ -320,7 +322,7 @@ fn merge_codegen_units<'tcx>( assert!(codegen_units.is_sorted_by(|a, b| a.name().as_str() <= b.name().as_str())); // This map keeps track of what got merged into what. - let mut cgu_contents: FxHashMap> = + let mut cgu_contents: UnordMap> = codegen_units.iter().map(|cgu| (cgu.name(), vec![cgu.name()])).collect(); // If N is the maximum number of CGUs, and the CGUs are sorted from largest @@ -422,22 +424,24 @@ fn merge_codegen_units<'tcx>( // For CGUs that contain the code of multiple modules because of the // merging done above, we use a concatenation of the names of all // contained CGUs. - let new_cgu_names: FxHashMap = cgu_contents - .into_iter() - // This `filter` makes sure we only update the name of CGUs that - // were actually modified by merging. - .filter(|(_, cgu_contents)| cgu_contents.len() > 1) - .map(|(current_cgu_name, cgu_contents)| { - let mut cgu_contents: Vec<&str> = cgu_contents.iter().map(|s| s.as_str()).collect(); + let new_cgu_names = UnordMap::from( + cgu_contents + .items() + // This `filter` makes sure we only update the name of CGUs that + // were actually modified by merging. + .filter(|(_, cgu_contents)| cgu_contents.len() > 1) + .map(|(current_cgu_name, cgu_contents)| { + let mut cgu_contents: Vec<&str> = + cgu_contents.iter().map(|s| s.as_str()).collect(); - // Sort the names, so things are deterministic and easy to - // predict. We are sorting primitive `&str`s here so we can - // use unstable sort. - cgu_contents.sort_unstable(); + // Sort the names, so things are deterministic and easy to + // predict. We are sorting primitive `&str`s here so we can + // use unstable sort. + cgu_contents.sort_unstable(); - (current_cgu_name, cgu_contents.join("--")) - }) - .collect(); + (*current_cgu_name, cgu_contents.join("--")) + }), + ); for cgu in codegen_units.iter_mut() { if let Some(new_cgu_name) = new_cgu_names.get(&cgu.name()) { @@ -511,7 +515,7 @@ fn compute_inlined_overlap<'tcx>(cgu1: &CodegenUnit<'tcx>, cgu2: &CodegenUnit<'t fn internalize_symbols<'tcx>( cx: &PartitioningCx<'_, 'tcx>, codegen_units: &mut [CodegenUnit<'tcx>], - internalization_candidates: FxHashSet>, + internalization_candidates: UnordSet>, ) { /// For symbol internalization, we need to know whether a symbol/mono-item /// is used from outside the codegen unit it is defined in. This type is @@ -522,7 +526,7 @@ fn internalize_symbols<'tcx>( MultipleCgus, } - let mut mono_item_placements = FxHashMap::default(); + let mut mono_item_placements = UnordMap::default(); let single_codegen_unit = codegen_units.len() == 1; if !single_codegen_unit { @@ -739,7 +743,7 @@ fn mono_item_linkage_and_visibility<'tcx>( (Linkage::External, vis) } -type CguNameCache = FxHashMap<(DefId, bool), Symbol>; +type CguNameCache = UnordMap<(DefId, bool), Symbol>; fn static_visibility<'tcx>( tcx: TyCtxt<'tcx>, @@ -932,7 +936,7 @@ fn debug_dump<'a, 'tcx: 'a>(tcx: TyCtxt<'tcx>, label: &str, cgus: &[CodegenUnit< // // Also, unreached inlined items won't be counted here. This is fine. - let mut inlined_items = FxHashSet::default(); + let mut inlined_items = UnordSet::default(); let mut root_items = 0; let mut unique_inlined_items = 0; @@ -1164,7 +1168,7 @@ fn collect_and_partition_mono_items(tcx: TyCtxt<'_>, (): ()) -> (&DefIdSet, &[Co } if tcx.sess.opts.unstable_opts.print_mono_items.is_some() { - let mut item_to_cgus: FxHashMap<_, Vec<_>> = Default::default(); + let mut item_to_cgus: UnordMap<_, Vec<_>> = Default::default(); for cgu in codegen_units { for (&mono_item, &data) in cgu.items() { @@ -1240,7 +1244,7 @@ fn dump_mono_items_stats<'tcx>( let mut file = BufWriter::new(file); // Gather instantiated mono items grouped by def_id - let mut items_per_def_id: FxHashMap<_, Vec<_>> = Default::default(); + let mut items_per_def_id: FxIndexMap<_, Vec<_>> = Default::default(); for cgu in codegen_units { cgu.items() .keys() From fd5777c4c5c8fbc54d33d15afb29479180c532be Mon Sep 17 00:00:00 2001 From: schvv31n Date: Tue, 4 Jun 2024 11:53:59 +0100 Subject: [PATCH 04/92] impl OsString::leak & PathBuf::leak --- library/std/src/ffi/os_str.rs | 19 +++++++++++++++++++ library/std/src/ffi/os_str/tests.rs | 7 +++++++ library/std/src/path.rs | 19 +++++++++++++++++++ library/std/src/path/tests.rs | 7 +++++++ library/std/src/sys/os_str/bytes.rs | 5 +++++ library/std/src/sys/os_str/wtf8.rs | 5 +++++ library/std/src/sys_common/wtf8.rs | 5 +++++ 7 files changed, 67 insertions(+) diff --git a/library/std/src/ffi/os_str.rs b/library/std/src/ffi/os_str.rs index 9dd3d7d3fa16..f6b9de26c1c4 100644 --- a/library/std/src/ffi/os_str.rs +++ b/library/std/src/ffi/os_str.rs @@ -533,6 +533,25 @@ impl OsString { unsafe { Box::from_raw(rw) } } + /// Consumes and leaks the `OsString`, returning a mutable reference to the contents, + /// `&'a mut OsStr`. + /// + /// The caller has free choice over the returned lifetime, including 'static. + /// Indeed, this function is ideally used for data that lives for the remainder of + /// the program’s life, as dropping the returned reference will cause a memory leak. + /// + /// It does not reallocate or shrink the `OsString`, so the leaked allocation may include + /// unused capacity that is not part of the returned slice. If you want to discard excess + /// capacity, call [`into_boxed_os_str`], and then [`Box::leak`] instead. + /// However, keep in mind that trimming the capacity may result in a reallocation and copy. + /// + /// [`into_boxed_os_str`]: Self::into_boxed_os_str + #[unstable(feature = "os_string_pathbuf_leak", issue = "125965")] + #[inline] + pub fn leak<'a>(self) -> &'a mut OsStr { + OsStr::from_inner_mut(self.inner.leak()) + } + /// Part of a hack to make PathBuf::push/pop more efficient. #[inline] pub(crate) fn as_mut_vec_for_path_buf(&mut self) -> &mut Vec { diff --git a/library/std/src/ffi/os_str/tests.rs b/library/std/src/ffi/os_str/tests.rs index b020e05eaab2..2379f0ba489e 100644 --- a/library/std/src/ffi/os_str/tests.rs +++ b/library/std/src/ffi/os_str/tests.rs @@ -23,6 +23,13 @@ fn test_os_string_clear() { assert_eq!(0, os_string.inner.as_inner().len()); } +#[test] +fn test_os_string_leak() { + let os_string = OsString::from("have a cake"); + let leaked = os_string.leak(); + assert_eq!(leaked.as_encoded_bytes(), b"have a cake"); +} + #[test] fn test_os_string_capacity() { let os_string = OsString::with_capacity(0); diff --git a/library/std/src/path.rs b/library/std/src/path.rs index f4e1e2a38c10..adbcdcd2e67e 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -1226,6 +1226,25 @@ impl PathBuf { self } + /// Consumes and leaks the `PathBuf`, returning a mutable reference to the contents, + /// `&'a mut Path`. + /// + /// The caller has free choice over the returned lifetime, including 'static. + /// Indeed, this function is ideally used for data that lives for the remainder of + /// the program’s life, as dropping the returned reference will cause a memory leak. + /// + /// It does not reallocate or shrink the `PathBuf`, so the leaked allocation may include + /// unused capacity that is not part of the returned slice. If you want to discard excess + /// capacity, call [`into_boxed_path`], and then [`Box::leak`] instead. + /// However, keep in mind that trimming the capacity may result in a reallocation and copy. + /// + /// [`into_boxed_path`]: Self::into_boxed_path + #[unstable(feature = "os_string_pathbuf_leak", issue = "125965")] + #[inline] + pub fn leak<'a>(self) -> &'a mut Path { + Path::from_inner_mut(self.inner.leak()) + } + /// Extends `self` with `path`. /// /// If `path` is absolute, it replaces the current path. diff --git a/library/std/src/path/tests.rs b/library/std/src/path/tests.rs index 2d8e50d1f88e..d29f895ba38f 100644 --- a/library/std/src/path/tests.rs +++ b/library/std/src/path/tests.rs @@ -126,6 +126,13 @@ fn into() { assert_eq!(static_cow_path, owned_cow_path); } +#[test] +fn test_pathbuf_leak() { + let buf = PathBuf::from("/have/a/cake".to_owned()); + let leaked = buf.leak(); + assert_eq!(leaked.as_os_str().as_encoded_bytes(), b"/have/a/cake"); +} + #[test] #[cfg(unix)] pub fn test_decompositions_unix() { diff --git a/library/std/src/sys/os_str/bytes.rs b/library/std/src/sys/os_str/bytes.rs index 18b969bca85a..f7c6b0877aa6 100644 --- a/library/std/src/sys/os_str/bytes.rs +++ b/library/std/src/sys/os_str/bytes.rs @@ -176,6 +176,11 @@ impl Buf { self.inner.extend_from_slice(&s.inner) } + #[inline] + pub fn leak<'a>(self) -> &'a mut Slice { + unsafe { mem::transmute(self.inner.leak()) } + } + #[inline] pub fn into_box(self) -> Box { unsafe { mem::transmute(self.inner.into_boxed_slice()) } diff --git a/library/std/src/sys/os_str/wtf8.rs b/library/std/src/sys/os_str/wtf8.rs index b3ceb55802dc..dfff4dd4fb02 100644 --- a/library/std/src/sys/os_str/wtf8.rs +++ b/library/std/src/sys/os_str/wtf8.rs @@ -138,6 +138,11 @@ impl Buf { self.inner.shrink_to(min_capacity) } + #[inline] + pub fn leak<'a>(self) -> &'a mut Slice { + unsafe { mem::transmute(self.inner.leak()) } + } + #[inline] pub fn into_box(self) -> Box { unsafe { mem::transmute(self.inner.into_box()) } diff --git a/library/std/src/sys_common/wtf8.rs b/library/std/src/sys_common/wtf8.rs index 38e15f9f5496..bb1e505285bf 100644 --- a/library/std/src/sys_common/wtf8.rs +++ b/library/std/src/sys_common/wtf8.rs @@ -325,6 +325,11 @@ impl Wtf8Buf { self.bytes.shrink_to(min_capacity) } + #[inline] + pub fn leak<'a>(self) -> &'a mut Wtf8 { + unsafe { Wtf8::from_mut_bytes_unchecked(self.bytes.leak()) } + } + /// Returns the number of bytes that this string buffer can hold without reallocating. #[inline] pub fn capacity(&self) -> usize { From a272f8ed44155e422d1e7f85f1cfe708ae0721b6 Mon Sep 17 00:00:00 2001 From: Chris Denton Date: Wed, 5 Jun 2024 13:53:46 +0000 Subject: [PATCH 05/92] Update `./x fmt` command --- library/std/src/sys/pal/windows/c/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/sys/pal/windows/c/README.md b/library/std/src/sys/pal/windows/c/README.md index d458e55efbcd..efefc5faba7a 100644 --- a/library/std/src/sys/pal/windows/c/README.md +++ b/library/std/src/sys/pal/windows/c/README.md @@ -3,7 +3,7 @@ be edited manually. To add bindings, edit `bindings.txt` then regenerate using the following command: - ./x run generate-windows-sys && ./x fmt library/std + ./x run generate-windows-sys && ./x fmt If you need to override generated functions or types then add them to `library/std/src/sys/pal/windows/c.rs`. From 35130d7233e939cc9a6fd8b72a4baee2eb59c3b2 Mon Sep 17 00:00:00 2001 From: r0cky Date: Wed, 5 Jun 2024 23:20:09 +0800 Subject: [PATCH 06/92] Detect pub structs never constructed and unused associated constants in traits --- compiler/rustc_passes/src/dead.rs | 121 ++++++++++++++---- .../item-collection/generic-impl.rs | 10 +- .../item-collection/overloaded-operators.rs | 24 ++-- tests/ui/coherence/re-rebalance-coherence.rs | 1 + .../defaults/repr-c-issue-82792.rs | 1 + .../generic_const_exprs/associated-consts.rs | 3 +- .../transparent-maybeunit-array-wrapper.rs | 1 + ...lone-debug-dead-code-in-the-same-struct.rs | 4 +- ...-debug-dead-code-in-the-same-struct.stderr | 16 +-- tests/ui/issues/issue-5708.rs | 1 + tests/ui/lint/dead-code/lint-dead-code-1.rs | 5 +- .../ui/lint/dead-code/lint-dead-code-1.stderr | 26 +++- tests/ui/lint/dead-code/unused-assoc-const.rs | 20 +++ .../lint/dead-code/unused-assoc-const.stderr | 16 +++ tests/ui/lint/dead-code/unused-pub-struct.rs | 48 +++++++ .../lint/dead-code/unused-pub-struct.stderr | 14 ++ ...re-clause-before-tuple-struct-body-0.fixed | 3 +- ...where-clause-before-tuple-struct-body-0.rs | 3 +- ...e-clause-before-tuple-struct-body-0.stderr | 10 +- tests/ui/pub/pub-ident-struct-4.fixed | 3 +- tests/ui/pub/pub-ident-struct-4.rs | 3 +- tests/ui/pub/pub-ident-struct-4.stderr | 6 +- tests/ui/regions/regions-issue-21422.rs | 1 + .../structs-enums/newtype-struct-with-dtor.rs | 2 + .../ui/structs-enums/uninstantiable-struct.rs | 3 +- .../ui/suggestions/derive-clone-for-eq.fixed | 1 + tests/ui/suggestions/derive-clone-for-eq.rs | 1 + .../ui/suggestions/derive-clone-for-eq.stderr | 4 +- .../ui/suggestions/option-content-move.fixed | 2 + tests/ui/suggestions/option-content-move.rs | 2 + .../ui/suggestions/option-content-move.stderr | 4 +- tests/ui/traits/object/generics.rs | 1 + 32 files changed, 272 insertions(+), 88 deletions(-) create mode 100644 tests/ui/lint/dead-code/unused-assoc-const.rs create mode 100644 tests/ui/lint/dead-code/unused-assoc-const.stderr create mode 100644 tests/ui/lint/dead-code/unused-pub-struct.rs create mode 100644 tests/ui/lint/dead-code/unused-pub-struct.stderr diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index 0049afff528c..4c2ea76ff651 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -15,7 +15,7 @@ use rustc_hir::{Node, PatKind, TyKind}; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::middle::privacy::Level; use rustc_middle::query::Providers; -use rustc_middle::ty::{self, TyCtxt}; +use rustc_middle::ty::{self, AssocItemContainer, TyCtxt}; use rustc_middle::{bug, span_bug}; use rustc_session::lint; use rustc_session::lint::builtin::DEAD_CODE; @@ -44,16 +44,63 @@ fn should_explore(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { ) } -fn ty_ref_to_pub_struct(tcx: TyCtxt<'_>, ty: &hir::Ty<'_>) -> bool { +struct Publicness { + ty_is_public: bool, + ty_and_all_fields_are_public: bool, +} + +impl Publicness { + fn new(ty_is_public: bool, ty_and_all_fields_are_public: bool) -> Self { + Self { ty_is_public, ty_and_all_fields_are_public } + } +} + +fn struct_all_fields_are_public(tcx: TyCtxt<'_>, id: DefId) -> bool { + // treat PhantomData and positional ZST as public, + // we don't want to lint types which only have them, + // cause it's a common way to use such types to check things like well-formedness + tcx.adt_def(id).all_fields().all(|field| { + let field_type = tcx.type_of(field.did).instantiate_identity(); + if field_type.is_phantom_data() { + return true; + } + let is_positional = field.name.as_str().starts_with(|c: char| c.is_ascii_digit()); + if is_positional + && tcx + .layout_of(tcx.param_env(field.did).and(field_type)) + .map_or(true, |layout| layout.is_zst()) + { + return true; + } + field.vis.is_public() + }) +} + +/// check struct and its fields are public or not, +/// for enum and union, just check they are public, +/// and doesn't solve types like &T for now, just skip them +fn ty_ref_to_pub_struct(tcx: TyCtxt<'_>, ty: &hir::Ty<'_>) -> Publicness { if let TyKind::Path(hir::QPath::Resolved(_, path)) = ty.kind && let Res::Def(def_kind, def_id) = path.res && def_id.is_local() - && matches!(def_kind, DefKind::Struct | DefKind::Enum | DefKind::Union) { - tcx.visibility(def_id).is_public() - } else { - true + return match def_kind { + DefKind::Enum | DefKind::Union => { + let ty_is_public = tcx.visibility(def_id).is_public(); + Publicness::new(ty_is_public, ty_is_public) + } + DefKind::Struct => { + let ty_is_public = tcx.visibility(def_id).is_public(); + Publicness::new( + ty_is_public, + ty_is_public && struct_all_fields_are_public(tcx, def_id), + ) + } + _ => Publicness::new(true, true), + }; } + + Publicness::new(true, true) } /// Determine if a work from the worklist is coming from the a `#[allow]` @@ -427,9 +474,11 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { { if matches!(trait_item.kind, hir::TraitItemKind::Fn(..)) && !ty_ref_to_pub_struct(self.tcx, impl_ref.self_ty) + .ty_and_all_fields_are_public { - // skip methods of private ty, - // they would be solved in `solve_rest_impl_items` + // skip impl-items of non pure pub ty, + // cause we don't know the ty is constructed or not, + // check these later in `solve_rest_impl_items` continue; } @@ -510,22 +559,21 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { && let Some(local_def_id) = def_id.as_local() && matches!(def_kind, DefKind::Struct | DefKind::Enum | DefKind::Union) { - if self.tcx.visibility(impl_item_id).is_public() { - // for the public method, we don't know the trait item is used or not, - // so we mark the method live if the self is used - return self.live_symbols.contains(&local_def_id); - } - if let Some(trait_item_id) = self.tcx.associated_item(impl_item_id).trait_item_def_id && let Some(local_id) = trait_item_id.as_local() { - // for the private method, we can know the trait item is used or not, + // for the local impl item, we can know the trait item is used or not, // so we mark the method live if the self is used and the trait item is used - return self.live_symbols.contains(&local_id) - && self.live_symbols.contains(&local_def_id); + self.live_symbols.contains(&local_id) && self.live_symbols.contains(&local_def_id) + } else { + // for the foreign method and inherent pub method, + // we don't know the trait item or the method is used or not, + // so we mark the method live if the self is used + self.live_symbols.contains(&local_def_id) } + } else { + false } - false } } @@ -746,7 +794,9 @@ fn check_item<'tcx>( .iter() .filter_map(|def_id| def_id.as_local()); - let ty_is_pub = ty_ref_to_pub_struct(tcx, tcx.hir().item(id).expect_impl().self_ty); + let self_ty = tcx.hir().item(id).expect_impl().self_ty; + let Publicness { ty_is_public, ty_and_all_fields_are_public } = + ty_ref_to_pub_struct(tcx, self_ty); // And we access the Map here to get HirId from LocalDefId for local_def_id in local_def_ids { @@ -762,18 +812,20 @@ fn check_item<'tcx>( // for trait impl blocks, // mark the method live if the self_ty is public, // or the method is public and may construct self - if of_trait - && (!matches!(tcx.def_kind(local_def_id), DefKind::AssocFn) - || tcx.visibility(local_def_id).is_public() - && (ty_is_pub || may_construct_self)) + if of_trait && matches!(tcx.def_kind(local_def_id), DefKind::AssocTy) + || tcx.visibility(local_def_id).is_public() + && (ty_and_all_fields_are_public || may_construct_self) { + // if the impl item is public, + // and the ty may be constructed or can be constructed in foreign crates, + // mark the impl item live worklist.push((local_def_id, ComesFromAllowExpect::No)); } else if let Some(comes_from_allow) = has_allow_dead_code_or_lang_attr(tcx, local_def_id) { worklist.push((local_def_id, comes_from_allow)); - } else if of_trait { - // private method || public method not constructs self + } else if of_trait || tcx.visibility(local_def_id).is_public() && ty_is_public { + // private impl items of traits || public impl items not constructs self unsolved_impl_items.push((id, local_def_id)); } } @@ -840,6 +892,14 @@ fn create_and_seed_worklist( effective_vis .is_public_at_level(Level::Reachable) .then_some(id) + .filter(|&id| + // checks impls, impl-items and pub structs with all public fields later + match tcx.def_kind(id) { + DefKind::Impl { .. } => false, + DefKind::AssocConst | DefKind::AssocFn => !matches!(tcx.associated_item(id).container, AssocItemContainer::ImplContainer), + DefKind::Struct => struct_all_fields_are_public(tcx, id.to_def_id()), + _ => true + }) .map(|id| (id, ComesFromAllowExpect::No)) }) // Seed entry point @@ -1112,10 +1172,15 @@ fn check_mod_deathness(tcx: TyCtxt<'_>, module: LocalModDefId) { || (def_kind == DefKind::Trait && live_symbols.contains(&item.owner_id.def_id)) { for &def_id in tcx.associated_item_def_ids(item.owner_id.def_id) { - // We have diagnosed unused methods in traits + // We have diagnosed unused assoc consts and fns in traits if matches!(def_kind, DefKind::Impl { of_trait: true }) - && tcx.def_kind(def_id) == DefKind::AssocFn - || def_kind == DefKind::Trait && tcx.def_kind(def_id) != DefKind::AssocFn + && matches!(tcx.def_kind(def_id), DefKind::AssocConst | DefKind::AssocFn) + // skip unused public inherent methods, + // cause we have diagnosed unconstructed struct + || matches!(def_kind, DefKind::Impl { of_trait: false }) + && tcx.visibility(def_id).is_public() + && ty_ref_to_pub_struct(tcx, tcx.hir().item(item).expect_impl().self_ty).ty_is_public + || def_kind == DefKind::Trait && tcx.def_kind(def_id) == DefKind::AssocTy { continue; } diff --git a/tests/codegen-units/item-collection/generic-impl.rs b/tests/codegen-units/item-collection/generic-impl.rs index 23d09e0d8af3..b4cd99272b15 100644 --- a/tests/codegen-units/item-collection/generic-impl.rs +++ b/tests/codegen-units/item-collection/generic-impl.rs @@ -22,16 +22,16 @@ impl Struct { } } -pub struct LifeTimeOnly<'a> { +pub struct _LifeTimeOnly<'a> { _a: &'a u32, } -impl<'a> LifeTimeOnly<'a> { - //~ MONO_ITEM fn LifeTimeOnly::<'_>::foo +impl<'a> _LifeTimeOnly<'a> { + //~ MONO_ITEM fn _LifeTimeOnly::<'_>::foo pub fn foo(&self) {} - //~ MONO_ITEM fn LifeTimeOnly::<'_>::bar + //~ MONO_ITEM fn _LifeTimeOnly::<'_>::bar pub fn bar(&'a self) {} - //~ MONO_ITEM fn LifeTimeOnly::<'_>::baz + //~ MONO_ITEM fn _LifeTimeOnly::<'_>::baz pub fn baz<'b>(&'b self) {} pub fn non_instantiated(&self) {} diff --git a/tests/codegen-units/item-collection/overloaded-operators.rs b/tests/codegen-units/item-collection/overloaded-operators.rs index 69b55695d3d3..e00e22dbab99 100644 --- a/tests/codegen-units/item-collection/overloaded-operators.rs +++ b/tests/codegen-units/item-collection/overloaded-operators.rs @@ -5,44 +5,44 @@ use std::ops::{Add, Deref, Index, IndexMut}; -pub struct Indexable { +pub struct _Indexable { data: [u8; 3], } -impl Index for Indexable { +impl Index for _Indexable { type Output = u8; - //~ MONO_ITEM fn >::index + //~ MONO_ITEM fn <_Indexable as std::ops::Index>::index fn index(&self, index: usize) -> &Self::Output { if index >= 3 { &self.data[0] } else { &self.data[index] } } } -impl IndexMut for Indexable { - //~ MONO_ITEM fn >::index_mut +impl IndexMut for _Indexable { + //~ MONO_ITEM fn <_Indexable as std::ops::IndexMut>::index_mut fn index_mut(&mut self, index: usize) -> &mut Self::Output { if index >= 3 { &mut self.data[0] } else { &mut self.data[index] } } } -//~ MONO_ITEM fn ::eq -//~ MONO_ITEM fn ::ne +//~ MONO_ITEM fn <_Equatable as std::cmp::PartialEq>::eq +//~ MONO_ITEM fn <_Equatable as std::cmp::PartialEq>::ne #[derive(PartialEq)] -pub struct Equatable(u32); +pub struct _Equatable(u32); -impl Add for Equatable { +impl Add for _Equatable { type Output = u32; - //~ MONO_ITEM fn >::add + //~ MONO_ITEM fn <_Equatable as std::ops::Add>::add fn add(self, rhs: u32) -> u32 { self.0 + rhs } } -impl Deref for Equatable { +impl Deref for _Equatable { type Target = u32; - //~ MONO_ITEM fn ::deref + //~ MONO_ITEM fn <_Equatable as std::ops::Deref>::deref fn deref(&self) -> &Self::Target { &self.0 } diff --git a/tests/ui/coherence/re-rebalance-coherence.rs b/tests/ui/coherence/re-rebalance-coherence.rs index 9c176d5b1b12..5383a634617f 100644 --- a/tests/ui/coherence/re-rebalance-coherence.rs +++ b/tests/ui/coherence/re-rebalance-coherence.rs @@ -4,6 +4,7 @@ extern crate re_rebalance_coherence_lib as lib; use lib::*; +#[allow(dead_code)] struct Oracle; impl Backend for Oracle {} impl<'a, T:'a, Tab> QueryFragment for BatchInsert<'a, T, Tab> {} diff --git a/tests/ui/const-generics/defaults/repr-c-issue-82792.rs b/tests/ui/const-generics/defaults/repr-c-issue-82792.rs index c23187598bce..4bf2fa761eae 100644 --- a/tests/ui/const-generics/defaults/repr-c-issue-82792.rs +++ b/tests/ui/const-generics/defaults/repr-c-issue-82792.rs @@ -2,6 +2,7 @@ //@ run-pass +#[allow(dead_code)] #[repr(C)] pub struct Loaf { head: [T; N], diff --git a/tests/ui/const-generics/generic_const_exprs/associated-consts.rs b/tests/ui/const-generics/generic_const_exprs/associated-consts.rs index 5d2198f50ad9..50a6102c605a 100644 --- a/tests/ui/const-generics/generic_const_exprs/associated-consts.rs +++ b/tests/ui/const-generics/generic_const_exprs/associated-consts.rs @@ -16,7 +16,8 @@ impl BlockCipher for BarCipher { const BLOCK_SIZE: usize = 32; } -pub struct Block(#[allow(dead_code)] C); +#[allow(dead_code)] +pub struct Block(C); pub fn test() where diff --git a/tests/ui/const-generics/transparent-maybeunit-array-wrapper.rs b/tests/ui/const-generics/transparent-maybeunit-array-wrapper.rs index 419d605d0c87..35c41ae46152 100644 --- a/tests/ui/const-generics/transparent-maybeunit-array-wrapper.rs +++ b/tests/ui/const-generics/transparent-maybeunit-array-wrapper.rs @@ -6,6 +6,7 @@ use std::mem::MaybeUninit; +#[allow(dead_code)] #[repr(transparent)] pub struct MaybeUninitWrapper(MaybeUninit<[u64; N]>); diff --git a/tests/ui/derives/clone-debug-dead-code-in-the-same-struct.rs b/tests/ui/derives/clone-debug-dead-code-in-the-same-struct.rs index 6ab1fb7b039b..885dacc727af 100644 --- a/tests/ui/derives/clone-debug-dead-code-in-the-same-struct.rs +++ b/tests/ui/derives/clone-debug-dead-code-in-the-same-struct.rs @@ -1,9 +1,9 @@ #![forbid(dead_code)] #[derive(Debug)] -pub struct Whatever { +pub struct Whatever { //~ ERROR struct `Whatever` is never constructed pub field0: (), - field1: (), //~ ERROR fields `field1`, `field2`, `field3`, and `field4` are never read + field1: (), field2: (), field3: (), field4: (), diff --git a/tests/ui/derives/clone-debug-dead-code-in-the-same-struct.stderr b/tests/ui/derives/clone-debug-dead-code-in-the-same-struct.stderr index e9b757b6bae7..e10d28ad03a4 100644 --- a/tests/ui/derives/clone-debug-dead-code-in-the-same-struct.stderr +++ b/tests/ui/derives/clone-debug-dead-code-in-the-same-struct.stderr @@ -1,19 +1,9 @@ -error: fields `field1`, `field2`, `field3`, and `field4` are never read - --> $DIR/clone-debug-dead-code-in-the-same-struct.rs:6:5 +error: struct `Whatever` is never constructed + --> $DIR/clone-debug-dead-code-in-the-same-struct.rs:4:12 | LL | pub struct Whatever { - | -------- fields in this struct -LL | pub field0: (), -LL | field1: (), - | ^^^^^^ -LL | field2: (), - | ^^^^^^ -LL | field3: (), - | ^^^^^^ -LL | field4: (), - | ^^^^^^ + | ^^^^^^^^ | - = note: `Whatever` has a derived impl for the trait `Debug`, but this is intentionally ignored during dead code analysis note: the lint level is defined here --> $DIR/clone-debug-dead-code-in-the-same-struct.rs:1:11 | diff --git a/tests/ui/issues/issue-5708.rs b/tests/ui/issues/issue-5708.rs index ce9ef78ffcd9..89ea9fbdcd8f 100644 --- a/tests/ui/issues/issue-5708.rs +++ b/tests/ui/issues/issue-5708.rs @@ -44,6 +44,7 @@ pub trait MyTrait { fn dummy(&self, t: T) -> T { panic!() } } +#[allow(dead_code)] pub struct MyContainer<'a, T:'a> { foos: Vec<&'a (dyn MyTrait+'a)> , } diff --git a/tests/ui/lint/dead-code/lint-dead-code-1.rs b/tests/ui/lint/dead-code/lint-dead-code-1.rs index ddcafedf7bc5..3386dfa47470 100644 --- a/tests/ui/lint/dead-code/lint-dead-code-1.rs +++ b/tests/ui/lint/dead-code/lint-dead-code-1.rs @@ -46,11 +46,10 @@ struct SemiUsedStruct; impl SemiUsedStruct { fn la_la_la() {} } -struct StructUsedAsField; +struct StructUsedAsField; //~ ERROR struct `StructUsedAsField` is never constructed pub struct StructUsedInEnum; struct StructUsedInGeneric; -pub struct PubStruct2 { - #[allow(dead_code)] +pub struct PubStruct2 { //~ ERROR struct `PubStruct2` is never constructed struct_used_as_field: *const StructUsedAsField } diff --git a/tests/ui/lint/dead-code/lint-dead-code-1.stderr b/tests/ui/lint/dead-code/lint-dead-code-1.stderr index eb728b5b9305..b0163df8855c 100644 --- a/tests/ui/lint/dead-code/lint-dead-code-1.stderr +++ b/tests/ui/lint/dead-code/lint-dead-code-1.stderr @@ -22,14 +22,26 @@ error: struct `PrivStruct` is never constructed LL | struct PrivStruct; | ^^^^^^^^^^ +error: struct `StructUsedAsField` is never constructed + --> $DIR/lint-dead-code-1.rs:49:8 + | +LL | struct StructUsedAsField; + | ^^^^^^^^^^^^^^^^^ + +error: struct `PubStruct2` is never constructed + --> $DIR/lint-dead-code-1.rs:52:12 + | +LL | pub struct PubStruct2 { + | ^^^^^^^^^^ + error: enum `priv_enum` is never used - --> $DIR/lint-dead-code-1.rs:64:6 + --> $DIR/lint-dead-code-1.rs:63:6 | LL | enum priv_enum { foo2, bar2 } | ^^^^^^^^^ error: variant `bar3` is never constructed - --> $DIR/lint-dead-code-1.rs:67:5 + --> $DIR/lint-dead-code-1.rs:66:5 | LL | enum used_enum { | --------- variant in this enum @@ -38,25 +50,25 @@ LL | bar3 | ^^^^ error: function `priv_fn` is never used - --> $DIR/lint-dead-code-1.rs:88:4 + --> $DIR/lint-dead-code-1.rs:87:4 | LL | fn priv_fn() { | ^^^^^^^ error: function `foo` is never used - --> $DIR/lint-dead-code-1.rs:93:4 + --> $DIR/lint-dead-code-1.rs:92:4 | LL | fn foo() { | ^^^ error: function `bar` is never used - --> $DIR/lint-dead-code-1.rs:98:4 + --> $DIR/lint-dead-code-1.rs:97:4 | LL | fn bar() { | ^^^ error: function `baz` is never used - --> $DIR/lint-dead-code-1.rs:102:4 + --> $DIR/lint-dead-code-1.rs:101:4 | LL | fn baz() -> impl Copy { | ^^^ @@ -67,5 +79,5 @@ error: struct `Bar` is never constructed LL | pub struct Bar; | ^^^ -error: aborting due to 10 previous errors +error: aborting due to 12 previous errors diff --git a/tests/ui/lint/dead-code/unused-assoc-const.rs b/tests/ui/lint/dead-code/unused-assoc-const.rs new file mode 100644 index 000000000000..36e8315ad360 --- /dev/null +++ b/tests/ui/lint/dead-code/unused-assoc-const.rs @@ -0,0 +1,20 @@ +#![deny(dead_code)] + +trait Trait { + const UNUSED_CONST: i32; //~ ERROR associated constant `UNUSED_CONST` is never used + const USED_CONST: i32; + + fn foo(&self) {} +} + +pub struct T(()); + +impl Trait for T { + const UNUSED_CONST: i32 = 0; + const USED_CONST: i32 = 1; +} + +fn main() { + T(()).foo(); + T::USED_CONST; +} diff --git a/tests/ui/lint/dead-code/unused-assoc-const.stderr b/tests/ui/lint/dead-code/unused-assoc-const.stderr new file mode 100644 index 000000000000..78296d706638 --- /dev/null +++ b/tests/ui/lint/dead-code/unused-assoc-const.stderr @@ -0,0 +1,16 @@ +error: associated constant `UNUSED_CONST` is never used + --> $DIR/unused-assoc-const.rs:4:11 + | +LL | trait Trait { + | ----- associated constant in this trait +LL | const UNUSED_CONST: i32; + | ^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/unused-assoc-const.rs:1:9 + | +LL | #![deny(dead_code)] + | ^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/lint/dead-code/unused-pub-struct.rs b/tests/ui/lint/dead-code/unused-pub-struct.rs new file mode 100644 index 000000000000..aaf4dd612de4 --- /dev/null +++ b/tests/ui/lint/dead-code/unused-pub-struct.rs @@ -0,0 +1,48 @@ +#![deny(dead_code)] + +pub struct NotLint1(()); +pub struct NotLint2(std::marker::PhantomData); + +pub struct NeverConstructed(i32); //~ ERROR struct `NeverConstructed` is never constructed + +impl NeverConstructed { + pub fn not_construct_self(&self) {} +} + +impl Clone for NeverConstructed { + fn clone(&self) -> NeverConstructed { + NeverConstructed(0) + } +} + +pub trait Trait { + fn not_construct_self(&self); +} + +impl Trait for NeverConstructed { + fn not_construct_self(&self) { + self.0; + } +} + +pub struct Constructed(i32); + +impl Constructed { + pub fn construct_self() -> Self { + Constructed(0) + } +} + +impl Clone for Constructed { + fn clone(&self) -> Constructed { + Constructed(0) + } +} + +impl Trait for Constructed { + fn not_construct_self(&self) { + self.0; + } +} + +fn main() {} diff --git a/tests/ui/lint/dead-code/unused-pub-struct.stderr b/tests/ui/lint/dead-code/unused-pub-struct.stderr new file mode 100644 index 000000000000..3667ddb97bd3 --- /dev/null +++ b/tests/ui/lint/dead-code/unused-pub-struct.stderr @@ -0,0 +1,14 @@ +error: struct `NeverConstructed` is never constructed + --> $DIR/unused-pub-struct.rs:6:12 + | +LL | pub struct NeverConstructed(i32); + | ^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/unused-pub-struct.rs:1:9 + | +LL | #![deny(dead_code)] + | ^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.fixed b/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.fixed index a851300a9828..e9c89807fa56 100644 --- a/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.fixed +++ b/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.fixed @@ -1,7 +1,8 @@ // Regression test for issues #100790 and #106439. //@ run-rustfix -pub struct Example(#[allow(dead_code)] usize) +#[allow(dead_code)] +pub struct Example(usize) where (): Sized; //~^^^ ERROR where clauses are not allowed before tuple struct bodies diff --git a/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.rs b/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.rs index 10f435859f15..3bd0f51ec2cb 100644 --- a/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.rs +++ b/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.rs @@ -1,10 +1,11 @@ // Regression test for issues #100790 and #106439. //@ run-rustfix +#[allow(dead_code)] pub struct Example where (): Sized, -(#[allow(dead_code)] usize); +(usize); //~^^^ ERROR where clauses are not allowed before tuple struct bodies struct _Demo diff --git a/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.stderr b/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.stderr index ddbf237e8662..77eafa6bea33 100644 --- a/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.stderr +++ b/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.stderr @@ -1,23 +1,23 @@ error: where clauses are not allowed before tuple struct bodies - --> $DIR/recover-where-clause-before-tuple-struct-body-0.rs:5:1 + --> $DIR/recover-where-clause-before-tuple-struct-body-0.rs:6:1 | LL | pub struct Example | ------- while parsing this tuple struct LL | / where LL | | (): Sized, | |______________^ unexpected where clause -LL | (#[allow(dead_code)] usize); - | --------------------------- the struct body +LL | (usize); + | ------- the struct body | help: move the body before the where clause | -LL ~ pub struct Example(#[allow(dead_code)] usize) +LL ~ pub struct Example(usize) LL | where LL ~ (): Sized; | error: where clauses are not allowed before tuple struct bodies - --> $DIR/recover-where-clause-before-tuple-struct-body-0.rs:11:1 + --> $DIR/recover-where-clause-before-tuple-struct-body-0.rs:12:1 | LL | struct _Demo | ----- while parsing this tuple struct diff --git a/tests/ui/pub/pub-ident-struct-4.fixed b/tests/ui/pub/pub-ident-struct-4.fixed index 5fedbb724374..a62ece43eced 100644 --- a/tests/ui/pub/pub-ident-struct-4.fixed +++ b/tests/ui/pub/pub-ident-struct-4.fixed @@ -1,6 +1,7 @@ //@ run-rustfix -pub struct T(#[allow(dead_code)] String); +#[allow(dead_code)] +pub struct T(String); //~^ ERROR missing `struct` for struct definition fn main() {} diff --git a/tests/ui/pub/pub-ident-struct-4.rs b/tests/ui/pub/pub-ident-struct-4.rs index 5c721c25a781..0d56a31beaf1 100644 --- a/tests/ui/pub/pub-ident-struct-4.rs +++ b/tests/ui/pub/pub-ident-struct-4.rs @@ -1,6 +1,7 @@ //@ run-rustfix -pub T(#[allow(dead_code)] String); +#[allow(dead_code)] +pub T(String); //~^ ERROR missing `struct` for struct definition fn main() {} diff --git a/tests/ui/pub/pub-ident-struct-4.stderr b/tests/ui/pub/pub-ident-struct-4.stderr index 5fbb02c8673c..ec1367832113 100644 --- a/tests/ui/pub/pub-ident-struct-4.stderr +++ b/tests/ui/pub/pub-ident-struct-4.stderr @@ -1,12 +1,12 @@ error: missing `struct` for struct definition - --> $DIR/pub-ident-struct-4.rs:3:4 + --> $DIR/pub-ident-struct-4.rs:4:4 | -LL | pub T(#[allow(dead_code)] String); +LL | pub T(String); | ^ | help: add `struct` here to parse `T` as a public struct | -LL | pub struct T(#[allow(dead_code)] String); +LL | pub struct T(String); | ++++++ error: aborting due to 1 previous error diff --git a/tests/ui/regions/regions-issue-21422.rs b/tests/ui/regions/regions-issue-21422.rs index 54beed9b3ac2..67852a6f5de0 100644 --- a/tests/ui/regions/regions-issue-21422.rs +++ b/tests/ui/regions/regions-issue-21422.rs @@ -5,6 +5,7 @@ //@ pretty-expanded FIXME #23616 +#[allow(dead_code)] pub struct P<'a> { _ptr: *const &'a u8, } diff --git a/tests/ui/structs-enums/newtype-struct-with-dtor.rs b/tests/ui/structs-enums/newtype-struct-with-dtor.rs index 19672e41c9a3..16439a7fedd3 100644 --- a/tests/ui/structs-enums/newtype-struct-with-dtor.rs +++ b/tests/ui/structs-enums/newtype-struct-with-dtor.rs @@ -3,8 +3,10 @@ #![allow(unused_variables)] //@ pretty-expanded FIXME #23616 +#[allow(dead_code)] pub struct Fd(u32); +#[allow(dead_code)] fn foo(a: u32) {} impl Drop for Fd { diff --git a/tests/ui/structs-enums/uninstantiable-struct.rs b/tests/ui/structs-enums/uninstantiable-struct.rs index 97bc7d8414e7..1074dbcd6e6b 100644 --- a/tests/ui/structs-enums/uninstantiable-struct.rs +++ b/tests/ui/structs-enums/uninstantiable-struct.rs @@ -1,4 +1,5 @@ //@ run-pass -pub struct Z(#[allow(dead_code)] &'static Z); +#[allow(dead_code)] +pub struct Z(&'static Z); pub fn main() {} diff --git a/tests/ui/suggestions/derive-clone-for-eq.fixed b/tests/ui/suggestions/derive-clone-for-eq.fixed index 4dc362f94787..cf800c6e47d8 100644 --- a/tests/ui/suggestions/derive-clone-for-eq.fixed +++ b/tests/ui/suggestions/derive-clone-for-eq.fixed @@ -1,6 +1,7 @@ //@ run-rustfix // https://github.com/rust-lang/rust/issues/79076 +#[allow(dead_code)] #[derive(Clone, Eq)] //~ ERROR [E0277] pub struct Struct(T); diff --git a/tests/ui/suggestions/derive-clone-for-eq.rs b/tests/ui/suggestions/derive-clone-for-eq.rs index b3635000f165..84736426bac0 100644 --- a/tests/ui/suggestions/derive-clone-for-eq.rs +++ b/tests/ui/suggestions/derive-clone-for-eq.rs @@ -1,6 +1,7 @@ //@ run-rustfix // https://github.com/rust-lang/rust/issues/79076 +#[allow(dead_code)] #[derive(Clone, Eq)] //~ ERROR [E0277] pub struct Struct(T); diff --git a/tests/ui/suggestions/derive-clone-for-eq.stderr b/tests/ui/suggestions/derive-clone-for-eq.stderr index 6fae6e1316df..54670fbffcfb 100644 --- a/tests/ui/suggestions/derive-clone-for-eq.stderr +++ b/tests/ui/suggestions/derive-clone-for-eq.stderr @@ -1,11 +1,11 @@ error[E0277]: the trait bound `T: Clone` is not satisfied - --> $DIR/derive-clone-for-eq.rs:4:17 + --> $DIR/derive-clone-for-eq.rs:5:17 | LL | #[derive(Clone, Eq)] | ^^ the trait `Clone` is not implemented for `T`, which is required by `Struct: PartialEq` | note: required for `Struct` to implement `PartialEq` - --> $DIR/derive-clone-for-eq.rs:7:19 + --> $DIR/derive-clone-for-eq.rs:8:19 | LL | impl PartialEq for Struct | ----- ^^^^^^^^^^^^ ^^^^^^^^^ diff --git a/tests/ui/suggestions/option-content-move.fixed b/tests/ui/suggestions/option-content-move.fixed index 4a5a9483c20c..ef07d55871e7 100644 --- a/tests/ui/suggestions/option-content-move.fixed +++ b/tests/ui/suggestions/option-content-move.fixed @@ -1,4 +1,5 @@ //@ run-rustfix +#[allow(dead_code)] pub struct LipogramCorpora { selections: Vec<(char, Option)>, } @@ -17,6 +18,7 @@ impl LipogramCorpora { } } +#[allow(dead_code)] pub struct LipogramCorpora2 { selections: Vec<(char, Result)>, } diff --git a/tests/ui/suggestions/option-content-move.rs b/tests/ui/suggestions/option-content-move.rs index 90d05c743997..5be6358fd6a5 100644 --- a/tests/ui/suggestions/option-content-move.rs +++ b/tests/ui/suggestions/option-content-move.rs @@ -1,4 +1,5 @@ //@ run-rustfix +#[allow(dead_code)] pub struct LipogramCorpora { selections: Vec<(char, Option)>, } @@ -17,6 +18,7 @@ impl LipogramCorpora { } } +#[allow(dead_code)] pub struct LipogramCorpora2 { selections: Vec<(char, Result)>, } diff --git a/tests/ui/suggestions/option-content-move.stderr b/tests/ui/suggestions/option-content-move.stderr index a382a04344ae..b4ec5b180d21 100644 --- a/tests/ui/suggestions/option-content-move.stderr +++ b/tests/ui/suggestions/option-content-move.stderr @@ -1,5 +1,5 @@ error[E0507]: cannot move out of `selection.1` which is behind a shared reference - --> $DIR/option-content-move.rs:10:20 + --> $DIR/option-content-move.rs:11:20 | LL | if selection.1.unwrap().contains(selection.0) { | ^^^^^^^^^^^ -------- `selection.1` moved due to this method call @@ -19,7 +19,7 @@ LL | if selection.1.clone().unwrap().contains(selection.0) { | ++++++++ error[E0507]: cannot move out of `selection.1` which is behind a shared reference - --> $DIR/option-content-move.rs:28:20 + --> $DIR/option-content-move.rs:30:20 | LL | if selection.1.unwrap().contains(selection.0) { | ^^^^^^^^^^^ -------- `selection.1` moved due to this method call diff --git a/tests/ui/traits/object/generics.rs b/tests/ui/traits/object/generics.rs index 462b0bc5bb77..0ae562c0d30a 100644 --- a/tests/ui/traits/object/generics.rs +++ b/tests/ui/traits/object/generics.rs @@ -7,6 +7,7 @@ pub trait Trait2 { fn doit(&self) -> A; } +#[allow(dead_code)] pub struct Impl { m1: marker::PhantomData<(A1,A2,A3)>, /* From c0e25431478a0b621310b514022ccfae3dee1974 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Wed, 5 Jun 2024 13:20:17 -0700 Subject: [PATCH 07/92] Simplify the rayon calls in the installer Rayon's `try_for_each` makes the `CombinedEncoder` a lot simpler. --- src/tools/rust-installer/src/compression.rs | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/tools/rust-installer/src/compression.rs b/src/tools/rust-installer/src/compression.rs index 4e840dbfbb44..40c7c597e9b4 100644 --- a/src/tools/rust-installer/src/compression.rs +++ b/src/tools/rust-installer/src/compression.rs @@ -214,22 +214,16 @@ impl Write for CombinedEncoder { } fn write_all(&mut self, buf: &[u8]) -> std::io::Result<()> { - self.encoders - .par_iter_mut() - .map(|w| w.write_all(buf)) - .collect::>>()?; - Ok(()) + self.encoders.par_iter_mut().try_for_each(|w| w.write_all(buf)) } fn flush(&mut self) -> std::io::Result<()> { - self.encoders.par_iter_mut().map(|w| w.flush()).collect::>>()?; - Ok(()) + self.encoders.par_iter_mut().try_for_each(Write::flush) } } impl Encoder for CombinedEncoder { fn finish(self: Box) -> Result<(), Error> { - self.encoders.into_par_iter().map(|e| e.finish()).collect::, Error>>()?; - Ok(()) + self.encoders.into_par_iter().try_for_each(Encoder::finish) } } From 95b4c07ef897974a8978b4a43d0d98103a824f58 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 3 Jun 2024 15:47:46 +1000 Subject: [PATCH 08/92] Reduce `pub` exposure. --- .../rustc_parse/src/parser/diagnostics.rs | 26 ++++++----- compiler/rustc_parse/src/parser/expr.rs | 8 +++- compiler/rustc_parse/src/parser/item.rs | 4 +- compiler/rustc_parse/src/parser/mod.rs | 43 ++++++++++--------- compiler/rustc_parse/src/parser/path.rs | 2 +- compiler/rustc_parse/src/parser/stmt.rs | 2 +- compiler/rustc_parse/src/parser/ty.rs | 2 +- compiler/rustc_parse/src/validate_attr.rs | 6 +-- 8 files changed, 51 insertions(+), 42 deletions(-) diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 9677eea06044..2bb6fb53869b 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -130,14 +130,14 @@ pub enum AttemptLocalParseRecovery { } impl AttemptLocalParseRecovery { - pub fn yes(&self) -> bool { + pub(super) fn yes(&self) -> bool { match self { AttemptLocalParseRecovery::Yes => true, AttemptLocalParseRecovery::No => false, } } - pub fn no(&self) -> bool { + pub(super) fn no(&self) -> bool { match self { AttemptLocalParseRecovery::Yes => false, AttemptLocalParseRecovery::No => true, @@ -891,7 +891,7 @@ impl<'a> Parser<'a> { } } - pub fn maybe_suggest_struct_literal( + pub(super) fn maybe_suggest_struct_literal( &mut self, lo: Span, s: BlockCheckMode, @@ -2459,7 +2459,7 @@ impl<'a> Parser<'a> { /// Handle encountering a symbol in a generic argument list that is not a `,` or `>`. In this /// case, we emit an error and try to suggest enclosing a const argument in braces if it looks /// like the user has forgotten them. - pub fn handle_ambiguous_unbraced_const_arg( + pub(super) fn handle_ambiguous_unbraced_const_arg( &mut self, args: &mut ThinVec, ) -> PResult<'a, bool> { @@ -2500,7 +2500,7 @@ impl<'a> Parser<'a> { /// - Single-segment paths (i.e. standalone generic const parameters). /// All other expressions that can be parsed will emit an error suggesting the expression be /// wrapped in braces. - pub fn handle_unambiguous_unbraced_const_arg(&mut self) -> PResult<'a, P> { + pub(super) fn handle_unambiguous_unbraced_const_arg(&mut self) -> PResult<'a, P> { let start = self.token.span; let expr = self.parse_expr_res(Restrictions::CONST_EXPR, None).map_err(|mut err| { err.span_label( @@ -2559,7 +2559,7 @@ impl<'a> Parser<'a> { Some(GenericArg::Const(AnonConst { id: ast::DUMMY_NODE_ID, value })) } - pub fn recover_const_param_declaration( + pub(super) fn recover_const_param_declaration( &mut self, ty_generics: Option<&Generics>, ) -> PResult<'a, Option> { @@ -2589,7 +2589,11 @@ impl<'a> Parser<'a> { /// When encountering code like `foo::< bar + 3 >` or `foo::< bar - baz >` we suggest /// `foo::<{ bar + 3 }>` and `foo::<{ bar - baz }>`, respectively. We only provide a suggestion /// if we think that the resulting expression would be well formed. - pub fn recover_const_arg(&mut self, start: Span, mut err: Diag<'a>) -> PResult<'a, GenericArg> { + pub(super) fn recover_const_arg( + &mut self, + start: Span, + mut err: Diag<'a>, + ) -> PResult<'a, GenericArg> { let is_op_or_dot = AssocOp::from_token(&self.token) .and_then(|op| { if let AssocOp::Greater @@ -2690,7 +2694,7 @@ impl<'a> Parser<'a> { } /// Creates a dummy const argument, and reports that the expression must be enclosed in braces - pub fn dummy_const_arg_needs_braces(&self, mut err: Diag<'a>, span: Span) -> GenericArg { + pub(super) fn dummy_const_arg_needs_braces(&self, mut err: Diag<'a>, span: Span) -> GenericArg { err.multipart_suggestion( "expressions must be enclosed in braces to be used as const generic \ arguments", @@ -2961,7 +2965,7 @@ impl<'a> Parser<'a> { /// * `=====` /// * `<<<<<` /// - pub fn is_vcs_conflict_marker( + pub(super) fn is_vcs_conflict_marker( &mut self, long_kind: &TokenKind, short_kind: &TokenKind, @@ -2981,14 +2985,14 @@ impl<'a> Parser<'a> { None } - pub fn recover_vcs_conflict_marker(&mut self) { + pub(super) fn recover_vcs_conflict_marker(&mut self) { if let Err(err) = self.err_vcs_conflict_marker() { err.emit(); FatalError.raise(); } } - pub fn err_vcs_conflict_marker(&mut self) -> PResult<'a, ()> { + pub(crate) fn err_vcs_conflict_marker(&mut self) -> PResult<'a, ()> { let Some(start) = self.conflict_marker(&TokenKind::BinOp(token::Shl), &TokenKind::Lt) else { return Ok(()); diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 1b99bc015b60..5a39248e49a3 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -445,7 +445,7 @@ impl<'a> Parser<'a> { /// The method does not advance the current token. /// /// Also performs recovery for `and` / `or` which are mistaken for `&&` and `||` respectively. - pub fn check_assoc_op(&self) -> Option> { + pub(super) fn check_assoc_op(&self) -> Option> { let (op, span) = match (AssocOp::from_token(&self.token), self.token.ident()) { // When parsing const expressions, stop parsing when encountering `>`. ( @@ -1022,7 +1022,11 @@ impl<'a> Parser<'a> { } } - pub fn parse_dot_suffix_expr(&mut self, lo: Span, base: P) -> PResult<'a, P> { + pub(super) fn parse_dot_suffix_expr( + &mut self, + lo: Span, + base: P, + ) -> PResult<'a, P> { // At this point we've consumed something like `expr.` and `self.token` holds the token // after the dot. match self.token.uninterpolate().kind { diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 53757c38e8b0..fcafa2432155 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -2256,7 +2256,7 @@ pub(crate) struct FnParseMode { /// to true. /// * The span is from Edition 2015. In particular, you can get a /// 2015 span inside a 2021 crate using macros. - pub req_name: ReqName, + pub(super) req_name: ReqName, /// If this flag is set to `true`, then plain, semicolon-terminated function /// prototypes are not allowed here. /// @@ -2275,7 +2275,7 @@ pub(crate) struct FnParseMode { /// This field should only be set to false if the item is inside of a trait /// definition or extern block. Within an impl block or a module, it should /// always be set to true. - pub req_body: bool, + pub(super) req_body: bool, } /// Parsing of functions and methods. diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index bab8b6c06ebe..d00dbe3a4a67 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -11,14 +11,13 @@ mod stmt; mod ty; use crate::lexer::UnmatchedDelim; -pub use attr_wrapper::AttrWrapper; +use attr_wrapper::AttrWrapper; pub use diagnostics::AttemptLocalParseRecovery; pub(crate) use expr::ForbiddenLetReason; pub(crate) use item::FnParseMode; pub use pat::{CommaRecoveryMode, RecoverColon, RecoverComma}; -pub use path::PathStyle; +use path::PathStyle; -use core::fmt; use rustc_ast::ptr::P; use rustc_ast::token::{self, Delimiter, IdentIsRaw, Nonterminal, Token, TokenKind}; use rustc_ast::tokenstream::{AttributesData, DelimSpacing, DelimSpan, Spacing}; @@ -37,7 +36,7 @@ use rustc_session::parse::ParseSess; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{Span, DUMMY_SP}; use std::ops::Range; -use std::{mem, slice}; +use std::{fmt, mem, slice}; use thin_vec::ThinVec; use tracing::debug; @@ -146,7 +145,7 @@ pub struct Parser<'a> { /// The current token. pub token: Token, /// The spacing for the current token. - pub token_spacing: Spacing, + token_spacing: Spacing, /// The previous token. pub prev_token: Token, pub capture_cfg: bool, @@ -187,7 +186,7 @@ pub struct Parser<'a> { current_closure: Option, /// Whether the parser is allowed to do recovery. /// This is disabled when parsing macro arguments, see #103534 - pub recovery: Recovery, + recovery: Recovery, } // This type is used a lot, e.g. it's cloned when matching many declarative macro rules with nonterminals. Make sure @@ -197,10 +196,10 @@ rustc_data_structures::static_assert_size!(Parser<'_>, 264); /// Stores span information about a closure. #[derive(Clone, Debug)] -pub struct ClosureSpans { - pub whole_closure: Span, - pub closing_pipe: Span, - pub body: Span, +struct ClosureSpans { + whole_closure: Span, + closing_pipe: Span, + body: Span, } /// Indicates a range of tokens that should be replaced by @@ -220,13 +219,13 @@ pub struct ClosureSpans { /// the first macro inner attribute to invoke a proc-macro). /// When create a `TokenStream`, the inner attributes get inserted /// into the proper place in the token stream. -pub type ReplaceRange = (Range, Vec<(FlatToken, Spacing)>); +type ReplaceRange = (Range, Vec<(FlatToken, Spacing)>); /// Controls how we capture tokens. Capturing can be expensive, /// so we try to avoid performing capturing in cases where /// we will never need an `AttrTokenStream`. #[derive(Copy, Clone, Debug)] -pub enum Capturing { +enum Capturing { /// We aren't performing any capturing - this is the default mode. No, /// We are capturing tokens @@ -374,13 +373,13 @@ pub enum FollowedByType { } #[derive(Copy, Clone, Debug)] -pub enum Trailing { +enum Trailing { No, Yes, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum TokenDescription { +pub(super) enum TokenDescription { ReservedIdentifier, Keyword, ReservedKeyword, @@ -388,7 +387,7 @@ pub enum TokenDescription { } impl TokenDescription { - pub fn from_token(token: &Token) -> Option { + pub(super) fn from_token(token: &Token) -> Option { match token.kind { _ if token.is_special_ident() => Some(TokenDescription::ReservedIdentifier), _ if token.is_used_keyword() => Some(TokenDescription::Keyword), @@ -502,7 +501,7 @@ impl<'a> Parser<'a> { /// Expect next token to be edible or inedible token. If edible, /// then consume it; if inedible, then return without consuming /// anything. Signal a fatal error if next token is unexpected. - pub fn expect_one_of( + fn expect_one_of( &mut self, edible: &[TokenKind], inedible: &[TokenKind], @@ -572,7 +571,7 @@ impl<'a> Parser<'a> { /// the main purpose of this function is to reduce the cluttering of the suggestions list /// which using the normal eat method could introduce in some cases. #[inline] - pub fn eat_noexpect(&mut self, tok: &TokenKind) -> bool { + fn eat_noexpect(&mut self, tok: &TokenKind) -> bool { let is_present = self.check_noexpect(tok); if is_present { self.bump() @@ -1515,7 +1514,7 @@ impl<'a> Parser<'a> { } } - pub fn collect_tokens_no_attrs( + fn collect_tokens_no_attrs( &mut self, f: impl FnOnce(&mut Self) -> PResult<'a, R>, ) -> PResult<'a, R> { @@ -1536,8 +1535,10 @@ impl<'a> Parser<'a> { }) } - // debug view of the parser's token stream, up to `{lookahead}` tokens - pub fn debug_lookahead(&self, lookahead: usize) -> impl fmt::Debug + '_ { + // Debug view of the parser's token stream, up to `{lookahead}` tokens. + // Only used when debugging. + #[allow(unused)] + pub(crate) fn debug_lookahead(&self, lookahead: usize) -> impl fmt::Debug + '_ { struct DebugParser<'dbg> { parser: &'dbg Parser<'dbg>, lookahead: usize, @@ -1613,7 +1614,7 @@ pub(crate) fn make_unclosed_delims_error( /// is then 'parsed' to build up an `AttrTokenStream` with nested /// `AttrTokenTree::Delimited` tokens. #[derive(Debug, Clone)] -pub enum FlatToken { +enum FlatToken { /// A token - this holds both delimiter (e.g. '{' and '}') /// and non-delimiter tokens Token(Token), diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs index fcedc1a4af3a..9beecd9849fb 100644 --- a/compiler/rustc_parse/src/parser/path.rs +++ b/compiler/rustc_parse/src/parser/path.rs @@ -20,7 +20,7 @@ use tracing::debug; /// Specifies how to parse a path. #[derive(Copy, Clone, PartialEq)] -pub enum PathStyle { +pub(super) enum PathStyle { /// In some contexts, notably in expressions, paths with generic arguments are ambiguous /// with something else. For example, in expressions `segment < ....` can be interpreted /// as a comparison and `segment ( ....` can be interpreted as a function call. diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index be539d15386f..104aae9b257e 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -31,7 +31,7 @@ impl<'a> Parser<'a> { /// Parses a statement. This stops just before trailing semicolons on everything but items. /// e.g., a `StmtKind::Semi` parses to a `StmtKind::Expr`, leaving the trailing `;` unconsumed. // Public for rustfmt usage. - pub fn parse_stmt(&mut self, force_collect: ForceCollect) -> PResult<'a, Option> { + pub(super) fn parse_stmt(&mut self, force_collect: ForceCollect) -> PResult<'a, Option> { Ok(self.parse_stmt_without_recovery(false, force_collect).unwrap_or_else(|e| { e.emit(); self.recover_stmt_(SemiColonMode::Break, BlockMode::Ignore); diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 2033f3878875..5bed0317e5eb 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -127,7 +127,7 @@ impl<'a> Parser<'a> { /// Parse a type suitable for a field definition. /// The difference from `parse_ty` is that this version /// allows anonymous structs and unions. - pub fn parse_ty_for_field_def(&mut self) -> PResult<'a, P> { + pub(super) fn parse_ty_for_field_def(&mut self) -> PResult<'a, P> { if self.can_begin_anon_struct_or_union() { self.parse_anon_struct_or_union() } else { diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs index b91ef1ae1f32..e897611212f9 100644 --- a/compiler/rustc_parse/src/validate_attr.rs +++ b/compiler/rustc_parse/src/validate_attr.rs @@ -103,7 +103,7 @@ pub fn parse_meta<'a>(psess: &'a ParseSess, attr: &Attribute) -> PResult<'a, Met }) } -pub fn check_meta_bad_delim(psess: &ParseSess, span: DelimSpan, delim: Delimiter) { +fn check_meta_bad_delim(psess: &ParseSess, span: DelimSpan, delim: Delimiter) { if let Delimiter::Parenthesis = delim { return; } @@ -113,7 +113,7 @@ pub fn check_meta_bad_delim(psess: &ParseSess, span: DelimSpan, delim: Delimiter }); } -pub fn check_cfg_attr_bad_delim(psess: &ParseSess, span: DelimSpan, delim: Delimiter) { +pub(super) fn check_cfg_attr_bad_delim(psess: &ParseSess, span: DelimSpan, delim: Delimiter) { if let Delimiter::Parenthesis = delim { return; } @@ -133,7 +133,7 @@ fn is_attr_template_compatible(template: &AttributeTemplate, meta: &ast::MetaIte } } -pub fn check_builtin_attribute( +fn check_builtin_attribute( psess: &ParseSess, attr: &Attribute, name: Symbol, From 13f2bc9a651abe9cb1711293be7eae8d48169300 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 4 Jun 2024 13:20:43 +1000 Subject: [PATCH 09/92] Specialize `assert_pred`. It has only two uses, and both use `matches_codepattern`. So just change it to `assert_matches_codepattern`. --- .../rustc_parse/src/parser/mut_visit/tests.rs | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_parse/src/parser/mut_visit/tests.rs b/compiler/rustc_parse/src/parser/mut_visit/tests.rs index b3cb28af657e..677bcdf7fcdf 100644 --- a/compiler/rustc_parse/src/parser/mut_visit/tests.rs +++ b/compiler/rustc_parse/src/parser/mut_visit/tests.rs @@ -21,14 +21,12 @@ impl MutVisitor for ToZzIdentMutVisitor { } } -// Maybe add to `expand.rs`. -macro_rules! assert_pred { - ($pred:expr, $predname:expr, $a:expr , $b:expr) => {{ - let pred_val = $pred; +macro_rules! assert_matches_codepattern { + ($a:expr , $b:expr) => {{ let a_val = $a; let b_val = $b; - if !(pred_val(&a_val, &b_val)) { - panic!("expected args satisfying {}, got {} and {}", $predname, a_val, b_val); + if !matches_codepattern(&a_val, &b_val) { + panic!("expected args satisfying `matches_codepattern`, got {} and {}", a_val, b_val); } }}; } @@ -41,9 +39,7 @@ fn ident_transformation() { let mut krate = string_to_crate("#[a] mod b {fn c (d : e, f : g) {h!(i,j,k);l;m}}".to_string()); zz_visitor.visit_crate(&mut krate); - assert_pred!( - matches_codepattern, - "matches_codepattern", + assert_matches_codepattern!( print_crate_items(&krate), "#[zz]mod zz{fn zz(zz:zz,zz:zz){zz!(zz,zz,zz);zz;zz}}".to_string() ); @@ -61,9 +57,7 @@ fn ident_transformation_in_defs() { .to_string(), ); zz_visitor.visit_crate(&mut krate); - assert_pred!( - matches_codepattern, - "matches_codepattern", + assert_matches_codepattern!( print_crate_items(&krate), "macro_rules! zz{(zz$zz:zz$(zz $zz:zz)zz+=>(zz$(zz$zz$zz)+))}".to_string() ); From 4c731c2f6b5d1305515c7c6820f0da731c7af4c3 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 4 Jun 2024 13:33:03 +1000 Subject: [PATCH 10/92] Inline and remove `check_builtin_attribute`. It's small and has a single call site. Also change the second `parse_meta` call to use a simple `match`, like the first `parse_meta` call, instead of a confusing `map_err`+`ok` combination. --- compiler/rustc_parse/src/validate_attr.rs | 30 +++++++++-------------- 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs index e897611212f9..88d6dd297d19 100644 --- a/compiler/rustc_parse/src/validate_attr.rs +++ b/compiler/rustc_parse/src/validate_attr.rs @@ -25,15 +25,21 @@ pub fn check_attr(psess: &ParseSess, attr: &Attribute) { match attr_info { // `rustc_dummy` doesn't have any restrictions specific to built-in attributes. Some(BuiltinAttribute { name, template, .. }) if *name != sym::rustc_dummy => { - check_builtin_attribute(psess, attr, *name, *template) + match parse_meta(psess, attr) { + Ok(meta) => check_builtin_meta_item(psess, &meta, attr.style, *name, *template), + Err(err) => { + err.emit(); + } + } } _ if let AttrArgs::Eq(..) = attr.get_normal_item().args => { // All key-value attributes are restricted to meta-item syntax. - parse_meta(psess, attr) - .map_err(|err| { + match parse_meta(psess, attr) { + Ok(_) => {} + Err(err) => { err.emit(); - }) - .ok(); + } + } } _ => {} } @@ -133,20 +139,6 @@ fn is_attr_template_compatible(template: &AttributeTemplate, meta: &ast::MetaIte } } -fn check_builtin_attribute( - psess: &ParseSess, - attr: &Attribute, - name: Symbol, - template: AttributeTemplate, -) { - match parse_meta(psess, attr) { - Ok(meta) => check_builtin_meta_item(psess, &meta, attr.style, name, template), - Err(err) => { - err.emit(); - } - } -} - pub fn check_builtin_meta_item( psess: &ParseSess, meta: &MetaItem, From c3309ef44653898d779ebb5b8292189cc28f7e59 Mon Sep 17 00:00:00 2001 From: Sunshine Date: Thu, 6 Jun 2024 12:06:53 +0800 Subject: [PATCH 11/92] Make html rendered by rustdoc allow searching non-English identifier / alias. Fix alias search result showing `undefined` description. --- src/librustdoc/html/static/js/search.js | 148 +++++++++++++----------- 1 file changed, 78 insertions(+), 70 deletions(-) diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 76a6fc9008ea..8322ea904908 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -89,6 +89,10 @@ const ROOT_PATH = typeof window !== "undefined" ? window.rootPath : "../"; // of permutations we need to check. const UNBOXING_LIMIT = 5; +// used for search query verification +const REGEX_IDENT = /\p{ID_Start}\p{ID_Continue}*|_\p{ID_Continue}+/uy; +const REGEX_INVALID_TYPE_FILTER = /[^a-z]/ui; + // In the search display, allows to switch between tabs. function printTab(nb) { let iter = 0; @@ -410,18 +414,21 @@ function initSearch(rawSearchIndex) { } /** - * Returns `true` if the given `c` character is valid for an ident. + * If the current parser position is at the beginning of an identifier, + * move the position to the end of it and return `true`. Otherwise, return `false`. * - * @param {string} c + * @param {ParserState} parserState * * @return {boolean} */ - function isIdentCharacter(c) { - return ( - c === "_" || - (c >= "0" && c <= "9") || - (c >= "a" && c <= "z") || - (c >= "A" && c <= "Z")); + function consumeIdent(parserState) { + REGEX_IDENT.lastIndex = parserState.pos; + const match = parserState.userQuery.match(REGEX_IDENT); + if (match) { + parserState.pos += match[0].length; + return true; + } + return false; } /** @@ -619,69 +626,61 @@ function initSearch(rawSearchIndex) { */ function getIdentEndPosition(parserState) { const start = parserState.pos; + let afterIdent = consumeIdent(parserState); let end = parserState.pos; - let foundExclamation = -1; + let macroExclamation = -1; while (parserState.pos < parserState.length) { const c = parserState.userQuery[parserState.pos]; - if (!isIdentCharacter(c)) { - if (c === "!") { - if (foundExclamation !== -1) { - throw ["Cannot have more than one ", "!", " in an ident"]; - } else if (parserState.pos + 1 < parserState.length && - isIdentCharacter(parserState.userQuery[parserState.pos + 1]) - ) { + if (c === "!") { + if (macroExclamation !== -1) { + throw ["Cannot have more than one ", "!", " in an ident"]; + } else if (parserState.pos + 1 < parserState.length) { + const pos = parserState.pos; + parserState.pos++; + const beforeIdent = consumeIdent(parserState); + parserState.pos = pos; + if (beforeIdent) { throw ["Unexpected ", "!", ": it can only be at the end of an ident"]; } - foundExclamation = parserState.pos; - } else if (isPathSeparator(c)) { - if (c === ":") { - if (!isPathStart(parserState)) { + } + if (afterIdent) macroExclamation = parserState.pos; + } else if (isPathSeparator(c)) { + if (c === ":") { + if (!isPathStart(parserState)) { + break; + } + // Skip current ":". + parserState.pos += 1; + } else { + while (parserState.pos + 1 < parserState.length) { + const next_c = parserState.userQuery[parserState.pos + 1]; + if (next_c !== " ") { break; } - // Skip current ":". parserState.pos += 1; - } else { - while (parserState.pos + 1 < parserState.length) { - const next_c = parserState.userQuery[parserState.pos + 1]; - if (next_c !== " ") { - break; - } - parserState.pos += 1; - } } - if (foundExclamation !== -1) { - if (foundExclamation !== start && - isIdentCharacter(parserState.userQuery[foundExclamation - 1]) - ) { - throw ["Cannot have associated items in macros"]; - } else { - // while the never type has no associated macros, we still - // can parse a path like that - foundExclamation = -1; - } - } - } else if ( - c === "[" || - c === "(" || - isEndCharacter(c) || - isSpecialStartCharacter(c) || - isSeparatorCharacter(c) - ) { - break; - } else if (parserState.pos > 0) { - throw ["Unexpected ", c, " after ", parserState.userQuery[parserState.pos - 1]]; - } else { - throw ["Unexpected ", c]; } + if (macroExclamation !== -1) { + throw ["Cannot have associated items in macros"]; + } + } else if ( + c === "[" || + c === "(" || + isEndCharacter(c) || + isSpecialStartCharacter(c) || + isSeparatorCharacter(c) + ) { + break; + } else if (parserState.pos > 0) { + throw ["Unexpected ", c, " after ", parserState.userQuery[parserState.pos - 1]]; + } else { + throw ["Unexpected ", c]; } parserState.pos += 1; + afterIdent = consumeIdent(parserState); end = parserState.pos; } - // if start == end - 1, we got the never type - if (foundExclamation !== -1 && - foundExclamation !== start && - isIdentCharacter(parserState.userQuery[foundExclamation - 1]) - ) { + if (macroExclamation !== -1) { if (parserState.typeFilter === null) { parserState.typeFilter = "macro"; } else if (parserState.typeFilter !== "macro") { @@ -693,7 +692,7 @@ function initSearch(rawSearchIndex) { " both specified", ]; } - end = foundExclamation; + end = macroExclamation; } return end; } @@ -1071,16 +1070,15 @@ function initSearch(rawSearchIndex) { function checkExtraTypeFilterCharacters(start, parserState) { const query = parserState.userQuery.slice(start, parserState.pos).trim(); - for (const c in query) { - if (!isIdentCharacter(query[c])) { - throw [ - "Unexpected ", - query[c], - " in type filter (before ", - ":", - ")", - ]; - } + const match = query.match(REGEX_INVALID_TYPE_FILTER); + if (match) { + throw [ + "Unexpected ", + match[0], + " in type filter (before ", + ":", + ")", + ]; } } @@ -2127,7 +2125,7 @@ function initSearch(rawSearchIndex) { }; } - function handleAliases(ret, query, filterCrates, currentCrate) { + async function handleAliases(ret, query, filterCrates, currentCrate) { const lowerQuery = query.toLowerCase(); // We separate aliases and crate aliases because we want to have current crate // aliases to be before the others in the displayed results. @@ -2163,6 +2161,14 @@ function initSearch(rawSearchIndex) { crateAliases.sort(sortFunc); aliases.sort(sortFunc); + const fetchDesc = alias => { + return searchIndexEmptyDesc.get(alias.crate).contains(alias.bitIndex) ? "" : searchState.loadDesc(alias); + }; + const [crateDescs, descs] = await Promise.all([ + Promise.all(crateAliases.map(fetchDesc)), + Promise.all(aliases.map(fetchDesc)) + ]); + const pushFunc = alias => { alias.alias = query; const res = buildHrefAndPath(alias); @@ -2176,7 +2182,9 @@ function initSearch(rawSearchIndex) { } }; + aliases.forEach((alias, i) => alias.desc = descs[i]); aliases.forEach(pushFunc); + crateAliases.forEach((alias, i) => alias.desc = crateDescs[i]); crateAliases.forEach(pushFunc); } @@ -2538,7 +2546,7 @@ function initSearch(rawSearchIndex) { sorted_returned, sorted_others, parsedQuery); - handleAliases(ret, parsedQuery.original.replace(/"/g, ""), filterCrates, currentCrate); + await handleAliases(ret, parsedQuery.original.replace(/"/g, ""), filterCrates, currentCrate); await Promise.all([ret.others, ret.returned, ret.in_args].map(async list => { const descs = await Promise.all(list.map(result => { return searchIndexEmptyDesc.get(result.crate).contains(result.bitIndex) ? From c74efbca71243fd3ccac0669d6a3d7297e49b69b Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 27 May 2024 14:29:37 -0400 Subject: [PATCH 12/92] Make middle not rely on next_trait_solver --- Cargo.lock | 1 - compiler/rustc_middle/Cargo.toml | 1 - compiler/rustc_middle/src/arena.rs | 2 +- compiler/rustc_middle/src/traits/mod.rs | 2 +- compiler/rustc_middle/src/traits/query.rs | 2 +- compiler/rustc_middle/src/traits/solve.rs | 4 ++-- 6 files changed, 5 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0e950e288072..99cb7196544e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4392,7 +4392,6 @@ dependencies = [ "rustc_hir_pretty", "rustc_index", "rustc_macros", - "rustc_next_trait_solver", "rustc_query_system", "rustc_serialize", "rustc_session", diff --git a/compiler/rustc_middle/Cargo.toml b/compiler/rustc_middle/Cargo.toml index ab0c598ea0c1..d1cdabc293dd 100644 --- a/compiler/rustc_middle/Cargo.toml +++ b/compiler/rustc_middle/Cargo.toml @@ -28,7 +28,6 @@ rustc_hir = { path = "../rustc_hir" } rustc_hir_pretty = { path = "../rustc_hir_pretty" } rustc_index = { path = "../rustc_index" } rustc_macros = { path = "../rustc_macros" } -rustc_next_trait_solver = { path = "../rustc_next_trait_solver" } rustc_query_system = { path = "../rustc_query_system" } rustc_serialize = { path = "../rustc_serialize" } rustc_session = { path = "../rustc_session" } diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs index bf6ab8000645..de786c38326f 100644 --- a/compiler/rustc_middle/src/arena.rs +++ b/compiler/rustc_middle/src/arena.rs @@ -62,7 +62,7 @@ macro_rules! arena_types { [] candidate_step: rustc_middle::traits::query::CandidateStep<'tcx>, [] autoderef_bad_ty: rustc_middle::traits::query::MethodAutoderefBadTy<'tcx>, [] canonical_goal_evaluation: - rustc_next_trait_solver::solve::inspect::CanonicalGoalEvaluationStep< + rustc_type_ir::solve::inspect::CanonicalGoalEvaluationStep< rustc_middle::ty::TyCtxt<'tcx> >, [] query_region_constraints: rustc_middle::infer::canonical::QueryRegionConstraints<'tcx>, diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index 63678ab659df..202d587f0ad2 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -32,7 +32,7 @@ use std::hash::{Hash, Hasher}; pub use self::select::{EvaluationCache, EvaluationResult, OverflowError, SelectionCache}; // FIXME: Remove this import and import via `solve::` -pub use rustc_next_trait_solver::solve::BuiltinImplSource; +pub use rustc_type_ir::solve::BuiltinImplSource; /// Depending on the stage of compilation, we want projection to be /// more or less conservative. diff --git a/compiler/rustc_middle/src/traits/query.rs b/compiler/rustc_middle/src/traits/query.rs index 66e50307733d..3e9a2cb05fed 100644 --- a/compiler/rustc_middle/src/traits/query.rs +++ b/compiler/rustc_middle/src/traits/query.rs @@ -13,7 +13,7 @@ use crate::ty::{self, Ty, TyCtxt}; use rustc_macros::{HashStable, TypeFoldable, TypeVisitable}; use rustc_span::Span; // FIXME: Remove this import and import via `traits::solve`. -pub use rustc_next_trait_solver::solve::NoSolution; +pub use rustc_type_ir::solve::NoSolution; pub mod type_op { use crate::ty::fold::TypeFoldable; diff --git a/compiler/rustc_middle/src/traits/solve.rs b/compiler/rustc_middle/src/traits/solve.rs index c8c16ec1e2ce..0d9ce402c64e 100644 --- a/compiler/rustc_middle/src/traits/solve.rs +++ b/compiler/rustc_middle/src/traits/solve.rs @@ -1,8 +1,8 @@ use rustc_ast_ir::try_visit; use rustc_data_structures::intern::Interned; use rustc_macros::{HashStable, TypeFoldable, TypeVisitable}; -use rustc_next_trait_solver as ir; -pub use rustc_next_trait_solver::solve::*; +use rustc_type_ir as ir; +pub use rustc_type_ir::solve::*; use crate::infer::canonical::QueryRegionConstraints; use crate::ty::{ From 82ef3ad98031377e20634d1f9f4aa3039db40025 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 27 May 2024 16:12:28 -0400 Subject: [PATCH 13/92] Uplift TypeError --- .../rustc_hir_analysis/src/check/check.rs | 1 + compiler/rustc_hir_typeck/src/expr.rs | 5 +- .../src/infer/error_reporting/mod.rs | 1 + .../infer/error_reporting/note_and_explain.rs | 7 +- .../infer/region_constraints/leak_check.rs | 2 +- compiler/rustc_middle/src/traits/query.rs | 7 - compiler/rustc_middle/src/ty/error.rs | 163 +++++------------- compiler/rustc_middle/src/ty/mod.rs | 32 ---- .../rustc_middle/src/ty/structural_impls.rs | 8 +- .../src/traits/error_reporting/suggestions.rs | 12 +- .../src/traits/select/mod.rs | 1 + compiler/rustc_type_ir/src/error.rs | 106 ++++++++++++ compiler/rustc_type_ir/src/lib.rs | 7 +- compiler/rustc_type_ir/src/macros.rs | 20 ++- compiler/rustc_type_ir/src/predicate.rs | 33 ++++ compiler/rustc_type_ir/src/solve.rs | 6 +- 16 files changed, 217 insertions(+), 194 deletions(-) create mode 100644 compiler/rustc_type_ir/src/error.rs diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 8a0623ef93e3..72e5995e892e 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -15,6 +15,7 @@ use rustc_lint_defs::builtin::REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS; use rustc_middle::middle::resolve_bound_vars::ResolvedArg; use rustc_middle::middle::stability::EvalResult; use rustc_middle::span_bug; +use rustc_middle::ty::error::TypeErrorToStringExt; use rustc_middle::ty::fold::BottomUpFolder; use rustc_middle::ty::layout::{LayoutError, MAX_SIMD_LANES}; use rustc_middle::ty::util::{Discr, InspectCoroutineFields, IntTypeExt}; diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 7dd7b3ff055e..f0f32ae07a95 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -40,7 +40,7 @@ use rustc_infer::infer::InferOk; use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::ObligationCause; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase}; -use rustc_middle::ty::error::{ExpectedFound, TypeError::Sorts}; +use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::GenericArgsRef; use rustc_middle::ty::{self, AdtKind, Ty, TypeVisitableExt}; use rustc_middle::{bug, span_bug}; @@ -682,7 +682,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.suggest_mismatched_types_on_tail( &mut err, expr, ty, e_ty, target_id, ); - let error = Some(Sorts(ExpectedFound { expected: ty, found: e_ty })); + let error = + Some(TypeError::Sorts(ExpectedFound { expected: ty, found: e_ty })); self.annotate_loop_expected_due_to_inference(err, expr, error); if let Some(val) = self.err_ctxt().ty_kind_suggestion(self.param_env, ty) diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index fe0a246abbc6..cee87f8f6e4d 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -71,6 +71,7 @@ use rustc_hir::lang_items::LangItem; use rustc_macros::extension; use rustc_middle::bug; use rustc_middle::dep_graph::DepContext; +use rustc_middle::ty::error::TypeErrorToStringExt; use rustc_middle::ty::print::{with_forced_trimmed_paths, PrintError, PrintTraitRefExt as _}; use rustc_middle::ty::relate::{self, RelateResult, TypeRelation}; use rustc_middle::ty::Upcast; diff --git a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs index 19ef2d61fca3..b88677b3a4ec 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs @@ -21,13 +21,12 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { sp: Span, body_owner_def_id: DefId, ) { - use ty::error::TypeError::*; debug!("note_and_explain_type_err err={:?} cause={:?}", err, cause); let tcx = self.tcx; match err { - ArgumentSorts(values, _) | Sorts(values) => { + TypeError::ArgumentSorts(values, _) | TypeError::Sorts(values) => { match (*values.expected.kind(), *values.found.kind()) { (ty::Closure(..), ty::Closure(..)) => { diag.note("no two closures, even if identical, have the same type"); @@ -483,7 +482,7 @@ impl Trait for X { values.found.kind(), ); } - CyclicTy(ty) => { + TypeError::CyclicTy(ty) => { // Watch out for various cases of cyclic types and try to explain. if ty.is_closure() || ty.is_coroutine() || ty.is_coroutine_closure() { diag.note( @@ -494,7 +493,7 @@ impl Trait for X { ); } } - TargetFeatureCast(def_id) => { + TypeError::TargetFeatureCast(def_id) => { let target_spans = tcx.get_attrs(def_id, sym::target_feature).map(|attr| attr.span); diag.note( "functions with `#[target_feature]` can only be coerced to `unsafe` function pointers" diff --git a/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs b/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs index 255ca52d3e98..d05ac1d81518 100644 --- a/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs +++ b/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs @@ -276,7 +276,7 @@ impl<'a, 'b, 'tcx> LeakCheck<'a, 'b, 'tcx> { other_region: ty::Region<'tcx>, ) -> TypeError<'tcx> { debug!("error: placeholder={:?}, other_region={:?}", placeholder, other_region); - TypeError::RegionsInsufficientlyPolymorphic(placeholder.bound.kind, other_region) + TypeError::RegionsInsufficientlyPolymorphic(placeholder.bound, other_region) } } diff --git a/compiler/rustc_middle/src/traits/query.rs b/compiler/rustc_middle/src/traits/query.rs index 3e9a2cb05fed..50b6c77e1b29 100644 --- a/compiler/rustc_middle/src/traits/query.rs +++ b/compiler/rustc_middle/src/traits/query.rs @@ -7,7 +7,6 @@ use crate::error::DropCheckOverflow; use crate::infer::canonical::{Canonical, QueryResponse}; -use crate::ty::error::TypeError; use crate::ty::GenericArg; use crate::ty::{self, Ty, TyCtxt}; use rustc_macros::{HashStable, TypeFoldable, TypeVisitable}; @@ -91,12 +90,6 @@ pub type CanonicalTypeOpProvePredicateGoal<'tcx> = pub type CanonicalTypeOpNormalizeGoal<'tcx, T> = Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::Normalize>>; -impl<'tcx> From> for NoSolution { - fn from(_: TypeError<'tcx>) -> NoSolution { - NoSolution - } -} - #[derive(Clone, Debug, Default, HashStable, TypeFoldable, TypeVisitable)] pub struct DropckOutlivesResult<'tcx> { pub kinds: Vec>, diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs index 9e2c626478ac..32dc9fa5fc62 100644 --- a/compiler/rustc_middle/src/ty/error.rs +++ b/compiler/rustc_middle/src/ty/error.rs @@ -1,89 +1,26 @@ use crate::ty::print::{with_forced_trimmed_paths, FmtPrinter, PrettyPrinter}; -use crate::ty::{self, BoundRegionKind, Region, Ty, TyCtxt}; +use crate::ty::{self, Ty, TyCtxt}; + use rustc_errors::pluralize; use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind}; -use rustc_hir::def_id::DefId; -use rustc_macros::{TypeFoldable, TypeVisitable}; -use rustc_span::symbol::Symbol; -use rustc_target::spec::abi; +use rustc_macros::extension; +pub use rustc_type_ir::error::ExpectedFound; + use std::borrow::Cow; use std::hash::{DefaultHasher, Hash, Hasher}; use std::path::PathBuf; -#[derive(Clone, Copy, Debug, PartialEq, Eq, TypeFoldable, TypeVisitable)] -pub struct ExpectedFound { - pub expected: T, - pub found: T, -} +pub type TypeError<'tcx> = rustc_type_ir::error::TypeError>; -impl ExpectedFound { - pub fn new(a_is_expected: bool, a: T, b: T) -> Self { - if a_is_expected { - ExpectedFound { expected: a, found: b } - } else { - ExpectedFound { expected: b, found: a } - } - } -} - -// Data structures used in type unification -#[derive(Copy, Clone, Debug, TypeVisitable, PartialEq, Eq)] -#[rustc_pass_by_value] -pub enum TypeError<'tcx> { - Mismatch, - ConstnessMismatch(ExpectedFound), - PolarityMismatch(ExpectedFound), - SafetyMismatch(ExpectedFound), - AbiMismatch(ExpectedFound), - Mutability, - ArgumentMutability(usize), - TupleSize(ExpectedFound), - FixedArraySize(ExpectedFound), - ArgCount, - FieldMisMatch(Symbol, Symbol), - - RegionsDoesNotOutlive(Region<'tcx>, Region<'tcx>), - RegionsInsufficientlyPolymorphic(BoundRegionKind, Region<'tcx>), - RegionsPlaceholderMismatch, - - Sorts(ExpectedFound>), - ArgumentSorts(ExpectedFound>, usize), - Traits(ExpectedFound), - VariadicMismatch(ExpectedFound), - - /// Instantiating a type variable with the given type would have - /// created a cycle (because it appears somewhere within that - /// type). - CyclicTy(Ty<'tcx>), - CyclicConst(ty::Const<'tcx>), - ProjectionMismatched(ExpectedFound), - ExistentialMismatch(ExpectedFound<&'tcx ty::List>>), - ConstMismatch(ExpectedFound>), - - IntrinsicCast, - /// Safe `#[target_feature]` functions are not assignable to safe function pointers. - TargetFeatureCast(DefId), -} - -impl TypeError<'_> { - pub fn involves_regions(self) -> bool { - match self { - TypeError::RegionsDoesNotOutlive(_, _) - | TypeError::RegionsInsufficientlyPolymorphic(_, _) - | TypeError::RegionsPlaceholderMismatch => true, - _ => false, - } - } -} - -/// Explains the source of a type err in a short, human readable way. This is meant to be placed -/// in parentheses after some larger message. You should also invoke `note_and_explain_type_err()` -/// afterwards to present additional details, particularly when it comes to lifetime-related -/// errors. +/// Explains the source of a type err in a short, human readable way. +/// This is meant to be placed in parentheses after some larger message. +/// You should also invoke `note_and_explain_type_err()` afterwards +/// to present additional details, particularly when it comes to lifetime- +/// related errors. +#[extension(pub trait TypeErrorToStringExt<'tcx>)] impl<'tcx> TypeError<'tcx> { - pub fn to_string(self, tcx: TyCtxt<'tcx>) -> Cow<'static, str> { - use self::TypeError::*; + fn to_string(self, tcx: TyCtxt<'tcx>) -> Cow<'static, str> { fn report_maybe_different(expected: &str, found: &str) -> String { // A naive approach to making sure that we're not reporting silly errors such as: // (expected closure, found closure). @@ -95,24 +32,26 @@ impl<'tcx> TypeError<'tcx> { } match self { - CyclicTy(_) => "cyclic type of infinite size".into(), - CyclicConst(_) => "encountered a self-referencing constant".into(), - Mismatch => "types differ".into(), - ConstnessMismatch(values) => { + TypeError::CyclicTy(_) => "cyclic type of infinite size".into(), + TypeError::CyclicConst(_) => "encountered a self-referencing constant".into(), + TypeError::Mismatch => "types differ".into(), + TypeError::ConstnessMismatch(values) => { format!("expected {} bound, found {} bound", values.expected, values.found).into() } - PolarityMismatch(values) => { + TypeError::PolarityMismatch(values) => { format!("expected {} polarity, found {} polarity", values.expected, values.found) .into() } - SafetyMismatch(values) => { + TypeError::SafetyMismatch(values) => { format!("expected {} fn, found {} fn", values.expected, values.found).into() } - AbiMismatch(values) => { + TypeError::AbiMismatch(values) => { format!("expected {} fn, found {} fn", values.expected, values.found).into() } - ArgumentMutability(_) | Mutability => "types differ in mutability".into(), - TupleSize(values) => format!( + TypeError::ArgumentMutability(_) | TypeError::Mutability => { + "types differ in mutability".into() + } + TypeError::TupleSize(values) => format!( "expected a tuple with {} element{}, found one with {} element{}", values.expected, pluralize!(values.expected), @@ -120,7 +59,7 @@ impl<'tcx> TypeError<'tcx> { pluralize!(values.found) ) .into(), - FixedArraySize(values) => format!( + TypeError::FixedArraySize(values) => format!( "expected an array with a fixed size of {} element{}, found one with {} element{}", values.expected, pluralize!(values.expected), @@ -128,20 +67,21 @@ impl<'tcx> TypeError<'tcx> { pluralize!(values.found) ) .into(), - ArgCount => "incorrect number of function parameters".into(), - FieldMisMatch(adt, field) => format!("field type mismatch: {adt}.{field}").into(), - RegionsDoesNotOutlive(..) => "lifetime mismatch".into(), + TypeError::ArgCount => "incorrect number of function parameters".into(), + TypeError::RegionsDoesNotOutlive(..) => "lifetime mismatch".into(), // Actually naming the region here is a bit confusing because context is lacking - RegionsInsufficientlyPolymorphic(..) => { + TypeError::RegionsInsufficientlyPolymorphic(..) => { "one type is more general than the other".into() } - RegionsPlaceholderMismatch => "one type is more general than the other".into(), - ArgumentSorts(values, _) | Sorts(values) => { + TypeError::RegionsPlaceholderMismatch => { + "one type is more general than the other".into() + } + TypeError::ArgumentSorts(values, _) | TypeError::Sorts(values) => { let expected = values.expected.sort_string(tcx); let found = values.found.sort_string(tcx); report_maybe_different(&expected, &found).into() } - Traits(values) => { + TypeError::Traits(values) => { let (mut expected, mut found) = with_forced_trimmed_paths!(( tcx.def_path_str(values.expected), tcx.def_path_str(values.found), @@ -153,59 +93,34 @@ impl<'tcx> TypeError<'tcx> { report_maybe_different(&format!("trait `{expected}`"), &format!("trait `{found}`")) .into() } - VariadicMismatch(ref values) => format!( + TypeError::VariadicMismatch(ref values) => format!( "expected {} fn, found {} function", if values.expected { "variadic" } else { "non-variadic" }, if values.found { "variadic" } else { "non-variadic" } ) .into(), - ProjectionMismatched(ref values) => format!( + TypeError::ProjectionMismatched(ref values) => format!( "expected `{}`, found `{}`", tcx.def_path_str(values.expected), tcx.def_path_str(values.found) ) .into(), - ExistentialMismatch(ref values) => report_maybe_different( + TypeError::ExistentialMismatch(ref values) => report_maybe_different( &format!("trait `{}`", values.expected), &format!("trait `{}`", values.found), ) .into(), - ConstMismatch(ref values) => { + TypeError::ConstMismatch(ref values) => { format!("expected `{}`, found `{}`", values.expected, values.found).into() } - IntrinsicCast => "cannot coerce intrinsics to function pointers".into(), - TargetFeatureCast(_) => { + TypeError::IntrinsicCast => "cannot coerce intrinsics to function pointers".into(), + TypeError::TargetFeatureCast(_) => { "cannot coerce functions with `#[target_feature]` to safe function pointers".into() } } } } -impl<'tcx> TypeError<'tcx> { - pub fn must_include_note(self) -> bool { - use self::TypeError::*; - match self { - CyclicTy(_) | CyclicConst(_) | SafetyMismatch(_) | ConstnessMismatch(_) - | PolarityMismatch(_) | Mismatch | AbiMismatch(_) | FixedArraySize(_) - | ArgumentSorts(..) | Sorts(_) | VariadicMismatch(_) | TargetFeatureCast(_) => false, - - Mutability - | ArgumentMutability(_) - | TupleSize(_) - | ArgCount - | FieldMisMatch(..) - | RegionsDoesNotOutlive(..) - | RegionsInsufficientlyPolymorphic(..) - | RegionsPlaceholderMismatch - | Traits(_) - | ProjectionMismatched(_) - | ExistentialMismatch(_) - | ConstMismatch(_) - | IntrinsicCast => true, - } - } -} - impl<'tcx> Ty<'tcx> { pub fn sort_string(self, tcx: TyCtxt<'tcx>) -> Cow<'static, str> { match *self.kind() { diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index e0fbf127e708..131c62d6d19f 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -313,38 +313,6 @@ impl Visibility { } } -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, HashStable, TyEncodable, TyDecodable)] -pub enum BoundConstness { - /// `Type: Trait` - NotConst, - /// `Type: const Trait` - Const, - /// `Type: ~const Trait` - /// - /// Requires resolving to const only when we are in a const context. - ConstIfConst, -} - -impl BoundConstness { - pub fn as_str(self) -> &'static str { - match self { - Self::NotConst => "", - Self::Const => "const", - Self::ConstIfConst => "~const", - } - } -} - -impl fmt::Display for BoundConstness { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::NotConst => f.write_str("normal"), - Self::Const => f.write_str("const"), - Self::ConstIfConst => f.write_str("~const"), - } - } -} - #[derive(Clone, Debug, PartialEq, Eq, Copy, Hash, TyEncodable, TyDecodable, HashStable)] #[derive(TypeFoldable, TypeVisitable)] pub struct ClosureSizeProfileData<'tcx> { diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index cf4854d13646..cc6b1d57f870 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -296,7 +296,6 @@ TrivialTypeTraversalImpls! { ::rustc_target::abi::FieldIdx, ::rustc_target::abi::VariantIdx, crate::middle::region::Scope, - crate::ty::FloatTy, ::rustc_ast::InlineAsmOptions, ::rustc_ast::InlineAsmTemplatePiece, ::rustc_ast::NodeId, @@ -316,7 +315,7 @@ TrivialTypeTraversalImpls! { crate::traits::Reveal, crate::ty::adjustment::AutoBorrowMutability, crate::ty::AdtKind, - crate::ty::BoundConstness, + crate::ty::BoundRegion, // Including `BoundRegionKind` is a *bit* dubious, but direct // references to bound region appear in `ty::Error`, and aren't // really meant to be folded. In general, we can only fold a fully @@ -324,16 +323,11 @@ TrivialTypeTraversalImpls! { crate::ty::BoundRegionKind, crate::ty::AssocItem, crate::ty::AssocKind, - crate::ty::AliasTyKind, crate::ty::Placeholder, crate::ty::Placeholder, crate::ty::Placeholder, crate::ty::LateParamRegion, - crate::ty::InferTy, - crate::ty::IntVarValue, crate::ty::adjustment::PointerCoercion, - crate::ty::RegionVid, - crate::ty::Variance, ::rustc_span::Span, ::rustc_span::symbol::Ident, ::rustc_errors::ErrorGuaranteed, diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 6a96a03e047f..c7da85bd1cc4 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -28,7 +28,7 @@ use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferOk}; use rustc_macros::extension; use rustc_middle::hir::map; use rustc_middle::traits::IsConstable; -use rustc_middle::ty::error::TypeError::{self, Sorts}; +use rustc_middle::ty::error::TypeError; use rustc_middle::ty::print::PrintPolyTraitRefExt; use rustc_middle::ty::{ self, suggest_arbitrary_trait_bound, suggest_constraining_type_param, AdtKind, GenericArgs, @@ -3842,7 +3842,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { && let Some(failed_pred) = failed_pred.as_projection_clause() && let Some(found) = failed_pred.skip_binder().term.as_type() { - type_diffs = vec![Sorts(ty::error::ExpectedFound { + type_diffs = vec![TypeError::Sorts(ty::error::ExpectedFound { expected: where_pred .skip_binder() .projection_term @@ -3985,7 +3985,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { continue; }; for diff in type_diffs { - let Sorts(expected_found) = diff else { + let TypeError::Sorts(expected_found) = diff else { continue; }; if tcx.is_diagnostic_item(sym::IteratorItem, *def_id) @@ -4165,7 +4165,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { }; if primary_spans.is_empty() || type_diffs.iter().any(|diff| { - let Sorts(expected_found) = diff else { + let TypeError::Sorts(expected_found) = diff else { return false; }; self.can_eq(param_env, expected_found.found, ty) @@ -4198,7 +4198,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { let assoc = with_forced_trimmed_paths!(self.tcx.def_path_str(assoc)); if !self.can_eq(param_env, ty, *prev_ty) { if type_diffs.iter().any(|diff| { - let Sorts(expected_found) = diff else { + let TypeError::Sorts(expected_found) = diff else { return false; }; self.can_eq(param_env, expected_found.found, ty) @@ -4248,7 +4248,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { let ocx = ObligationCtxt::new(self.infcx); let mut assocs_in_this_method = Vec::with_capacity(type_diffs.len()); for diff in type_diffs { - let Sorts(expected_found) = diff else { + let TypeError::Sorts(expected_found) = diff else { continue; }; let ty::Alias(ty::Projection, proj) = expected_found.expected.kind() else { diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index ce4fa5fa47cb..9e6ae9125150 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -42,6 +42,7 @@ use rustc_middle::dep_graph::DepNodeIndex; use rustc_middle::mir::interpret::ErrorHandled; use rustc_middle::ty::_match::MatchAgainstFreshVars; use rustc_middle::ty::abstract_const::NotConstEvaluatable; +use rustc_middle::ty::error::TypeErrorToStringExt; use rustc_middle::ty::print::PrintTraitRefExt as _; use rustc_middle::ty::relate::TypeRelation; use rustc_middle::ty::GenericArgsRef; diff --git a/compiler/rustc_type_ir/src/error.rs b/compiler/rustc_type_ir/src/error.rs new file mode 100644 index 000000000000..27623ea9cacb --- /dev/null +++ b/compiler/rustc_type_ir/src/error.rs @@ -0,0 +1,106 @@ +use rustc_type_ir_macros::{TypeFoldable_Generic, TypeVisitable_Generic}; + +use crate::solve::NoSolution; +use crate::{self as ty, Interner}; + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[derive(TypeFoldable_Generic, TypeVisitable_Generic)] +pub struct ExpectedFound { + pub expected: T, + pub found: T, +} + +impl ExpectedFound { + pub fn new(a_is_expected: bool, a: T, b: T) -> Self { + if a_is_expected { + ExpectedFound { expected: a, found: b } + } else { + ExpectedFound { expected: b, found: a } + } + } +} + +// Data structures used in type unification +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = ""), + Copy(bound = ""), + PartialEq(bound = ""), + Eq(bound = ""), + Debug(bound = "") +)] +#[derive(TypeVisitable_Generic)] +#[rustc_pass_by_value] +pub enum TypeError { + Mismatch, + ConstnessMismatch(ExpectedFound), + PolarityMismatch(ExpectedFound), + SafetyMismatch(ExpectedFound), + AbiMismatch(ExpectedFound), + Mutability, + ArgumentMutability(usize), + TupleSize(ExpectedFound), + FixedArraySize(ExpectedFound), + ArgCount, + + RegionsDoesNotOutlive(I::Region, I::Region), + RegionsInsufficientlyPolymorphic(I::BoundRegion, I::Region), + RegionsPlaceholderMismatch, + + Sorts(ExpectedFound), + ArgumentSorts(ExpectedFound, usize), + Traits(ExpectedFound), + VariadicMismatch(ExpectedFound), + + /// Instantiating a type variable with the given type would have + /// created a cycle (because it appears somewhere within that + /// type). + CyclicTy(I::Ty), + CyclicConst(I::Const), + ProjectionMismatched(ExpectedFound), + ExistentialMismatch(ExpectedFound), + ConstMismatch(ExpectedFound), + + IntrinsicCast, + /// Safe `#[target_feature]` functions are not assignable to safe function pointers. + TargetFeatureCast(I::DefId), +} + +impl TypeError { + pub fn involves_regions(self) -> bool { + match self { + TypeError::RegionsDoesNotOutlive(_, _) + | TypeError::RegionsInsufficientlyPolymorphic(_, _) + | TypeError::RegionsPlaceholderMismatch => true, + _ => false, + } + } + + pub fn must_include_note(self) -> bool { + use self::TypeError::*; + match self { + CyclicTy(_) | CyclicConst(_) | SafetyMismatch(_) | ConstnessMismatch(_) + | PolarityMismatch(_) | Mismatch | AbiMismatch(_) | FixedArraySize(_) + | ArgumentSorts(..) | Sorts(_) | VariadicMismatch(_) | TargetFeatureCast(_) => false, + + Mutability + | ArgumentMutability(_) + | TupleSize(_) + | ArgCount + | RegionsDoesNotOutlive(..) + | RegionsInsufficientlyPolymorphic(..) + | RegionsPlaceholderMismatch + | Traits(_) + | ProjectionMismatched(_) + | ExistentialMismatch(_) + | ConstMismatch(_) + | IntrinsicCast => true, + } + } +} + +impl From> for NoSolution { + fn from(_: TypeError) -> NoSolution { + NoSolution + } +} diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs index 217c056d0bae..7d363daa420a 100644 --- a/compiler/rustc_type_ir/src/lib.rs +++ b/compiler/rustc_type_ir/src/lib.rs @@ -21,18 +21,19 @@ use std::hash::Hash; #[cfg(not(feature = "nightly"))] use std::sync::Arc as Lrc; +// These modules are `pub` since they are not glob-imported. #[macro_use] pub mod visit; #[cfg(feature = "nightly")] pub mod codec; +pub mod error; pub mod fold; pub mod inherent; pub mod ir_print; pub mod lift; pub mod solve; -pub mod ty_info; -pub mod ty_kind; +// These modules are not `pub` since they are glob-imported. #[macro_use] mod macros; mod binder; @@ -46,6 +47,8 @@ mod interner; mod predicate; mod predicate_kind; mod region_kind; +mod ty_info; +mod ty_kind; mod upcast; pub use binder::*; diff --git a/compiler/rustc_type_ir/src/macros.rs b/compiler/rustc_type_ir/src/macros.rs index f2f7b165de52..aae5aeb5fb36 100644 --- a/compiler/rustc_type_ir/src/macros.rs +++ b/compiler/rustc_type_ir/src/macros.rs @@ -48,10 +48,22 @@ TrivialTypeTraversalImpls! { u32, u64, String, - crate::DebruijnIndex, crate::AliasRelationDirection, - crate::UniverseIndex, - rustc_ast_ir::Mutability, - rustc_ast_ir::Movability, + crate::AliasTyKind, + crate::BoundConstness, + crate::DebruijnIndex, + crate::FloatTy, + crate::InferTy, + crate::IntVarValue, crate::PredicatePolarity, + crate::RegionVid, + crate::solve::BuiltinImplSource, + crate::solve::Certainty, + crate::solve::GoalSource, + crate::solve::MaybeCause, + crate::solve::NoSolution, + crate::UniverseIndex, + crate::Variance, + rustc_ast_ir::Movability, + rustc_ast_ir::Mutability, } diff --git a/compiler/rustc_type_ir/src/predicate.rs b/compiler/rustc_type_ir/src/predicate.rs index 9e0e52cfb4b7..63a4c2e9d1f7 100644 --- a/compiler/rustc_type_ir/src/predicate.rs +++ b/compiler/rustc_type_ir/src/predicate.rs @@ -793,3 +793,36 @@ pub struct CoercePredicate { pub a: I::Ty, pub b: I::Ty, } + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "nightly", derive(HashStable_NoContext, TyEncodable, TyDecodable))] +pub enum BoundConstness { + /// `Type: Trait` + NotConst, + /// `Type: const Trait` + Const, + /// `Type: ~const Trait` + /// + /// Requires resolving to const only when we are in a const context. + ConstIfConst, +} + +impl BoundConstness { + pub fn as_str(self) -> &'static str { + match self { + Self::NotConst => "", + Self::Const => "const", + Self::ConstIfConst => "~const", + } + } +} + +impl fmt::Display for BoundConstness { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::NotConst => f.write_str("normal"), + Self::Const => f.write_str("const"), + Self::ConstIfConst => f.write_str("~const"), + } + } +} diff --git a/compiler/rustc_type_ir/src/solve.rs b/compiler/rustc_type_ir/src/solve.rs index 3c24e851d7b6..45125fe61919 100644 --- a/compiler/rustc_type_ir/src/solve.rs +++ b/compiler/rustc_type_ir/src/solve.rs @@ -20,7 +20,6 @@ pub type CanonicalResponse = Canonical>; pub type QueryResult = Result, NoSolution>; #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] -#[derive(TypeFoldable_Generic, TypeVisitable_Generic)] #[cfg_attr(feature = "nightly", derive(HashStable_NoContext))] pub struct NoSolution; @@ -60,7 +59,7 @@ impl Goal { /// /// This is necessary as we treat nested goals different depending on /// their source. -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, TypeVisitable_Generic, TypeFoldable_Generic)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] #[cfg_attr(feature = "nightly", derive(HashStable_NoContext))] pub enum GoalSource { Misc, @@ -170,7 +169,6 @@ pub enum CandidateSource { } #[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)] -#[derive(TypeVisitable_Generic, TypeFoldable_Generic)] #[cfg_attr(feature = "nightly", derive(HashStable_NoContext, TyEncodable, TyDecodable))] pub enum BuiltinImplSource { /// Some builtin impl we don't need to differentiate. This should be used @@ -214,7 +212,6 @@ pub struct Response { } #[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)] -#[derive(TypeVisitable_Generic, TypeFoldable_Generic)] #[cfg_attr(feature = "nightly", derive(HashStable_NoContext))] pub enum Certainty { Yes, @@ -252,7 +249,6 @@ impl Certainty { /// Why we failed to evaluate a goal. #[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)] -#[derive(TypeVisitable_Generic, TypeFoldable_Generic)] #[cfg_attr(feature = "nightly", derive(HashStable_NoContext))] pub enum MaybeCause { /// We failed due to ambiguity. This ambiguity can either From 91274c84b9f3790192219c71a8cb6908747beef4 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 4 Jun 2024 16:02:36 -0400 Subject: [PATCH 14/92] Uplift TypeRelation and Relate --- Cargo.lock | 2 + .../rustc_borrowck/src/constraints/mod.rs | 4 +- .../rustc_borrowck/src/region_infer/mod.rs | 2 +- .../src/type_check/relate_tys.rs | 16 +- compiler/rustc_hir_typeck/src/coercion.rs | 2 +- compiler/rustc_infer/Cargo.toml | 1 + compiler/rustc_infer/src/infer/at.rs | 4 +- .../src/infer/error_reporting/mod.rs | 12 +- compiler/rustc_infer/src/infer/mod.rs | 7 +- .../src/infer/outlives/test_type_match.rs | 17 +- .../infer/region_constraints/leak_check.rs | 2 +- .../src/infer/relate}/_match.rs | 22 +- .../rustc_infer/src/infer/relate/combine.rs | 17 +- .../src/infer/relate/generalize.rs | 15 +- compiler/rustc_infer/src/infer/relate/glb.rs | 10 +- .../src/infer/relate/higher_ranked.rs | 2 +- .../rustc_infer/src/infer/relate/lattice.rs | 2 +- compiler/rustc_infer/src/infer/relate/lub.rs | 10 +- compiler/rustc_infer/src/infer/relate/mod.rs | 20 +- .../src/infer/relate/type_relating.rs | 17 +- compiler/rustc_middle/src/ty/adt.rs | 6 + compiler/rustc_middle/src/ty/consts.rs | 8 + compiler/rustc_middle/src/ty/context.rs | 38 +- compiler/rustc_middle/src/ty/mod.rs | 4 +- compiler/rustc_middle/src/ty/relate.rs | 819 +++--------------- compiler/rustc_middle/src/ty/sty.rs | 119 ++- compiler/rustc_next_trait_solver/Cargo.toml | 13 +- .../rustc_next_trait_solver/src/resolve.rs | 8 +- .../src/solve/eval_ctxt/mod.rs | 8 +- .../src/traits/select/mod.rs | 4 +- compiler/rustc_type_ir/src/inherent.rs | 56 +- compiler/rustc_type_ir/src/interner.rs | 46 +- compiler/rustc_type_ir/src/lib.rs | 1 + compiler/rustc_type_ir/src/relate.rs | 666 ++++++++++++++ 34 files changed, 1114 insertions(+), 866 deletions(-) rename compiler/{rustc_middle/src/ty => rustc_infer/src/infer/relate}/_match.rs (82%) create mode 100644 compiler/rustc_type_ir/src/relate.rs diff --git a/Cargo.lock b/Cargo.lock index 99cb7196544e..019e7a3b16bd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4201,6 +4201,7 @@ dependencies = [ "rustc_middle", "rustc_span", "rustc_target", + "rustc_type_ir", "smallvec", "tracing", ] @@ -4508,6 +4509,7 @@ dependencies = [ "rustc_serialize", "rustc_type_ir", "rustc_type_ir_macros", + "tracing", ] [[package]] diff --git a/compiler/rustc_borrowck/src/constraints/mod.rs b/compiler/rustc_borrowck/src/constraints/mod.rs index ff11e4db1213..97408fa20d79 100644 --- a/compiler/rustc_borrowck/src/constraints/mod.rs +++ b/compiler/rustc_borrowck/src/constraints/mod.rs @@ -1,7 +1,7 @@ use rustc_data_structures::graph::scc::Sccs; use rustc_index::{IndexSlice, IndexVec}; use rustc_middle::mir::ConstraintCategory; -use rustc_middle::ty::{RegionVid, VarianceDiagInfo}; +use rustc_middle::ty::{RegionVid, TyCtxt, VarianceDiagInfo}; use rustc_span::Span; use std::fmt; use std::ops::Index; @@ -97,7 +97,7 @@ pub struct OutlivesConstraint<'tcx> { pub category: ConstraintCategory<'tcx>, /// Variance diagnostic information - pub variance_info: VarianceDiagInfo<'tcx>, + pub variance_info: VarianceDiagInfo>, /// If this constraint is promoted from closure requirements. pub from_closure: bool, diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index b57cf9066cf3..0e3140ca98b0 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -2304,5 +2304,5 @@ pub struct BlameConstraint<'tcx> { pub category: ConstraintCategory<'tcx>, pub from_closure: bool, pub cause: ObligationCause<'tcx>, - pub variance_info: ty::VarianceDiagInfo<'tcx>, + pub variance_info: ty::VarianceDiagInfo>, } diff --git a/compiler/rustc_borrowck/src/type_check/relate_tys.rs b/compiler/rustc_borrowck/src/type_check/relate_tys.rs index 9863c4a38832..2c34fc583c83 100644 --- a/compiler/rustc_borrowck/src/type_check/relate_tys.rs +++ b/compiler/rustc_borrowck/src/type_check/relate_tys.rs @@ -1,14 +1,14 @@ use rustc_data_structures::fx::FxHashMap; use rustc_errors::ErrorGuaranteed; +use rustc_infer::infer::relate::{ObligationEmittingRelation, StructurallyRelateAliases}; +use rustc_infer::infer::relate::{Relate, RelateResult, TypeRelation}; use rustc_infer::infer::NllRegionVariableOrigin; -use rustc_infer::infer::{ObligationEmittingRelation, StructurallyRelateAliases}; use rustc_infer::traits::{Obligation, PredicateObligations}; use rustc_middle::mir::ConstraintCategory; use rustc_middle::span_bug; use rustc_middle::traits::query::NoSolution; use rustc_middle::traits::ObligationCause; use rustc_middle::ty::fold::FnMutDelegate; -use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation}; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; use rustc_span::symbol::sym; use rustc_span::{Span, Symbol}; @@ -82,7 +82,7 @@ pub struct NllTypeRelating<'me, 'bccx, 'tcx> { /// - Bivariant means that it doesn't matter. ambient_variance: ty::Variance, - ambient_variance_info: ty::VarianceDiagInfo<'tcx>, + ambient_variance_info: ty::VarianceDiagInfo>, } impl<'me, 'bccx, 'tcx> NllTypeRelating<'me, 'bccx, 'tcx> { @@ -296,7 +296,7 @@ impl<'me, 'bccx, 'tcx> NllTypeRelating<'me, 'bccx, 'tcx> { &mut self, sup: ty::Region<'tcx>, sub: ty::Region<'tcx>, - info: ty::VarianceDiagInfo<'tcx>, + info: ty::VarianceDiagInfo>, ) { let sub = self.type_checker.borrowck_context.universal_regions.to_region_vid(sub); let sup = self.type_checker.borrowck_context.universal_regions.to_region_vid(sup); @@ -314,7 +314,7 @@ impl<'me, 'bccx, 'tcx> NllTypeRelating<'me, 'bccx, 'tcx> { } } -impl<'bccx, 'tcx> TypeRelation<'tcx> for NllTypeRelating<'_, 'bccx, 'tcx> { +impl<'bccx, 'tcx> TypeRelation> for NllTypeRelating<'_, 'bccx, 'tcx> { fn tcx(&self) -> TyCtxt<'tcx> { self.type_checker.infcx.tcx } @@ -324,10 +324,10 @@ impl<'bccx, 'tcx> TypeRelation<'tcx> for NllTypeRelating<'_, 'bccx, 'tcx> { } #[instrument(skip(self, info), level = "trace", ret)] - fn relate_with_variance>( + fn relate_with_variance>>( &mut self, variance: ty::Variance, - info: ty::VarianceDiagInfo<'tcx>, + info: ty::VarianceDiagInfo>, a: T, b: T, ) -> RelateResult<'tcx, T> { @@ -445,7 +445,7 @@ impl<'bccx, 'tcx> TypeRelation<'tcx> for NllTypeRelating<'_, 'bccx, 'tcx> { b: ty::Binder<'tcx, T>, ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> where - T: Relate<'tcx>, + T: Relate>, { // We want that // diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 93726ce2b3eb..dbaa6e398c88 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -41,6 +41,7 @@ use rustc_errors::{codes::*, struct_span_code_err, Applicability, Diag}; use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer; +use rustc_infer::infer::relate::RelateResult; use rustc_infer::infer::{Coercion, DefineOpaqueTypes, InferOk, InferResult}; use rustc_infer::traits::{IfExpressionCause, MatchExpressionArmCause}; use rustc_infer::traits::{Obligation, PredicateObligation}; @@ -51,7 +52,6 @@ use rustc_middle::ty::adjustment::{ Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, PointerCoercion, }; use rustc_middle::ty::error::TypeError; -use rustc_middle::ty::relate::RelateResult; use rustc_middle::ty::visit::TypeVisitableExt; use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt}; use rustc_session::parse::feature_err; diff --git a/compiler/rustc_infer/Cargo.toml b/compiler/rustc_infer/Cargo.toml index c1565a7d40fd..5136ab79a0f8 100644 --- a/compiler/rustc_infer/Cargo.toml +++ b/compiler/rustc_infer/Cargo.toml @@ -18,6 +18,7 @@ rustc_macros = { path = "../rustc_macros" } rustc_middle = { path = "../rustc_middle" } rustc_span = { path = "../rustc_span" } rustc_target = { path = "../rustc_target" } +rustc_type_ir = { path = "../rustc_type_ir" } smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } tracing = "0.1" # tidy-alphabetical-end diff --git a/compiler/rustc_infer/src/infer/at.rs b/compiler/rustc_infer/src/infer/at.rs index 17e6d6250ad5..046d908d148d 100644 --- a/compiler/rustc_infer/src/infer/at.rs +++ b/compiler/rustc_infer/src/infer/at.rs @@ -27,8 +27,8 @@ use super::*; +use crate::infer::relate::{Relate, StructurallyRelateAliases, TypeRelation}; use rustc_middle::bug; -use rustc_middle::ty::relate::{Relate, TypeRelation}; use rustc_middle::ty::{Const, ImplSubject}; /// Whether we should define opaque types or just treat them opaquely. @@ -90,7 +90,7 @@ impl<'tcx> InferCtxt<'tcx> { } } -pub trait ToTrace<'tcx>: Relate<'tcx> + Copy { +pub trait ToTrace<'tcx>: Relate> + Copy { fn to_trace( cause: &ObligationCause<'tcx>, a_is_expected: bool, diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index cee87f8f6e4d..ed483c6cbeb4 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -58,6 +58,7 @@ use crate::traits::{ PredicateObligation, }; +use crate::infer::relate::{self, RelateResult, TypeRelation}; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_errors::{ codes::*, pluralize, struct_span_code_err, Applicability, Diag, DiagCtxt, DiagStyledString, @@ -73,7 +74,6 @@ use rustc_middle::bug; use rustc_middle::dep_graph::DepContext; use rustc_middle::ty::error::TypeErrorToStringExt; use rustc_middle::ty::print::{with_forced_trimmed_paths, PrintError, PrintTraitRefExt as _}; -use rustc_middle::ty::relate::{self, RelateResult, TypeRelation}; use rustc_middle::ty::Upcast; use rustc_middle::ty::{ self, error::TypeError, IsSuggestable, List, Region, Ty, TyCtxt, TypeFoldable, @@ -2687,7 +2687,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { /// with the other type. A TyVar inference type is compatible with any type, and an IntVar or /// FloatVar inference type are compatible with themselves or their concrete types (Int and /// Float types, respectively). When comparing two ADTs, these rules apply recursively. - pub fn same_type_modulo_infer>(&self, a: T, b: T) -> bool { + pub fn same_type_modulo_infer>>(&self, a: T, b: T) -> bool { let (a, b) = self.resolve_vars_if_possible((a, b)); SameTypeModuloInfer(self).relate(a, b).is_ok() } @@ -2695,7 +2695,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { struct SameTypeModuloInfer<'a, 'tcx>(&'a InferCtxt<'tcx>); -impl<'tcx> TypeRelation<'tcx> for SameTypeModuloInfer<'_, 'tcx> { +impl<'tcx> TypeRelation> for SameTypeModuloInfer<'_, 'tcx> { fn tcx(&self) -> TyCtxt<'tcx> { self.0.tcx } @@ -2704,10 +2704,10 @@ impl<'tcx> TypeRelation<'tcx> for SameTypeModuloInfer<'_, 'tcx> { "SameTypeModuloInfer" } - fn relate_with_variance>( + fn relate_with_variance>>( &mut self, _variance: ty::Variance, - _info: ty::VarianceDiagInfo<'tcx>, + _info: ty::VarianceDiagInfo>, a: T, b: T, ) -> relate::RelateResult<'tcx, T> { @@ -2755,7 +2755,7 @@ impl<'tcx> TypeRelation<'tcx> for SameTypeModuloInfer<'_, 'tcx> { b: ty::Binder<'tcx, T>, ) -> relate::RelateResult<'tcx, ty::Binder<'tcx, T>> where - T: relate::Relate<'tcx>, + T: relate::Relate>, { Ok(a.rebind(self.relate(a.skip_binder(), b.skip_binder())?)) } diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 4476611d9c82..c606ab808ef8 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -1,9 +1,6 @@ pub use at::DefineOpaqueTypes; pub use freshen::TypeFreshener; pub use lexical_region_resolve::RegionResolutionError; -pub use relate::combine::CombineFields; -pub use relate::combine::ObligationEmittingRelation; -pub use relate::StructurallyRelateAliases; pub use rustc_macros::{TypeFoldable, TypeVisitable}; pub use rustc_middle::ty::IntVarValue; pub use BoundRegionConversionTime::*; @@ -11,6 +8,7 @@ pub use RegionVariableOrigin::*; pub use SubregionOrigin::*; pub use ValuePairs::*; +use crate::infer::relate::{CombineFields, RelateResult}; use crate::traits::{ self, ObligationCause, ObligationInspector, PredicateObligations, TraitEngine, }; @@ -39,7 +37,6 @@ use rustc_middle::traits::select; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::fold::BoundVarReplacerDelegate; use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; -use rustc_middle::ty::relate::RelateResult; use rustc_middle::ty::visit::TypeVisitableExt; use rustc_middle::ty::{self, GenericParamDefKind, InferConst, Ty, TyCtxt}; use rustc_middle::ty::{ConstVid, EffectVid, FloatVid, IntVid, TyVid}; @@ -62,7 +59,7 @@ pub mod opaque_types; pub mod outlives; mod projection; pub mod region_constraints; -mod relate; +pub mod relate; pub mod resolve; pub(crate) mod snapshot; pub mod type_variable; diff --git a/compiler/rustc_infer/src/infer/outlives/test_type_match.rs b/compiler/rustc_infer/src/infer/outlives/test_type_match.rs index 29c11d4247d0..978b92fd8980 100644 --- a/compiler/rustc_infer/src/infer/outlives/test_type_match.rs +++ b/compiler/rustc_infer/src/infer/outlives/test_type_match.rs @@ -1,15 +1,12 @@ use std::collections::hash_map::Entry; use rustc_data_structures::fx::FxHashMap; +use rustc_middle::ty::error::TypeError; use rustc_middle::ty::TypeVisitableExt; -use rustc_middle::ty::{ - self, - error::TypeError, - relate::{self, Relate, RelateResult, TypeRelation}, - Ty, TyCtxt, -}; +use rustc_middle::ty::{self, Ty, TyCtxt}; use crate::infer::region_constraints::VerifyIfEq; +use crate::infer::relate::{self as relate, Relate, RelateResult, TypeRelation}; /// Given a "verify-if-eq" type test like: /// @@ -135,7 +132,7 @@ impl<'tcx> MatchAgainstHigherRankedOutlives<'tcx> { } } -impl<'tcx> TypeRelation<'tcx> for MatchAgainstHigherRankedOutlives<'tcx> { +impl<'tcx> TypeRelation> for MatchAgainstHigherRankedOutlives<'tcx> { fn tag(&self) -> &'static str { "MatchAgainstHigherRankedOutlives" } @@ -145,10 +142,10 @@ impl<'tcx> TypeRelation<'tcx> for MatchAgainstHigherRankedOutlives<'tcx> { } #[instrument(level = "trace", skip(self))] - fn relate_with_variance>( + fn relate_with_variance>>( &mut self, variance: ty::Variance, - _: ty::VarianceDiagInfo<'tcx>, + _: ty::VarianceDiagInfo>, a: T, b: T, ) -> RelateResult<'tcx, T> { @@ -208,7 +205,7 @@ impl<'tcx> TypeRelation<'tcx> for MatchAgainstHigherRankedOutlives<'tcx> { value: ty::Binder<'tcx, T>, ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> where - T: Relate<'tcx>, + T: Relate>, { self.pattern_depth.shift_in(1); let result = Ok(pattern.rebind(self.relate(pattern.skip_binder(), value.skip_binder())?)); diff --git a/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs b/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs index d05ac1d81518..5b159d627312 100644 --- a/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs +++ b/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs @@ -1,11 +1,11 @@ use super::*; +use crate::infer::relate::RelateResult; use crate::infer::snapshot::CombinedSnapshot; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::graph::{scc::Sccs, vec_graph::VecGraph}; use rustc_index::Idx; use rustc_middle::span_bug; use rustc_middle::ty::error::TypeError; -use rustc_middle::ty::relate::RelateResult; impl<'tcx> RegionConstraintCollector<'_, 'tcx> { /// Searches new universes created during `snapshot`, looking for diff --git a/compiler/rustc_middle/src/ty/_match.rs b/compiler/rustc_infer/src/infer/relate/_match.rs similarity index 82% rename from compiler/rustc_middle/src/ty/_match.rs rename to compiler/rustc_infer/src/infer/relate/_match.rs index f30270abd5c1..30a066a265ac 100644 --- a/compiler/rustc_middle/src/ty/_match.rs +++ b/compiler/rustc_infer/src/infer/relate/_match.rs @@ -1,8 +1,10 @@ -use crate::ty::error::TypeError; -use crate::ty::relate::{self, Relate, RelateResult, TypeRelation}; -use crate::ty::{self, InferConst, Ty, TyCtxt}; +use rustc_middle::ty::error::{ExpectedFound, TypeError}; +use rustc_middle::ty::{self, InferConst, Ty, TyCtxt}; use tracing::{debug, instrument}; +use super::{structurally_relate_tys, Relate, RelateResult, TypeRelation}; +use crate::infer::relate; + /// A type "A" *matches* "B" if the fresh types in B could be /// instantiated with values so as to make it equal to A. Matching is /// intended to be used only on freshened types, and it basically @@ -29,7 +31,7 @@ impl<'tcx> MatchAgainstFreshVars<'tcx> { } } -impl<'tcx> TypeRelation<'tcx> for MatchAgainstFreshVars<'tcx> { +impl<'tcx> TypeRelation> for MatchAgainstFreshVars<'tcx> { fn tag(&self) -> &'static str { "MatchAgainstFreshVars" } @@ -38,10 +40,10 @@ impl<'tcx> TypeRelation<'tcx> for MatchAgainstFreshVars<'tcx> { self.tcx } - fn relate_with_variance>( + fn relate_with_variance>>( &mut self, _: ty::Variance, - _: ty::VarianceDiagInfo<'tcx>, + _: ty::VarianceDiagInfo>, a: T, b: T, ) -> RelateResult<'tcx, T> { @@ -72,12 +74,12 @@ impl<'tcx> TypeRelation<'tcx> for MatchAgainstFreshVars<'tcx> { ) => Ok(a), (&ty::Infer(_), _) | (_, &ty::Infer(_)) => { - Err(TypeError::Sorts(relate::expected_found(a, b))) + Err(TypeError::Sorts(ExpectedFound::new(true, a, b))) } (&ty::Error(guar), _) | (_, &ty::Error(guar)) => Ok(Ty::new_error(self.tcx(), guar)), - _ => relate::structurally_relate_tys(self, a, b), + _ => structurally_relate_tys(self, a, b), } } @@ -97,7 +99,7 @@ impl<'tcx> TypeRelation<'tcx> for MatchAgainstFreshVars<'tcx> { } (ty::ConstKind::Infer(_), _) | (_, ty::ConstKind::Infer(_)) => { - return Err(TypeError::ConstMismatch(relate::expected_found(a, b))); + return Err(TypeError::ConstMismatch(ExpectedFound::new(true, a, b))); } _ => {} @@ -112,7 +114,7 @@ impl<'tcx> TypeRelation<'tcx> for MatchAgainstFreshVars<'tcx> { b: ty::Binder<'tcx, T>, ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> where - T: Relate<'tcx>, + T: Relate>, { Ok(a.rebind(self.relate(a.skip_binder(), b.skip_binder())?)) } diff --git a/compiler/rustc_infer/src/infer/relate/combine.rs b/compiler/rustc_infer/src/infer/relate/combine.rs index b193f4bcede6..30cb2bab9008 100644 --- a/compiler/rustc_infer/src/infer/relate/combine.rs +++ b/compiler/rustc_infer/src/infer/relate/combine.rs @@ -22,12 +22,13 @@ use super::glb::Glb; use super::lub::Lub; use super::type_relating::TypeRelating; use super::StructurallyRelateAliases; +use super::{RelateResult, TypeRelation}; +use crate::infer::relate; use crate::infer::{DefineOpaqueTypes, InferCtxt, TypeTrace}; use crate::traits::{Obligation, PredicateObligations}; use rustc_middle::bug; use rustc_middle::infer::unify_key::EffectVarValue; -use rustc_middle::ty::error::TypeError; -use rustc_middle::ty::relate::{RelateResult, TypeRelation}; +use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::{self, InferConst, Ty, TyCtxt, TypeVisitableExt, Upcast}; use rustc_middle::ty::{IntType, UintType}; use rustc_span::Span; @@ -121,7 +122,7 @@ impl<'tcx> InferCtxt<'tcx> { (_, ty::Alias(..)) | (ty::Alias(..), _) if self.next_trait_solver() => { match relation.structurally_relate_aliases() { StructurallyRelateAliases::Yes => { - ty::relate::structurally_relate_tys(relation, a, b) + relate::structurally_relate_tys(relation, a, b) } StructurallyRelateAliases::No => { relation.register_type_relate_obligation(a, b); @@ -132,7 +133,7 @@ impl<'tcx> InferCtxt<'tcx> { // All other cases of inference are errors (&ty::Infer(_), _) | (_, &ty::Infer(_)) => { - Err(TypeError::Sorts(ty::relate::expected_found(a, b))) + Err(TypeError::Sorts(ExpectedFound::new(true, a, b))) } // During coherence, opaque types should be treated as *possibly* @@ -144,7 +145,7 @@ impl<'tcx> InferCtxt<'tcx> { Ok(a) } - _ => ty::relate::structurally_relate_tys(relation, a, b), + _ => relate::structurally_relate_tys(relation, a, b), } } @@ -234,11 +235,11 @@ impl<'tcx> InferCtxt<'tcx> { Ok(b) } StructurallyRelateAliases::Yes => { - ty::relate::structurally_relate_consts(relation, a, b) + relate::structurally_relate_consts(relation, a, b) } } } - _ => ty::relate::structurally_relate_consts(relation, a, b), + _ => relate::structurally_relate_consts(relation, a, b), } } @@ -303,7 +304,7 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> { } } -pub trait ObligationEmittingRelation<'tcx>: TypeRelation<'tcx> { +pub trait ObligationEmittingRelation<'tcx>: TypeRelation> { fn span(&self) -> Span; fn param_env(&self) -> ty::ParamEnv<'tcx>; diff --git a/compiler/rustc_infer/src/infer/relate/generalize.rs b/compiler/rustc_infer/src/infer/relate/generalize.rs index 225c126fcf81..5478afda455f 100644 --- a/compiler/rustc_infer/src/infer/relate/generalize.rs +++ b/compiler/rustc_infer/src/infer/relate/generalize.rs @@ -1,15 +1,16 @@ use std::mem; use super::StructurallyRelateAliases; +use super::{ObligationEmittingRelation, Relate, RelateResult, TypeRelation}; +use crate::infer::relate; use crate::infer::type_variable::TypeVariableValue; -use crate::infer::{InferCtxt, ObligationEmittingRelation, RegionVariableOrigin}; +use crate::infer::{InferCtxt, RegionVariableOrigin}; use rustc_data_structures::sso::SsoHashMap; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_hir::def_id::DefId; use rustc_middle::bug; use rustc_middle::infer::unify_key::ConstVariableValue; use rustc_middle::ty::error::TypeError; -use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation}; use rustc_middle::ty::visit::MaxUniverse; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::ty::{AliasRelationDirection, InferConst, Term, TypeVisitable, TypeVisitableExt}; @@ -228,7 +229,7 @@ impl<'tcx> InferCtxt<'tcx> { /// Attempts to generalize `source_term` for the type variable `target_vid`. /// This checks for cycles -- that is, whether `source_term` references `target_vid`. - fn generalize> + Relate<'tcx>>( + fn generalize> + Relate>>( &self, span: Span, structurally_relate_aliases: StructurallyRelateAliases, @@ -395,7 +396,7 @@ impl<'tcx> Generalizer<'_, 'tcx> { } } -impl<'tcx> TypeRelation<'tcx> for Generalizer<'_, 'tcx> { +impl<'tcx> TypeRelation> for Generalizer<'_, 'tcx> { fn tcx(&self) -> TyCtxt<'tcx> { self.infcx.tcx } @@ -430,10 +431,10 @@ impl<'tcx> TypeRelation<'tcx> for Generalizer<'_, 'tcx> { } #[instrument(level = "debug", skip(self, variance, b), ret)] - fn relate_with_variance>( + fn relate_with_variance>>( &mut self, variance: ty::Variance, - _info: ty::VarianceDiagInfo<'tcx>, + _info: ty::VarianceDiagInfo>, a: T, b: T, ) -> RelateResult<'tcx, T> { @@ -695,7 +696,7 @@ impl<'tcx> TypeRelation<'tcx> for Generalizer<'_, 'tcx> { _: ty::Binder<'tcx, T>, ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> where - T: Relate<'tcx>, + T: Relate>, { let result = self.relate(a.skip_binder(), a.skip_binder())?; Ok(a.rebind(result)) diff --git a/compiler/rustc_infer/src/infer/relate/glb.rs b/compiler/rustc_infer/src/infer/relate/glb.rs index a224a86492ab..98e8f07c7a21 100644 --- a/compiler/rustc_infer/src/infer/relate/glb.rs +++ b/compiler/rustc_infer/src/infer/relate/glb.rs @@ -1,6 +1,6 @@ //! Greatest lower bound. See [`lattice`]. -use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation}; +use super::{Relate, RelateResult, TypeRelation}; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; use rustc_span::Span; @@ -21,7 +21,7 @@ impl<'combine, 'infcx, 'tcx> Glb<'combine, 'infcx, 'tcx> { } } -impl<'tcx> TypeRelation<'tcx> for Glb<'_, '_, 'tcx> { +impl<'tcx> TypeRelation> for Glb<'_, '_, 'tcx> { fn tag(&self) -> &'static str { "Glb" } @@ -30,10 +30,10 @@ impl<'tcx> TypeRelation<'tcx> for Glb<'_, '_, 'tcx> { self.fields.tcx() } - fn relate_with_variance>( + fn relate_with_variance>>( &mut self, variance: ty::Variance, - _info: ty::VarianceDiagInfo<'tcx>, + _info: ty::VarianceDiagInfo>, a: T, b: T, ) -> RelateResult<'tcx, T> { @@ -81,7 +81,7 @@ impl<'tcx> TypeRelation<'tcx> for Glb<'_, '_, 'tcx> { b: ty::Binder<'tcx, T>, ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> where - T: Relate<'tcx>, + T: Relate>, { // GLB of a binder and itself is just itself if a == b { diff --git a/compiler/rustc_infer/src/infer/relate/higher_ranked.rs b/compiler/rustc_infer/src/infer/relate/higher_ranked.rs index d3001eb5838b..cfce28aca5d6 100644 --- a/compiler/rustc_infer/src/infer/relate/higher_ranked.rs +++ b/compiler/rustc_infer/src/infer/relate/higher_ranked.rs @@ -1,10 +1,10 @@ //! Helper routines for higher-ranked things. See the `doc` module at //! the end of the file for details. +use super::RelateResult; use crate::infer::snapshot::CombinedSnapshot; use crate::infer::InferCtxt; use rustc_middle::ty::fold::FnMutDelegate; -use rustc_middle::ty::relate::RelateResult; use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable}; impl<'tcx> InferCtxt<'tcx> { diff --git a/compiler/rustc_infer/src/infer/relate/lattice.rs b/compiler/rustc_infer/src/infer/relate/lattice.rs index c0c51a2820b3..f05b984142ae 100644 --- a/compiler/rustc_infer/src/infer/relate/lattice.rs +++ b/compiler/rustc_infer/src/infer/relate/lattice.rs @@ -21,7 +21,7 @@ use super::combine::ObligationEmittingRelation; use crate::infer::{DefineOpaqueTypes, InferCtxt}; use crate::traits::ObligationCause; -use rustc_middle::ty::relate::RelateResult; +use super::RelateResult; use rustc_middle::ty::TyVar; use rustc_middle::ty::{self, Ty}; diff --git a/compiler/rustc_infer/src/infer/relate/lub.rs b/compiler/rustc_infer/src/infer/relate/lub.rs index 83ab77707709..28dbaa94f95d 100644 --- a/compiler/rustc_infer/src/infer/relate/lub.rs +++ b/compiler/rustc_infer/src/infer/relate/lub.rs @@ -6,7 +6,7 @@ use super::StructurallyRelateAliases; use crate::infer::{DefineOpaqueTypes, InferCtxt, SubregionOrigin}; use crate::traits::{ObligationCause, PredicateObligations}; -use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation}; +use super::{Relate, RelateResult, TypeRelation}; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; use rustc_span::Span; @@ -21,7 +21,7 @@ impl<'combine, 'infcx, 'tcx> Lub<'combine, 'infcx, 'tcx> { } } -impl<'tcx> TypeRelation<'tcx> for Lub<'_, '_, 'tcx> { +impl<'tcx> TypeRelation> for Lub<'_, '_, 'tcx> { fn tag(&self) -> &'static str { "Lub" } @@ -30,10 +30,10 @@ impl<'tcx> TypeRelation<'tcx> for Lub<'_, '_, 'tcx> { self.fields.tcx() } - fn relate_with_variance>( + fn relate_with_variance>>( &mut self, variance: ty::Variance, - _info: ty::VarianceDiagInfo<'tcx>, + _info: ty::VarianceDiagInfo>, a: T, b: T, ) -> RelateResult<'tcx, T> { @@ -81,7 +81,7 @@ impl<'tcx> TypeRelation<'tcx> for Lub<'_, '_, 'tcx> { b: ty::Binder<'tcx, T>, ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> where - T: Relate<'tcx>, + T: Relate>, { // LUB of a binder and itself is just itself if a == b { diff --git a/compiler/rustc_infer/src/infer/relate/mod.rs b/compiler/rustc_infer/src/infer/relate/mod.rs index 86a01130167a..627c527cba15 100644 --- a/compiler/rustc_infer/src/infer/relate/mod.rs +++ b/compiler/rustc_infer/src/infer/relate/mod.rs @@ -1,6 +1,14 @@ //! This module contains the definitions of most `TypeRelation`s in the type system //! (except for some relations used for diagnostics and heuristics in the compiler). +//! As well as the implementation of `Relate` for interned things (`Ty`/`Const`/etc). +pub use rustc_middle::ty::relate::*; + +pub use self::_match::MatchAgainstFreshVars; +pub use self::combine::CombineFields; +pub use self::combine::ObligationEmittingRelation; + +pub mod _match; pub(super) mod combine; mod generalize; mod glb; @@ -8,15 +16,3 @@ mod higher_ranked; mod lattice; mod lub; mod type_relating; - -/// Whether aliases should be related structurally or not. Used -/// to adjust the behavior of generalization and combine. -/// -/// This should always be `No` unless in a few special-cases when -/// instantiating canonical responses and in the new solver. Each -/// such case should have a comment explaining why it is used. -#[derive(Debug, Copy, Clone)] -pub enum StructurallyRelateAliases { - Yes, - No, -} diff --git a/compiler/rustc_infer/src/infer/relate/type_relating.rs b/compiler/rustc_infer/src/infer/relate/type_relating.rs index e55a58788212..fd0bc9f44f75 100644 --- a/compiler/rustc_infer/src/infer/relate/type_relating.rs +++ b/compiler/rustc_infer/src/infer/relate/type_relating.rs @@ -1,12 +1,11 @@ use super::combine::CombineFields; use crate::infer::BoundRegionConversionTime::HigherRankedType; -use crate::infer::{ - DefineOpaqueTypes, ObligationEmittingRelation, StructurallyRelateAliases, SubregionOrigin, -}; +use crate::infer::{DefineOpaqueTypes, SubregionOrigin}; use crate::traits::{Obligation, PredicateObligations}; -use rustc_middle::ty::relate::{ - relate_args_invariantly, relate_args_with_variances, Relate, RelateResult, TypeRelation, +use super::{ + relate_args_invariantly, relate_args_with_variances, ObligationEmittingRelation, Relate, + RelateResult, StructurallyRelateAliases, TypeRelation, }; use rustc_middle::ty::TyVar; use rustc_middle::ty::{self, Ty, TyCtxt}; @@ -29,7 +28,7 @@ impl<'combine, 'infcx, 'tcx> TypeRelating<'combine, 'infcx, 'tcx> { } } -impl<'tcx> TypeRelation<'tcx> for TypeRelating<'_, '_, 'tcx> { +impl<'tcx> TypeRelation> for TypeRelating<'_, '_, 'tcx> { fn tag(&self) -> &'static str { "TypeRelating" } @@ -56,10 +55,10 @@ impl<'tcx> TypeRelation<'tcx> for TypeRelating<'_, '_, 'tcx> { } } - fn relate_with_variance>( + fn relate_with_variance>>( &mut self, variance: ty::Variance, - _info: ty::VarianceDiagInfo<'tcx>, + _info: ty::VarianceDiagInfo>, a: T, b: T, ) -> RelateResult<'tcx, T> { @@ -226,7 +225,7 @@ impl<'tcx> TypeRelation<'tcx> for TypeRelating<'_, '_, 'tcx> { b: ty::Binder<'tcx, T>, ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> where - T: Relate<'tcx>, + T: Relate>, { if a == b { // Do nothing diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs index 5f9b870331c9..886dbd317afa 100644 --- a/compiler/rustc_middle/src/ty/adt.rs +++ b/compiler/rustc_middle/src/ty/adt.rs @@ -200,6 +200,12 @@ impl<'tcx> AdtDef<'tcx> { } } +impl<'tcx> rustc_type_ir::inherent::AdtDef> for AdtDef<'tcx> { + fn def_id(self) -> DefId { + self.did() + } +} + #[derive(Copy, Clone, Debug, Eq, PartialEq, HashStable, TyEncodable, TyDecodable)] pub enum AdtKind { Struct, diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs index dc13cc5437d5..cc1daeb64191 100644 --- a/compiler/rustc_middle/src/ty/consts.rs +++ b/compiler/rustc_middle/src/ty/consts.rs @@ -149,6 +149,10 @@ impl<'tcx> Const<'tcx> { } impl<'tcx> rustc_type_ir::inherent::Const> for Const<'tcx> { + fn try_to_target_usize(self, interner: TyCtxt<'tcx>) -> Option { + self.try_to_target_usize(interner) + } + fn new_infer(tcx: TyCtxt<'tcx>, infer: ty::InferConst) -> Self { Const::new_infer(tcx, infer) } @@ -168,6 +172,10 @@ impl<'tcx> rustc_type_ir::inherent::Const> for Const<'tcx> { fn new_unevaluated(interner: TyCtxt<'tcx>, uv: ty::UnevaluatedConst<'tcx>) -> Self { Const::new_unevaluated(interner, uv) } + + fn new_expr(interner: TyCtxt<'tcx>, expr: ty::Expr<'tcx>) -> Self { + Const::new_expr(interner, expr) + } } impl<'tcx> Const<'tcx> { diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index f9a22aa72bee..61d47eb1420b 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -69,6 +69,7 @@ use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{Span, DUMMY_SP}; use rustc_target::abi::{FieldIdx, Layout, LayoutS, TargetDataLayout, VariantIdx}; use rustc_target::spec::abi; +use rustc_type_ir::fold::TypeFoldable; use rustc_type_ir::TyKind::*; use rustc_type_ir::WithCachedTypeInfo; use rustc_type_ir::{CollectAndApply, Interner, TypeFlags}; @@ -135,9 +136,12 @@ impl<'tcx> Interner for TyCtxt<'tcx> { type ParamEnv = ty::ParamEnv<'tcx>; type Predicate = Predicate<'tcx>; type Clause = Clause<'tcx>; - type Clauses = ty::Clauses<'tcx>; + fn expand_abstract_consts>>(self, t: T) -> T { + self.expand_abstract_consts(t) + } + fn mk_canonical_var_infos(self, infos: &[ty::CanonicalVarInfo]) -> Self::CanonicalVars { self.mk_canonical_var_infos(infos) } @@ -148,6 +152,12 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.generics_of(def_id) } + type VariancesOf = &'tcx [ty::Variance]; + + fn variances_of(self, def_id: Self::DefId) -> Self::VariancesOf { + self.variances_of(def_id) + } + fn type_of(self, def_id: DefId) -> ty::EarlyBinder<'tcx, Ty<'tcx>> { self.type_of(def_id) } @@ -205,7 +215,11 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.mk_args(args) } - fn mk_args_from_iter(self, args: impl Iterator) -> Self::GenericArgs { + fn mk_args_from_iter(self, args: I) -> T::Output + where + I: Iterator, + T: CollectAndApply, + { self.mk_args_from_iter(args) } @@ -224,6 +238,14 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.arena.alloc(step) } + fn mk_type_list_from_iter(self, args: I) -> T::Output + where + I: Iterator, + T: CollectAndApply, + { + self.mk_type_list_from_iter(args) + } + fn parent(self, def_id: Self::DefId) -> Self::DefId { self.parent(def_id) } @@ -231,6 +253,12 @@ impl<'tcx> Interner for TyCtxt<'tcx> { fn recursion_limit(self) -> usize { self.recursion_limit().0 } + + type Features = &'tcx rustc_feature::Features; + + fn features(self) -> Self::Features { + self.features() + } } impl<'tcx> rustc_type_ir::inherent::Abi> for abi::Abi { @@ -249,6 +277,12 @@ impl<'tcx> rustc_type_ir::inherent::Safety> for hir::Safety { } } +impl<'tcx> rustc_type_ir::inherent::Features> for &'tcx rustc_feature::Features { + fn generic_const_exprs(self) -> bool { + self.generic_const_exprs + } +} + type InternedSet<'tcx, T> = ShardedHashMap, ()>; pub struct CtxtInterners<'tcx> { diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 131c62d6d19f..7ff1b7998227 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -60,6 +60,7 @@ use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{ExpnId, ExpnKind, Span}; use rustc_target::abi::{Align, FieldIdx, Integer, IntegerType, VariantIdx}; pub use rustc_target::abi::{ReprFlags, ReprOptions}; +pub use rustc_type_ir::relate::VarianceDiagInfo; pub use rustc_type_ir::{DebugWithInfcx, InferCtxtLike, WithInfcx}; use tracing::{debug, instrument}; pub use vtable::*; @@ -114,7 +115,7 @@ pub use self::rvalue_scopes::RvalueScopes; pub use self::sty::{ AliasTy, Article, Binder, BoundTy, BoundTyKind, BoundVariableKind, CanonicalPolyFnSig, CoroutineArgsExt, EarlyBinder, FnSig, InlineConstArgs, InlineConstArgsParts, ParamConst, - ParamTy, PolyFnSig, TyKind, TypeAndMut, UpvarArgs, VarianceDiagInfo, + ParamTy, PolyFnSig, TyKind, TypeAndMut, UpvarArgs, }; pub use self::trait_def::TraitDef; pub use self::typeck_results::{ @@ -122,7 +123,6 @@ pub use self::typeck_results::{ TypeckResults, UserType, UserTypeAnnotationIndex, }; -pub mod _match; pub mod abstract_const; pub mod adjustment; pub mod cast; diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs index e24e64b23013..b169d672a846 100644 --- a/compiler/rustc_middle/src/ty/relate.rs +++ b/compiler/rustc_middle/src/ty/relate.rs @@ -1,383 +1,54 @@ -//! Generalized type relating mechanism. -//! -//! A type relation `R` relates a pair of values `(A, B)`. `A and B` are usually -//! types or regions but can be other things. Examples of type relations are -//! subtyping, type equality, etc. +use std::iter; + +use rustc_hir as hir; +use rustc_target::spec::abi; +pub use rustc_type_ir::relate::*; use crate::ty::error::{ExpectedFound, TypeError}; -use crate::ty::{ - self, ExistentialPredicate, ExistentialPredicateStableCmpExt as _, GenericArg, GenericArgKind, - GenericArgsRef, ImplSubject, Term, TermKind, Ty, TyCtxt, TypeFoldable, -}; -use rustc_hir as hir; -use rustc_hir::def_id::DefId; -use rustc_macros::TypeVisitable; -use rustc_target::spec::abi; -use std::iter; -use tracing::{debug, instrument}; +use crate::ty::predicate::ExistentialPredicateStableCmpExt as _; +use crate::ty::{self as ty, Ty, TyCtxt}; -use super::Pattern; +pub type RelateResult<'tcx, T> = rustc_type_ir::relate::RelateResult, T>; -pub type RelateResult<'tcx, T> = Result>; - -pub trait TypeRelation<'tcx>: Sized { - fn tcx(&self) -> TyCtxt<'tcx>; - - /// Returns a static string we can use for printouts. - fn tag(&self) -> &'static str; - - /// Generic relation routine suitable for most anything. - fn relate>(&mut self, a: T, b: T) -> RelateResult<'tcx, T> { - Relate::relate(self, a, b) - } - - /// Relate the two args for the given item. The default - /// is to look up the variance for the item and proceed - /// accordingly. - fn relate_item_args( - &mut self, - item_def_id: DefId, - a_arg: GenericArgsRef<'tcx>, - b_arg: GenericArgsRef<'tcx>, - ) -> RelateResult<'tcx, GenericArgsRef<'tcx>> { - debug!( - "relate_item_args(item_def_id={:?}, a_arg={:?}, b_arg={:?})", - item_def_id, a_arg, b_arg - ); - - let tcx = self.tcx(); - let opt_variances = tcx.variances_of(item_def_id); - relate_args_with_variances(self, item_def_id, opt_variances, a_arg, b_arg, true) - } - - /// Switch variance for the purpose of relating `a` and `b`. - fn relate_with_variance>( - &mut self, - variance: ty::Variance, - info: ty::VarianceDiagInfo<'tcx>, - a: T, - b: T, - ) -> RelateResult<'tcx, T>; - - // Overridable relations. You shouldn't typically call these - // directly, instead call `relate()`, which in turn calls - // these. This is both more uniform but also allows us to add - // additional hooks for other types in the future if needed - // without making older code, which called `relate`, obsolete. - - fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>>; - - fn regions( - &mut self, - a: ty::Region<'tcx>, - b: ty::Region<'tcx>, - ) -> RelateResult<'tcx, ty::Region<'tcx>>; - - fn consts( - &mut self, - a: ty::Const<'tcx>, - b: ty::Const<'tcx>, - ) -> RelateResult<'tcx, ty::Const<'tcx>>; - - fn binders( - &mut self, - a: ty::Binder<'tcx, T>, - b: ty::Binder<'tcx, T>, - ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> - where - T: Relate<'tcx>; +/// Whether aliases should be related structurally or not. Used +/// to adjust the behavior of generalization and combine. +/// +/// This should always be `No` unless in a few special-cases when +/// instantiating canonical responses and in the new solver. Each +/// such case should have a comment explaining why it is used. +#[derive(Debug, Copy, Clone)] +pub enum StructurallyRelateAliases { + Yes, + No, } -pub trait Relate<'tcx>: TypeFoldable> + PartialEq + Copy { - fn relate>( - relation: &mut R, - a: Self, - b: Self, - ) -> RelateResult<'tcx, Self>; -} - -/////////////////////////////////////////////////////////////////////////// -// Relate impls - -#[inline] -pub fn relate_args_invariantly<'tcx, R: TypeRelation<'tcx>>( - relation: &mut R, - a_arg: GenericArgsRef<'tcx>, - b_arg: GenericArgsRef<'tcx>, -) -> RelateResult<'tcx, GenericArgsRef<'tcx>> { - relation.tcx().mk_args_from_iter(iter::zip(a_arg, b_arg).map(|(a, b)| { - relation.relate_with_variance(ty::Invariant, ty::VarianceDiagInfo::default(), a, b) - })) -} - -pub fn relate_args_with_variances<'tcx, R: TypeRelation<'tcx>>( - relation: &mut R, - ty_def_id: DefId, - variances: &[ty::Variance], - a_arg: GenericArgsRef<'tcx>, - b_arg: GenericArgsRef<'tcx>, - fetch_ty_for_diag: bool, -) -> RelateResult<'tcx, GenericArgsRef<'tcx>> { - let tcx = relation.tcx(); - - let mut cached_ty = None; - let params = iter::zip(a_arg, b_arg).enumerate().map(|(i, (a, b))| { - let variance = variances[i]; - let variance_info = if variance == ty::Invariant && fetch_ty_for_diag { - let ty = - *cached_ty.get_or_insert_with(|| tcx.type_of(ty_def_id).instantiate(tcx, a_arg)); - ty::VarianceDiagInfo::Invariant { ty, param_index: i.try_into().unwrap() } - } else { - ty::VarianceDiagInfo::default() - }; - relation.relate_with_variance(variance, variance_info, a, b) - }); - - tcx.mk_args_from_iter(params) -} - -impl<'tcx> Relate<'tcx> for ty::FnSig<'tcx> { - fn relate>( - relation: &mut R, - a: ty::FnSig<'tcx>, - b: ty::FnSig<'tcx>, - ) -> RelateResult<'tcx, ty::FnSig<'tcx>> { - let tcx = relation.tcx(); - - if a.c_variadic != b.c_variadic { - return Err(TypeError::VariadicMismatch(expected_found(a.c_variadic, b.c_variadic))); - } - let safety = relation.relate(a.safety, b.safety)?; - let abi = relation.relate(a.abi, b.abi)?; - - if a.inputs().len() != b.inputs().len() { - return Err(TypeError::ArgCount); - } - - let inputs_and_output = iter::zip(a.inputs(), b.inputs()) - .map(|(&a, &b)| ((a, b), false)) - .chain(iter::once(((a.output(), b.output()), true))) - .map(|((a, b), is_output)| { - if is_output { - relation.relate(a, b) - } else { - relation.relate_with_variance( - ty::Contravariant, - ty::VarianceDiagInfo::default(), - a, - b, - ) - } - }) - .enumerate() - .map(|(i, r)| match r { - Err(TypeError::Sorts(exp_found) | TypeError::ArgumentSorts(exp_found, _)) => { - Err(TypeError::ArgumentSorts(exp_found, i)) - } - Err(TypeError::Mutability | TypeError::ArgumentMutability(_)) => { - Err(TypeError::ArgumentMutability(i)) - } - r => r, - }); - Ok(ty::FnSig { - inputs_and_output: tcx.mk_type_list_from_iter(inputs_and_output)?, - c_variadic: a.c_variadic, - safety, - abi, - }) - } -} - -impl<'tcx> Relate<'tcx> for ty::BoundConstness { - fn relate>( - _relation: &mut R, - a: ty::BoundConstness, - b: ty::BoundConstness, - ) -> RelateResult<'tcx, ty::BoundConstness> { - if a != b { Err(TypeError::ConstnessMismatch(expected_found(a, b))) } else { Ok(a) } - } -} - -impl<'tcx> Relate<'tcx> for hir::Safety { - fn relate>( - _relation: &mut R, - a: hir::Safety, - b: hir::Safety, - ) -> RelateResult<'tcx, hir::Safety> { - if a != b { Err(TypeError::SafetyMismatch(expected_found(a, b))) } else { Ok(a) } - } -} - -impl<'tcx> Relate<'tcx> for abi::Abi { - fn relate>( - _relation: &mut R, - a: abi::Abi, - b: abi::Abi, - ) -> RelateResult<'tcx, abi::Abi> { - if a == b { Ok(a) } else { Err(TypeError::AbiMismatch(expected_found(a, b))) } - } -} - -impl<'tcx> Relate<'tcx> for ty::AliasTy<'tcx> { - fn relate>( - relation: &mut R, - a: ty::AliasTy<'tcx>, - b: ty::AliasTy<'tcx>, - ) -> RelateResult<'tcx, ty::AliasTy<'tcx>> { - if a.def_id != b.def_id { - Err(TypeError::ProjectionMismatched(expected_found(a.def_id, b.def_id))) - } else { - let args = match a.kind(relation.tcx()) { - ty::Opaque => relate_args_with_variances( - relation, - a.def_id, - relation.tcx().variances_of(a.def_id), - a.args, - b.args, - false, // do not fetch `type_of(a_def_id)`, as it will cause a cycle - )?, - ty::Projection | ty::Weak | ty::Inherent => { - relate_args_invariantly(relation, a.args, b.args)? - } - }; - Ok(ty::AliasTy::new(relation.tcx(), a.def_id, args)) - } - } -} - -impl<'tcx> Relate<'tcx> for ty::AliasTerm<'tcx> { - fn relate>( - relation: &mut R, - a: ty::AliasTerm<'tcx>, - b: ty::AliasTerm<'tcx>, - ) -> RelateResult<'tcx, ty::AliasTerm<'tcx>> { - if a.def_id != b.def_id { - Err(TypeError::ProjectionMismatched(expected_found(a.def_id, b.def_id))) - } else { - let args = match a.kind(relation.tcx()) { - ty::AliasTermKind::OpaqueTy => relate_args_with_variances( - relation, - a.def_id, - relation.tcx().variances_of(a.def_id), - a.args, - b.args, - false, // do not fetch `type_of(a_def_id)`, as it will cause a cycle - )?, - ty::AliasTermKind::ProjectionTy - | ty::AliasTermKind::WeakTy - | ty::AliasTermKind::InherentTy - | ty::AliasTermKind::UnevaluatedConst - | ty::AliasTermKind::ProjectionConst => { - relate_args_invariantly(relation, a.args, b.args)? - } - }; - Ok(ty::AliasTerm::new(relation.tcx(), a.def_id, args)) - } - } -} - -impl<'tcx> Relate<'tcx> for ty::ExistentialProjection<'tcx> { - fn relate>( - relation: &mut R, - a: ty::ExistentialProjection<'tcx>, - b: ty::ExistentialProjection<'tcx>, - ) -> RelateResult<'tcx, ty::ExistentialProjection<'tcx>> { - if a.def_id != b.def_id { - Err(TypeError::ProjectionMismatched(expected_found(a.def_id, b.def_id))) - } else { - let term = relation.relate_with_variance( - ty::Invariant, - ty::VarianceDiagInfo::default(), - a.term, - b.term, - )?; - let args = relation.relate_with_variance( - ty::Invariant, - ty::VarianceDiagInfo::default(), - a.args, - b.args, - )?; - Ok(ty::ExistentialProjection { def_id: a.def_id, args, term }) - } - } -} - -impl<'tcx> Relate<'tcx> for ty::TraitRef<'tcx> { - fn relate>( - relation: &mut R, - a: ty::TraitRef<'tcx>, - b: ty::TraitRef<'tcx>, - ) -> RelateResult<'tcx, ty::TraitRef<'tcx>> { - // Different traits cannot be related. - if a.def_id != b.def_id { - Err(TypeError::Traits(expected_found(a.def_id, b.def_id))) - } else { - let args = relate_args_invariantly(relation, a.args, b.args)?; - Ok(ty::TraitRef::new(relation.tcx(), a.def_id, args)) - } - } -} - -impl<'tcx> Relate<'tcx> for ty::ExistentialTraitRef<'tcx> { - fn relate>( - relation: &mut R, - a: ty::ExistentialTraitRef<'tcx>, - b: ty::ExistentialTraitRef<'tcx>, - ) -> RelateResult<'tcx, ty::ExistentialTraitRef<'tcx>> { - // Different traits cannot be related. - if a.def_id != b.def_id { - Err(TypeError::Traits(expected_found(a.def_id, b.def_id))) - } else { - let args = relate_args_invariantly(relation, a.args, b.args)?; - Ok(ty::ExistentialTraitRef { def_id: a.def_id, args }) - } - } -} - -#[derive(PartialEq, Copy, Debug, Clone, TypeFoldable, TypeVisitable)] -struct CoroutineWitness<'tcx>(&'tcx ty::List>); - -impl<'tcx> Relate<'tcx> for CoroutineWitness<'tcx> { - fn relate>( - relation: &mut R, - a: CoroutineWitness<'tcx>, - b: CoroutineWitness<'tcx>, - ) -> RelateResult<'tcx, CoroutineWitness<'tcx>> { - assert_eq!(a.0.len(), b.0.len()); - let tcx = relation.tcx(); - let types = - tcx.mk_type_list_from_iter(iter::zip(a.0, b.0).map(|(a, b)| relation.relate(a, b)))?; - Ok(CoroutineWitness(types)) - } -} - -impl<'tcx> Relate<'tcx> for ImplSubject<'tcx> { +impl<'tcx> Relate> for ty::ImplSubject<'tcx> { #[inline] - fn relate>( + fn relate>>( relation: &mut R, - a: ImplSubject<'tcx>, - b: ImplSubject<'tcx>, - ) -> RelateResult<'tcx, ImplSubject<'tcx>> { + a: ty::ImplSubject<'tcx>, + b: ty::ImplSubject<'tcx>, + ) -> RelateResult<'tcx, ty::ImplSubject<'tcx>> { match (a, b) { - (ImplSubject::Trait(trait_ref_a), ImplSubject::Trait(trait_ref_b)) => { + (ty::ImplSubject::Trait(trait_ref_a), ty::ImplSubject::Trait(trait_ref_b)) => { let trait_ref = ty::TraitRef::relate(relation, trait_ref_a, trait_ref_b)?; - Ok(ImplSubject::Trait(trait_ref)) + Ok(ty::ImplSubject::Trait(trait_ref)) } - (ImplSubject::Inherent(ty_a), ImplSubject::Inherent(ty_b)) => { + (ty::ImplSubject::Inherent(ty_a), ty::ImplSubject::Inherent(ty_b)) => { let ty = Ty::relate(relation, ty_a, ty_b)?; - Ok(ImplSubject::Inherent(ty)) + Ok(ty::ImplSubject::Inherent(ty)) } - (ImplSubject::Trait(_), ImplSubject::Inherent(_)) - | (ImplSubject::Inherent(_), ImplSubject::Trait(_)) => { + (ty::ImplSubject::Trait(_), ty::ImplSubject::Inherent(_)) + | (ty::ImplSubject::Inherent(_), ty::ImplSubject::Trait(_)) => { bug!("can not relate TraitRef and Ty"); } } } } -impl<'tcx> Relate<'tcx> for Ty<'tcx> { +impl<'tcx> Relate> for Ty<'tcx> { #[inline] - fn relate>( + fn relate>>( relation: &mut R, a: Ty<'tcx>, b: Ty<'tcx>, @@ -386,9 +57,9 @@ impl<'tcx> Relate<'tcx> for Ty<'tcx> { } } -impl<'tcx> Relate<'tcx> for Pattern<'tcx> { +impl<'tcx> Relate> for ty::Pattern<'tcx> { #[inline] - fn relate>( + fn relate>>( relation: &mut R, a: Self, b: Self, @@ -416,276 +87,8 @@ impl<'tcx> Relate<'tcx> for Pattern<'tcx> { } } -/// Relates `a` and `b` structurally, calling the relation for all nested values. -/// Any semantic equality, e.g. of projections, and inference variables have to be -/// handled by the caller. -#[instrument(level = "trace", skip(relation), ret)] -pub fn structurally_relate_tys<'tcx, R: TypeRelation<'tcx>>( - relation: &mut R, - a: Ty<'tcx>, - b: Ty<'tcx>, -) -> RelateResult<'tcx, Ty<'tcx>> { - let tcx = relation.tcx(); - match (a.kind(), b.kind()) { - (&ty::Infer(_), _) | (_, &ty::Infer(_)) => { - // The caller should handle these cases! - bug!("var types encountered in structurally_relate_tys") - } - - (ty::Bound(..), _) | (_, ty::Bound(..)) => { - bug!("bound types encountered in structurally_relate_tys") - } - - (&ty::Error(guar), _) | (_, &ty::Error(guar)) => Ok(Ty::new_error(tcx, guar)), - - (&ty::Never, _) - | (&ty::Char, _) - | (&ty::Bool, _) - | (&ty::Int(_), _) - | (&ty::Uint(_), _) - | (&ty::Float(_), _) - | (&ty::Str, _) - if a == b => - { - Ok(a) - } - - (ty::Param(a_p), ty::Param(b_p)) if a_p.index == b_p.index => { - debug_assert_eq!(a_p.name, b_p.name, "param types with same index differ in name"); - Ok(a) - } - - (ty::Placeholder(p1), ty::Placeholder(p2)) if p1 == p2 => Ok(a), - - (&ty::Adt(a_def, a_args), &ty::Adt(b_def, b_args)) if a_def == b_def => { - let args = relation.relate_item_args(a_def.did(), a_args, b_args)?; - Ok(Ty::new_adt(tcx, a_def, args)) - } - - (&ty::Foreign(a_id), &ty::Foreign(b_id)) if a_id == b_id => Ok(Ty::new_foreign(tcx, a_id)), - - (&ty::Dynamic(a_obj, a_region, a_repr), &ty::Dynamic(b_obj, b_region, b_repr)) - if a_repr == b_repr => - { - Ok(Ty::new_dynamic( - tcx, - relation.relate(a_obj, b_obj)?, - relation.relate(a_region, b_region)?, - a_repr, - )) - } - - (&ty::Coroutine(a_id, a_args), &ty::Coroutine(b_id, b_args)) if a_id == b_id => { - // All Coroutine types with the same id represent - // the (anonymous) type of the same coroutine expression. So - // all of their regions should be equated. - let args = relate_args_invariantly(relation, a_args, b_args)?; - Ok(Ty::new_coroutine(tcx, a_id, args)) - } - - (&ty::CoroutineWitness(a_id, a_args), &ty::CoroutineWitness(b_id, b_args)) - if a_id == b_id => - { - // All CoroutineWitness types with the same id represent - // the (anonymous) type of the same coroutine expression. So - // all of their regions should be equated. - let args = relate_args_invariantly(relation, a_args, b_args)?; - Ok(Ty::new_coroutine_witness(tcx, a_id, args)) - } - - (&ty::Closure(a_id, a_args), &ty::Closure(b_id, b_args)) if a_id == b_id => { - // All Closure types with the same id represent - // the (anonymous) type of the same closure expression. So - // all of their regions should be equated. - let args = relate_args_invariantly(relation, a_args, b_args)?; - Ok(Ty::new_closure(tcx, a_id, args)) - } - - (&ty::CoroutineClosure(a_id, a_args), &ty::CoroutineClosure(b_id, b_args)) - if a_id == b_id => - { - let args = relate_args_invariantly(relation, a_args, b_args)?; - Ok(Ty::new_coroutine_closure(tcx, a_id, args)) - } - - (&ty::RawPtr(a_ty, a_mutbl), &ty::RawPtr(b_ty, b_mutbl)) => { - if a_mutbl != b_mutbl { - return Err(TypeError::Mutability); - } - - let (variance, info) = match a_mutbl { - hir::Mutability::Not => (ty::Covariant, ty::VarianceDiagInfo::None), - hir::Mutability::Mut => { - (ty::Invariant, ty::VarianceDiagInfo::Invariant { ty: a, param_index: 0 }) - } - }; - - let ty = relation.relate_with_variance(variance, info, a_ty, b_ty)?; - - Ok(Ty::new_ptr(tcx, ty, a_mutbl)) - } - - (&ty::Ref(a_r, a_ty, a_mutbl), &ty::Ref(b_r, b_ty, b_mutbl)) => { - if a_mutbl != b_mutbl { - return Err(TypeError::Mutability); - } - - let (variance, info) = match a_mutbl { - hir::Mutability::Not => (ty::Covariant, ty::VarianceDiagInfo::None), - hir::Mutability::Mut => { - (ty::Invariant, ty::VarianceDiagInfo::Invariant { ty: a, param_index: 0 }) - } - }; - - let r = relation.relate(a_r, b_r)?; - let ty = relation.relate_with_variance(variance, info, a_ty, b_ty)?; - - Ok(Ty::new_ref(tcx, r, ty, a_mutbl)) - } - - (&ty::Array(a_t, sz_a), &ty::Array(b_t, sz_b)) => { - let t = relation.relate(a_t, b_t)?; - match relation.relate(sz_a, sz_b) { - Ok(sz) => Ok(Ty::new_array_with_const_len(tcx, t, sz)), - Err(err) => { - // Check whether the lengths are both concrete/known values, - // but are unequal, for better diagnostics. - let sz_a = sz_a.try_to_target_usize(tcx); - let sz_b = sz_b.try_to_target_usize(tcx); - - match (sz_a, sz_b) { - (Some(sz_a_val), Some(sz_b_val)) if sz_a_val != sz_b_val => { - Err(TypeError::FixedArraySize(expected_found(sz_a_val, sz_b_val))) - } - _ => Err(err), - } - } - } - } - - (&ty::Slice(a_t), &ty::Slice(b_t)) => { - let t = relation.relate(a_t, b_t)?; - Ok(Ty::new_slice(tcx, t)) - } - - (&ty::Tuple(as_), &ty::Tuple(bs)) => { - if as_.len() == bs.len() { - Ok(Ty::new_tup_from_iter( - tcx, - iter::zip(as_, bs).map(|(a, b)| relation.relate(a, b)), - )?) - } else if !(as_.is_empty() || bs.is_empty()) { - Err(TypeError::TupleSize(expected_found(as_.len(), bs.len()))) - } else { - Err(TypeError::Sorts(expected_found(a, b))) - } - } - - (&ty::FnDef(a_def_id, a_args), &ty::FnDef(b_def_id, b_args)) if a_def_id == b_def_id => { - let args = relation.relate_item_args(a_def_id, a_args, b_args)?; - Ok(Ty::new_fn_def(tcx, a_def_id, args)) - } - - (&ty::FnPtr(a_fty), &ty::FnPtr(b_fty)) => { - let fty = relation.relate(a_fty, b_fty)?; - Ok(Ty::new_fn_ptr(tcx, fty)) - } - - // Alias tend to mostly already be handled downstream due to normalization. - (&ty::Alias(a_kind, a_data), &ty::Alias(b_kind, b_data)) => { - let alias_ty = relation.relate(a_data, b_data)?; - assert_eq!(a_kind, b_kind); - Ok(Ty::new_alias(tcx, a_kind, alias_ty)) - } - - (&ty::Pat(a_ty, a_pat), &ty::Pat(b_ty, b_pat)) => { - let ty = relation.relate(a_ty, b_ty)?; - let pat = relation.relate(a_pat, b_pat)?; - Ok(Ty::new_pat(tcx, ty, pat)) - } - - _ => Err(TypeError::Sorts(expected_found(a, b))), - } -} - -/// Relates `a` and `b` structurally, calling the relation for all nested values. -/// Any semantic equality, e.g. of unevaluated consts, and inference variables have -/// to be handled by the caller. -/// -/// FIXME: This is not totally structual, which probably should be fixed. -/// See the HACKs below. -pub fn structurally_relate_consts<'tcx, R: TypeRelation<'tcx>>( - relation: &mut R, - mut a: ty::Const<'tcx>, - mut b: ty::Const<'tcx>, -) -> RelateResult<'tcx, ty::Const<'tcx>> { - debug!("{}.structurally_relate_consts(a = {:?}, b = {:?})", relation.tag(), a, b); - let tcx = relation.tcx(); - - if tcx.features().generic_const_exprs { - a = tcx.expand_abstract_consts(a); - b = tcx.expand_abstract_consts(b); - } - - debug!("{}.structurally_relate_consts(normed_a = {:?}, normed_b = {:?})", relation.tag(), a, b); - - // Currently, the values that can be unified are primitive types, - // and those that derive both `PartialEq` and `Eq`, corresponding - // to structural-match types. - let is_match = match (a.kind(), b.kind()) { - (ty::ConstKind::Infer(_), _) | (_, ty::ConstKind::Infer(_)) => { - // The caller should handle these cases! - bug!("var types encountered in structurally_relate_consts: {:?} {:?}", a, b) - } - - (ty::ConstKind::Error(_), _) => return Ok(a), - (_, ty::ConstKind::Error(_)) => return Ok(b), - - (ty::ConstKind::Param(a_p), ty::ConstKind::Param(b_p)) if a_p.index == b_p.index => { - debug_assert_eq!(a_p.name, b_p.name, "param types with same index differ in name"); - true - } - (ty::ConstKind::Placeholder(p1), ty::ConstKind::Placeholder(p2)) => p1 == p2, - (ty::ConstKind::Value(_, a_val), ty::ConstKind::Value(_, b_val)) => a_val == b_val, - - // While this is slightly incorrect, it shouldn't matter for `min_const_generics` - // and is the better alternative to waiting until `generic_const_exprs` can - // be stabilized. - (ty::ConstKind::Unevaluated(au), ty::ConstKind::Unevaluated(bu)) if au.def == bu.def => { - if cfg!(debug_assertions) { - let a_ty = tcx.type_of(au.def).instantiate(tcx, au.args); - let b_ty = tcx.type_of(bu.def).instantiate(tcx, bu.args); - assert_eq!(a_ty, b_ty); - } - - let args = relation.relate_with_variance( - ty::Variance::Invariant, - ty::VarianceDiagInfo::default(), - au.args, - bu.args, - )?; - return Ok(ty::Const::new_unevaluated(tcx, ty::UnevaluatedConst { def: au.def, args })); - } - (ty::ConstKind::Expr(ae), ty::ConstKind::Expr(be)) => { - match (ae.kind, be.kind) { - (ty::ExprKind::Binop(a_binop), ty::ExprKind::Binop(b_binop)) - if a_binop == b_binop => {} - (ty::ExprKind::UnOp(a_unop), ty::ExprKind::UnOp(b_unop)) if a_unop == b_unop => {} - (ty::ExprKind::FunctionCall, ty::ExprKind::FunctionCall) => {} - (ty::ExprKind::Cast(a_kind), ty::ExprKind::Cast(b_kind)) if a_kind == b_kind => {} - _ => return Err(TypeError::ConstMismatch(expected_found(a, b))), - } - - let args = relation.relate(ae.args(), be.args())?; - return Ok(ty::Const::new_expr(tcx, ty::Expr::new(ae.kind, args))); - } - _ => false, - }; - if is_match { Ok(a) } else { Err(TypeError::ConstMismatch(expected_found(a, b))) } -} - -impl<'tcx> Relate<'tcx> for &'tcx ty::List> { - fn relate>( +impl<'tcx> Relate> for &'tcx ty::List> { + fn relate>>( relation: &mut R, a: Self, b: Self, @@ -703,44 +106,65 @@ impl<'tcx> Relate<'tcx> for &'tcx ty::List> { b_v.sort_by(|a, b| a.skip_binder().stable_cmp(tcx, &b.skip_binder())); b_v.dedup(); if a_v.len() != b_v.len() { - return Err(TypeError::ExistentialMismatch(expected_found(a, b))); + return Err(TypeError::ExistentialMismatch(ExpectedFound::new(true, a, b))); } let v = iter::zip(a_v, b_v).map(|(ep_a, ep_b)| { match (ep_a.skip_binder(), ep_b.skip_binder()) { - (ExistentialPredicate::Trait(a), ExistentialPredicate::Trait(b)) => Ok(ep_a - .rebind(ExistentialPredicate::Trait( - relation.relate(ep_a.rebind(a), ep_b.rebind(b))?.skip_binder(), - ))), - (ExistentialPredicate::Projection(a), ExistentialPredicate::Projection(b)) => { - Ok(ep_a.rebind(ExistentialPredicate::Projection( + (ty::ExistentialPredicate::Trait(a), ty::ExistentialPredicate::Trait(b)) => { + Ok(ep_a.rebind(ty::ExistentialPredicate::Trait( relation.relate(ep_a.rebind(a), ep_b.rebind(b))?.skip_binder(), ))) } - (ExistentialPredicate::AutoTrait(a), ExistentialPredicate::AutoTrait(b)) - if a == b => - { - Ok(ep_a.rebind(ExistentialPredicate::AutoTrait(a))) - } - _ => Err(TypeError::ExistentialMismatch(expected_found(a, b))), + ( + ty::ExistentialPredicate::Projection(a), + ty::ExistentialPredicate::Projection(b), + ) => Ok(ep_a.rebind(ty::ExistentialPredicate::Projection( + relation.relate(ep_a.rebind(a), ep_b.rebind(b))?.skip_binder(), + ))), + ( + ty::ExistentialPredicate::AutoTrait(a), + ty::ExistentialPredicate::AutoTrait(b), + ) if a == b => Ok(ep_a.rebind(ty::ExistentialPredicate::AutoTrait(a))), + _ => Err(TypeError::ExistentialMismatch(ExpectedFound::new(true, a, b))), } }); tcx.mk_poly_existential_predicates_from_iter(v) } } -impl<'tcx> Relate<'tcx> for GenericArgsRef<'tcx> { - fn relate>( +impl<'tcx> Relate> for hir::Safety { + fn relate>>( + _relation: &mut R, + a: hir::Safety, + b: hir::Safety, + ) -> RelateResult<'tcx, hir::Safety> { + if a != b { Err(TypeError::SafetyMismatch(ExpectedFound::new(true, a, b))) } else { Ok(a) } + } +} + +impl<'tcx> Relate> for abi::Abi { + fn relate>>( + _relation: &mut R, + a: abi::Abi, + b: abi::Abi, + ) -> RelateResult<'tcx, abi::Abi> { + if a == b { Ok(a) } else { Err(TypeError::AbiMismatch(ExpectedFound::new(true, a, b))) } + } +} + +impl<'tcx> Relate> for ty::GenericArgsRef<'tcx> { + fn relate>>( relation: &mut R, - a: GenericArgsRef<'tcx>, - b: GenericArgsRef<'tcx>, - ) -> RelateResult<'tcx, GenericArgsRef<'tcx>> { + a: ty::GenericArgsRef<'tcx>, + b: ty::GenericArgsRef<'tcx>, + ) -> RelateResult<'tcx, ty::GenericArgsRef<'tcx>> { relate_args_invariantly(relation, a, b) } } -impl<'tcx> Relate<'tcx> for ty::Region<'tcx> { - fn relate>( +impl<'tcx> Relate> for ty::Region<'tcx> { + fn relate>>( relation: &mut R, a: ty::Region<'tcx>, b: ty::Region<'tcx>, @@ -749,8 +173,8 @@ impl<'tcx> Relate<'tcx> for ty::Region<'tcx> { } } -impl<'tcx> Relate<'tcx> for ty::Const<'tcx> { - fn relate>( +impl<'tcx> Relate> for ty::Const<'tcx> { + fn relate>>( relation: &mut R, a: ty::Const<'tcx>, b: ty::Const<'tcx>, @@ -759,85 +183,70 @@ impl<'tcx> Relate<'tcx> for ty::Const<'tcx> { } } -impl<'tcx, T: Relate<'tcx>> Relate<'tcx> for ty::Binder<'tcx, T> { - fn relate>( +impl<'tcx> Relate> for ty::Expr<'tcx> { + fn relate>>( relation: &mut R, - a: ty::Binder<'tcx, T>, - b: ty::Binder<'tcx, T>, - ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> { - relation.binders(a, b) + ae: ty::Expr<'tcx>, + be: ty::Expr<'tcx>, + ) -> RelateResult<'tcx, ty::Expr<'tcx>> { + // FIXME(generic_const_exprs): is it possible to relate two consts which are not identical + // exprs? Should we care about that? + // FIXME(generic_const_exprs): relating the `ty()`s is a little weird since it is supposed to + // ICE If they mismatch. Unfortunately `ConstKind::Expr` is a little special and can be thought + // of as being generic over the argument types, however this is implicit so these types don't get + // related when we relate the args of the item this const arg is for. + match (ae.kind, be.kind) { + (ty::ExprKind::Binop(a_binop), ty::ExprKind::Binop(b_binop)) if a_binop == b_binop => {} + (ty::ExprKind::UnOp(a_unop), ty::ExprKind::UnOp(b_unop)) if a_unop == b_unop => {} + (ty::ExprKind::FunctionCall, ty::ExprKind::FunctionCall) => {} + (ty::ExprKind::Cast(a_kind), ty::ExprKind::Cast(b_kind)) if a_kind == b_kind => {} + _ => return Err(TypeError::Mismatch), + } + + let args = relation.relate(ae.args(), be.args())?; + Ok(ty::Expr::new(ae.kind, args)) } } -impl<'tcx> Relate<'tcx> for GenericArg<'tcx> { - fn relate>( +impl<'tcx> Relate> for ty::GenericArg<'tcx> { + fn relate>>( relation: &mut R, - a: GenericArg<'tcx>, - b: GenericArg<'tcx>, - ) -> RelateResult<'tcx, GenericArg<'tcx>> { + a: ty::GenericArg<'tcx>, + b: ty::GenericArg<'tcx>, + ) -> RelateResult<'tcx, ty::GenericArg<'tcx>> { match (a.unpack(), b.unpack()) { - (GenericArgKind::Lifetime(a_lt), GenericArgKind::Lifetime(b_lt)) => { + (ty::GenericArgKind::Lifetime(a_lt), ty::GenericArgKind::Lifetime(b_lt)) => { Ok(relation.relate(a_lt, b_lt)?.into()) } - (GenericArgKind::Type(a_ty), GenericArgKind::Type(b_ty)) => { + (ty::GenericArgKind::Type(a_ty), ty::GenericArgKind::Type(b_ty)) => { Ok(relation.relate(a_ty, b_ty)?.into()) } - (GenericArgKind::Const(a_ct), GenericArgKind::Const(b_ct)) => { + (ty::GenericArgKind::Const(a_ct), ty::GenericArgKind::Const(b_ct)) => { Ok(relation.relate(a_ct, b_ct)?.into()) } - (GenericArgKind::Lifetime(unpacked), x) => { + (ty::GenericArgKind::Lifetime(unpacked), x) => { bug!("impossible case reached: can't relate: {:?} with {:?}", unpacked, x) } - (GenericArgKind::Type(unpacked), x) => { + (ty::GenericArgKind::Type(unpacked), x) => { bug!("impossible case reached: can't relate: {:?} with {:?}", unpacked, x) } - (GenericArgKind::Const(unpacked), x) => { + (ty::GenericArgKind::Const(unpacked), x) => { bug!("impossible case reached: can't relate: {:?} with {:?}", unpacked, x) } } } } -impl<'tcx> Relate<'tcx> for ty::PredicatePolarity { - fn relate>( - _relation: &mut R, - a: ty::PredicatePolarity, - b: ty::PredicatePolarity, - ) -> RelateResult<'tcx, ty::PredicatePolarity> { - if a != b { Err(TypeError::PolarityMismatch(expected_found(a, b))) } else { Ok(a) } - } -} - -impl<'tcx> Relate<'tcx> for ty::TraitPredicate<'tcx> { - fn relate>( - relation: &mut R, - a: ty::TraitPredicate<'tcx>, - b: ty::TraitPredicate<'tcx>, - ) -> RelateResult<'tcx, ty::TraitPredicate<'tcx>> { - Ok(ty::TraitPredicate { - trait_ref: relation.relate(a.trait_ref, b.trait_ref)?, - polarity: relation.relate(a.polarity, b.polarity)?, - }) - } -} - -impl<'tcx> Relate<'tcx> for Term<'tcx> { - fn relate>( +impl<'tcx> Relate> for ty::Term<'tcx> { + fn relate>>( relation: &mut R, a: Self, b: Self, ) -> RelateResult<'tcx, Self> { Ok(match (a.unpack(), b.unpack()) { - (TermKind::Ty(a), TermKind::Ty(b)) => relation.relate(a, b)?.into(), - (TermKind::Const(a), TermKind::Const(b)) => relation.relate(a, b)?.into(), + (ty::TermKind::Ty(a), ty::TermKind::Ty(b)) => relation.relate(a, b)?.into(), + (ty::TermKind::Const(a), ty::TermKind::Const(b)) => relation.relate(a, b)?.into(), _ => return Err(TypeError::Mismatch), }) } } - -/////////////////////////////////////////////////////////////////////////// -// Error handling - -pub fn expected_found(a: T, b: T) -> ExpectedFound { - ExpectedFound::new(true, a, b) -} diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index c83f6b0b9ec6..ba9ed0d5b70a 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -810,6 +810,31 @@ impl<'tcx> rustc_type_ir::inherent::Ty> for Ty<'tcx> { Ty::new_alias(interner, kind, alias_ty) } + fn new_error(interner: TyCtxt<'tcx>, guar: ErrorGuaranteed) -> Self { + Ty::new_error(interner, guar) + } + + fn new_adt( + interner: TyCtxt<'tcx>, + adt_def: ty::AdtDef<'tcx>, + args: ty::GenericArgsRef<'tcx>, + ) -> Self { + Ty::new_adt(interner, adt_def, args) + } + + fn new_foreign(interner: TyCtxt<'tcx>, def_id: DefId) -> Self { + Ty::new_foreign(interner, def_id) + } + + fn new_dynamic( + interner: TyCtxt<'tcx>, + preds: &'tcx List>, + region: ty::Region<'tcx>, + kind: ty::DynKind, + ) -> Self { + Ty::new_dynamic(interner, preds, region, kind) + } + fn new_coroutine( interner: TyCtxt<'tcx>, def_id: DefId, @@ -818,6 +843,51 @@ impl<'tcx> rustc_type_ir::inherent::Ty> for Ty<'tcx> { Ty::new_coroutine(interner, def_id, args) } + fn new_coroutine_closure( + interner: TyCtxt<'tcx>, + def_id: DefId, + args: ty::GenericArgsRef<'tcx>, + ) -> Self { + Ty::new_coroutine_closure(interner, def_id, args) + } + + fn new_closure(interner: TyCtxt<'tcx>, def_id: DefId, args: ty::GenericArgsRef<'tcx>) -> Self { + Ty::new_closure(interner, def_id, args) + } + + fn new_coroutine_witness( + interner: TyCtxt<'tcx>, + def_id: DefId, + args: ty::GenericArgsRef<'tcx>, + ) -> Self { + Ty::new_coroutine_witness(interner, def_id, args) + } + + fn new_ptr(interner: TyCtxt<'tcx>, ty: Self, mutbl: hir::Mutability) -> Self { + Ty::new_ptr(interner, ty, mutbl) + } + + fn new_ref( + interner: TyCtxt<'tcx>, + region: ty::Region<'tcx>, + ty: Self, + mutbl: hir::Mutability, + ) -> Self { + Ty::new_ref(interner, region, ty, mutbl) + } + + fn new_array_with_const_len(interner: TyCtxt<'tcx>, ty: Self, len: ty::Const<'tcx>) -> Self { + Ty::new_array_with_const_len(interner, ty, len) + } + + fn new_slice(interner: TyCtxt<'tcx>, ty: Self) -> Self { + Ty::new_slice(interner, ty) + } + + fn new_tup(interner: TyCtxt<'tcx>, tys: &[Ty<'tcx>]) -> Self { + Ty::new_tup(interner, tys) + } + fn new_tup_from_iter(interner: TyCtxt<'tcx>, iter: It) -> T::Output where It: Iterator, @@ -844,6 +914,18 @@ impl<'tcx> rustc_type_ir::inherent::Ty> for Ty<'tcx> { ) -> Self { Ty::from_coroutine_closure_kind(interner, kind) } + + fn new_fn_def(interner: TyCtxt<'tcx>, def_id: DefId, args: ty::GenericArgsRef<'tcx>) -> Self { + Ty::new_fn_def(interner, def_id, args) + } + + fn new_fn_ptr(interner: TyCtxt<'tcx>, sig: ty::Binder<'tcx, ty::FnSig<'tcx>>) -> Self { + Ty::new_fn_ptr(interner, sig) + } + + fn new_pat(interner: TyCtxt<'tcx>, ty: Self, pat: ty::Pattern<'tcx>) -> Self { + Ty::new_pat(interner, ty, pat) + } } /// Type utilities @@ -1812,43 +1894,6 @@ impl<'tcx> rustc_type_ir::inherent::Tys> for &'tcx ty::List { - /// No additional information - this is the default. - /// We will not add any additional information to error messages. - #[default] - None, - /// We switched our variance because a generic argument occurs inside - /// the invariant generic argument of another type. - Invariant { - /// The generic type containing the generic parameter - /// that changes the variance (e.g. `*mut T`, `MyStruct`) - ty: Ty<'tcx>, - /// The index of the generic parameter being used - /// (e.g. `0` for `*mut T`, `1` for `MyStruct<'CovariantParam, 'InvariantParam>`) - param_index: u32, - }, -} - -impl<'tcx> VarianceDiagInfo<'tcx> { - /// Mirrors `Variance::xform` - used to 'combine' the existing - /// and new `VarianceDiagInfo`s when our variance changes. - pub fn xform(self, other: VarianceDiagInfo<'tcx>) -> VarianceDiagInfo<'tcx> { - // For now, just use the first `VarianceDiagInfo::Invariant` that we see - match self { - VarianceDiagInfo::None => other, - VarianceDiagInfo::Invariant { .. } => self, - } - } -} - // Some types are used a lot. Make sure they don't unintentionally get bigger. #[cfg(target_pointer_width = "64")] mod size_asserts { diff --git a/compiler/rustc_next_trait_solver/Cargo.toml b/compiler/rustc_next_trait_solver/Cargo.toml index 8bcc21d82f85..c30d21fd7843 100644 --- a/compiler/rustc_next_trait_solver/Cargo.toml +++ b/compiler/rustc_next_trait_solver/Cargo.toml @@ -4,13 +4,16 @@ version = "0.0.0" edition = "2021" [dependencies] -rustc_type_ir = { path = "../rustc_type_ir", default-features = false } +# tidy-alphabetical-start derivative = "2.2.0" -rustc_macros = { path = "../rustc_macros", optional = true } -rustc_type_ir_macros = { path = "../rustc_type_ir_macros" } -rustc_serialize = { path = "../rustc_serialize", optional = true } -rustc_data_structures = { path = "../rustc_data_structures", optional = true } rustc_ast_ir = { path = "../rustc_ast_ir", default-features = false } +rustc_data_structures = { path = "../rustc_data_structures", optional = true } +rustc_macros = { path = "../rustc_macros", optional = true } +rustc_serialize = { path = "../rustc_serialize", optional = true } +rustc_type_ir = { path = "../rustc_type_ir", default-features = false } +rustc_type_ir_macros = { path = "../rustc_type_ir_macros" } +tracing = "0.1" +# tidy-alphabetical-end [features] default = ["nightly"] diff --git a/compiler/rustc_next_trait_solver/src/resolve.rs b/compiler/rustc_next_trait_solver/src/resolve.rs index 92e05cc4901a..5c00b6978d69 100644 --- a/compiler/rustc_next_trait_solver/src/resolve.rs +++ b/compiler/rustc_next_trait_solver/src/resolve.rs @@ -7,11 +7,11 @@ use rustc_type_ir::{self as ty, InferCtxtLike, Interner}; // EAGER RESOLUTION /// Resolves ty, region, and const vars to their inferred values or their root vars. -pub struct EagerResolver< - 'a, +pub struct EagerResolver<'a, Infcx, I = ::Interner> +where Infcx: InferCtxtLike, - I: Interner = ::Interner, -> { + I: Interner, +{ infcx: &'a Infcx, } diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs index f90e47110376..b522022c2067 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs @@ -37,11 +37,11 @@ pub(super) mod canonical; mod probe; mod select; -pub struct EvalCtxt< - 'a, +pub struct EvalCtxt<'a, Infcx, I = ::Interner> +where Infcx: InferCtxtLike, - I: Interner = ::Interner, -> { + I: Interner, +{ /// The inference context that backs (mostly) inference and placeholder terms /// instantiated while solving goals. /// diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 9e6ae9125150..4a935f4a64a0 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -32,6 +32,8 @@ use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::{Diag, EmissionGuarantee}; use rustc_hir as hir; use rustc_hir::def_id::DefId; +use rustc_infer::infer::relate::MatchAgainstFreshVars; +use rustc_infer::infer::relate::TypeRelation; use rustc_infer::infer::BoundRegionConversionTime; use rustc_infer::infer::BoundRegionConversionTime::HigherRankedType; use rustc_infer::infer::DefineOpaqueTypes; @@ -40,11 +42,9 @@ use rustc_middle::bug; use rustc_middle::dep_graph::dep_kinds; use rustc_middle::dep_graph::DepNodeIndex; use rustc_middle::mir::interpret::ErrorHandled; -use rustc_middle::ty::_match::MatchAgainstFreshVars; use rustc_middle::ty::abstract_const::NotConstEvaluatable; use rustc_middle::ty::error::TypeErrorToStringExt; use rustc_middle::ty::print::PrintTraitRefExt as _; -use rustc_middle::ty::relate::TypeRelation; use rustc_middle::ty::GenericArgsRef; use rustc_middle::ty::{self, PolyProjectionPredicate, Upcast}; use rustc_middle::ty::{Ty, TyCtxt, TypeFoldable, TypeVisitableExt}; diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs index 94874a6acfce..205a1e5f100e 100644 --- a/compiler/rustc_type_ir/src/inherent.rs +++ b/compiler/rustc_type_ir/src/inherent.rs @@ -7,7 +7,10 @@ use std::fmt::Debug; use std::hash::Hash; use std::ops::Deref; +use rustc_ast_ir::Mutability; + use crate::fold::{TypeFoldable, TypeSuperFoldable}; +use crate::relate::Relate; use crate::visit::{Flags, TypeSuperVisitable, TypeVisitable}; use crate::{self as ty, CollectAndApply, DebugWithInfcx, Interner, UpcastFrom}; @@ -21,6 +24,7 @@ pub trait Ty>: + IntoKind> + TypeSuperVisitable + TypeSuperFoldable + + Relate + Flags { fn new_bool(interner: I) -> Self; @@ -35,8 +39,37 @@ pub trait Ty>: fn new_alias(interner: I, kind: ty::AliasTyKind, alias_ty: ty::AliasTy) -> Self; + fn new_error(interner: I, guar: I::ErrorGuaranteed) -> Self; + + fn new_adt(interner: I, adt_def: I::AdtDef, args: I::GenericArgs) -> Self; + + fn new_foreign(interner: I, def_id: I::DefId) -> Self; + + fn new_dynamic( + interner: I, + preds: I::BoundExistentialPredicates, + region: I::Region, + kind: ty::DynKind, + ) -> Self; + fn new_coroutine(interner: I, def_id: I::DefId, args: I::GenericArgs) -> Self; + fn new_coroutine_closure(interner: I, def_id: I::DefId, args: I::GenericArgs) -> Self; + + fn new_closure(interner: I, def_id: I::DefId, args: I::GenericArgs) -> Self; + + fn new_coroutine_witness(interner: I, def_id: I::DefId, args: I::GenericArgs) -> Self; + + fn new_ptr(interner: I, ty: Self, mutbl: Mutability) -> Self; + + fn new_ref(interner: I, region: I::Region, ty: Self, mutbl: Mutability) -> Self; + + fn new_array_with_const_len(interner: I, ty: Self, len: I::Const) -> Self; + + fn new_slice(interner: I, ty: Self) -> Self; + + fn new_tup(interner: I, tys: &[I::Ty]) -> Self; + fn new_tup_from_iter(interner: I, iter: It) -> T::Output where It: Iterator, @@ -49,6 +82,12 @@ pub trait Ty>: fn from_closure_kind(interner: I, kind: ty::ClosureKind) -> Self; fn from_coroutine_closure_kind(interner: I, kind: ty::ClosureKind) -> Self; + + fn new_fn_def(interner: I, def_id: I::DefId, args: I::GenericArgs) -> Self; + + fn new_fn_ptr(interner: I, sig: ty::Binder>) -> Self; + + fn new_pat(interner: I, ty: Self, pat: I::Pat) -> Self; } pub trait Tys>: @@ -84,6 +123,7 @@ pub trait Region>: + IntoKind> + Flags + TypeVisitable + + Relate { fn new_bound(interner: I, debruijn: ty::DebruijnIndex, var: I::BoundRegion) -> Self; @@ -102,8 +142,11 @@ pub trait Const>: + IntoKind> + TypeSuperVisitable + TypeSuperFoldable + + Relate + Flags { + fn try_to_target_usize(self, interner: I) -> Option; + fn new_infer(interner: I, var: ty::InferConst) -> Self; fn new_var(interner: I, var: ty::ConstVid) -> Self; @@ -113,6 +156,8 @@ pub trait Const>: fn new_anon_bound(interner: I, debruijn: ty::DebruijnIndex, var: ty::BoundVar) -> Self; fn new_unevaluated(interner: I, uv: ty::UnevaluatedConst) -> Self; + + fn new_expr(interner: I, expr: I::ExprConst) -> Self; } pub trait GenericsOf> { @@ -128,13 +173,14 @@ pub trait GenericArgs>: + Deref> + Default + TypeFoldable + + Relate { fn type_at(self, i: usize) -> I::Ty; fn identity_for_item(interner: I, def_id: I::DefId) -> I::GenericArgs; fn extend_with_error( - tcx: I, + interner: I, def_id: I::DefId, original_args: &[I::GenericArg], ) -> I::GenericArgs; @@ -193,3 +239,11 @@ pub trait BoundVarLike { pub trait ParamLike { fn index(self) -> u32; } + +pub trait AdtDef: Copy + Debug + Hash + Eq { + fn def_id(self) -> I::DefId; +} + +pub trait Features: Copy { + fn generic_const_exprs(self) -> bool; +} diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index 6ebb434299b3..ad1d2753b282 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -6,6 +6,7 @@ use std::ops::Deref; use crate::fold::TypeFoldable; use crate::inherent::*; use crate::ir_print::IrPrint; +use crate::relate::Relate; use crate::solve::inspect::CanonicalGoalEvaluationStep; use crate::visit::{Flags, TypeSuperVisitable, TypeVisitable}; use crate::{self as ty, DebugWithInfcx}; @@ -25,8 +26,8 @@ pub trait Interner: + IrPrint> + IrPrint> { - type DefId: Copy + Debug + Hash + Eq + TypeVisitable; - type AdtDef: Copy + Debug + Hash + Eq; + type DefId: Copy + Debug + Hash + Eq + TypeFoldable; + type AdtDef: AdtDef; type GenericArgs: GenericArgs; type GenericArgsSlice: Copy + Debug + Hash + Eq + Deref; @@ -35,8 +36,15 @@ pub trait Interner: + Hash + Eq + IntoKind> - + TypeVisitable; - type Term: Copy + Debug + Hash + Eq + IntoKind> + TypeVisitable; + + TypeVisitable + + Relate; + type Term: Copy + + Debug + + Hash + + Eq + + IntoKind> + + TypeFoldable + + Relate; type BoundVarKinds: Copy + Debug @@ -66,11 +74,11 @@ pub trait Interner: // Things stored inside of tys type ErrorGuaranteed: Copy + Debug + Hash + Eq; - type BoundExistentialPredicates: Copy + DebugWithInfcx + Hash + Eq; + type BoundExistentialPredicates: Copy + DebugWithInfcx + Hash + Eq + Relate; type AllocId: Copy + Debug + Hash + Eq; - type Pat: Copy + Debug + Hash + Eq + DebugWithInfcx; - type Safety: Safety; - type Abi: Abi; + type Pat: Copy + Debug + Hash + Eq + DebugWithInfcx + Relate; + type Safety: Safety + TypeFoldable + Relate; + type Abi: Abi + TypeFoldable + Relate; // Kinds of consts type Const: Const; @@ -78,7 +86,7 @@ pub trait Interner: type ParamConst: Copy + Debug + Hash + Eq + ParamLike; type BoundConst: Copy + Debug + Hash + Eq + BoundVarLike; type ValueConst: Copy + Debug + Hash + Eq; - type ExprConst: Copy + DebugWithInfcx + Hash + Eq; + type ExprConst: Copy + DebugWithInfcx + Hash + Eq + Relate; // Kinds of regions type Region: Region; @@ -93,11 +101,16 @@ pub trait Interner: type Clause: Clause; type Clauses: Copy + Debug + Hash + Eq + TypeSuperVisitable + Flags; + fn expand_abstract_consts>(self, t: T) -> T; + fn mk_canonical_var_infos(self, infos: &[ty::CanonicalVarInfo]) -> Self::CanonicalVars; type GenericsOf: GenericsOf; fn generics_of(self, def_id: Self::DefId) -> Self::GenericsOf; + type VariancesOf: Copy + Debug + Deref; + fn variances_of(self, def_id: Self::DefId) -> Self::VariancesOf; + // FIXME: Remove after uplifting `EarlyBinder` fn type_of(self, def_id: Self::DefId) -> ty::EarlyBinder; @@ -112,7 +125,12 @@ pub trait Interner: ) -> (ty::TraitRef, Self::GenericArgsSlice); fn mk_args(self, args: &[Self::GenericArg]) -> Self::GenericArgs; - fn mk_args_from_iter(self, args: impl Iterator) -> Self::GenericArgs; + + fn mk_args_from_iter(self, args: I) -> T::Output + where + I: Iterator, + T: CollectAndApply; + fn check_and_mk_args( self, def_id: Self::DefId, @@ -124,9 +142,17 @@ pub trait Interner: step: CanonicalGoalEvaluationStep, ) -> Self::CanonicalGoalEvaluationStepRef; + fn mk_type_list_from_iter(self, args: I) -> T::Output + where + I: Iterator, + T: CollectAndApply; + fn parent(self, def_id: Self::DefId) -> Self::DefId; fn recursion_limit(self) -> usize; + + type Features: Features; + fn features(self) -> Self::Features; } /// Imagine you have a function `F: FnOnce(&[T]) -> R`, plus an iterator `iter` diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs index 7d363daa420a..737164689303 100644 --- a/compiler/rustc_type_ir/src/lib.rs +++ b/compiler/rustc_type_ir/src/lib.rs @@ -31,6 +31,7 @@ pub mod fold; pub mod inherent; pub mod ir_print; pub mod lift; +pub mod relate; pub mod solve; // These modules are not `pub` since they are glob-imported. diff --git a/compiler/rustc_type_ir/src/relate.rs b/compiler/rustc_type_ir/src/relate.rs new file mode 100644 index 000000000000..cae1d13020d3 --- /dev/null +++ b/compiler/rustc_type_ir/src/relate.rs @@ -0,0 +1,666 @@ +use std::iter; + +use rustc_ast_ir::Mutability; +use rustc_type_ir::error::{ExpectedFound, TypeError}; +use rustc_type_ir::fold::TypeFoldable; +use rustc_type_ir::inherent::*; +use rustc_type_ir::{self as ty, Interner}; +use tracing::{debug, instrument}; + +pub type RelateResult = Result>; + +/// Extra information about why we ended up with a particular variance. +/// This is only used to add more information to error messages, and +/// has no effect on soundness. While choosing the 'wrong' `VarianceDiagInfo` +/// may lead to confusing notes in error messages, it will never cause +/// a miscompilation or unsoundness. +/// +/// When in doubt, use `VarianceDiagInfo::default()` +#[derive(derivative::Derivative)] +#[derivative( + Copy(bound = ""), + Clone(bound = ""), + Debug(bound = ""), + Default(bound = ""), + PartialEq(bound = ""), + Eq(bound = "") +)] +pub enum VarianceDiagInfo { + /// No additional information - this is the default. + /// We will not add any additional information to error messages. + #[derivative(Default)] + None, + /// We switched our variance because a generic argument occurs inside + /// the invariant generic argument of another type. + Invariant { + /// The generic type containing the generic parameter + /// that changes the variance (e.g. `*mut T`, `MyStruct`) + ty: I::Ty, + /// The index of the generic parameter being used + /// (e.g. `0` for `*mut T`, `1` for `MyStruct<'CovariantParam, 'InvariantParam>`) + param_index: u32, + }, +} + +impl VarianceDiagInfo { + /// Mirrors `Variance::xform` - used to 'combine' the existing + /// and new `VarianceDiagInfo`s when our variance changes. + pub fn xform(self, other: VarianceDiagInfo) -> VarianceDiagInfo { + // For now, just use the first `VarianceDiagInfo::Invariant` that we see + match self { + VarianceDiagInfo::None => other, + VarianceDiagInfo::Invariant { .. } => self, + } + } +} + +pub trait TypeRelation: Sized { + fn tcx(&self) -> I; + + /// Returns a static string we can use for printouts. + fn tag(&self) -> &'static str; + + /// Generic relation routine suitable for most anything. + fn relate>(&mut self, a: T, b: T) -> RelateResult { + Relate::relate(self, a, b) + } + + /// Relate the two args for the given item. The default + /// is to look up the variance for the item and proceed + /// accordingly. + fn relate_item_args( + &mut self, + item_def_id: I::DefId, + a_arg: I::GenericArgs, + b_arg: I::GenericArgs, + ) -> RelateResult { + debug!( + "relate_item_args(item_def_id={:?}, a_arg={:?}, b_arg={:?})", + item_def_id, a_arg, b_arg + ); + + let tcx = self.tcx(); + let opt_variances = tcx.variances_of(item_def_id); + relate_args_with_variances(self, item_def_id, &opt_variances, a_arg, b_arg, true) + } + + /// Switch variance for the purpose of relating `a` and `b`. + fn relate_with_variance>( + &mut self, + variance: ty::Variance, + info: VarianceDiagInfo, + a: T, + b: T, + ) -> RelateResult; + + // Overridable relations. You shouldn't typically call these + // directly, instead call `relate()`, which in turn calls + // these. This is both more uniform but also allows us to add + // additional hooks for other types in the future if needed + // without making older code, which called `relate`, obsolete. + + fn tys(&mut self, a: I::Ty, b: I::Ty) -> RelateResult; + + fn regions(&mut self, a: I::Region, b: I::Region) -> RelateResult; + + fn consts(&mut self, a: I::Const, b: I::Const) -> RelateResult; + + fn binders( + &mut self, + a: ty::Binder, + b: ty::Binder, + ) -> RelateResult> + where + T: Relate; +} + +pub trait Relate: TypeFoldable + PartialEq + Copy { + fn relate>(relation: &mut R, a: Self, b: Self) -> RelateResult; +} + +/////////////////////////////////////////////////////////////////////////// +// Relate impls + +#[inline] +pub fn relate_args_invariantly>( + relation: &mut R, + a_arg: I::GenericArgs, + b_arg: I::GenericArgs, +) -> RelateResult { + relation.tcx().mk_args_from_iter(iter::zip(a_arg, b_arg).map(|(a, b)| { + relation.relate_with_variance(ty::Variance::Invariant, VarianceDiagInfo::default(), a, b) + })) +} + +pub fn relate_args_with_variances>( + relation: &mut R, + ty_def_id: I::DefId, + variances: &[ty::Variance], + a_arg: I::GenericArgs, + b_arg: I::GenericArgs, + fetch_ty_for_diag: bool, +) -> RelateResult { + let tcx = relation.tcx(); + + let mut cached_ty = None; + let params = iter::zip(a_arg, b_arg).enumerate().map(|(i, (a, b))| { + let variance = variances[i]; + let variance_info = if variance == ty::Variance::Invariant && fetch_ty_for_diag { + let ty = + *cached_ty.get_or_insert_with(|| tcx.type_of(ty_def_id).instantiate(tcx, &a_arg)); + VarianceDiagInfo::Invariant { ty, param_index: i.try_into().unwrap() } + } else { + VarianceDiagInfo::default() + }; + relation.relate_with_variance(variance, variance_info, a, b) + }); + + tcx.mk_args_from_iter(params) +} + +impl Relate for ty::FnSig { + fn relate>( + relation: &mut R, + a: ty::FnSig, + b: ty::FnSig, + ) -> RelateResult> { + let tcx = relation.tcx(); + + if a.c_variadic != b.c_variadic { + return Err(TypeError::VariadicMismatch({ + let a = a.c_variadic; + let b = b.c_variadic; + ExpectedFound::new(true, a, b) + })); + } + let safety = relation.relate(a.safety, b.safety)?; + let abi = relation.relate(a.abi, b.abi)?; + + let a_inputs = a.inputs(); + let b_inputs = b.inputs(); + + if a_inputs.len() != b_inputs.len() { + return Err(TypeError::ArgCount); + } + + let inputs_and_output = iter::zip(a_inputs.iter(), b_inputs.iter()) + .map(|(&a, &b)| ((a, b), false)) + .chain(iter::once(((a.output(), b.output()), true))) + .map(|((a, b), is_output)| { + if is_output { + relation.relate(a, b) + } else { + relation.relate_with_variance( + ty::Variance::Contravariant, + VarianceDiagInfo::default(), + a, + b, + ) + } + }) + .enumerate() + .map(|(i, r)| match r { + Err(TypeError::Sorts(exp_found) | TypeError::ArgumentSorts(exp_found, _)) => { + Err(TypeError::ArgumentSorts(exp_found, i)) + } + Err(TypeError::Mutability | TypeError::ArgumentMutability(_)) => { + Err(TypeError::ArgumentMutability(i)) + } + r => r, + }); + Ok(ty::FnSig { + inputs_and_output: tcx.mk_type_list_from_iter(inputs_and_output)?, + c_variadic: a.c_variadic, + safety, + abi, + }) + } +} + +impl Relate for ty::BoundConstness { + fn relate>( + _relation: &mut R, + a: ty::BoundConstness, + b: ty::BoundConstness, + ) -> RelateResult { + if a != b { + Err(TypeError::ConstnessMismatch(ExpectedFound::new(true, a, b))) + } else { + Ok(a) + } + } +} + +impl Relate for ty::AliasTy { + fn relate>( + relation: &mut R, + a: ty::AliasTy, + b: ty::AliasTy, + ) -> RelateResult> { + if a.def_id != b.def_id { + Err(TypeError::ProjectionMismatched({ + let a = a.def_id; + let b = b.def_id; + ExpectedFound::new(true, a, b) + })) + } else { + let args = match a.kind(relation.tcx()) { + ty::Opaque => relate_args_with_variances( + relation, + a.def_id, + &relation.tcx().variances_of(a.def_id), + a.args, + b.args, + false, // do not fetch `type_of(a_def_id)`, as it will cause a cycle + )?, + ty::Projection | ty::Weak | ty::Inherent => { + relate_args_invariantly(relation, a.args, b.args)? + } + }; + Ok(ty::AliasTy::new(relation.tcx(), a.def_id, args)) + } + } +} + +impl Relate for ty::AliasTerm { + fn relate>( + relation: &mut R, + a: ty::AliasTerm, + b: ty::AliasTerm, + ) -> RelateResult> { + if a.def_id != b.def_id { + Err(TypeError::ProjectionMismatched({ + let a = a.def_id; + let b = b.def_id; + ExpectedFound::new(true, a, b) + })) + } else { + let args = match a.kind(relation.tcx()) { + ty::AliasTermKind::OpaqueTy => relate_args_with_variances( + relation, + a.def_id, + &relation.tcx().variances_of(a.def_id), + a.args, + b.args, + false, // do not fetch `type_of(a_def_id)`, as it will cause a cycle + )?, + ty::AliasTermKind::ProjectionTy + | ty::AliasTermKind::WeakTy + | ty::AliasTermKind::InherentTy + | ty::AliasTermKind::UnevaluatedConst + | ty::AliasTermKind::ProjectionConst => { + relate_args_invariantly(relation, a.args, b.args)? + } + }; + Ok(ty::AliasTerm::new(relation.tcx(), a.def_id, args)) + } + } +} + +impl Relate for ty::ExistentialProjection { + fn relate>( + relation: &mut R, + a: ty::ExistentialProjection, + b: ty::ExistentialProjection, + ) -> RelateResult> { + if a.def_id != b.def_id { + Err(TypeError::ProjectionMismatched({ + let a = a.def_id; + let b = b.def_id; + ExpectedFound::new(true, a, b) + })) + } else { + let term = relation.relate_with_variance( + ty::Variance::Invariant, + VarianceDiagInfo::default(), + a.term, + b.term, + )?; + let args = relation.relate_with_variance( + ty::Variance::Invariant, + VarianceDiagInfo::default(), + a.args, + b.args, + )?; + Ok(ty::ExistentialProjection { def_id: a.def_id, args, term }) + } + } +} + +impl Relate for ty::TraitRef { + fn relate>( + relation: &mut R, + a: ty::TraitRef, + b: ty::TraitRef, + ) -> RelateResult> { + // Different traits cannot be related. + if a.def_id != b.def_id { + Err(TypeError::Traits({ + let a = a.def_id; + let b = b.def_id; + ExpectedFound::new(true, a, b) + })) + } else { + let args = relate_args_invariantly(relation, a.args, b.args)?; + Ok(ty::TraitRef::new(relation.tcx(), a.def_id, args)) + } + } +} + +impl Relate for ty::ExistentialTraitRef { + fn relate>( + relation: &mut R, + a: ty::ExistentialTraitRef, + b: ty::ExistentialTraitRef, + ) -> RelateResult> { + // Different traits cannot be related. + if a.def_id != b.def_id { + Err(TypeError::Traits({ + let a = a.def_id; + let b = b.def_id; + ExpectedFound::new(true, a, b) + })) + } else { + let args = relate_args_invariantly(relation, a.args, b.args)?; + Ok(ty::ExistentialTraitRef { def_id: a.def_id, args }) + } + } +} + +/// Relates `a` and `b` structurally, calling the relation for all nested values. +/// Any semantic equality, e.g. of projections, and inference variables have to be +/// handled by the caller. +#[instrument(level = "trace", skip(relation), ret)] +pub fn structurally_relate_tys>( + relation: &mut R, + a: I::Ty, + b: I::Ty, +) -> RelateResult { + let tcx = relation.tcx(); + match (a.kind(), b.kind()) { + (ty::Infer(_), _) | (_, ty::Infer(_)) => { + // The caller should handle these cases! + panic!("var types encountered in structurally_relate_tys") + } + + (ty::Bound(..), _) | (_, ty::Bound(..)) => { + panic!("bound types encountered in structurally_relate_tys") + } + + (ty::Error(guar), _) | (_, ty::Error(guar)) => Ok(Ty::new_error(tcx, guar)), + + (ty::Never, _) + | (ty::Char, _) + | (ty::Bool, _) + | (ty::Int(_), _) + | (ty::Uint(_), _) + | (ty::Float(_), _) + | (ty::Str, _) + if a == b => + { + Ok(a) + } + + (ty::Param(a_p), ty::Param(b_p)) if a_p.index() == b_p.index() => { + // FIXME: Put this back + //debug_assert_eq!(a_p.name(), b_p.name(), "param types with same index differ in name"); + Ok(a) + } + + (ty::Placeholder(p1), ty::Placeholder(p2)) if p1 == p2 => Ok(a), + + (ty::Adt(a_def, a_args), ty::Adt(b_def, b_args)) if a_def == b_def => { + let args = relation.relate_item_args(a_def.def_id(), a_args, b_args)?; + Ok(Ty::new_adt(tcx, a_def, args)) + } + + (ty::Foreign(a_id), ty::Foreign(b_id)) if a_id == b_id => Ok(Ty::new_foreign(tcx, a_id)), + + (ty::Dynamic(a_obj, a_region, a_repr), ty::Dynamic(b_obj, b_region, b_repr)) + if a_repr == b_repr => + { + Ok(Ty::new_dynamic( + tcx, + relation.relate(a_obj, b_obj)?, + relation.relate(a_region, b_region)?, + a_repr, + )) + } + + (ty::Coroutine(a_id, a_args), ty::Coroutine(b_id, b_args)) if a_id == b_id => { + // All Coroutine types with the same id represent + // the (anonymous) type of the same coroutine expression. So + // all of their regions should be equated. + let args = relate_args_invariantly(relation, a_args, b_args)?; + Ok(Ty::new_coroutine(tcx, a_id, args)) + } + + (ty::CoroutineWitness(a_id, a_args), ty::CoroutineWitness(b_id, b_args)) + if a_id == b_id => + { + // All CoroutineWitness types with the same id represent + // the (anonymous) type of the same coroutine expression. So + // all of their regions should be equated. + let args = relate_args_invariantly(relation, a_args, b_args)?; + Ok(Ty::new_coroutine_witness(tcx, a_id, args)) + } + + (ty::Closure(a_id, a_args), ty::Closure(b_id, b_args)) if a_id == b_id => { + // All Closure types with the same id represent + // the (anonymous) type of the same closure expression. So + // all of their regions should be equated. + let args = relate_args_invariantly(relation, a_args, b_args)?; + Ok(Ty::new_closure(tcx, a_id, args)) + } + + (ty::CoroutineClosure(a_id, a_args), ty::CoroutineClosure(b_id, b_args)) + if a_id == b_id => + { + let args = relate_args_invariantly(relation, a_args, b_args)?; + Ok(Ty::new_coroutine_closure(tcx, a_id, args)) + } + + (ty::RawPtr(a_ty, a_mutbl), ty::RawPtr(b_ty, b_mutbl)) => { + if a_mutbl != b_mutbl { + return Err(TypeError::Mutability); + } + + let (variance, info) = match a_mutbl { + Mutability::Not => (ty::Variance::Covariant, VarianceDiagInfo::None), + Mutability::Mut => { + (ty::Variance::Invariant, VarianceDiagInfo::Invariant { ty: a, param_index: 0 }) + } + }; + + let ty = relation.relate_with_variance(variance, info, a_ty, b_ty)?; + + Ok(Ty::new_ptr(tcx, ty, a_mutbl)) + } + + (ty::Ref(a_r, a_ty, a_mutbl), ty::Ref(b_r, b_ty, b_mutbl)) => { + if a_mutbl != b_mutbl { + return Err(TypeError::Mutability); + } + + let (variance, info) = match a_mutbl { + Mutability::Not => (ty::Variance::Covariant, VarianceDiagInfo::None), + Mutability::Mut => { + (ty::Variance::Invariant, VarianceDiagInfo::Invariant { ty: a, param_index: 0 }) + } + }; + + let r = relation.relate(a_r, b_r)?; + let ty = relation.relate_with_variance(variance, info, a_ty, b_ty)?; + + Ok(Ty::new_ref(tcx, r, ty, a_mutbl)) + } + + (ty::Array(a_t, sz_a), ty::Array(b_t, sz_b)) => { + let t = relation.relate(a_t, b_t)?; + match relation.relate(sz_a, sz_b) { + Ok(sz) => Ok(Ty::new_array_with_const_len(tcx, t, sz)), + Err(err) => { + // Check whether the lengths are both concrete/known values, + // but are unequal, for better diagnostics. + let sz_a = sz_a.try_to_target_usize(tcx); + let sz_b = sz_b.try_to_target_usize(tcx); + + match (sz_a, sz_b) { + (Some(sz_a_val), Some(sz_b_val)) if sz_a_val != sz_b_val => Err( + TypeError::FixedArraySize(ExpectedFound::new(true, sz_a_val, sz_b_val)), + ), + _ => Err(err), + } + } + } + } + + (ty::Slice(a_t), ty::Slice(b_t)) => { + let t = relation.relate(a_t, b_t)?; + Ok(Ty::new_slice(tcx, t)) + } + + (ty::Tuple(as_), ty::Tuple(bs)) => { + if as_.len() == bs.len() { + Ok(Ty::new_tup_from_iter( + tcx, + iter::zip(as_, bs).map(|(a, b)| relation.relate(a, b)), + )?) + } else if !(as_.is_empty() || bs.is_empty()) { + Err(TypeError::TupleSize(ExpectedFound::new(true, as_.len(), bs.len()))) + } else { + Err(TypeError::Sorts(ExpectedFound::new(true, a, b))) + } + } + + (ty::FnDef(a_def_id, a_args), ty::FnDef(b_def_id, b_args)) if a_def_id == b_def_id => { + let args = relation.relate_item_args(a_def_id, a_args, b_args)?; + Ok(Ty::new_fn_def(tcx, a_def_id, args)) + } + + (ty::FnPtr(a_fty), ty::FnPtr(b_fty)) => { + let fty = relation.relate(a_fty, b_fty)?; + Ok(Ty::new_fn_ptr(tcx, fty)) + } + + // Alias tend to mostly already be handled downstream due to normalization. + (ty::Alias(a_kind, a_data), ty::Alias(b_kind, b_data)) => { + let alias_ty = relation.relate(a_data, b_data)?; + assert_eq!(a_kind, b_kind); + Ok(Ty::new_alias(tcx, a_kind, alias_ty)) + } + + (ty::Pat(a_ty, a_pat), ty::Pat(b_ty, b_pat)) => { + let ty = relation.relate(a_ty, b_ty)?; + let pat = relation.relate(a_pat, b_pat)?; + Ok(Ty::new_pat(tcx, ty, pat)) + } + + _ => Err(TypeError::Sorts(ExpectedFound::new(true, a, b))), + } +} + +/// Relates `a` and `b` structurally, calling the relation for all nested values. +/// Any semantic equality, e.g. of unevaluated consts, and inference variables have +/// to be handled by the caller. +/// +/// FIXME: This is not totally structual, which probably should be fixed. +/// See the HACKs below. +pub fn structurally_relate_consts>( + relation: &mut R, + mut a: I::Const, + mut b: I::Const, +) -> RelateResult { + debug!("{}.structurally_relate_consts(a = {:?}, b = {:?})", relation.tag(), a, b); + let tcx = relation.tcx(); + + if tcx.features().generic_const_exprs() { + a = tcx.expand_abstract_consts(a); + b = tcx.expand_abstract_consts(b); + } + + debug!("{}.structurally_relate_consts(normed_a = {:?}, normed_b = {:?})", relation.tag(), a, b); + + // Currently, the values that can be unified are primitive types, + // and those that derive both `PartialEq` and `Eq`, corresponding + // to structural-match types. + let is_match = match (a.kind(), b.kind()) { + (ty::ConstKind::Infer(_), _) | (_, ty::ConstKind::Infer(_)) => { + // The caller should handle these cases! + panic!("var types encountered in structurally_relate_consts: {:?} {:?}", a, b) + } + + (ty::ConstKind::Error(_), _) => return Ok(a), + (_, ty::ConstKind::Error(_)) => return Ok(b), + + (ty::ConstKind::Param(a_p), ty::ConstKind::Param(b_p)) if a_p.index() == b_p.index() => { + // FIXME: Put this back + // debug_assert_eq!(a_p.name, b_p.name, "param types with same index differ in name"); + true + } + (ty::ConstKind::Placeholder(p1), ty::ConstKind::Placeholder(p2)) => p1 == p2, + (ty::ConstKind::Value(_, a_val), ty::ConstKind::Value(_, b_val)) => a_val == b_val, + + // While this is slightly incorrect, it shouldn't matter for `min_const_generics` + // and is the better alternative to waiting until `generic_const_exprs` can + // be stabilized. + (ty::ConstKind::Unevaluated(au), ty::ConstKind::Unevaluated(bu)) if au.def == bu.def => { + if cfg!(debug_assertions) { + let a_ty = tcx.type_of(au.def).instantiate(tcx, &au.args); + let b_ty = tcx.type_of(bu.def).instantiate(tcx, &bu.args); + assert_eq!(a_ty, b_ty); + } + + let args = relation.relate_with_variance( + ty::Variance::Invariant, + VarianceDiagInfo::default(), + au.args, + bu.args, + )?; + return Ok(Const::new_unevaluated(tcx, ty::UnevaluatedConst { def: au.def, args })); + } + (ty::ConstKind::Expr(ae), ty::ConstKind::Expr(be)) => { + let expr = relation.relate(ae, be)?; + return Ok(Const::new_expr(tcx, expr)); + } + _ => false, + }; + if is_match { Ok(a) } else { Err(TypeError::ConstMismatch(ExpectedFound::new(true, a, b))) } +} + +impl> Relate for ty::Binder { + fn relate>( + relation: &mut R, + a: ty::Binder, + b: ty::Binder, + ) -> RelateResult> { + relation.binders(a, b) + } +} + +impl Relate for ty::PredicatePolarity { + fn relate>( + _relation: &mut R, + a: ty::PredicatePolarity, + b: ty::PredicatePolarity, + ) -> RelateResult { + if a != b { + Err(TypeError::PolarityMismatch(ExpectedFound::new(true, a, b))) + } else { + Ok(a) + } + } +} + +impl Relate for ty::TraitPredicate { + fn relate>( + relation: &mut R, + a: ty::TraitPredicate, + b: ty::TraitPredicate, + ) -> RelateResult> { + Ok(ty::TraitPredicate { + trait_ref: relation.relate(a.trait_ref, b.trait_ref)?, + polarity: relation.relate(a.polarity, b.polarity)?, + }) + } +} From 72cbb8bd94c6c3cfa3ca8d90da06a3e6f2d6673e Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Thu, 6 Jun 2024 15:31:00 +0000 Subject: [PATCH 15/92] Revert "Use the HIR instead of mir_keys for determining whether something will have a MIR body." This reverts commit e5cba17b84bf7bf755686e8bb36aa3775ef53f77. --- compiler/rustc_mir_transform/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index a8741254ffbf..7a6f3cbe2034 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -211,7 +211,7 @@ fn remap_mir_for_const_eval_select<'tcx>( } fn is_mir_available(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { - tcx.hir().maybe_body_owned_by(def_id).is_some() + tcx.mir_keys(()).contains(&def_id) } /// Finds the full set of `DefId`s within the current crate that have From c76e59e71295677317389783ecb7b4ebb27f8535 Mon Sep 17 00:00:00 2001 From: onur-ozkan Date: Thu, 6 Jun 2024 20:06:20 +0300 Subject: [PATCH 16/92] prefer compile::stream_cargo for building tools Previously, we were running bare commands for `ToolBuild` step and were unable to utilize some of the flags which are already handled by `compile::stream_cargo`. This change makes `ToolBuild` to use `compile::stream_cargo`, allowing us to benefit from the flags supported by the bootstrap cargo. Signed-off-by: onur-ozkan --- src/bootstrap/src/core/build_steps/tool.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index 05b19c0a6e33..7d1a6419243c 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -9,7 +9,6 @@ use crate::core::builder; use crate::core::builder::{Builder, Cargo as CargoCommand, RunConfig, ShouldRun, Step}; use crate::core::config::TargetSelection; use crate::utils::channel::GitInfo; -use crate::utils::exec::BootstrapCommand; use crate::utils::helpers::output; use crate::utils::helpers::{add_dylib_path, exe, t}; use crate::Compiler; @@ -110,9 +109,8 @@ impl Step for ToolBuild { &self.target, ); - let mut cargo = Command::from(cargo); // we check this below - let build_success = builder.run_cmd(BootstrapCommand::from(&mut cargo).allow_failure()); + let build_success = compile::stream_cargo(builder, cargo, vec![], &mut |_| {}); builder.save_toolstate( tool, From 89d86ae5bd221b14ec7a444062413dd81ecbf6d6 Mon Sep 17 00:00:00 2001 From: Urgau Date: Wed, 5 Jun 2024 19:16:43 +0200 Subject: [PATCH 17/92] Don't warn on fields in the `unreachable_pub` lint --- compiler/rustc_lint/src/builtin.rs | 21 ++++++++++++------ tests/ui/lint/unreachable_pub.rs | 10 ++++++--- tests/ui/lint/unreachable_pub.stderr | 32 +++++++++++----------------- 3 files changed, 34 insertions(+), 29 deletions(-) diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index ba42eae34415..a06a90b2e87a 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -51,7 +51,7 @@ use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID}; use rustc_hir::intravisit::FnKind as HirFnKind; -use rustc_hir::{Body, FnDecl, GenericParamKind, Node, PatKind, PredicateOrigin}; +use rustc_hir::{Body, FnDecl, GenericParamKind, PatKind, PredicateOrigin}; use rustc_middle::bug; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::layout::LayoutOf; @@ -1423,11 +1423,20 @@ impl<'tcx> LateLintPass<'tcx> for UnreachablePub { self.perform_lint(cx, "item", foreign_item.owner_id.def_id, foreign_item.vis_span, true); } - fn check_field_def(&mut self, cx: &LateContext<'_>, field: &hir::FieldDef<'_>) { - if matches!(cx.tcx.parent_hir_node(field.hir_id), Node::Variant(_)) { - return; - } - self.perform_lint(cx, "field", field.def_id, field.vis_span, false); + fn check_field_def(&mut self, _cx: &LateContext<'_>, _field: &hir::FieldDef<'_>) { + // - If an ADT definition is reported then we don't need to check fields + // (as it would add unnecessary complexity to the source code, the struct + // definition is in the immediate proximity to give the "real" visibility). + // - If an ADT is not reported because it's not `pub` - we don't need to + // check fields. + // - If an ADT is not reported because it's reachable - we also don't need + // to check fields because then they are reachable by construction if they + // are pub. + // + // Therefore in no case we check the fields. + // + // cf. https://github.com/rust-lang/rust/pull/126013#issuecomment-2152839205 + // cf. https://github.com/rust-lang/rust/pull/126040#issuecomment-2152944506 } fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &hir::ImplItem<'_>) { diff --git a/tests/ui/lint/unreachable_pub.rs b/tests/ui/lint/unreachable_pub.rs index 22c091e112be..f21f6640342c 100644 --- a/tests/ui/lint/unreachable_pub.rs +++ b/tests/ui/lint/unreachable_pub.rs @@ -9,12 +9,16 @@ mod private_mod { pub use std::env::{Args}; // braced-use has different item spans than unbraced //~^ WARNING unreachable_pub + // we lint on struct definition pub struct Hydrogen { //~ WARNING unreachable_pub - // `pub` struct fields, too - pub neutrons: usize, //~ WARNING unreachable_pub - // (... but not more-restricted fields) + // but not on fields, even if they are `pub` as putting `pub(crate)` + // it would clutter the source code for little value + pub neutrons: usize, pub(crate) electrons: usize } + pub(crate) struct Calcium { + pub neutrons: usize, + } impl Hydrogen { // impls, too pub fn count_neutrons(&self) -> usize { self.neutrons } //~ WARNING unreachable_pub diff --git a/tests/ui/lint/unreachable_pub.stderr b/tests/ui/lint/unreachable_pub.stderr index 762834b97b90..705a537a3f1c 100644 --- a/tests/ui/lint/unreachable_pub.stderr +++ b/tests/ui/lint/unreachable_pub.stderr @@ -24,7 +24,7 @@ LL | pub use std::env::{Args}; // braced-use has different item spans than u = help: or consider exporting it for use by other crates warning: unreachable `pub` item - --> $DIR/unreachable_pub.rs:12:5 + --> $DIR/unreachable_pub.rs:13:5 | LL | pub struct Hydrogen { | ---^^^^^^^^^^^^^^^^ @@ -33,16 +33,8 @@ LL | pub struct Hydrogen { | = help: or consider exporting it for use by other crates -warning: unreachable `pub` field - --> $DIR/unreachable_pub.rs:14:9 - | -LL | pub neutrons: usize, - | ---^^^^^^^^^^^^^^^^ - | | - | help: consider restricting its visibility: `pub(crate)` - warning: unreachable `pub` item - --> $DIR/unreachable_pub.rs:20:9 + --> $DIR/unreachable_pub.rs:24:9 | LL | pub fn count_neutrons(&self) -> usize { self.neutrons } | ---^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -50,7 +42,7 @@ LL | pub fn count_neutrons(&self) -> usize { self.neutrons } | help: consider restricting its visibility: `pub(crate)` warning: unreachable `pub` item - --> $DIR/unreachable_pub.rs:29:5 + --> $DIR/unreachable_pub.rs:33:5 | LL | pub enum Helium {} | ---^^^^^^^^^^^^ @@ -60,7 +52,7 @@ LL | pub enum Helium {} = help: or consider exporting it for use by other crates warning: unreachable `pub` item - --> $DIR/unreachable_pub.rs:30:5 + --> $DIR/unreachable_pub.rs:34:5 | LL | pub union Lithium { c1: usize, c2: u8 } | ---^^^^^^^^^^^^^^ @@ -70,7 +62,7 @@ LL | pub union Lithium { c1: usize, c2: u8 } = help: or consider exporting it for use by other crates warning: unreachable `pub` item - --> $DIR/unreachable_pub.rs:31:5 + --> $DIR/unreachable_pub.rs:35:5 | LL | pub fn beryllium() {} | ---^^^^^^^^^^^^^^^ @@ -80,7 +72,7 @@ LL | pub fn beryllium() {} = help: or consider exporting it for use by other crates warning: unreachable `pub` item - --> $DIR/unreachable_pub.rs:32:5 + --> $DIR/unreachable_pub.rs:36:5 | LL | pub trait Boron {} | ---^^^^^^^^^^^^ @@ -90,7 +82,7 @@ LL | pub trait Boron {} = help: or consider exporting it for use by other crates warning: unreachable `pub` item - --> $DIR/unreachable_pub.rs:33:5 + --> $DIR/unreachable_pub.rs:37:5 | LL | pub const CARBON: usize = 1; | ---^^^^^^^^^^^^^^^^^^^^ @@ -100,7 +92,7 @@ LL | pub const CARBON: usize = 1; = help: or consider exporting it for use by other crates warning: unreachable `pub` item - --> $DIR/unreachable_pub.rs:34:5 + --> $DIR/unreachable_pub.rs:38:5 | LL | pub static NITROGEN: usize = 2; | ---^^^^^^^^^^^^^^^^^^^^^^^ @@ -110,7 +102,7 @@ LL | pub static NITROGEN: usize = 2; = help: or consider exporting it for use by other crates warning: unreachable `pub` item - --> $DIR/unreachable_pub.rs:35:5 + --> $DIR/unreachable_pub.rs:39:5 | LL | pub type Oxygen = bool; | ---^^^^^^^^^^^^ @@ -120,7 +112,7 @@ LL | pub type Oxygen = bool; = help: or consider exporting it for use by other crates warning: unreachable `pub` item - --> $DIR/unreachable_pub.rs:38:47 + --> $DIR/unreachable_pub.rs:42:47 | LL | ($visibility: vis, $name: ident) => { $visibility struct $name {} } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -135,7 +127,7 @@ LL | define_empty_struct_with_visibility!(pub, Fluorine); = note: this warning originates in the macro `define_empty_struct_with_visibility` (in Nightly builds, run with -Z macro-backtrace for more info) warning: unreachable `pub` item - --> $DIR/unreachable_pub.rs:44:9 + --> $DIR/unreachable_pub.rs:48:9 | LL | pub fn catalyze() -> bool; | ---^^^^^^^^^^^^^^^^^^^^^^ @@ -144,5 +136,5 @@ LL | pub fn catalyze() -> bool; | = help: or consider exporting it for use by other crates -warning: 14 warnings emitted +warning: 13 warnings emitted From 26dc8bd5b074ee699c38d9fa47718661b3142c0f Mon Sep 17 00:00:00 2001 From: Daniel Sedlak Date: Thu, 6 Jun 2024 20:01:59 +0200 Subject: [PATCH 18/92] Stabilize Option::take_if --- compiler/rustc_trait_selection/src/lib.rs | 1 - library/core/src/option.rs | 4 +--- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/compiler/rustc_trait_selection/src/lib.rs b/compiler/rustc_trait_selection/src/lib.rs index 521e4ef0c9ed..381da6f7e2a7 100644 --- a/compiler/rustc_trait_selection/src/lib.rs +++ b/compiler/rustc_trait_selection/src/lib.rs @@ -23,7 +23,6 @@ #![feature(extract_if)] #![feature(if_let_guard)] #![feature(let_chains)] -#![feature(option_take_if)] #![feature(never_type)] #![feature(type_alias_impl_trait)] #![recursion_limit = "512"] // For rustdoc diff --git a/library/core/src/option.rs b/library/core/src/option.rs index 9a527073602e..e253cfd2822b 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -1708,8 +1708,6 @@ impl Option { /// # Examples /// /// ``` - /// #![feature(option_take_if)] - /// /// let mut x = Some(42); /// /// let prev = x.take_if(|v| if *v == 42 { @@ -1726,7 +1724,7 @@ impl Option { /// assert_eq!(prev, Some(43)); /// ``` #[inline] - #[unstable(feature = "option_take_if", issue = "98934")] + #[stable(feature = "option_take_if", since = "CURRENT_RUSTC_VERSION")] pub fn take_if

, and replace that - // slot with the new name: `[h1, name]`. - self.names.truncate(level); - self.names[level - 1] = name; - } else { - // ... On the other hand, consider `level == 5`. This means we - // need to extend `self.names` to contain five headers. We fill - // in the missing level (

) with `_`. Thus `self.names` will - // become `[h1, h2, h3, "_", name]`. - if level - 1 > self.names.len() { - self.names.resize(level - 1, "_".to_owned()); - } - self.names.push(name); - } - } - } } #[cfg(test)] // used in tests diff --git a/src/librustdoc/doctest/markdown.rs b/src/librustdoc/doctest/markdown.rs index 13239b9a5172..6f2769b76810 100644 --- a/src/librustdoc/doctest/markdown.rs +++ b/src/librustdoc/doctest/markdown.rs @@ -2,12 +2,84 @@ use std::fs::read_to_string; -use rustc_span::DUMMY_SP; +use rustc_span::FileName; use tempfile::tempdir; -use super::{generate_args_file, Collector, GlobalTestOptions}; +use super::{generate_args_file, Collector, DoctestVisitor, GlobalTestOptions, ScrapedDoctest}; use crate::config::Options; -use crate::html::markdown::{find_testable_code, ErrorCodes}; +use crate::html::markdown::{find_testable_code, ErrorCodes, LangString}; + +pub(super) struct MdDoctest { + pub(super) filename: FileName, + pub(super) line: usize, + pub(super) logical_path: Vec, + pub(super) langstr: LangString, + pub(super) text: String, +} + +struct MdCollector { + tests: Vec, + cur_path: Vec, + filename: FileName, +} + +impl DoctestVisitor for MdCollector { + fn visit_test(&mut self, test: String, config: LangString, line: usize) { + let filename = self.filename.clone(); + self.tests.push(MdDoctest { + filename, + line, + logical_path: self.cur_path.clone(), + langstr: config, + text: test, + }); + } + + fn get_line(&self) -> usize { + 0 + } + + fn visit_header(&mut self, name: &str, level: u32) { + // We use these headings as test names, so it's good if + // they're valid identifiers. + let name = name + .chars() + .enumerate() + .map(|(i, c)| { + if (i == 0 && rustc_lexer::is_id_start(c)) + || (i != 0 && rustc_lexer::is_id_continue(c)) + { + c + } else { + '_' + } + }) + .collect::(); + + // Here we try to efficiently assemble the header titles into the + // test name in the form of `h1::h2::h3::h4::h5::h6`. + // + // Suppose that originally `self.cur_path` contains `[h1, h2, h3]`... + let level = level as usize; + if level <= self.cur_path.len() { + // ... Consider `level == 2`. All headers in the lower levels + // are irrelevant in this new level. So we should reset + // `self.names` to contain headers until

, and replace that + // slot with the new name: `[h1, name]`. + self.cur_path.truncate(level); + self.cur_path[level - 1] = name; + } else { + // ... On the other hand, consider `level == 5`. This means we + // need to extend `self.names` to contain five headers. We fill + // in the missing level (

) with `_`. Thus `self.names` will + // become `[h1, h2, h3, "_", name]`. + if level - 1 > self.cur_path.len() { + self.cur_path.resize(level - 1, "_".to_owned()); + } + self.cur_path.push(name); + } + } +} /// Runs any tests/code examples in the markdown file `input`. pub(crate) fn test(options: Options) -> Result<(), String> { @@ -27,21 +99,29 @@ pub(crate) fn test(options: Options) -> Result<(), String> { let file_path = temp_dir.path().join("rustdoc-cfgs"); generate_args_file(&file_path, &options)?; - let mut collector = Collector::new( - options.input.filestem().to_string(), - options.clone(), - true, - opts, - None, - options.input.opt_path().map(ToOwned::to_owned), - options.enable_per_target_ignores, - file_path, - ); - collector.set_position(DUMMY_SP); + let mut md_collector = MdCollector { + tests: vec![], + cur_path: vec![], + filename: options + .input + .opt_path() + .map(ToOwned::to_owned) + .map(FileName::from) + .unwrap_or(FileName::Custom("input".to_owned())), + }; let codes = ErrorCodes::from(options.unstable_features.is_nightly_build()); - find_testable_code(&input_str, &mut collector, codes, options.enable_per_target_ignores, None); + find_testable_code( + &input_str, + &mut md_collector, + codes, + options.enable_per_target_ignores, + None, + ); + let mut collector = + Collector::new(options.input.filestem().to_string(), options.clone(), opts, file_path); + md_collector.tests.into_iter().for_each(|t| collector.add_test(ScrapedDoctest::Markdown(t))); crate::doctest::run_tests(options.test_args, options.nocapture, collector.tests); Ok(()) } diff --git a/src/librustdoc/doctest/rust.rs b/src/librustdoc/doctest/rust.rs index e4527de77a02..9e62ed34a586 100644 --- a/src/librustdoc/doctest/rust.rs +++ b/src/librustdoc/doctest/rust.rs @@ -1,29 +1,108 @@ //! Doctest functionality used only for doctests in `.rs` source files. -use rustc_data_structures::fx::FxHashSet; -use rustc_hir::def_id::LocalDefId; -use rustc_hir::{self as hir, intravisit}; +use std::env; + +use rustc_data_structures::{fx::FxHashSet, sync::Lrc}; +use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID}; +use rustc_hir::{self as hir, intravisit, CRATE_HIR_ID}; use rustc_middle::hir::map::Map; use rustc_middle::hir::nested_filter; use rustc_middle::ty::TyCtxt; use rustc_resolve::rustdoc::span_of_fragments; use rustc_session::Session; -use rustc_span::{Span, DUMMY_SP}; +use rustc_span::source_map::SourceMap; +use rustc_span::{BytePos, FileName, Pos, Span, DUMMY_SP}; -use super::Collector; +use super::DoctestVisitor; use crate::clean::{types::AttributesExt, Attributes}; -use crate::html::markdown::{self, ErrorCodes}; +use crate::html::markdown::{self, ErrorCodes, LangString}; -pub(super) struct HirCollector<'a, 'hir, 'tcx> { - pub(super) sess: &'a Session, - pub(super) collector: &'a mut Collector, - pub(super) map: Map<'hir>, - pub(super) codes: ErrorCodes, - pub(super) tcx: TyCtxt<'tcx>, +pub(super) struct RustDoctest { + pub(super) filename: FileName, + pub(super) line: usize, + pub(super) logical_path: Vec, + pub(super) langstr: LangString, + pub(super) text: String, } -impl<'a, 'hir, 'tcx> HirCollector<'a, 'hir, 'tcx> { - pub(super) fn visit_testable( +struct RustCollector { + source_map: Lrc, + tests: Vec, + cur_path: Vec, + position: Span, +} + +impl RustCollector { + fn get_filename(&self) -> FileName { + let filename = self.source_map.span_to_filename(self.position); + if let FileName::Real(ref filename) = filename + && let Ok(cur_dir) = env::current_dir() + && let Some(local_path) = filename.local_path() + && let Ok(path) = local_path.strip_prefix(&cur_dir) + { + return path.to_owned().into(); + } + filename + } +} + +impl DoctestVisitor for RustCollector { + fn visit_test(&mut self, test: String, config: LangString, line: usize) { + self.tests.push(RustDoctest { + filename: self.get_filename(), + line, + logical_path: self.cur_path.clone(), + langstr: config, + text: test, + }); + } + + fn get_line(&self) -> usize { + let line = self.position.lo().to_usize(); + let line = self.source_map.lookup_char_pos(BytePos(line as u32)).line; + if line > 0 { line - 1 } else { line } + } + + fn visit_header(&mut self, _name: &str, _level: u32) {} +} + +pub(super) struct HirCollector<'a, 'tcx> { + sess: &'a Session, + map: Map<'tcx>, + codes: ErrorCodes, + tcx: TyCtxt<'tcx>, + enable_per_target_ignores: bool, + collector: RustCollector, +} + +impl<'a, 'tcx> HirCollector<'a, 'tcx> { + pub fn new( + sess: &'a Session, + map: Map<'tcx>, + codes: ErrorCodes, + enable_per_target_ignores: bool, + tcx: TyCtxt<'tcx>, + ) -> Self { + let collector = RustCollector { + source_map: sess.psess.clone_source_map(), + cur_path: vec![], + position: DUMMY_SP, + tests: vec![], + }; + Self { sess, map, codes, enable_per_target_ignores, tcx, collector } + } + + pub fn collect_crate(mut self) -> Vec { + let tcx = self.tcx; + self.visit_testable("".to_string(), CRATE_DEF_ID, tcx.hir().span(CRATE_HIR_ID), |this| { + tcx.hir().walk_toplevel_module(this) + }); + self.collector.tests + } +} + +impl<'a, 'tcx> HirCollector<'a, 'tcx> { + fn visit_testable( &mut self, name: String, def_id: LocalDefId, @@ -39,7 +118,7 @@ impl<'a, 'hir, 'tcx> HirCollector<'a, 'hir, 'tcx> { let has_name = !name.is_empty(); if has_name { - self.collector.names.push(name); + self.collector.cur_path.push(name); } // The collapse-docs pass won't combine sugared/raw doc attributes, or included files with @@ -52,12 +131,12 @@ impl<'a, 'hir, 'tcx> HirCollector<'a, 'hir, 'tcx> { .find(|attr| attr.doc_str().is_some()) .map(|attr| attr.span.ctxt().outer_expn().expansion_cause().unwrap_or(attr.span)) .unwrap_or(DUMMY_SP); - self.collector.set_position(span); + self.collector.position = span; markdown::find_testable_code( &doc, - self.collector, + &mut self.collector, self.codes, - self.collector.enable_per_target_ignores, + self.enable_per_target_ignores, Some(&crate::html::markdown::ExtraInfo::new( self.tcx, def_id.to_def_id(), @@ -69,19 +148,19 @@ impl<'a, 'hir, 'tcx> HirCollector<'a, 'hir, 'tcx> { nested(self); if has_name { - self.collector.names.pop(); + self.collector.cur_path.pop(); } } } -impl<'a, 'hir, 'tcx> intravisit::Visitor<'hir> for HirCollector<'a, 'hir, 'tcx> { +impl<'a, 'tcx> intravisit::Visitor<'tcx> for HirCollector<'a, 'tcx> { type NestedFilter = nested_filter::All; fn nested_visit_map(&mut self) -> Self::Map { self.map } - fn visit_item(&mut self, item: &'hir hir::Item<'_>) { + fn visit_item(&mut self, item: &'tcx hir::Item<'_>) { let name = match &item.kind { hir::ItemKind::Impl(impl_) => { rustc_hir_pretty::id_to_string(&self.map, impl_.self_ty.hir_id) @@ -94,31 +173,31 @@ impl<'a, 'hir, 'tcx> intravisit::Visitor<'hir> for HirCollector<'a, 'hir, 'tcx> }); } - fn visit_trait_item(&mut self, item: &'hir hir::TraitItem<'_>) { + fn visit_trait_item(&mut self, item: &'tcx hir::TraitItem<'_>) { self.visit_testable(item.ident.to_string(), item.owner_id.def_id, item.span, |this| { intravisit::walk_trait_item(this, item); }); } - fn visit_impl_item(&mut self, item: &'hir hir::ImplItem<'_>) { + fn visit_impl_item(&mut self, item: &'tcx hir::ImplItem<'_>) { self.visit_testable(item.ident.to_string(), item.owner_id.def_id, item.span, |this| { intravisit::walk_impl_item(this, item); }); } - fn visit_foreign_item(&mut self, item: &'hir hir::ForeignItem<'_>) { + fn visit_foreign_item(&mut self, item: &'tcx hir::ForeignItem<'_>) { self.visit_testable(item.ident.to_string(), item.owner_id.def_id, item.span, |this| { intravisit::walk_foreign_item(this, item); }); } - fn visit_variant(&mut self, v: &'hir hir::Variant<'_>) { + fn visit_variant(&mut self, v: &'tcx hir::Variant<'_>) { self.visit_testable(v.ident.to_string(), v.def_id, v.span, |this| { intravisit::walk_variant(this, v); }); } - fn visit_field_def(&mut self, f: &'hir hir::FieldDef<'_>) { + fn visit_field_def(&mut self, f: &'tcx hir::FieldDef<'_>) { self.visit_testable(f.ident.to_string(), f.def_id, f.span, |this| { intravisit::walk_field_def(this, f); }); From b7dd401a78598615a7733bfe615cbcbd9393e44a Mon Sep 17 00:00:00 2001 From: Noah Lev Date: Fri, 31 May 2024 00:50:41 -0700 Subject: [PATCH 73/92] rustdoc: Extract actual doctest running logic into function --- src/librustdoc/doctest.rs | 191 ++++++++++++++++++----------- src/librustdoc/doctest/markdown.rs | 12 +- 2 files changed, 127 insertions(+), 76 deletions(-) diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 8088b57dd76b..61195f914ebf 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -179,7 +179,7 @@ pub(crate) fn run( let opts = scrape_test_config(crate_attrs); let enable_per_target_ignores = options.enable_per_target_ignores; - let mut collector = Collector::new( + let mut collector = CreateRunnableDoctests::new( tcx.crate_name(LOCAL_CRATE).to_string(), options, opts, @@ -989,7 +989,7 @@ pub(crate) trait DoctestVisitor { fn visit_header(&mut self, _name: &str, _level: u32) {} } -pub(crate) struct Collector { +pub(crate) struct CreateRunnableDoctests { pub(crate) tests: Vec, rustdoc_options: RustdocOptions, @@ -1001,14 +1001,14 @@ pub(crate) struct Collector { arg_file: PathBuf, } -impl Collector { +impl CreateRunnableDoctests { pub(crate) fn new( crate_name: String, rustdoc_options: RustdocOptions, opts: GlobalTestOptions, arg_file: PathBuf, - ) -> Collector { - Collector { + ) -> CreateRunnableDoctests { + CreateRunnableDoctests { tests: Vec::new(), rustdoc_options, crate_name, @@ -1105,79 +1105,124 @@ impl Collector { test_type: test::TestType::DocTest, }, testfn: test::DynTestFn(Box::new(move || { - let report_unused_externs = |uext| { - unused_externs.lock().unwrap().push(uext); - }; - let res = run_test( - &text, - &crate_name, - line, - rustdoc_test_options, - langstr, - no_run, - &opts, - edition, - path, - report_unused_externs, - ); - - if let Err(err) = res { - match err { - TestFailure::CompileError => { - eprint!("Couldn't compile the test."); - } - TestFailure::UnexpectedCompilePass => { - eprint!("Test compiled successfully, but it's marked `compile_fail`."); - } - TestFailure::UnexpectedRunPass => { - eprint!("Test executable succeeded, but it's marked `should_panic`."); - } - TestFailure::MissingErrorCodes(codes) => { - eprint!("Some expected error codes were not found: {codes:?}"); - } - TestFailure::ExecutionError(err) => { - eprint!("Couldn't run the test: {err}"); - if err.kind() == io::ErrorKind::PermissionDenied { - eprint!(" - maybe your tempdir is mounted with noexec?"); - } - } - TestFailure::ExecutionFailure(out) => { - eprintln!("Test executable failed ({reason}).", reason = out.status); - - // FIXME(#12309): An unfortunate side-effect of capturing the test - // executable's output is that the relative ordering between the test's - // stdout and stderr is lost. However, this is better than the - // alternative: if the test executable inherited the parent's I/O - // handles the output wouldn't be captured at all, even on success. - // - // The ordering could be preserved if the test process' stderr was - // redirected to stdout, but that functionality does not exist in the - // standard library, so it may not be portable enough. - let stdout = str::from_utf8(&out.stdout).unwrap_or_default(); - let stderr = str::from_utf8(&out.stderr).unwrap_or_default(); - - if !stdout.is_empty() || !stderr.is_empty() { - eprintln!(); - - if !stdout.is_empty() { - eprintln!("stdout:\n{stdout}"); - } - - if !stderr.is_empty() { - eprintln!("stderr:\n{stderr}"); - } - } - } - } - - panic::resume_unwind(Box::new(())); - } - Ok(()) + doctest_run_fn( + RunnableDoctest { + crate_name, + line, + rustdoc_test_options, + langstr, + no_run, + opts, + edition, + path, + text, + }, + unused_externs, + ) })), }); } } +/// A doctest that is ready to run. +struct RunnableDoctest { + crate_name: String, + line: usize, + rustdoc_test_options: IndividualTestOptions, + langstr: LangString, + no_run: bool, + opts: GlobalTestOptions, + edition: Edition, + path: PathBuf, + text: String, +} + +fn doctest_run_fn( + test: RunnableDoctest, + unused_externs: Arc>>, +) -> Result<(), String> { + let RunnableDoctest { + crate_name, + line, + rustdoc_test_options, + langstr, + no_run, + opts, + edition, + path, + text, + } = test; + + let report_unused_externs = |uext| { + unused_externs.lock().unwrap().push(uext); + }; + let res = run_test( + &text, + &crate_name, + line, + rustdoc_test_options, + langstr, + no_run, + &opts, + edition, + path, + report_unused_externs, + ); + + if let Err(err) = res { + match err { + TestFailure::CompileError => { + eprint!("Couldn't compile the test."); + } + TestFailure::UnexpectedCompilePass => { + eprint!("Test compiled successfully, but it's marked `compile_fail`."); + } + TestFailure::UnexpectedRunPass => { + eprint!("Test executable succeeded, but it's marked `should_panic`."); + } + TestFailure::MissingErrorCodes(codes) => { + eprint!("Some expected error codes were not found: {codes:?}"); + } + TestFailure::ExecutionError(err) => { + eprint!("Couldn't run the test: {err}"); + if err.kind() == io::ErrorKind::PermissionDenied { + eprint!(" - maybe your tempdir is mounted with noexec?"); + } + } + TestFailure::ExecutionFailure(out) => { + eprintln!("Test executable failed ({reason}).", reason = out.status); + + // FIXME(#12309): An unfortunate side-effect of capturing the test + // executable's output is that the relative ordering between the test's + // stdout and stderr is lost. However, this is better than the + // alternative: if the test executable inherited the parent's I/O + // handles the output wouldn't be captured at all, even on success. + // + // The ordering could be preserved if the test process' stderr was + // redirected to stdout, but that functionality does not exist in the + // standard library, so it may not be portable enough. + let stdout = str::from_utf8(&out.stdout).unwrap_or_default(); + let stderr = str::from_utf8(&out.stderr).unwrap_or_default(); + + if !stdout.is_empty() || !stderr.is_empty() { + eprintln!(); + + if !stdout.is_empty() { + eprintln!("stdout:\n{stdout}"); + } + + if !stderr.is_empty() { + eprintln!("stderr:\n{stderr}"); + } + } + } + } + + panic::resume_unwind(Box::new(())); + } + Ok(()) +} + #[cfg(test)] // used in tests impl DoctestVisitor for Vec { fn visit_test(&mut self, _test: String, _config: LangString, line: usize) { diff --git a/src/librustdoc/doctest/markdown.rs b/src/librustdoc/doctest/markdown.rs index 6f2769b76810..7a52a8cb18d0 100644 --- a/src/librustdoc/doctest/markdown.rs +++ b/src/librustdoc/doctest/markdown.rs @@ -5,7 +5,9 @@ use std::fs::read_to_string; use rustc_span::FileName; use tempfile::tempdir; -use super::{generate_args_file, Collector, DoctestVisitor, GlobalTestOptions, ScrapedDoctest}; +use super::{ + generate_args_file, CreateRunnableDoctests, DoctestVisitor, GlobalTestOptions, ScrapedDoctest, +}; use crate::config::Options; use crate::html::markdown::{find_testable_code, ErrorCodes, LangString}; @@ -119,8 +121,12 @@ pub(crate) fn test(options: Options) -> Result<(), String> { None, ); - let mut collector = - Collector::new(options.input.filestem().to_string(), options.clone(), opts, file_path); + let mut collector = CreateRunnableDoctests::new( + options.input.filestem().to_string(), + options.clone(), + opts, + file_path, + ); md_collector.tests.into_iter().for_each(|t| collector.add_test(ScrapedDoctest::Markdown(t))); crate::doctest::run_tests(options.test_args, options.nocapture, collector.tests); Ok(()) From 279b4d22f70f8bdb84dad547cf0dbefd587c4bf3 Mon Sep 17 00:00:00 2001 From: Noah Lev Date: Fri, 31 May 2024 11:19:24 -0700 Subject: [PATCH 74/92] Merge `RustDoctest` and `MdDoctest` into one type --- src/librustdoc/doctest.rs | 89 ++++++++++++------------------ src/librustdoc/doctest/markdown.rs | 14 +---- src/librustdoc/doctest/rust.rs | 16 ++---- 3 files changed, 42 insertions(+), 77 deletions(-) diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 61195f914ebf..5026abb3bffb 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -37,8 +37,7 @@ use crate::config::Options as RustdocOptions; use crate::html::markdown::{ErrorCodes, Ignore, LangString}; use crate::lint::init_lints; -use self::markdown::MdDoctest; -use self::rust::{HirCollector, RustDoctest}; +use self::rust::HirCollector; /// Options that apply to all doctests in a crate or Markdown file (for `rustdoc foo.md`). #[derive(Clone, Default)] @@ -194,7 +193,7 @@ pub(crate) fn run( tcx, ); let tests = hir_collector.collect_crate(); - tests.into_iter().for_each(|t| collector.add_test(ScrapedDoctest::Rust(t))); + tests.into_iter().for_each(|t| collector.add_test(t)); collector }); @@ -976,9 +975,12 @@ impl IndividualTestOptions { } /// A doctest scraped from the code, ready to be turned into a runnable test. -enum ScrapedDoctest { - Rust(RustDoctest), - Markdown(MdDoctest), +struct ScrapedDoctest { + filename: FileName, + line: usize, + logical_path: Vec, + langstr: LangString, + text: String, } pub(crate) trait DoctestVisitor { @@ -1030,27 +1032,18 @@ impl CreateRunnableDoctests { } fn add_test(&mut self, test: ScrapedDoctest) { - let (filename, line, logical_path, langstr, text) = match test { - ScrapedDoctest::Rust(RustDoctest { filename, line, logical_path, langstr, text }) => { - (filename, line, logical_path, langstr, text) - } - ScrapedDoctest::Markdown(MdDoctest { filename, line, logical_path, langstr, text }) => { - (filename, line, logical_path, langstr, text) - } - }; - - let name = self.generate_name(&filename, line, &logical_path); + let name = self.generate_name(&test.filename, test.line, &test.logical_path); let crate_name = self.crate_name.clone(); let opts = self.opts.clone(); - let edition = langstr.edition.unwrap_or(self.rustdoc_options.edition); + let edition = test.langstr.edition.unwrap_or(self.rustdoc_options.edition); let target_str = self.rustdoc_options.target.to_string(); let unused_externs = self.unused_extern_reports.clone(); - let no_run = langstr.no_run || self.rustdoc_options.no_run; - if !langstr.compile_fail { + let no_run = test.langstr.no_run || self.rustdoc_options.no_run; + if !test.langstr.compile_fail { self.compiling_test_count.fetch_add(1, Ordering::SeqCst); } - let path = match &filename { + let path = match &test.filename { FileName::Real(path) => { if let Some(local_path) = path.local_path() { local_path.to_path_buf() @@ -1063,7 +1056,8 @@ impl CreateRunnableDoctests { }; // For example `module/file.rs` would become `module_file_rs` - let file = filename + let file = test + .filename .prefer_local() .to_string_lossy() .chars() @@ -1072,22 +1066,25 @@ impl CreateRunnableDoctests { let test_id = format!( "{file}_{line}_{number}", file = file, - line = line, + line = test.line, number = { // Increases the current test number, if this file already // exists or it creates a new entry with a test number of 0. - self.visited_tests.entry((file.clone(), line)).and_modify(|v| *v += 1).or_insert(0) + self.visited_tests + .entry((file.clone(), test.line)) + .and_modify(|v| *v += 1) + .or_insert(0) }, ); let rustdoc_test_options = IndividualTestOptions::new(&self.rustdoc_options, &self.arg_file, test_id); - debug!("creating test {name}: {text}"); + debug!("creating test {name}: {}", test.text); self.tests.push(test::TestDescAndFn { desc: test::TestDesc { name: test::DynTestName(name), - ignore: match langstr.ignore { + ignore: match test.langstr.ignore { Ignore::All => true, Ignore::None => false, Ignore::Some(ref ignores) => ignores.iter().any(|s| target_str.contains(s)), @@ -1100,7 +1097,7 @@ impl CreateRunnableDoctests { end_col: 0, // compiler failures are test failures should_panic: test::ShouldPanic::No, - compile_fail: langstr.compile_fail, + compile_fail: test.langstr.compile_fail, no_run, test_type: test::TestType::DocTest, }, @@ -1108,14 +1105,12 @@ impl CreateRunnableDoctests { doctest_run_fn( RunnableDoctest { crate_name, - line, rustdoc_test_options, - langstr, no_run, opts, edition, path, - text, + scraped_test: test, }, unused_externs, ) @@ -1127,45 +1122,31 @@ impl CreateRunnableDoctests { /// A doctest that is ready to run. struct RunnableDoctest { crate_name: String, - line: usize, rustdoc_test_options: IndividualTestOptions, - langstr: LangString, no_run: bool, opts: GlobalTestOptions, edition: Edition, path: PathBuf, - text: String, + scraped_test: ScrapedDoctest, } fn doctest_run_fn( - test: RunnableDoctest, + runnable_test: RunnableDoctest, unused_externs: Arc>>, ) -> Result<(), String> { - let RunnableDoctest { - crate_name, - line, - rustdoc_test_options, - langstr, - no_run, - opts, - edition, - path, - text, - } = test; - let report_unused_externs = |uext| { unused_externs.lock().unwrap().push(uext); }; let res = run_test( - &text, - &crate_name, - line, - rustdoc_test_options, - langstr, - no_run, - &opts, - edition, - path, + &runnable_test.scraped_test.text, + &runnable_test.crate_name, + runnable_test.scraped_test.line, + runnable_test.rustdoc_test_options, + runnable_test.scraped_test.langstr, + runnable_test.no_run, + &runnable_test.opts, + runnable_test.edition, + runnable_test.path, report_unused_externs, ); diff --git a/src/librustdoc/doctest/markdown.rs b/src/librustdoc/doctest/markdown.rs index 7a52a8cb18d0..5d0dc5926e88 100644 --- a/src/librustdoc/doctest/markdown.rs +++ b/src/librustdoc/doctest/markdown.rs @@ -11,16 +11,8 @@ use super::{ use crate::config::Options; use crate::html::markdown::{find_testable_code, ErrorCodes, LangString}; -pub(super) struct MdDoctest { - pub(super) filename: FileName, - pub(super) line: usize, - pub(super) logical_path: Vec, - pub(super) langstr: LangString, - pub(super) text: String, -} - struct MdCollector { - tests: Vec, + tests: Vec, cur_path: Vec, filename: FileName, } @@ -28,7 +20,7 @@ struct MdCollector { impl DoctestVisitor for MdCollector { fn visit_test(&mut self, test: String, config: LangString, line: usize) { let filename = self.filename.clone(); - self.tests.push(MdDoctest { + self.tests.push(ScrapedDoctest { filename, line, logical_path: self.cur_path.clone(), @@ -127,7 +119,7 @@ pub(crate) fn test(options: Options) -> Result<(), String> { opts, file_path, ); - md_collector.tests.into_iter().for_each(|t| collector.add_test(ScrapedDoctest::Markdown(t))); + md_collector.tests.into_iter().for_each(|t| collector.add_test(t)); crate::doctest::run_tests(options.test_args, options.nocapture, collector.tests); Ok(()) } diff --git a/src/librustdoc/doctest/rust.rs b/src/librustdoc/doctest/rust.rs index 9e62ed34a586..458c90881c93 100644 --- a/src/librustdoc/doctest/rust.rs +++ b/src/librustdoc/doctest/rust.rs @@ -13,21 +13,13 @@ use rustc_session::Session; use rustc_span::source_map::SourceMap; use rustc_span::{BytePos, FileName, Pos, Span, DUMMY_SP}; -use super::DoctestVisitor; +use super::{DoctestVisitor, ScrapedDoctest}; use crate::clean::{types::AttributesExt, Attributes}; use crate::html::markdown::{self, ErrorCodes, LangString}; -pub(super) struct RustDoctest { - pub(super) filename: FileName, - pub(super) line: usize, - pub(super) logical_path: Vec, - pub(super) langstr: LangString, - pub(super) text: String, -} - struct RustCollector { source_map: Lrc, - tests: Vec, + tests: Vec, cur_path: Vec, position: Span, } @@ -48,7 +40,7 @@ impl RustCollector { impl DoctestVisitor for RustCollector { fn visit_test(&mut self, test: String, config: LangString, line: usize) { - self.tests.push(RustDoctest { + self.tests.push(ScrapedDoctest { filename: self.get_filename(), line, logical_path: self.cur_path.clone(), @@ -92,7 +84,7 @@ impl<'a, 'tcx> HirCollector<'a, 'tcx> { Self { sess, map, codes, enable_per_target_ignores, tcx, collector } } - pub fn collect_crate(mut self) -> Vec { + pub fn collect_crate(mut self) -> Vec { let tcx = self.tcx; self.visit_testable("".to_string(), CRATE_DEF_ID, tcx.hir().span(CRATE_HIR_ID), |this| { tcx.hir().walk_toplevel_module(this) From 46d2aa5a8f8d09d4789d2af01a6acdb3ba9e41a4 Mon Sep 17 00:00:00 2001 From: Noah Lev Date: Fri, 31 May 2024 11:40:01 -0700 Subject: [PATCH 75/92] Remove global options from `IndividualTestOptions` --- src/librustdoc/doctest.rs | 61 ++++++++++++++------------------------- 1 file changed, 21 insertions(+), 40 deletions(-) diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 5026abb3bffb..f1a4df52a59b 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -365,7 +365,8 @@ fn run_test( test: &str, crate_name: &str, line: usize, - rustdoc_options: IndividualTestOptions, + rustdoc_options: &RustdocOptions, + test_options: IndividualTestOptions, mut lang_string: LangString, no_run: bool, opts: &GlobalTestOptions, @@ -379,12 +380,12 @@ fn run_test( lang_string.test_harness, opts, edition, - Some(&rustdoc_options.test_id), + Some(&test_options.test_id), ); // Make sure we emit well-formed executable names for our target. let rust_out = add_exe_suffix("rust_out".to_owned(), &rustdoc_options.target); - let output_file = rustdoc_options.outdir.path().join(rust_out); + let output_file = test_options.outdir.path().join(rust_out); let rustc_binary = rustdoc_options .test_builder @@ -392,7 +393,7 @@ fn run_test( .unwrap_or_else(|| rustc_interface::util::rustc_path().expect("found rustc")); let mut compiler = wrapped_rustc_command(&rustdoc_options.test_builder_wrappers, rustc_binary); - compiler.arg(&format!("@{}", rustdoc_options.arg_file.display())); + compiler.arg(&format!("@{}", test_options.arg_file.display())); if let Some(sysroot) = &rustdoc_options.maybe_sysroot { compiler.arg(format!("--sysroot={}", sysroot.display())); @@ -405,20 +406,22 @@ fn run_test( if lang_string.test_harness { compiler.arg("--test"); } - if rustdoc_options.is_json_unused_externs_enabled && !lang_string.compile_fail { + if rustdoc_options.json_unused_externs.is_enabled() && !lang_string.compile_fail { compiler.arg("--error-format=json"); compiler.arg("--json").arg("unused-externs"); compiler.arg("-W").arg("unused_crate_dependencies"); compiler.arg("-Z").arg("unstable-options"); } - if no_run && !lang_string.compile_fail && rustdoc_options.should_persist_doctests { + if no_run && !lang_string.compile_fail && rustdoc_options.persist_doctests.is_none() { + // FIXME: why does this code check if it *shouldn't* persist doctests + // -- shouldn't it be the negation? compiler.arg("--emit=metadata"); } - compiler.arg("--target").arg(match rustdoc_options.target { + compiler.arg("--target").arg(match &rustdoc_options.target { TargetTriple::TargetTriple(s) => s, TargetTriple::TargetJson { path_for_rustdoc, .. } => { - path_for_rustdoc.to_str().expect("target path must be valid unicode").to_string() + path_for_rustdoc.to_str().expect("target path must be valid unicode") } }); if let ErrorOutputType::HumanReadable(kind) = rustdoc_options.error_format { @@ -511,15 +514,15 @@ fn run_test( let mut cmd; let output_file = make_maybe_absolute_path(output_file); - if let Some(tool) = rustdoc_options.runtool { + if let Some(tool) = &rustdoc_options.runtool { let tool = make_maybe_absolute_path(tool.into()); cmd = Command::new(tool); - cmd.args(rustdoc_options.runtool_args); + cmd.args(&rustdoc_options.runtool_args); cmd.arg(output_file); } else { cmd = Command::new(output_file); } - if let Some(run_directory) = rustdoc_options.test_run_directory { + if let Some(run_directory) = &rustdoc_options.test_run_directory { cmd.current_dir(run_directory); } @@ -923,20 +926,9 @@ fn partition_source(s: &str, edition: Edition) -> (String, String, String) { } pub(crate) struct IndividualTestOptions { - test_builder: Option, - test_builder_wrappers: Vec, - is_json_unused_externs_enabled: bool, - should_persist_doctests: bool, - error_format: ErrorOutputType, - test_run_directory: Option, - nocapture: bool, arg_file: PathBuf, outdir: DirState, - runtool: Option, - runtool_args: Vec, - target: TargetTriple, test_id: String, - maybe_sysroot: Option, } impl IndividualTestOptions { @@ -955,22 +947,7 @@ impl IndividualTestOptions { DirState::Temp(get_doctest_dir().expect("rustdoc needs a tempdir")) }; - Self { - test_builder: options.test_builder.clone(), - test_builder_wrappers: options.test_builder_wrappers.clone(), - is_json_unused_externs_enabled: options.json_unused_externs.is_enabled(), - should_persist_doctests: options.persist_doctests.is_none(), - error_format: options.error_format, - test_run_directory: options.test_run_directory.clone(), - nocapture: options.nocapture, - arg_file: arg_file.into(), - outdir, - runtool: options.runtool.clone(), - runtool_args: options.runtool_args.clone(), - target: options.target.clone(), - test_id, - maybe_sysroot: options.maybe_sysroot.clone(), - } + Self { arg_file: arg_file.into(), outdir, test_id } } } @@ -994,7 +971,7 @@ pub(crate) trait DoctestVisitor { pub(crate) struct CreateRunnableDoctests { pub(crate) tests: Vec, - rustdoc_options: RustdocOptions, + rustdoc_options: Arc, crate_name: String, opts: GlobalTestOptions, visited_tests: FxHashMap<(String, usize), usize>, @@ -1012,7 +989,7 @@ impl CreateRunnableDoctests { ) -> CreateRunnableDoctests { CreateRunnableDoctests { tests: Vec::new(), - rustdoc_options, + rustdoc_options: Arc::new(rustdoc_options), crate_name, opts, visited_tests: FxHashMap::default(), @@ -1077,6 +1054,7 @@ impl CreateRunnableDoctests { }, ); + let rustdoc_options = self.rustdoc_options.clone(); let rustdoc_test_options = IndividualTestOptions::new(&self.rustdoc_options, &self.arg_file, test_id); @@ -1112,6 +1090,7 @@ impl CreateRunnableDoctests { path, scraped_test: test, }, + rustdoc_options, unused_externs, ) })), @@ -1132,6 +1111,7 @@ struct RunnableDoctest { fn doctest_run_fn( runnable_test: RunnableDoctest, + rustdoc_options: Arc, unused_externs: Arc>>, ) -> Result<(), String> { let report_unused_externs = |uext| { @@ -1141,6 +1121,7 @@ fn doctest_run_fn( &runnable_test.scraped_test.text, &runnable_test.crate_name, runnable_test.scraped_test.line, + &rustdoc_options, runnable_test.rustdoc_test_options, runnable_test.scraped_test.langstr, runnable_test.no_run, From 790b7e9cbfb6aa591520856b018402d62eceb690 Mon Sep 17 00:00:00 2001 From: Noah Lev Date: Fri, 31 May 2024 16:56:04 -0700 Subject: [PATCH 76/92] rustdoc: Remove `DoctestVisitor::get_line` This was used to get the line number of the first line from the current docstring, which was then used together with an offset within the docstring. It's simpler to just pass the offset to the visitor and have it do the math because it's clearer and this calculation only needs to be done in one place (the Rust doctest visitor). --- src/librustdoc/doctest.rs | 11 ++++----- src/librustdoc/doctest/markdown.rs | 10 ++++---- src/librustdoc/doctest/rust.rs | 17 ++++++------- src/librustdoc/html/markdown.rs | 24 ++++++++++++++++++- .../passes/check_doc_test_visibility.rs | 4 ++-- 5 files changed, 42 insertions(+), 24 deletions(-) diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index f1a4df52a59b..dcb350748b64 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -34,7 +34,7 @@ use std::sync::{Arc, Mutex}; use tempfile::{Builder as TempFileBuilder, TempDir}; use crate::config::Options as RustdocOptions; -use crate::html::markdown::{ErrorCodes, Ignore, LangString}; +use crate::html::markdown::{ErrorCodes, Ignore, LangString, MdRelLine}; use crate::lint::init_lints; use self::rust::HirCollector; @@ -961,10 +961,7 @@ struct ScrapedDoctest { } pub(crate) trait DoctestVisitor { - fn visit_test(&mut self, test: String, config: LangString, line: usize); - fn get_line(&self) -> usize { - 0 - } + fn visit_test(&mut self, test: String, config: LangString, rel_line: MdRelLine); fn visit_header(&mut self, _name: &str, _level: u32) {} } @@ -1187,8 +1184,8 @@ fn doctest_run_fn( #[cfg(test)] // used in tests impl DoctestVisitor for Vec { - fn visit_test(&mut self, _test: String, _config: LangString, line: usize) { - self.push(line); + fn visit_test(&mut self, _test: String, _config: LangString, rel_line: MdRelLine) { + self.push(1 + rel_line.offset()); } } diff --git a/src/librustdoc/doctest/markdown.rs b/src/librustdoc/doctest/markdown.rs index 5d0dc5926e88..5fc5f59036bf 100644 --- a/src/librustdoc/doctest/markdown.rs +++ b/src/librustdoc/doctest/markdown.rs @@ -9,7 +9,7 @@ use super::{ generate_args_file, CreateRunnableDoctests, DoctestVisitor, GlobalTestOptions, ScrapedDoctest, }; use crate::config::Options; -use crate::html::markdown::{find_testable_code, ErrorCodes, LangString}; +use crate::html::markdown::{find_testable_code, ErrorCodes, LangString, MdRelLine}; struct MdCollector { tests: Vec, @@ -18,8 +18,10 @@ struct MdCollector { } impl DoctestVisitor for MdCollector { - fn visit_test(&mut self, test: String, config: LangString, line: usize) { + fn visit_test(&mut self, test: String, config: LangString, rel_line: MdRelLine) { let filename = self.filename.clone(); + // First line of Markdown is line 1. + let line = 1 + rel_line.offset(); self.tests.push(ScrapedDoctest { filename, line, @@ -29,10 +31,6 @@ impl DoctestVisitor for MdCollector { }); } - fn get_line(&self) -> usize { - 0 - } - fn visit_header(&mut self, name: &str, level: u32) { // We use these headings as test names, so it's good if // they're valid identifiers. diff --git a/src/librustdoc/doctest/rust.rs b/src/librustdoc/doctest/rust.rs index 458c90881c93..e6bef395fa9c 100644 --- a/src/librustdoc/doctest/rust.rs +++ b/src/librustdoc/doctest/rust.rs @@ -15,7 +15,7 @@ use rustc_span::{BytePos, FileName, Pos, Span, DUMMY_SP}; use super::{DoctestVisitor, ScrapedDoctest}; use crate::clean::{types::AttributesExt, Attributes}; -use crate::html::markdown::{self, ErrorCodes, LangString}; +use crate::html::markdown::{self, ErrorCodes, LangString, MdRelLine}; struct RustCollector { source_map: Lrc, @@ -36,10 +36,17 @@ impl RustCollector { } filename } + + fn get_base_line(&self) -> usize { + let sp_lo = self.position.lo().to_usize(); + let loc = self.source_map.lookup_char_pos(BytePos(sp_lo as u32)); + loc.line + } } impl DoctestVisitor for RustCollector { - fn visit_test(&mut self, test: String, config: LangString, line: usize) { + fn visit_test(&mut self, test: String, config: LangString, rel_line: MdRelLine) { + let line = self.get_base_line() + rel_line.offset(); self.tests.push(ScrapedDoctest { filename: self.get_filename(), line, @@ -49,12 +56,6 @@ impl DoctestVisitor for RustCollector { }); } - fn get_line(&self) -> usize { - let line = self.position.lo().to_usize(); - let line = self.source_map.lookup_char_pos(BytePos(line as u32)).line; - if line > 0 { line - 1 } else { line } - } - fn visit_header(&mut self, _name: &str, _level: u32) {} } diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index c8973679132c..17aa0ecf23dc 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -710,6 +710,28 @@ impl<'a, I: Iterator>> Iterator for Footnotes<'a, I> { } } +/// A newtype that represents a relative line number in Markdown. +/// +/// In other words, this represents an offset from the first line of Markdown +/// in a doc comment or other source. If the first Markdown line appears on line 32, +/// and the `MdRelLine` is 3, then the absolute line for this one is 35. I.e., it's +/// a zero-based offset. +pub(crate) struct MdRelLine { + offset: usize, +} + +impl MdRelLine { + /// See struct docs. + pub(crate) const fn new(offset: usize) -> Self { + Self { offset } + } + + /// See struct docs. + pub(crate) const fn offset(self) -> usize { + self.offset + } +} + pub(crate) fn find_testable_code( doc: &str, tests: &mut T, @@ -772,7 +794,7 @@ pub(crate) fn find_codes( if nb_lines != 0 && !&doc[prev_offset..offset.start].ends_with('\n') { nb_lines -= 1; } - let line = tests.get_line() + nb_lines + 1; + let line = MdRelLine::new(nb_lines); tests.visit_test(text, block_info, line); prev_offset = offset.start; } diff --git a/src/librustdoc/passes/check_doc_test_visibility.rs b/src/librustdoc/passes/check_doc_test_visibility.rs index a4cdb488f05f..0437f5e5fd81 100644 --- a/src/librustdoc/passes/check_doc_test_visibility.rs +++ b/src/librustdoc/passes/check_doc_test_visibility.rs @@ -10,7 +10,7 @@ use crate::clean; use crate::clean::utils::inherits_doc_hidden; use crate::clean::*; use crate::core::DocContext; -use crate::html::markdown::{find_testable_code, ErrorCodes, Ignore, LangString}; +use crate::html::markdown::{find_testable_code, ErrorCodes, Ignore, LangString, MdRelLine}; use crate::visit::DocVisitor; use rustc_hir as hir; use rustc_middle::lint::LintLevelSource; @@ -45,7 +45,7 @@ pub(crate) struct Tests { } impl crate::doctest::DoctestVisitor for Tests { - fn visit_test(&mut self, _: String, config: LangString, _: usize) { + fn visit_test(&mut self, _: String, config: LangString, _: MdRelLine) { if config.rust && config.ignore == Ignore::None { self.found_tests += 1; } From e9e26345475f08e4c5d9be597eddcc9b437b9a1a Mon Sep 17 00:00:00 2001 From: Noah Lev Date: Mon, 3 Jun 2024 20:32:28 -0700 Subject: [PATCH 77/92] Make two fields computed on-demand --- src/librustdoc/doctest.rs | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index dcb350748b64..1d027fbd15d8 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -960,6 +960,16 @@ struct ScrapedDoctest { text: String, } +impl ScrapedDoctest { + fn edition(&self, opts: &RustdocOptions) -> Edition { + self.langstr.edition.unwrap_or(opts.edition) + } + + fn no_run(&self, opts: &RustdocOptions) -> bool { + self.langstr.no_run || opts.no_run + } +} + pub(crate) trait DoctestVisitor { fn visit_test(&mut self, test: String, config: LangString, rel_line: MdRelLine); fn visit_header(&mut self, _name: &str, _level: u32) {} @@ -1009,10 +1019,8 @@ impl CreateRunnableDoctests { let name = self.generate_name(&test.filename, test.line, &test.logical_path); let crate_name = self.crate_name.clone(); let opts = self.opts.clone(); - let edition = test.langstr.edition.unwrap_or(self.rustdoc_options.edition); let target_str = self.rustdoc_options.target.to_string(); let unused_externs = self.unused_extern_reports.clone(); - let no_run = test.langstr.no_run || self.rustdoc_options.no_run; if !test.langstr.compile_fail { self.compiling_test_count.fetch_add(1, Ordering::SeqCst); } @@ -1073,7 +1081,7 @@ impl CreateRunnableDoctests { // compiler failures are test failures should_panic: test::ShouldPanic::No, compile_fail: test.langstr.compile_fail, - no_run, + no_run: test.no_run(&rustdoc_options), test_type: test::TestType::DocTest, }, testfn: test::DynTestFn(Box::new(move || { @@ -1081,9 +1089,7 @@ impl CreateRunnableDoctests { RunnableDoctest { crate_name, rustdoc_test_options, - no_run, opts, - edition, path, scraped_test: test, }, @@ -1099,9 +1105,7 @@ impl CreateRunnableDoctests { struct RunnableDoctest { crate_name: String, rustdoc_test_options: IndividualTestOptions, - no_run: bool, opts: GlobalTestOptions, - edition: Edition, path: PathBuf, scraped_test: ScrapedDoctest, } @@ -1114,6 +1118,8 @@ fn doctest_run_fn( let report_unused_externs = |uext| { unused_externs.lock().unwrap().push(uext); }; + let no_run = runnable_test.scraped_test.no_run(&rustdoc_options); + let edition = runnable_test.scraped_test.edition(&rustdoc_options); let res = run_test( &runnable_test.scraped_test.text, &runnable_test.crate_name, @@ -1121,9 +1127,9 @@ fn doctest_run_fn( &rustdoc_options, runnable_test.rustdoc_test_options, runnable_test.scraped_test.langstr, - runnable_test.no_run, + no_run, &runnable_test.opts, - runnable_test.edition, + edition, runnable_test.path, report_unused_externs, ); From a429afacbc4c6a753af6b526e74f19b7caedaaad Mon Sep 17 00:00:00 2001 From: Noah Lev Date: Mon, 3 Jun 2024 22:49:54 -0700 Subject: [PATCH 78/92] Remove `RunnableDoctest` It should instead be the actual input to the running logic. Currently it's not actually quite runnable since it's still missing some information. --- src/librustdoc/doctest.rs | 41 ++++++++++++++++----------------------- 1 file changed, 17 insertions(+), 24 deletions(-) diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 1d027fbd15d8..043d70dc6aac 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -1086,13 +1086,11 @@ impl CreateRunnableDoctests { }, testfn: test::DynTestFn(Box::new(move || { doctest_run_fn( - RunnableDoctest { - crate_name, - rustdoc_test_options, - opts, - path, - scraped_test: test, - }, + crate_name, + rustdoc_test_options, + opts, + path, + test, rustdoc_options, unused_externs, ) @@ -1101,36 +1099,31 @@ impl CreateRunnableDoctests { } } -/// A doctest that is ready to run. -struct RunnableDoctest { +fn doctest_run_fn( crate_name: String, - rustdoc_test_options: IndividualTestOptions, - opts: GlobalTestOptions, + test_opts: IndividualTestOptions, + global_opts: GlobalTestOptions, path: PathBuf, scraped_test: ScrapedDoctest, -} - -fn doctest_run_fn( - runnable_test: RunnableDoctest, rustdoc_options: Arc, unused_externs: Arc>>, ) -> Result<(), String> { let report_unused_externs = |uext| { unused_externs.lock().unwrap().push(uext); }; - let no_run = runnable_test.scraped_test.no_run(&rustdoc_options); - let edition = runnable_test.scraped_test.edition(&rustdoc_options); + let no_run = scraped_test.no_run(&rustdoc_options); + let edition = scraped_test.edition(&rustdoc_options); let res = run_test( - &runnable_test.scraped_test.text, - &runnable_test.crate_name, - runnable_test.scraped_test.line, + &scraped_test.text, + &crate_name, + scraped_test.line, &rustdoc_options, - runnable_test.rustdoc_test_options, - runnable_test.scraped_test.langstr, + test_opts, + scraped_test.langstr, no_run, - &runnable_test.opts, + &global_opts, edition, - runnable_test.path, + path, report_unused_externs, ); From 366000dc07340abfeba6de1d9e7db0913304fa59 Mon Sep 17 00:00:00 2001 From: Noah Lev Date: Mon, 3 Jun 2024 23:16:40 -0700 Subject: [PATCH 79/92] Move some arguments to fields and reorganize fields I moved some local arguments and options to either the local options struct or, if it made sense, the global options struct. --- src/librustdoc/doctest.rs | 74 ++++++++++++------------------ src/librustdoc/doctest/markdown.rs | 26 ++++++----- src/librustdoc/html/markdown.rs | 12 ++++- 3 files changed, 54 insertions(+), 58 deletions(-) diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 043d70dc6aac..5e3dc4200b7b 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -40,8 +40,10 @@ use crate::lint::init_lints; use self::rust::HirCollector; /// Options that apply to all doctests in a crate or Markdown file (for `rustdoc foo.md`). -#[derive(Clone, Default)] +#[derive(Clone)] pub(crate) struct GlobalTestOptions { + /// Name of the crate (for regular `rustdoc`) or Markdown file (for `rustdoc foo.md`). + pub(crate) crate_name: String, /// Whether to disable the default `extern crate my_crate;` when creating doctests. pub(crate) no_crate_inject: bool, /// Whether inserting extra indent spaces in code block, @@ -49,6 +51,8 @@ pub(crate) struct GlobalTestOptions { pub(crate) insert_indent_space: bool, /// Additional crate-level attributes to add to doctests. pub(crate) attrs: Vec, + /// Path to file containing arguments for the invocation of rustc. + pub(crate) args_file: PathBuf, } pub(crate) fn generate_args_file(file_path: &Path, options: &RustdocOptions) -> Result<(), String> { @@ -167,24 +171,19 @@ pub(crate) fn run( Ok(temp_dir) => temp_dir, Err(error) => return crate::wrap_return(dcx, Err(error)), }; - let file_path = temp_dir.path().join("rustdoc-cfgs"); - crate::wrap_return(dcx, generate_args_file(&file_path, &options))?; + let args_path = temp_dir.path().join("rustdoc-cfgs"); + crate::wrap_return(dcx, generate_args_file(&args_path, &options))?; let (tests, unused_extern_reports, compiling_test_count) = interface::run_compiler(config, |compiler| { compiler.enter(|queries| { let collector = queries.global_ctxt()?.enter(|tcx| { + let crate_name = tcx.crate_name(LOCAL_CRATE).to_string(); let crate_attrs = tcx.hir().attrs(CRATE_HIR_ID); - - let opts = scrape_test_config(crate_attrs); + let opts = scrape_test_config(crate_name, crate_attrs, args_path); let enable_per_target_ignores = options.enable_per_target_ignores; - let mut collector = CreateRunnableDoctests::new( - tcx.crate_name(LOCAL_CRATE).to_string(), - options, - opts, - file_path, - ); + let mut collector = CreateRunnableDoctests::new(options, opts); let hir_collector = HirCollector::new( &compiler.sess, tcx.hir(), @@ -264,11 +263,20 @@ pub(crate) fn run_tests( } // Look for `#![doc(test(no_crate_inject))]`, used by crates in the std facade. -fn scrape_test_config(attrs: &[ast::Attribute]) -> GlobalTestOptions { +fn scrape_test_config( + crate_name: String, + attrs: &[ast::Attribute], + args_file: PathBuf, +) -> GlobalTestOptions { use rustc_ast_pretty::pprust; - let mut opts = - GlobalTestOptions { no_crate_inject: false, attrs: Vec::new(), insert_indent_space: false }; + let mut opts = GlobalTestOptions { + crate_name, + no_crate_inject: false, + attrs: Vec::new(), + insert_indent_space: false, + args_file, + }; let test_attrs: Vec<_> = attrs .iter() @@ -363,7 +371,6 @@ fn wrapped_rustc_command(rustc_wrappers: &[PathBuf], rustc_binary: &Path) -> Com fn run_test( test: &str, - crate_name: &str, line: usize, rustdoc_options: &RustdocOptions, test_options: IndividualTestOptions, @@ -371,12 +378,11 @@ fn run_test( no_run: bool, opts: &GlobalTestOptions, edition: Edition, - path: PathBuf, report_unused_externs: impl Fn(UnusedExterns), ) -> Result<(), TestFailure> { let (test, line_offset, supports_color) = make_test( test, - Some(crate_name), + Some(&opts.crate_name), lang_string.test_harness, opts, edition, @@ -393,14 +399,14 @@ fn run_test( .unwrap_or_else(|| rustc_interface::util::rustc_path().expect("found rustc")); let mut compiler = wrapped_rustc_command(&rustdoc_options.test_builder_wrappers, rustc_binary); - compiler.arg(&format!("@{}", test_options.arg_file.display())); + compiler.arg(&format!("@{}", opts.args_file.display())); if let Some(sysroot) = &rustdoc_options.maybe_sysroot { compiler.arg(format!("--sysroot={}", sysroot.display())); } compiler.arg("--edition").arg(&edition.to_string()); - compiler.env("UNSTABLE_RUSTDOC_TEST_PATH", path); + compiler.env("UNSTABLE_RUSTDOC_TEST_PATH", &test_options.path); compiler.env("UNSTABLE_RUSTDOC_TEST_LINE", format!("{}", line as isize - line_offset as isize)); compiler.arg("-o").arg(&output_file); if lang_string.test_harness { @@ -926,13 +932,13 @@ fn partition_source(s: &str, edition: Edition) -> (String, String, String) { } pub(crate) struct IndividualTestOptions { - arg_file: PathBuf, outdir: DirState, test_id: String, + path: PathBuf, } impl IndividualTestOptions { - fn new(options: &RustdocOptions, arg_file: &Path, test_id: String) -> Self { + fn new(options: &RustdocOptions, test_id: String, test_path: PathBuf) -> Self { let outdir = if let Some(ref path) = options.persist_doctests { let mut path = path.clone(); path.push(&test_id); @@ -947,7 +953,7 @@ impl IndividualTestOptions { DirState::Temp(get_doctest_dir().expect("rustdoc needs a tempdir")) }; - Self { arg_file: arg_file.into(), outdir, test_id } + Self { outdir, test_id, path: test_path } } } @@ -979,30 +985,24 @@ pub(crate) struct CreateRunnableDoctests { pub(crate) tests: Vec, rustdoc_options: Arc, - crate_name: String, opts: GlobalTestOptions, visited_tests: FxHashMap<(String, usize), usize>, unused_extern_reports: Arc>>, compiling_test_count: AtomicUsize, - arg_file: PathBuf, } impl CreateRunnableDoctests { pub(crate) fn new( - crate_name: String, rustdoc_options: RustdocOptions, opts: GlobalTestOptions, - arg_file: PathBuf, ) -> CreateRunnableDoctests { CreateRunnableDoctests { tests: Vec::new(), rustdoc_options: Arc::new(rustdoc_options), - crate_name, opts, visited_tests: FxHashMap::default(), unused_extern_reports: Default::default(), compiling_test_count: AtomicUsize::new(0), - arg_file, } } @@ -1017,7 +1017,6 @@ impl CreateRunnableDoctests { fn add_test(&mut self, test: ScrapedDoctest) { let name = self.generate_name(&test.filename, test.line, &test.logical_path); - let crate_name = self.crate_name.clone(); let opts = self.opts.clone(); let target_str = self.rustdoc_options.target.to_string(); let unused_externs = self.unused_extern_reports.clone(); @@ -1060,8 +1059,7 @@ impl CreateRunnableDoctests { ); let rustdoc_options = self.rustdoc_options.clone(); - let rustdoc_test_options = - IndividualTestOptions::new(&self.rustdoc_options, &self.arg_file, test_id); + let rustdoc_test_options = IndividualTestOptions::new(&self.rustdoc_options, test_id, path); debug!("creating test {name}: {}", test.text); self.tests.push(test::TestDescAndFn { @@ -1085,25 +1083,15 @@ impl CreateRunnableDoctests { test_type: test::TestType::DocTest, }, testfn: test::DynTestFn(Box::new(move || { - doctest_run_fn( - crate_name, - rustdoc_test_options, - opts, - path, - test, - rustdoc_options, - unused_externs, - ) + doctest_run_fn(rustdoc_test_options, opts, test, rustdoc_options, unused_externs) })), }); } } fn doctest_run_fn( - crate_name: String, test_opts: IndividualTestOptions, global_opts: GlobalTestOptions, - path: PathBuf, scraped_test: ScrapedDoctest, rustdoc_options: Arc, unused_externs: Arc>>, @@ -1115,7 +1103,6 @@ fn doctest_run_fn( let edition = scraped_test.edition(&rustdoc_options); let res = run_test( &scraped_test.text, - &crate_name, scraped_test.line, &rustdoc_options, test_opts, @@ -1123,7 +1110,6 @@ fn doctest_run_fn( no_run, &global_opts, edition, - path, report_unused_externs, ); diff --git a/src/librustdoc/doctest/markdown.rs b/src/librustdoc/doctest/markdown.rs index 5fc5f59036bf..b8ab7adb36e8 100644 --- a/src/librustdoc/doctest/markdown.rs +++ b/src/librustdoc/doctest/markdown.rs @@ -73,7 +73,7 @@ impl DoctestVisitor for MdCollector { } } -/// Runs any tests/code examples in the markdown file `input`. +/// Runs any tests/code examples in the markdown file `options.input`. pub(crate) fn test(options: Options) -> Result<(), String> { use rustc_session::config::Input; let input_str = match &options.input { @@ -83,13 +83,20 @@ pub(crate) fn test(options: Options) -> Result<(), String> { Input::Str { name: _, input } => input.clone(), }; - let mut opts = GlobalTestOptions::default(); - opts.no_crate_inject = true; - + // Obviously not a real crate name, but close enough for purposes of doctests. + let crate_name = options.input.filestem().to_string(); let temp_dir = tempdir().map_err(|error| format!("failed to create temporary directory: {error:?}"))?; - let file_path = temp_dir.path().join("rustdoc-cfgs"); - generate_args_file(&file_path, &options)?; + let args_file = temp_dir.path().join("rustdoc-cfgs"); + generate_args_file(&args_file, &options)?; + + let opts = GlobalTestOptions { + crate_name, + no_crate_inject: true, + insert_indent_space: false, + attrs: vec![], + args_file, + }; let mut md_collector = MdCollector { tests: vec![], @@ -111,12 +118,7 @@ pub(crate) fn test(options: Options) -> Result<(), String> { None, ); - let mut collector = CreateRunnableDoctests::new( - options.input.filestem().to_string(), - options.clone(), - opts, - file_path, - ); + let mut collector = CreateRunnableDoctests::new(options.clone(), opts); md_collector.tests.into_iter().for_each(|t| collector.add_test(t)); crate::doctest::run_tests(options.test_args, options.nocapture, collector.tests); Ok(()) diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index 17aa0ecf23dc..bae929c64eab 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -39,6 +39,7 @@ use std::collections::VecDeque; use std::fmt::Write; use std::iter::Peekable; use std::ops::{ControlFlow, Range}; +use std::path::PathBuf; use std::str::{self, CharIndices}; use std::sync::OnceLock; @@ -287,8 +288,15 @@ impl<'a, I: Iterator>> Iterator for CodeBlocks<'_, 'a, I> { .collect::(); let krate = krate.as_ref().map(|s| s.as_str()); - let mut opts: GlobalTestOptions = Default::default(); - opts.insert_indent_space = true; + // FIXME: separate out the code to make a code block into runnable code + // from the complicated doctest logic + let opts = GlobalTestOptions { + crate_name: krate.map(String::from).unwrap_or_default(), + no_crate_inject: false, + insert_indent_space: true, + attrs: vec![], + args_file: PathBuf::new(), + }; let (test, _, _) = doctest::make_test(&test, krate, false, &opts, edition, None); let channel = if test.contains("#![feature(") { "&version=nightly" } else { "" }; From 0dc72d95514d3d11eca2e430bbd206ceb0c8b135 Mon Sep 17 00:00:00 2001 From: Noah Lev Date: Mon, 3 Jun 2024 23:47:12 -0700 Subject: [PATCH 80/92] Make doctests before running them; reintroduce `RunnableDoctest` --- src/librustdoc/doctest.rs | 94 ++++++++++++++++++++++----------------- 1 file changed, 53 insertions(+), 41 deletions(-) diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 5e3dc4200b7b..c7622d5b5394 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -369,29 +369,25 @@ fn wrapped_rustc_command(rustc_wrappers: &[PathBuf], rustc_binary: &Path) -> Com command } +struct RunnableDoctest { + full_test_code: String, + full_test_line_offset: usize, + test_opts: IndividualTestOptions, + global_opts: GlobalTestOptions, + scraped_test: ScrapedDoctest, +} + fn run_test( - test: &str, - line: usize, + doctest: RunnableDoctest, rustdoc_options: &RustdocOptions, - test_options: IndividualTestOptions, - mut lang_string: LangString, - no_run: bool, - opts: &GlobalTestOptions, - edition: Edition, + supports_color: bool, report_unused_externs: impl Fn(UnusedExterns), ) -> Result<(), TestFailure> { - let (test, line_offset, supports_color) = make_test( - test, - Some(&opts.crate_name), - lang_string.test_harness, - opts, - edition, - Some(&test_options.test_id), - ); - + let scraped_test = &doctest.scraped_test; + let langstr = &scraped_test.langstr; // Make sure we emit well-formed executable names for our target. let rust_out = add_exe_suffix("rust_out".to_owned(), &rustdoc_options.target); - let output_file = test_options.outdir.path().join(rust_out); + let output_file = doctest.test_opts.outdir.path().join(rust_out); let rustc_binary = rustdoc_options .test_builder @@ -399,27 +395,33 @@ fn run_test( .unwrap_or_else(|| rustc_interface::util::rustc_path().expect("found rustc")); let mut compiler = wrapped_rustc_command(&rustdoc_options.test_builder_wrappers, rustc_binary); - compiler.arg(&format!("@{}", opts.args_file.display())); + compiler.arg(&format!("@{}", doctest.global_opts.args_file.display())); if let Some(sysroot) = &rustdoc_options.maybe_sysroot { compiler.arg(format!("--sysroot={}", sysroot.display())); } - compiler.arg("--edition").arg(&edition.to_string()); - compiler.env("UNSTABLE_RUSTDOC_TEST_PATH", &test_options.path); - compiler.env("UNSTABLE_RUSTDOC_TEST_LINE", format!("{}", line as isize - line_offset as isize)); + compiler.arg("--edition").arg(&scraped_test.edition(rustdoc_options).to_string()); + compiler.env("UNSTABLE_RUSTDOC_TEST_PATH", &doctest.test_opts.path); + compiler.env( + "UNSTABLE_RUSTDOC_TEST_LINE", + format!("{}", scraped_test.line as isize - doctest.full_test_line_offset as isize), + ); compiler.arg("-o").arg(&output_file); - if lang_string.test_harness { + if langstr.test_harness { compiler.arg("--test"); } - if rustdoc_options.json_unused_externs.is_enabled() && !lang_string.compile_fail { + if rustdoc_options.json_unused_externs.is_enabled() && !langstr.compile_fail { compiler.arg("--error-format=json"); compiler.arg("--json").arg("unused-externs"); compiler.arg("-W").arg("unused_crate_dependencies"); compiler.arg("-Z").arg("unstable-options"); } - if no_run && !lang_string.compile_fail && rustdoc_options.persist_doctests.is_none() { + if scraped_test.no_run(rustdoc_options) + && !langstr.compile_fail + && rustdoc_options.persist_doctests.is_none() + { // FIXME: why does this code check if it *shouldn't* persist doctests // -- shouldn't it be the negation? compiler.arg("--emit=metadata"); @@ -459,7 +461,7 @@ fn run_test( let mut child = compiler.spawn().expect("Failed to spawn rustc process"); { let stdin = child.stdin.as_mut().expect("Failed to open stdin"); - stdin.write_all(test.as_bytes()).expect("could write out test sources"); + stdin.write_all(doctest.full_test_code.as_bytes()).expect("could write out test sources"); } let output = child.wait_with_output().expect("Failed to read stdout"); @@ -490,20 +492,26 @@ fn run_test( } let _bomb = Bomb(&out); - match (output.status.success(), lang_string.compile_fail) { + match (output.status.success(), langstr.compile_fail) { (true, true) => { return Err(TestFailure::UnexpectedCompilePass); } (true, false) => {} (false, true) => { - if !lang_string.error_codes.is_empty() { + if !langstr.error_codes.is_empty() { // We used to check if the output contained "error[{}]: " but since we added the // colored output, we can't anymore because of the color escape characters before // the ":". - lang_string.error_codes.retain(|err| !out.contains(&format!("error[{err}]"))); + let missing_codes: Vec = scraped_test + .langstr + .error_codes + .iter() + .filter(|err| !out.contains(&format!("error[{err}]"))) + .cloned() + .collect(); - if !lang_string.error_codes.is_empty() { - return Err(TestFailure::MissingErrorCodes(lang_string.error_codes)); + if !missing_codes.is_empty() { + return Err(TestFailure::MissingErrorCodes(missing_codes)); } } } @@ -512,7 +520,7 @@ fn run_test( } } - if no_run { + if scraped_test.no_run(rustdoc_options) { return Ok(()); } @@ -544,9 +552,9 @@ fn run_test( match result { Err(e) => return Err(TestFailure::ExecutionError(e)), Ok(out) => { - if lang_string.should_panic && out.status.success() { + if langstr.should_panic && out.status.success() { return Err(TestFailure::UnexpectedRunPass); - } else if !lang_string.should_panic && !out.status.success() { + } else if !langstr.should_panic && !out.status.success() { return Err(TestFailure::ExecutionFailure(out)); } } @@ -1099,19 +1107,23 @@ fn doctest_run_fn( let report_unused_externs = |uext| { unused_externs.lock().unwrap().push(uext); }; - let no_run = scraped_test.no_run(&rustdoc_options); let edition = scraped_test.edition(&rustdoc_options); - let res = run_test( + let (full_test_code, full_test_line_offset, supports_color) = make_test( &scraped_test.text, - scraped_test.line, - &rustdoc_options, - test_opts, - scraped_test.langstr, - no_run, + Some(&global_opts.crate_name), + scraped_test.langstr.test_harness, &global_opts, edition, - report_unused_externs, + Some(&test_opts.test_id), ); + let runnable_test = RunnableDoctest { + full_test_code, + full_test_line_offset, + test_opts, + global_opts, + scraped_test, + }; + let res = run_test(runnable_test, &rustdoc_options, supports_color, report_unused_externs); if let Err(err) = res { match err { From d06a05e26289ce55114462786faeaf0e7a559cf5 Mon Sep 17 00:00:00 2001 From: Noah Lev Date: Mon, 3 Jun 2024 23:58:42 -0700 Subject: [PATCH 81/92] Move logic for "making" doctests to submodule This code turns the raw code given by the user into something actually runnable, e.g. by adding a `main` function if it doesn't already exist. I also made a couple other items private that didn't need to be crate-public. --- src/librustdoc/doctest.rs | 381 +-------------------------------- src/librustdoc/doctest/make.rs | 381 +++++++++++++++++++++++++++++++++ 2 files changed, 387 insertions(+), 375 deletions(-) create mode 100644 src/librustdoc/doctest/make.rs diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index c7622d5b5394..a732e645b6ba 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -1,23 +1,19 @@ +mod make; mod markdown; mod rust; +pub(crate) use make::make_test; pub(crate) use markdown::test as test_markdown; use rustc_ast as ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_data_structures::sync::Lrc; -use rustc_errors::emitter::stderr_destination; use rustc_errors::{ColorConfig, ErrorGuaranteed, FatalError}; use rustc_hir::def_id::LOCAL_CRATE; use rustc_hir::CRATE_HIR_ID; use rustc_interface::interface; -use rustc_parse::new_parser_from_source_str; -use rustc_parse::parser::attr::InnerAttrPolicy; use rustc_session::config::{self, CrateType, ErrorOutputType}; use rustc_session::lint; -use rustc_session::parse::ParseSess; use rustc_span::edition::Edition; -use rustc_span::source_map::SourceMap; use rustc_span::symbol::sym; use rustc_span::FileName; use rustc_target::spec::{Target, TargetTriple}; @@ -577,369 +573,7 @@ fn make_maybe_absolute_path(path: PathBuf) -> PathBuf { } } -/// Transforms a test into code that can be compiled into a Rust binary, and returns the number of -/// lines before the test code begins as well as if the output stream supports colors or not. -pub(crate) fn make_test( - s: &str, - crate_name: Option<&str>, - dont_insert_main: bool, - opts: &GlobalTestOptions, - edition: Edition, - test_id: Option<&str>, -) -> (String, usize, bool) { - let (crate_attrs, everything_else, crates) = partition_source(s, edition); - let everything_else = everything_else.trim(); - let mut line_offset = 0; - let mut prog = String::new(); - let mut supports_color = false; - - if opts.attrs.is_empty() { - // If there aren't any attributes supplied by #![doc(test(attr(...)))], then allow some - // lints that are commonly triggered in doctests. The crate-level test attributes are - // commonly used to make tests fail in case they trigger warnings, so having this there in - // that case may cause some tests to pass when they shouldn't have. - prog.push_str("#![allow(unused)]\n"); - line_offset += 1; - } - - // Next, any attributes that came from the crate root via #![doc(test(attr(...)))]. - for attr in &opts.attrs { - prog.push_str(&format!("#![{attr}]\n")); - line_offset += 1; - } - - // Now push any outer attributes from the example, assuming they - // are intended to be crate attributes. - prog.push_str(&crate_attrs); - prog.push_str(&crates); - - // 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_span::create_session_if_not_set_then(edition, |_| { - use rustc_errors::emitter::{Emitter, HumanEmitter}; - use rustc_errors::DiagCtxt; - use rustc_parse::parser::ForceCollect; - use rustc_span::source_map::FilePathMapping; - - let filename = FileName::anon_source_code(s); - let source = crates + everything_else; - - // Any errors in parsing should also appear when the doctest is compiled for real, so just - // send all the errors that librustc_ast emits directly into a `Sink` instead of stderr. - let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); - let fallback_bundle = rustc_errors::fallback_fluent_bundle( - rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(), - false, - ); - supports_color = - HumanEmitter::new(stderr_destination(ColorConfig::Auto), fallback_bundle.clone()) - .supports_color(); - - let emitter = HumanEmitter::new(Box::new(io::sink()), fallback_bundle); - - // FIXME(misdreavus): pass `-Z treat-err-as-bug` to the doctest parser - let dcx = DiagCtxt::new(Box::new(emitter)).disable_warnings(); - let psess = ParseSess::with_dcx(dcx, sm); - - let mut found_main = false; - let mut found_extern_crate = crate_name.is_none(); - let mut found_macro = false; - - let mut parser = match new_parser_from_source_str(&psess, filename, source) { - Ok(p) => p, - Err(errs) => { - errs.into_iter().for_each(|err| err.cancel()); - return (found_main, found_extern_crate, found_macro); - } - }; - - loop { - match parser.parse_item(ForceCollect::No) { - Ok(Some(item)) => { - if !found_main - && let ast::ItemKind::Fn(..) = item.kind - && item.ident.name == sym::main - { - found_main = true; - } - - if !found_extern_crate - && let ast::ItemKind::ExternCrate(original) = item.kind - { - // This code will never be reached if `crate_name` is none because - // `found_extern_crate` is initialized to `true` if it is none. - let crate_name = crate_name.unwrap(); - - match original { - Some(name) => found_extern_crate = name.as_str() == crate_name, - None => found_extern_crate = item.ident.as_str() == crate_name, - } - } - - if !found_macro && let ast::ItemKind::MacCall(..) = item.kind { - found_macro = true; - } - - if found_main && found_extern_crate { - break; - } - } - Ok(None) => break, - Err(e) => { - e.cancel(); - break; - } - } - - // The supplied item is only used for diagnostics, - // which are swallowed here anyway. - parser.maybe_consume_incorrect_semicolon(None); - } - - // Reset errors so that they won't be reported as compiler bugs when dropping the - // dcx. Any errors in the tests will be reported when the test file is compiled, - // Note that we still need to cancel the errors above otherwise `Diag` will panic on - // drop. - psess.dcx.reset_err_count(); - - (found_main, found_extern_crate, found_macro) - }) - }); - let Ok((already_has_main, already_has_extern_crate, found_macro)) = result else { - // If the parser panicked due to a fatal error, pass the test code through unchanged. - // The error will be reported during compilation. - return (s.to_owned(), 0, false); - }; - - // If a doctest's `fn main` is being masked by a wrapper macro, the parsing loop above won't - // see it. In that case, run the old text-based scan to see if they at least have a main - // function written inside a macro invocation. See - // https://github.com/rust-lang/rust/issues/56898 - let already_has_main = if found_macro && !already_has_main { - s.lines() - .map(|line| { - let comment = line.find("//"); - if let Some(comment_begins) = comment { &line[0..comment_begins] } else { line } - }) - .any(|code| code.contains("fn main")) - } else { - already_has_main - }; - - // Don't inject `extern crate std` because it's already injected by the - // compiler. - if !already_has_extern_crate && !opts.no_crate_inject && crate_name != Some("std") { - if let Some(crate_name) = crate_name { - // Don't inject `extern crate` if the crate is never used. - // NOTE: this is terribly inaccurate because it doesn't actually - // parse the source, but only has false positives, not false - // negatives. - if s.contains(crate_name) { - // rustdoc implicitly inserts an `extern crate` item for the own crate - // which may be unused, so we need to allow the lint. - prog.push_str("#[allow(unused_extern_crates)]\n"); - - prog.push_str(&format!("extern crate r#{crate_name};\n")); - line_offset += 1; - } - } - } - - // FIXME: This code cannot yet handle no_std test cases yet - if dont_insert_main || already_has_main || prog.contains("![no_std]") { - prog.push_str(everything_else); - } else { - let returns_result = everything_else.trim_end().ends_with("(())"); - // Give each doctest main function a unique name. - // This is for example needed for the tooling around `-C instrument-coverage`. - let inner_fn_name = if let Some(test_id) = test_id { - format!("_doctest_main_{test_id}") - } else { - "_inner".into() - }; - let inner_attr = if test_id.is_some() { "#[allow(non_snake_case)] " } else { "" }; - let (main_pre, main_post) = if returns_result { - ( - format!( - "fn main() {{ {inner_attr}fn {inner_fn_name}() -> Result<(), impl core::fmt::Debug> {{\n", - ), - format!("\n}} {inner_fn_name}().unwrap() }}"), - ) - } else if test_id.is_some() { - ( - format!("fn main() {{ {inner_attr}fn {inner_fn_name}() {{\n",), - format!("\n}} {inner_fn_name}() }}"), - ) - } else { - ("fn main() {\n".into(), "\n}".into()) - }; - // Note on newlines: We insert a line/newline *before*, and *after* - // the doctest and adjust the `line_offset` accordingly. - // In the case of `-C instrument-coverage`, this means that the generated - // inner `main` function spans from the doctest opening codeblock to the - // closing one. For example - // /// ``` <- start of the inner main - // /// <- code under doctest - // /// ``` <- end of the inner main - line_offset += 1; - - // add extra 4 spaces for each line to offset the code block - let content = if opts.insert_indent_space { - everything_else - .lines() - .map(|line| format!(" {}", line)) - .collect::>() - .join("\n") - } else { - everything_else.to_string() - }; - prog.extend([&main_pre, content.as_str(), &main_post].iter().cloned()); - } - - debug!("final doctest:\n{prog}"); - - (prog, line_offset, supports_color) -} - -fn check_if_attr_is_complete(source: &str, edition: Edition) -> bool { - if source.is_empty() { - // Empty content so nothing to check in here... - return true; - } - rustc_driver::catch_fatal_errors(|| { - rustc_span::create_session_if_not_set_then(edition, |_| { - use rustc_errors::emitter::HumanEmitter; - use rustc_errors::DiagCtxt; - use rustc_span::source_map::FilePathMapping; - - let filename = FileName::anon_source_code(source); - // Any errors in parsing should also appear when the doctest is compiled for real, so just - // send all the errors that librustc_ast emits directly into a `Sink` instead of stderr. - let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); - let fallback_bundle = rustc_errors::fallback_fluent_bundle( - rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(), - false, - ); - - let emitter = HumanEmitter::new(Box::new(io::sink()), fallback_bundle); - - let dcx = DiagCtxt::new(Box::new(emitter)).disable_warnings(); - let psess = ParseSess::with_dcx(dcx, sm); - let mut parser = match new_parser_from_source_str(&psess, filename, source.to_owned()) { - Ok(p) => p, - Err(errs) => { - errs.into_iter().for_each(|err| err.cancel()); - // If there is an unclosed delimiter, an error will be returned by the - // tokentrees. - return false; - } - }; - // If a parsing error happened, it's very likely that the attribute is incomplete. - if let Err(e) = parser.parse_attribute(InnerAttrPolicy::Permitted) { - e.cancel(); - return false; - } - true - }) - }) - .unwrap_or(false) -} - -fn partition_source(s: &str, edition: Edition) -> (String, String, String) { - #[derive(Copy, Clone, PartialEq)] - enum PartitionState { - Attrs, - Crates, - Other, - } - let mut state = PartitionState::Attrs; - let mut before = String::new(); - let mut crates = String::new(); - let mut after = String::new(); - - let mut mod_attr_pending = String::new(); - - for line in s.lines() { - let trimline = line.trim(); - - // FIXME(misdreavus): if a doc comment is placed on an extern crate statement, it will be - // shunted into "everything else" - match state { - PartitionState::Attrs => { - state = if trimline.starts_with("#![") { - if !check_if_attr_is_complete(line, edition) { - mod_attr_pending = line.to_owned(); - } else { - mod_attr_pending.clear(); - } - PartitionState::Attrs - } else if trimline.chars().all(|c| c.is_whitespace()) - || (trimline.starts_with("//") && !trimline.starts_with("///")) - { - PartitionState::Attrs - } else if trimline.starts_with("extern crate") - || trimline.starts_with("#[macro_use] extern crate") - { - PartitionState::Crates - } else { - // First we check if the previous attribute was "complete"... - if !mod_attr_pending.is_empty() { - // If not, then we append the new line into the pending attribute to check - // if this time it's complete... - mod_attr_pending.push_str(line); - if !trimline.is_empty() - && check_if_attr_is_complete(&mod_attr_pending, edition) - { - // If it's complete, then we can clear the pending content. - mod_attr_pending.clear(); - } - // In any case, this is considered as `PartitionState::Attrs` so it's - // prepended before rustdoc's inserts. - PartitionState::Attrs - } else { - PartitionState::Other - } - }; - } - PartitionState::Crates => { - state = if trimline.starts_with("extern crate") - || trimline.starts_with("#[macro_use] extern crate") - || trimline.chars().all(|c| c.is_whitespace()) - || (trimline.starts_with("//") && !trimline.starts_with("///")) - { - PartitionState::Crates - } else { - PartitionState::Other - }; - } - PartitionState::Other => {} - } - - match state { - PartitionState::Attrs => { - before.push_str(line); - before.push('\n'); - } - PartitionState::Crates => { - crates.push_str(line); - crates.push('\n'); - } - PartitionState::Other => { - after.push_str(line); - after.push('\n'); - } - } - } - - debug!("before:\n{before}"); - debug!("crates:\n{crates}"); - debug!("after:\n{after}"); - - (before, after, crates) -} - -pub(crate) struct IndividualTestOptions { +struct IndividualTestOptions { outdir: DirState, test_id: String, path: PathBuf, @@ -989,8 +623,8 @@ pub(crate) trait DoctestVisitor { fn visit_header(&mut self, _name: &str, _level: u32) {} } -pub(crate) struct CreateRunnableDoctests { - pub(crate) tests: Vec, +struct CreateRunnableDoctests { + tests: Vec, rustdoc_options: Arc, opts: GlobalTestOptions, @@ -1000,10 +634,7 @@ pub(crate) struct CreateRunnableDoctests { } impl CreateRunnableDoctests { - pub(crate) fn new( - rustdoc_options: RustdocOptions, - opts: GlobalTestOptions, - ) -> CreateRunnableDoctests { + fn new(rustdoc_options: RustdocOptions, opts: GlobalTestOptions) -> CreateRunnableDoctests { CreateRunnableDoctests { tests: Vec::new(), rustdoc_options: Arc::new(rustdoc_options), diff --git a/src/librustdoc/doctest/make.rs b/src/librustdoc/doctest/make.rs new file mode 100644 index 000000000000..1eafad9cfc6e --- /dev/null +++ b/src/librustdoc/doctest/make.rs @@ -0,0 +1,381 @@ +//! Logic for transforming the raw code given by the user into something actually +//! runnable, e.g. by adding a `main` function if it doesn't already exist. + +use std::io; + +use rustc_ast as ast; +use rustc_data_structures::sync::Lrc; +use rustc_errors::emitter::stderr_destination; +use rustc_errors::ColorConfig; +use rustc_parse::new_parser_from_source_str; +use rustc_parse::parser::attr::InnerAttrPolicy; +use rustc_session::parse::ParseSess; +use rustc_span::edition::Edition; +use rustc_span::source_map::SourceMap; +use rustc_span::symbol::sym; +use rustc_span::FileName; + +use super::GlobalTestOptions; + +/// Transforms a test into code that can be compiled into a Rust binary, and returns the number of +/// lines before the test code begins as well as if the output stream supports colors or not. +pub(crate) fn make_test( + s: &str, + crate_name: Option<&str>, + dont_insert_main: bool, + opts: &GlobalTestOptions, + edition: Edition, + test_id: Option<&str>, +) -> (String, usize, bool) { + let (crate_attrs, everything_else, crates) = partition_source(s, edition); + let everything_else = everything_else.trim(); + let mut line_offset = 0; + let mut prog = String::new(); + let mut supports_color = false; + + if opts.attrs.is_empty() { + // If there aren't any attributes supplied by #![doc(test(attr(...)))], then allow some + // lints that are commonly triggered in doctests. The crate-level test attributes are + // commonly used to make tests fail in case they trigger warnings, so having this there in + // that case may cause some tests to pass when they shouldn't have. + prog.push_str("#![allow(unused)]\n"); + line_offset += 1; + } + + // Next, any attributes that came from the crate root via #![doc(test(attr(...)))]. + for attr in &opts.attrs { + prog.push_str(&format!("#![{attr}]\n")); + line_offset += 1; + } + + // Now push any outer attributes from the example, assuming they + // are intended to be crate attributes. + prog.push_str(&crate_attrs); + prog.push_str(&crates); + + // 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_span::create_session_if_not_set_then(edition, |_| { + use rustc_errors::emitter::{Emitter, HumanEmitter}; + use rustc_errors::DiagCtxt; + use rustc_parse::parser::ForceCollect; + use rustc_span::source_map::FilePathMapping; + + let filename = FileName::anon_source_code(s); + let source = crates + everything_else; + + // Any errors in parsing should also appear when the doctest is compiled for real, so just + // send all the errors that librustc_ast emits directly into a `Sink` instead of stderr. + let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); + let fallback_bundle = rustc_errors::fallback_fluent_bundle( + rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(), + false, + ); + supports_color = + HumanEmitter::new(stderr_destination(ColorConfig::Auto), fallback_bundle.clone()) + .supports_color(); + + let emitter = HumanEmitter::new(Box::new(io::sink()), fallback_bundle); + + // FIXME(misdreavus): pass `-Z treat-err-as-bug` to the doctest parser + let dcx = DiagCtxt::new(Box::new(emitter)).disable_warnings(); + let psess = ParseSess::with_dcx(dcx, sm); + + let mut found_main = false; + let mut found_extern_crate = crate_name.is_none(); + let mut found_macro = false; + + let mut parser = match new_parser_from_source_str(&psess, filename, source) { + Ok(p) => p, + Err(errs) => { + errs.into_iter().for_each(|err| err.cancel()); + return (found_main, found_extern_crate, found_macro); + } + }; + + loop { + match parser.parse_item(ForceCollect::No) { + Ok(Some(item)) => { + if !found_main + && let ast::ItemKind::Fn(..) = item.kind + && item.ident.name == sym::main + { + found_main = true; + } + + if !found_extern_crate + && let ast::ItemKind::ExternCrate(original) = item.kind + { + // This code will never be reached if `crate_name` is none because + // `found_extern_crate` is initialized to `true` if it is none. + let crate_name = crate_name.unwrap(); + + match original { + Some(name) => found_extern_crate = name.as_str() == crate_name, + None => found_extern_crate = item.ident.as_str() == crate_name, + } + } + + if !found_macro && let ast::ItemKind::MacCall(..) = item.kind { + found_macro = true; + } + + if found_main && found_extern_crate { + break; + } + } + Ok(None) => break, + Err(e) => { + e.cancel(); + break; + } + } + + // The supplied item is only used for diagnostics, + // which are swallowed here anyway. + parser.maybe_consume_incorrect_semicolon(None); + } + + // Reset errors so that they won't be reported as compiler bugs when dropping the + // dcx. Any errors in the tests will be reported when the test file is compiled, + // Note that we still need to cancel the errors above otherwise `Diag` will panic on + // drop. + psess.dcx.reset_err_count(); + + (found_main, found_extern_crate, found_macro) + }) + }); + let Ok((already_has_main, already_has_extern_crate, found_macro)) = result else { + // If the parser panicked due to a fatal error, pass the test code through unchanged. + // The error will be reported during compilation. + return (s.to_owned(), 0, false); + }; + + // If a doctest's `fn main` is being masked by a wrapper macro, the parsing loop above won't + // see it. In that case, run the old text-based scan to see if they at least have a main + // function written inside a macro invocation. See + // https://github.com/rust-lang/rust/issues/56898 + let already_has_main = if found_macro && !already_has_main { + s.lines() + .map(|line| { + let comment = line.find("//"); + if let Some(comment_begins) = comment { &line[0..comment_begins] } else { line } + }) + .any(|code| code.contains("fn main")) + } else { + already_has_main + }; + + // Don't inject `extern crate std` because it's already injected by the + // compiler. + if !already_has_extern_crate && !opts.no_crate_inject && crate_name != Some("std") { + if let Some(crate_name) = crate_name { + // Don't inject `extern crate` if the crate is never used. + // NOTE: this is terribly inaccurate because it doesn't actually + // parse the source, but only has false positives, not false + // negatives. + if s.contains(crate_name) { + // rustdoc implicitly inserts an `extern crate` item for the own crate + // which may be unused, so we need to allow the lint. + prog.push_str("#[allow(unused_extern_crates)]\n"); + + prog.push_str(&format!("extern crate r#{crate_name};\n")); + line_offset += 1; + } + } + } + + // FIXME: This code cannot yet handle no_std test cases yet + if dont_insert_main || already_has_main || prog.contains("![no_std]") { + prog.push_str(everything_else); + } else { + let returns_result = everything_else.trim_end().ends_with("(())"); + // Give each doctest main function a unique name. + // This is for example needed for the tooling around `-C instrument-coverage`. + let inner_fn_name = if let Some(test_id) = test_id { + format!("_doctest_main_{test_id}") + } else { + "_inner".into() + }; + let inner_attr = if test_id.is_some() { "#[allow(non_snake_case)] " } else { "" }; + let (main_pre, main_post) = if returns_result { + ( + format!( + "fn main() {{ {inner_attr}fn {inner_fn_name}() -> Result<(), impl core::fmt::Debug> {{\n", + ), + format!("\n}} {inner_fn_name}().unwrap() }}"), + ) + } else if test_id.is_some() { + ( + format!("fn main() {{ {inner_attr}fn {inner_fn_name}() {{\n",), + format!("\n}} {inner_fn_name}() }}"), + ) + } else { + ("fn main() {\n".into(), "\n}".into()) + }; + // Note on newlines: We insert a line/newline *before*, and *after* + // the doctest and adjust the `line_offset` accordingly. + // In the case of `-C instrument-coverage`, this means that the generated + // inner `main` function spans from the doctest opening codeblock to the + // closing one. For example + // /// ``` <- start of the inner main + // /// <- code under doctest + // /// ``` <- end of the inner main + line_offset += 1; + + // add extra 4 spaces for each line to offset the code block + let content = if opts.insert_indent_space { + everything_else + .lines() + .map(|line| format!(" {}", line)) + .collect::>() + .join("\n") + } else { + everything_else.to_string() + }; + prog.extend([&main_pre, content.as_str(), &main_post].iter().cloned()); + } + + debug!("final doctest:\n{prog}"); + + (prog, line_offset, supports_color) +} + +fn check_if_attr_is_complete(source: &str, edition: Edition) -> bool { + if source.is_empty() { + // Empty content so nothing to check in here... + return true; + } + rustc_driver::catch_fatal_errors(|| { + rustc_span::create_session_if_not_set_then(edition, |_| { + use rustc_errors::emitter::HumanEmitter; + use rustc_errors::DiagCtxt; + use rustc_span::source_map::FilePathMapping; + + let filename = FileName::anon_source_code(source); + // Any errors in parsing should also appear when the doctest is compiled for real, so just + // send all the errors that librustc_ast emits directly into a `Sink` instead of stderr. + let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); + let fallback_bundle = rustc_errors::fallback_fluent_bundle( + rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(), + false, + ); + + let emitter = HumanEmitter::new(Box::new(io::sink()), fallback_bundle); + + let dcx = DiagCtxt::new(Box::new(emitter)).disable_warnings(); + let psess = ParseSess::with_dcx(dcx, sm); + let mut parser = + match new_parser_from_source_str(&psess, filename, source.to_owned()) { + Ok(p) => p, + Err(errs) => { + errs.into_iter().for_each(|err| err.cancel()); + // If there is an unclosed delimiter, an error will be returned by the + // tokentrees. + return false; + } + }; + // If a parsing error happened, it's very likely that the attribute is incomplete. + if let Err(e) = parser.parse_attribute(InnerAttrPolicy::Permitted) { + e.cancel(); + return false; + } + true + }) + }) + .unwrap_or(false) +} + +fn partition_source(s: &str, edition: Edition) -> (String, String, String) { + #[derive(Copy, Clone, PartialEq)] + enum PartitionState { + Attrs, + Crates, + Other, + } + let mut state = PartitionState::Attrs; + let mut before = String::new(); + let mut crates = String::new(); + let mut after = String::new(); + + let mut mod_attr_pending = String::new(); + + for line in s.lines() { + let trimline = line.trim(); + + // FIXME(misdreavus): if a doc comment is placed on an extern crate statement, it will be + // shunted into "everything else" + match state { + PartitionState::Attrs => { + state = if trimline.starts_with("#![") { + if !check_if_attr_is_complete(line, edition) { + mod_attr_pending = line.to_owned(); + } else { + mod_attr_pending.clear(); + } + PartitionState::Attrs + } else if trimline.chars().all(|c| c.is_whitespace()) + || (trimline.starts_with("//") && !trimline.starts_with("///")) + { + PartitionState::Attrs + } else if trimline.starts_with("extern crate") + || trimline.starts_with("#[macro_use] extern crate") + { + PartitionState::Crates + } else { + // First we check if the previous attribute was "complete"... + if !mod_attr_pending.is_empty() { + // If not, then we append the new line into the pending attribute to check + // if this time it's complete... + mod_attr_pending.push_str(line); + if !trimline.is_empty() + && check_if_attr_is_complete(&mod_attr_pending, edition) + { + // If it's complete, then we can clear the pending content. + mod_attr_pending.clear(); + } + // In any case, this is considered as `PartitionState::Attrs` so it's + // prepended before rustdoc's inserts. + PartitionState::Attrs + } else { + PartitionState::Other + } + }; + } + PartitionState::Crates => { + state = if trimline.starts_with("extern crate") + || trimline.starts_with("#[macro_use] extern crate") + || trimline.chars().all(|c| c.is_whitespace()) + || (trimline.starts_with("//") && !trimline.starts_with("///")) + { + PartitionState::Crates + } else { + PartitionState::Other + }; + } + PartitionState::Other => {} + } + + match state { + PartitionState::Attrs => { + before.push_str(line); + before.push('\n'); + } + PartitionState::Crates => { + crates.push_str(line); + crates.push('\n'); + } + PartitionState::Other => { + after.push_str(line); + after.push('\n'); + } + } + } + + debug!("before:\n{before}"); + debug!("crates:\n{crates}"); + debug!("after:\n{after}"); + + (before, after, crates) +} From 815c447680a984c953d9c00a774aa151651f075f Mon Sep 17 00:00:00 2001 From: Noah Lev Date: Tue, 4 Jun 2024 00:16:44 -0700 Subject: [PATCH 82/92] Parse full doctest source; extract helper for parsing code It doesn't really make sense to skip part of the source when we're parsing it, so parse the whole doctest. This simplifies things too. --- src/librustdoc/doctest/make.rs | 230 +++++++++++++++++---------------- 1 file changed, 122 insertions(+), 108 deletions(-) diff --git a/src/librustdoc/doctest/make.rs b/src/librustdoc/doctest/make.rs index 1eafad9cfc6e..90efcbdd8b1b 100644 --- a/src/librustdoc/doctest/make.rs +++ b/src/librustdoc/doctest/make.rs @@ -6,7 +6,7 @@ use std::io; use rustc_ast as ast; use rustc_data_structures::sync::Lrc; use rustc_errors::emitter::stderr_destination; -use rustc_errors::ColorConfig; +use rustc_errors::{ColorConfig, FatalError}; use rustc_parse::new_parser_from_source_str; use rustc_parse::parser::attr::InnerAttrPolicy; use rustc_session::parse::ParseSess; @@ -55,118 +55,14 @@ pub(crate) fn make_test( // 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_span::create_session_if_not_set_then(edition, |_| { - use rustc_errors::emitter::{Emitter, HumanEmitter}; - use rustc_errors::DiagCtxt; - use rustc_parse::parser::ForceCollect; - use rustc_span::source_map::FilePathMapping; - - let filename = FileName::anon_source_code(s); - let source = crates + everything_else; - - // Any errors in parsing should also appear when the doctest is compiled for real, so just - // send all the errors that librustc_ast emits directly into a `Sink` instead of stderr. - let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); - let fallback_bundle = rustc_errors::fallback_fluent_bundle( - rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(), - false, - ); - supports_color = - HumanEmitter::new(stderr_destination(ColorConfig::Auto), fallback_bundle.clone()) - .supports_color(); - - let emitter = HumanEmitter::new(Box::new(io::sink()), fallback_bundle); - - // FIXME(misdreavus): pass `-Z treat-err-as-bug` to the doctest parser - let dcx = DiagCtxt::new(Box::new(emitter)).disable_warnings(); - let psess = ParseSess::with_dcx(dcx, sm); - - let mut found_main = false; - let mut found_extern_crate = crate_name.is_none(); - let mut found_macro = false; - - let mut parser = match new_parser_from_source_str(&psess, filename, source) { - Ok(p) => p, - Err(errs) => { - errs.into_iter().for_each(|err| err.cancel()); - return (found_main, found_extern_crate, found_macro); - } - }; - - loop { - match parser.parse_item(ForceCollect::No) { - Ok(Some(item)) => { - if !found_main - && let ast::ItemKind::Fn(..) = item.kind - && item.ident.name == sym::main - { - found_main = true; - } - - if !found_extern_crate - && let ast::ItemKind::ExternCrate(original) = item.kind - { - // This code will never be reached if `crate_name` is none because - // `found_extern_crate` is initialized to `true` if it is none. - let crate_name = crate_name.unwrap(); - - match original { - Some(name) => found_extern_crate = name.as_str() == crate_name, - None => found_extern_crate = item.ident.as_str() == crate_name, - } - } - - if !found_macro && let ast::ItemKind::MacCall(..) = item.kind { - found_macro = true; - } - - if found_main && found_extern_crate { - break; - } - } - Ok(None) => break, - Err(e) => { - e.cancel(); - break; - } - } - - // The supplied item is only used for diagnostics, - // which are swallowed here anyway. - parser.maybe_consume_incorrect_semicolon(None); - } - - // Reset errors so that they won't be reported as compiler bugs when dropping the - // dcx. Any errors in the tests will be reported when the test file is compiled, - // Note that we still need to cancel the errors above otherwise `Diag` will panic on - // drop. - psess.dcx.reset_err_count(); - - (found_main, found_extern_crate, found_macro) - }) - }); - let Ok((already_has_main, already_has_extern_crate, found_macro)) = result else { + let Ok((already_has_main, already_has_extern_crate)) = + check_for_main_and_extern_crate(crate_name, s.to_owned(), edition, &mut supports_color) + else { // If the parser panicked due to a fatal error, pass the test code through unchanged. // The error will be reported during compilation. return (s.to_owned(), 0, false); }; - // If a doctest's `fn main` is being masked by a wrapper macro, the parsing loop above won't - // see it. In that case, run the old text-based scan to see if they at least have a main - // function written inside a macro invocation. See - // https://github.com/rust-lang/rust/issues/56898 - let already_has_main = if found_macro && !already_has_main { - s.lines() - .map(|line| { - let comment = line.find("//"); - if let Some(comment_begins) = comment { &line[0..comment_begins] } else { line } - }) - .any(|code| code.contains("fn main")) - } else { - already_has_main - }; - // Don't inject `extern crate std` because it's already injected by the // compiler. if !already_has_extern_crate && !opts.no_crate_inject && crate_name != Some("std") { @@ -242,6 +138,124 @@ pub(crate) fn make_test( (prog, line_offset, supports_color) } +fn check_for_main_and_extern_crate( + crate_name: Option<&str>, + source: String, + edition: Edition, + supports_color: &mut bool, +) -> Result<(bool, bool), FatalError> { + let result = rustc_driver::catch_fatal_errors(|| { + rustc_span::create_session_if_not_set_then(edition, |_| { + use rustc_errors::emitter::{Emitter, HumanEmitter}; + use rustc_errors::DiagCtxt; + use rustc_parse::parser::ForceCollect; + use rustc_span::source_map::FilePathMapping; + + let filename = FileName::anon_source_code(&source); + + // Any errors in parsing should also appear when the doctest is compiled for real, so just + // send all the errors that librustc_ast emits directly into a `Sink` instead of stderr. + let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); + let fallback_bundle = rustc_errors::fallback_fluent_bundle( + rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(), + false, + ); + *supports_color = + HumanEmitter::new(stderr_destination(ColorConfig::Auto), fallback_bundle.clone()) + .supports_color(); + + let emitter = HumanEmitter::new(Box::new(io::sink()), fallback_bundle); + + // FIXME(misdreavus): pass `-Z treat-err-as-bug` to the doctest parser + let dcx = DiagCtxt::new(Box::new(emitter)).disable_warnings(); + let psess = ParseSess::with_dcx(dcx, sm); + + let mut found_main = false; + let mut found_extern_crate = crate_name.is_none(); + let mut found_macro = false; + + let mut parser = + match new_parser_from_source_str(&psess, filename, source.clone()) { + Ok(p) => p, + Err(errs) => { + errs.into_iter().for_each(|err| err.cancel()); + return (found_main, found_extern_crate, found_macro); + } + }; + + loop { + match parser.parse_item(ForceCollect::No) { + Ok(Some(item)) => { + if !found_main + && let ast::ItemKind::Fn(..) = item.kind + && item.ident.name == sym::main + { + found_main = true; + } + + if !found_extern_crate + && let ast::ItemKind::ExternCrate(original) = item.kind + { + // This code will never be reached if `crate_name` is none because + // `found_extern_crate` is initialized to `true` if it is none. + let crate_name = crate_name.unwrap(); + + match original { + Some(name) => found_extern_crate = name.as_str() == crate_name, + None => found_extern_crate = item.ident.as_str() == crate_name, + } + } + + if !found_macro && let ast::ItemKind::MacCall(..) = item.kind { + found_macro = true; + } + + if found_main && found_extern_crate { + break; + } + } + Ok(None) => break, + Err(e) => { + e.cancel(); + break; + } + } + + // The supplied item is only used for diagnostics, + // which are swallowed here anyway. + parser.maybe_consume_incorrect_semicolon(None); + } + + // Reset errors so that they won't be reported as compiler bugs when dropping the + // dcx. Any errors in the tests will be reported when the test file is compiled, + // Note that we still need to cancel the errors above otherwise `Diag` will panic on + // drop. + psess.dcx.reset_err_count(); + + (found_main, found_extern_crate, found_macro) + }) + }); + let (already_has_main, already_has_extern_crate, found_macro) = result?; + + // If a doctest's `fn main` is being masked by a wrapper macro, the parsing loop above won't + // see it. In that case, run the old text-based scan to see if they at least have a main + // function written inside a macro invocation. See + // https://github.com/rust-lang/rust/issues/56898 + let already_has_main = if found_macro && !already_has_main { + source + .lines() + .map(|line| { + let comment = line.find("//"); + if let Some(comment_begins) = comment { &line[0..comment_begins] } else { line } + }) + .any(|code| code.contains("fn main")) + } else { + already_has_main + }; + + Ok((already_has_main, already_has_extern_crate)) +} + fn check_if_attr_is_complete(source: &str, edition: Edition) -> bool { if source.is_empty() { // Empty content so nothing to check in here... From 3670ad59ade784226515327f2d776f02a6613f2c Mon Sep 17 00:00:00 2001 From: Noah Lev Date: Tue, 4 Jun 2024 01:05:50 -0700 Subject: [PATCH 83/92] Fix broken rustdoc unit tests --- src/librustdoc/doctest/tests.rs | 59 +++++++++++++++++++++------------ 1 file changed, 37 insertions(+), 22 deletions(-) diff --git a/src/librustdoc/doctest/tests.rs b/src/librustdoc/doctest/tests.rs index 9629acb31eb6..9124ec63267c 100644 --- a/src/librustdoc/doctest/tests.rs +++ b/src/librustdoc/doctest/tests.rs @@ -1,10 +1,23 @@ +use std::path::PathBuf; + use super::{make_test, GlobalTestOptions}; use rustc_span::edition::DEFAULT_EDITION; +/// Default [`GlobalTestOptions`] for these unit tests. +fn default_global_opts(crate_name: impl Into) -> GlobalTestOptions { + GlobalTestOptions { + crate_name: crate_name.into(), + no_crate_inject: false, + insert_indent_space: false, + attrs: vec![], + args_file: PathBuf::new(), + } +} + #[test] fn make_test_basic() { //basic use: wraps with `fn main`, adds `#![allow(unused)]` - let opts = GlobalTestOptions::default(); + let opts = default_global_opts(""); let input = "assert_eq!(2+2, 4);"; let expected = "#![allow(unused)] fn main() { @@ -19,7 +32,7 @@ assert_eq!(2+2, 4); fn make_test_crate_name_no_use() { // If you give a crate name but *don't* use it within the test, it won't bother inserting // the `extern crate` statement. - let opts = GlobalTestOptions::default(); + let opts = default_global_opts("asdf"); let input = "assert_eq!(2+2, 4);"; let expected = "#![allow(unused)] fn main() { @@ -34,7 +47,7 @@ assert_eq!(2+2, 4); fn make_test_crate_name() { // If you give a crate name and use it within the test, it will insert an `extern crate` // statement before `fn main`. - let opts = GlobalTestOptions::default(); + let opts = default_global_opts("asdf"); let input = "use asdf::qwop; assert_eq!(2+2, 4);"; let expected = "#![allow(unused)] @@ -53,8 +66,7 @@ assert_eq!(2+2, 4); fn make_test_no_crate_inject() { // Even if you do use the crate within the test, setting `opts.no_crate_inject` will skip // adding it anyway. - let opts = - GlobalTestOptions { no_crate_inject: true, attrs: vec![], insert_indent_space: false }; + let opts = GlobalTestOptions { no_crate_inject: true, ..default_global_opts("asdf") }; let input = "use asdf::qwop; assert_eq!(2+2, 4);"; let expected = "#![allow(unused)] @@ -72,7 +84,7 @@ fn make_test_ignore_std() { // Even if you include a crate name, and use it in the doctest, we still won't include an // `extern crate` statement if the crate is "std" -- that's included already by the // compiler! - let opts = GlobalTestOptions::default(); + let opts = default_global_opts("std"); let input = "use std::*; assert_eq!(2+2, 4);"; let expected = "#![allow(unused)] @@ -89,7 +101,7 @@ assert_eq!(2+2, 4); fn make_test_manual_extern_crate() { // When you manually include an `extern crate` statement in your doctest, `make_test` // assumes you've included one for your own crate too. - let opts = GlobalTestOptions::default(); + let opts = default_global_opts("asdf"); let input = "extern crate asdf; use asdf::qwop; assert_eq!(2+2, 4);"; @@ -106,7 +118,7 @@ assert_eq!(2+2, 4); #[test] fn make_test_manual_extern_crate_with_macro_use() { - let opts = GlobalTestOptions::default(); + let opts = default_global_opts("asdf"); let input = "#[macro_use] extern crate asdf; use asdf::qwop; assert_eq!(2+2, 4);"; @@ -125,7 +137,7 @@ assert_eq!(2+2, 4); fn make_test_opts_attrs() { // If you supplied some doctest attributes with `#![doc(test(attr(...)))]`, it will use // those instead of the stock `#![allow(unused)]`. - let mut opts = GlobalTestOptions::default(); + let mut opts = default_global_opts("asdf"); opts.attrs.push("feature(sick_rad)".to_string()); let input = "use asdf::qwop; assert_eq!(2+2, 4);"; @@ -159,7 +171,7 @@ assert_eq!(2+2, 4); fn make_test_crate_attrs() { // Including inner attributes in your doctest will apply them to the whole "crate", pasting // them outside the generated main function. - let opts = GlobalTestOptions::default(); + let opts = default_global_opts(""); let input = "#![feature(sick_rad)] assert_eq!(2+2, 4);"; let expected = "#![allow(unused)] @@ -175,7 +187,7 @@ assert_eq!(2+2, 4); #[test] fn make_test_with_main() { // Including your own `fn main` wrapper lets the test use it verbatim. - let opts = GlobalTestOptions::default(); + let opts = default_global_opts(""); let input = "fn main() { assert_eq!(2+2, 4); }"; @@ -191,7 +203,7 @@ fn main() { #[test] fn make_test_fake_main() { // ... but putting it in a comment will still provide a wrapper. - let opts = GlobalTestOptions::default(); + let opts = default_global_opts(""); let input = "//Ceci n'est pas une `fn main` assert_eq!(2+2, 4);"; let expected = "#![allow(unused)] @@ -207,7 +219,7 @@ assert_eq!(2+2, 4); #[test] fn make_test_dont_insert_main() { // Even with that, if you set `dont_insert_main`, it won't create the `fn main` wrapper. - let opts = GlobalTestOptions::default(); + let opts = default_global_opts(""); let input = "//Ceci n'est pas une `fn main` assert_eq!(2+2, 4);"; let expected = "#![allow(unused)] @@ -219,8 +231,8 @@ assert_eq!(2+2, 4);" } #[test] -fn make_test_issues_21299_33731() { - let opts = GlobalTestOptions::default(); +fn make_test_issues_21299() { + let opts = default_global_opts(""); let input = "// fn main assert_eq!(2+2, 4);"; @@ -234,6 +246,11 @@ assert_eq!(2+2, 4); let (output, len, _) = make_test(input, None, false, &opts, DEFAULT_EDITION, None); assert_eq!((output, len), (expected, 2)); +} + +#[test] +fn make_test_issues_33731() { + let opts = default_global_opts("asdf"); let input = "extern crate hella_qwop; assert_eq!(asdf::foo, 4);"; @@ -253,7 +270,7 @@ assert_eq!(asdf::foo, 4); #[test] fn make_test_main_in_macro() { - let opts = GlobalTestOptions::default(); + let opts = default_global_opts("my_crate"); let input = "#[macro_use] extern crate my_crate; test_wrapper! { fn main() {} @@ -272,7 +289,7 @@ test_wrapper! { #[test] fn make_test_returns_result() { // creates an inner function and unwraps it - let opts = GlobalTestOptions::default(); + let opts = default_global_opts(""); let input = "use std::io; let mut input = String::new(); io::stdin().read_line(&mut input)?; @@ -292,7 +309,7 @@ Ok::<(), io:Error>(()) #[test] fn make_test_named_wrapper() { // creates an inner function with a specific name - let opts = GlobalTestOptions::default(); + let opts = default_global_opts(""); let input = "assert_eq!(2+2, 4);"; let expected = "#![allow(unused)] fn main() { #[allow(non_snake_case)] fn _doctest_main__some_unique_name() { @@ -307,8 +324,7 @@ assert_eq!(2+2, 4); #[test] fn make_test_insert_extra_space() { // will insert indent spaces in the code block if `insert_indent_space` is true - let opts = - GlobalTestOptions { no_crate_inject: false, attrs: vec![], insert_indent_space: true }; + let opts = GlobalTestOptions { insert_indent_space: true, ..default_global_opts("") }; let input = "use std::*; assert_eq!(2+2, 4); eprintln!(\"hello anan\"); @@ -327,8 +343,7 @@ fn main() { #[test] fn make_test_insert_extra_space_fn_main() { // if input already has a fn main, it should insert a space before it - let opts = - GlobalTestOptions { no_crate_inject: false, attrs: vec![], insert_indent_space: true }; + let opts = GlobalTestOptions { insert_indent_space: true, ..default_global_opts("") }; let input = "use std::*; fn main() { assert_eq!(2+2, 4); From 6aab04e9b7d89af51eb96fcc35d7b3dab96f940a Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 7 Jun 2024 18:10:31 +0200 Subject: [PATCH 84/92] run fmt --- src/librustdoc/doctest/make.rs | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/src/librustdoc/doctest/make.rs b/src/librustdoc/doctest/make.rs index 90efcbdd8b1b..599611407ed8 100644 --- a/src/librustdoc/doctest/make.rs +++ b/src/librustdoc/doctest/make.rs @@ -174,14 +174,13 @@ fn check_for_main_and_extern_crate( let mut found_extern_crate = crate_name.is_none(); let mut found_macro = false; - let mut parser = - match new_parser_from_source_str(&psess, filename, source.clone()) { - Ok(p) => p, - Err(errs) => { - errs.into_iter().for_each(|err| err.cancel()); - return (found_main, found_extern_crate, found_macro); - } - }; + let mut parser = match new_parser_from_source_str(&psess, filename, source.clone()) { + Ok(p) => p, + Err(errs) => { + errs.into_iter().for_each(|err| err.cancel()); + return (found_main, found_extern_crate, found_macro); + } + }; loop { match parser.parse_item(ForceCollect::No) { @@ -280,16 +279,15 @@ fn check_if_attr_is_complete(source: &str, edition: Edition) -> bool { let dcx = DiagCtxt::new(Box::new(emitter)).disable_warnings(); let psess = ParseSess::with_dcx(dcx, sm); - let mut parser = - match new_parser_from_source_str(&psess, filename, source.to_owned()) { - Ok(p) => p, - Err(errs) => { - errs.into_iter().for_each(|err| err.cancel()); - // If there is an unclosed delimiter, an error will be returned by the - // tokentrees. - return false; - } - }; + let mut parser = match new_parser_from_source_str(&psess, filename, source.to_owned()) { + Ok(p) => p, + Err(errs) => { + errs.into_iter().for_each(|err| err.cancel()); + // If there is an unclosed delimiter, an error will be returned by the + // tokentrees. + return false; + } + }; // If a parsing error happened, it's very likely that the attribute is incomplete. if let Err(e) = parser.parse_attribute(InnerAttrPolicy::Permitted) { e.cancel(); From 9aa96b462348905977ee683114f3dbe235fc7c17 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Fri, 7 Jun 2024 19:41:37 +0200 Subject: [PATCH 85/92] config.example.toml: minor improves --- config.example.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config.example.toml b/config.example.toml index 61655dad6385..fb45493ec4a1 100644 --- a/config.example.toml +++ b/config.example.toml @@ -389,8 +389,8 @@ # a Nix toolchain on non-NixOS distributions. #patch-binaries-for-nix = false -# Collect information and statistics about the current build and writes it to -# disk. Enabling this or not has no impact on the resulting build output. The +# Collect information and statistics about the current build, and write it to +# disk. Enabling this has no impact on the resulting build output. The # schema of the file generated by the build metrics feature is unstable, and # this is not intended to be used during local development. #metrics = false From f82f0530f36914545820f6307f73ab024fb82441 Mon Sep 17 00:00:00 2001 From: Walter Kalata Date: Fri, 7 Jun 2024 12:18:43 -0700 Subject: [PATCH 86/92] Fix typo in docs for std::pin --- library/core/src/pin.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index d8fc3b7177f3..0d2aa3070a19 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -184,7 +184,7 @@ //! requires at least a level of pointer indirection each time a new object is added to the mix //! (and, practically, a heap allocation). //! -//! Although there were other reason as well, this issue of expensive composition is the key thing +//! Although there were other reasons as well, this issue of expensive composition is the key thing //! that drove Rust towards adopting a different model. It is particularly a problem //! when one considers, for example, the implications of composing together the [`Future`]s which //! will eventually make up an asynchronous task (including address-sensitive `async fn` state From 63ba3f3aaf0a8ed67119ba55f48cd5bfa63408d4 Mon Sep 17 00:00:00 2001 From: Tyler Mandry Date: Thu, 6 Jun 2024 14:56:13 -0700 Subject: [PATCH 87/92] Add debugging utils and comments to Fuchsia scripts This should help when debugging a failure in the Fuchsia build in CI. --- .../x86_64-gnu-integration/Dockerfile | 3 + .../x86_64-gnu-integration/build-fuchsia.sh | 97 +++++++++++++------ 2 files changed, 70 insertions(+), 30 deletions(-) diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-integration/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-integration/Dockerfile index c8c754914aa6..d228dfc87ebe 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-integration/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu-integration/Dockerfile @@ -1,3 +1,6 @@ +# This job builds a toolchain capable of building Fuchsia, and then builds +# Fuchsia. See the build-fuchsia.sh script in this directory for more details. + FROM ubuntu:22.04 ARG DEBIAN_FRONTEND=noninteractive diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-integration/build-fuchsia.sh b/src/ci/docker/host-x86_64/x86_64-gnu-integration/build-fuchsia.sh index 9cc508fe9288..913a0b0c09c3 100755 --- a/src/ci/docker/host-x86_64/x86_64-gnu-integration/build-fuchsia.sh +++ b/src/ci/docker/host-x86_64/x86_64-gnu-integration/build-fuchsia.sh @@ -2,50 +2,87 @@ # Downloads and builds the Fuchsia operating system using a toolchain installed # in $RUST_INSTALL_DIR. +# +# You may run this script locally using Docker with the following command: +# +# $ src/ci/docker/run.sh x86_64-gnu-integration +# +# Alternatively, from within the container with --dev, assuming you have made it +# as far as building the toolchain with the above command: +# +# $ src/ci/docker/run.sh --dev x86_64-gnu-integration +# docker# git config --global --add safe.directory /checkout/obj/fuchsia +# docker# ../src/ci/docker/host-x86_64/x86_64-gnu-integration/build-fuchsia.sh +# +# Also see the docs in the rustc-dev-guide for more info: +# https://github.com/rust-lang/rustc-dev-guide/pull/1989 set -euf -o pipefail -INTEGRATION_SHA=1011e3298775ee7cdf6f6dc73e808d6a86e33bd6 +# Set this variable to 1 to disable updating the Fuchsia checkout. This is +# useful for making local changes. You can find the Fuchsia checkout in +# `obj/x86_64-gnu-integration/fuchsia` in your local checkout after running this +# job for the first time. +KEEP_CHECKOUT= + +# Any upstream refs that should be cherry-picked. This can be used to include +# Gerrit changes from https://fxrev.dev during development (click the "Download" +# button on a changelist to see the cherry pick ref). Example: +# PICK_REFS=(refs/changes/71/1054071/2 refs/changes/74/1054574/2) PICK_REFS=() +# The commit hash of Fuchsia's integration.git to check out. This controls the +# commit hash of fuchsia.git and some other repos in the "monorepo" checkout, in +# addition to versions of prebuilts. It should be bumped regularly by the +# Fuchsia team – we aim for every 1-2 months. +INTEGRATION_SHA=1011e3298775ee7cdf6f6dc73e808d6a86e33bd6 + checkout=fuchsia jiri=.jiri_root/bin/jiri set -x -# This script will: -# - create a directory named "fuchsia" if it does not exist -# - download "jiri" to "fuchsia/.jiri_root/bin" -curl -s "https://fuchsia.googlesource.com/jiri/+/HEAD/scripts/bootstrap_jiri?format=TEXT" \ - | base64 --decode \ - | bash -s $checkout +if [ -z "$KEEP_CHECKOUT" ]; then + # This script will: + # - create a directory named "fuchsia" if it does not exist + # - download "jiri" to "fuchsia/.jiri_root/bin" + curl -s "https://fuchsia.googlesource.com/jiri/+/HEAD/scripts/bootstrap_jiri?format=TEXT" \ + | base64 --decode \ + | bash -s $checkout -cd $checkout + cd $checkout -$jiri init \ - -partial=true \ - -analytics-opt=false \ - . + $jiri init \ + -partial=true \ + -analytics-opt=false \ + . -$jiri import \ - -name=integration \ - -revision=$INTEGRATION_SHA \ - -overwrite=true \ - flower \ - "https://fuchsia.googlesource.com/integration" + $jiri import \ + -name=integration \ + -revision=$INTEGRATION_SHA \ + -overwrite=true \ + flower \ + "https://fuchsia.googlesource.com/integration" -if [ -d ".git" ]; then - # Wipe out any local changes if we're reusing a checkout. - git checkout --force JIRI_HEAD + if [ -d ".git" ]; then + # Wipe out any local changes if we're reusing a checkout. + git checkout --force JIRI_HEAD + fi + + $jiri update -autoupdate=false + + echo integration commit = $(git -C integration rev-parse HEAD) + + for git_ref in "${PICK_REFS[@]}"; do + git fetch https://fuchsia.googlesource.com/fuchsia $git_ref + git cherry-pick --no-commit FETCH_HEAD + done +else + echo Reusing existing Fuchsia checkout + cd $checkout fi -$jiri update -autoupdate=false - -echo integration commit = $(git -C integration rev-parse HEAD) - -for git_ref in "${PICK_REFS[@]}"; do - git fetch https://fuchsia.googlesource.com/fuchsia $git_ref - git cherry-pick --no-commit FETCH_HEAD -done - +# Run the script inside the Fuchsia checkout responsible for building Fuchsia. +# You can change arguments to the build by setting KEEP_CHECKOUT=1 above and +# modifying them in build_fuchsia_from_rust_ci.sh. bash scripts/rust/build_fuchsia_from_rust_ci.sh From ea748abb5b2d721af0299bc5302c4b220688c4b8 Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Fri, 7 Jun 2024 22:23:01 -0400 Subject: [PATCH 88/92] Update cargo --- src/tools/cargo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/cargo b/src/tools/cargo index 34a6a87d8a23..b1feb75d0624 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit 34a6a87d8a2330d8c9d578f927489689328a652d +Subproject commit b1feb75d062444e2cee8b3d2aaa95309d65e9ccd From d5671d06a493eae3dd4973ef98c7f1a81998e9da Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 8 Jun 2024 10:35:20 +0200 Subject: [PATCH 89/92] Miri std tests: don't set BOOTSTRAP_SKIP_TARGET_SANITY unnecessarily --- src/bootstrap/mk/Makefile.in | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/bootstrap/mk/Makefile.in b/src/bootstrap/mk/Makefile.in index fc433bc5843b..cab37e0da473 100644 --- a/src/bootstrap/mk/Makefile.in +++ b/src/bootstrap/mk/Makefile.in @@ -59,17 +59,18 @@ check-aux: library/alloc \ --no-doc # Some doctests have intentional memory leaks. + # Some use file system operations to demonstrate dealing with `Result`. $(Q)MIRIFLAGS="-Zmiri-ignore-leaks -Zmiri-disable-isolation" \ $(BOOTSTRAP) miri --stage 2 \ library/core \ library/alloc \ --doc - # In `std` we cannot test everything. - $(Q)MIRIFLAGS="-Zmiri-disable-isolation" BOOTSTRAP_SKIP_TARGET_SANITY=1 \ + # In `std` we cannot test everything, so we skip some modules. + $(Q)MIRIFLAGS="-Zmiri-disable-isolation" \ $(BOOTSTRAP) miri --stage 2 library/std \ --no-doc -- \ --skip fs:: --skip net:: --skip process:: --skip sys::pal:: - $(Q)MIRIFLAGS="-Zmiri-ignore-leaks -Zmiri-disable-isolation" BOOTSTRAP_SKIP_TARGET_SANITY=1 \ + $(Q)MIRIFLAGS="-Zmiri-ignore-leaks -Zmiri-disable-isolation" \ $(BOOTSTRAP) miri --stage 2 library/std \ --doc -- \ --skip fs:: --skip net:: --skip process:: --skip sys::pal:: From 5844b679f3289aa50fda5463667de1d63b7d41d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Fri, 7 Jun 2024 14:10:27 +0200 Subject: [PATCH 90/92] Remove unnecessary functions and the last mention of TMPDIR from `run-make-support` --- src/tools/run-make-support/src/lib.rs | 21 +------------------ .../arguments-non-c-like-enum/rmake.rs | 4 ++-- .../c-link-to-rust-staticlib/rmake.rs | 6 +++--- .../c-link-to-rust-va-list-fn/rmake.rs | 4 ++-- tests/run-make/cdylib/rmake.rs | 4 ++-- .../rmake.rs | 4 ++-- .../rustdoc-scrape-examples-macros/rmake.rs | 2 +- tests/run-make/suspicious-library/rmake.rs | 6 +++--- tests/run-make/wasm-abi/rmake.rs | 2 +- .../wasm-symbols-different-module/rmake.rs | 1 + 10 files changed, 18 insertions(+), 36 deletions(-) diff --git a/src/tools/run-make-support/src/lib.rs b/src/tools/run-make-support/src/lib.rs index 79f732ecebb5..e40f13f497b0 100644 --- a/src/tools/run-make-support/src/lib.rs +++ b/src/tools/run-make-support/src/lib.rs @@ -65,12 +65,6 @@ pub fn is_darwin() -> bool { target().contains("darwin") } -/// Construct a path to a static library under `$TMPDIR` given the library name. This will return a -/// path with `$TMPDIR` joined with platform-and-compiler-specific library name. -pub fn static_lib(name: &str) -> PathBuf { - PathBuf::from(static_lib_name(name)) -} - pub fn python_command() -> Command { let python_path = env_var("PYTHON"); Command::new(python_path) @@ -111,12 +105,6 @@ pub fn static_lib_name(name: &str) -> String { if is_msvc() { format!("{name}.lib") } else { format!("lib{name}.a") } } -/// Construct a path to a dynamic library under `$TMPDIR` given the library name. This will return a -/// path with `$TMPDIR` joined with platform-and-compiler-specific library name. -pub fn dynamic_lib(name: &str) -> PathBuf { - PathBuf::from(dynamic_lib_name(name)) -} - /// Construct the dynamic library name based on the platform. pub fn dynamic_lib_name(name: &str) -> String { // See tools.mk (irrelevant lines omitted): @@ -154,14 +142,7 @@ pub fn dynamic_lib_extension() -> &'static str { } } -/// Construct a path to a rust library (rlib) under `$TMPDIR` given the library name. This will return a -/// path with `$TMPDIR` joined with the library name. -pub fn rust_lib(name: &str) -> PathBuf { - PathBuf::from(rust_lib_name(name)) -} - -/// Generate the name a rust library (rlib) would have. If you want the complete path, use -/// [`rust_lib`] instead. +/// Construct a rust library (rlib) name. pub fn rust_lib_name(name: &str) -> String { format!("lib{name}.rlib") } diff --git a/tests/run-make/arguments-non-c-like-enum/rmake.rs b/tests/run-make/arguments-non-c-like-enum/rmake.rs index 13230206ca87..88f4d664aa62 100644 --- a/tests/run-make/arguments-non-c-like-enum/rmake.rs +++ b/tests/run-make/arguments-non-c-like-enum/rmake.rs @@ -1,14 +1,14 @@ //! Check that non-trivial `repr(C)` enum in Rust has valid C layout. //@ ignore-cross-compile -use run_make_support::{cc, extra_c_flags, extra_cxx_flags, run, rustc, static_lib}; +use run_make_support::{cc, extra_c_flags, extra_cxx_flags, run, rustc, static_lib_name}; pub fn main() { use std::path::Path; rustc().input("nonclike.rs").crate_type("staticlib").run(); cc().input("test.c") - .input(static_lib("nonclike")) + .input(static_lib_name("nonclike")) .out_exe("test") .args(&extra_c_flags()) .args(&extra_cxx_flags()) diff --git a/tests/run-make/c-link-to-rust-staticlib/rmake.rs b/tests/run-make/c-link-to-rust-staticlib/rmake.rs index 63d5eb78c698..ca28944a0264 100644 --- a/tests/run-make/c-link-to-rust-staticlib/rmake.rs +++ b/tests/run-make/c-link-to-rust-staticlib/rmake.rs @@ -3,13 +3,13 @@ //@ ignore-cross-compile -use run_make_support::{cc, extra_c_flags, run, rustc, static_lib}; +use run_make_support::{cc, extra_c_flags, run, rustc, static_lib_name}; use std::fs; fn main() { rustc().input("foo.rs").run(); - cc().input("bar.c").input(static_lib("foo")).out_exe("bar").args(&extra_c_flags()).run(); + cc().input("bar.c").input(static_lib_name("foo")).out_exe("bar").args(&extra_c_flags()).run(); run("bar"); - fs::remove_file(static_lib("foo")); + fs::remove_file(static_lib_name("foo")); run("bar"); } diff --git a/tests/run-make/c-link-to-rust-va-list-fn/rmake.rs b/tests/run-make/c-link-to-rust-va-list-fn/rmake.rs index 7a450efff942..a01e259bce01 100644 --- a/tests/run-make/c-link-to-rust-va-list-fn/rmake.rs +++ b/tests/run-make/c-link-to-rust-va-list-fn/rmake.rs @@ -5,12 +5,12 @@ //@ ignore-cross-compile -use run_make_support::{cc, extra_c_flags, run, rustc, static_lib}; +use run_make_support::{cc, extra_c_flags, run, rustc, static_lib_name}; fn main() { rustc().input("checkrust.rs").run(); cc().input("test.c") - .input(static_lib("checkrust")) + .input(static_lib_name("checkrust")) .out_exe("test") .args(&extra_c_flags()) .run(); diff --git a/tests/run-make/cdylib/rmake.rs b/tests/run-make/cdylib/rmake.rs index 1920f618473b..81166867b790 100644 --- a/tests/run-make/cdylib/rmake.rs +++ b/tests/run-make/cdylib/rmake.rs @@ -12,7 +12,7 @@ use std::fs::remove_file; -use run_make_support::{cc, cwd, dynamic_lib, is_msvc, run, rustc}; +use run_make_support::{cc, cwd, dynamic_lib_name, is_msvc, run, rustc}; fn main() { rustc().input("bar.rs").run(); @@ -25,7 +25,7 @@ fn main() { } run("foo"); - remove_file(dynamic_lib("foo")).unwrap(); + remove_file(dynamic_lib_name("foo")).unwrap(); rustc().input("foo.rs").arg("-Clto").run(); run("foo"); diff --git a/tests/run-make/reachable-extern-fn-available-lto/rmake.rs b/tests/run-make/reachable-extern-fn-available-lto/rmake.rs index c7262b9461b3..558444846fd1 100644 --- a/tests/run-make/reachable-extern-fn-available-lto/rmake.rs +++ b/tests/run-make/reachable-extern-fn-available-lto/rmake.rs @@ -9,10 +9,10 @@ //@ ignore-cross-compile -use run_make_support::{cc, extra_c_flags, run, rustc, static_lib}; +use run_make_support::{cc, extra_c_flags, run, rustc, static_lib_name}; fn main() { - let libbar_path = static_lib("bar"); + let libbar_path = static_lib_name("bar"); rustc().input("foo.rs").crate_type("rlib").run(); rustc() .input("bar.rs") diff --git a/tests/run-make/rustdoc-scrape-examples-macros/rmake.rs b/tests/run-make/rustdoc-scrape-examples-macros/rmake.rs index c99112b6e5f2..a6d08f28cda3 100644 --- a/tests/run-make/rustdoc-scrape-examples-macros/rmake.rs +++ b/tests/run-make/rustdoc-scrape-examples-macros/rmake.rs @@ -1,6 +1,6 @@ //@ ignore-cross-compile -use run_make_support::{htmldocck, rustc, rustdoc, rust_lib_name}; +use run_make_support::{htmldocck, rust_lib_name, rustc, rustdoc}; fn main() { let out_dir = "rustdoc"; diff --git a/tests/run-make/suspicious-library/rmake.rs b/tests/run-make/suspicious-library/rmake.rs index 9e91de70bfce..e789607482b8 100644 --- a/tests/run-make/suspicious-library/rmake.rs +++ b/tests/run-make/suspicious-library/rmake.rs @@ -3,12 +3,12 @@ //@ ignore-cross-compile -use run_make_support::{dynamic_lib, rustc}; +use run_make_support::{dynamic_lib_name, rustc}; use std::fs::File; fn main() { rustc().input("foo.rs").arg("-Cprefer-dynamic").run(); - File::create(dynamic_lib("foo-something-special")).unwrap(); - File::create(dynamic_lib("foo-something-special2")).unwrap(); + File::create(dynamic_lib_name("foo-something-special")).unwrap(); + File::create(dynamic_lib_name("foo-something-special2")).unwrap(); rustc().input("bar.rs").run(); } diff --git a/tests/run-make/wasm-abi/rmake.rs b/tests/run-make/wasm-abi/rmake.rs index 8e90d83af6cd..0fc326babd94 100644 --- a/tests/run-make/wasm-abi/rmake.rs +++ b/tests/run-make/wasm-abi/rmake.rs @@ -8,7 +8,7 @@ use std::process::Command; fn main() { rustc().input("foo.rs").target("wasm32-wasip1").run(); - let file = PathBuf::from("foo.wasm"); + let file = Path::new("foo.wasm"); run(&file, "return_two_i32", "1\n2\n"); run(&file, "return_two_i64", "3\n4\n"); diff --git a/tests/run-make/wasm-symbols-different-module/rmake.rs b/tests/run-make/wasm-symbols-different-module/rmake.rs index 4960827f151c..83970ef0b249 100644 --- a/tests/run-make/wasm-symbols-different-module/rmake.rs +++ b/tests/run-make/wasm-symbols-different-module/rmake.rs @@ -2,6 +2,7 @@ use run_make_support::{rustc, wasmparser}; use std::collections::{HashMap, HashSet}; +use std::path::Path; fn main() { test_file("foo.rs", &[("a", &["foo"]), ("b", &["foo"])]); From e26e42fabee2c3dc2060fbfe03856f8ab16eb762 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 8 Jun 2024 14:48:32 +0200 Subject: [PATCH 91/92] add missing Scalar::from_i128 --- compiler/rustc_middle/src/mir/interpret/value.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/compiler/rustc_middle/src/mir/interpret/value.rs b/compiler/rustc_middle/src/mir/interpret/value.rs index c5c87c506b77..853572656879 100644 --- a/compiler/rustc_middle/src/mir/interpret/value.rs +++ b/compiler/rustc_middle/src/mir/interpret/value.rs @@ -196,6 +196,11 @@ impl Scalar { Self::from_int(i, Size::from_bits(64)) } + #[inline] + pub fn from_i128(i: i128) -> Self { + Self::from_int(i, Size::from_bits(128)) + } + #[inline] pub fn from_target_isize(i: i64, cx: &impl HasDataLayout) -> Self { Self::from_int(i, cx.data_layout().pointer_size) From b10a404199f0c837445e2c8692a6e4f31b8d6789 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Sat, 8 Jun 2024 15:40:07 +0200 Subject: [PATCH 92/92] Fix windows test --- tests/run-make/windows-binary-no-external-deps/rmake.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/run-make/windows-binary-no-external-deps/rmake.rs b/tests/run-make/windows-binary-no-external-deps/rmake.rs index 73c1dfcdfa81..61fdb202221c 100644 --- a/tests/run-make/windows-binary-no-external-deps/rmake.rs +++ b/tests/run-make/windows-binary-no-external-deps/rmake.rs @@ -2,7 +2,7 @@ //! a "hello world" application by setting `PATH` to `C:\Windows\System32`. //@ only-windows -use run_make_support::{env_var, rustc}; +use run_make_support::{cwd, env_var, rustc}; use std::path::PathBuf; use std::process::Command; @@ -12,7 +12,7 @@ fn main() { let windows_dir = env_var("SystemRoot"); let system32: PathBuf = [&windows_dir, "System32"].iter().collect(); // Note: This does not use the support wrappers so that we can precisely control the PATH - let exe = "hello.exe"; + let exe = cwd().join("hello.exe"); let status = Command::new(exe).env("PATH", &system32).spawn().unwrap().wait().unwrap(); if !status.success() { panic!("Command failed!\noutput status: `{status}`");

(&mut self, predicate: P) -> Option where P: FnOnce(&mut T) -> bool, From a5429d53afa0db4bc22950f4ac62ace8019a7481 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Thu, 6 Jun 2024 21:41:47 +0200 Subject: [PATCH 19/92] Remove `same-lib-two-locations-no-panic` run-make test This test doesn't really make any sense anymore, it became broken a long time ago. --- .../same-lib-two-locations-no-panic/bar.rs | 3 -- .../same-lib-two-locations-no-panic/foo.rs | 1 - .../same-lib-two-locations-no-panic/rmake.rs | 28 ------------------- 3 files changed, 32 deletions(-) delete mode 100644 tests/run-make/same-lib-two-locations-no-panic/bar.rs delete mode 100644 tests/run-make/same-lib-two-locations-no-panic/foo.rs delete mode 100644 tests/run-make/same-lib-two-locations-no-panic/rmake.rs diff --git a/tests/run-make/same-lib-two-locations-no-panic/bar.rs b/tests/run-make/same-lib-two-locations-no-panic/bar.rs deleted file mode 100644 index bb7b36c496ef..000000000000 --- a/tests/run-make/same-lib-two-locations-no-panic/bar.rs +++ /dev/null @@ -1,3 +0,0 @@ -extern crate foo; - -fn main() {} diff --git a/tests/run-make/same-lib-two-locations-no-panic/foo.rs b/tests/run-make/same-lib-two-locations-no-panic/foo.rs deleted file mode 100644 index 82b2dfe9fdb5..000000000000 --- a/tests/run-make/same-lib-two-locations-no-panic/foo.rs +++ /dev/null @@ -1 +0,0 @@ -#![crate_name = "foo"] diff --git a/tests/run-make/same-lib-two-locations-no-panic/rmake.rs b/tests/run-make/same-lib-two-locations-no-panic/rmake.rs deleted file mode 100644 index 2900c3c8b749..000000000000 --- a/tests/run-make/same-lib-two-locations-no-panic/rmake.rs +++ /dev/null @@ -1,28 +0,0 @@ -// A path which contains the same rlib or dylib in two locations -// should not cause an assertion panic in the compiler. -// This test tries to replicate the linked issue and checks -// if the bugged error makes a resurgence. - -// See https://github.com/rust-lang/rust/issues/11908 - -//@ ignore-cross-compile - -use run_make_support::{dynamic_lib, rust_lib, rustc, tmp_dir}; -use std::fs; - -fn main() { - let tmp_dir_other = tmp_dir().join("other"); - - fs::create_dir(&tmp_dir_other); - rustc().input("foo.rs").crate_type("dylib").arg("-Cprefer-dynamic").run(); - fs::rename(dynamic_lib("foo"), &tmp_dir_other); - rustc().input("foo.rs").crate_type("dylib").arg("-Cprefer-dynamic").run(); - rustc().input("bar.rs").library_search_path(&tmp_dir_other).run(); - fs::remove_dir_all(tmp_dir()); - - fs::create_dir_all(&tmp_dir_other); - rustc().input("foo.rs").crate_type("rlib").run(); - fs::rename(rust_lib("foo"), &tmp_dir_other); - rustc().input("foo.rs").crate_type("rlib").run(); - rustc().input("bar.rs").library_search_path(tmp_dir_other).run(); -} From 3a02cdb54fc4e7439d2fec5562052ddee746c7fa Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Thu, 6 Jun 2024 21:50:24 +0200 Subject: [PATCH 20/92] Remove constant parameter from `CrateLocator::new` --- compiler/rustc_metadata/src/creader.rs | 1 - compiler/rustc_metadata/src/locator.rs | 16 +++------------- 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs index 44a3e9760e1e..ad283117d7e0 100644 --- a/compiler/rustc_metadata/src/creader.rs +++ b/compiler/rustc_metadata/src/creader.rs @@ -581,7 +581,6 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> { self.tcx.crate_types().iter().all(|c| *c == CrateType::Rlib), hash, extra_filename, - false, // is_host path_kind, ); diff --git a/compiler/rustc_metadata/src/locator.rs b/compiler/rustc_metadata/src/locator.rs index 73443de35538..861c4d42189d 100644 --- a/compiler/rustc_metadata/src/locator.rs +++ b/compiler/rustc_metadata/src/locator.rs @@ -222,7 +222,6 @@ use rustc_data_structures::owned_slice::slice_owned; use rustc_data_structures::svh::Svh; use rustc_errors::{DiagArgValue, IntoDiagArg}; use rustc_fs_util::try_canonicalize; -use rustc_session::config; use rustc_session::cstore::CrateSource; use rustc_session::filesearch::FileSearch; use rustc_session::search_paths::PathKind; @@ -309,7 +308,6 @@ impl<'a> CrateLocator<'a> { is_rlib: bool, hash: Option, extra_filename: Option<&'a str>, - is_host: bool, path_kind: PathKind, ) -> CrateLocator<'a> { let needs_object_code = sess.opts.output_types.should_codegen(); @@ -340,17 +338,9 @@ impl<'a> CrateLocator<'a> { }, hash, extra_filename, - target: if is_host { &sess.host } else { &sess.target }, - triple: if is_host { - TargetTriple::from_triple(config::host_triple()) - } else { - sess.opts.target_triple.clone() - }, - filesearch: if is_host { - sess.host_filesearch(path_kind) - } else { - sess.target_filesearch(path_kind) - }, + target: &sess.target, + triple: sess.opts.target_triple.clone(), + filesearch: sess.target_filesearch(path_kind), is_proc_macro: false, crate_rejections: CrateRejections::default(), } From 4c2b21a2eff18212ae8363fd993d8a56d7ebfe83 Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Thu, 6 Jun 2024 21:50:46 +0200 Subject: [PATCH 21/92] Simplify string operations in crate loader --- compiler/rustc_metadata/src/locator.rs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_metadata/src/locator.rs b/compiler/rustc_metadata/src/locator.rs index 861c4d42189d..90fe52a34385 100644 --- a/compiler/rustc_metadata/src/locator.rs +++ b/compiler/rustc_metadata/src/locator.rs @@ -414,12 +414,18 @@ impl<'a> CrateLocator<'a> { debug!("testing {}", spf.path.display()); let f = &spf.file_name_str; - let (hash, kind) = if f.starts_with(rlib_prefix) && f.ends_with(rlib_suffix) { - (&f[rlib_prefix.len()..(f.len() - rlib_suffix.len())], CrateFlavor::Rlib) - } else if f.starts_with(rmeta_prefix) && f.ends_with(rmeta_suffix) { - (&f[rmeta_prefix.len()..(f.len() - rmeta_suffix.len())], CrateFlavor::Rmeta) - } else if f.starts_with(dylib_prefix) && f.ends_with(dylib_suffix.as_ref()) { - (&f[dylib_prefix.len()..(f.len() - dylib_suffix.len())], CrateFlavor::Dylib) + let (hash, kind) = if let Some(f) = f.strip_prefix(rlib_prefix) + && let Some(f) = f.strip_suffix(rlib_suffix) + { + (f, CrateFlavor::Rlib) + } else if let Some(f) = f.strip_prefix(rmeta_prefix) + && let Some(f) = f.strip_suffix(rmeta_suffix) + { + (f, CrateFlavor::Rmeta) + } else if let Some(f) = f.strip_prefix(dylib_prefix) + && let Some(f) = f.strip_suffix(dylib_suffix.as_ref()) + { + (f, CrateFlavor::Dylib) } else { if f.starts_with(staticlib_prefix) && f.ends_with(staticlib_suffix.as_ref()) { self.crate_rejections.via_kind.push(CrateMismatch { From b4c439c3de913953a2968c6917e125fafaacf886 Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Thu, 6 Jun 2024 21:52:22 +0200 Subject: [PATCH 22/92] Improve naming and path operations in crate loader Simplify the path operation with `join`, clarify some of the names. --- compiler/rustc_session/src/filesearch.rs | 18 +++++++++--------- compiler/rustc_target/src/lib.rs | 12 ++++-------- compiler/rustc_target/src/spec/mod.rs | 2 +- 3 files changed, 14 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_session/src/filesearch.rs b/compiler/rustc_session/src/filesearch.rs index 9cb8cd836e6b..6f63776bedcc 100644 --- a/compiler/rustc_session/src/filesearch.rs +++ b/compiler/rustc_session/src/filesearch.rs @@ -12,7 +12,7 @@ use tracing::debug; pub struct FileSearch<'a> { sysroot: &'a Path, triple: &'a str, - search_paths: &'a [SearchPath], + cli_search_paths: &'a [SearchPath], tlib_path: &'a SearchPath, kind: PathKind, } @@ -20,7 +20,7 @@ pub struct FileSearch<'a> { impl<'a> FileSearch<'a> { pub fn search_paths(&self) -> impl Iterator { let kind = self.kind; - self.search_paths + self.cli_search_paths .iter() .filter(move |sp| sp.kind.matches(kind)) .chain(std::iter::once(self.tlib_path)) @@ -37,26 +37,26 @@ impl<'a> FileSearch<'a> { pub fn new( sysroot: &'a Path, triple: &'a str, - search_paths: &'a [SearchPath], + cli_search_paths: &'a [SearchPath], tlib_path: &'a SearchPath, kind: PathKind, ) -> FileSearch<'a> { debug!("using sysroot = {}, triple = {}", sysroot.display(), triple); - FileSearch { sysroot, triple, search_paths, tlib_path, kind } + FileSearch { sysroot, triple, cli_search_paths, tlib_path, kind } } } pub fn make_target_lib_path(sysroot: &Path, target_triple: &str) -> PathBuf { - let rustlib_path = rustc_target::target_rustlib_path(sysroot, target_triple); - PathBuf::from_iter([sysroot, Path::new(&rustlib_path), Path::new("lib")]) + let rustlib_path = rustc_target::relative_target_rustlib_path(sysroot, target_triple); + sysroot.join(rustlib_path).join("lib") } /// Returns a path to the target's `bin` folder within its `rustlib` path in the sysroot. This is /// where binaries are usually installed, e.g. the self-contained linkers, lld-wrappers, LLVM tools, /// etc. pub fn make_target_bin_path(sysroot: &Path, target_triple: &str) -> PathBuf { - let rustlib_path = rustc_target::target_rustlib_path(sysroot, target_triple); - PathBuf::from_iter([sysroot, Path::new(&rustlib_path), Path::new("bin")]) + let rustlib_path = rustc_target::relative_target_rustlib_path(sysroot, target_triple); + sysroot.join(rustlib_path).join("bin") } #[cfg(unix)] @@ -275,7 +275,7 @@ pub fn get_or_default_sysroot() -> Result { p.pop(); p.pop(); // Look for the target rustlib directory in the suspected sysroot. - let mut rustlib_path = rustc_target::target_rustlib_path(&p, "dummy"); + let mut rustlib_path = rustc_target::relative_target_rustlib_path(&p, "dummy"); rustlib_path.pop(); // pop off the dummy target. rustlib_path.exists().then_some(p) } diff --git a/compiler/rustc_target/src/lib.rs b/compiler/rustc_target/src/lib.rs index 46c83be9d95f..ecc91ab9a310 100644 --- a/compiler/rustc_target/src/lib.rs +++ b/compiler/rustc_target/src/lib.rs @@ -41,17 +41,13 @@ const RUST_LIB_DIR: &str = "rustlib"; /// /// For example: `target_sysroot_path("/usr", "x86_64-unknown-linux-gnu")` => /// `"lib*/rustlib/x86_64-unknown-linux-gnu"`. -pub fn target_rustlib_path(sysroot: &Path, target_triple: &str) -> PathBuf { - let libdir = find_libdir(sysroot); - PathBuf::from_iter([ - Path::new(libdir.as_ref()), - Path::new(RUST_LIB_DIR), - Path::new(target_triple), - ]) +pub fn relative_target_rustlib_path(sysroot: &Path, target_triple: &str) -> PathBuf { + let libdir = find_relative_libdir(sysroot); + Path::new(libdir.as_ref()).join(RUST_LIB_DIR).join(target_triple) } /// The name of the directory rustc expects libraries to be located. -fn find_libdir(sysroot: &Path) -> std::borrow::Cow<'static, str> { +fn find_relative_libdir(sysroot: &Path) -> std::borrow::Cow<'static, str> { // FIXME: This is a quick hack to make the rustc binary able to locate // Rust libraries in Linux environments where libraries might be installed // to lib64/lib32. This would be more foolproof by basing the sysroot off diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 83ee63e2cf28..fe07d116726b 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -3367,7 +3367,7 @@ impl Target { // Additionally look in the sysroot under `lib/rustlib//target.json` // as a fallback. - let rustlib_path = crate::target_rustlib_path(sysroot, target_triple); + let rustlib_path = crate::relative_target_rustlib_path(sysroot, target_triple); let p = PathBuf::from_iter([ Path::new(sysroot), Path::new(&rustlib_path), From 216424da32db1bf0d224b97ff3d0424622c97c4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Thu, 6 Jun 2024 20:39:54 +0000 Subject: [PATCH 23/92] Revert "Rollup merge of #124099 - voidc:disallow-ambiguous-expr-attrs, r=davidtwco" This reverts commit 57dad1d75e562ff73051c1c43b07eaf65c7dbd74, reversing changes made to 36316df9fe6c3e246153fe6e78967643cf08c148. --- compiler/rustc_parse/messages.ftl | 2 - compiler/rustc_parse/src/errors.rs | 9 ---- compiler/rustc_parse/src/parser/expr.rs | 32 ++++++------- .../clippy/tests/ui/cfg_attr_rustfmt.fixed | 6 +-- src/tools/clippy/tests/ui/cfg_attr_rustfmt.rs | 6 +-- src/tools/rustfmt/tests/source/attrib.rs | 4 +- src/tools/rustfmt/tests/target/attrib.rs | 4 +- tests/pretty/ast-stmt-expr-attr.rs | 18 ++++---- tests/pretty/stmt_expr_attributes.rs | 12 ++--- .../unused/unused-doc-comments-edge-cases.rs | 4 +- .../unused-doc-comments-edge-cases.stderr | 14 +++--- .../attribute/attr-binary-expr-ambigous.fixed | 15 ------ .../attribute/attr-binary-expr-ambigous.rs | 15 ------ .../attr-binary-expr-ambigous.stderr | 46 ------------------- tests/ui/proc-macro/issue-81555.rs | 3 +- 15 files changed, 49 insertions(+), 141 deletions(-) delete mode 100644 tests/ui/parser/attribute/attr-binary-expr-ambigous.fixed delete mode 100644 tests/ui/parser/attribute/attr-binary-expr-ambigous.rs delete mode 100644 tests/ui/parser/attribute/attr-binary-expr-ambigous.stderr diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index 04f855e4f559..f678d11213c7 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -622,8 +622,6 @@ parse_or_pattern_not_allowed_in_let_binding = top-level or-patterns are not allo parse_out_of_range_hex_escape = out of range hex escape .label = must be a character in the range [\x00-\x7f] -parse_outer_attr_ambiguous = ambiguous outer attributes - parse_outer_attr_explanation = outer attributes, like `#[test]`, annotate the item following them parse_outer_attribute_not_allowed_on_if_else = outer attributes are not allowed on `if` and `else` branches diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 3f08a830b0c9..6c1fcbe06fc5 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -495,15 +495,6 @@ pub(crate) struct OuterAttributeNotAllowedOnIfElse { pub attributes: Span, } -#[derive(Diagnostic)] -#[diag(parse_outer_attr_ambiguous)] -pub(crate) struct AmbiguousOuterAttributes { - #[primary_span] - pub span: Span, - #[subdiagnostic] - pub sugg: WrapInParentheses, -} - #[derive(Diagnostic)] #[diag(parse_missing_in_in_for_loop)] pub(crate) struct MissingInInForLoop { diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 1b99bc015b60..285bac3f4b88 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -328,9 +328,7 @@ impl<'a> Parser<'a> { this.parse_expr_assoc_with(prec + prec_adjustment, LhsExpr::NotYetParsed) })?; - self.error_ambiguous_outer_attrs(&lhs, lhs_span, rhs.span); - let span = lhs_span.to(rhs.span); - + let span = self.mk_expr_sp(&lhs, lhs_span, rhs.span); lhs = match op { AssocOp::Add | AssocOp::Subtract @@ -429,18 +427,6 @@ impl<'a> Parser<'a> { }); } - fn error_ambiguous_outer_attrs(&self, lhs: &P, lhs_span: Span, rhs_span: Span) { - if let Some(attr) = lhs.attrs.iter().find(|a| a.style == AttrStyle::Outer) { - self.dcx().emit_err(errors::AmbiguousOuterAttributes { - span: attr.span.to(rhs_span), - sugg: errors::WrapInParentheses::Expression { - left: attr.span.shrink_to_lo(), - right: lhs_span.shrink_to_hi(), - }, - }); - } - } - /// Possibly translate the current token to an associative operator. /// The method does not advance the current token. /// @@ -520,8 +506,7 @@ impl<'a> Parser<'a> { None }; let rhs_span = rhs.as_ref().map_or(cur_op_span, |x| x.span); - self.error_ambiguous_outer_attrs(&lhs, lhs.span, rhs_span); - let span = lhs.span.to(rhs_span); + let span = self.mk_expr_sp(&lhs, lhs.span, rhs_span); let limits = if op == AssocOp::DotDot { RangeLimits::HalfOpen } else { RangeLimits::Closed }; let range = self.mk_range(Some(lhs), rhs, limits); @@ -739,8 +724,7 @@ impl<'a> Parser<'a> { expr_kind: fn(P, P) -> ExprKind, ) -> PResult<'a, P> { let mk_expr = |this: &mut Self, lhs: P, rhs: P| { - this.error_ambiguous_outer_attrs(&lhs, lhs_span, rhs.span); - this.mk_expr(lhs_span.to(rhs.span), expr_kind(lhs, rhs)) + this.mk_expr(this.mk_expr_sp(&lhs, lhs_span, rhs.span), expr_kind(lhs, rhs)) }; // Save the state of the parser before parsing type normally, in case there is a @@ -3858,6 +3842,16 @@ impl<'a> Parser<'a> { self.mk_expr(span, ExprKind::Err(guar)) } + /// Create expression span ensuring the span of the parent node + /// is larger than the span of lhs and rhs, including the attributes. + fn mk_expr_sp(&self, lhs: &P, lhs_span: Span, rhs_span: Span) -> Span { + lhs.attrs + .iter() + .find(|a| a.style == AttrStyle::Outer) + .map_or(lhs_span, |a| a.span) + .to(rhs_span) + } + fn collect_tokens_for_expr( &mut self, attrs: AttrWrapper, diff --git a/src/tools/clippy/tests/ui/cfg_attr_rustfmt.fixed b/src/tools/clippy/tests/ui/cfg_attr_rustfmt.fixed index 84dac431169a..05d5b3d10eaf 100644 --- a/src/tools/clippy/tests/ui/cfg_attr_rustfmt.fixed +++ b/src/tools/clippy/tests/ui/cfg_attr_rustfmt.fixed @@ -16,7 +16,7 @@ fn foo( fn skip_on_statements() { #[rustfmt::skip] - { 5+3; } + 5+3; } #[rustfmt::skip] @@ -33,11 +33,11 @@ mod foo { #[clippy::msrv = "1.29"] fn msrv_1_29() { #[cfg_attr(rustfmt, rustfmt::skip)] - { 1+29; } + 1+29; } #[clippy::msrv = "1.30"] fn msrv_1_30() { #[rustfmt::skip] - { 1+30; } + 1+30; } diff --git a/src/tools/clippy/tests/ui/cfg_attr_rustfmt.rs b/src/tools/clippy/tests/ui/cfg_attr_rustfmt.rs index 4ab5c70e13b5..bc29e20210e8 100644 --- a/src/tools/clippy/tests/ui/cfg_attr_rustfmt.rs +++ b/src/tools/clippy/tests/ui/cfg_attr_rustfmt.rs @@ -16,7 +16,7 @@ fn foo( fn skip_on_statements() { #[cfg_attr(rustfmt, rustfmt::skip)] - { 5+3; } + 5+3; } #[cfg_attr(rustfmt, rustfmt_skip)] @@ -33,11 +33,11 @@ mod foo { #[clippy::msrv = "1.29"] fn msrv_1_29() { #[cfg_attr(rustfmt, rustfmt::skip)] - { 1+29; } + 1+29; } #[clippy::msrv = "1.30"] fn msrv_1_30() { #[cfg_attr(rustfmt, rustfmt::skip)] - { 1+30; } + 1+30; } diff --git a/src/tools/rustfmt/tests/source/attrib.rs b/src/tools/rustfmt/tests/source/attrib.rs index fc13cd02b03e..d45fba552243 100644 --- a/src/tools/rustfmt/tests/source/attrib.rs +++ b/src/tools/rustfmt/tests/source/attrib.rs @@ -214,8 +214,8 @@ type Os = NoSource; // #3313 fn stmt_expr_attributes() { let foo ; - (#[must_use] - foo) = false ; + #[must_use] + foo = false ; } // #3509 diff --git a/src/tools/rustfmt/tests/target/attrib.rs b/src/tools/rustfmt/tests/target/attrib.rs index 7b3309676de2..7e61f68d76a1 100644 --- a/src/tools/rustfmt/tests/target/attrib.rs +++ b/src/tools/rustfmt/tests/target/attrib.rs @@ -248,8 +248,8 @@ type Os = NoSource; // #3313 fn stmt_expr_attributes() { let foo; - (#[must_use] - foo) = false; + #[must_use] + foo = false; } // #3509 diff --git a/tests/pretty/ast-stmt-expr-attr.rs b/tests/pretty/ast-stmt-expr-attr.rs index 899f7173f704..fd7272a1b1fd 100644 --- a/tests/pretty/ast-stmt-expr-attr.rs +++ b/tests/pretty/ast-stmt-expr-attr.rs @@ -13,17 +13,17 @@ fn syntax() { let _ = #[attr] (); let _ = #[attr] (#[attr] 0,); let _ = #[attr] (#[attr] 0, 0); - let _ = (#[attr] 0) + #[attr] 0; - let _ = (#[attr] 0) / #[attr] 0; - let _ = (#[attr] 0) & #[attr] 0; - let _ = (#[attr] 0) % #[attr] 0; + let _ = #[attr] 0 + #[attr] 0; + let _ = #[attr] 0 / #[attr] 0; + let _ = #[attr] 0 & #[attr] 0; + let _ = #[attr] 0 % #[attr] 0; let _ = #[attr] (0 + 0); let _ = #[attr] !0; let _ = #[attr] -0; let _ = #[attr] false; let _ = #[attr] 0; let _ = #[attr] 'c'; - let _ = (#[attr] x) as Y; + let _ = #[attr] x as Y; let _ = #[attr] (x as Y); let _ = #[attr] while true { @@ -88,9 +88,9 @@ fn syntax() { let _ = (); foo }; - let _ = (#[attr] x) = y; + let _ = #[attr] x = y; let _ = #[attr] (x = y); - let _ = (#[attr] x) += y; + let _ = #[attr] x += y; let _ = #[attr] (x += y); let _ = #[attr] foo.bar; let _ = (#[attr] foo).bar; @@ -98,8 +98,8 @@ fn syntax() { let _ = (#[attr] foo).0; let _ = #[attr] foo[bar]; let _ = (#[attr] foo)[bar]; - let _ = (#[attr] 0)..#[attr] 0; - let _ = (#[attr] 0)..; + let _ = #[attr] 0..#[attr] 0; + let _ = #[attr] 0..; let _ = #[attr] (0..0); let _ = #[attr] (0..); let _ = #[attr] (..0); diff --git a/tests/pretty/stmt_expr_attributes.rs b/tests/pretty/stmt_expr_attributes.rs index f90412682110..e7fd46427242 100644 --- a/tests/pretty/stmt_expr_attributes.rs +++ b/tests/pretty/stmt_expr_attributes.rs @@ -147,13 +147,13 @@ fn _11() { let _ = #[rustc_dummy] (0); let _ = #[rustc_dummy] (0,); let _ = #[rustc_dummy] (0, 0); - let _ = (#[rustc_dummy] 0) + #[rustc_dummy] 0; + let _ = #[rustc_dummy] 0 + #[rustc_dummy] 0; let _ = #[rustc_dummy] !0; let _ = #[rustc_dummy] -0i32; let _ = #[rustc_dummy] false; let _ = #[rustc_dummy] 'c'; let _ = #[rustc_dummy] 0; - let _ = (#[rustc_dummy] 0) as usize; + let _ = #[rustc_dummy] 0 as usize; let _ = #[rustc_dummy] while false { #![rustc_dummy] @@ -208,8 +208,8 @@ fn _11() { }; let const {} = #[rustc_dummy] const {}; let mut x = 0; - let _ = (#[rustc_dummy] x) = 15; - let _ = (#[rustc_dummy] x) += 15; + let _ = #[rustc_dummy] x = 15; + let _ = #[rustc_dummy] x += 15; let s = Foo { data: () }; let _ = #[rustc_dummy] s.data; let _ = (#[rustc_dummy] s).data; @@ -219,8 +219,8 @@ fn _11() { let v = vec!(0); let _ = #[rustc_dummy] v[0]; let _ = (#[rustc_dummy] v)[0]; - let _ = (#[rustc_dummy] 0)..#[rustc_dummy] 0; - let _ = (#[rustc_dummy] 0)..; + let _ = #[rustc_dummy] 0..#[rustc_dummy] 0; + let _ = #[rustc_dummy] 0..; let _ = #[rustc_dummy] (0..0); let _ = #[rustc_dummy] (0..); let _ = #[rustc_dummy] (..0); diff --git a/tests/ui/lint/unused/unused-doc-comments-edge-cases.rs b/tests/ui/lint/unused/unused-doc-comments-edge-cases.rs index 0f9eac93930b..ba32fb566e83 100644 --- a/tests/ui/lint/unused/unused-doc-comments-edge-cases.rs +++ b/tests/ui/lint/unused/unused-doc-comments-edge-cases.rs @@ -20,10 +20,10 @@ fn doc_comment_between_if_else(num: u8) -> bool { } fn doc_comment_on_expr(num: u8) -> bool { - (/// useless doc comment + /// useless doc comment //~^ ERROR: attributes on expressions are experimental //~| ERROR: unused doc comment - num) == 3 + num == 3 } fn doc_comment_on_expr_field() -> bool { diff --git a/tests/ui/lint/unused/unused-doc-comments-edge-cases.stderr b/tests/ui/lint/unused/unused-doc-comments-edge-cases.stderr index 1ff5c8d88253..c07411745549 100644 --- a/tests/ui/lint/unused/unused-doc-comments-edge-cases.stderr +++ b/tests/ui/lint/unused/unused-doc-comments-edge-cases.stderr @@ -5,10 +5,10 @@ LL | else { | ^^^^ expected expression error[E0658]: attributes on expressions are experimental - --> $DIR/unused-doc-comments-edge-cases.rs:23:6 + --> $DIR/unused-doc-comments-edge-cases.rs:23:5 | -LL | (/// useless doc comment - | ^^^^^^^^^^^^^^^^^^^^^^^ +LL | /// useless doc comment + | ^^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #15701 for more information = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable @@ -32,12 +32,12 @@ LL | #![deny(unused_doc_comments)] | ^^^^^^^^^^^^^^^^^^^ error: unused doc comment - --> $DIR/unused-doc-comments-edge-cases.rs:23:6 + --> $DIR/unused-doc-comments-edge-cases.rs:23:5 | -LL | (/// useless doc comment - | ^^^^^^^^^^^^^^^^^^^^^^^ +LL | /// useless doc comment + | ^^^^^^^^^^^^^^^^^^^^^^^ ... -LL | num) == 3 +LL | num == 3 | --- rustdoc does not generate documentation for expressions | = help: use `//` for a plain comment diff --git a/tests/ui/parser/attribute/attr-binary-expr-ambigous.fixed b/tests/ui/parser/attribute/attr-binary-expr-ambigous.fixed deleted file mode 100644 index aae71ede7713..000000000000 --- a/tests/ui/parser/attribute/attr-binary-expr-ambigous.fixed +++ /dev/null @@ -1,15 +0,0 @@ -//@ run-rustfix -#![feature(stmt_expr_attributes)] -#![allow(unused_assignments, unused_attributes)] - -fn main() { - let mut x = (#[deprecated] 1) + 2; //~ ERROR ambiguous - - (#[deprecated] x) = 4; //~ ERROR ambiguous - - x = (#[deprecated] 5) as i32; //~ ERROR ambiguous - - let _r = (#[deprecated] 1)..6; //~ ERROR ambiguous - - println!("{}", x); -} diff --git a/tests/ui/parser/attribute/attr-binary-expr-ambigous.rs b/tests/ui/parser/attribute/attr-binary-expr-ambigous.rs deleted file mode 100644 index 613e01d743bd..000000000000 --- a/tests/ui/parser/attribute/attr-binary-expr-ambigous.rs +++ /dev/null @@ -1,15 +0,0 @@ -//@ run-rustfix -#![feature(stmt_expr_attributes)] -#![allow(unused_assignments, unused_attributes)] - -fn main() { - let mut x = #[deprecated] 1 + 2; //~ ERROR ambiguous - - #[deprecated] x = 4; //~ ERROR ambiguous - - x = #[deprecated] 5 as i32; //~ ERROR ambiguous - - let _r = #[deprecated] 1..6; //~ ERROR ambiguous - - println!("{}", x); -} diff --git a/tests/ui/parser/attribute/attr-binary-expr-ambigous.stderr b/tests/ui/parser/attribute/attr-binary-expr-ambigous.stderr deleted file mode 100644 index 0430570fd49d..000000000000 --- a/tests/ui/parser/attribute/attr-binary-expr-ambigous.stderr +++ /dev/null @@ -1,46 +0,0 @@ -error: ambiguous outer attributes - --> $DIR/attr-binary-expr-ambigous.rs:6:17 - | -LL | let mut x = #[deprecated] 1 + 2; - | ^^^^^^^^^^^^^^^^^^^ - | -help: wrap the expression in parentheses - | -LL | let mut x = (#[deprecated] 1) + 2; - | + + - -error: ambiguous outer attributes - --> $DIR/attr-binary-expr-ambigous.rs:8:5 - | -LL | #[deprecated] x = 4; - | ^^^^^^^^^^^^^^^^^^^ - | -help: wrap the expression in parentheses - | -LL | (#[deprecated] x) = 4; - | + + - -error: ambiguous outer attributes - --> $DIR/attr-binary-expr-ambigous.rs:10:9 - | -LL | x = #[deprecated] 5 as i32; - | ^^^^^^^^^^^^^^^^^^^^^^ - | -help: wrap the expression in parentheses - | -LL | x = (#[deprecated] 5) as i32; - | + + - -error: ambiguous outer attributes - --> $DIR/attr-binary-expr-ambigous.rs:12:14 - | -LL | let _r = #[deprecated] 1..6; - | ^^^^^^^^^^^^^^^^^^ - | -help: wrap the expression in parentheses - | -LL | let _r = (#[deprecated] 1)..6; - | + + - -error: aborting due to 4 previous errors - diff --git a/tests/ui/proc-macro/issue-81555.rs b/tests/ui/proc-macro/issue-81555.rs index b337ab7ce37d..7a61a31952f4 100644 --- a/tests/ui/proc-macro/issue-81555.rs +++ b/tests/ui/proc-macro/issue-81555.rs @@ -10,5 +10,6 @@ use test_macros::identity_attr; fn main() { let _x; let y = (); - (#[identity_attr] _x) = y; + #[identity_attr] + _x = y; } From dd5103bb6810e2e8d7e84f35749564a2a35dd5df Mon Sep 17 00:00:00 2001 From: Sunshine Date: Fri, 7 Jun 2024 05:49:46 +0800 Subject: [PATCH 24/92] Add test for PR #126057 --- src/librustdoc/html/static/js/externs.js | 3 +- tests/rustdoc-js/basic.js | 2 +- tests/rustdoc-js/doc-alias.js | 22 +++ tests/rustdoc-js/doc-alias.rs | 21 +++ tests/rustdoc-js/non-english-identifier.js | 163 +++++++++++++++++++++ tests/rustdoc-js/non-english-identifier.rs | 47 ++++++ 6 files changed, 256 insertions(+), 2 deletions(-) create mode 100644 tests/rustdoc-js/non-english-identifier.js create mode 100644 tests/rustdoc-js/non-english-identifier.rs diff --git a/src/librustdoc/html/static/js/externs.js b/src/librustdoc/html/static/js/externs.js index 8cebce7ae861..6fd60d6cc346 100644 --- a/src/librustdoc/html/static/js/externs.js +++ b/src/librustdoc/html/static/js/externs.js @@ -41,8 +41,9 @@ let ParserState; * foundElems: number, * totalElems: number, * literalSearch: boolean, - * corrections: Array<{from: string, to: integer}>, + * corrections: Array<{from: string, to: integer}> | null, * typeFingerprint: Uint32Array, + * error: Array | null, * }} */ let ParsedQuery; diff --git a/tests/rustdoc-js/basic.js b/tests/rustdoc-js/basic.js index e186d510887c..c38b8435c2d8 100644 --- a/tests/rustdoc-js/basic.js +++ b/tests/rustdoc-js/basic.js @@ -1,6 +1,6 @@ const EXPECTED = { 'query': 'Fo', 'others': [ - { 'path': 'basic', 'name': 'Foo' }, + { 'path': 'basic', 'name': 'Foo', 'desc': 'Docs for Foo' }, ], }; diff --git a/tests/rustdoc-js/doc-alias.js b/tests/rustdoc-js/doc-alias.js index 7e4e8a776d89..e57bd71419dc 100644 --- a/tests/rustdoc-js/doc-alias.js +++ b/tests/rustdoc-js/doc-alias.js @@ -5,6 +5,7 @@ const EXPECTED = [ { 'path': 'doc_alias', 'name': 'Struct', + 'desc': 'Doc for Struct', 'alias': 'StructItem', 'href': '../doc_alias/struct.Struct.html', 'is_alias': true @@ -17,6 +18,7 @@ const EXPECTED = [ { 'path': 'doc_alias::Struct', 'name': 'field', + 'desc': 'Doc for Struct’s field', 'alias': 'StructFieldItem', 'href': '../doc_alias/struct.Struct.html#structfield.field', 'is_alias': true @@ -29,6 +31,7 @@ const EXPECTED = [ { 'path': 'doc_alias::Struct', 'name': 'method', + 'desc': 'Doc for Struct::method', 'alias': 'StructMethodItem', 'href': '../doc_alias/struct.Struct.html#method.method', 'is_alias': true @@ -45,6 +48,7 @@ const EXPECTED = [ { 'path': 'doc_alias::Struct', 'name': 'ImplConstItem', + 'desc': 'Doc for Struct::ImplConstItem', 'alias': 'StructImplConstItem', 'href': '../doc_alias/struct.Struct.html#associatedconstant.ImplConstItem', 'is_alias': true @@ -57,6 +61,7 @@ const EXPECTED = [ { 'path': 'doc_alias::Struct', 'name': 'function', + 'desc': 'Doc for Trait::function implemented for Struct', 'alias': 'ImplTraitFunction', 'href': '../doc_alias/struct.Struct.html#method.function', 'is_alias': true @@ -69,6 +74,7 @@ const EXPECTED = [ { 'path': 'doc_alias', 'name': 'Enum', + 'desc': 'Doc for Enum', 'alias': 'EnumItem', 'href': '../doc_alias/enum.Enum.html', 'is_alias': true @@ -81,6 +87,7 @@ const EXPECTED = [ { 'path': 'doc_alias::Enum', 'name': 'Variant', + 'desc': 'Doc for Enum::Variant', 'alias': 'VariantItem', 'href': '../doc_alias/enum.Enum.html#variant.Variant', 'is_alias': true @@ -93,6 +100,7 @@ const EXPECTED = [ { 'path': 'doc_alias::Enum', 'name': 'method', + 'desc': 'Doc for Enum::method', 'alias': 'EnumMethodItem', 'href': '../doc_alias/enum.Enum.html#method.method', 'is_alias': true @@ -105,6 +113,7 @@ const EXPECTED = [ { 'path': 'doc_alias', 'name': 'Typedef', + 'desc': 'Doc for type alias Typedef', 'alias': 'TypedefItem', 'href': '../doc_alias/type.Typedef.html', 'is_alias': true @@ -117,6 +126,7 @@ const EXPECTED = [ { 'path': 'doc_alias', 'name': 'Trait', + 'desc': 'Doc for Trait', 'alias': 'TraitItem', 'href': '../doc_alias/trait.Trait.html', 'is_alias': true @@ -129,6 +139,7 @@ const EXPECTED = [ { 'path': 'doc_alias::Trait', 'name': 'Target', + 'desc': 'Doc for Trait::Target', 'alias': 'TraitTypeItem', 'href': '../doc_alias/trait.Trait.html#associatedtype.Target', 'is_alias': true @@ -141,6 +152,7 @@ const EXPECTED = [ { 'path': 'doc_alias::Trait', 'name': 'AssociatedConst', + 'desc': 'Doc for Trait::AssociatedConst', 'alias': 'AssociatedConstItem', 'href': '../doc_alias/trait.Trait.html#associatedconstant.AssociatedConst', 'is_alias': true @@ -153,6 +165,7 @@ const EXPECTED = [ { 'path': 'doc_alias::Trait', 'name': 'function', + 'desc': 'Doc for Trait::function', 'alias': 'TraitFunctionItem', 'href': '../doc_alias/trait.Trait.html#tymethod.function', 'is_alias': true @@ -165,6 +178,7 @@ const EXPECTED = [ { 'path': 'doc_alias', 'name': 'function', + 'desc': 'Doc for function', 'alias': 'FunctionItem', 'href': '../doc_alias/fn.function.html', 'is_alias': true @@ -177,6 +191,7 @@ const EXPECTED = [ { 'path': 'doc_alias', 'name': 'Module', + 'desc': 'Doc for Module', 'alias': 'ModuleItem', 'href': '../doc_alias/Module/index.html', 'is_alias': true @@ -189,6 +204,7 @@ const EXPECTED = [ { 'path': 'doc_alias', 'name': 'Const', + 'desc': 'Doc for Const', 'alias': 'ConstItem', 'href': '../doc_alias/constant.Const.html', 'is_alias': true @@ -205,6 +221,7 @@ const EXPECTED = [ { 'path': 'doc_alias', 'name': 'Static', + 'desc': 'Doc for Static', 'alias': 'StaticItem', 'href': '../doc_alias/static.Static.html', 'is_alias': true @@ -217,6 +234,7 @@ const EXPECTED = [ { 'path': 'doc_alias', 'name': 'Union', + 'desc': 'Doc for Union', 'alias': 'UnionItem', 'href': '../doc_alias/union.Union.html', 'is_alias': true @@ -225,6 +243,7 @@ const EXPECTED = [ { 'path': 'doc_alias::Union', 'name': 'union_item', + 'desc': 'Doc for Union::union_item', 'href': '../doc_alias/union.Union.html#structfield.union_item' }, ], @@ -235,6 +254,7 @@ const EXPECTED = [ { 'path': 'doc_alias::Union', 'name': 'union_item', + 'desc': 'Doc for Union::union_item', 'alias': 'UnionFieldItem', 'href': '../doc_alias/union.Union.html#structfield.union_item', 'is_alias': true @@ -247,6 +267,7 @@ const EXPECTED = [ { 'path': 'doc_alias::Union', 'name': 'method', + 'desc': 'Doc for Union::method', 'alias': 'UnionMethodItem', 'href': '../doc_alias/union.Union.html#method.method', 'is_alias': true @@ -259,6 +280,7 @@ const EXPECTED = [ { 'path': 'doc_alias', 'name': 'Macro', + 'desc': 'Doc for Macro', 'alias': 'MacroItem', 'href': '../doc_alias/macro.Macro.html', 'is_alias': true diff --git a/tests/rustdoc-js/doc-alias.rs b/tests/rustdoc-js/doc-alias.rs index 453b55c956c7..bf075385327a 100644 --- a/tests/rustdoc-js/doc-alias.rs +++ b/tests/rustdoc-js/doc-alias.rs @@ -1,12 +1,16 @@ +/// Doc for `Struct` #[doc(alias = "StructItem")] pub struct Struct { + /// Doc for `Struct`'s `field` #[doc(alias = "StructFieldItem")] pub field: u32, } impl Struct { + /// Doc for `Struct::ImplConstItem` #[doc(alias = "StructImplConstItem")] pub const ImplConstItem: i32 = 0; + /// Doc for `Struct::method` #[doc(alias = "StructMethodItem")] pub fn method(&self) {} } @@ -15,61 +19,78 @@ impl Trait for Struct { type Target = u32; const AssociatedConst: i32 = 12; + /// Doc for `Trait::function` implemented for Struct #[doc(alias = "ImplTraitFunction")] fn function() -> Self::Target { 0 } } +/// Doc for `Enum` #[doc(alias = "EnumItem")] pub enum Enum { + /// Doc for `Enum::Variant` #[doc(alias = "VariantItem")] Variant, } impl Enum { + /// Doc for `Enum::method` #[doc(alias = "EnumMethodItem")] pub fn method(&self) {} } +/// Doc for type alias `Typedef` #[doc(alias = "TypedefItem")] pub type Typedef = i32; +/// Doc for `Trait` #[doc(alias = "TraitItem")] pub trait Trait { + /// Doc for `Trait::Target` #[doc(alias = "TraitTypeItem")] type Target; + /// Doc for `Trait::AssociatedConst` #[doc(alias = "AssociatedConstItem")] const AssociatedConst: i32; + /// Doc for `Trait::function` #[doc(alias = "TraitFunctionItem")] fn function() -> Self::Target; } +/// Doc for `function` #[doc(alias = "FunctionItem")] pub fn function() {} +/// Doc for `Module` #[doc(alias = "ModuleItem")] pub mod Module {} +/// Doc for `Const` #[doc(alias = "ConstItem")] pub const Const: u32 = 0; +/// Doc for `Static` #[doc(alias = "StaticItem")] pub static Static: u32 = 0; +/// Doc for `Union` #[doc(alias = "UnionItem")] pub union Union { + /// Doc for `Union::union_item` #[doc(alias = "UnionFieldItem")] pub union_item: u32, pub y: f32, } impl Union { + /// Doc for `Union::method` #[doc(alias = "UnionMethodItem")] pub fn method(&self) {} } +/// Doc for `Macro` #[doc(alias = "MacroItem")] #[macro_export] macro_rules! Macro { diff --git a/tests/rustdoc-js/non-english-identifier.js b/tests/rustdoc-js/non-english-identifier.js new file mode 100644 index 000000000000..59c6a3a7f992 --- /dev/null +++ b/tests/rustdoc-js/non-english-identifier.js @@ -0,0 +1,163 @@ +const PARSED = [ + { + query: '中文', + elems: [{ + name: "中文", + fullPath: ["中文"], + pathWithoutLast: [], + pathLast: "中文", + generics: [], + typeFilter: -1, + }], + returned: [], + foundElems: 1, + original: "中文", + userQuery: "中文", + error: null, + }, + { + query: '_0Mixed中英文', + elems: [{ + name: "_0mixed中英文", + fullPath: ["_0mixed中英文"], + pathWithoutLast: [], + pathLast: "_0mixed中英文", + generics: [], + typeFilter: -1, + }], + foundElems: 1, + original: "_0Mixed中英文", + returned: [], + userQuery: "_0mixed中英文", + error: null, + }, + { + query: 'my_crate::中文API', + elems: [{ + name: "my_crate::中文api", + fullPath: ["my_crate", "中文api"], + pathWithoutLast: ["my_crate"], + pathLast: "中文api", + generics: [], + typeFilter: -1, + }], + foundElems: 1, + original: "my_crate::中文API", + returned: [], + userQuery: "my_crate::中文api", + error: null, + }, + { + query: '类型A,类型B<约束C>->返回类型<关联类型=路径::约束D>', + elems: [{ + name: "类型a", + fullPath: ["类型a"], + pathWithoutLast: [], + pathLast: "类型a", + generics: [], + }, { + name: "类型b", + fullPath: ["类型b"], + pathWithoutLast: [], + pathLast: "类型b", + generics: [{ + name: "约束c", + fullPath: ["约束c"], + pathWithoutLast: [], + pathLast: "约束c", + generics: [], + }], + }], + foundElems: 3, + totalElems: 5, + literalSearch: true, + original: "类型A,类型B<约束C>->返回类型<关联类型=路径::约束D>", + returned: [{ + name: "返回类型", + fullPath: ["返回类型"], + pathWithoutLast: [], + pathLast: "返回类型", + generics: [], + }], + userQuery: "类型a,类型b<约束c>->返回类型<关联类型=路径::约束d>", + error: null, + }, + { + query: 'my_crate 中文宏!', + elems: [{ + name: "my_crate 中文宏", + fullPath: ["my_crate", "中文宏"], + pathWithoutLast: ["my_crate"], + pathLast: "中文宏", + generics: [], + typeFilter: 16, + }], + foundElems: 1, + original: "my_crate 中文宏!", + returned: [], + userQuery: "my_crate 中文宏!", + error: null, + }, + { + query: '非法符号——', + elems: [], + foundElems: 0, + original: "非法符号——", + returned: [], + userQuery: "非法符号——", + error: "Unexpected `—` after `号`", + } +] +const EXPECTED = [ + { + query: '加法', + others: [ + { + name: "add", + path: "non_english_identifier", + is_alias: true, + alias: "加法", + href: "../non_english_identifier/macro.add.html" + }, + { + name: "add", + path: "non_english_identifier", + is_alias: true, + alias: "加法", + href: "../non_english_identifier/fn.add.html" + }, + { + name: "加法", + path: "non_english_identifier", + href: "../non_english_identifier/trait.加法.html", + desc: "Add" + }, + { + name: "中文名称的加法宏", + path: "non_english_identifier", + href: "../non_english_identifier/macro.中文名称的加法宏.html", + }, + { + name: "中文名称的加法API", + path: "non_english_identifier", + href: "../non_english_identifier/fn.中文名称的加法API.html", + }], + in_args: [{ + name: "加上", + path: "non_english_identifier::加法", + href: "../non_english_identifier/trait.加法.html#tymethod.加上", + }], + returned: [], + }, + { // Extensive type-based search is still buggy, experimental & work-in-progress. + query: '可迭代->可选', + others: [{ + name: "总计", + path: "non_english_identifier", + href: "../non_english_identifier/fn.总计.html", + desc: "“sum”" + }], + in_args: [], + returned: [], + }, +]; \ No newline at end of file diff --git a/tests/rustdoc-js/non-english-identifier.rs b/tests/rustdoc-js/non-english-identifier.rs new file mode 100644 index 000000000000..70b5141472c8 --- /dev/null +++ b/tests/rustdoc-js/non-english-identifier.rs @@ -0,0 +1,47 @@ +#[doc(alias = "加法")] +pub fn add(left: usize, right: usize) -> usize { + left + right +} + +pub fn 中文名称的加法API(left: usize, right: usize) -> usize { + left + right +} + +#[macro_export] +macro_rules! 中文名称的加法宏 { + ($left:expr, $right:expr) => { + ($left) + ($right) + }; +} + +#[doc(alias = "加法")] +#[macro_export] +macro_rules! add { + ($left:expr, $right:expr) => { + ($left) + ($right) + }; +} + +/// Add +pub trait 加法<类型> { + type 结果; + fn 加上(self, 被加数: 类型) -> Self::结果; +} + +/// IntoIterator +pub trait 可迭代 { + type 项; + type 转为迭代器: Iterator; + fn 迭代(self) -> Self::转为迭代器; +} + +pub type 可选<类型> = Option<类型>; + +/// "sum" +pub fn 总计<集合, 个体>(容器: 集合) -> 可选<集合::项> +where + 集合: 可迭代<项 = 个体>, + 个体: 加法<个体, 结果 = 个体>, +{ + 容器.迭代().reduce(|累计值, 当前值| 累计值.加上(当前值)) +} From f9f51839e5b5ebc92848bfcf924040c4f4b65df4 Mon Sep 17 00:00:00 2001 From: Sunshine Date: Fri, 7 Jun 2024 06:09:30 +0800 Subject: [PATCH 25/92] Tidying --- src/librustdoc/html/static/js/search.js | 6 ++++-- tests/rustdoc-js/non-english-identifier.js | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 8322ea904908..f4b81e4b7eef 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -2162,7 +2162,8 @@ function initSearch(rawSearchIndex) { aliases.sort(sortFunc); const fetchDesc = alias => { - return searchIndexEmptyDesc.get(alias.crate).contains(alias.bitIndex) ? "" : searchState.loadDesc(alias); + return searchIndexEmptyDesc.get(alias.crate).contains(alias.bitIndex) ? + "" : searchState.loadDesc(alias); }; const [crateDescs, descs] = await Promise.all([ Promise.all(crateAliases.map(fetchDesc)), @@ -2546,7 +2547,8 @@ function initSearch(rawSearchIndex) { sorted_returned, sorted_others, parsedQuery); - await handleAliases(ret, parsedQuery.original.replace(/"/g, ""), filterCrates, currentCrate); + await handleAliases(ret, parsedQuery.original.replace(/"/g, ""), + filterCrates, currentCrate); await Promise.all([ret.others, ret.returned, ret.in_args].map(async list => { const descs = await Promise.all(list.map(result => { return searchIndexEmptyDesc.get(result.crate).contains(result.bitIndex) ? diff --git a/tests/rustdoc-js/non-english-identifier.js b/tests/rustdoc-js/non-english-identifier.js index 59c6a3a7f992..84209aa04c02 100644 --- a/tests/rustdoc-js/non-english-identifier.js +++ b/tests/rustdoc-js/non-english-identifier.js @@ -160,4 +160,4 @@ const EXPECTED = [ in_args: [], returned: [], }, -]; \ No newline at end of file +]; From 2bdc53b153f16af9d81f8b68bdf27575ff82dc71 Mon Sep 17 00:00:00 2001 From: schvv31n Date: Thu, 6 Jun 2024 23:16:39 +0100 Subject: [PATCH 26/92] fixed memory leaks in PathBuf::leak & OsString::leak tests --- library/std/src/ffi/os_str/tests.rs | 2 ++ library/std/src/path/tests.rs | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/library/std/src/ffi/os_str/tests.rs b/library/std/src/ffi/os_str/tests.rs index 2379f0ba489e..5b39b9e34d8c 100644 --- a/library/std/src/ffi/os_str/tests.rs +++ b/library/std/src/ffi/os_str/tests.rs @@ -26,8 +26,10 @@ fn test_os_string_clear() { #[test] fn test_os_string_leak() { let os_string = OsString::from("have a cake"); + let (len, cap) = (os_string.len(), os_string.capacity()); let leaked = os_string.leak(); assert_eq!(leaked.as_encoded_bytes(), b"have a cake"); + unsafe { drop(String::from_raw_parts(leaked as *mut OsStr as _, len, cap)) } } #[test] diff --git a/library/std/src/path/tests.rs b/library/std/src/path/tests.rs index d29f895ba38f..92702b395dfe 100644 --- a/library/std/src/path/tests.rs +++ b/library/std/src/path/tests.rs @@ -128,9 +128,12 @@ fn into() { #[test] fn test_pathbuf_leak() { - let buf = PathBuf::from("/have/a/cake".to_owned()); + let string = "/have/a/cake".to_owned(); + let (len, cap) = (string.len(), string.capacity()); + let buf = PathBuf::from(string); let leaked = buf.leak(); assert_eq!(leaked.as_os_str().as_encoded_bytes(), b"/have/a/cake"); + unsafe { drop(String::from_raw_parts(leaked.as_mut_os_str() as *mut OsStr as _, len, cap)) } } #[test] From d73d27348206d794995cd7acb65a1869c178d225 Mon Sep 17 00:00:00 2001 From: Sunshine Date: Fri, 7 Jun 2024 07:51:29 +0800 Subject: [PATCH 27/92] Bump the EcmaScript version in the eslint configuration. --- src/librustdoc/html/static/.eslintrc.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustdoc/html/static/.eslintrc.js b/src/librustdoc/html/static/.eslintrc.js index a1e9cc6dfa14..1eeda5a6ea4c 100644 --- a/src/librustdoc/html/static/.eslintrc.js +++ b/src/librustdoc/html/static/.eslintrc.js @@ -5,7 +5,7 @@ module.exports = { }, "extends": "eslint:recommended", "parserOptions": { - "ecmaVersion": 8, + "ecmaVersion": 9, "sourceType": "module" }, "rules": { From 76065f5b27b780132ccb5590ccf657ac71ecbf73 Mon Sep 17 00:00:00 2001 From: Slanterns Date: Tue, 4 Jun 2024 05:26:55 +0800 Subject: [PATCH 28/92] Stabilize `error_in_core` --- library/alloc/src/lib.rs | 1 - library/core/src/error.rs | 12 +----------- library/core/tests/lib.rs | 1 - library/std/src/lib.rs | 1 - 4 files changed, 1 insertion(+), 14 deletions(-) diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 4ac0c9b15be7..4749b8880fbc 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -121,7 +121,6 @@ #![feature(deref_pure_trait)] #![feature(dispatch_from_dyn)] #![feature(error_generic_member_access)] -#![feature(error_in_core)] #![feature(exact_size_is_empty)] #![feature(extend_one)] #![feature(fmt_internals)] diff --git a/library/core/src/error.rs b/library/core/src/error.rs index a3f2b767054e..99f10e6d9d10 100644 --- a/library/core/src/error.rs +++ b/library/core/src/error.rs @@ -1,5 +1,5 @@ #![doc = include_str!("error.md")] -#![unstable(feature = "error_in_core", issue = "103765")] +#![stable(feature = "error_in_core", since = "CURRENT_RUSTC_VERSION")] #[cfg(test)] mod tests; @@ -130,7 +130,6 @@ pub trait Error: Debug + Display { /// /// ```rust /// #![feature(error_generic_member_access)] - /// #![feature(error_in_core)] /// use core::fmt; /// use core::error::{request_ref, Request}; /// @@ -362,7 +361,6 @@ impl dyn Error { /// /// ```rust /// # #![feature(error_generic_member_access)] -/// # #![feature(error_in_core)] /// use std::error::Error; /// use core::error::request_value; /// @@ -386,7 +384,6 @@ where /// /// ```rust /// # #![feature(error_generic_member_access)] -/// # #![feature(error_in_core)] /// use core::error::Error; /// use core::error::request_ref; /// @@ -458,7 +455,6 @@ where /// /// ``` /// #![feature(error_generic_member_access)] -/// #![feature(error_in_core)] /// use core::fmt; /// use core::error::Request; /// use core::error::request_ref; @@ -529,7 +525,6 @@ impl<'a> Request<'a> { /// /// ```rust /// #![feature(error_generic_member_access)] - /// #![feature(error_in_core)] /// /// use core::error::Request; /// @@ -564,7 +559,6 @@ impl<'a> Request<'a> { /// /// ```rust /// #![feature(error_generic_member_access)] - /// #![feature(error_in_core)] /// /// use core::error::Request; /// @@ -600,7 +594,6 @@ impl<'a> Request<'a> { /// /// ```rust /// #![feature(error_generic_member_access)] - /// #![feature(error_in_core)] /// /// use core::error::Request; /// @@ -633,7 +626,6 @@ impl<'a> Request<'a> { /// /// ```rust /// #![feature(error_generic_member_access)] - /// #![feature(error_in_core)] /// /// use core::error::Request; /// @@ -700,7 +692,6 @@ impl<'a> Request<'a> { /// /// ```rust /// #![feature(error_generic_member_access)] - /// #![feature(error_in_core)] /// /// use core::error::Request; /// use core::error::request_value; @@ -788,7 +779,6 @@ impl<'a> Request<'a> { /// /// ```rust /// #![feature(error_generic_member_access)] - /// #![feature(error_in_core)] /// /// use core::error::Request; /// use core::error::request_ref; diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index 20ff6fd7687f..f632883b563d 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -112,7 +112,6 @@ #![feature(const_slice_from_ref)] #![feature(waker_getters)] #![feature(error_generic_member_access)] -#![feature(error_in_core)] #![feature(trait_upcasting)] #![feature(is_ascii_octdigit)] #![feature(get_many_mut)] diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 9d6576fa8411..1c226f9f08f1 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -324,7 +324,6 @@ #![feature(core_io_borrowed_buf)] #![feature(duration_constants)] #![feature(error_generic_member_access)] -#![feature(error_in_core)] #![feature(error_iter)] #![feature(exact_size_is_empty)] #![feature(exclusive_wrapper)] From c6f1934d34b49d95560b201ca25869c6bc0d051f Mon Sep 17 00:00:00 2001 From: Slanterns Date: Tue, 4 Jun 2024 05:33:03 +0800 Subject: [PATCH 29/92] fix doc comments about `error_generic_member_access` --- library/core/src/error.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/core/src/error.rs b/library/core/src/error.rs index 99f10e6d9d10..da18fdc6e1d1 100644 --- a/library/core/src/error.rs +++ b/library/core/src/error.rs @@ -360,7 +360,7 @@ impl dyn Error { /// Get a string value from an error. /// /// ```rust -/// # #![feature(error_generic_member_access)] +/// #![feature(error_generic_member_access)] /// use std::error::Error; /// use core::error::request_value; /// @@ -383,7 +383,7 @@ where /// Get a string reference from an error. /// /// ```rust -/// # #![feature(error_generic_member_access)] +/// #![feature(error_generic_member_access)] /// use core::error::Error; /// use core::error::request_ref; /// From f275ca09ddae04d656798bec5e862ad90325bdb8 Mon Sep 17 00:00:00 2001 From: Slanterns Date: Fri, 7 Jun 2024 08:37:05 +0800 Subject: [PATCH 30/92] bless `std_instead_of_core` --- src/tools/clippy/tests/ui/std_instead_of_core.fixed | 4 ++-- src/tools/clippy/tests/ui/std_instead_of_core.rs | 2 +- src/tools/clippy/tests/ui/std_instead_of_core.stderr | 8 +++++++- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/tools/clippy/tests/ui/std_instead_of_core.fixed b/src/tools/clippy/tests/ui/std_instead_of_core.fixed index ec4ae2ea13c5..6ede7bfcd9f6 100644 --- a/src/tools/clippy/tests/ui/std_instead_of_core.fixed +++ b/src/tools/clippy/tests/ui/std_instead_of_core.fixed @@ -45,8 +45,8 @@ fn std_instead_of_core() { let _ = std::env!("PATH"); - // do not lint until `error_in_core` is stable - use std::error::Error; + use core::error::Error; + //~^ ERROR: used import from `std` instead of `core` // lint items re-exported from private modules, `core::iter::traits::iterator::Iterator` use core::iter::Iterator; diff --git a/src/tools/clippy/tests/ui/std_instead_of_core.rs b/src/tools/clippy/tests/ui/std_instead_of_core.rs index c12c459c7eb4..e22b4f61f3ec 100644 --- a/src/tools/clippy/tests/ui/std_instead_of_core.rs +++ b/src/tools/clippy/tests/ui/std_instead_of_core.rs @@ -45,8 +45,8 @@ fn std_instead_of_core() { let _ = std::env!("PATH"); - // do not lint until `error_in_core` is stable use std::error::Error; + //~^ ERROR: used import from `std` instead of `core` // lint items re-exported from private modules, `core::iter::traits::iterator::Iterator` use std::iter::Iterator; diff --git a/src/tools/clippy/tests/ui/std_instead_of_core.stderr b/src/tools/clippy/tests/ui/std_instead_of_core.stderr index 8f920511cc5d..22cb9db7050b 100644 --- a/src/tools/clippy/tests/ui/std_instead_of_core.stderr +++ b/src/tools/clippy/tests/ui/std_instead_of_core.stderr @@ -49,6 +49,12 @@ error: used import from `std` instead of `core` LL | let cell_absolute = ::std::cell::Cell::new(8u32); | ^^^ help: consider importing the item from `core`: `core` +error: used import from `std` instead of `core` + --> tests/ui/std_instead_of_core.rs:48:9 + | +LL | use std::error::Error; + | ^^^ help: consider importing the item from `core`: `core` + error: used import from `std` instead of `core` --> tests/ui/std_instead_of_core.rs:52:9 | @@ -79,5 +85,5 @@ LL | use alloc::slice::from_ref; = note: `-D clippy::alloc-instead-of-core` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::alloc_instead_of_core)]` -error: aborting due to 12 previous errors +error: aborting due to 13 previous errors From 63411076c3531f2c5ed77d8f1cfc451d6ccddaed Mon Sep 17 00:00:00 2001 From: Sunshine Date: Fri, 7 Jun 2024 08:44:52 +0800 Subject: [PATCH 31/92] Other EcmaScript version bump --- src/ci/docker/host-x86_64/mingw-check/Dockerfile | 4 ++-- src/librustdoc/html/static/.eslintrc.js | 2 +- src/tools/rustdoc-gui/.eslintrc.js | 2 +- src/tools/rustdoc-js/.eslintrc.js | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/ci/docker/host-x86_64/mingw-check/Dockerfile b/src/ci/docker/host-x86_64/mingw-check/Dockerfile index d0da7d306601..0d9c21c4487b 100644 --- a/src/ci/docker/host-x86_64/mingw-check/Dockerfile +++ b/src/ci/docker/host-x86_64/mingw-check/Dockerfile @@ -61,8 +61,8 @@ ENV SCRIPT python3 ../x.py check --stage 0 --set build.optimized-compiler-builti /scripts/validate-toolstate.sh && \ /scripts/validate-error-codes.sh && \ reuse --include-submodules lint && \ - # Runs checks to ensure that there are no ES5 issues in our JS code. - es-check es8 ../src/librustdoc/html/static/js/*.js && \ + # Runs checks to ensure that there are no issues in our JS code. + es-check es2019 ../src/librustdoc/html/static/js/*.js && \ eslint -c ../src/librustdoc/html/static/.eslintrc.js ../src/librustdoc/html/static/js/*.js && \ eslint -c ../src/tools/rustdoc-js/.eslintrc.js ../src/tools/rustdoc-js/tester.js && \ eslint -c ../src/tools/rustdoc-gui/.eslintrc.js ../src/tools/rustdoc-gui/tester.js diff --git a/src/librustdoc/html/static/.eslintrc.js b/src/librustdoc/html/static/.eslintrc.js index 1eeda5a6ea4c..fbb096fe9c75 100644 --- a/src/librustdoc/html/static/.eslintrc.js +++ b/src/librustdoc/html/static/.eslintrc.js @@ -5,7 +5,7 @@ module.exports = { }, "extends": "eslint:recommended", "parserOptions": { - "ecmaVersion": 9, + "ecmaVersion": 2019, "sourceType": "module" }, "rules": { diff --git a/src/tools/rustdoc-gui/.eslintrc.js b/src/tools/rustdoc-gui/.eslintrc.js index f4aadc071998..3eccbc74cb95 100644 --- a/src/tools/rustdoc-gui/.eslintrc.js +++ b/src/tools/rustdoc-gui/.eslintrc.js @@ -6,7 +6,7 @@ module.exports = { }, "extends": "eslint:recommended", "parserOptions": { - "ecmaVersion": 2018, + "ecmaVersion": 2019, "sourceType": "module" }, "rules": { diff --git a/src/tools/rustdoc-js/.eslintrc.js b/src/tools/rustdoc-js/.eslintrc.js index b9d0e251c242..3eccbc74cb95 100644 --- a/src/tools/rustdoc-js/.eslintrc.js +++ b/src/tools/rustdoc-js/.eslintrc.js @@ -6,7 +6,7 @@ module.exports = { }, "extends": "eslint:recommended", "parserOptions": { - "ecmaVersion": 8, + "ecmaVersion": 2019, "sourceType": "module" }, "rules": { From cddf291a254fd36a51af235f29c129772656d69f Mon Sep 17 00:00:00 2001 From: Veera Date: Thu, 6 Jun 2024 17:23:59 -0400 Subject: [PATCH 32/92] Improve Docs for `hir::Impl` and `hir::ImplItem` --- compiler/rustc_hir/src/hir.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 770dfcb98c91..29bc87eae7c3 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -2345,7 +2345,9 @@ impl ImplItemId { } } -/// Represents anything within an `impl` block. +/// Represents an associated item within an impl block. +/// +/// Refer to [`Impl`] for an impl block declaration. #[derive(Debug, Clone, Copy, HashStable_Generic)] pub struct ImplItem<'hir> { pub ident: Ident, @@ -3327,6 +3329,10 @@ pub enum ItemKind<'hir> { Impl(&'hir Impl<'hir>), } +/// Represents an impl block declaration. +/// +/// E.g., `impl $Type { .. }` or `impl $Trait for $Type { .. }` +/// Refer to [`ImplItem`] for an associated item within an impl block. #[derive(Debug, Clone, Copy, HashStable_Generic)] pub struct Impl<'hir> { pub safety: Safety, From 67f5dd1ef112c8a87bc3353793cf4c1cece10c2d Mon Sep 17 00:00:00 2001 From: carbotaniuman <41451839+carbotaniuman@users.noreply.github.com> Date: Sat, 20 Apr 2024 23:54:50 -0500 Subject: [PATCH 33/92] Parse unsafe attributes --- compiler/rustc_ast/src/ast.rs | 9 ++- compiler/rustc_ast/src/attr/mod.rs | 33 ++++++++--- compiler/rustc_ast/src/mut_visit.rs | 10 ++-- compiler/rustc_ast_lowering/src/expr.rs | 1 + compiler/rustc_ast_lowering/src/lib.rs | 1 + compiler/rustc_ast_passes/src/feature_gate.rs | 1 + compiler/rustc_ast_pretty/src/pprust/state.rs | 6 +- .../rustc_builtin_macros/src/cmdline_attrs.rs | 3 +- .../rustc_builtin_macros/src/test_harness.rs | 1 + compiler/rustc_expand/src/build.rs | 6 +- compiler/rustc_expand/src/expand.rs | 8 ++- compiler/rustc_feature/src/builtin_attrs.rs | 56 ++++++++++++++++++- compiler/rustc_feature/src/unstable.rs | 1 + compiler/rustc_parse/src/parser/attr.rs | 35 +++++++++++- compiler/rustc_parse/src/validate_attr.rs | 1 + compiler/rustc_span/src/symbol.rs | 1 + src/librustdoc/clean/cfg/tests.rs | 6 +- .../feature-gate-unsafe-attributes.rs | 8 +++ .../feature-gate-unsafe-attributes.stderr | 13 +++++ 19 files changed, 173 insertions(+), 27 deletions(-) create mode 100644 tests/ui/feature-gates/feature-gate-unsafe-attributes.rs create mode 100644 tests/ui/feature-gates/feature-gate-unsafe-attributes.stderr diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 910fbb876974..8cb49a1b7604 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -488,6 +488,7 @@ pub struct Crate { /// E.g., `#[test]`, `#[derive(..)]`, `#[rustfmt::skip]` or `#[feature = "foo"]`. #[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)] pub struct MetaItem { + pub unsafety: Unsafe, pub path: Path, pub kind: MetaItemKind, pub span: Span, @@ -2823,7 +2824,12 @@ pub struct NormalAttr { impl NormalAttr { pub fn from_ident(ident: Ident) -> Self { Self { - item: AttrItem { path: Path::from_ident(ident), args: AttrArgs::Empty, tokens: None }, + item: AttrItem { + unsafety: Unsafe::No, + path: Path::from_ident(ident), + args: AttrArgs::Empty, + tokens: None, + }, tokens: None, } } @@ -2831,6 +2837,7 @@ impl NormalAttr { #[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)] pub struct AttrItem { + pub unsafety: Unsafe, pub path: Path, pub args: AttrArgs, // Tokens for the meta item, e.g. just the `foo` within `#[foo]` or `#![foo]`. diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs index d5c9fc960c86..62b7a2762194 100644 --- a/compiler/rustc_ast/src/attr/mod.rs +++ b/compiler/rustc_ast/src/attr/mod.rs @@ -1,6 +1,8 @@ //! Functions dealing with attributes and meta items. -use crate::ast::{AttrArgs, AttrArgsEq, AttrId, AttrItem, AttrKind, AttrStyle, AttrVec, Attribute}; +use crate::ast::{ + AttrArgs, AttrArgsEq, AttrId, AttrItem, AttrKind, AttrStyle, AttrVec, Attribute, Unsafe, +}; use crate::ast::{DelimArgs, Expr, ExprKind, LitKind, MetaItemLit}; use crate::ast::{MetaItem, MetaItemKind, NestedMetaItem, NormalAttr}; use crate::ast::{Path, PathSegment, DUMMY_NODE_ID}; @@ -238,7 +240,12 @@ impl AttrItem { } pub fn meta(&self, span: Span) -> Option { - Some(MetaItem { path: self.path.clone(), kind: self.meta_kind()?, span }) + Some(MetaItem { + unsafety: Unsafe::No, + path: self.path.clone(), + kind: self.meta_kind()?, + span, + }) } pub fn meta_kind(&self) -> Option { @@ -371,7 +378,8 @@ impl MetaItem { _ => path.span.hi(), }; let span = path.span.with_hi(hi); - Some(MetaItem { path, kind, span }) + // FIX THIS LATER + Some(MetaItem { unsafety: Unsafe::No, path, kind, span }) } } @@ -555,11 +563,12 @@ pub fn mk_doc_comment( pub fn mk_attr( g: &AttrIdGenerator, style: AttrStyle, + unsafety: Unsafe, path: Path, args: AttrArgs, span: Span, ) -> Attribute { - mk_attr_from_item(g, AttrItem { path, args, tokens: None }, None, style, span) + mk_attr_from_item(g, AttrItem { unsafety, path, args, tokens: None }, None, style, span) } pub fn mk_attr_from_item( @@ -577,15 +586,22 @@ pub fn mk_attr_from_item( } } -pub fn mk_attr_word(g: &AttrIdGenerator, style: AttrStyle, name: Symbol, span: Span) -> Attribute { +pub fn mk_attr_word( + g: &AttrIdGenerator, + style: AttrStyle, + unsafety: Unsafe, + name: Symbol, + span: Span, +) -> Attribute { let path = Path::from_ident(Ident::new(name, span)); let args = AttrArgs::Empty; - mk_attr(g, style, path, args, span) + mk_attr(g, style, unsafety, path, args, span) } pub fn mk_attr_nested_word( g: &AttrIdGenerator, style: AttrStyle, + unsafety: Unsafe, outer: Symbol, inner: Symbol, span: Span, @@ -601,12 +617,13 @@ pub fn mk_attr_nested_word( delim: Delimiter::Parenthesis, tokens: inner_tokens, }); - mk_attr(g, style, path, attr_args, span) + mk_attr(g, style, unsafety, path, attr_args, span) } pub fn mk_attr_name_value_str( g: &AttrIdGenerator, style: AttrStyle, + unsafety: Unsafe, name: Symbol, val: Symbol, span: Span, @@ -621,7 +638,7 @@ pub fn mk_attr_name_value_str( }); let path = Path::from_ident(Ident::new(name, span)); let args = AttrArgs::Eq(span, AttrArgsEq::Ast(expr)); - mk_attr(g, style, path, args, span) + mk_attr(g, style, unsafety, path, args, span) } pub fn filter_by_name(attrs: &[Attribute], name: Symbol) -> impl Iterator { diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index a04c648ac731..dfe82309531b 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -647,8 +647,10 @@ fn noop_visit_attribute(attr: &mut Attribute, vis: &mut T) { let Attribute { kind, id: _, style: _, span } = attr; match kind { AttrKind::Normal(normal) => { - let NormalAttr { item: AttrItem { path, args, tokens }, tokens: attr_tokens } = - &mut **normal; + let NormalAttr { + item: AttrItem { unsafety: _, path, args, tokens }, + tokens: attr_tokens, + } = &mut **normal; vis.visit_path(path); visit_attr_args(args, vis); visit_lazy_tts(tokens, vis); @@ -678,7 +680,7 @@ fn noop_visit_meta_list_item(li: &mut NestedMetaItem, vis: &mut T } fn noop_visit_meta_item(mi: &mut MetaItem, vis: &mut T) { - let MetaItem { path: _, kind, span } = mi; + let MetaItem { unsafety: _, path: _, kind, span } = mi; match kind { MetaItemKind::Word => {} MetaItemKind::List(mis) => visit_thin_vec(mis, |mi| vis.visit_meta_list_item(mi)), @@ -840,7 +842,7 @@ fn visit_nonterminal(nt: &mut token::Nonterminal, vis: &mut T) { token::NtTy(ty) => vis.visit_ty(ty), token::NtLiteral(expr) => vis.visit_expr(expr), token::NtMeta(item) => { - let AttrItem { path, args, tokens } = item.deref_mut(); + let AttrItem { unsafety: _, path, args, tokens } = item.deref_mut(); vis.visit_path(path); visit_attr_args(args, vis); visit_lazy_tts(tokens, vis); diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index eb206a09be31..5fbae6a34e5d 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -1801,6 +1801,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let attr = attr::mk_attr_nested_word( &self.tcx.sess.psess.attr_id_generator, AttrStyle::Outer, + Unsafe::No, sym::allow, sym::unreachable_code, self.lower_span(span), diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 023dc6d52c3e..e4959853d3f4 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -911,6 +911,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let kind = match attr.kind { AttrKind::Normal(ref normal) => AttrKind::Normal(P(NormalAttr { item: AttrItem { + unsafety: normal.item.unsafety, path: normal.item.path.clone(), args: self.lower_attr_args(&normal.item.args), tokens: None, diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index a9dca9b6a293..764d942836c2 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -561,6 +561,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { gate_all!(mut_ref, "mutable by-reference bindings are experimental"); gate_all!(precise_capturing, "precise captures on `impl Trait` are experimental"); gate_all!(global_registration, "global registration is experimental"); + gate_all!(unsafe_attributes, "`#[unsafe()]` markers for attributes are experimental"); if !visitor.features.never_patterns { if let Some(spans) = spans.get(&sym::never_patterns) { diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index ca26b436b82e..e3ccbbe391e0 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -16,7 +16,7 @@ use rustc_ast::token::{self, BinOpToken, CommentKind, Delimiter, Nonterminal, To use rustc_ast::tokenstream::{Spacing, TokenStream, TokenTree}; use rustc_ast::util::classify; use rustc_ast::util::comments::{Comment, CommentStyle}; -use rustc_ast::{self as ast, AttrArgs, AttrArgsEq, BlockCheckMode, PatKind}; +use rustc_ast::{self as ast, AttrArgs, AttrArgsEq, BlockCheckMode, PatKind, Unsafe}; use rustc_ast::{attr, BindingMode, ByRef, DelimArgs, RangeEnd, RangeSyntax, Term}; use rustc_ast::{GenericArg, GenericBound, SelfKind}; use rustc_ast::{InlineAsmOperand, InlineAsmRegOrRegClass}; @@ -249,6 +249,7 @@ pub fn print_crate<'a>( let fake_attr = attr::mk_attr_nested_word( g, ast::AttrStyle::Inner, + Unsafe::No, sym::feature, sym::prelude_import, DUMMY_SP, @@ -259,7 +260,8 @@ pub fn print_crate<'a>( // root, so this is not needed, and actually breaks things. if edition.is_rust_2015() { // `#![no_std]` - let fake_attr = attr::mk_attr_word(g, ast::AttrStyle::Inner, sym::no_std, DUMMY_SP); + let fake_attr = + attr::mk_attr_word(g, ast::AttrStyle::Inner, Unsafe::No, sym::no_std, DUMMY_SP); s.print_attribute(&fake_attr); } } diff --git a/compiler/rustc_builtin_macros/src/cmdline_attrs.rs b/compiler/rustc_builtin_macros/src/cmdline_attrs.rs index e9b63b4abebb..16184ec75113 100644 --- a/compiler/rustc_builtin_macros/src/cmdline_attrs.rs +++ b/compiler/rustc_builtin_macros/src/cmdline_attrs.rs @@ -17,7 +17,7 @@ pub fn inject(krate: &mut ast::Crate, psess: &ParseSess, attrs: &[String]) { )); let start_span = parser.token.span; - let AttrItem { path, args, tokens: _ } = match parser.parse_attr_item(false) { + let AttrItem { unsafety, path, args, tokens: _ } = match parser.parse_attr_item(false) { Ok(ai) => ai, Err(err) => { err.emit(); @@ -33,6 +33,7 @@ pub fn inject(krate: &mut ast::Crate, psess: &ParseSess, attrs: &[String]) { krate.attrs.push(mk_attr( &psess.attr_id_generator, AttrStyle::Inner, + unsafety, path, args, start_span.to(end_span), diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs index 38ac2f15fe75..fec54cfe38fb 100644 --- a/compiler/rustc_builtin_macros/src/test_harness.rs +++ b/compiler/rustc_builtin_macros/src/test_harness.rs @@ -203,6 +203,7 @@ impl<'a> MutVisitor for EntryPointCleaner<'a> { let allow_dead_code = attr::mk_attr_nested_word( &self.sess.psess.attr_id_generator, ast::AttrStyle::Outer, + ast::Unsafe::No, sym::allow, sym::dead_code, self.def_site, diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs index b3d41908260e..9451102c010b 100644 --- a/compiler/rustc_expand/src/build.rs +++ b/compiler/rustc_expand/src/build.rs @@ -666,7 +666,7 @@ impl<'a> ExtCtxt<'a> { // Builds `#[name]`. pub fn attr_word(&self, name: Symbol, span: Span) -> ast::Attribute { let g = &self.sess.psess.attr_id_generator; - attr::mk_attr_word(g, ast::AttrStyle::Outer, name, span) + attr::mk_attr_word(g, ast::AttrStyle::Outer, ast::Unsafe::No, name, span) } // Builds `#[name = val]`. @@ -674,12 +674,12 @@ impl<'a> ExtCtxt<'a> { // Note: `span` is used for both the identifier and the value. pub fn attr_name_value_str(&self, name: Symbol, val: Symbol, span: Span) -> ast::Attribute { let g = &self.sess.psess.attr_id_generator; - attr::mk_attr_name_value_str(g, ast::AttrStyle::Outer, name, val, span) + attr::mk_attr_name_value_str(g, ast::AttrStyle::Outer, ast::Unsafe::No, name, val, span) } // Builds `#[outer(inner)]`. pub fn attr_nested_word(&self, outer: Symbol, inner: Symbol, span: Span) -> ast::Attribute { let g = &self.sess.psess.attr_id_generator; - attr::mk_attr_nested_word(g, ast::AttrStyle::Outer, outer, inner, span) + attr::mk_attr_nested_word(g, ast::AttrStyle::Outer, ast::Unsafe::No, outer, inner, span) } } diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index d8f0f221189a..77cb617eb33a 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -778,7 +778,13 @@ impl<'a, 'b> MacroExpander<'a, 'b> { if let SyntaxExtensionKind::Derive(..) = ext { self.gate_proc_macro_input(&item); } - let meta = ast::MetaItem { kind: MetaItemKind::Word, span, path }; + // FIX THIS LATER + let meta = ast::MetaItem { + unsafety: ast::Unsafe::No, + kind: MetaItemKind::Word, + span, + path, + }; let items = match expander.expand(self.cx, span, &meta, item, is_const) { ExpandResult::Ready(items) => items, ExpandResult::Retry(item) => { diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 8b7e93fd5558..edfda9bf066b 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -59,6 +59,17 @@ pub enum AttributeType { CrateLevel, } +#[derive(Copy, Clone, PartialEq, Debug)] +#[allow(dead_code)] +pub enum AttributeSafety { + /// Normal attribute that does not need `#[unsafe(...)]` + Normal, + + /// Unsafe attribute that requires safety obligations + /// to be discharged + Unsafe, +} + #[derive(Clone, Copy)] pub enum AttributeGate { /// Is gated by a given feature gate, reason @@ -177,6 +188,18 @@ macro_rules! ungated { name: sym::$attr, encode_cross_crate: $encode_cross_crate, type_: $typ, + safety: AttributeSafety::Normal, + template: $tpl, + gate: Ungated, + duplicates: $duplicates, + } + }; + (unsafe $attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr $(,)?) => { + BuiltinAttribute { + name: sym::$attr, + encode_cross_crate: $encode_cross_crate, + type_: $typ, + safety: AttributeSafety::Unsafe, template: $tpl, gate: Ungated, duplicates: $duplicates, @@ -190,6 +213,7 @@ macro_rules! gated { name: sym::$attr, encode_cross_crate: $encode_cross_crate, type_: $typ, + safety: AttributeSafety::Normal, template: $tpl, duplicates: $duplicates, gate: Gated(Stability::Unstable, sym::$gate, $msg, cfg_fn!($gate)), @@ -200,6 +224,29 @@ macro_rules! gated { name: sym::$attr, encode_cross_crate: $encode_cross_crate, type_: $typ, + safety: AttributeSafety::Normal, + template: $tpl, + duplicates: $duplicates, + gate: Gated(Stability::Unstable, sym::$attr, $msg, cfg_fn!($attr)), + } + }; + (unsafe $attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr, $gate:ident, $msg:expr $(,)?) => { + BuiltinAttribute { + name: sym::$attr, + encode_cross_crate: $encode_cross_crate, + type_: $typ, + safety: AttributeSafety::Unsafe, + template: $tpl, + duplicates: $duplicates, + gate: Gated(Stability::Unstable, sym::$gate, $msg, cfg_fn!($gate)), + } + }; + (unsafe $attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr, $msg:expr $(,)?) => { + BuiltinAttribute { + name: sym::$attr, + encode_cross_crate: $encode_cross_crate, + type_: $typ, + safety: AttributeSafety::Unsafe, template: $tpl, duplicates: $duplicates, gate: Gated(Stability::Unstable, sym::$attr, $msg, cfg_fn!($attr)), @@ -228,6 +275,7 @@ macro_rules! rustc_attr { name: sym::$attr, encode_cross_crate: $encode_cross_crate, type_: $typ, + safety: AttributeSafety::Normal, template: $tpl, duplicates: $duplicates, gate: Gated(Stability::Unstable, sym::rustc_attrs, $msg, cfg_fn!(rustc_attrs)), @@ -258,6 +306,7 @@ pub struct BuiltinAttribute { /// Otherwise, it can only be used in the local crate. pub encode_cross_crate: EncodeCrossCrate, pub type_: AttributeType, + pub safety: AttributeSafety, pub template: AttributeTemplate, pub duplicates: AttributeDuplicates, pub gate: AttributeGate, @@ -375,9 +424,9 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ ), ungated!(no_link, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No), ungated!(repr, Normal, template!(List: "C"), DuplicatesOk, EncodeCrossCrate::No), - ungated!(export_name, Normal, template!(NameValueStr: "name"), FutureWarnPreceding, EncodeCrossCrate::No), - ungated!(link_section, Normal, template!(NameValueStr: "name"), FutureWarnPreceding, EncodeCrossCrate::No), - ungated!(no_mangle, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No), + ungated!(unsafe export_name, Normal, template!(NameValueStr: "name"), FutureWarnPreceding, EncodeCrossCrate::No), + ungated!(unsafe link_section, Normal, template!(NameValueStr: "name"), FutureWarnPreceding, EncodeCrossCrate::No), + ungated!(unsafe no_mangle, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No), ungated!(used, Normal, template!(Word, List: "compiler|linker"), WarnFollowing, EncodeCrossCrate::No), ungated!(link_ordinal, Normal, template!(List: "ordinal"), ErrorPreceding, EncodeCrossCrate::Yes), @@ -850,6 +899,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ // FIXME: This can be `true` once we always use `tcx.is_diagnostic_item`. encode_cross_crate: EncodeCrossCrate::Yes, type_: Normal, + safety: AttributeSafety::Normal, template: template!(NameValueStr: "name"), duplicates: ErrorFollowing, gate: Gated( diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 8add2b927614..83a50f017d3b 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -623,6 +623,7 @@ declare_features! ( /// Allows unsafe on extern declarations and safety qualifiers over internal items. (unstable, unsafe_extern_blocks, "CURRENT_RUSTC_VERSION", Some(123743)), /// Allows unsized fn parameters. + (unstable, unsafe_attributes, "CURRENT_RUSTC_VERSION", Some(123757)), (unstable, unsized_fn_params, "1.49.0", Some(48055)), /// Allows unsized rvalues at arguments and parameters. (incomplete, unsized_locals, "1.30.0", Some(48055)), diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs index 4acc610d8c40..2dd19f6c20af 100644 --- a/compiler/rustc_parse/src/parser/attr.rs +++ b/compiler/rustc_parse/src/parser/attr.rs @@ -7,7 +7,7 @@ use rustc_ast as ast; use rustc_ast::attr; use rustc_ast::token::{self, Delimiter}; use rustc_errors::{codes::*, Diag, PResult}; -use rustc_span::{sym, BytePos, Span}; +use rustc_span::{sym, symbol::kw, BytePos, Span}; use thin_vec::ThinVec; use tracing::debug; @@ -252,9 +252,23 @@ impl<'a> Parser<'a> { maybe_whole!(self, NtMeta, |attr| attr.into_inner()); let do_parse = |this: &mut Self| { + let is_unsafe = this.eat_keyword(kw::Unsafe); + let unsafety = if is_unsafe { + let unsafe_span = this.prev_token.span; + this.psess.gated_spans.gate(sym::unsafe_attributes, unsafe_span); + this.expect(&token::OpenDelim(Delimiter::Parenthesis))?; + + ast::Unsafe::Yes(unsafe_span) + } else { + ast::Unsafe::No + }; + let path = this.parse_path(PathStyle::Mod)?; let args = this.parse_attr_args()?; - Ok(ast::AttrItem { path, args, tokens: None }) + if is_unsafe { + this.expect(&token::CloseDelim(Delimiter::Parenthesis))?; + } + Ok(ast::AttrItem { unsafety, path, args, tokens: None }) }; // Attr items don't have attributes if capture_tokens { self.collect_tokens_no_attrs(do_parse) } else { do_parse(self) } @@ -375,10 +389,25 @@ impl<'a> Parser<'a> { } let lo = self.token.span; + let is_unsafe = self.eat_keyword(kw::Unsafe); + let unsafety = if is_unsafe { + let unsafe_span = self.prev_token.span; + self.psess.gated_spans.gate(sym::unsafe_attributes, unsafe_span); + self.expect(&token::OpenDelim(Delimiter::Parenthesis))?; + + ast::Unsafe::Yes(unsafe_span) + } else { + ast::Unsafe::No + }; + let path = self.parse_path(PathStyle::Mod)?; let kind = self.parse_meta_item_kind()?; + if is_unsafe { + self.expect(&token::CloseDelim(Delimiter::Parenthesis))?; + } let span = lo.to(self.prev_token.span); - Ok(ast::MetaItem { path, kind, span }) + + Ok(ast::MetaItem { unsafety, path, kind, span }) } pub(crate) fn parse_meta_item_kind(&mut self) -> PResult<'a, ast::MetaItemKind> { diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs index b91ef1ae1f32..58342ff82907 100644 --- a/compiler/rustc_parse/src/validate_attr.rs +++ b/compiler/rustc_parse/src/validate_attr.rs @@ -42,6 +42,7 @@ pub fn check_attr(psess: &ParseSess, attr: &Attribute) { pub fn parse_meta<'a>(psess: &'a ParseSess, attr: &Attribute) -> PResult<'a, MetaItem> { let item = attr.get_normal_item(); Ok(MetaItem { + unsafety: item.unsafety, span: attr.span, path: item.path.clone(), kind: match &item.args { diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index f530d1dd1d4b..935942641677 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1962,6 +1962,7 @@ symbols! { unreachable_display, unreachable_macro, unrestricted_attribute_tokens, + unsafe_attributes, unsafe_block_in_unsafe_fn, unsafe_cell, unsafe_cell_raw_get, diff --git a/src/librustdoc/clean/cfg/tests.rs b/src/librustdoc/clean/cfg/tests.rs index 20bcf1abf417..2579c15fc5fc 100644 --- a/src/librustdoc/clean/cfg/tests.rs +++ b/src/librustdoc/clean/cfg/tests.rs @@ -1,6 +1,6 @@ use super::*; -use rustc_ast::{MetaItemLit, Path, StrStyle}; +use rustc_ast::{MetaItemLit, Path, StrStyle, Unsafe}; use rustc_span::create_default_session_globals_then; use rustc_span::symbol::{kw, Ident}; use rustc_span::DUMMY_SP; @@ -16,6 +16,7 @@ fn name_value_cfg(name: &str, value: &str) -> Cfg { fn dummy_meta_item_word(name: &str) -> MetaItem { MetaItem { + unsafety: Unsafe::No, path: Path::from_ident(Ident::from_str(name)), kind: MetaItemKind::Word, span: DUMMY_SP, @@ -25,6 +26,7 @@ fn dummy_meta_item_word(name: &str) -> MetaItem { fn dummy_meta_item_name_value(name: &str, symbol: Symbol, kind: LitKind) -> MetaItem { let lit = MetaItemLit { symbol, suffix: None, kind, span: DUMMY_SP }; MetaItem { + unsafety: Unsafe::No, path: Path::from_ident(Ident::from_str(name)), kind: MetaItemKind::NameValue(lit), span: DUMMY_SP, @@ -34,6 +36,7 @@ fn dummy_meta_item_name_value(name: &str, symbol: Symbol, kind: LitKind) -> Meta macro_rules! dummy_meta_item_list { ($name:ident, [$($list:ident),* $(,)?]) => { MetaItem { + unsafety: Unsafe::No, path: Path::from_ident(Ident::from_str(stringify!($name))), kind: MetaItemKind::List(thin_vec![ $( @@ -48,6 +51,7 @@ macro_rules! dummy_meta_item_list { ($name:ident, [$($list:expr),* $(,)?]) => { MetaItem { + unsafety: Unsafe::No, path: Path::from_ident(Ident::from_str(stringify!($name))), kind: MetaItemKind::List(thin_vec![ $( diff --git a/tests/ui/feature-gates/feature-gate-unsafe-attributes.rs b/tests/ui/feature-gates/feature-gate-unsafe-attributes.rs new file mode 100644 index 000000000000..9eba415dda0e --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-unsafe-attributes.rs @@ -0,0 +1,8 @@ +#[unsafe(no_mangle)] //~ ERROR [E0658] +extern "C" fn foo() { + +} + +fn main() { + foo(); +} diff --git a/tests/ui/feature-gates/feature-gate-unsafe-attributes.stderr b/tests/ui/feature-gates/feature-gate-unsafe-attributes.stderr new file mode 100644 index 000000000000..dfcea756b02b --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-unsafe-attributes.stderr @@ -0,0 +1,13 @@ +error[E0658]: `#[unsafe()]` markers for attributes are experimental + --> $DIR/feature-gate-unsafe-attributes.rs:1:3 + | +LL | #[unsafe(no_mangle)] + | ^^^^^^ + | + = note: see issue #123757 for more information + = help: add `#![feature(unsafe_attributes)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0658`. From c4de986afa792fa910f255f6315c50b82275abf9 Mon Sep 17 00:00:00 2001 From: carbotaniuman <41451839+carbotaniuman@users.noreply.github.com> Date: Tue, 23 Apr 2024 07:50:53 -0500 Subject: [PATCH 34/92] Disallow unsafe in derive --- compiler/rustc_builtin_macros/messages.ftl | 3 +++ compiler/rustc_builtin_macros/src/derive.rs | 12 +++++++++++- compiler/rustc_builtin_macros/src/errors.rs | 7 +++++++ compiler/rustc_expand/src/expand.rs | 3 ++- .../ui/attributes/unsafe/derive-unsafe-attributes.rs | 6 ++++++ .../unsafe/derive-unsafe-attributes.stderr | 8 ++++++++ 6 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 tests/ui/attributes/unsafe/derive-unsafe-attributes.rs create mode 100644 tests/ui/attributes/unsafe/derive-unsafe-attributes.stderr diff --git a/compiler/rustc_builtin_macros/messages.ftl b/compiler/rustc_builtin_macros/messages.ftl index a3d6a1c73607..2d1269e1b6ad 100644 --- a/compiler/rustc_builtin_macros/messages.ftl +++ b/compiler/rustc_builtin_macros/messages.ftl @@ -110,6 +110,9 @@ builtin_macros_derive_path_args_list = traits in `#[derive(...)]` don't accept a builtin_macros_derive_path_args_value = traits in `#[derive(...)]` don't accept values .suggestion = remove the value +builtin_macros_derive_unsafe_path = traits in `#[derive(...)]` don't accept `unsafe(...)` + .suggestion = remove the `unsafe(...)` + builtin_macros_env_not_defined = environment variable `{$var}` not defined at compile time .cargo = Cargo sets build script variables at run time. Use `std::env::var({$var_expr})` instead .custom = use `std::env::var({$var_expr})` to read the variable at run time diff --git a/compiler/rustc_builtin_macros/src/derive.rs b/compiler/rustc_builtin_macros/src/derive.rs index d14858e5c1db..d70eaa617ca8 100644 --- a/compiler/rustc_builtin_macros/src/derive.rs +++ b/compiler/rustc_builtin_macros/src/derive.rs @@ -2,7 +2,7 @@ use crate::cfg_eval::cfg_eval; use crate::errors; use rustc_ast as ast; -use rustc_ast::{GenericParamKind, ItemKind, MetaItemKind, NestedMetaItem, StmtKind}; +use rustc_ast::{GenericParamKind, ItemKind, MetaItemKind, NestedMetaItem, StmtKind, Unsafe}; use rustc_expand::base::{ Annotatable, DeriveResolution, ExpandResult, ExtCtxt, Indeterminate, MultiItemModifier, }; @@ -60,6 +60,7 @@ impl MultiItemModifier for Expander { // Reject `#[derive(Debug = "value", Debug(abc))]`, but recover the // paths. report_path_args(sess, meta); + report_unsafe_args(sess, meta); meta.path.clone() }) .map(|path| DeriveResolution { @@ -159,3 +160,12 @@ fn report_path_args(sess: &Session, meta: &ast::MetaItem) { } } } + +fn report_unsafe_args(sess: &Session, meta: &ast::MetaItem) { + match meta.unsafety { + Unsafe::Yes(span) => { + sess.dcx().emit_err(errors::DeriveUnsafePath { span }); + } + Unsafe::No => {} + } +} diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs index d157703723be..b14eb2b5ee60 100644 --- a/compiler/rustc_builtin_macros/src/errors.rs +++ b/compiler/rustc_builtin_macros/src/errors.rs @@ -295,6 +295,13 @@ pub(crate) struct DerivePathArgsValue { pub(crate) span: Span, } +#[derive(Diagnostic)] +#[diag(builtin_macros_derive_unsafe_path)] +pub(crate) struct DeriveUnsafePath { + #[primary_span] + pub(crate) span: Span, +} + #[derive(Diagnostic)] #[diag(builtin_macros_no_default_variant)] #[help] diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 77cb617eb33a..dfef2540d29d 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -778,7 +778,8 @@ impl<'a, 'b> MacroExpander<'a, 'b> { if let SyntaxExtensionKind::Derive(..) = ext { self.gate_proc_macro_input(&item); } - // FIX THIS LATER + // The `MetaItem` representing the trait to derive can't + // have an unsafe around it (as of now). let meta = ast::MetaItem { unsafety: ast::Unsafe::No, kind: MetaItemKind::Word, diff --git a/tests/ui/attributes/unsafe/derive-unsafe-attributes.rs b/tests/ui/attributes/unsafe/derive-unsafe-attributes.rs new file mode 100644 index 000000000000..774ce86c0960 --- /dev/null +++ b/tests/ui/attributes/unsafe/derive-unsafe-attributes.rs @@ -0,0 +1,6 @@ +#![feature(unsafe_attributes)] + +#[derive(unsafe(Debug))] //~ ERROR: traits in `#[derive(...)]` don't accept `unsafe(...)` +struct Foo; + +fn main() {} diff --git a/tests/ui/attributes/unsafe/derive-unsafe-attributes.stderr b/tests/ui/attributes/unsafe/derive-unsafe-attributes.stderr new file mode 100644 index 000000000000..fc0daf16790a --- /dev/null +++ b/tests/ui/attributes/unsafe/derive-unsafe-attributes.stderr @@ -0,0 +1,8 @@ +error: traits in `#[derive(...)]` don't accept `unsafe(...)` + --> $DIR/derive-unsafe-attributes.rs:3:10 + | +LL | #[derive(unsafe(Debug))] + | ^^^^^^ + +error: aborting due to 1 previous error + From 6ec492fddea96012db4ec8473de3bb27718a25b3 Mon Sep 17 00:00:00 2001 From: carbotaniuman <41451839+carbotaniuman@users.noreply.github.com> Date: Thu, 25 Apr 2024 11:12:02 -0500 Subject: [PATCH 35/92] Reorder `unsafe` macro cases --- compiler/rustc_feature/src/builtin_attrs.rs | 66 ++++++++++----------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index edfda9bf066b..8fa0c30173ef 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -183,17 +183,6 @@ macro_rules! template { } macro_rules! ungated { - ($attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr $(,)?) => { - BuiltinAttribute { - name: sym::$attr, - encode_cross_crate: $encode_cross_crate, - type_: $typ, - safety: AttributeSafety::Normal, - template: $tpl, - gate: Ungated, - duplicates: $duplicates, - } - }; (unsafe $attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr $(,)?) => { BuiltinAttribute { name: sym::$attr, @@ -205,31 +194,20 @@ macro_rules! ungated { duplicates: $duplicates, } }; + ($attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr $(,)?) => { + BuiltinAttribute { + name: sym::$attr, + encode_cross_crate: $encode_cross_crate, + type_: $typ, + safety: AttributeSafety::Normal, + template: $tpl, + gate: Ungated, + duplicates: $duplicates, + } + }; } macro_rules! gated { - ($attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr, $gate:ident, $msg:expr $(,)?) => { - BuiltinAttribute { - name: sym::$attr, - encode_cross_crate: $encode_cross_crate, - type_: $typ, - safety: AttributeSafety::Normal, - template: $tpl, - duplicates: $duplicates, - gate: Gated(Stability::Unstable, sym::$gate, $msg, cfg_fn!($gate)), - } - }; - ($attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr, $msg:expr $(,)?) => { - BuiltinAttribute { - name: sym::$attr, - encode_cross_crate: $encode_cross_crate, - type_: $typ, - safety: AttributeSafety::Normal, - template: $tpl, - duplicates: $duplicates, - gate: Gated(Stability::Unstable, sym::$attr, $msg, cfg_fn!($attr)), - } - }; (unsafe $attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr, $gate:ident, $msg:expr $(,)?) => { BuiltinAttribute { name: sym::$attr, @@ -252,6 +230,28 @@ macro_rules! gated { gate: Gated(Stability::Unstable, sym::$attr, $msg, cfg_fn!($attr)), } }; + ($attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr, $gate:ident, $msg:expr $(,)?) => { + BuiltinAttribute { + name: sym::$attr, + encode_cross_crate: $encode_cross_crate, + type_: $typ, + safety: AttributeSafety::Normal, + template: $tpl, + duplicates: $duplicates, + gate: Gated(Stability::Unstable, sym::$gate, $msg, cfg_fn!($gate)), + } + }; + ($attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr, $msg:expr $(,)?) => { + BuiltinAttribute { + name: sym::$attr, + encode_cross_crate: $encode_cross_crate, + type_: $typ, + safety: AttributeSafety::Normal, + template: $tpl, + duplicates: $duplicates, + gate: Gated(Stability::Unstable, sym::$attr, $msg, cfg_fn!($attr)), + } + }; } macro_rules! rustc_attr { From 48851d9adbd358a678e8ef8102d131aaa42884fc Mon Sep 17 00:00:00 2001 From: carbotaniuman <41451839+carbotaniuman@users.noreply.github.com> Date: Sun, 28 Apr 2024 15:43:07 -0500 Subject: [PATCH 36/92] Add tests --- tests/ui/attributes/unsafe/cfg-unsafe-attributes.rs | 7 +++++++ tests/ui/attributes/unsafe/unsafe-attributes.rs | 7 +++++++ 2 files changed, 14 insertions(+) create mode 100644 tests/ui/attributes/unsafe/cfg-unsafe-attributes.rs create mode 100644 tests/ui/attributes/unsafe/unsafe-attributes.rs diff --git a/tests/ui/attributes/unsafe/cfg-unsafe-attributes.rs b/tests/ui/attributes/unsafe/cfg-unsafe-attributes.rs new file mode 100644 index 000000000000..ce365d1a8b1c --- /dev/null +++ b/tests/ui/attributes/unsafe/cfg-unsafe-attributes.rs @@ -0,0 +1,7 @@ +//@ build-pass +#![feature(unsafe_attributes)] + +#[cfg_attr(all(), unsafe(no_mangle))] +fn a() {} + +fn main() {} diff --git a/tests/ui/attributes/unsafe/unsafe-attributes.rs b/tests/ui/attributes/unsafe/unsafe-attributes.rs new file mode 100644 index 000000000000..e7620a18048e --- /dev/null +++ b/tests/ui/attributes/unsafe/unsafe-attributes.rs @@ -0,0 +1,7 @@ +//@ build-pass +#![feature(unsafe_attributes)] + +#[unsafe(no_mangle)] +fn a() {} + +fn main() {} From 230b58febf78bb1680b4960d32350a7430a8aab5 Mon Sep 17 00:00:00 2001 From: carbotaniuman <41451839+carbotaniuman@users.noreply.github.com> Date: Wed, 15 May 2024 18:52:08 -0500 Subject: [PATCH 37/92] Error on unsafe on non-unsafe attribute --- compiler/rustc_feature/src/builtin_attrs.rs | 4 ++++ compiler/rustc_feature/src/lib.rs | 4 ++-- compiler/rustc_passes/messages.ftl | 4 ++++ compiler/rustc_passes/src/check_attr.rs | 22 ++++++++++++++++++- compiler/rustc_passes/src/errors.rs | 11 +++++++++- .../unsafe/unsafe-safe-attribute.rs | 6 +++++ .../unsafe/unsafe-safe-attribute.stderr | 10 +++++++++ .../unsafe-safe-attribute_diagnostic.rs | 8 +++++++ .../unsafe-safe-attribute_diagnostic.stderr | 10 +++++++++ 9 files changed, 75 insertions(+), 4 deletions(-) create mode 100644 tests/ui/attributes/unsafe/unsafe-safe-attribute.rs create mode 100644 tests/ui/attributes/unsafe/unsafe-safe-attribute.stderr create mode 100644 tests/ui/attributes/unsafe/unsafe-safe-attribute_diagnostic.rs create mode 100644 tests/ui/attributes/unsafe/unsafe-safe-attribute_diagnostic.stderr diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 8fa0c30173ef..f901f5c8c41a 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -1146,6 +1146,10 @@ pub fn is_valid_for_get_attr(name: Symbol) -> bool { }) } +pub fn is_unsafe_attr(name: Symbol) -> bool { + BUILTIN_ATTRIBUTE_MAP.get(&name).is_some_and(|attr| attr.safety == AttributeSafety::Unsafe) +} + pub static BUILTIN_ATTRIBUTE_MAP: LazyLock> = LazyLock::new(|| { let mut map = FxHashMap::default(); diff --git a/compiler/rustc_feature/src/lib.rs b/compiler/rustc_feature/src/lib.rs index 9cbf836ec76f..9db9073e2f02 100644 --- a/compiler/rustc_feature/src/lib.rs +++ b/compiler/rustc_feature/src/lib.rs @@ -123,8 +123,8 @@ pub use accepted::ACCEPTED_FEATURES; pub use builtin_attrs::AttributeDuplicates; pub use builtin_attrs::{ deprecated_attributes, encode_cross_crate, find_gated_cfg, is_builtin_attr_name, - is_valid_for_get_attr, AttributeGate, AttributeTemplate, AttributeType, BuiltinAttribute, - GatedCfg, BUILTIN_ATTRIBUTES, BUILTIN_ATTRIBUTE_MAP, + is_unsafe_attr, is_valid_for_get_attr, AttributeGate, AttributeTemplate, AttributeType, + BuiltinAttribute, GatedCfg, BUILTIN_ATTRIBUTES, BUILTIN_ATTRIBUTE_MAP, }; pub use removed::REMOVED_FEATURES; pub use unstable::{Features, INCOMPATIBLE_FEATURES, UNSTABLE_FEATURES}; diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index d850644bb452..07c82065a80d 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -384,6 +384,10 @@ passes_invalid_attr_at_crate_level = passes_invalid_attr_at_crate_level_item = the inner attribute doesn't annotate this {$kind} +passes_invalid_attr_unsafe = `{$name}` is not an unsafe attribute + .suggestion = remove the `unsafe(...)` + .note = extraneous unsafe is not allowed in attributes + passes_invalid_macro_export_arguments = `{$name}` isn't a valid `#[macro_export]` argument passes_invalid_macro_export_arguments_too_many_items = `#[macro_export]` can only take 1 or 0 arguments diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 39cb48c1af3a..d826eacbd156 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -10,7 +10,9 @@ use rustc_ast::{MetaItemKind, MetaItemLit, NestedMetaItem}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::StashKey; use rustc_errors::{Applicability, DiagCtxt, IntoDiagArg, MultiSpan}; -use rustc_feature::{AttributeDuplicates, AttributeType, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP}; +use rustc_feature::{ + is_unsafe_attr, AttributeDuplicates, AttributeType, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP, +}; use rustc_hir::def_id::LocalModDefId; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{self as hir}; @@ -114,6 +116,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> { let mut seen = FxHashMap::default(); let attrs = self.tcx.hir().attrs(hir_id); for attr in attrs { + self.check_unsafe_attr(attr); + match attr.path().as_slice() { [sym::diagnostic, sym::do_not_recommend] => { self.check_do_not_recommend(attr.span, hir_id, target) @@ -308,6 +312,22 @@ impl<'tcx> CheckAttrVisitor<'tcx> { true } + + /// Checks if `unsafe()` is applied to an invalid attribute. + fn check_unsafe_attr(&self, attr: &Attribute) { + if !attr.is_doc_comment() { + let attr_item = attr.get_normal_item(); + if let ast::Unsafe::Yes(unsafe_span) = attr_item.unsafety { + if !is_unsafe_attr(attr.name_or_empty()) { + self.dcx().emit_err(errors::InvalidAttrUnsafe { + span: unsafe_span, + name: attr_item.path.clone(), + }); + } + } + } + } + /// Checks if `#[diagnostic::on_unimplemented]` is applied to a trait definition fn check_diagnostic_on_unimplemented( &self, diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index 7fdd9924b513..a935f1ad7d35 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -4,7 +4,7 @@ use std::{ }; use crate::fluent_generated as fluent; -use rustc_ast::Label; +use rustc_ast::{ast, Label}; use rustc_errors::{ codes::*, Applicability, Diag, DiagCtxt, DiagSymbolList, Diagnostic, EmissionGuarantee, Level, MultiSpan, SubdiagMessageOp, Subdiagnostic, @@ -863,6 +863,15 @@ pub struct InvalidAttrAtCrateLevel { pub item: Option, } +#[derive(Diagnostic)] +#[diag(passes_invalid_attr_unsafe)] +#[note] +pub struct InvalidAttrUnsafe { + #[primary_span] + pub span: Span, + pub name: ast::Path, +} + #[derive(Clone, Copy)] pub struct ItemFollowingInnerAttr { pub span: Span, diff --git a/tests/ui/attributes/unsafe/unsafe-safe-attribute.rs b/tests/ui/attributes/unsafe/unsafe-safe-attribute.rs new file mode 100644 index 000000000000..67db36afd2e6 --- /dev/null +++ b/tests/ui/attributes/unsafe/unsafe-safe-attribute.rs @@ -0,0 +1,6 @@ +#![feature(unsafe_attributes)] + +#[unsafe(repr(C))] //~ ERROR: is not an unsafe attribute +struct Foo {} + +fn main() {} diff --git a/tests/ui/attributes/unsafe/unsafe-safe-attribute.stderr b/tests/ui/attributes/unsafe/unsafe-safe-attribute.stderr new file mode 100644 index 000000000000..0602af34e4f6 --- /dev/null +++ b/tests/ui/attributes/unsafe/unsafe-safe-attribute.stderr @@ -0,0 +1,10 @@ +error: `repr` is not an unsafe attribute + --> $DIR/unsafe-safe-attribute.rs:3:3 + | +LL | #[unsafe(repr(C))] + | ^^^^^^ + | + = note: extraneous unsafe is not allowed in attributes + +error: aborting due to 1 previous error + diff --git a/tests/ui/attributes/unsafe/unsafe-safe-attribute_diagnostic.rs b/tests/ui/attributes/unsafe/unsafe-safe-attribute_diagnostic.rs new file mode 100644 index 000000000000..ff2eb61b4053 --- /dev/null +++ b/tests/ui/attributes/unsafe/unsafe-safe-attribute_diagnostic.rs @@ -0,0 +1,8 @@ +#![feature(unsafe_attributes)] + +#[unsafe(diagnostic::on_unimplemented( //~ ERROR: is not an unsafe attribute + message = "testing", +))] +trait Foo {} + +fn main() {} diff --git a/tests/ui/attributes/unsafe/unsafe-safe-attribute_diagnostic.stderr b/tests/ui/attributes/unsafe/unsafe-safe-attribute_diagnostic.stderr new file mode 100644 index 000000000000..584dacf4d8c9 --- /dev/null +++ b/tests/ui/attributes/unsafe/unsafe-safe-attribute_diagnostic.stderr @@ -0,0 +1,10 @@ +error: `diagnostic::on_unimplemented` is not an unsafe attribute + --> $DIR/unsafe-safe-attribute_diagnostic.rs:3:3 + | +LL | #[unsafe(diagnostic::on_unimplemented( + | ^^^^^^ + | + = note: extraneous unsafe is not allowed in attributes + +error: aborting due to 1 previous error + From 68719e0bda178103a3e4af0dc1e7df45f1d9dd00 Mon Sep 17 00:00:00 2001 From: carbotaniuman <41451839+carbotaniuman@users.noreply.github.com> Date: Thu, 16 May 2024 01:41:55 -0500 Subject: [PATCH 38/92] Fix doc comment --- compiler/rustc_feature/src/unstable.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 83a50f017d3b..4688624f310b 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -622,8 +622,9 @@ declare_features! ( (incomplete, unnamed_fields, "1.74.0", Some(49804)), /// Allows unsafe on extern declarations and safety qualifiers over internal items. (unstable, unsafe_extern_blocks, "CURRENT_RUSTC_VERSION", Some(123743)), - /// Allows unsized fn parameters. + /// Allows unsafe attributes. (unstable, unsafe_attributes, "CURRENT_RUSTC_VERSION", Some(123757)), + /// Allows unsized fn parameters. (unstable, unsized_fn_params, "1.49.0", Some(48055)), /// Allows unsized rvalues at arguments and parameters. (incomplete, unsized_locals, "1.30.0", Some(48055)), From b82c5249961a6bd3ecacb98c68f9090892ec2615 Mon Sep 17 00:00:00 2001 From: carbotaniuman <41451839+carbotaniuman@users.noreply.github.com> Date: Thu, 16 May 2024 02:08:26 -0500 Subject: [PATCH 39/92] Add double unsafe test --- .../unsafe/double-unsafe-attributes.rs | 9 +++++++ .../unsafe/double-unsafe-attributes.stderr | 27 +++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 tests/ui/attributes/unsafe/double-unsafe-attributes.rs create mode 100644 tests/ui/attributes/unsafe/double-unsafe-attributes.stderr diff --git a/tests/ui/attributes/unsafe/double-unsafe-attributes.rs b/tests/ui/attributes/unsafe/double-unsafe-attributes.rs new file mode 100644 index 000000000000..a6c0ea578f25 --- /dev/null +++ b/tests/ui/attributes/unsafe/double-unsafe-attributes.rs @@ -0,0 +1,9 @@ +#![feature(unsafe_attributes)] + +#[unsafe(unsafe(no_mangle))] +//~^ ERROR expected identifier, found keyword `unsafe` +//~| ERROR cannot find attribute `r#unsafe` in this scope +//~| ERROR `r#unsafe` is not an unsafe attribute +fn a() {} + +fn main() {} diff --git a/tests/ui/attributes/unsafe/double-unsafe-attributes.stderr b/tests/ui/attributes/unsafe/double-unsafe-attributes.stderr new file mode 100644 index 000000000000..1c07a5bf8baa --- /dev/null +++ b/tests/ui/attributes/unsafe/double-unsafe-attributes.stderr @@ -0,0 +1,27 @@ +error: expected identifier, found keyword `unsafe` + --> $DIR/double-unsafe-attributes.rs:3:10 + | +LL | #[unsafe(unsafe(no_mangle))] + | ^^^^^^ expected identifier, found keyword + | +help: escape `unsafe` to use it as an identifier + | +LL | #[unsafe(r#unsafe(no_mangle))] + | ++ + +error: cannot find attribute `r#unsafe` in this scope + --> $DIR/double-unsafe-attributes.rs:3:10 + | +LL | #[unsafe(unsafe(no_mangle))] + | ^^^^^^ + +error: `r#unsafe` is not an unsafe attribute + --> $DIR/double-unsafe-attributes.rs:3:3 + | +LL | #[unsafe(unsafe(no_mangle))] + | ^^^^^^ + | + = note: extraneous unsafe is not allowed in attributes + +error: aborting due to 3 previous errors + From 87be1bae73586eaf917790f10ac2240186cb7b2b Mon Sep 17 00:00:00 2001 From: carbotaniuman <41451839+carbotaniuman@users.noreply.github.com> Date: Tue, 21 May 2024 08:37:05 -0500 Subject: [PATCH 40/92] Fix build --- compiler/rustc_ast/src/ast.rs | 6 +++--- compiler/rustc_ast/src/attr/mod.rs | 14 +++++++------- compiler/rustc_ast_lowering/src/expr.rs | 2 +- compiler/rustc_ast_pretty/src/pprust/state.rs | 6 +++--- compiler/rustc_builtin_macros/src/derive.rs | 6 +++--- compiler/rustc_builtin_macros/src/test_harness.rs | 2 +- compiler/rustc_expand/src/build.rs | 6 +++--- compiler/rustc_expand/src/expand.rs | 2 +- compiler/rustc_parse/src/parser/attr.rs | 8 ++++---- compiler/rustc_passes/src/check_attr.rs | 2 +- src/librustdoc/clean/cfg/tests.rs | 10 +++++----- 11 files changed, 32 insertions(+), 32 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 8cb49a1b7604..957c4d7c698f 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -488,7 +488,7 @@ pub struct Crate { /// E.g., `#[test]`, `#[derive(..)]`, `#[rustfmt::skip]` or `#[feature = "foo"]`. #[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)] pub struct MetaItem { - pub unsafety: Unsafe, + pub unsafety: Safety, pub path: Path, pub kind: MetaItemKind, pub span: Span, @@ -2825,7 +2825,7 @@ impl NormalAttr { pub fn from_ident(ident: Ident) -> Self { Self { item: AttrItem { - unsafety: Unsafe::No, + unsafety: Safety::Default, path: Path::from_ident(ident), args: AttrArgs::Empty, tokens: None, @@ -2837,7 +2837,7 @@ impl NormalAttr { #[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)] pub struct AttrItem { - pub unsafety: Unsafe, + pub unsafety: Safety, pub path: Path, pub args: AttrArgs, // Tokens for the meta item, e.g. just the `foo` within `#[foo]` or `#![foo]`. diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs index 62b7a2762194..ed1b4733820e 100644 --- a/compiler/rustc_ast/src/attr/mod.rs +++ b/compiler/rustc_ast/src/attr/mod.rs @@ -1,7 +1,7 @@ //! Functions dealing with attributes and meta items. use crate::ast::{ - AttrArgs, AttrArgsEq, AttrId, AttrItem, AttrKind, AttrStyle, AttrVec, Attribute, Unsafe, + AttrArgs, AttrArgsEq, AttrId, AttrItem, AttrKind, AttrStyle, AttrVec, Attribute, Safety, }; use crate::ast::{DelimArgs, Expr, ExprKind, LitKind, MetaItemLit}; use crate::ast::{MetaItem, MetaItemKind, NestedMetaItem, NormalAttr}; @@ -241,7 +241,7 @@ impl AttrItem { pub fn meta(&self, span: Span) -> Option { Some(MetaItem { - unsafety: Unsafe::No, + unsafety: Safety::Default, path: self.path.clone(), kind: self.meta_kind()?, span, @@ -379,7 +379,7 @@ impl MetaItem { }; let span = path.span.with_hi(hi); // FIX THIS LATER - Some(MetaItem { unsafety: Unsafe::No, path, kind, span }) + Some(MetaItem { unsafety: Safety::Default, path, kind, span }) } } @@ -563,7 +563,7 @@ pub fn mk_doc_comment( pub fn mk_attr( g: &AttrIdGenerator, style: AttrStyle, - unsafety: Unsafe, + unsafety: Safety, path: Path, args: AttrArgs, span: Span, @@ -589,7 +589,7 @@ pub fn mk_attr_from_item( pub fn mk_attr_word( g: &AttrIdGenerator, style: AttrStyle, - unsafety: Unsafe, + unsafety: Safety, name: Symbol, span: Span, ) -> Attribute { @@ -601,7 +601,7 @@ pub fn mk_attr_word( pub fn mk_attr_nested_word( g: &AttrIdGenerator, style: AttrStyle, - unsafety: Unsafe, + unsafety: Safety, outer: Symbol, inner: Symbol, span: Span, @@ -623,7 +623,7 @@ pub fn mk_attr_nested_word( pub fn mk_attr_name_value_str( g: &AttrIdGenerator, style: AttrStyle, - unsafety: Unsafe, + unsafety: Safety, name: Symbol, val: Symbol, span: Span, diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 5fbae6a34e5d..554f9f098586 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -1801,7 +1801,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let attr = attr::mk_attr_nested_word( &self.tcx.sess.psess.attr_id_generator, AttrStyle::Outer, - Unsafe::No, + Safety::Default, sym::allow, sym::unreachable_code, self.lower_span(span), diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index e3ccbbe391e0..54d642f6a0ff 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -16,7 +16,7 @@ use rustc_ast::token::{self, BinOpToken, CommentKind, Delimiter, Nonterminal, To use rustc_ast::tokenstream::{Spacing, TokenStream, TokenTree}; use rustc_ast::util::classify; use rustc_ast::util::comments::{Comment, CommentStyle}; -use rustc_ast::{self as ast, AttrArgs, AttrArgsEq, BlockCheckMode, PatKind, Unsafe}; +use rustc_ast::{self as ast, AttrArgs, AttrArgsEq, BlockCheckMode, PatKind, Safety}; use rustc_ast::{attr, BindingMode, ByRef, DelimArgs, RangeEnd, RangeSyntax, Term}; use rustc_ast::{GenericArg, GenericBound, SelfKind}; use rustc_ast::{InlineAsmOperand, InlineAsmRegOrRegClass}; @@ -249,7 +249,7 @@ pub fn print_crate<'a>( let fake_attr = attr::mk_attr_nested_word( g, ast::AttrStyle::Inner, - Unsafe::No, + Safety::Default, sym::feature, sym::prelude_import, DUMMY_SP, @@ -261,7 +261,7 @@ pub fn print_crate<'a>( if edition.is_rust_2015() { // `#![no_std]` let fake_attr = - attr::mk_attr_word(g, ast::AttrStyle::Inner, Unsafe::No, sym::no_std, DUMMY_SP); + attr::mk_attr_word(g, ast::AttrStyle::Inner, Safety::Default, sym::no_std, DUMMY_SP); s.print_attribute(&fake_attr); } } diff --git a/compiler/rustc_builtin_macros/src/derive.rs b/compiler/rustc_builtin_macros/src/derive.rs index d70eaa617ca8..dea668ff8f4b 100644 --- a/compiler/rustc_builtin_macros/src/derive.rs +++ b/compiler/rustc_builtin_macros/src/derive.rs @@ -2,7 +2,7 @@ use crate::cfg_eval::cfg_eval; use crate::errors; use rustc_ast as ast; -use rustc_ast::{GenericParamKind, ItemKind, MetaItemKind, NestedMetaItem, StmtKind, Unsafe}; +use rustc_ast::{GenericParamKind, ItemKind, MetaItemKind, NestedMetaItem, StmtKind, Safety}; use rustc_expand::base::{ Annotatable, DeriveResolution, ExpandResult, ExtCtxt, Indeterminate, MultiItemModifier, }; @@ -163,9 +163,9 @@ fn report_path_args(sess: &Session, meta: &ast::MetaItem) { fn report_unsafe_args(sess: &Session, meta: &ast::MetaItem) { match meta.unsafety { - Unsafe::Yes(span) => { + Safety::Unsafe(span) => { sess.dcx().emit_err(errors::DeriveUnsafePath { span }); } - Unsafe::No => {} + Safety::Default => {} } } diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs index fec54cfe38fb..ba4e5cfd1307 100644 --- a/compiler/rustc_builtin_macros/src/test_harness.rs +++ b/compiler/rustc_builtin_macros/src/test_harness.rs @@ -203,7 +203,7 @@ impl<'a> MutVisitor for EntryPointCleaner<'a> { let allow_dead_code = attr::mk_attr_nested_word( &self.sess.psess.attr_id_generator, ast::AttrStyle::Outer, - ast::Unsafe::No, + ast::Safety::Default, sym::allow, sym::dead_code, self.def_site, diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs index 9451102c010b..e513e61f4d1e 100644 --- a/compiler/rustc_expand/src/build.rs +++ b/compiler/rustc_expand/src/build.rs @@ -666,7 +666,7 @@ impl<'a> ExtCtxt<'a> { // Builds `#[name]`. pub fn attr_word(&self, name: Symbol, span: Span) -> ast::Attribute { let g = &self.sess.psess.attr_id_generator; - attr::mk_attr_word(g, ast::AttrStyle::Outer, ast::Unsafe::No, name, span) + attr::mk_attr_word(g, ast::AttrStyle::Outer, ast::Safety::Default, name, span) } // Builds `#[name = val]`. @@ -674,12 +674,12 @@ impl<'a> ExtCtxt<'a> { // Note: `span` is used for both the identifier and the value. pub fn attr_name_value_str(&self, name: Symbol, val: Symbol, span: Span) -> ast::Attribute { let g = &self.sess.psess.attr_id_generator; - attr::mk_attr_name_value_str(g, ast::AttrStyle::Outer, ast::Unsafe::No, name, val, span) + attr::mk_attr_name_value_str(g, ast::AttrStyle::Outer, ast::Safety::Default, name, val, span) } // Builds `#[outer(inner)]`. pub fn attr_nested_word(&self, outer: Symbol, inner: Symbol, span: Span) -> ast::Attribute { let g = &self.sess.psess.attr_id_generator; - attr::mk_attr_nested_word(g, ast::AttrStyle::Outer, ast::Unsafe::No, outer, inner, span) + attr::mk_attr_nested_word(g, ast::AttrStyle::Outer, ast::Safety::Default, outer, inner, span) } } diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index dfef2540d29d..c28a09eb57c7 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -781,7 +781,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { // The `MetaItem` representing the trait to derive can't // have an unsafe around it (as of now). let meta = ast::MetaItem { - unsafety: ast::Unsafe::No, + unsafety: ast::Safety::Default, kind: MetaItemKind::Word, span, path, diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs index 2dd19f6c20af..58fef9b6c456 100644 --- a/compiler/rustc_parse/src/parser/attr.rs +++ b/compiler/rustc_parse/src/parser/attr.rs @@ -258,9 +258,9 @@ impl<'a> Parser<'a> { this.psess.gated_spans.gate(sym::unsafe_attributes, unsafe_span); this.expect(&token::OpenDelim(Delimiter::Parenthesis))?; - ast::Unsafe::Yes(unsafe_span) + ast::Safety::Unsafe(unsafe_span) } else { - ast::Unsafe::No + ast::Safety::Default }; let path = this.parse_path(PathStyle::Mod)?; @@ -395,9 +395,9 @@ impl<'a> Parser<'a> { self.psess.gated_spans.gate(sym::unsafe_attributes, unsafe_span); self.expect(&token::OpenDelim(Delimiter::Parenthesis))?; - ast::Unsafe::Yes(unsafe_span) + ast::Safety::Unsafe(unsafe_span) } else { - ast::Unsafe::No + ast::Safety::Default }; let path = self.parse_path(PathStyle::Mod)?; diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index d826eacbd156..29bc165159d8 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -317,7 +317,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { fn check_unsafe_attr(&self, attr: &Attribute) { if !attr.is_doc_comment() { let attr_item = attr.get_normal_item(); - if let ast::Unsafe::Yes(unsafe_span) = attr_item.unsafety { + if let ast::Safety::Unsafe(unsafe_span) = attr_item.unsafety { if !is_unsafe_attr(attr.name_or_empty()) { self.dcx().emit_err(errors::InvalidAttrUnsafe { span: unsafe_span, diff --git a/src/librustdoc/clean/cfg/tests.rs b/src/librustdoc/clean/cfg/tests.rs index 2579c15fc5fc..56a10f9fb9c2 100644 --- a/src/librustdoc/clean/cfg/tests.rs +++ b/src/librustdoc/clean/cfg/tests.rs @@ -1,6 +1,6 @@ use super::*; -use rustc_ast::{MetaItemLit, Path, StrStyle, Unsafe}; +use rustc_ast::{MetaItemLit, Path, StrStyle, Safety}; use rustc_span::create_default_session_globals_then; use rustc_span::symbol::{kw, Ident}; use rustc_span::DUMMY_SP; @@ -16,7 +16,7 @@ fn name_value_cfg(name: &str, value: &str) -> Cfg { fn dummy_meta_item_word(name: &str) -> MetaItem { MetaItem { - unsafety: Unsafe::No, + unsafety: Safety::Default, path: Path::from_ident(Ident::from_str(name)), kind: MetaItemKind::Word, span: DUMMY_SP, @@ -26,7 +26,7 @@ fn dummy_meta_item_word(name: &str) -> MetaItem { fn dummy_meta_item_name_value(name: &str, symbol: Symbol, kind: LitKind) -> MetaItem { let lit = MetaItemLit { symbol, suffix: None, kind, span: DUMMY_SP }; MetaItem { - unsafety: Unsafe::No, + unsafety: Safety::Default, path: Path::from_ident(Ident::from_str(name)), kind: MetaItemKind::NameValue(lit), span: DUMMY_SP, @@ -36,7 +36,7 @@ fn dummy_meta_item_name_value(name: &str, symbol: Symbol, kind: LitKind) -> Meta macro_rules! dummy_meta_item_list { ($name:ident, [$($list:ident),* $(,)?]) => { MetaItem { - unsafety: Unsafe::No, + unsafety: Safety::Default, path: Path::from_ident(Ident::from_str(stringify!($name))), kind: MetaItemKind::List(thin_vec![ $( @@ -51,7 +51,7 @@ macro_rules! dummy_meta_item_list { ($name:ident, [$($list:expr),* $(,)?]) => { MetaItem { - unsafety: Unsafe::No, + unsafety: Safety::Default, path: Path::from_ident(Ident::from_str(stringify!($name))), kind: MetaItemKind::List(thin_vec![ $( From 8aa2553b509b8ea033560aedebe0df7c1d8ca42c Mon Sep 17 00:00:00 2001 From: carbotaniuman <41451839+carbotaniuman@users.noreply.github.com> Date: Mon, 27 May 2024 11:41:41 -0500 Subject: [PATCH 41/92] Change comment to FIXME --- compiler/rustc_ast/src/attr/mod.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs index ed1b4733820e..676a2377c3b3 100644 --- a/compiler/rustc_ast/src/attr/mod.rs +++ b/compiler/rustc_ast/src/attr/mod.rs @@ -378,7 +378,9 @@ impl MetaItem { _ => path.span.hi(), }; let span = path.span.with_hi(hi); - // FIX THIS LATER + // FIXME: This parses `unsafe()` not as unsafe attribute syntax in `MetaItem`, + // but as a parenthesized list. This (and likely `MetaItem`) should be changed in + // such a way that builtin macros don't accept extraneous `unsafe()`. Some(MetaItem { unsafety: Safety::Default, path, kind, span }) } } From 15f8ffe371debffe922d8dde74918a2d14acc1fd Mon Sep 17 00:00:00 2001 From: carbotaniuman <41451839+carbotaniuman@users.noreply.github.com> Date: Mon, 27 May 2024 11:45:32 -0500 Subject: [PATCH 42/92] Fix formatting --- compiler/rustc_ast_pretty/src/pprust/state.rs | 9 +++++++-- compiler/rustc_builtin_macros/src/derive.rs | 2 +- compiler/rustc_expand/src/build.rs | 18 ++++++++++++++++-- compiler/rustc_passes/src/check_attr.rs | 1 - src/librustdoc/clean/cfg/tests.rs | 2 +- 5 files changed, 25 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 54d642f6a0ff..f32b63a39f0a 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -260,8 +260,13 @@ pub fn print_crate<'a>( // root, so this is not needed, and actually breaks things. if edition.is_rust_2015() { // `#![no_std]` - let fake_attr = - attr::mk_attr_word(g, ast::AttrStyle::Inner, Safety::Default, sym::no_std, DUMMY_SP); + let fake_attr = attr::mk_attr_word( + g, + ast::AttrStyle::Inner, + Safety::Default, + sym::no_std, + DUMMY_SP, + ); s.print_attribute(&fake_attr); } } diff --git a/compiler/rustc_builtin_macros/src/derive.rs b/compiler/rustc_builtin_macros/src/derive.rs index dea668ff8f4b..eed7fea11c19 100644 --- a/compiler/rustc_builtin_macros/src/derive.rs +++ b/compiler/rustc_builtin_macros/src/derive.rs @@ -2,7 +2,7 @@ use crate::cfg_eval::cfg_eval; use crate::errors; use rustc_ast as ast; -use rustc_ast::{GenericParamKind, ItemKind, MetaItemKind, NestedMetaItem, StmtKind, Safety}; +use rustc_ast::{GenericParamKind, ItemKind, MetaItemKind, NestedMetaItem, Safety, StmtKind}; use rustc_expand::base::{ Annotatable, DeriveResolution, ExpandResult, ExtCtxt, Indeterminate, MultiItemModifier, }; diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs index e513e61f4d1e..37dfd8305126 100644 --- a/compiler/rustc_expand/src/build.rs +++ b/compiler/rustc_expand/src/build.rs @@ -674,12 +674,26 @@ impl<'a> ExtCtxt<'a> { // Note: `span` is used for both the identifier and the value. pub fn attr_name_value_str(&self, name: Symbol, val: Symbol, span: Span) -> ast::Attribute { let g = &self.sess.psess.attr_id_generator; - attr::mk_attr_name_value_str(g, ast::AttrStyle::Outer, ast::Safety::Default, name, val, span) + attr::mk_attr_name_value_str( + g, + ast::AttrStyle::Outer, + ast::Safety::Default, + name, + val, + span, + ) } // Builds `#[outer(inner)]`. pub fn attr_nested_word(&self, outer: Symbol, inner: Symbol, span: Span) -> ast::Attribute { let g = &self.sess.psess.attr_id_generator; - attr::mk_attr_nested_word(g, ast::AttrStyle::Outer, ast::Safety::Default, outer, inner, span) + attr::mk_attr_nested_word( + g, + ast::AttrStyle::Outer, + ast::Safety::Default, + outer, + inner, + span, + ) } } diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 29bc165159d8..6ce7c41acc8f 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -312,7 +312,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { true } - /// Checks if `unsafe()` is applied to an invalid attribute. fn check_unsafe_attr(&self, attr: &Attribute) { if !attr.is_doc_comment() { diff --git a/src/librustdoc/clean/cfg/tests.rs b/src/librustdoc/clean/cfg/tests.rs index 56a10f9fb9c2..2857f74c744f 100644 --- a/src/librustdoc/clean/cfg/tests.rs +++ b/src/librustdoc/clean/cfg/tests.rs @@ -1,6 +1,6 @@ use super::*; -use rustc_ast::{MetaItemLit, Path, StrStyle, Safety}; +use rustc_ast::{MetaItemLit, Path, Safety, StrStyle}; use rustc_span::create_default_session_globals_then; use rustc_span::symbol::{kw, Ident}; use rustc_span::DUMMY_SP; From 1cc4e8c73393137709080ce9b91c3996c62ad1f9 Mon Sep 17 00:00:00 2001 From: carbotaniuman <41451839+carbotaniuman@users.noreply.github.com> Date: Mon, 27 May 2024 11:46:19 -0500 Subject: [PATCH 43/92] Add unsafe to `ffi_pure` and `ffi_const` --- compiler/rustc_feature/src/builtin_attrs.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index f901f5c8c41a..1cea9cccd913 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -535,11 +535,11 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ ), gated!( - ffi_pure, Normal, template!(Word), WarnFollowing, + unsafe ffi_pure, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No, experimental!(ffi_pure) ), gated!( - ffi_const, Normal, template!(Word), WarnFollowing, + unsafe ffi_const, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No, experimental!(ffi_const) ), gated!( From 453093b7918dee3dda3b6a5bfe707a931013facd Mon Sep 17 00:00:00 2001 From: carbotaniuman <41451839+carbotaniuman@users.noreply.github.com> Date: Thu, 6 Jun 2024 20:36:23 -0500 Subject: [PATCH 44/92] Remove extraneous attribute --- compiler/rustc_feature/src/builtin_attrs.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 1cea9cccd913..9b5e4e50d3ca 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -60,7 +60,6 @@ pub enum AttributeType { } #[derive(Copy, Clone, PartialEq, Debug)] -#[allow(dead_code)] pub enum AttributeSafety { /// Normal attribute that does not need `#[unsafe(...)]` Normal, From 5a23111b77924ef5f1b1472eae0b6fe6d76547ac Mon Sep 17 00:00:00 2001 From: Sunshine Date: Fri, 7 Jun 2024 09:36:43 +0800 Subject: [PATCH 45/92] Follow ESLint instructions. --- src/librustdoc/html/static/js/search.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index f4b81e4b7eef..e5a1f9d1be0e 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -625,7 +625,6 @@ function initSearch(rawSearchIndex) { * @return {integer} */ function getIdentEndPosition(parserState) { - const start = parserState.pos; let afterIdent = consumeIdent(parserState); let end = parserState.pos; let macroExclamation = -1; @@ -2167,7 +2166,7 @@ function initSearch(rawSearchIndex) { }; const [crateDescs, descs] = await Promise.all([ Promise.all(crateAliases.map(fetchDesc)), - Promise.all(aliases.map(fetchDesc)) + Promise.all(aliases.map(fetchDesc)), ]); const pushFunc = alias => { @@ -2183,9 +2182,13 @@ function initSearch(rawSearchIndex) { } }; - aliases.forEach((alias, i) => alias.desc = descs[i]); + aliases.forEach((alias, i) => { + alias.desc = descs[i]; + }); aliases.forEach(pushFunc); - crateAliases.forEach((alias, i) => alias.desc = crateDescs[i]); + crateAliases.forEach((alias, i) => { + alias.desc = crateDescs[i]; + }); crateAliases.forEach(pushFunc); } From f83cdbd2e0f6ce2a10f1475b16ada9fff5c094d4 Mon Sep 17 00:00:00 2001 From: carbotaniuman <41451839+carbotaniuman@users.noreply.github.com> Date: Thu, 6 Jun 2024 20:42:20 -0500 Subject: [PATCH 46/92] Fix ordering --- compiler/rustc_feature/src/unstable.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 4688624f310b..d67422849d8a 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -620,10 +620,10 @@ declare_features! ( (unstable, type_changing_struct_update, "1.58.0", Some(86555)), /// Allows unnamed fields of struct and union type (incomplete, unnamed_fields, "1.74.0", Some(49804)), - /// Allows unsafe on extern declarations and safety qualifiers over internal items. - (unstable, unsafe_extern_blocks, "CURRENT_RUSTC_VERSION", Some(123743)), /// Allows unsafe attributes. (unstable, unsafe_attributes, "CURRENT_RUSTC_VERSION", Some(123757)), + /// Allows unsafe on extern declarations and safety qualifiers over internal items. + (unstable, unsafe_extern_blocks, "CURRENT_RUSTC_VERSION", Some(123743)), /// Allows unsized fn parameters. (unstable, unsized_fn_params, "1.49.0", Some(48055)), /// Allows unsized rvalues at arguments and parameters. From f9104f0dfb531581aa96b19b333835351a566b04 Mon Sep 17 00:00:00 2001 From: carbotaniuman <41451839+carbotaniuman@users.noreply.github.com> Date: Thu, 6 Jun 2024 20:54:51 -0500 Subject: [PATCH 47/92] Handle safe case --- compiler/rustc_builtin_macros/src/derive.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/rustc_builtin_macros/src/derive.rs b/compiler/rustc_builtin_macros/src/derive.rs index eed7fea11c19..b5cbfdf0ec6e 100644 --- a/compiler/rustc_builtin_macros/src/derive.rs +++ b/compiler/rustc_builtin_macros/src/derive.rs @@ -167,5 +167,6 @@ fn report_unsafe_args(sess: &Session, meta: &ast::MetaItem) { sess.dcx().emit_err(errors::DeriveUnsafePath { span }); } Safety::Default => {} + Safety::Safe(_) => unreachable!(), } } From ceaa42b81785e58b2ddbd1826d4f0d97143f13ca Mon Sep 17 00:00:00 2001 From: Sunshine Date: Fri, 7 Jun 2024 11:42:17 +0800 Subject: [PATCH 48/92] Update tests --- src/librustdoc/html/static/js/search.js | 5 +++-- tests/rustdoc-js-std/parser-errors.js | 23 +++++++++++++++------- tests/rustdoc-js/non-english-identifier.js | 2 +- 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index e5a1f9d1be0e..8ac4b53673f4 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -671,9 +671,10 @@ function initSearch(rawSearchIndex) { ) { break; } else if (parserState.pos > 0) { - throw ["Unexpected ", c, " after ", parserState.userQuery[parserState.pos - 1]]; + throw ["Unexpected ", c, " after ", parserState.userQuery[parserState.pos - 1], + " (not a valid identifier)"]; } else { - throw ["Unexpected ", c]; + throw ["Unexpected ", c, " (not a valid identifier)"]; } parserState.pos += 1; afterIdent = consumeIdent(parserState); diff --git a/tests/rustdoc-js-std/parser-errors.js b/tests/rustdoc-js-std/parser-errors.js index ffd169812b63..c4d7c2b0b859 100644 --- a/tests/rustdoc-js-std/parser-errors.js +++ b/tests/rustdoc-js-std/parser-errors.js @@ -24,7 +24,7 @@ const PARSED = [ original: "-> *", returned: [], userQuery: "-> *", - error: "Unexpected `*` after ` `", + error: "Unexpected `*` after ` ` (not a valid identifier)", }, { query: 'a<"P">', @@ -204,16 +204,25 @@ const PARSED = [ original: "_:", returned: [], userQuery: "_:", - error: "Unexpected `:` (expected path after type filter `_:`)", + error: "Unexpected `_` (not a valid identifier)", }, { - query: "_:a", + query: "ab:", elems: [], foundElems: 0, - original: "_:a", + original: "ab:", returned: [], - userQuery: "_:a", - error: "Unknown type filter `_`", + userQuery: "ab:", + error: "Unexpected `:` (expected path after type filter `ab:`)", + }, + { + query: "a:b", + elems: [], + foundElems: 0, + original: "a:b", + returned: [], + userQuery: "a:b", + error: "Unknown type filter `a`", }, { query: "a-bb", @@ -240,7 +249,7 @@ const PARSED = [ original: "ab'", returned: [], userQuery: "ab'", - error: "Unexpected `'` after `b`", + error: "Unexpected `'` after `b` (not a valid identifier)", }, { query: "a->", diff --git a/tests/rustdoc-js/non-english-identifier.js b/tests/rustdoc-js/non-english-identifier.js index 84209aa04c02..1765a69152a8 100644 --- a/tests/rustdoc-js/non-english-identifier.js +++ b/tests/rustdoc-js/non-english-identifier.js @@ -105,7 +105,7 @@ const PARSED = [ original: "非法符号——", returned: [], userQuery: "非法符号——", - error: "Unexpected `—` after `号`", + error: "Unexpected `—` after `号` (not a valid identifier)", } ] const EXPECTED = [ From 49c2a1dda9e1735f2520c79c2059a5856d2e061d Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Thu, 6 Jun 2024 22:50:28 -0700 Subject: [PATCH 49/92] Update backtrace to 0.3.73 --- Cargo.lock | 17 +++++++++++++---- library/backtrace | 2 +- library/std/Cargo.toml | 4 ++-- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0e950e288072..5202a763afba 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2652,14 +2652,11 @@ version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" dependencies = [ - "compiler_builtins", "crc32fast", "flate2", "hashbrown", "indexmap", "memchr", - "rustc-std-workspace-alloc", - "rustc-std-workspace-core", "ruzstd 0.5.0", "wasmparser", ] @@ -2675,6 +2672,18 @@ dependencies = [ "ruzstd 0.6.0", ] +[[package]] +name = "object" +version = "0.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "576dfe1fc8f9df304abb159d767a29d0476f7750fbf8aa7ad07816004a207434" +dependencies = [ + "compiler_builtins", + "memchr", + "rustc-std-workspace-alloc", + "rustc-std-workspace-core", +] + [[package]] name = "odht" version = "0.3.1" @@ -5356,7 +5365,7 @@ dependencies = [ "hermit-abi", "libc", "miniz_oxide", - "object 0.32.2", + "object 0.36.0", "panic_abort", "panic_unwind", "profiler_builtins", diff --git a/library/backtrace b/library/backtrace index 5e05efa87905..72265bea2108 160000 --- a/library/backtrace +++ b/library/backtrace @@ -1 +1 @@ -Subproject commit 5e05efa87905fb5b351a2bc5644d60c57d6d9327 +Subproject commit 72265bea210891ae47bbe6d4f17b493ef0606619 diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index 79a504c5a5e2..68bba5c2be11 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -34,10 +34,10 @@ addr2line = { version = "0.22.0", optional = true, default-features = false } libc = { version = "0.2.153", default-features = false, features = ['rustc-dep-of-std'], public = true } [target.'cfg(all(not(target_os = "aix"), not(all(windows, target_env = "msvc", not(target_vendor = "uwp")))))'.dependencies] -object = { version = "0.32.0", default-features = false, optional = true, features = ['read_core', 'elf', 'macho', 'pe', 'unaligned', 'archive'] } +object = { version = "0.36.0", default-features = false, optional = true, features = ['read_core', 'elf', 'macho', 'pe', 'unaligned', 'archive'] } [target.'cfg(target_os = "aix")'.dependencies] -object = { version = "0.32.0", default-features = false, optional = true, features = ['read_core', 'xcoff', 'unaligned', 'archive'] } +object = { version = "0.36.0", default-features = false, optional = true, features = ['read_core', 'xcoff', 'unaligned', 'archive'] } [dev-dependencies] rand = { version = "0.8.5", default-features = false, features = ["alloc"] } From 7eff2d9b2268eff7289ed969fa158c83e1077026 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Fri, 7 Jun 2024 09:20:25 +0200 Subject: [PATCH 50/92] Do not pass source root when building run-make tests --- src/tools/compiletest/src/runtest.rs | 1 - tests/run-make/CURRENT_RUSTC_VERSION/rmake.rs | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 2c425a07b2ac..db08d9b54ba2 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -3515,7 +3515,6 @@ impl<'test> TestCx<'test> { .arg(&self.testpaths.file.join("rmake.rs")) .env("TARGET", &self.config.target) .env("PYTHON", &self.config.python) - .env("S", &src_root) .env("RUST_BUILD_STAGE", &self.config.stage_id) .env("RUSTC", cwd.join(&self.config.rustc_path)) .env("TMPDIR", &rmake_out_dir) diff --git a/tests/run-make/CURRENT_RUSTC_VERSION/rmake.rs b/tests/run-make/CURRENT_RUSTC_VERSION/rmake.rs index 1bdb63475712..827a1b7ce67d 100644 --- a/tests/run-make/CURRENT_RUSTC_VERSION/rmake.rs +++ b/tests/run-make/CURRENT_RUSTC_VERSION/rmake.rs @@ -5,7 +5,7 @@ use std::path::PathBuf; -use run_make_support::{aux_build, rustc}; +use run_make_support::{aux_build, rustc, source_root}; fn main() { aux_build().input("stable.rs").emit("metadata").run(); @@ -17,7 +17,7 @@ fn main() { rustc().input("main.rs").emit("metadata").extern_("stable", &stable_path).command_output(); let stderr = String::from_utf8_lossy(&output.stderr); - let version = include_str!(concat!(env!("S"), "/src/version")); + let version = std::fs::read_to_string(source_root().join("src/version")).unwrap(); let expected_string = format!("stable since {}", version.trim()); assert!(stderr.contains(&expected_string)); } From eac63b77257d69d2a89a50f165ac58971689a31d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Fri, 7 Jun 2024 09:20:58 +0200 Subject: [PATCH 51/92] Rename `S` environment variable to `SOURCE_ROOT` in run-make tests --- src/tools/compiletest/src/runtest.rs | 2 +- src/tools/run-make-support/src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index db08d9b54ba2..41972b6994c6 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -3566,7 +3566,7 @@ impl<'test> TestCx<'test> { .env(dylib_env_var(), &dylib_env_paths) .env("TARGET", &self.config.target) .env("PYTHON", &self.config.python) - .env("S", &src_root) + .env("SOURCE_ROOT", &src_root) .env("RUST_BUILD_STAGE", &self.config.stage_id) .env("RUSTC", cwd.join(&self.config.rustc_path)) .env("TMPDIR", &rmake_out_dir) diff --git a/src/tools/run-make-support/src/lib.rs b/src/tools/run-make-support/src/lib.rs index d74a0272a627..0f9d5a3b6c36 100644 --- a/src/tools/run-make-support/src/lib.rs +++ b/src/tools/run-make-support/src/lib.rs @@ -89,7 +89,7 @@ pub fn htmldocck() -> Command { /// Path to the root rust-lang/rust source checkout. pub fn source_root() -> PathBuf { - env_var("S").into() + env_var("SOURCE_ROOT").into() } /// Construct the static library name based on the platform. From 92c54db22f3ee0ef13be629b55607c23ed305f3b Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 3 Jun 2024 08:38:40 +0000 Subject: [PATCH 52/92] Revert "Cache whether a body has inline consts" This reverts commit eae5031ecbda434e92966099e0dc93917de03eff. --- compiler/rustc_ast_lowering/src/expr.rs | 5 +---- compiler/rustc_ast_lowering/src/lib.rs | 8 +------- compiler/rustc_hir/src/hir.rs | 3 --- compiler/rustc_hir/src/stable_hash_impls.rs | 3 +-- compiler/rustc_middle/src/ty/context.rs | 1 - compiler/rustc_mir_transform/src/lib.rs | 6 +----- 6 files changed, 4 insertions(+), 22 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index eb206a09be31..c0a424115aad 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -74,10 +74,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let kind = match &e.kind { ExprKind::Array(exprs) => hir::ExprKind::Array(self.lower_exprs(exprs)), - ExprKind::ConstBlock(c) => { - self.has_inline_consts = true; - hir::ExprKind::ConstBlock(self.lower_expr(c)) - } + ExprKind::ConstBlock(c) => hir::ExprKind::ConstBlock(self.lower_expr(c)), ExprKind::Repeat(expr, count) => { let expr = self.lower_expr(expr); let count = self.lower_array_length(count); diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 023dc6d52c3e..cd8afd076bbe 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -96,8 +96,6 @@ struct LoweringContext<'a, 'hir> { /// Bodies inside the owner being lowered. bodies: Vec<(hir::ItemLocalId, &'hir hir::Body<'hir>)>, - /// Whether there were inline consts that typeck will split out into bodies - has_inline_consts: bool, /// Attributes inside the owner being lowered. attrs: SortedMap, /// Collect items that were created by lowering the current owner. @@ -160,7 +158,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { item_local_id_counter: hir::ItemLocalId::ZERO, node_id_to_local_id: Default::default(), trait_map: Default::default(), - has_inline_consts: false, // Lowering state. catch_scope: None, @@ -570,7 +567,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let current_attrs = std::mem::take(&mut self.attrs); let current_bodies = std::mem::take(&mut self.bodies); - let current_has_inline_consts = std::mem::take(&mut self.has_inline_consts); let current_node_ids = std::mem::take(&mut self.node_id_to_local_id); let current_trait_map = std::mem::take(&mut self.trait_map); let current_owner = @@ -597,7 +593,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { self.attrs = current_attrs; self.bodies = current_bodies; - self.has_inline_consts = current_has_inline_consts; self.node_id_to_local_id = current_node_ids; self.trait_map = current_trait_map; self.current_hir_id_owner = current_owner; @@ -634,7 +629,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let attrs = std::mem::take(&mut self.attrs); let mut bodies = std::mem::take(&mut self.bodies); let trait_map = std::mem::take(&mut self.trait_map); - let has_inline_consts = std::mem::take(&mut self.has_inline_consts); #[cfg(debug_assertions)] for (id, attrs) in attrs.iter() { @@ -652,7 +646,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { self.tcx.hash_owner_nodes(node, &bodies, &attrs); let num_nodes = self.item_local_id_counter.as_usize(); let (nodes, parenting) = index::index_hir(self.tcx, node, &bodies, num_nodes); - let nodes = hir::OwnerNodes { opt_hash_including_bodies, nodes, bodies, has_inline_consts }; + let nodes = hir::OwnerNodes { opt_hash_including_bodies, nodes, bodies }; let attrs = hir::AttributeMap { map: attrs, opt_hash: attrs_hash }; self.arena.alloc(hir::OwnerInfo { nodes, parenting, attrs, trait_map }) diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 770dfcb98c91..d5f545c1c159 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -907,9 +907,6 @@ pub struct OwnerNodes<'tcx> { pub nodes: IndexVec>, /// Content of local bodies. pub bodies: SortedMap>, - /// Whether the body contains inline constants that are created for the query system during typeck - /// of the body. - pub has_inline_consts: bool, } impl<'tcx> OwnerNodes<'tcx> { diff --git a/compiler/rustc_hir/src/stable_hash_impls.rs b/compiler/rustc_hir/src/stable_hash_impls.rs index 1ebd4b80e183..baa1635f7313 100644 --- a/compiler/rustc_hir/src/stable_hash_impls.rs +++ b/compiler/rustc_hir/src/stable_hash_impls.rs @@ -93,8 +93,7 @@ impl<'tcx, HirCtx: crate::HashStableContext> HashStable for OwnerNodes<' // `local_id_to_def_id` is also ignored because is dependent on the body, then just hashing // the body satisfies the condition of two nodes being different have different // `hash_stable` results. - let OwnerNodes { opt_hash_including_bodies, nodes: _, bodies: _, has_inline_consts: _ } = - *self; + let OwnerNodes { opt_hash_including_bodies, nodes: _, bodies: _ } = *self; opt_hash_including_bodies.unwrap().hash_stable(hcx, hasher); } } diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 6b35b1f2d13c..20b3d4822e2f 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -740,7 +740,6 @@ impl<'tcx> TyCtxtFeed<'tcx, LocalDefId> { 1, ), bodies, - has_inline_consts: false, }))); self.feed_owner_id().hir_attrs(attrs); } diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index a8741254ffbf..b66e3d66f4d3 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -225,11 +225,7 @@ fn mir_keys(tcx: TyCtxt<'_>, (): ()) -> FxIndexSet { // Inline consts' bodies are created in // typeck instead of during ast lowering, like all other bodies so far. for def_id in tcx.hir().body_owners() { - // Incremental performance optimization: only load typeck results for things that actually have inline consts - if tcx.hir_owner_nodes(tcx.hir().body_owned_by(def_id).id().hir_id.owner).has_inline_consts - { - set.extend(tcx.typeck(def_id).inline_consts.values()) - } + set.extend(tcx.typeck(def_id).inline_consts.values()) } // Additionally, tuple struct/variant constructors have MIR, but From cbee17d5025c7824828c793ac776ad6e1addbeac Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 3 Jun 2024 09:11:58 +0000 Subject: [PATCH 53/92] Revert "Create const block DefIds in typeck instead of ast lowering" This reverts commit ddc5f9b6c1f21da5d4596bf7980185a00984ac42. --- compiler/rustc_ast/src/ast.rs | 2 +- compiler/rustc_ast/src/mut_visit.rs | 2 +- compiler/rustc_ast/src/visit.rs | 2 +- compiler/rustc_ast_lowering/src/expr.rs | 9 +++- compiler/rustc_ast_lowering/src/index.rs | 8 ++++ .../rustc_ast_pretty/src/pprust/state/expr.rs | 5 +-- .../src/const_eval/fn_queries.rs | 2 +- compiler/rustc_hir/src/hir.rs | 14 ++++++- compiler/rustc_hir/src/intravisit.rs | 13 +++++- .../rustc_hir_analysis/src/check/region.rs | 7 +--- .../src/collect/generics_of.rs | 10 ++--- .../rustc_hir_analysis/src/collect/type_of.rs | 3 +- compiler/rustc_hir_analysis/src/lib.rs | 8 ++-- compiler/rustc_hir_pretty/src/lib.rs | 5 ++- compiler/rustc_hir_typeck/src/expr.rs | 21 +++++++++- .../src/fn_ctxt/suggestions.rs | 4 -- compiler/rustc_hir_typeck/src/upvar.rs | 4 ++ compiler/rustc_hir_typeck/src/writeback.rs | 13 +++--- compiler/rustc_middle/src/hir/map/mod.rs | 42 ++++++------------- .../rustc_middle/src/ty/typeck_results.rs | 5 --- compiler/rustc_mir_build/src/build/mod.rs | 7 +++- compiler/rustc_mir_build/src/thir/cx/expr.rs | 6 +-- compiler/rustc_mir_build/src/thir/cx/mod.rs | 2 +- .../rustc_mir_build/src/thir/pattern/mod.rs | 8 ++-- compiler/rustc_mir_transform/src/lib.rs | 6 --- compiler/rustc_parse/src/parser/mod.rs | 13 +++--- compiler/rustc_passes/src/check_const.rs | 10 ++--- compiler/rustc_passes/src/dead.rs | 21 +++++----- compiler/rustc_passes/src/liveness.rs | 8 +--- compiler/rustc_passes/src/loops.rs | 7 ++-- compiler/rustc_resolve/src/def_collector.rs | 10 +++++ compiler/rustc_resolve/src/late.rs | 6 +-- src/tools/clippy/clippy_utils/src/consts.rs | 6 +-- .../clippy/clippy_utils/src/hir_utils.rs | 6 +-- .../tests/ui/arithmetic_side_effects.stderr | 32 ++------------ .../custom/consts.consts.built.after.mir | 2 +- tests/pretty/stmt_expr_attributes.rs | 7 +++- tests/ui/lint/non-local-defs/consts.stderr | 19 +++++---- tests/ui/unpretty/expanded-exhaustive.stdout | 3 +- 39 files changed, 190 insertions(+), 168 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 910fbb876974..b5a59442f14a 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -1392,7 +1392,7 @@ pub enum ExprKind { /// An array (e.g, `[a, b, c, d]`). Array(ThinVec>), /// Allow anonymous constants from an inline `const` block - ConstBlock(P), + ConstBlock(AnonConst), /// A function call /// /// The first field resolves to the function itself, diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index a04c648ac731..635535a1125d 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -1417,7 +1417,7 @@ pub fn noop_visit_expr( match kind { ExprKind::Array(exprs) => visit_thin_exprs(exprs, vis), ExprKind::ConstBlock(anon_const) => { - vis.visit_expr(anon_const); + vis.visit_anon_const(anon_const); } ExprKind::Repeat(expr, count) => { vis.visit_expr(expr); diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index de285aed1656..fa97c8db3267 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -959,7 +959,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) -> V ExprKind::Array(subexpressions) => { walk_list!(visitor, visit_expr, subexpressions); } - ExprKind::ConstBlock(anon_const) => try_visit!(visitor.visit_expr(anon_const)), + ExprKind::ConstBlock(anon_const) => try_visit!(visitor.visit_anon_const(anon_const)), ExprKind::Repeat(element, count) => { try_visit!(visitor.visit_expr(element)); try_visit!(visitor.visit_anon_const(count)); diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index c0a424115aad..a55310909284 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -74,7 +74,14 @@ impl<'hir> LoweringContext<'_, 'hir> { let kind = match &e.kind { ExprKind::Array(exprs) => hir::ExprKind::Array(self.lower_exprs(exprs)), - ExprKind::ConstBlock(c) => hir::ExprKind::ConstBlock(self.lower_expr(c)), + ExprKind::ConstBlock(c) => { + let c = self.with_new_scopes(c.value.span, |this| hir::ConstBlock { + def_id: this.local_def_id(c.id), + hir_id: this.lower_node_id(c.id), + body: this.lower_const_body(c.value.span, Some(&c.value)), + }); + hir::ExprKind::ConstBlock(c) + } ExprKind::Repeat(expr, count) => { let expr = self.lower_expr(expr); let count = self.lower_array_length(count); diff --git a/compiler/rustc_ast_lowering/src/index.rs b/compiler/rustc_ast_lowering/src/index.rs index 741a44eb0c52..44f37b5533a6 100644 --- a/compiler/rustc_ast_lowering/src/index.rs +++ b/compiler/rustc_ast_lowering/src/index.rs @@ -236,6 +236,14 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> { }); } + fn visit_inline_const(&mut self, constant: &'hir ConstBlock) { + self.insert(DUMMY_SP, constant.hir_id, Node::ConstBlock(constant)); + + self.with_parent(constant.hir_id, |this| { + intravisit::walk_inline_const(this, constant); + }); + } + fn visit_expr(&mut self, expr: &'hir Expr<'hir>) { self.insert(expr.span, expr.hir_id, Node::Expr(expr)); diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs index 993ccc5b9560..1e117c46b6e2 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs @@ -380,9 +380,8 @@ impl<'a> State<'a> { ast::ExprKind::Array(exprs) => { self.print_expr_vec(exprs); } - ast::ExprKind::ConstBlock(expr) => { - self.word_space("const"); - self.print_expr(expr, FixupContext::default()); + ast::ExprKind::ConstBlock(anon_const) => { + self.print_expr_anon_const(anon_const, attrs); } ast::ExprKind::Repeat(element, count) => { self.print_expr_repeat(element, count); diff --git a/compiler/rustc_const_eval/src/const_eval/fn_queries.rs b/compiler/rustc_const_eval/src/const_eval/fn_queries.rs index 3c11d67e7489..8c66888d1007 100644 --- a/compiler/rustc_const_eval/src/const_eval/fn_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/fn_queries.rs @@ -38,6 +38,7 @@ fn constness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> hir::Constness { match node { hir::Node::Ctor(_) | hir::Node::AnonConst(_) + | hir::Node::ConstBlock(_) | hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Const(..), .. }) => { hir::Constness::Const } @@ -56,7 +57,6 @@ fn constness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> hir::Constness { if is_const { hir::Constness::Const } else { hir::Constness::NotConst } } hir::Node::Expr(e) if let hir::ExprKind::Closure(c) = e.kind => c.constness, - hir::Node::Expr(e) if let hir::ExprKind::ConstBlock(_) = e.kind => hir::Constness::Const, _ => { if let Some(fn_kind) = node.fn_kind() { if fn_kind.constness() == hir::Constness::Const { diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index d5f545c1c159..1570f8d5990e 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -1623,6 +1623,14 @@ pub struct AnonConst { pub span: Span, } +/// An inline constant expression `const { something }`. +#[derive(Copy, Clone, Debug, HashStable_Generic)] +pub struct ConstBlock { + pub hir_id: HirId, + pub def_id: LocalDefId, + pub body: BodyId, +} + /// An expression. #[derive(Debug, Clone, Copy, HashStable_Generic)] pub struct Expr<'hir> { @@ -1909,7 +1917,7 @@ pub fn is_range_literal(expr: &Expr<'_>) -> bool { #[derive(Debug, Clone, Copy, HashStable_Generic)] pub enum ExprKind<'hir> { /// Allow anonymous constants from an inline `const` block - ConstBlock(&'hir Expr<'hir>), + ConstBlock(ConstBlock), /// An array (e.g., `[a, b, c, d]`). Array(&'hir [Expr<'hir>]), /// A function call. @@ -3641,6 +3649,7 @@ pub enum Node<'hir> { Variant(&'hir Variant<'hir>), Field(&'hir FieldDef<'hir>), AnonConst(&'hir AnonConst), + ConstBlock(&'hir ConstBlock), Expr(&'hir Expr<'hir>), ExprField(&'hir ExprField<'hir>), Stmt(&'hir Stmt<'hir>), @@ -3701,6 +3710,7 @@ impl<'hir> Node<'hir> { Node::PreciseCapturingNonLifetimeArg(a) => Some(a.ident), Node::Param(..) | Node::AnonConst(..) + | Node::ConstBlock(..) | Node::Expr(..) | Node::Stmt(..) | Node::Block(..) @@ -3798,6 +3808,7 @@ impl<'hir> Node<'hir> { } Node::AnonConst(constant) => Some((constant.def_id, constant.body)), + Node::ConstBlock(constant) => Some((constant.def_id, constant.body)), _ => None, } @@ -3866,6 +3877,7 @@ impl<'hir> Node<'hir> { expect_variant, &'hir Variant<'hir>, Node::Variant(n), n; expect_field, &'hir FieldDef<'hir>, Node::Field(n), n; expect_anon_const, &'hir AnonConst, Node::AnonConst(n), n; + expect_inline_const, &'hir ConstBlock, Node::ConstBlock(n), n; expect_expr, &'hir Expr<'hir>, Node::Expr(n), n; expect_expr_field, &'hir ExprField<'hir>, Node::ExprField(n), n; expect_stmt, &'hir Stmt<'hir>, Node::Stmt(n), n; diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index e37473df956d..5a16f266dab3 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -344,6 +344,9 @@ pub trait Visitor<'v>: Sized { fn visit_anon_const(&mut self, c: &'v AnonConst) -> Self::Result { walk_anon_const(self, c) } + fn visit_inline_const(&mut self, c: &'v ConstBlock) -> Self::Result { + walk_inline_const(self, c) + } fn visit_expr(&mut self, ex: &'v Expr<'v>) -> Self::Result { walk_expr(self, ex) } @@ -718,6 +721,14 @@ pub fn walk_anon_const<'v, V: Visitor<'v>>(visitor: &mut V, constant: &'v AnonCo visitor.visit_nested_body(constant.body) } +pub fn walk_inline_const<'v, V: Visitor<'v>>( + visitor: &mut V, + constant: &'v ConstBlock, +) -> V::Result { + try_visit!(visitor.visit_id(constant.hir_id)); + visitor.visit_nested_body(constant.body) +} + pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>) -> V::Result { try_visit!(visitor.visit_id(expression.hir_id)); match expression.kind { @@ -725,7 +736,7 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>) walk_list!(visitor, visit_expr, subexpressions); } ExprKind::ConstBlock(ref const_block) => { - try_visit!(visitor.visit_expr(const_block)) + try_visit!(visitor.visit_inline_const(const_block)) } ExprKind::Repeat(ref element, ref count) => { try_visit!(visitor.visit_expr(element)); diff --git a/compiler/rustc_hir_analysis/src/check/region.rs b/compiler/rustc_hir_analysis/src/check/region.rs index 30b03b43872a..72e431926ca3 100644 --- a/compiler/rustc_hir_analysis/src/check/region.rs +++ b/compiler/rustc_hir_analysis/src/check/region.rs @@ -407,14 +407,11 @@ fn resolve_expr<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, expr: &'tcx h match expr.kind { // Manually recurse over closures and inline consts, because they are the only // case of nested bodies that share the parent environment. - hir::ExprKind::Closure(&hir::Closure { body, .. }) => { + hir::ExprKind::Closure(&hir::Closure { body, .. }) + | hir::ExprKind::ConstBlock(hir::ConstBlock { body, .. }) => { let body = visitor.tcx.hir().body(body); visitor.visit_body(body); } - hir::ExprKind::ConstBlock(expr) => visitor.enter_body(expr.hir_id, |this| { - this.cx.var_parent = None; - resolve_local(this, None, Some(expr)); - }), hir::ExprKind::AssignOp(_, left_expr, right_expr) => { debug!( "resolve_expr - enabling pessimistic_yield, was previously {}", diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs index 9af959681fbf..abdf85ad707b 100644 --- a/compiler/rustc_hir_analysis/src/collect/generics_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs @@ -177,10 +177,10 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { } } } - Node::Expr(&hir::Expr { - kind: hir::ExprKind::Closure { .. } | hir::ExprKind::ConstBlock { .. }, - .. - }) => Some(tcx.typeck_root_def_id(def_id.to_def_id())), + Node::ConstBlock(_) + | Node::Expr(&hir::Expr { kind: hir::ExprKind::Closure { .. }, .. }) => { + Some(tcx.typeck_root_def_id(def_id.to_def_id())) + } Node::Item(item) => match item.kind { ItemKind::OpaqueTy(&hir::OpaqueTy { origin: @@ -415,7 +415,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { } // provide junk type parameter defs for const blocks. - if let Node::Expr(Expr { kind: ExprKind::ConstBlock(..), .. }) = node { + if let Node::ConstBlock(_) = node { own_params.push(ty::GenericParamDef { index: next_index(), name: Symbol::intern(""), diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index 6811f62de07b..2684467a4381 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -485,7 +485,8 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_ } Node::AnonConst(_) => anon_const_type_of(tcx, def_id), - Node::Expr(&Expr { kind: ExprKind::ConstBlock(..), .. }) => { + + Node::ConstBlock(_) => { let args = ty::GenericArgs::identity_for_item(tcx, def_id.to_def_id()); args.as_inline_const().ty() } diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index 65b02a2ec565..8fe81851f932 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -190,6 +190,10 @@ pub fn check_crate(tcx: TyCtxt<'_>) { } }); + // Freeze definitions as we don't add new ones at this point. This improves performance by + // allowing lock-free access to them. + tcx.untracked().definitions.freeze(); + // FIXME: Remove this when we implement creating `DefId`s // for anon constants during their parents' typeck. // Typeck all body owners in parallel will produce queries @@ -201,10 +205,6 @@ pub fn check_crate(tcx: TyCtxt<'_>) { } }); - // Freeze definitions as we don't add new ones at this point. This improves performance by - // allowing lock-free access to them. - tcx.untracked().definitions.freeze(); - tcx.ensure().check_unused_traits(()); } diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 6983bbcb052c..6f2febd86b0f 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -84,6 +84,7 @@ impl<'a> State<'a> { Node::ImplItem(a) => self.print_impl_item(a), Node::Variant(a) => self.print_variant(a), Node::AnonConst(a) => self.print_anon_const(a), + Node::ConstBlock(a) => self.print_inline_const(a), Node::Expr(a) => self.print_expr(a), Node::ExprField(a) => self.print_expr_field(a), Node::Stmt(a) => self.print_stmt(a), @@ -1049,10 +1050,10 @@ impl<'a> State<'a> { self.end() } - fn print_inline_const(&mut self, constant: &hir::Expr<'_>) { + fn print_inline_const(&mut self, constant: &hir::ConstBlock) { self.ibox(INDENT_UNIT); self.word_space("const"); - self.print_expr(constant); + self.ann.nested(self, Nested::Body(constant.body)); self.end() } diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 7dd7b3ff055e..811b7ab24634 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -31,6 +31,7 @@ use rustc_errors::{ use rustc_hir as hir; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::DefId; +use rustc_hir::intravisit::Visitor; use rustc_hir::lang_items::LangItem; use rustc_hir::{ExprKind, HirId, QPath}; use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer as _; @@ -334,7 +335,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } ExprKind::DropTemps(e) => self.check_expr_with_expectation(e, expected), ExprKind::Array(args) => self.check_expr_array(args, expected, expr), - ExprKind::ConstBlock(ref block) => self.check_expr_with_expectation(block, expected), + ExprKind::ConstBlock(ref block) => self.check_expr_const_block(block, expected), ExprKind::Repeat(element, ref count) => { self.check_expr_repeat(element, count, expected, expr) } @@ -1456,6 +1457,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + fn check_expr_const_block( + &self, + block: &'tcx hir::ConstBlock, + expected: Expectation<'tcx>, + ) -> Ty<'tcx> { + let body = self.tcx.hir().body(block.body); + + // Create a new function context. + let def_id = block.def_id; + let fcx = FnCtxt::new(self, self.param_env, def_id); + crate::GatherLocalsVisitor::new(&fcx).visit_body(body); + + let ty = fcx.check_expr_with_expectation(body.value, expected); + fcx.require_type_is_sized(ty, body.value.span, ObligationCauseCode::ConstSized); + fcx.write_ty(block.hir_id, ty); + ty + } + fn check_expr_repeat( &self, element: &'tcx hir::Expr<'tcx>, diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index caaf4142f7d1..9ab89f3444c9 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -1051,10 +1051,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .take_while(|(_, node)| { // look at parents until we find the first body owner node.body_id().is_none() - && !matches!( - node, - Node::Expr(Expr { kind: ExprKind::ConstBlock { .. }, .. }) - ) }) .any(|(parent_id, _)| self.is_loop(parent_id)); diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs index 4386e68ce867..466397817dae 100644 --- a/compiler/rustc_hir_typeck/src/upvar.rs +++ b/compiler/rustc_hir_typeck/src/upvar.rs @@ -149,6 +149,10 @@ impl<'a, 'tcx> Visitor<'tcx> for InferBorrowKindVisitor<'a, 'tcx> { self.visit_body(body); self.fcx.analyze_closure(expr.hir_id, expr.span, body_id, body, capture_clause); } + hir::ExprKind::ConstBlock(anon_const) => { + let body = self.fcx.tcx.hir().body(anon_const.body); + self.visit_body(body); + } _ => {} } diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index 8727c0f87dc8..b67d29fce921 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -3,7 +3,6 @@ // generic parameters. use crate::FnCtxt; -use hir::def::DefKind; use rustc_data_structures::unord::ExtendUnord; use rustc_errors::{ErrorGuaranteed, StashKey}; use rustc_hir as hir; @@ -17,7 +16,7 @@ use rustc_middle::ty::fold::{TypeFoldable, TypeFolder}; use rustc_middle::ty::visit::TypeVisitableExt; use rustc_middle::ty::TypeSuperFoldable; use rustc_middle::ty::{self, Ty, TyCtxt}; -use rustc_span::symbol::{kw, sym}; +use rustc_span::symbol::sym; use rustc_span::Span; use rustc_trait_selection::solve; use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt; @@ -296,11 +295,11 @@ impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> { hir::ExprKind::Field(..) | hir::ExprKind::OffsetOf(..) => { self.visit_field_id(e.hir_id); } - hir::ExprKind::ConstBlock(_) => { - let feed = self.tcx().create_def(self.fcx.body_id, kw::Empty, DefKind::InlineConst); - feed.def_span(e.span); - feed.local_def_id_to_hir_id(e.hir_id); - self.typeck_results.inline_consts.insert(e.hir_id.local_id, feed.def_id()); + hir::ExprKind::ConstBlock(anon_const) => { + self.visit_node_id(e.span, anon_const.hir_id); + + let body = self.tcx().hir().body(anon_const.body); + self.visit_body(body); } _ => {} } diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index 8a3bbf71eb61..305ba1ef3bbc 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -1,5 +1,3 @@ -use std::borrow::Cow; - use crate::hir::ModuleItems; use crate::middle::debugger_visualizer::DebuggerVisualizerFile; use crate::query::LocalCrate; @@ -256,26 +254,13 @@ impl<'hir> Map<'hir> { /// Given a `LocalDefId`, returns the `BodyId` associated with it, /// if the node is a body owner, otherwise returns `None`. - pub fn maybe_body_owned_by(self, id: LocalDefId) -> Option>> { - Some(match self.tcx.def_kind(id) { - // Inline consts do not have bodies of their own, so create one to make the follow-up logic simpler. - DefKind::InlineConst => { - let e = self.expect_expr(self.tcx.local_def_id_to_hir_id(id)); - Cow::Owned(Body { - params: &[], - value: match e.kind { - ExprKind::ConstBlock(body) => body, - _ => span_bug!(e.span, "InlineConst was not a ConstBlock: {e:#?}"), - }, - }) - } - _ => Cow::Borrowed(self.body(self.tcx.hir_node_by_def_id(id).body_id()?)), - }) + pub fn maybe_body_owned_by(self, id: LocalDefId) -> Option<&'hir Body<'hir>> { + Some(self.body(self.tcx.hir_node_by_def_id(id).body_id()?)) } /// Given a body owner's id, returns the `BodyId` associated with it. #[track_caller] - pub fn body_owned_by(self, id: LocalDefId) -> Cow<'hir, Body<'hir>> { + pub fn body_owned_by(self, id: LocalDefId) -> &'hir Body<'hir> { self.maybe_body_owned_by(id).unwrap_or_else(|| { let hir_id = self.tcx.local_def_id_to_hir_id(id); span_bug!( @@ -338,7 +323,7 @@ impl<'hir> Map<'hir> { /// Returns an iterator of the `DefId`s for all body-owners in this /// crate. If you would prefer to iterate over the bodies - /// themselves, you can do `self.hir().krate().owners.iter()`. + /// themselves, you can do `self.hir().krate().body_ids.iter()`. #[inline] pub fn body_owners(self) -> impl Iterator + 'hir { self.tcx.hir_crate_items(()).body_owners.iter().copied() @@ -525,17 +510,7 @@ impl<'hir> Map<'hir> { /// Whether the expression pointed at by `hir_id` belongs to a `const` evaluation context. /// Used exclusively for diagnostics, to avoid suggestion function calls. pub fn is_inside_const_context(self, hir_id: HirId) -> bool { - for (_, node) in self.parent_iter(hir_id) { - if let Some((def_id, _)) = node.associated_body() { - return self.body_const_context(def_id).is_some(); - } - if let Node::Expr(e) = node { - if let ExprKind::ConstBlock(_) = e.kind { - return true; - } - } - } - false + self.body_const_context(self.enclosing_body_owner(hir_id)).is_some() } /// Retrieves the `HirId` for `id`'s enclosing function *if* the `id` block or return is @@ -918,6 +893,7 @@ impl<'hir> Map<'hir> { Node::Variant(variant) => variant.span, Node::Field(field) => field.span, Node::AnonConst(constant) => constant.span, + Node::ConstBlock(constant) => self.body(constant.body).value.span, Node::Expr(expr) => expr.span, Node::ExprField(field) => field.span, Node::Stmt(stmt) => stmt.span, @@ -1187,6 +1163,7 @@ fn hir_id_to_string(map: Map<'_>, id: HirId) -> String { format!("{id} (field `{}` in {})", field.ident, path_str(field.def_id)) } Node::AnonConst(_) => node_str("const"), + Node::ConstBlock(_) => node_str("const"), Node::Expr(_) => node_str("expr"), Node::ExprField(_) => node_str("expr field"), Node::Stmt(_) => node_str("stmt"), @@ -1336,6 +1313,11 @@ impl<'hir> Visitor<'hir> for ItemCollector<'hir> { intravisit::walk_anon_const(self, c) } + fn visit_inline_const(&mut self, c: &'hir ConstBlock) { + self.body_owners.push(c.def_id); + intravisit::walk_inline_const(self, c) + } + fn visit_expr(&mut self, ex: &'hir Expr<'hir>) { if let ExprKind::Closure(closure) = ex.kind { self.body_owners.push(closure.def_id); diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs index 69ea9c9843a0..24e3e623ff27 100644 --- a/compiler/rustc_middle/src/ty/typeck_results.rs +++ b/compiler/rustc_middle/src/ty/typeck_results.rs @@ -217,10 +217,6 @@ pub struct TypeckResults<'tcx> { /// Container types and field indices of `offset_of!` expressions offset_of_data: ItemLocalMap<(Ty<'tcx>, Vec<(VariantIdx, FieldIdx)>)>, - - /// Maps from `HirId`s of const blocks (the `ExprKind::ConstBlock`, not the inner expression's) - /// to the `DefId` of the corresponding inline const. - pub inline_consts: FxIndexMap, } impl<'tcx> TypeckResults<'tcx> { @@ -253,7 +249,6 @@ impl<'tcx> TypeckResults<'tcx> { treat_byte_string_as_slice: Default::default(), closure_size_eval: Default::default(), offset_of_data: Default::default(), - inline_consts: Default::default(), } } diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs index 14d1b502474f..193f0d124bb8 100644 --- a/compiler/rustc_mir_build/src/build/mod.rs +++ b/compiler/rustc_mir_build/src/build/mod.rs @@ -568,8 +568,11 @@ fn construct_const<'a, 'tcx>( .. }) => (*span, ty.span), Node::AnonConst(ct) => (ct.span, ct.span), - Node::Expr(&hir::Expr { span, kind: hir::ExprKind::ConstBlock(_), .. }) => (span, span), - node => span_bug!(tcx.def_span(def), "can't build MIR for {def:?}: {node:#?}"), + Node::ConstBlock(_) => { + let span = tcx.def_span(def); + (span, span) + } + _ => span_bug!(tcx.def_span(def), "can't build MIR for {:?}", def), }; let infcx = tcx.infer_ctxt().build(); diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index bd66257e6b68..28f9300b97a8 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -671,9 +671,9 @@ impl<'tcx> Cx<'tcx> { ExprKind::OffsetOf { container, fields } } - hir::ExprKind::ConstBlock(body) => { - let ty = self.typeck_results().node_type(body.hir_id); - let did = self.typeck_results().inline_consts[&expr.hir_id.local_id].into(); + hir::ExprKind::ConstBlock(ref anon_const) => { + let ty = self.typeck_results().node_type(anon_const.hir_id); + let did = anon_const.def_id.to_def_id(); let typeck_root_def_id = tcx.typeck_root_def_id(did); let parent_args = tcx.erase_regions(GenericArgs::identity_for_item(tcx, typeck_root_def_id)); diff --git a/compiler/rustc_mir_build/src/thir/cx/mod.rs b/compiler/rustc_mir_build/src/thir/cx/mod.rs index bd9e34ae80fc..244ac409fd38 100644 --- a/compiler/rustc_mir_build/src/thir/cx/mod.rs +++ b/compiler/rustc_mir_build/src/thir/cx/mod.rs @@ -165,7 +165,7 @@ impl<'tcx> Cx<'tcx> { &'a mut self, owner_id: HirId, fn_decl: &'tcx hir::FnDecl<'tcx>, - body: &hir::Body<'tcx>, + body: &'tcx hir::Body<'tcx>, ) -> impl Iterator> + 'a { let fn_sig = self.typeck_results.liberated_fn_sigs()[owner_id]; diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index 145a40ca3cd6..158ca91fcf13 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -637,13 +637,15 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { /// Converts inline const patterns. fn lower_inline_const( &mut self, - expr: &'tcx hir::Expr<'tcx>, + block: &'tcx hir::ConstBlock, id: hir::HirId, span: Span, ) -> PatKind<'tcx> { let tcx = self.tcx; - let def_id = self.typeck_results.inline_consts[&id.local_id]; - let ty = tcx.typeck(def_id).node_type(expr.hir_id); + let def_id = block.def_id; + let body_id = block.body; + let expr = &tcx.hir().body(body_id).value; + let ty = tcx.typeck(def_id).node_type(block.hir_id); // Special case inline consts that are just literals. This is solely // a performance optimization, as we could also just go through the regular diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index b66e3d66f4d3..9d88f8bf9bec 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -222,12 +222,6 @@ fn mir_keys(tcx: TyCtxt<'_>, (): ()) -> FxIndexSet { // All body-owners have MIR associated with them. set.extend(tcx.hir().body_owners()); - // Inline consts' bodies are created in - // typeck instead of during ast lowering, like all other bodies so far. - for def_id in tcx.hir().body_owners() { - set.extend(tcx.typeck(def_id).inline_consts.values()) - } - // Additionally, tuple struct/variant constructors have MIR, but // they don't have a BodyId, so we need to build them separately. struct GatherCtors<'a> { diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 8f733b4fcbbc..29a84078c51d 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -25,8 +25,8 @@ use rustc_ast::tokenstream::{AttributesData, DelimSpacing, DelimSpan, Spacing}; use rustc_ast::tokenstream::{TokenStream, TokenTree, TokenTreeCursor}; use rustc_ast::util::case::Case; use rustc_ast::{ - self as ast, AttrArgs, AttrArgsEq, AttrId, ByRef, Const, CoroutineKind, DelimArgs, Expr, - ExprKind, Extern, HasAttrs, HasTokens, Mutability, Recovered, Safety, StrLit, Visibility, + self as ast, AnonConst, AttrArgs, AttrArgsEq, AttrId, ByRef, Const, CoroutineKind, DelimArgs, + Expr, ExprKind, Extern, HasAttrs, HasTokens, Mutability, Recovered, Safety, StrLit, Visibility, VisibilityKind, DUMMY_NODE_ID, }; use rustc_ast_pretty::pprust; @@ -1262,9 +1262,12 @@ impl<'a> Parser<'a> { } self.eat_keyword(kw::Const); let (attrs, blk) = self.parse_inner_attrs_and_block()?; - let expr = self.mk_expr(blk.span, ExprKind::Block(blk, None)); - let blk_span = expr.span; - Ok(self.mk_expr_with_attrs(span.to(blk_span), ExprKind::ConstBlock(expr), attrs)) + let anon_const = AnonConst { + id: DUMMY_NODE_ID, + value: self.mk_expr(blk.span, ExprKind::Block(blk, None)), + }; + let blk_span = anon_const.value.span; + Ok(self.mk_expr_with_attrs(span.to(blk_span), ExprKind::ConstBlock(anon_const), attrs)) } /// Parses mutability (`mut` or nothing). diff --git a/compiler/rustc_passes/src/check_const.rs b/compiler/rustc_passes/src/check_const.rs index 71f3e8b7b5d9..3f6eccbd5a52 100644 --- a/compiler/rustc_passes/src/check_const.rs +++ b/compiler/rustc_passes/src/check_const.rs @@ -196,6 +196,11 @@ impl<'tcx> Visitor<'tcx> for CheckConstVisitor<'tcx> { self.recurse_into(kind, None, |this| intravisit::walk_anon_const(this, anon)); } + fn visit_inline_const(&mut self, block: &'tcx hir::ConstBlock) { + let kind = Some(hir::ConstContext::Const { inline: true }); + self.recurse_into(kind, None, |this| intravisit::walk_inline_const(this, block)); + } + fn visit_body(&mut self, body: &hir::Body<'tcx>) { let owner = self.tcx.hir().body_owner_def_id(body.id()); let kind = self.tcx.hir().body_const_context(owner); @@ -223,11 +228,6 @@ impl<'tcx> Visitor<'tcx> for CheckConstVisitor<'tcx> { self.const_check_violated(expr, e.span); } } - hir::ExprKind::ConstBlock(expr) => { - let kind = Some(hir::ConstContext::Const { inline: true }); - self.recurse_into(kind, None, |this| intravisit::walk_expr(this, expr)); - return; - } _ => {} } diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index 0049afff528c..ddc50e2b811a 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -587,16 +587,6 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> { hir::ExprKind::OffsetOf(..) => { self.handle_offset_of(expr); } - hir::ExprKind::ConstBlock(expr) => { - // When inline const blocks are used in pattern position, paths - // referenced by it should be considered as used. - let in_pat = mem::replace(&mut self.in_pat, false); - - intravisit::walk_expr(self, expr); - - self.in_pat = in_pat; - return; - } _ => (), } @@ -658,6 +648,17 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> { self.in_pat = in_pat; } + + fn visit_inline_const(&mut self, c: &'tcx hir::ConstBlock) { + // When inline const blocks are used in pattern position, paths + // referenced by it should be considered as used. + let in_pat = mem::replace(&mut self.in_pat, false); + + self.live_symbols.insert(c.def_id); + intravisit::walk_inline_const(self, c); + + self.in_pat = in_pat; + } } fn has_allow_dead_code_or_lang_attr( diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs index 698f15015c48..dfa7dfa33981 100644 --- a/compiler/rustc_passes/src/liveness.rs +++ b/compiler/rustc_passes/src/liveness.rs @@ -147,11 +147,6 @@ fn check_liveness(tcx: TyCtxt<'_>, def_id: LocalDefId) { return; } - // Don't run for inline consts, they are collected together with their parent - if let DefKind::InlineConst = tcx.def_kind(def_id) { - return; - } - // Don't run unused pass for #[naked] if tcx.has_attr(def_id.to_def_id(), sym::naked) { return; @@ -1148,13 +1143,12 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { } hir::ExprKind::Lit(..) + | hir::ExprKind::ConstBlock(..) | hir::ExprKind::Err(_) | hir::ExprKind::Path(hir::QPath::TypeRelative(..)) | hir::ExprKind::Path(hir::QPath::LangItem(..)) | hir::ExprKind::OffsetOf(..) => succ, - hir::ExprKind::ConstBlock(expr) => self.propagate_through_expr(expr, succ), - // Note that labels have been resolved, so we don't need to look // at the label ident hir::ExprKind::Block(ref blk, _) => self.propagate_through_block(blk, succ), diff --git a/compiler/rustc_passes/src/loops.rs b/compiler/rustc_passes/src/loops.rs index 737310e5c04a..2587a18b8c89 100644 --- a/compiler/rustc_passes/src/loops.rs +++ b/compiler/rustc_passes/src/loops.rs @@ -93,6 +93,10 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> { self.with_context(Constant, |v| intravisit::walk_anon_const(v, c)); } + fn visit_inline_const(&mut self, c: &'hir hir::ConstBlock) { + self.with_context(Constant, |v| intravisit::walk_inline_const(v, c)); + } + fn visit_fn( &mut self, fk: hir::intravisit::FnKind<'hir>, @@ -285,9 +289,6 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> { self.cx_stack.len() - 1, ) } - hir::ExprKind::ConstBlock(expr) => { - self.with_context(Constant, |v| intravisit::walk_expr(v, expr)); - } _ => intravisit::walk_expr(self, e), } } diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index d7416ead325d..ca97d10617b6 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -340,6 +340,16 @@ impl<'a, 'b, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'b, 'tcx> { ExprKind::Gen(_, _, _) => { self.create_def(expr.id, kw::Empty, DefKind::Closure, expr.span) } + ExprKind::ConstBlock(ref constant) => { + let def = self.create_def( + constant.id, + kw::Empty, + DefKind::InlineConst, + constant.value.span, + ); + self.with_parent(def, |this| visit::walk_anon_const(this, constant)); + return; + } _ => self.parent_def, }; diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index c7c95addf492..b0adc3775f66 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -4535,10 +4535,8 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { self.visit_expr(elem); self.resolve_anon_const(ct, AnonConstKind::ConstArg(IsRepeatExpr::Yes)); } - ExprKind::ConstBlock(ref expr) => { - self.resolve_anon_const_manual(false, AnonConstKind::InlineConst, |this| { - this.visit_expr(expr) - }); + ExprKind::ConstBlock(ref ct) => { + self.resolve_anon_const(ct, AnonConstKind::InlineConst); } ExprKind::Index(ref elem, ref idx, _) => { self.resolve_expr(elem, Some(expr)); diff --git a/src/tools/clippy/clippy_utils/src/consts.rs b/src/tools/clippy/clippy_utils/src/consts.rs index cd88ccd87cf0..5c9cad2b45d4 100644 --- a/src/tools/clippy/clippy_utils/src/consts.rs +++ b/src/tools/clippy/clippy_utils/src/consts.rs @@ -6,7 +6,7 @@ use crate::{clip, is_direct_expn_of, sext, unsext}; use rustc_ast::ast::{self, LitFloatType, LitKind}; use rustc_data_structures::sync::Lrc; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{BinOp, BinOpKind, Block, Expr, ExprKind, HirId, Item, ItemKind, Node, QPath, UnOp}; +use rustc_hir::{BinOp, BinOpKind, Block, ConstBlock, Expr, ExprKind, HirId, Item, ItemKind, Node, QPath, UnOp}; use rustc_lexer::tokenize; use rustc_lint::LateContext; use rustc_middle::mir::interpret::{alloc_range, Scalar}; @@ -412,7 +412,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { /// Simple constant folding: Insert an expression, get a constant or none. pub fn expr(&mut self, e: &Expr<'_>) -> Option> { match e.kind { - ExprKind::ConstBlock(e) | ExprKind::DropTemps(e) => self.expr(e), + ExprKind::ConstBlock(ConstBlock { body, .. }) => self.expr(self.lcx.tcx.hir().body(body).value), ExprKind::DropTemps(e) => self.expr(e), ExprKind::Path(ref qpath) => { self.fetch_path_and_apply(qpath, e.hir_id, self.typeck_results.expr_ty(e), |this, result| { let result = mir_to_const(this.lcx, result)?; @@ -490,7 +490,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { /// leaves the local crate. pub fn expr_is_empty(&mut self, e: &Expr<'_>) -> Option { match e.kind { - ExprKind::ConstBlock(e) | ExprKind::DropTemps(e) => self.expr_is_empty(e), + ExprKind::ConstBlock(ConstBlock { body, .. }) => self.expr_is_empty(self.lcx.tcx.hir().body(body).value), ExprKind::DropTemps(e) => self.expr_is_empty(e), ExprKind::Path(ref qpath) => { if !self .typeck_results diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs index c649c1794684..36634817fc91 100644 --- a/src/tools/clippy/clippy_utils/src/hir_utils.rs +++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs @@ -295,7 +295,7 @@ impl HirEqInterExpr<'_, '_, '_> { self.eq_expr(lx, rx) && self.eq_ty(lt, rt) }, (&ExprKind::Closure(_l), &ExprKind::Closure(_r)) => false, - (&ExprKind::ConstBlock(lb), &ExprKind::ConstBlock(rb)) => self.eq_expr(lb, rb), + (&ExprKind::ConstBlock(lb), &ExprKind::ConstBlock(rb)) => self.eq_body(lb.body, rb.body), (&ExprKind::Continue(li), &ExprKind::Continue(ri)) => { both(&li.label, &ri.label, |l, r| l.ident.name == r.ident.name) }, @@ -769,8 +769,8 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { // closures inherit TypeckResults self.hash_expr(self.cx.tcx.hir().body(body).value); }, - ExprKind::ConstBlock(l_id) => { - self.hash_expr(l_id); + ExprKind::ConstBlock(ref l_id) => { + self.hash_body(l_id.body); }, ExprKind::DropTemps(e) | ExprKind::Yield(e, _) => { self.hash_expr(e); diff --git a/src/tools/clippy/tests/ui/arithmetic_side_effects.stderr b/src/tools/clippy/tests/ui/arithmetic_side_effects.stderr index df14ff396f6c..8039c0bfa248 100644 --- a/src/tools/clippy/tests/ui/arithmetic_side_effects.stderr +++ b/src/tools/clippy/tests/ui/arithmetic_side_effects.stderr @@ -1,35 +1,11 @@ -error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:188:36 - | -LL | let _ = const { let mut n = 1; n += 1; n }; - | ^^^^^^ - | - = note: `-D clippy::arithmetic-side-effects` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::arithmetic_side_effects)]` - -error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:191:40 - | -LL | let _ = const { let mut n = 1; n = n + 1; n }; - | ^^^^^ - -error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:194:40 - | -LL | let _ = const { let mut n = 1; n = 1 + n; n }; - | ^^^^^ - -error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:200:59 - | -LL | let _ = const { let mut n = 1; n = -1; n = -(-1); n = -n; n }; - | ^^ - error: arithmetic operation that can potentially result in unexpected side-effects --> tests/ui/arithmetic_side_effects.rs:304:5 | LL | _n += 1; | ^^^^^^^ + | + = note: `-D clippy::arithmetic-side-effects` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::arithmetic_side_effects)]` error: arithmetic operation that can potentially result in unexpected side-effects --> tests/ui/arithmetic_side_effects.rs:305:5 @@ -751,5 +727,5 @@ error: arithmetic operation that can potentially result in unexpected side-effec LL | one.sub_assign(1); | ^^^^^^^^^^^^^^^^^ -error: aborting due to 125 previous errors +error: aborting due to 121 previous errors diff --git a/tests/mir-opt/building/custom/consts.consts.built.after.mir b/tests/mir-opt/building/custom/consts.consts.built.after.mir index 7b6964849d4b..a011fadcef11 100644 --- a/tests/mir-opt/building/custom/consts.consts.built.after.mir +++ b/tests/mir-opt/building/custom/consts.consts.built.after.mir @@ -10,7 +10,7 @@ fn consts() -> () { bb0: { _1 = const 5_u8; - _2 = const consts::::{constant#1}; + _2 = const consts::::{constant#0}; _3 = const C; _4 = const D; _5 = consts::<10>; diff --git a/tests/pretty/stmt_expr_attributes.rs b/tests/pretty/stmt_expr_attributes.rs index f90412682110..5eb7d2fcae36 100644 --- a/tests/pretty/stmt_expr_attributes.rs +++ b/tests/pretty/stmt_expr_attributes.rs @@ -206,7 +206,12 @@ fn _11() { let _ = (); () }; - let const {} = #[rustc_dummy] const {}; + let const { + #![rustc_dummy] + } = + #[rustc_dummy] const { + #![rustc_dummy] + }; let mut x = 0; let _ = (#[rustc_dummy] x) = 15; let _ = (#[rustc_dummy] x) += 15; diff --git a/tests/ui/lint/non-local-defs/consts.stderr b/tests/ui/lint/non-local-defs/consts.stderr index 48bdaa60823f..2756ea401387 100644 --- a/tests/ui/lint/non-local-defs/consts.stderr +++ b/tests/ui/lint/non-local-defs/consts.stderr @@ -67,13 +67,18 @@ LL | impl Test { warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item --> $DIR/consts.rs:50:9 | -LL | fn main() { - | --------- move the `impl` block outside of this function `main` -... -LL | impl Test { - | ^^^^^---- - | | - | `Test` is not local +LL | const { + | ___________- +LL | | impl Test { + | | ^^^^^---- + | | | + | | `Test` is not local +LL | | +LL | | fn hoo() {} +... | +LL | | 1 +LL | | }; + | |_____- move the `impl` block outside of this inline constant `` and up 2 bodies | = note: methods and associated constants are still usable outside the current expression, only `impl Local` and `impl dyn Local` can ever be private, and only if the type is nested in the same item as the `impl` = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue diff --git a/tests/ui/unpretty/expanded-exhaustive.stdout b/tests/ui/unpretty/expanded-exhaustive.stdout index 9e45f57af354..137a8aa5510d 100644 --- a/tests/ui/unpretty/expanded-exhaustive.stdout +++ b/tests/ui/unpretty/expanded-exhaustive.stdout @@ -83,8 +83,7 @@ mod expressions { fn expr_const_block() { const {}; const { 1 }; - const - { + const { struct S; }; } From bbcd8794d03e4061a061a199a6c3c47dc65054c3 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 3 Jun 2024 09:39:26 +0000 Subject: [PATCH 54/92] Add regression test --- .../inline-const/const_block_pat_liveness.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 tests/ui/inline-const/const_block_pat_liveness.rs diff --git a/tests/ui/inline-const/const_block_pat_liveness.rs b/tests/ui/inline-const/const_block_pat_liveness.rs new file mode 100644 index 000000000000..26393a4f65b8 --- /dev/null +++ b/tests/ui/inline-const/const_block_pat_liveness.rs @@ -0,0 +1,18 @@ +//! This test used to ICE because const blocks didn't have a body +//! anymore, making a lot of logic very fragile around handling the +//! HIR of a const block. +//! https://github.com/rust-lang/rust/issues/125846 + +//@ check-pass + +#![feature(inline_const_pat)] + +fn main() { + match 0 { + const { + let a = 10_usize; + *&a + } + | _ => {} + } +} From 6110a51898e2f3754726e67fac169cbd18b41ce2 Mon Sep 17 00:00:00 2001 From: Pietro Albini Date: Fri, 7 Jun 2024 10:36:26 +0200 Subject: [PATCH 55/92] set version number to 1.81.0 --- src/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/version b/src/version index aaceec04e040..dbd41264aa9f 100644 --- a/src/version +++ b/src/version @@ -1 +1 @@ -1.80.0 +1.81.0 From 321d69d1955fe02d25129a353218f1e56e28a3bb Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Fri, 7 Jun 2024 08:50:18 +0000 Subject: [PATCH 56/92] Fix codegen test --- .../cfi/emit-type-metadata-id-itanium-cxx-abi-paths.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-paths.rs b/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-paths.rs index 4e5ad8ad4a93..9870ecc69baf 100644 --- a/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-paths.rs +++ b/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-paths.rs @@ -82,9 +82,9 @@ pub fn foo12(_: &Type4, _: &Type4, _: &Type4) {} // CHECK: ![[TYPE4]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtNCNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo11{{[{}][{}]}}closure{{[}][}]}}3FooEE"} // CHECK: ![[TYPE5]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtNCNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo11{{[{}][{}]}}closure{{[}][}]}}3FooES0_E"} // CHECK: ![[TYPE6]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtNCNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo11{{[{}][{}]}}closure{{[}][}]}}3FooES0_S0_E"} -// CHECK: ![[TYPE7]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo3FooEE"} -// CHECK: ![[TYPE8]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo3FooES0_E"} -// CHECK: ![[TYPE9]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo3FooES0_S0_E"} +// CHECK: ![[TYPE7]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtNkNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo12{{[{}][{}]}}constant{{[}][}]}}3FooEE"} +// CHECK: ![[TYPE8]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtNkNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo12{{[{}][{}]}}constant{{[}][}]}}3FooES0_E"} +// CHECK: ![[TYPE9]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtNkNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo12{{[{}][{}]}}constant{{[}][}]}}3FooES0_S0_E"} // CHECK: ![[TYPE10]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NvNINvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo8{{[{}][{}]}}impl{{[}][}]}}3barEE"} // CHECK: ![[TYPE11]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NvNINvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo8{{[{}][{}]}}impl{{[}][}]}}3barES0_E"} // CHECK: ![[TYPE12]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NvNINvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo8{{[{}][{}]}}impl{{[}][}]}}3barES0_S0_E"} From 585c89849510a367a6a84907641ce7ecd6a96589 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Thu, 6 Jun 2024 18:59:18 +0200 Subject: [PATCH 57/92] Do not run run-make tests in the test source directory --- src/tools/compiletest/src/runtest.rs | 19 +++++++++++++++++-- src/tools/compiletest/src/util.rs | 16 +++++++++++++++- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 2c425a07b2ac..8ef1d46bef64 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -15,7 +15,7 @@ use crate::errors::{self, Error, ErrorKind}; use crate::header::TestProps; use crate::json; use crate::read2::{read2_abbreviated, Truncated}; -use crate::util::{add_dylib_path, dylib_env_var, logv, PathBufExt}; +use crate::util::{add_dylib_path, copy_dir_all, dylib_env_var, logv, PathBufExt}; use crate::ColorConfig; use colored::Colorize; use miropt_test_tools::{files_for_miropt_test, MiroptTest, MiroptTestFile}; @@ -3458,6 +3458,21 @@ impl<'test> TestCx<'test> { let rmake_out_dir = base_dir.join("rmake_out"); create_dir_all(&rmake_out_dir).unwrap(); + // Copy all input files (apart from rmake.rs) to the temporary directory, + // so that the input directory structure from `tests/run-make/` is mirrored + // to the `rmake_out` directory. + for path in walkdir::WalkDir::new(&self.testpaths.file).min_depth(1) { + let path = path.unwrap().path().to_path_buf(); + if path.file_name().map(|s| s != OsStr::new("rmake.rs")).unwrap_or(false) { + let target = rmake_out_dir.join(path.strip_prefix(&self.testpaths.file).unwrap()); + if path.is_dir() { + copy_dir_all(&path, target).unwrap(); + } else { + fs::copy(&path, target).unwrap(); + } + } + } + // HACK: assume stageN-target, we only want stageN. let stage = self.config.stage_id.split('-').next().unwrap(); @@ -3559,7 +3574,7 @@ impl<'test> TestCx<'test> { let target_rpath_env_path = env::join_paths(target_rpath_env_path).unwrap(); let mut cmd = Command::new(&recipe_bin); - cmd.current_dir(&self.testpaths.file) + cmd.current_dir(&rmake_out_dir) .stdout(Stdio::piped()) .stderr(Stdio::piped()) .env("LD_LIB_PATH_ENVVAR", dylib_env_var()) diff --git a/src/tools/compiletest/src/util.rs b/src/tools/compiletest/src/util.rs index 09a7f0395cfb..ec20bda8c189 100644 --- a/src/tools/compiletest/src/util.rs +++ b/src/tools/compiletest/src/util.rs @@ -1,7 +1,7 @@ use crate::common::Config; use std::env; use std::ffi::OsStr; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::process::Command; use tracing::*; @@ -76,3 +76,17 @@ pub fn add_dylib_path(cmd: &mut Command, paths: impl Iterator, dst: impl AsRef) -> std::io::Result<()> { + std::fs::create_dir_all(&dst)?; + for entry in std::fs::read_dir(src)? { + let entry = entry?; + let ty = entry.file_type()?; + if ty.is_dir() { + copy_dir_all(entry.path(), dst.as_ref().join(entry.file_name()))?; + } else { + std::fs::copy(entry.path(), dst.as_ref().join(entry.file_name()))?; + } + } + Ok(()) +} From 3f20c721ceed518f3a94c2a29582b1d8789fb29f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Thu, 6 Jun 2024 18:59:43 +0200 Subject: [PATCH 58/92] Fix `mixing-formats` run-make test --- src/tools/run-make-support/src/lib.rs | 24 +++++++++++++++++++++- src/tools/run-make-support/src/rustc.rs | 5 +++-- tests/run-make/mixing-formats/rmake.rs | 27 +++++++++---------------- 3 files changed, 36 insertions(+), 20 deletions(-) diff --git a/src/tools/run-make-support/src/lib.rs b/src/tools/run-make-support/src/lib.rs index d74a0272a627..2b5d983faa31 100644 --- a/src/tools/run-make-support/src/lib.rs +++ b/src/tools/run-make-support/src/lib.rs @@ -227,7 +227,7 @@ pub fn set_host_rpath(cmd: &mut Command) { let ld_lib_path_envvar = env_var("LD_LIB_PATH_ENVVAR"); cmd.env(&ld_lib_path_envvar, { let mut paths = vec![]; - paths.push(PathBuf::from(env_var("TMPDIR"))); + paths.push(env::current_dir().unwrap()); paths.push(PathBuf::from(env_var("HOST_RPATH_DIR"))); for p in env::split_paths(&env_var(&ld_lib_path_envvar)) { paths.push(p.to_path_buf()); @@ -315,6 +315,28 @@ pub fn assert_not_contains(haystack: &str, needle: &str) { } } +/// This function is designed for running commands in a temporary directory +/// that is cleared after the function ends. +/// +/// What this function does: +/// 1) Creates a temporary directory (`tmpdir`) +/// 2) Copies all files from the current directory to `tmpdir` +/// 3) Changes the current working directory to `tmpdir` +/// 4) Calls `callback` +/// 5) Switches working directory back to the original one +/// 6) Removes `tmpdir` +/// Switch current working directory to a temporary directory +pub fn run_in_tmpdir(callback: F) { + let original_dir = env::current_dir().unwrap(); + let tmpdir = original_dir.join("../temporary-directory"); + copy_dir_all(".", &tmpdir); + + env::set_current_dir(&tmpdir).unwrap(); + callback(); + env::set_current_dir(original_dir).unwrap(); + fs::remove_dir_all(tmpdir).unwrap(); +} + /// Implement common helpers for command wrappers. This assumes that the command wrapper is a struct /// containing a `cmd: Command` field and a `output` function. The provided helpers are: /// diff --git a/src/tools/run-make-support/src/rustc.rs b/src/tools/run-make-support/src/rustc.rs index e923c3cf4ad8..bcdedb2722fa 100644 --- a/src/tools/run-make-support/src/rustc.rs +++ b/src/tools/run-make-support/src/rustc.rs @@ -1,9 +1,10 @@ +use std::env; use std::ffi::{OsStr, OsString}; use std::io::Write; use std::path::Path; use std::process::{Command, Output, Stdio}; -use crate::{env_var, handle_failed_output, set_host_rpath, tmp_dir}; +use crate::{env_var, handle_failed_output, set_host_rpath}; /// Construct a new `rustc` invocation. pub fn rustc() -> Rustc { @@ -28,7 +29,7 @@ fn setup_common() -> Command { let rustc = env_var("RUSTC"); let mut cmd = Command::new(rustc); set_host_rpath(&mut cmd); - cmd.arg("--out-dir").arg(tmp_dir()).arg("-L").arg(tmp_dir()); + cmd.arg("-L").arg(env::current_dir().unwrap()); cmd } diff --git a/tests/run-make/mixing-formats/rmake.rs b/tests/run-make/mixing-formats/rmake.rs index 0d40b0325f77..9d072d9a6e4f 100644 --- a/tests/run-make/mixing-formats/rmake.rs +++ b/tests/run-make/mixing-formats/rmake.rs @@ -12,31 +12,24 @@ //@ ignore-cross-compile -use run_make_support::{rustc, tmp_dir}; +use run_make_support::{rustc, tmp_dir, run_in_tmpdir}; use std::fs; -fn test_with_teardown(rustc_calls: impl Fn()) { - rustc_calls(); - //FIXME(Oneirical): This should be replaced with the run-make-support fs wrappers. - fs::remove_dir_all(tmp_dir()).unwrap(); - fs::create_dir(tmp_dir()).unwrap(); -} - fn main() { - test_with_teardown(|| { + run_in_tmpdir(|| { // Building just baz rustc().crate_type("rlib").input("foo.rs").run(); rustc().crate_type("dylib").input("bar1.rs").arg("-Cprefer-dynamic").run(); rustc().crate_type("dylib,rlib").input("baz.rs").arg("-Cprefer-dynamic").run(); rustc().crate_type("bin").input("baz.rs").run(); }); - test_with_teardown(|| { + run_in_tmpdir(|| { rustc().crate_type("dylib").input("foo.rs").arg("-Cprefer-dynamic").run(); rustc().crate_type("rlib").input("bar1.rs").run(); rustc().crate_type("dylib,rlib").input("baz.rs").arg("-Cprefer-dynamic").run(); rustc().crate_type("bin").input("baz.rs").run(); }); - test_with_teardown(|| { + run_in_tmpdir(|| { // Building baz2 rustc().crate_type("rlib").input("foo.rs").run(); rustc().crate_type("dylib").input("bar1.rs").arg("-Cprefer-dynamic").run(); @@ -44,42 +37,42 @@ fn main() { rustc().crate_type("dylib").input("baz2.rs").run_fail_assert_exit_code(1); rustc().crate_type("bin").input("baz2.rs").run_fail_assert_exit_code(1); }); - test_with_teardown(|| { + run_in_tmpdir(|| { rustc().crate_type("rlib").input("foo.rs").run(); rustc().crate_type("rlib").input("bar1.rs").run(); rustc().crate_type("dylib").input("bar2.rs").arg("-Cprefer-dynamic").run(); rustc().crate_type("dylib,rlib").input("baz2.rs").run(); rustc().crate_type("bin").input("baz2.rs").run(); }); - test_with_teardown(|| { + run_in_tmpdir(|| { rustc().crate_type("rlib").input("foo.rs").run(); rustc().crate_type("dylib").input("bar1.rs").arg("-Cprefer-dynamic").run(); rustc().crate_type("rlib").input("bar2.rs").run(); rustc().crate_type("dylib,rlib").input("baz2.rs").arg("-Cprefer-dynamic").run(); rustc().crate_type("bin").input("baz2.rs").run(); }); - test_with_teardown(|| { + run_in_tmpdir(|| { rustc().crate_type("rlib").input("foo.rs").run(); rustc().crate_type("rlib").input("bar1.rs").run(); rustc().crate_type("rlib").input("bar2.rs").run(); rustc().crate_type("dylib,rlib").input("baz2.rs").arg("-Cprefer-dynamic").run(); rustc().crate_type("bin").input("baz2.rs").run(); }); - test_with_teardown(|| { + run_in_tmpdir(|| { rustc().crate_type("dylib").input("foo.rs").arg("-Cprefer-dynamic").run(); rustc().crate_type("rlib").input("bar1.rs").run(); rustc().crate_type("rlib").input("bar2.rs").run(); rustc().crate_type("dylib,rlib").input("baz2.rs").arg("-Cprefer-dynamic").run(); rustc().crate_type("bin").input("baz2.rs").run(); }); - test_with_teardown(|| { + run_in_tmpdir(|| { rustc().crate_type("dylib").input("foo.rs").arg("-Cprefer-dynamic").run(); rustc().crate_type("dylib").input("bar1.rs").arg("-Cprefer-dynamic").run(); rustc().crate_type("rlib").input("bar2.rs").run(); rustc().crate_type("dylib,rlib").input("baz2.rs").run(); rustc().crate_type("bin").input("baz2.rs").run(); }); - test_with_teardown(|| { + run_in_tmpdir(|| { rustc().crate_type("dylib").input("foo.rs").arg("-Cprefer-dynamic").run(); rustc().crate_type("rlib").input("bar1.rs").run(); rustc().crate_type("dylib").input("bar2.rs").arg("-Cprefer-dynamic").run(); From 7ebcc37d653bf6551bf53a2453750c05dc745fc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Thu, 6 Jun 2024 20:40:08 +0200 Subject: [PATCH 59/92] Fix `bare-outfile` test --- tests/run-make/bare-outfile/rmake.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/tests/run-make/bare-outfile/rmake.rs b/tests/run-make/bare-outfile/rmake.rs index 82d0fab5073b..badf1396b73e 100644 --- a/tests/run-make/bare-outfile/rmake.rs +++ b/tests/run-make/bare-outfile/rmake.rs @@ -3,13 +3,9 @@ //@ ignore-cross-compile -use run_make_support::{run, rustc, tmp_dir}; -use std::env; -use std::fs; +use run_make_support::{run, rustc}; fn main() { - fs::copy("foo.rs", tmp_dir().join("foo.rs")).unwrap(); - env::set_current_dir(tmp_dir()); rustc().output("foo").input("foo.rs").run(); run("foo"); } From 94ccb9b34d5cd9472bd2651055efb6a75d6649c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Thu, 6 Jun 2024 20:47:00 +0200 Subject: [PATCH 60/92] Remove dependence on `tmp_dir` And also remove some environment variables passed to compilation of `rmake.rs`. --- src/tools/compiletest/src/runtest.rs | 18 ++---------------- src/tools/run-make-support/src/cc.rs | 9 ++++----- src/tools/run-make-support/src/clang.rs | 6 +++--- src/tools/run-make-support/src/lib.rs | 11 +++-------- src/tools/run-make-support/src/run.rs | 4 ++-- 5 files changed, 14 insertions(+), 34 deletions(-) diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 8ef1d46bef64..c50dd2d5a990 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -3533,18 +3533,11 @@ impl<'test> TestCx<'test> { .env("S", &src_root) .env("RUST_BUILD_STAGE", &self.config.stage_id) .env("RUSTC", cwd.join(&self.config.rustc_path)) - .env("TMPDIR", &rmake_out_dir) .env("LD_LIB_PATH_ENVVAR", dylib_env_var()) .env(dylib_env_var(), &host_dylib_env_paths) .env("HOST_RPATH_DIR", cwd.join(&self.config.compile_lib_path)) .env("TARGET_RPATH_DIR", cwd.join(&self.config.run_lib_path)) - .env("LLVM_COMPONENTS", &self.config.llvm_components) - // We for sure don't want these tests to run in parallel, so make - // sure they don't have access to these vars if we run via `make` - // at the top level - .env_remove("MAKEFLAGS") - .env_remove("MFLAGS") - .env_remove("CARGO_MAKEFLAGS"); + .env("LLVM_COMPONENTS", &self.config.llvm_components); if std::env::var_os("COMPILETEST_FORCE_STAGE0").is_some() { let mut stage0_sysroot = build_root.clone(); @@ -3585,16 +3578,9 @@ impl<'test> TestCx<'test> { .env("S", &src_root) .env("RUST_BUILD_STAGE", &self.config.stage_id) .env("RUSTC", cwd.join(&self.config.rustc_path)) - .env("TMPDIR", &rmake_out_dir) .env("HOST_RPATH_DIR", cwd.join(&self.config.compile_lib_path)) .env("TARGET_RPATH_DIR", cwd.join(&self.config.run_lib_path)) - .env("LLVM_COMPONENTS", &self.config.llvm_components) - // We for sure don't want these tests to run in parallel, so make - // sure they don't have access to these vars if we run via `make` - // at the top level - .env_remove("MAKEFLAGS") - .env_remove("MFLAGS") - .env_remove("CARGO_MAKEFLAGS"); + .env("LLVM_COMPONENTS", &self.config.llvm_components); if let Some(ref rustdoc) = self.config.rustdoc_path { cmd.env("RUSTDOC", cwd.join(rustdoc)); diff --git a/src/tools/run-make-support/src/cc.rs b/src/tools/run-make-support/src/cc.rs index 1472a39305e3..798f0b4c23b5 100644 --- a/src/tools/run-make-support/src/cc.rs +++ b/src/tools/run-make-support/src/cc.rs @@ -54,8 +54,7 @@ impl Cc { self } - /// Specify `-o` or `-Fe`/`-Fo` depending on platform/compiler. This assumes that the executable - /// is under `$TMPDIR`. + /// Specify `-o` or `-Fe`/`-Fo` depending on platform/compiler. pub fn out_exe(&mut self, name: &str) -> &mut Self { // Ref: tools.mk (irrelevant lines omitted): // @@ -69,13 +68,13 @@ impl Cc { // ``` if is_msvc() { - let fe_path = cygpath_windows(tmp_dir().join(bin_name(name))); - let fo_path = cygpath_windows(tmp_dir().join(format!("{name}.obj"))); + let fe_path = cygpath_windows(bin_name(name)); + let fo_path = cygpath_windows(format!("{name}.obj")); self.cmd.arg(format!("-Fe:{fe_path}")); self.cmd.arg(format!("-Fo:{fo_path}")); } else { self.cmd.arg("-o"); - self.cmd.arg(tmp_dir().join(name)); + self.cmd.arg(name); } self diff --git a/src/tools/run-make-support/src/clang.rs b/src/tools/run-make-support/src/clang.rs index 63c5af17c1db..f3c2a7446424 100644 --- a/src/tools/run-make-support/src/clang.rs +++ b/src/tools/run-make-support/src/clang.rs @@ -30,11 +30,11 @@ impl Clang { self } - /// Specify the name of the executable. The executable will be placed under `$TMPDIR`, and the - /// extension will be determined by [`bin_name`]. + /// Specify the name of the executable. The executable will be placed under the current directory + /// and the extension will be determined by [`bin_name`]. pub fn out_exe(&mut self, name: &str) -> &mut Self { self.cmd.arg("-o"); - self.cmd.arg(tmp_dir().join(bin_name(name))); + self.cmd.arg(bin_name(name)); self } diff --git a/src/tools/run-make-support/src/lib.rs b/src/tools/run-make-support/src/lib.rs index 2b5d983faa31..c4b74ce08845 100644 --- a/src/tools/run-make-support/src/lib.rs +++ b/src/tools/run-make-support/src/lib.rs @@ -45,11 +45,6 @@ pub fn env_var_os(name: &str) -> OsString { } } -/// Path of `TMPDIR` (a temporary build directory, not under `/tmp`). -pub fn tmp_dir() -> PathBuf { - env_var_os("TMPDIR").into() -} - /// `TARGET` pub fn target() -> String { env_var("TARGET") @@ -73,7 +68,7 @@ pub fn is_darwin() -> bool { /// Construct a path to a static library under `$TMPDIR` given the library name. This will return a /// path with `$TMPDIR` joined with platform-and-compiler-specific library name. pub fn static_lib(name: &str) -> PathBuf { - tmp_dir().join(static_lib_name(name)) + PathBuf::from(static_lib_name(name)) } pub fn python_command() -> Command { @@ -119,7 +114,7 @@ pub fn static_lib_name(name: &str) -> String { /// Construct a path to a dynamic library under `$TMPDIR` given the library name. This will return a /// path with `$TMPDIR` joined with platform-and-compiler-specific library name. pub fn dynamic_lib(name: &str) -> PathBuf { - tmp_dir().join(dynamic_lib_name(name)) + PathBuf::from(dynamic_lib_name(name)) } /// Construct the dynamic library name based on the platform. @@ -162,7 +157,7 @@ pub fn dynamic_lib_extension() -> &'static str { /// Construct a path to a rust library (rlib) under `$TMPDIR` given the library name. This will return a /// path with `$TMPDIR` joined with the library name. pub fn rust_lib(name: &str) -> PathBuf { - tmp_dir().join(rust_lib_name(name)) + PathBuf::from(rust_lib_name(name)) } /// Generate the name a rust library (rlib) would have. If you want the complete path, use diff --git a/src/tools/run-make-support/src/run.rs b/src/tools/run-make-support/src/run.rs index 8d8eafba99b7..20fc29650f87 100644 --- a/src/tools/run-make-support/src/run.rs +++ b/src/tools/run-make-support/src/run.rs @@ -8,13 +8,13 @@ use super::handle_failed_output; fn run_common(name: &str) -> (Command, Output) { let mut bin_path = PathBuf::new(); - bin_path.push(env_var("TMPDIR")); + bin_path.push(env::current_dir().unwrap()); bin_path.push(name); let ld_lib_path_envvar = env_var("LD_LIB_PATH_ENVVAR"); let mut cmd = Command::new(bin_path); cmd.env(&ld_lib_path_envvar, { let mut paths = vec![]; - paths.push(PathBuf::from(env_var("TMPDIR"))); + paths.push(env::current_dir().unwrap()); for p in env::split_paths(&env_var("TARGET_RPATH_ENV")) { paths.push(p.to_path_buf()); } From d86c9819087fbc64b7d0a638b2fd8584831bb661 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Thu, 6 Jun 2024 21:34:34 +0200 Subject: [PATCH 61/92] Remove all usages of `tmp_dir` from tests --- src/tools/run-make-support/src/cc.rs | 4 +--- src/tools/run-make-support/src/clang.rs | 2 +- src/tools/run-make-support/src/lib.rs | 5 +++++ tests/run-make/CURRENT_RUSTC_VERSION/rmake.rs | 10 +++++----- .../artifact-incr-cache-no-obj/rmake.rs | 6 ++---- tests/run-make/artifact-incr-cache/rmake.rs | 6 ++---- tests/run-make/box-struct-no-segfault/rmake.rs | 4 ++-- tests/run-make/c-link-to-rust-dylib/rmake.rs | 14 ++++---------- tests/run-make/cdylib/rmake.rs | 10 +++------- tests/run-make/compiler-builtins/Cargo.toml | 7 +++++++ tests/run-make/compiler-builtins/lib.rs | 1 + tests/run-make/compiler-builtins/rmake.rs | 18 +++--------------- tests/run-make/const-prop-lint/rmake.rs | 4 ++-- tests/run-make/core-no-oom-handling/rmake.rs | 4 ++-- .../run-make/cross-lang-lto-riscv-abi/rmake.rs | 6 +++--- tests/run-make/deref-impl-rustdoc-ice/rmake.rs | 4 ++-- tests/run-make/doctests-keep-binaries/rmake.rs | 14 ++++++-------- tests/run-make/doctests-runtool/rmake.rs | 8 +++----- tests/run-make/emit-named-files/rmake.rs | 4 ++-- tests/run-make/exit-code/rmake.rs | 4 ++-- .../rmake.rs | 4 ++-- .../incr-prev-body-beyond-eof/rmake.rs | 14 +++++++------- .../issue-107495-archive-permissions/rmake.rs | 4 ++-- .../issue-125484-used-dependencies/rmake.rs | 4 ++-- tests/run-make/manual-crate-name/rmake.rs | 5 +++-- tests/run-make/mixing-formats/rmake.rs | 2 +- tests/run-make/no-intermediate-extras/rmake.rs | 4 ++-- tests/run-make/non-pie-thread-local/rmake.rs | 6 +++--- .../non-unicode-in-incremental-dir/rmake.rs | 8 ++++---- .../notify-all-emit-artifacts/rmake.rs | 8 +++----- tests/run-make/panic-impl-transitive/rmake.rs | 9 ++------- tests/run-make/print-cfg/rmake.rs | 6 +++--- tests/run-make/print-to-output/rmake.rs | 5 +++-- tests/run-make/repr128-dwarf/rmake.rs | 8 ++++---- tests/run-make/reset-codegen-1/rmake.rs | 4 ++-- tests/run-make/resolve-rename/rmake.rs | 5 ++--- tests/run-make/rustdoc-determinism/rmake.rs | 7 ++++--- tests/run-make/rustdoc-map-file/rmake.rs | 4 ++-- tests/run-make/rustdoc-output-path/rmake.rs | 5 +++-- .../rustdoc-scrape-examples-macros/rmake.rs | 11 +++++------ .../rustdoc-scrape-examples-remap/scrape.rs | 7 +++---- .../rustdoc-target-spec-json-path/rmake.rs | 6 +++--- tests/run-make/rustdoc-test-args/rmake.rs | 6 +++--- tests/run-make/rustdoc-themes/rmake.rs | 7 ++++--- .../rustdoc-verify-output-files/rmake.rs | 6 +++--- .../rustdoc-with-out-dir-option/rmake.rs | 4 ++-- .../rustdoc-with-output-option/rmake.rs | 4 ++-- .../rustdoc-with-short-out-dir-option/rmake.rs | 4 ++-- tests/run-make/stdin-rustc/rmake.rs | 9 +++++---- tests/run-make/stdin-rustdoc/rmake.rs | 6 +++--- tests/run-make/wasm-abi/rmake.rs | 4 ++-- tests/run-make/wasm-custom-section/rmake.rs | 4 ++-- .../run-make/wasm-custom-sections-opt/rmake.rs | 4 ++-- .../run-make/wasm-export-all-symbols/rmake.rs | 9 +++------ tests/run-make/wasm-import-module/rmake.rs | 4 ++-- tests/run-make/wasm-panic-small/rmake.rs | 4 ++-- tests/run-make/wasm-spurious-import/rmake.rs | 4 ++-- .../wasm-stringify-ints-small/rmake.rs | 4 ++-- .../wasm-symbols-different-module/rmake.rs | 4 ++-- .../wasm-symbols-not-exported/rmake.rs | 10 +++++----- .../wasm-symbols-not-imported/rmake.rs | 10 +++++----- .../windows-binary-no-external-deps/rmake.rs | 4 ++-- tests/run-make/windows-spawn/rmake.rs | 5 ++--- tests/run-make/windows-ws2_32/rmake.rs | 5 ++--- 64 files changed, 182 insertions(+), 210 deletions(-) create mode 100644 tests/run-make/compiler-builtins/Cargo.toml create mode 100644 tests/run-make/compiler-builtins/lib.rs diff --git a/src/tools/run-make-support/src/cc.rs b/src/tools/run-make-support/src/cc.rs index 798f0b4c23b5..b33004974bf3 100644 --- a/src/tools/run-make-support/src/cc.rs +++ b/src/tools/run-make-support/src/cc.rs @@ -1,9 +1,7 @@ use std::path::Path; use std::process::Command; -use crate::{ - bin_name, cygpath_windows, env_var, handle_failed_output, is_msvc, is_windows, tmp_dir, uname, -}; +use crate::{bin_name, cygpath_windows, env_var, handle_failed_output, is_msvc, is_windows, uname}; /// Construct a new platform-specific C compiler invocation. /// diff --git a/src/tools/run-make-support/src/clang.rs b/src/tools/run-make-support/src/clang.rs index f3c2a7446424..7d9246b52227 100644 --- a/src/tools/run-make-support/src/clang.rs +++ b/src/tools/run-make-support/src/clang.rs @@ -1,7 +1,7 @@ use std::path::Path; use std::process::Command; -use crate::{bin_name, env_var, handle_failed_output, tmp_dir}; +use crate::{bin_name, env_var, handle_failed_output}; /// Construct a new `clang` invocation. `clang` is not always available for all targets. pub fn clang() -> Clang { diff --git a/src/tools/run-make-support/src/lib.rs b/src/tools/run-make-support/src/lib.rs index c4b74ce08845..d4b02a81a4f6 100644 --- a/src/tools/run-make-support/src/lib.rs +++ b/src/tools/run-make-support/src/lib.rs @@ -171,6 +171,11 @@ pub fn bin_name(name: &str) -> String { if is_windows() { format!("{name}.exe") } else { name.to_string() } } +/// Return the current working directory. +pub fn cwd() -> PathBuf { + env::current_dir().unwrap() +} + /// Use `cygpath -w` on a path to get a Windows path string back. This assumes that `cygpath` is /// available on the platform! #[track_caller] diff --git a/tests/run-make/CURRENT_RUSTC_VERSION/rmake.rs b/tests/run-make/CURRENT_RUSTC_VERSION/rmake.rs index 1bdb63475712..b411eab10c47 100644 --- a/tests/run-make/CURRENT_RUSTC_VERSION/rmake.rs +++ b/tests/run-make/CURRENT_RUSTC_VERSION/rmake.rs @@ -10,11 +10,11 @@ use run_make_support::{aux_build, rustc}; fn main() { aux_build().input("stable.rs").emit("metadata").run(); - let mut stable_path = PathBuf::from(env!("TMPDIR")); - stable_path.push("libstable.rmeta"); - - let output = - rustc().input("main.rs").emit("metadata").extern_("stable", &stable_path).command_output(); + let output = rustc() + .input("main.rs") + .emit("metadata") + .extern_("stable", "libstable.rmeta") + .command_output(); let stderr = String::from_utf8_lossy(&output.stderr); let version = include_str!(concat!(env!("S"), "/src/version")); diff --git a/tests/run-make/artifact-incr-cache-no-obj/rmake.rs b/tests/run-make/artifact-incr-cache-no-obj/rmake.rs index 6613698ae1df..d5bc46dff470 100644 --- a/tests/run-make/artifact-incr-cache-no-obj/rmake.rs +++ b/tests/run-make/artifact-incr-cache-no-obj/rmake.rs @@ -5,17 +5,15 @@ // // Fixes: rust-lang/rust#123234 -use run_make_support::{rustc, tmp_dir}; +use run_make_support::rustc; fn main() { - let inc_dir = tmp_dir(); - for _ in 0..=1 { rustc() .input("lib.rs") .crate_type("lib") .emit("asm,dep-info,link,mir,llvm-ir,llvm-bc") - .incremental(&inc_dir) + .incremental("incremental") .run(); } } diff --git a/tests/run-make/artifact-incr-cache/rmake.rs b/tests/run-make/artifact-incr-cache/rmake.rs index 106f363eb8da..b4b63313cfc4 100644 --- a/tests/run-make/artifact-incr-cache/rmake.rs +++ b/tests/run-make/artifact-incr-cache/rmake.rs @@ -7,17 +7,15 @@ // Also see discussion at // -use run_make_support::{rustc, tmp_dir}; +use run_make_support::rustc; fn main() { - let inc_dir = tmp_dir(); - for _ in 0..=1 { rustc() .input("lib.rs") .crate_type("lib") .emit("obj,asm,dep-info,link,mir,llvm-ir,llvm-bc") - .incremental(&inc_dir) + .incremental("incremental") .run(); } } diff --git a/tests/run-make/box-struct-no-segfault/rmake.rs b/tests/run-make/box-struct-no-segfault/rmake.rs index 5406f765e6c5..1bbefd03541a 100644 --- a/tests/run-make/box-struct-no-segfault/rmake.rs +++ b/tests/run-make/box-struct-no-segfault/rmake.rs @@ -5,9 +5,9 @@ // This test checks that this bug does not resurface. // See https://github.com/rust-lang/rust/issues/28766 -use run_make_support::{rustc, tmp_dir}; +use run_make_support::rustc; fn main() { rustc().opt().input("foo.rs").run(); - rustc().opt().library_search_path(tmp_dir()).input("main.rs").run(); + rustc().opt().input("main.rs").run(); } diff --git a/tests/run-make/c-link-to-rust-dylib/rmake.rs b/tests/run-make/c-link-to-rust-dylib/rmake.rs index 5c4b6d786496..37a326099d21 100644 --- a/tests/run-make/c-link-to-rust-dylib/rmake.rs +++ b/tests/run-make/c-link-to-rust-dylib/rmake.rs @@ -5,29 +5,23 @@ use std::fs::remove_file; -use run_make_support::{ - cc, dynamic_lib_extension, is_msvc, read_dir, run, run_fail, rustc, tmp_dir, -}; +use run_make_support::{cc, cwd, dynamic_lib_extension, is_msvc, read_dir, run, run_fail, rustc}; fn main() { rustc().input("foo.rs").run(); if is_msvc() { - let lib = tmp_dir().join("foo.dll.lib"); + let lib = "foo.dll.lib"; cc().input("bar.c").arg(lib).out_exe("bar").run(); } else { - cc().input("bar.c") - .arg("-lfoo") - .output(tmp_dir().join("bar")) - .library_search_path(tmp_dir()) - .run(); + cc().input("bar.c").arg("-lfoo").output("bar").library_search_path(cwd()).run(); } run("bar"); let expected_extension = dynamic_lib_extension(); - read_dir(tmp_dir(), |path| { + read_dir(std::env::current_dir().unwrap(), |path| { if path.is_file() && path.extension().is_some_and(|ext| ext == expected_extension) && path.file_name().and_then(|name| name.to_str()).is_some_and(|name| { diff --git a/tests/run-make/cdylib/rmake.rs b/tests/run-make/cdylib/rmake.rs index fcb4f56621f6..1920f618473b 100644 --- a/tests/run-make/cdylib/rmake.rs +++ b/tests/run-make/cdylib/rmake.rs @@ -12,20 +12,16 @@ use std::fs::remove_file; -use run_make_support::{cc, dynamic_lib, is_msvc, run, rustc, tmp_dir}; +use run_make_support::{cc, cwd, dynamic_lib, is_msvc, run, rustc}; fn main() { rustc().input("bar.rs").run(); rustc().input("foo.rs").run(); if is_msvc() { - cc().input("foo.c").arg(tmp_dir().join("foo.dll.lib")).out_exe("foo").run(); + cc().input("foo.c").arg("foo.dll.lib").out_exe("foo").run(); } else { - cc().input("foo.c") - .arg("-lfoo") - .output(tmp_dir().join("foo")) - .library_search_path(tmp_dir()) - .run(); + cc().input("foo.c").arg("-lfoo").library_search_path(cwd()).output("foo").run(); } run("foo"); diff --git a/tests/run-make/compiler-builtins/Cargo.toml b/tests/run-make/compiler-builtins/Cargo.toml new file mode 100644 index 000000000000..869210c4a683 --- /dev/null +++ b/tests/run-make/compiler-builtins/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "scratch" +version = "0.1.0" +edition = "2021" + +[lib] +path = "lib.rs" diff --git a/tests/run-make/compiler-builtins/lib.rs b/tests/run-make/compiler-builtins/lib.rs new file mode 100644 index 000000000000..0c9ac1ac8e4b --- /dev/null +++ b/tests/run-make/compiler-builtins/lib.rs @@ -0,0 +1 @@ +#![no_std] diff --git a/tests/run-make/compiler-builtins/rmake.rs b/tests/run-make/compiler-builtins/rmake.rs index b043b149d650..309d3a04b21c 100644 --- a/tests/run-make/compiler-builtins/rmake.rs +++ b/tests/run-make/compiler-builtins/rmake.rs @@ -20,29 +20,17 @@ use run_make_support::object::ObjectSection; use run_make_support::object::ObjectSymbol; use run_make_support::object::RelocationTarget; use run_make_support::set_host_rpath; -use run_make_support::tmp_dir; use run_make_support::{env_var, object}; use std::collections::HashSet; - -const MANIFEST: &str = r#" -[package] -name = "scratch" -version = "0.1.0" -edition = "2021" - -[lib] -path = "lib.rs""#; +use std::path::PathBuf; fn main() { - let target_dir = tmp_dir().join("target"); + let target_dir = PathBuf::from("target"); let target = env_var("TARGET"); println!("Testing compiler_builtins for {}", target); - // Set up the tiniest Cargo project: An empty no_std library. Just enough to run -Zbuild-std. - let manifest_path = tmp_dir().join("Cargo.toml"); - std::fs::write(&manifest_path, MANIFEST.as_bytes()).unwrap(); - std::fs::write(tmp_dir().join("lib.rs"), b"#![no_std]").unwrap(); + let manifest_path = PathBuf::from("Cargo.toml"); let path = env_var("PATH"); let rustc = env_var("RUSTC"); diff --git a/tests/run-make/const-prop-lint/rmake.rs b/tests/run-make/const-prop-lint/rmake.rs index fa27a18a591a..c2d3e1ab1366 100644 --- a/tests/run-make/const-prop-lint/rmake.rs +++ b/tests/run-make/const-prop-lint/rmake.rs @@ -2,12 +2,12 @@ use std::fs; -use run_make_support::{rustc, tmp_dir}; +use run_make_support::rustc; fn main() { rustc().input("input.rs").run_fail_assert_exit_code(1); - for entry in fs::read_dir(tmp_dir()).unwrap() { + for entry in fs::read_dir(std::env::current_dir().unwrap()).unwrap() { let entry = entry.unwrap(); let path = entry.path(); diff --git a/tests/run-make/core-no-oom-handling/rmake.rs b/tests/run-make/core-no-oom-handling/rmake.rs index 3ebbf63ab7d6..a9e2b33e2107 100644 --- a/tests/run-make/core-no-oom-handling/rmake.rs +++ b/tests/run-make/core-no-oom-handling/rmake.rs @@ -2,7 +2,7 @@ // when the no_global_oom_handling feature is turned on. // See https://github.com/rust-lang/rust/pull/110649 -use run_make_support::{rustc, source_root, tmp_dir}; +use run_make_support::{rustc, source_root}; fn main() { rustc() @@ -10,7 +10,7 @@ fn main() { .arg("-Dwarnings") .crate_type("rlib") .input(source_root().join("library/core/src/lib.rs")) - .sysroot(tmp_dir().join("fakeroot")) + .sysroot("fakeroot") .cfg("no_global_oom_handling") .run(); } diff --git a/tests/run-make/cross-lang-lto-riscv-abi/rmake.rs b/tests/run-make/cross-lang-lto-riscv-abi/rmake.rs index 61f32762d8b7..04afc92bf7e9 100644 --- a/tests/run-make/cross-lang-lto-riscv-abi/rmake.rs +++ b/tests/run-make/cross-lang-lto-riscv-abi/rmake.rs @@ -3,7 +3,7 @@ //@ needs-matching-clang //@ needs-llvm-components riscv -use run_make_support::{bin_name, clang, llvm_readobj, rustc, tmp_dir}; +use run_make_support::{bin_name, clang, llvm_readobj, rustc}; use std::{ env, path::PathBuf, @@ -30,11 +30,11 @@ fn check_target(target: &str, clang_target: &str, carch: &str, is_double_float: .no_stdlib() .out_exe("riscv-xlto") .input("cstart.c") - .input(tmp_dir().join("libriscv_xlto.rlib")) + .input("libriscv_xlto.rlib") .run(); // Check that the built binary has correct float abi - let executable = tmp_dir().join(bin_name("riscv-xlto")); + let executable = bin_name("riscv-xlto"); let output = llvm_readobj().input(&executable).file_header().run(); let stdout = String::from_utf8_lossy(&output.stdout); eprintln!("obj:\n{}", stdout); diff --git a/tests/run-make/deref-impl-rustdoc-ice/rmake.rs b/tests/run-make/deref-impl-rustdoc-ice/rmake.rs index c2156de03a97..91fc0a9025fa 100644 --- a/tests/run-make/deref-impl-rustdoc-ice/rmake.rs +++ b/tests/run-make/deref-impl-rustdoc-ice/rmake.rs @@ -7,10 +7,10 @@ //@ ignore-cross-compile -use run_make_support::{rustc, rustdoc, tmp_dir}; +use run_make_support::{cwd, rustc, rustdoc}; fn main() { rustc().input("foo.rs").run(); rustc().input("bar.rs").run(); - rustdoc().input("baz.rs").library_search_path(tmp_dir()).output(tmp_dir()).run(); + rustdoc().input("baz.rs").library_search_path(cwd()).output(cwd()).run(); } diff --git a/tests/run-make/doctests-keep-binaries/rmake.rs b/tests/run-make/doctests-keep-binaries/rmake.rs index 0613ef4839b1..5bc9480e4a39 100644 --- a/tests/run-make/doctests-keep-binaries/rmake.rs +++ b/tests/run-make/doctests-keep-binaries/rmake.rs @@ -1,15 +1,15 @@ // Check that valid binaries are persisted by running them, regardless of whether the // --run or --no-run option is used. -use run_make_support::{run, rustc, rustdoc, tmp_dir}; +use run_make_support::{run, rustc, rustdoc}; use std::fs::{create_dir, remove_dir_all}; use std::path::Path; fn setup_test_env(callback: F) { - let out_dir = tmp_dir().join("doctests"); + let out_dir = Path::new("doctests"); create_dir(&out_dir).expect("failed to create doctests folder"); rustc().input("t.rs").crate_type("rlib").run(); - callback(&out_dir, &tmp_dir().join("libt.rlib")); + callback(&out_dir, Path::new("libt.rlib")); remove_dir_all(out_dir); } @@ -44,19 +44,17 @@ fn main() { }); // Behavior with --test-run-directory with relative paths. setup_test_env(|_out_dir, extern_path| { - let run_dir = "rundir"; - let run_dir_path = tmp_dir().join("rundir"); + let run_dir_path = Path::new("rundir"); create_dir(&run_dir_path).expect("failed to create rundir folder"); rustdoc() - .current_dir(tmp_dir()) - .input(std::env::current_dir().unwrap().join("t.rs")) + .input("t.rs") .arg("-Zunstable-options") .arg("--test") .arg("--persist-doctests") .arg("doctests") .arg("--test-run-directory") - .arg(run_dir) + .arg(run_dir_path) .extern_("t", "libt.rlib") .run(); diff --git a/tests/run-make/doctests-runtool/rmake.rs b/tests/run-make/doctests-runtool/rmake.rs index 6cc7c6bbdafd..6a7a931249e8 100644 --- a/tests/run-make/doctests-runtool/rmake.rs +++ b/tests/run-make/doctests-runtool/rmake.rs @@ -1,12 +1,11 @@ // Tests behavior of rustdoc `--runtool`. -use run_make_support::{rustc, rustdoc, tmp_dir}; -use std::env::current_dir; +use run_make_support::{rustc, rustdoc}; use std::fs::{create_dir, remove_dir_all}; use std::path::PathBuf; fn mkdir(name: &str) -> PathBuf { - let dir = tmp_dir().join(name); + let dir = PathBuf::from(name); create_dir(&dir).expect("failed to create doctests folder"); dir } @@ -22,7 +21,7 @@ fn main() { rustc().input("runtool.rs").output(&run_tool_binary).run(); rustdoc() - .input(current_dir().unwrap().join("t.rs")) + .input("t.rs") .arg("-Zunstable-options") .arg("--test") .arg("--test-run-directory") @@ -30,7 +29,6 @@ fn main() { .arg("--runtool") .arg(&run_tool_binary) .extern_("t", "libt.rlib") - .current_dir(tmp_dir()) .run(); remove_dir_all(run_dir); diff --git a/tests/run-make/emit-named-files/rmake.rs b/tests/run-make/emit-named-files/rmake.rs index 068f9796d0e9..c4b7b9aebf6c 100644 --- a/tests/run-make/emit-named-files/rmake.rs +++ b/tests/run-make/emit-named-files/rmake.rs @@ -1,7 +1,7 @@ use std::fs::create_dir; use std::path::Path; -use run_make_support::{rustc, tmp_dir}; +use run_make_support::rustc; fn emit_and_check(out_dir: &Path, out_file: &str, format: &str) { let out_file = out_dir.join(out_file); @@ -10,7 +10,7 @@ fn emit_and_check(out_dir: &Path, out_file: &str, format: &str) { } fn main() { - let out_dir = tmp_dir().join("emit"); + let out_dir = Path::new("emit"); create_dir(&out_dir).unwrap(); diff --git a/tests/run-make/exit-code/rmake.rs b/tests/run-make/exit-code/rmake.rs index 76d7777581b5..6bf7a232642d 100644 --- a/tests/run-make/exit-code/rmake.rs +++ b/tests/run-make/exit-code/rmake.rs @@ -1,6 +1,6 @@ // Test that we exit with the correct exit code for successful / unsuccessful / ICE compilations -use run_make_support::{rustc, rustdoc, tmp_dir}; +use run_make_support::{rustc, rustdoc}; fn main() { rustc().arg("success.rs").run(); @@ -15,7 +15,7 @@ fn main() { .arg("compile-error.rs") .run_fail_assert_exit_code(101); - rustdoc().arg("success.rs").output(tmp_dir().join("exit-code")).run(); + rustdoc().arg("success.rs").output("exit-code").run(); rustdoc().arg("--invalid-arg-foo").run_fail_assert_exit_code(1); diff --git a/tests/run-make/external-crate-panic-handle-no-lint/rmake.rs b/tests/run-make/external-crate-panic-handle-no-lint/rmake.rs index de4023282ef6..e54ef53e3dcb 100644 --- a/tests/run-make/external-crate-panic-handle-no-lint/rmake.rs +++ b/tests/run-make/external-crate-panic-handle-no-lint/rmake.rs @@ -4,9 +4,9 @@ // and cause the test to fail. // See https://github.com/rust-lang/rust/issues/53964 -use run_make_support::{rustc, tmp_dir}; +use run_make_support::rustc; fn main() { rustc().input("panic.rs").run(); - rustc().input("app.rs").panic("abort").emit("obj").library_search_path(tmp_dir()).run(); + rustc().input("app.rs").panic("abort").emit("obj").run(); } diff --git a/tests/run-make/incr-prev-body-beyond-eof/rmake.rs b/tests/run-make/incr-prev-body-beyond-eof/rmake.rs index 8aa3893727f6..ccb1f95275e2 100644 --- a/tests/run-make/incr-prev-body-beyond-eof/rmake.rs +++ b/tests/run-make/incr-prev-body-beyond-eof/rmake.rs @@ -13,15 +13,15 @@ //@ ignore-nvptx64-nvidia-cuda // FIXME: can't find crate for `std` -use run_make_support::{rustc, tmp_dir}; +use run_make_support::rustc; use std::fs; fn main() { // FIXME(Oneirical): Use run_make_support::fs_wrapper here. - fs::create_dir(tmp_dir().join("src")).unwrap(); - fs::create_dir(tmp_dir().join("incr")).unwrap(); - fs::copy("a.rs", tmp_dir().join("src/main.rs")).unwrap(); - rustc().incremental(tmp_dir().join("incr")).input(tmp_dir().join("src/main.rs")).run(); - fs::copy("b.rs", tmp_dir().join("src/main.rs")).unwrap(); - rustc().incremental(tmp_dir().join("incr")).input(tmp_dir().join("src/main.rs")).run(); + fs::create_dir("src").unwrap(); + fs::create_dir("incr").unwrap(); + fs::copy("a.rs", "src/main.rs").unwrap(); + rustc().incremental("incr").input("src/main.rs").run(); + fs::copy("b.rs", "src/main.rs").unwrap(); + rustc().incremental("incr").input("src/main.rs").run(); } diff --git a/tests/run-make/issue-107495-archive-permissions/rmake.rs b/tests/run-make/issue-107495-archive-permissions/rmake.rs index db25e9b033c1..72ceb10c5911 100644 --- a/tests/run-make/issue-107495-archive-permissions/rmake.rs +++ b/tests/run-make/issue-107495-archive-permissions/rmake.rs @@ -3,7 +3,7 @@ #[cfg(unix)] extern crate libc; -use run_make_support::{aux_build, tmp_dir}; +use run_make_support::aux_build; use std::fs; #[cfg(unix)] use std::os::unix::fs::PermissionsExt; @@ -16,7 +16,7 @@ fn main() { } aux_build().arg("foo.rs").run(); - verify(&tmp_dir().join("libfoo.rlib")); + verify(Path::new("libfoo.rlib")); } fn verify(path: &Path) { diff --git a/tests/run-make/issue-125484-used-dependencies/rmake.rs b/tests/run-make/issue-125484-used-dependencies/rmake.rs index b75e82b42db7..bc0a18de66e7 100644 --- a/tests/run-make/issue-125484-used-dependencies/rmake.rs +++ b/tests/run-make/issue-125484-used-dependencies/rmake.rs @@ -6,7 +6,7 @@ // make compiletest annotations reproduce the ICE with the minimizations from issues #125474 and // #125484. -use run_make_support::{rustc, tmp_dir}; +use run_make_support::rustc; fn main() { // The dependency is not itself significant, apart from sharing a name with one of main's @@ -14,5 +14,5 @@ fn main() { rustc().crate_name("same").crate_type("rlib").input("dependency.rs").run(); // Here, an ICE would happen when building the linker command. - rustc().input("main.rs").extern_("same", tmp_dir().join("libsame.rlib")).run(); + rustc().input("main.rs").extern_("same", "libsame.rlib").run(); } diff --git a/tests/run-make/manual-crate-name/rmake.rs b/tests/run-make/manual-crate-name/rmake.rs index 531f531abd26..f171f78087c5 100644 --- a/tests/run-make/manual-crate-name/rmake.rs +++ b/tests/run-make/manual-crate-name/rmake.rs @@ -1,6 +1,7 @@ -use run_make_support::{rustc, tmp_dir}; +use run_make_support::rustc; +use std::path::Path; fn main() { rustc().input("bar.rs").crate_name("foo").run(); - assert!(tmp_dir().join("libfoo.rlib").is_file()); + assert!(Path::new("libfoo.rlib").is_file()); } diff --git a/tests/run-make/mixing-formats/rmake.rs b/tests/run-make/mixing-formats/rmake.rs index 9d072d9a6e4f..444751419d74 100644 --- a/tests/run-make/mixing-formats/rmake.rs +++ b/tests/run-make/mixing-formats/rmake.rs @@ -12,7 +12,7 @@ //@ ignore-cross-compile -use run_make_support::{rustc, tmp_dir, run_in_tmpdir}; +use run_make_support::{run_in_tmpdir, rustc}; use std::fs; fn main() { diff --git a/tests/run-make/no-intermediate-extras/rmake.rs b/tests/run-make/no-intermediate-extras/rmake.rs index 19479e3bd5b5..0641417504ef 100644 --- a/tests/run-make/no-intermediate-extras/rmake.rs +++ b/tests/run-make/no-intermediate-extras/rmake.rs @@ -5,13 +5,13 @@ //@ ignore-cross-compile -use run_make_support::{rustc, tmp_dir}; +use run_make_support::rustc; use std::fs; fn main() { rustc().crate_type("rlib").arg("--test").input("foo.rs").run(); assert!( - fs::remove_file(tmp_dir().join("foo.bc")).is_err(), + fs::remove_file("foo.bc").is_err(), "An unwanted .bc file was created by run-make/no-intermediate-extras." ); } diff --git a/tests/run-make/non-pie-thread-local/rmake.rs b/tests/run-make/non-pie-thread-local/rmake.rs index 1ef447e78601..1758f1a0855b 100644 --- a/tests/run-make/non-pie-thread-local/rmake.rs +++ b/tests/run-make/non-pie-thread-local/rmake.rs @@ -7,13 +7,13 @@ //@ ignore-cross-compile //@ only-linux -use run_make_support::{cc, run, rustc, tmp_dir}; +use run_make_support::{cc, cwd, run, rustc}; fn main() { rustc().input("foo.rs").run(); cc().input("foo.c") .arg("-lfoo") - .library_search_path(tmp_dir()) + .library_search_path(cwd()) .arg("-Wl,--gc-sections") .arg("-lpthread") .arg("-ldl") @@ -22,7 +22,7 @@ fn main() { run("foo"); cc().input("foo.c") .arg("-lfoo") - .library_search_path(tmp_dir()) + .library_search_path(cwd()) .arg("-Wl,--gc-sections") .arg("-lpthread") .arg("-ldl") diff --git a/tests/run-make/non-unicode-in-incremental-dir/rmake.rs b/tests/run-make/non-unicode-in-incremental-dir/rmake.rs index 40152e0411d3..ba1bd4487439 100644 --- a/tests/run-make/non-unicode-in-incremental-dir/rmake.rs +++ b/tests/run-make/non-unicode-in-incremental-dir/rmake.rs @@ -1,21 +1,21 @@ -use run_make_support::{rustc, tmp_dir}; +use run_make_support::rustc; fn main() { #[cfg(unix)] let non_unicode: &std::ffi::OsStr = std::os::unix::ffi::OsStrExt::from_bytes(&[0xFF]); #[cfg(windows)] let non_unicode: std::ffi::OsString = std::os::windows::ffi::OsStringExt::from_wide(&[0xD800]); - match std::fs::create_dir(tmp_dir().join(&non_unicode)) { + match std::fs::create_dir(&non_unicode) { // If an error occurs, check if creating a directory with a valid Unicode name would // succeed. - Err(e) if std::fs::create_dir(tmp_dir().join("valid_unicode")).is_ok() => { + Err(e) if std::fs::create_dir("valid_unicode").is_ok() => { // Filesystem doesn't appear support non-Unicode paths. return; } Err(e) => panic!("error creating non-Unicode directory: {e}"), _ => {} } - let incr_dir = tmp_dir().join("incr-dir"); + let incr_dir = "incr-dir"; rustc().input("foo.rs").incremental(&incr_dir).run(); for crate_dir in std::fs::read_dir(&incr_dir).unwrap() { std::fs::create_dir(crate_dir.unwrap().path().join(&non_unicode)).unwrap(); diff --git a/tests/run-make/notify-all-emit-artifacts/rmake.rs b/tests/run-make/notify-all-emit-artifacts/rmake.rs index c866d9179f94..6d966793c35e 100644 --- a/tests/run-make/notify-all-emit-artifacts/rmake.rs +++ b/tests/run-make/notify-all-emit-artifacts/rmake.rs @@ -6,11 +6,9 @@ // See extern crate run_make_support; -use run_make_support::{rustc, tmp_dir}; +use run_make_support::rustc; fn main() { - let inc_dir = tmp_dir(); - // With single codegen unit files are renamed to match the source file name for _ in 0..=1 { let output = rustc() @@ -19,7 +17,7 @@ fn main() { .codegen_units(1) .json("artifacts") .error_format("json") - .incremental(&inc_dir) + .incremental(&std::env::current_dir().unwrap()) .run(); let stderr = String::from_utf8_lossy(&output.stderr); for file in &["lib.o", "lib.ll", "lib.bc", "lib.s"] { @@ -35,7 +33,7 @@ fn main() { .codegen_units(2) .json("artifacts") .error_format("json") - .incremental(&inc_dir) + .incremental(&std::env::current_dir().unwrap()) .run(); let stderr = String::from_utf8_lossy(&output.stderr); for file in &["rcgu.o", "rcgu.ll", "rcgu.bc", "rcgu.s"] { diff --git a/tests/run-make/panic-impl-transitive/rmake.rs b/tests/run-make/panic-impl-transitive/rmake.rs index 86308f593b31..c09b233b23e1 100644 --- a/tests/run-make/panic-impl-transitive/rmake.rs +++ b/tests/run-make/panic-impl-transitive/rmake.rs @@ -6,14 +6,9 @@ // function in the crate. // See https://github.com/rust-lang/rust/pull/50338 -use run_make_support::{rustc, tmp_dir}; +use run_make_support::rustc; fn main() { rustc().input("panic-impl-provider.rs").run(); - rustc() - .input("panic-impl-consumer.rs") - .panic("abort") - .emit("llvm-ir") - .library_search_path(tmp_dir()) - .run(); + rustc().input("panic-impl-consumer.rs").panic("abort").emit("llvm-ir").run(); } diff --git a/tests/run-make/print-cfg/rmake.rs b/tests/run-make/print-cfg/rmake.rs index 6e72c16f1f9a..29cf0ba1b3fd 100644 --- a/tests/run-make/print-cfg/rmake.rs +++ b/tests/run-make/print-cfg/rmake.rs @@ -7,10 +7,10 @@ use std::collections::HashSet; use std::ffi::OsString; -use std::io::BufRead; use std::iter::FromIterator; +use std::path::PathBuf; -use run_make_support::{rustc, tmp_dir}; +use run_make_support::rustc; struct PrintCfg { target: &'static str, @@ -91,7 +91,7 @@ fn check(PrintCfg { target, includes, disallow }: PrintCfg) { // --print=cfg=PATH { - let tmp_path = tmp_dir().join(format!("{target}.cfg")); + let tmp_path = PathBuf::from(format!("{target}.cfg")); let mut print_arg = OsString::from("--print=cfg="); print_arg.push(tmp_path.as_os_str()); diff --git a/tests/run-make/print-to-output/rmake.rs b/tests/run-make/print-to-output/rmake.rs index 1763cd378d2d..b3f77e0633c4 100644 --- a/tests/run-make/print-to-output/rmake.rs +++ b/tests/run-make/print-to-output/rmake.rs @@ -2,8 +2,9 @@ //! output to a file (instead of stdout) use std::ffi::OsString; +use std::path::PathBuf; -use run_make_support::{rustc, target, tmp_dir}; +use run_make_support::{rustc, target}; struct Option<'a> { target: &'a str, @@ -46,7 +47,7 @@ fn check(args: Option) { // --print={option}=PATH let output = { - let tmp_path = tmp_dir().join(format!("{}.txt", args.option)); + let tmp_path = PathBuf::from(format!("{}.txt", args.option)); let mut print_arg = OsString::from(format!("--print={}=", args.option)); print_arg.push(tmp_path.as_os_str()); diff --git a/tests/run-make/repr128-dwarf/rmake.rs b/tests/run-make/repr128-dwarf/rmake.rs index fd5dd8104441..27e32099396b 100644 --- a/tests/run-make/repr128-dwarf/rmake.rs +++ b/tests/run-make/repr128-dwarf/rmake.rs @@ -1,15 +1,15 @@ //@ ignore-windows // This test should be replaced with one in tests/debuginfo once GDB or LLDB support 128-bit enums. -use gimli::{AttributeValue, Dwarf, EndianRcSlice, Reader, RunTimeEndian}; +use gimli::{AttributeValue, EndianRcSlice, Reader, RunTimeEndian}; use object::{Object, ObjectSection}; -use run_make_support::{gimli, object, rustc, tmp_dir}; -use std::borrow::Cow; +use run_make_support::{gimli, object, rustc}; use std::collections::HashMap; +use std::path::PathBuf; use std::rc::Rc; fn main() { - let output = tmp_dir().join("repr128"); + let output = PathBuf::from("repr128"); rustc().input("main.rs").output(&output).arg("-Cdebuginfo=2").run(); // Mach-O uses packed debug info let dsym_location = output diff --git a/tests/run-make/reset-codegen-1/rmake.rs b/tests/run-make/reset-codegen-1/rmake.rs index 4b91ba7df90b..c1385b2e015b 100644 --- a/tests/run-make/reset-codegen-1/rmake.rs +++ b/tests/run-make/reset-codegen-1/rmake.rs @@ -7,12 +7,12 @@ //@ ignore-cross-compile -use run_make_support::{rustc, tmp_dir}; +use run_make_support::rustc; use std::fs; fn compile(output_file: &str, emit: Option<&str>) { let mut rustc = rustc(); - let rustc = rustc.codegen_units(4).output(tmp_dir().join(output_file)).input("foo.rs"); + let rustc = rustc.codegen_units(4).output(output_file).input("foo.rs"); if let Some(emit) = emit { rustc.emit(emit); } diff --git a/tests/run-make/resolve-rename/rmake.rs b/tests/run-make/resolve-rename/rmake.rs index 8c6e3c24714e..a5f48c2bbccb 100644 --- a/tests/run-make/resolve-rename/rmake.rs +++ b/tests/run-make/resolve-rename/rmake.rs @@ -5,12 +5,11 @@ // the renamed library. // See https://github.com/rust-lang/rust/pull/49253 -use run_make_support::{rustc, tmp_dir}; +use run_make_support::rustc; use std::fs; fn main() { rustc().extra_filename("-hash").input("foo.rs").run(); rustc().input("bar.rs").run(); - fs::rename(tmp_dir().join("libfoo-hash.rlib"), tmp_dir().join("libfoo-another-hash.rlib")) - .unwrap(); + fs::rename("libfoo-hash.rlib", "libfoo-another-hash.rlib").unwrap(); rustc().input("baz.rs").run(); } diff --git a/tests/run-make/rustdoc-determinism/rmake.rs b/tests/run-make/rustdoc-determinism/rmake.rs index 09097d4507da..2d4f357d6819 100644 --- a/tests/run-make/rustdoc-determinism/rmake.rs +++ b/tests/run-make/rustdoc-determinism/rmake.rs @@ -1,14 +1,15 @@ // Assert that the search index is generated deterministically, regardless of the // order that crates are documented in. -use run_make_support::{diff, rustdoc, tmp_dir}; +use run_make_support::{diff, rustdoc}; +use std::path::Path; fn main() { - let foo_first = tmp_dir().join("foo_first"); + let foo_first = Path::new("foo_first"); rustdoc().input("foo.rs").output(&foo_first).run(); rustdoc().input("bar.rs").output(&foo_first).run(); - let bar_first = tmp_dir().join("bar_first"); + let bar_first = Path::new("bar_first"); rustdoc().input("bar.rs").output(&bar_first).run(); rustdoc().input("foo.rs").output(&bar_first).run(); diff --git a/tests/run-make/rustdoc-map-file/rmake.rs b/tests/run-make/rustdoc-map-file/rmake.rs index d017b41bcdd2..de75561c9fb3 100644 --- a/tests/run-make/rustdoc-map-file/rmake.rs +++ b/tests/run-make/rustdoc-map-file/rmake.rs @@ -1,7 +1,7 @@ -use run_make_support::{python_command, rustdoc, tmp_dir}; +use run_make_support::{python_command, rustdoc}; fn main() { - let out_dir = tmp_dir().join("out"); + let out_dir = "out"; rustdoc() .input("foo.rs") .arg("-Zunstable-options") diff --git a/tests/run-make/rustdoc-output-path/rmake.rs b/tests/run-make/rustdoc-output-path/rmake.rs index 74fb0a56ac63..28e0fd98f93c 100644 --- a/tests/run-make/rustdoc-output-path/rmake.rs +++ b/tests/run-make/rustdoc-output-path/rmake.rs @@ -1,9 +1,10 @@ // Checks that if the output folder doesn't exist, rustdoc will create it. -use run_make_support::{rustdoc, tmp_dir}; +use run_make_support::rustdoc; +use std::path::Path; fn main() { - let out_dir = tmp_dir().join("foo/bar/doc"); + let out_dir = Path::new("foo/bar/doc"); rustdoc().input("foo.rs").output(&out_dir).run(); assert!(out_dir.exists()); } diff --git a/tests/run-make/rustdoc-scrape-examples-macros/rmake.rs b/tests/run-make/rustdoc-scrape-examples-macros/rmake.rs index 81b7defafc6c..2daaab76e544 100644 --- a/tests/run-make/rustdoc-scrape-examples-macros/rmake.rs +++ b/tests/run-make/rustdoc-scrape-examples-macros/rmake.rs @@ -1,11 +1,10 @@ //@ ignore-cross-compile -use run_make_support::{htmldocck, rustc, rustdoc, tmp_dir}; +use run_make_support::{htmldocck, rustc, rustdoc}; fn main() { - let tmp_dir = tmp_dir(); - let out_dir = tmp_dir.join("rustdoc"); - let ex_dir = tmp_dir.join("ex.calls"); + let out_dir = "rustdoc"; + let ex_dir = "ex.calls"; let proc_crate_name = "foobar_macro"; let crate_name = "foobar"; @@ -41,8 +40,8 @@ fn main() { .crate_name("ex") .crate_type("bin") .output(&out_dir) - .extern_(crate_name, tmp_dir.join(format!("lib{crate_name}.rlib"))) - .extern_(proc_crate_name, tmp_dir.join(dylib_name.trim())) + .extern_(crate_name, format!("lib{crate_name}.rlib")) + .extern_(proc_crate_name, dylib_name.trim()) .arg("-Zunstable-options") .arg("--scrape-examples-output-path") .arg(&ex_dir) diff --git a/tests/run-make/rustdoc-scrape-examples-remap/scrape.rs b/tests/run-make/rustdoc-scrape-examples-remap/scrape.rs index b372c25447d0..41efb837458f 100644 --- a/tests/run-make/rustdoc-scrape-examples-remap/scrape.rs +++ b/tests/run-make/rustdoc-scrape-examples-remap/scrape.rs @@ -1,10 +1,9 @@ -use run_make_support::{htmldocck, rustc, rustdoc, source_root, tmp_dir}; +use run_make_support::{htmldocck, rustc, rustdoc, source_root}; use std::fs::read_dir; use std::path::Path; pub fn scrape(extra_args: &[&str]) { - let lib_dir = tmp_dir(); - let out_dir = tmp_dir().join("rustdoc"); + let out_dir = Path::new("rustdoc"); let crate_name = "foobar"; let deps = read_dir("examples") .unwrap() @@ -23,7 +22,7 @@ pub fn scrape(extra_args: &[&str]) { .crate_name(&dep_stem) .crate_type("bin") .output(&out_dir) - .extern_(crate_name, lib_dir.join(format!("lib{crate_name}.rmeta"))) + .extern_(crate_name, format!("lib{crate_name}.rmeta")) .arg("-Zunstable-options") .arg("--scrape-examples-output-path") .arg(&out_example) diff --git a/tests/run-make/rustdoc-target-spec-json-path/rmake.rs b/tests/run-make/rustdoc-target-spec-json-path/rmake.rs index 66530a4f08ee..3246fc565064 100644 --- a/tests/run-make/rustdoc-target-spec-json-path/rmake.rs +++ b/tests/run-make/rustdoc-target-spec-json-path/rmake.rs @@ -1,14 +1,14 @@ // Test that rustdoc will properly canonicalize the target spec json path just like rustc. -use run_make_support::{rustc, rustdoc, tmp_dir}; +use run_make_support::{cwd, rustc, rustdoc}; fn main() { - let out_dir = tmp_dir().join("rustdoc-target-spec-json-path"); + let out_dir = "rustdoc-target-spec-json-path"; rustc().crate_type("lib").input("dummy_core.rs").target("target.json").run(); rustdoc() .input("my_crate.rs") .output(out_dir) - .library_search_path(tmp_dir()) + .library_search_path(cwd()) .target("target.json") .run(); } diff --git a/tests/run-make/rustdoc-test-args/rmake.rs b/tests/run-make/rustdoc-test-args/rmake.rs index 66f3f7cf131c..340ab022880e 100644 --- a/tests/run-make/rustdoc-test-args/rmake.rs +++ b/tests/run-make/rustdoc-test-args/rmake.rs @@ -1,4 +1,4 @@ -use run_make_support::{rustdoc, tmp_dir}; +use run_make_support::rustdoc; use std::path::Path; use std::{fs, iter}; @@ -8,8 +8,8 @@ fn generate_a_lot_of_cfgs(path: &Path) { } fn main() { - let arg_file = tmp_dir().join("args"); + let arg_file = Path::new("args"); generate_a_lot_of_cfgs(&arg_file); - rustdoc().out_dir(tmp_dir()).input("foo.rs").arg_file(&arg_file).arg("--test").run(); + rustdoc().input("foo.rs").arg_file(&arg_file).arg("--test").run(); } diff --git a/tests/run-make/rustdoc-themes/rmake.rs b/tests/run-make/rustdoc-themes/rmake.rs index d6ddd45b4a45..c28821b7628d 100644 --- a/tests/run-make/rustdoc-themes/rmake.rs +++ b/tests/run-make/rustdoc-themes/rmake.rs @@ -1,10 +1,11 @@ // Test that rustdoc will properly load in a theme file and display it in the theme selector. -use run_make_support::{htmldocck, rustdoc, source_root, tmp_dir}; +use run_make_support::{htmldocck, rustdoc, source_root}; +use std::path::Path; fn main() { - let out_dir = tmp_dir().join("rustdoc-themes"); - let test_css = out_dir.join("test.css"); + let out_dir = Path::new("rustdoc-themes"); + let test_css = "test.css"; let no_script = std::fs::read_to_string(source_root().join("src/librustdoc/html/static/css/noscript.css")) diff --git a/tests/run-make/rustdoc-verify-output-files/rmake.rs b/tests/run-make/rustdoc-verify-output-files/rmake.rs index 212e7eaba2d6..d2d12ae83a21 100644 --- a/tests/run-make/rustdoc-verify-output-files/rmake.rs +++ b/tests/run-make/rustdoc-verify-output-files/rmake.rs @@ -1,7 +1,7 @@ use std::fs::copy; use std::path::{Path, PathBuf}; -use run_make_support::{copy_dir_all, recursive_diff, rustdoc, tmp_dir}; +use run_make_support::{copy_dir_all, recursive_diff, rustdoc}; #[derive(PartialEq)] enum JsonOutput { @@ -19,8 +19,8 @@ fn generate_docs(out_dir: &Path, json_output: JsonOutput) { } fn main() { - let out_dir = tmp_dir().join("rustdoc"); - let tmp_out_dir = tmp_dir().join("tmp-rustdoc"); + let out_dir = PathBuf::from("rustdoc"); + let tmp_out_dir = PathBuf::from("tmp-rustdoc"); // Generate HTML docs. generate_docs(&out_dir, JsonOutput::No); diff --git a/tests/run-make/rustdoc-with-out-dir-option/rmake.rs b/tests/run-make/rustdoc-with-out-dir-option/rmake.rs index 86471093834f..405da8412ae2 100644 --- a/tests/run-make/rustdoc-with-out-dir-option/rmake.rs +++ b/tests/run-make/rustdoc-with-out-dir-option/rmake.rs @@ -1,7 +1,7 @@ -use run_make_support::{htmldocck, rustdoc, tmp_dir}; +use run_make_support::{htmldocck, rustdoc}; fn main() { - let out_dir = tmp_dir().join("rustdoc"); + let out_dir = "rustdoc"; rustdoc().input("src/lib.rs").crate_name("foobar").crate_type("lib").output(&out_dir).run(); assert!(htmldocck().arg(out_dir).arg("src/lib.rs").status().unwrap().success()); } diff --git a/tests/run-make/rustdoc-with-output-option/rmake.rs b/tests/run-make/rustdoc-with-output-option/rmake.rs index 1a009419273e..a3b1c8ca0dd4 100644 --- a/tests/run-make/rustdoc-with-output-option/rmake.rs +++ b/tests/run-make/rustdoc-with-output-option/rmake.rs @@ -1,7 +1,7 @@ -use run_make_support::{htmldocck, rustdoc, tmp_dir}; +use run_make_support::{htmldocck, rustdoc}; fn main() { - let out_dir = tmp_dir().join("rustdoc"); + let out_dir = "rustdoc"; rustdoc() .input("src/lib.rs") diff --git a/tests/run-make/rustdoc-with-short-out-dir-option/rmake.rs b/tests/run-make/rustdoc-with-short-out-dir-option/rmake.rs index 6206173ecf16..b536fbe23039 100644 --- a/tests/run-make/rustdoc-with-short-out-dir-option/rmake.rs +++ b/tests/run-make/rustdoc-with-short-out-dir-option/rmake.rs @@ -1,7 +1,7 @@ -use run_make_support::{htmldocck, rustdoc, tmp_dir}; +use run_make_support::{htmldocck, rustdoc}; fn main() { - let out_dir = tmp_dir().join("rustdoc"); + let out_dir = "rustdoc"; rustdoc() .input("src/lib.rs") diff --git a/tests/run-make/stdin-rustc/rmake.rs b/tests/run-make/stdin-rustc/rmake.rs index c07a6df4d84a..1869b1dcb8c0 100644 --- a/tests/run-make/stdin-rustc/rmake.rs +++ b/tests/run-make/stdin-rustc/rmake.rs @@ -1,6 +1,7 @@ //! This test checks rustc `-` (stdin) support -use run_make_support::{is_windows, rustc, tmp_dir}; +use run_make_support::{is_windows, rustc}; +use std::path::PathBuf; const HELLO_WORLD: &str = r#" fn main() { @@ -11,12 +12,12 @@ fn main() { const NOT_UTF8: &[u8] = &[0xff, 0xff, 0xff]; fn main() { - let out_dir = tmp_dir(); - // echo $HELLO_WORLD | rustc - rustc().arg("-").stdin(HELLO_WORLD).run(); assert!( - out_dir.join(if !is_windows() { "rust_out" } else { "rust_out.exe" }).try_exists().unwrap() + PathBuf::from(if !is_windows() { "rust_out" } else { "rust_out.exe" }) + .try_exists() + .unwrap() ); // echo $NOT_UTF8 | rustc - diff --git a/tests/run-make/stdin-rustdoc/rmake.rs b/tests/run-make/stdin-rustdoc/rmake.rs index 584a610ed63f..a72fe1bc7dae 100644 --- a/tests/run-make/stdin-rustdoc/rmake.rs +++ b/tests/run-make/stdin-rustdoc/rmake.rs @@ -1,6 +1,7 @@ //! This test checks rustdoc `-` (stdin) handling -use run_make_support::{rustdoc, tmp_dir}; +use run_make_support::rustdoc; +use std::path::PathBuf; static INPUT: &str = r#" //! ``` @@ -10,8 +11,7 @@ pub struct F; "#; fn main() { - let tmp_dir = tmp_dir(); - let out_dir = tmp_dir.join("doc"); + let out_dir = PathBuf::from("doc"); // rustdoc - rustdoc().arg("-").out_dir(&out_dir).stdin(INPUT).run(); diff --git a/tests/run-make/wasm-abi/rmake.rs b/tests/run-make/wasm-abi/rmake.rs index a2dcafbbe0f1..8e90d83af6cd 100644 --- a/tests/run-make/wasm-abi/rmake.rs +++ b/tests/run-make/wasm-abi/rmake.rs @@ -1,14 +1,14 @@ //@ only-wasm32-wasip1 //@ needs-wasmtime -use run_make_support::{rustc, tmp_dir}; +use run_make_support::rustc; use std::path::Path; use std::process::Command; fn main() { rustc().input("foo.rs").target("wasm32-wasip1").run(); - let file = tmp_dir().join("foo.wasm"); + let file = PathBuf::from("foo.wasm"); run(&file, "return_two_i32", "1\n2\n"); run(&file, "return_two_i64", "3\n4\n"); diff --git a/tests/run-make/wasm-custom-section/rmake.rs b/tests/run-make/wasm-custom-section/rmake.rs index 0303ca05ca63..65f6d0bf3473 100644 --- a/tests/run-make/wasm-custom-section/rmake.rs +++ b/tests/run-make/wasm-custom-section/rmake.rs @@ -1,13 +1,13 @@ //@ only-wasm32-wasip1 -use run_make_support::{rustc, tmp_dir, wasmparser}; +use run_make_support::{rustc, wasmparser}; use std::collections::HashMap; fn main() { rustc().input("foo.rs").target("wasm32-wasip1").run(); rustc().input("bar.rs").target("wasm32-wasip1").arg("-Clto").opt().run(); - let file = std::fs::read(&tmp_dir().join("bar.wasm")).unwrap(); + let file = std::fs::read("bar.wasm").unwrap(); let mut custom = HashMap::new(); for payload in wasmparser::Parser::new(0).parse_all(&file) { diff --git a/tests/run-make/wasm-custom-sections-opt/rmake.rs b/tests/run-make/wasm-custom-sections-opt/rmake.rs index 50916b1bf815..8e66caa6d685 100644 --- a/tests/run-make/wasm-custom-sections-opt/rmake.rs +++ b/tests/run-make/wasm-custom-sections-opt/rmake.rs @@ -1,13 +1,13 @@ //@ only-wasm32-wasip1 -use run_make_support::{rustc, tmp_dir, wasmparser}; +use run_make_support::{rustc, wasmparser}; use std::collections::HashMap; use std::path::Path; fn main() { rustc().input("foo.rs").target("wasm32-wasip1").opt().run(); - verify(&tmp_dir().join("foo.wasm")); + verify(&Path::new("foo.wasm")); } fn verify(path: &Path) { diff --git a/tests/run-make/wasm-export-all-symbols/rmake.rs b/tests/run-make/wasm-export-all-symbols/rmake.rs index f4c51bc4ab41..a6fec1fb0eb4 100644 --- a/tests/run-make/wasm-export-all-symbols/rmake.rs +++ b/tests/run-make/wasm-export-all-symbols/rmake.rs @@ -1,6 +1,6 @@ //@ only-wasm32-wasip1 -use run_make_support::{rustc, tmp_dir, wasmparser}; +use run_make_support::{rustc, wasmparser}; use std::collections::HashMap; use std::path::Path; use wasmparser::ExternalKind::*; @@ -18,12 +18,9 @@ fn test(args: &[&str]) { rustc().input("foo.rs").target("wasm32-wasip1").args(args).run(); rustc().input("main.rs").target("wasm32-wasip1").args(args).run(); + verify_exports(Path::new("foo.wasm"), &[("foo", Func), ("FOO", Global), ("memory", Memory)]); verify_exports( - &tmp_dir().join("foo.wasm"), - &[("foo", Func), ("FOO", Global), ("memory", Memory)], - ); - verify_exports( - &tmp_dir().join("main.wasm"), + Path::new("main.wasm"), &[ ("foo", Func), ("FOO", Global), diff --git a/tests/run-make/wasm-import-module/rmake.rs b/tests/run-make/wasm-import-module/rmake.rs index 6eed229e9078..0d3152ae99ca 100644 --- a/tests/run-make/wasm-import-module/rmake.rs +++ b/tests/run-make/wasm-import-module/rmake.rs @@ -1,6 +1,6 @@ //@ only-wasm32-wasip1 -use run_make_support::{rustc, tmp_dir, wasmparser}; +use run_make_support::{rustc, wasmparser}; use std::collections::HashMap; use wasmparser::TypeRef::Func; @@ -8,7 +8,7 @@ fn main() { rustc().input("foo.rs").target("wasm32-wasip1").run(); rustc().input("bar.rs").target("wasm32-wasip1").arg("-Clto").opt().run(); - let file = std::fs::read(&tmp_dir().join("bar.wasm")).unwrap(); + let file = std::fs::read("bar.wasm").unwrap(); let mut imports = HashMap::new(); for payload in wasmparser::Parser::new(0).parse_all(&file) { diff --git a/tests/run-make/wasm-panic-small/rmake.rs b/tests/run-make/wasm-panic-small/rmake.rs index 373b966401ce..304e5d048330 100644 --- a/tests/run-make/wasm-panic-small/rmake.rs +++ b/tests/run-make/wasm-panic-small/rmake.rs @@ -1,7 +1,7 @@ //@ only-wasm32-wasip1 #![deny(warnings)] -use run_make_support::{rustc, tmp_dir}; +use run_make_support::rustc; fn main() { test("a"); @@ -15,7 +15,7 @@ fn test(cfg: &str) { rustc().input("foo.rs").target("wasm32-wasip1").arg("-Clto").opt().cfg(cfg).run(); - let bytes = std::fs::read(&tmp_dir().join("foo.wasm")).unwrap(); + let bytes = std::fs::read("foo.wasm").unwrap(); println!("{}", bytes.len()); assert!(bytes.len() < 40_000); } diff --git a/tests/run-make/wasm-spurious-import/rmake.rs b/tests/run-make/wasm-spurious-import/rmake.rs index 458c7bfccb74..eb28fdb2b013 100644 --- a/tests/run-make/wasm-spurious-import/rmake.rs +++ b/tests/run-make/wasm-spurious-import/rmake.rs @@ -1,6 +1,6 @@ //@ only-wasm32-wasip1 -use run_make_support::{rustc, tmp_dir, wasmparser}; +use run_make_support::{rustc, wasmparser}; use std::collections::HashMap; fn main() { @@ -13,7 +13,7 @@ fn main() { .arg("-Copt-level=z") .run(); - let file = std::fs::read(&tmp_dir().join("main.wasm")).unwrap(); + let file = std::fs::read("main.wasm").unwrap(); let mut imports = HashMap::new(); for payload in wasmparser::Parser::new(0).parse_all(&file) { diff --git a/tests/run-make/wasm-stringify-ints-small/rmake.rs b/tests/run-make/wasm-stringify-ints-small/rmake.rs index 9fac0c0c215d..8c51e26cc339 100644 --- a/tests/run-make/wasm-stringify-ints-small/rmake.rs +++ b/tests/run-make/wasm-stringify-ints-small/rmake.rs @@ -1,12 +1,12 @@ //@ only-wasm32-wasip1 #![deny(warnings)] -use run_make_support::{rustc, tmp_dir}; +use run_make_support::rustc; fn main() { rustc().input("foo.rs").target("wasm32-wasip1").arg("-Clto").opt().run(); - let bytes = std::fs::read(&tmp_dir().join("foo.wasm")).unwrap(); + let bytes = std::fs::read("foo.wasm").unwrap(); println!("{}", bytes.len()); assert!(bytes.len() < 50_000); } diff --git a/tests/run-make/wasm-symbols-different-module/rmake.rs b/tests/run-make/wasm-symbols-different-module/rmake.rs index 521d2c31ee61..4960827f151c 100644 --- a/tests/run-make/wasm-symbols-different-module/rmake.rs +++ b/tests/run-make/wasm-symbols-different-module/rmake.rs @@ -1,6 +1,6 @@ //@ only-wasm32-wasip1 -use run_make_support::{rustc, tmp_dir, wasmparser}; +use run_make_support::{rustc, wasmparser}; use std::collections::{HashMap, HashSet}; fn main() { @@ -22,7 +22,7 @@ fn test(file: &str, args: &[&str], expected_imports: &[(&str, &[&str])]) { rustc().input(file).target("wasm32-wasip1").args(args).run(); - let file = std::fs::read(&tmp_dir().join(file).with_extension("wasm")).unwrap(); + let file = std::fs::read(Path::new(file).with_extension("wasm")).unwrap(); let mut imports = HashMap::new(); for payload in wasmparser::Parser::new(0).parse_all(&file) { diff --git a/tests/run-make/wasm-symbols-not-exported/rmake.rs b/tests/run-make/wasm-symbols-not-exported/rmake.rs index 1b020b67a385..54604f51a9e8 100644 --- a/tests/run-make/wasm-symbols-not-exported/rmake.rs +++ b/tests/run-make/wasm-symbols-not-exported/rmake.rs @@ -1,18 +1,18 @@ //@ only-wasm32-wasip1 -use run_make_support::{rustc, tmp_dir, wasmparser}; +use run_make_support::{rustc, wasmparser}; use std::path::Path; fn main() { rustc().input("foo.rs").target("wasm32-wasip1").run(); - verify_symbols(&tmp_dir().join("foo.wasm")); + verify_symbols(Path::new("foo.wasm")); rustc().input("foo.rs").target("wasm32-wasip1").opt().run(); - verify_symbols(&tmp_dir().join("foo.wasm")); + verify_symbols(Path::new("foo.wasm")); rustc().input("bar.rs").target("wasm32-wasip1").run(); - verify_symbols(&tmp_dir().join("bar.wasm")); + verify_symbols(Path::new("bar.wasm")); rustc().input("bar.rs").target("wasm32-wasip1").opt().run(); - verify_symbols(&tmp_dir().join("bar.wasm")); + verify_symbols(Path::new("bar.wasm")); } fn verify_symbols(path: &Path) { diff --git a/tests/run-make/wasm-symbols-not-imported/rmake.rs b/tests/run-make/wasm-symbols-not-imported/rmake.rs index a653ab61b2c0..30408f078bc8 100644 --- a/tests/run-make/wasm-symbols-not-imported/rmake.rs +++ b/tests/run-make/wasm-symbols-not-imported/rmake.rs @@ -1,17 +1,17 @@ //@ only-wasm32-wasip1 -use run_make_support::{rustc, tmp_dir, wasmparser}; +use run_make_support::{rustc, wasmparser}; use std::path::Path; fn main() { rustc().input("foo.rs").target("wasm32-wasip1").run(); - verify_symbols(&tmp_dir().join("foo.wasm")); + verify_symbols(Path::new("foo.wasm")); rustc().input("foo.rs").target("wasm32-wasip1").arg("-Clto").run(); - verify_symbols(&tmp_dir().join("foo.wasm")); + verify_symbols(Path::new("foo.wasm")); rustc().input("foo.rs").target("wasm32-wasip1").opt().run(); - verify_symbols(&tmp_dir().join("foo.wasm")); + verify_symbols(Path::new("foo.wasm")); rustc().input("foo.rs").target("wasm32-wasip1").arg("-Clto").opt().run(); - verify_symbols(&tmp_dir().join("foo.wasm")); + verify_symbols(Path::new("foo.wasm")); } fn verify_symbols(path: &Path) { diff --git a/tests/run-make/windows-binary-no-external-deps/rmake.rs b/tests/run-make/windows-binary-no-external-deps/rmake.rs index ccf2d64c8539..73c1dfcdfa81 100644 --- a/tests/run-make/windows-binary-no-external-deps/rmake.rs +++ b/tests/run-make/windows-binary-no-external-deps/rmake.rs @@ -2,7 +2,7 @@ //! a "hello world" application by setting `PATH` to `C:\Windows\System32`. //@ only-windows -use run_make_support::{env_var, rustc, tmp_dir}; +use run_make_support::{env_var, rustc}; use std::path::PathBuf; use std::process::Command; @@ -12,7 +12,7 @@ fn main() { let windows_dir = env_var("SystemRoot"); let system32: PathBuf = [&windows_dir, "System32"].iter().collect(); // Note: This does not use the support wrappers so that we can precisely control the PATH - let exe = tmp_dir().join("hello.exe"); + let exe = "hello.exe"; let status = Command::new(exe).env("PATH", &system32).spawn().unwrap().wait().unwrap(); if !status.success() { panic!("Command failed!\noutput status: `{status}`"); diff --git a/tests/run-make/windows-spawn/rmake.rs b/tests/run-make/windows-spawn/rmake.rs index fb9cf1e21490..a6a7acd7ccb6 100644 --- a/tests/run-make/windows-spawn/rmake.rs +++ b/tests/run-make/windows-spawn/rmake.rs @@ -1,6 +1,6 @@ //@ only-windows -use run_make_support::{run, rustc, tmp_dir}; +use run_make_support::{run, rustc}; // On Windows `Command` uses `CreateProcessW` to run a new process. // However, in the past std used to not pass in the application name, leaving @@ -10,8 +10,7 @@ use run_make_support::{run, rustc, tmp_dir}; // `foo bar.exe` if foo.exe does not exist. Which is clearly not desired. fn main() { - let out_dir = tmp_dir(); - rustc().input("hello.rs").output(out_dir.join("hopefullydoesntexist bar.exe")).run(); + rustc().input("hello.rs").output("hopefullydoesntexist bar.exe").run(); rustc().input("spawn.rs").run(); run("spawn"); } diff --git a/tests/run-make/windows-ws2_32/rmake.rs b/tests/run-make/windows-ws2_32/rmake.rs index 543f8594478c..b3c70c354b42 100644 --- a/tests/run-make/windows-ws2_32/rmake.rs +++ b/tests/run-make/windows-ws2_32/rmake.rs @@ -3,7 +3,7 @@ // Tests that WS2_32.dll is not unnecessarily linked, see issue #85441 use run_make_support::object::{self, read::Object}; -use run_make_support::{rustc, tmp_dir}; +use run_make_support::rustc; use std::fs; fn main() { @@ -15,8 +15,7 @@ fn main() { } fn links_ws2_32(exe: &str) -> bool { - let path = tmp_dir().join(exe); - let binary_data = fs::read(path).unwrap(); + let binary_data = fs::read(exe).unwrap(); let file = object::File::parse(&*binary_data).unwrap(); for import in file.imports().unwrap() { if import.library().eq_ignore_ascii_case(b"WS2_32.dll") { From 9ce12c4cab44c2ce5356daf07bdc21fc56e2c48f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Fri, 7 Jun 2024 11:26:36 +0200 Subject: [PATCH 62/92] CI: remove `Setup Python` action --- .github/workflows/ci.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d2d4a7e3d42f..521f8ef0f5ae 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -156,12 +156,6 @@ jobs: - name: checkout submodules run: src/ci/scripts/checkout-submodules.sh - - - name: Setup Python - uses: actions/setup-python@v5 - with: - python-version: '3.x' - if: runner.environment == 'github-hosted' - name: install MinGW run: src/ci/scripts/install-mingw.sh From 4b0842f3cedc26ba841b6e8fb7314da2048c5ee2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Fri, 7 Jun 2024 11:31:45 +0200 Subject: [PATCH 63/92] Small refactoring --- src/tools/run-make-support/src/lib.rs | 5 ++--- src/tools/run-make-support/src/run.rs | 6 +++--- src/tools/run-make-support/src/rustc.rs | 5 ++--- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/tools/run-make-support/src/lib.rs b/src/tools/run-make-support/src/lib.rs index d4b02a81a4f6..79f732ecebb5 100644 --- a/src/tools/run-make-support/src/lib.rs +++ b/src/tools/run-make-support/src/lib.rs @@ -227,7 +227,7 @@ pub fn set_host_rpath(cmd: &mut Command) { let ld_lib_path_envvar = env_var("LD_LIB_PATH_ENVVAR"); cmd.env(&ld_lib_path_envvar, { let mut paths = vec![]; - paths.push(env::current_dir().unwrap()); + paths.push(cwd()); paths.push(PathBuf::from(env_var("HOST_RPATH_DIR"))); for p in env::split_paths(&env_var(&ld_lib_path_envvar)) { paths.push(p.to_path_buf()); @@ -325,9 +325,8 @@ pub fn assert_not_contains(haystack: &str, needle: &str) { /// 4) Calls `callback` /// 5) Switches working directory back to the original one /// 6) Removes `tmpdir` -/// Switch current working directory to a temporary directory pub fn run_in_tmpdir(callback: F) { - let original_dir = env::current_dir().unwrap(); + let original_dir = cwd(); let tmpdir = original_dir.join("../temporary-directory"); copy_dir_all(".", &tmpdir); diff --git a/src/tools/run-make-support/src/run.rs b/src/tools/run-make-support/src/run.rs index 20fc29650f87..b607c583e328 100644 --- a/src/tools/run-make-support/src/run.rs +++ b/src/tools/run-make-support/src/run.rs @@ -2,19 +2,19 @@ use std::env; use std::path::{Path, PathBuf}; use std::process::{Command, Output}; -use crate::{env_var, is_windows}; +use crate::{cwd, env_var, is_windows}; use super::handle_failed_output; fn run_common(name: &str) -> (Command, Output) { let mut bin_path = PathBuf::new(); - bin_path.push(env::current_dir().unwrap()); + bin_path.push(cwd()); bin_path.push(name); let ld_lib_path_envvar = env_var("LD_LIB_PATH_ENVVAR"); let mut cmd = Command::new(bin_path); cmd.env(&ld_lib_path_envvar, { let mut paths = vec![]; - paths.push(env::current_dir().unwrap()); + paths.push(cwd()); for p in env::split_paths(&env_var("TARGET_RPATH_ENV")) { paths.push(p.to_path_buf()); } diff --git a/src/tools/run-make-support/src/rustc.rs b/src/tools/run-make-support/src/rustc.rs index bcdedb2722fa..a64dd9d30cf5 100644 --- a/src/tools/run-make-support/src/rustc.rs +++ b/src/tools/run-make-support/src/rustc.rs @@ -1,10 +1,9 @@ -use std::env; use std::ffi::{OsStr, OsString}; use std::io::Write; use std::path::Path; use std::process::{Command, Output, Stdio}; -use crate::{env_var, handle_failed_output, set_host_rpath}; +use crate::{cwd, env_var, handle_failed_output, set_host_rpath}; /// Construct a new `rustc` invocation. pub fn rustc() -> Rustc { @@ -29,7 +28,7 @@ fn setup_common() -> Command { let rustc = env_var("RUSTC"); let mut cmd = Command::new(rustc); set_host_rpath(&mut cmd); - cmd.arg("-L").arg(env::current_dir().unwrap()); + cmd.arg("-L").arg(cwd()); cmd } From c9cb3280f365621d724d4f07a8dc96b325a107c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Fri, 7 Jun 2024 14:07:02 +0200 Subject: [PATCH 64/92] Address review comments --- src/tools/compiletest/src/runtest.rs | 2 +- tests/run-make/c-link-to-rust-dylib/rmake.rs | 2 +- tests/run-make/const-prop-lint/rmake.rs | 4 ++-- tests/run-make/notify-all-emit-artifacts/rmake.rs | 6 +++--- tests/run-make/rustdoc-scrape-examples-macros/rmake.rs | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index c50dd2d5a990..6d4b50a9238b 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -3463,7 +3463,7 @@ impl<'test> TestCx<'test> { // to the `rmake_out` directory. for path in walkdir::WalkDir::new(&self.testpaths.file).min_depth(1) { let path = path.unwrap().path().to_path_buf(); - if path.file_name().map(|s| s != OsStr::new("rmake.rs")).unwrap_or(false) { + if path.file_name().is_some_and(|s| s != "rmake.rs") { let target = rmake_out_dir.join(path.strip_prefix(&self.testpaths.file).unwrap()); if path.is_dir() { copy_dir_all(&path, target).unwrap(); diff --git a/tests/run-make/c-link-to-rust-dylib/rmake.rs b/tests/run-make/c-link-to-rust-dylib/rmake.rs index 37a326099d21..ec42e88032da 100644 --- a/tests/run-make/c-link-to-rust-dylib/rmake.rs +++ b/tests/run-make/c-link-to-rust-dylib/rmake.rs @@ -21,7 +21,7 @@ fn main() { run("bar"); let expected_extension = dynamic_lib_extension(); - read_dir(std::env::current_dir().unwrap(), |path| { + read_dir(cwd(), |path| { if path.is_file() && path.extension().is_some_and(|ext| ext == expected_extension) && path.file_name().and_then(|name| name.to_str()).is_some_and(|name| { diff --git a/tests/run-make/const-prop-lint/rmake.rs b/tests/run-make/const-prop-lint/rmake.rs index c2d3e1ab1366..6d0069a84d7a 100644 --- a/tests/run-make/const-prop-lint/rmake.rs +++ b/tests/run-make/const-prop-lint/rmake.rs @@ -2,12 +2,12 @@ use std::fs; -use run_make_support::rustc; +use run_make_support::{cwd, rustc}; fn main() { rustc().input("input.rs").run_fail_assert_exit_code(1); - for entry in fs::read_dir(std::env::current_dir().unwrap()).unwrap() { + for entry in fs::read_dir(cwd()).unwrap() { let entry = entry.unwrap(); let path = entry.path(); diff --git a/tests/run-make/notify-all-emit-artifacts/rmake.rs b/tests/run-make/notify-all-emit-artifacts/rmake.rs index 6d966793c35e..1c2e08ca8f53 100644 --- a/tests/run-make/notify-all-emit-artifacts/rmake.rs +++ b/tests/run-make/notify-all-emit-artifacts/rmake.rs @@ -6,7 +6,7 @@ // See extern crate run_make_support; -use run_make_support::rustc; +use run_make_support::{cwd, rustc}; fn main() { // With single codegen unit files are renamed to match the source file name @@ -17,7 +17,7 @@ fn main() { .codegen_units(1) .json("artifacts") .error_format("json") - .incremental(&std::env::current_dir().unwrap()) + .incremental(cwd()) .run(); let stderr = String::from_utf8_lossy(&output.stderr); for file in &["lib.o", "lib.ll", "lib.bc", "lib.s"] { @@ -33,7 +33,7 @@ fn main() { .codegen_units(2) .json("artifacts") .error_format("json") - .incremental(&std::env::current_dir().unwrap()) + .incremental(cwd()) .run(); let stderr = String::from_utf8_lossy(&output.stderr); for file in &["rcgu.o", "rcgu.ll", "rcgu.bc", "rcgu.s"] { diff --git a/tests/run-make/rustdoc-scrape-examples-macros/rmake.rs b/tests/run-make/rustdoc-scrape-examples-macros/rmake.rs index 2daaab76e544..c99112b6e5f2 100644 --- a/tests/run-make/rustdoc-scrape-examples-macros/rmake.rs +++ b/tests/run-make/rustdoc-scrape-examples-macros/rmake.rs @@ -1,6 +1,6 @@ //@ ignore-cross-compile -use run_make_support::{htmldocck, rustc, rustdoc}; +use run_make_support::{htmldocck, rustc, rustdoc, rust_lib_name}; fn main() { let out_dir = "rustdoc"; @@ -40,7 +40,7 @@ fn main() { .crate_name("ex") .crate_type("bin") .output(&out_dir) - .extern_(crate_name, format!("lib{crate_name}.rlib")) + .extern_(crate_name, rust_lib_name(crate_name)) .extern_(proc_crate_name, dylib_name.trim()) .arg("-Zunstable-options") .arg("--scrape-examples-output-path") From 3eb5341c3dbe1e0835ea76172a9ca798ca1423e1 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Fri, 7 Jun 2024 23:05:15 +1000 Subject: [PATCH 65/92] Improve docs for using custom paths with `--emit` --- src/doc/rustc/src/command-line-arguments.md | 40 +++++++++++++++------ 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/src/doc/rustc/src/command-line-arguments.md b/src/doc/rustc/src/command-line-arguments.md index 3e1ac2fa8ad7..72ab8ab5ce9b 100644 --- a/src/doc/rustc/src/command-line-arguments.md +++ b/src/doc/rustc/src/command-line-arguments.md @@ -213,21 +213,39 @@ The valid emit kinds are: `CRATE_NAME.o`. The output filename can be set with the [`-o` flag](#option-o-output). A -suffix may be added to the filename with the [`-C extra-filename` -flag](codegen-options/index.md#extra-filename). The files are written to the -current directory unless the [`--out-dir` flag](#option-out-dir) is used. Each -emission type may also specify the output filename with the form `KIND=PATH`, -which takes precedence over the `-o` flag. -Specifying `-o -` or `--emit KIND=-` asks rustc to emit to stdout. -Text output types (`asm`, `dep-info`, `llvm-ir` and `mir`) can be written to -stdout despite it being a tty or not. This will result in an error if any -binary output type is written to stdout that is a tty. -This will also result in an error if multiple output types -would be written to stdout, because they would be all mixed together. +suffix may be added to the filename with the +[`-C extra-filename` flag](codegen-options/index.md#extra-filename). + +Output files are written to the current directory unless the +[`--out-dir` flag](#option-out-dir) is used. [LLVM bitcode]: https://llvm.org/docs/BitCodeFormat.html [LLVM IR]: https://llvm.org/docs/LangRef.html +### Custom paths for individual emit kinds + +Each emit type can optionally be followed by `=` to specify an explicit output +path that only applies to the output of that type. For example: + +- `--emit=link,dep-info=/path/to/dep-info.d` + - Emit the crate itself as normal, + and also emit dependency info to the specified path. +- `--emit=llvm-ir=-,mir` + - Emit MIR to the default filename (based on crate name), + and emit LLVM IR to stdout. + +### Emitting to stdout + +When using `--emit` or [`-o`](#option-o-output), output can be sent to stdout +by specifying `-` as the path (e.g. `-o -`). + +Binary output types can only be written to stdout if it is not a tty. +Text output types (`asm`, `dep-info`, `llvm-ir` and `mir`) can be written to +stdout regardless of whether it is a tty or not. + +Only one type of output can be written to stdout. Attempting to write multiple +types to stdout at the same time will result in an error. + ## `--print`: print compiler information From 946185236349039b66da84cc95cfdc2fa3b3889d Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Fri, 7 Jun 2024 13:22:25 +0000 Subject: [PATCH 66/92] Add regression test --- tests/ui-fulldeps/stable-mir/check_instance.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/tests/ui-fulldeps/stable-mir/check_instance.rs b/tests/ui-fulldeps/stable-mir/check_instance.rs index 218c7b3e3863..f971426723f2 100644 --- a/tests/ui-fulldeps/stable-mir/check_instance.rs +++ b/tests/ui-fulldeps/stable-mir/check_instance.rs @@ -33,7 +33,7 @@ fn test_stable_mir() -> ControlFlow<()> { // Get all items and split generic vs monomorphic items. let (generic, mono): (Vec<_>, Vec<_>) = items.into_iter().partition(|item| item.requires_monomorphization()); - assert_eq!(mono.len(), 3, "Expected 2 mono functions and one constant"); + assert_eq!(mono.len(), 4, "Expected 2 mono functions and one constant"); assert_eq!(generic.len(), 2, "Expected 2 generic functions"); // For all monomorphic items, get the correspondent instances. @@ -57,8 +57,9 @@ fn test_body(body: mir::Body) { for term in body.blocks.iter().map(|bb| &bb.terminator) { match &term.kind { Call { func, .. } => { - let TyKind::RigidTy(ty) = func.ty(body.locals()).unwrap().kind() else { unreachable! - () }; + let TyKind::RigidTy(ty) = func.ty(body.locals()).unwrap().kind() else { + unreachable!() + }; let RigidTy::FnDef(def, args) = ty else { unreachable!() }; let instance = Instance::resolve(def, &args).unwrap(); let mangled_name = instance.mangled_name(); @@ -102,6 +103,9 @@ fn generate_input(path: &str) -> std::io::Result<()> { write!( file, r#" + + struct Foo(()); + pub fn ty_param(t: &T) -> T where T: Clone {{ t.clone() }} @@ -116,6 +120,7 @@ fn generate_input(path: &str) -> std::io::Result<()> { }} pub fn monomorphic() {{ + Foo(()); let v = vec![10]; let dup = ty_param(&v); assert_eq!(v, dup); From 93feaa668511cfd03a645217a2287699694002cf Mon Sep 17 00:00:00 2001 From: bohan Date: Thu, 6 Jun 2024 16:09:20 +0800 Subject: [PATCH 67/92] mark binding undetermined if target name exist and not obtained --- compiler/rustc_resolve/src/ident.rs | 10 +++++----- tests/ui/imports/cycle-import-in-std-1.rs | 9 +++++++++ tests/ui/imports/cycle-import-in-std-1.stderr | 13 +++++++++++++ tests/ui/imports/cycle-import-in-std-2.rs | 9 +++++++++ tests/ui/imports/cycle-import-in-std-2.stderr | 13 +++++++++++++ 5 files changed, 49 insertions(+), 5 deletions(-) create mode 100644 tests/ui/imports/cycle-import-in-std-1.rs create mode 100644 tests/ui/imports/cycle-import-in-std-1.stderr create mode 100644 tests/ui/imports/cycle-import-in-std-2.rs create mode 100644 tests/ui/imports/cycle-import-in-std-2.stderr diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 78bd3c4e49f2..b6a23317dc99 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -998,14 +998,14 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { let Some(module) = single_import.imported_module.get() else { return Err((Undetermined, Weak::No)); }; - let ImportKind::Single { source: ident, source_bindings, .. } = &single_import.kind + let ImportKind::Single { source: ident, target, target_bindings, .. } = + &single_import.kind else { unreachable!(); }; - if binding.map_or(false, |binding| binding.module().is_some()) - && source_bindings.iter().all(|binding| matches!(binding.get(), Err(Undetermined))) - { - // This branch allows the binding to be defined or updated later, + if (ident != target) && target_bindings.iter().all(|binding| binding.get().is_none()) { + // This branch allows the binding to be defined or updated later if the target name + // can hide the source but these bindings are not obtained. // avoiding module inconsistency between the resolve process and the finalize process. // See more details in #124840 return Err((Undetermined, Weak::No)); diff --git a/tests/ui/imports/cycle-import-in-std-1.rs b/tests/ui/imports/cycle-import-in-std-1.rs new file mode 100644 index 000000000000..2fa492c155d7 --- /dev/null +++ b/tests/ui/imports/cycle-import-in-std-1.rs @@ -0,0 +1,9 @@ +//@ edition: 2018 + +// https://github.com/rust-lang/rust/issues/124490 + +use ops::{self as std}; +//~^ ERROR: unresolved import `ops` +use std::collections::{self as ops}; + +fn main() {} diff --git a/tests/ui/imports/cycle-import-in-std-1.stderr b/tests/ui/imports/cycle-import-in-std-1.stderr new file mode 100644 index 000000000000..d4e6f32cc109 --- /dev/null +++ b/tests/ui/imports/cycle-import-in-std-1.stderr @@ -0,0 +1,13 @@ +error[E0432]: unresolved import `ops` + --> $DIR/cycle-import-in-std-1.rs:5:11 + | +LL | use ops::{self as std}; + | ^^^^^^^^^^^ no external crate `ops` + | + = help: consider importing one of these items instead: + core::ops + std::ops + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0432`. diff --git a/tests/ui/imports/cycle-import-in-std-2.rs b/tests/ui/imports/cycle-import-in-std-2.rs new file mode 100644 index 000000000000..d73c57944bc2 --- /dev/null +++ b/tests/ui/imports/cycle-import-in-std-2.rs @@ -0,0 +1,9 @@ +//@ edition: 2018 + +// https://github.com/rust-lang/rust/issues/125013 + +use ops::{self as std}; +//~^ ERROR: unresolved import `ops` +use std::ops::Deref::{self as ops}; + +fn main() {} diff --git a/tests/ui/imports/cycle-import-in-std-2.stderr b/tests/ui/imports/cycle-import-in-std-2.stderr new file mode 100644 index 000000000000..dc0270dffe4a --- /dev/null +++ b/tests/ui/imports/cycle-import-in-std-2.stderr @@ -0,0 +1,13 @@ +error[E0432]: unresolved import `ops` + --> $DIR/cycle-import-in-std-2.rs:5:11 + | +LL | use ops::{self as std}; + | ^^^^^^^^^^^ no external crate `ops` + | + = help: consider importing one of these items instead: + core::ops + std::ops + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0432`. From 3ee462944689cd1569240080f6c6da57453025d2 Mon Sep 17 00:00:00 2001 From: Noah Lev Date: Thu, 30 May 2024 21:05:12 -0700 Subject: [PATCH 68/92] rustdoc: Rename `Tester` to `DoctestVisitor` The new name more accurately captures what it is. --- src/librustdoc/doctest.rs | 16 ++++++++-------- src/librustdoc/html/markdown.rs | 8 ++++---- .../passes/check_doc_test_visibility.rs | 4 ++-- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 0d4bad6921db..f6f2b0439133 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -983,12 +983,12 @@ impl IndividualTestOptions { } } -pub(crate) trait Tester { - fn add_test(&mut self, test: String, config: LangString, line: usize); +pub(crate) trait DoctestVisitor { + fn visit_test(&mut self, test: String, config: LangString, line: usize); fn get_line(&self) -> usize { 0 } - fn register_header(&mut self, _name: &str, _level: u32) {} + fn visit_header(&mut self, _name: &str, _level: u32) {} } pub(crate) struct Collector { @@ -1091,8 +1091,8 @@ impl Collector { } } -impl Tester for Collector { - fn add_test(&mut self, test: String, config: LangString, line: usize) { +impl DoctestVisitor for Collector { + fn visit_test(&mut self, test: String, config: LangString, line: usize) { let filename = self.get_filename(); let name = self.generate_name(line, &filename); let crate_name = self.crate_name.clone(); @@ -1242,7 +1242,7 @@ impl Tester for Collector { } } - fn register_header(&mut self, name: &str, level: u32) { + fn visit_header(&mut self, name: &str, level: u32) { if self.use_headers { // We use these headings as test names, so it's good if // they're valid identifiers. @@ -1287,8 +1287,8 @@ impl Tester for Collector { } #[cfg(test)] // used in tests -impl Tester for Vec { - fn add_test(&mut self, _test: String, _config: LangString, line: usize) { +impl DoctestVisitor for Vec { + fn visit_test(&mut self, _test: String, _config: LangString, line: usize) { self.push(line); } } diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index 11cc81700ff5..c8973679132c 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -710,7 +710,7 @@ impl<'a, I: Iterator>> Iterator for Footnotes<'a, I> { } } -pub(crate) fn find_testable_code( +pub(crate) fn find_testable_code( doc: &str, tests: &mut T, error_codes: ErrorCodes, @@ -720,7 +720,7 @@ pub(crate) fn find_testable_code( find_codes(doc, tests, error_codes, enable_per_target_ignores, extra_info, false) } -pub(crate) fn find_codes( +pub(crate) fn find_codes( doc: &str, tests: &mut T, error_codes: ErrorCodes, @@ -773,7 +773,7 @@ pub(crate) fn find_codes( nb_lines -= 1; } let line = tests.get_line() + nb_lines + 1; - tests.add_test(text, block_info, line); + tests.visit_test(text, block_info, line); prev_offset = offset.start; } Event::Start(Tag::Heading(level, _, _)) => { @@ -781,7 +781,7 @@ pub(crate) fn find_codes( } Event::Text(ref s) if register_header.is_some() => { let level = register_header.unwrap(); - tests.register_header(s, level); + tests.visit_header(s, level); register_header = None; } _ => {} diff --git a/src/librustdoc/passes/check_doc_test_visibility.rs b/src/librustdoc/passes/check_doc_test_visibility.rs index d53eac0bccb1..a4cdb488f05f 100644 --- a/src/librustdoc/passes/check_doc_test_visibility.rs +++ b/src/librustdoc/passes/check_doc_test_visibility.rs @@ -44,8 +44,8 @@ pub(crate) struct Tests { pub(crate) found_tests: usize, } -impl crate::doctest::Tester for Tests { - fn add_test(&mut self, _: String, config: LangString, _: usize) { +impl crate::doctest::DoctestVisitor for Tests { + fn visit_test(&mut self, _: String, config: LangString, _: usize) { if config.rust && config.ignore == Ignore::None { self.found_tests += 1; } From f9e12ef946808f470f9d1cd97387a9381d13ad83 Mon Sep 17 00:00:00 2001 From: Noah Lev Date: Thu, 30 May 2024 22:10:14 -0700 Subject: [PATCH 69/92] rustdoc: Use `write_all` to ensure all content is written --- src/librustdoc/doctest.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index f6f2b0439133..08bc0f000845 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -80,7 +80,7 @@ pub(crate) fn generate_args_file(file_path: &Path, options: &RustdocOptions) -> let content = content.join("\n"); - file.write(content.as_bytes()) + file.write_all(content.as_bytes()) .map_err(|error| format!("failed to write arguments to temporary file: {error:?}"))?; Ok(()) } From 516010bd0f7ba82f29fb1cf56fdc990c9386f39c Mon Sep 17 00:00:00 2001 From: Noah Lev Date: Thu, 30 May 2024 22:36:52 -0700 Subject: [PATCH 70/92] Start moving format-specific code into doctest submodule --- src/librustdoc/doctest.rs | 129 ++--------------------------- src/librustdoc/doctest/markdown.rs | 1 + src/librustdoc/doctest/rust.rs | 126 ++++++++++++++++++++++++++++ 3 files changed, 136 insertions(+), 120 deletions(-) create mode 100644 src/librustdoc/doctest/markdown.rs create mode 100644 src/librustdoc/doctest/rust.rs diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 08bc0f000845..58f65450e69c 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -1,20 +1,19 @@ +mod markdown; +mod rust; + use rustc_ast as ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::sync::Lrc; use rustc_errors::emitter::stderr_destination; use rustc_errors::{ColorConfig, ErrorGuaranteed, FatalError}; -use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID, LOCAL_CRATE}; -use rustc_hir::{self as hir, intravisit, CRATE_HIR_ID}; +use rustc_hir::def_id::{CRATE_DEF_ID, LOCAL_CRATE}; +use rustc_hir::CRATE_HIR_ID; use rustc_interface::interface; -use rustc_middle::hir::map::Map; -use rustc_middle::hir::nested_filter; -use rustc_middle::ty::TyCtxt; use rustc_parse::new_parser_from_source_str; use rustc_parse::parser::attr::InnerAttrPolicy; -use rustc_resolve::rustdoc::span_of_fragments; use rustc_session::config::{self, CrateType, ErrorOutputType}; +use rustc_session::lint; use rustc_session::parse::ParseSess; -use rustc_session::{lint, Session}; use rustc_span::edition::Edition; use rustc_span::source_map::SourceMap; use rustc_span::symbol::sym; @@ -33,11 +32,12 @@ use std::sync::{Arc, Mutex}; use tempfile::{Builder as TempFileBuilder, TempDir}; -use crate::clean::{types::AttributesExt, Attributes}; use crate::config::Options as RustdocOptions; -use crate::html::markdown::{self, ErrorCodes, Ignore, LangString}; +use crate::html::markdown::{ErrorCodes, Ignore, LangString}; use crate::lint::init_lints; +use self::rust::HirCollector; + /// Options that apply to all doctests in a crate or Markdown file (for `rustdoc foo.md`). #[derive(Clone, Default)] pub(crate) struct GlobalTestOptions { @@ -1293,116 +1293,5 @@ impl DoctestVisitor for Vec { } } -struct HirCollector<'a, 'hir, 'tcx> { - sess: &'a Session, - collector: &'a mut Collector, - map: Map<'hir>, - codes: ErrorCodes, - tcx: TyCtxt<'tcx>, -} - -impl<'a, 'hir, 'tcx> HirCollector<'a, 'hir, 'tcx> { - fn visit_testable( - &mut self, - name: String, - def_id: LocalDefId, - sp: Span, - nested: F, - ) { - let ast_attrs = self.tcx.hir().attrs(self.tcx.local_def_id_to_hir_id(def_id)); - if let Some(ref cfg) = ast_attrs.cfg(self.tcx, &FxHashSet::default()) { - if !cfg.matches(&self.sess.psess, Some(self.tcx.features())) { - return; - } - } - - let has_name = !name.is_empty(); - if has_name { - self.collector.names.push(name); - } - - // The collapse-docs pass won't combine sugared/raw doc attributes, or included files with - // anything else, this will combine them for us. - let attrs = Attributes::from_ast(ast_attrs); - if let Some(doc) = attrs.opt_doc_value() { - // Use the outermost invocation, so that doctest names come from where the docs were written. - let span = ast_attrs - .iter() - .find(|attr| attr.doc_str().is_some()) - .map(|attr| attr.span.ctxt().outer_expn().expansion_cause().unwrap_or(attr.span)) - .unwrap_or(DUMMY_SP); - self.collector.set_position(span); - markdown::find_testable_code( - &doc, - self.collector, - self.codes, - self.collector.enable_per_target_ignores, - Some(&crate::html::markdown::ExtraInfo::new( - self.tcx, - def_id.to_def_id(), - span_of_fragments(&attrs.doc_strings).unwrap_or(sp), - )), - ); - } - - nested(self); - - if has_name { - self.collector.names.pop(); - } - } -} - -impl<'a, 'hir, 'tcx> intravisit::Visitor<'hir> for HirCollector<'a, 'hir, 'tcx> { - type NestedFilter = nested_filter::All; - - fn nested_visit_map(&mut self) -> Self::Map { - self.map - } - - fn visit_item(&mut self, item: &'hir hir::Item<'_>) { - let name = match &item.kind { - hir::ItemKind::Impl(impl_) => { - rustc_hir_pretty::id_to_string(&self.map, impl_.self_ty.hir_id) - } - _ => item.ident.to_string(), - }; - - self.visit_testable(name, item.owner_id.def_id, item.span, |this| { - intravisit::walk_item(this, item); - }); - } - - fn visit_trait_item(&mut self, item: &'hir hir::TraitItem<'_>) { - self.visit_testable(item.ident.to_string(), item.owner_id.def_id, item.span, |this| { - intravisit::walk_trait_item(this, item); - }); - } - - fn visit_impl_item(&mut self, item: &'hir hir::ImplItem<'_>) { - self.visit_testable(item.ident.to_string(), item.owner_id.def_id, item.span, |this| { - intravisit::walk_impl_item(this, item); - }); - } - - fn visit_foreign_item(&mut self, item: &'hir hir::ForeignItem<'_>) { - self.visit_testable(item.ident.to_string(), item.owner_id.def_id, item.span, |this| { - intravisit::walk_foreign_item(this, item); - }); - } - - fn visit_variant(&mut self, v: &'hir hir::Variant<'_>) { - self.visit_testable(v.ident.to_string(), v.def_id, v.span, |this| { - intravisit::walk_variant(this, v); - }); - } - - fn visit_field_def(&mut self, f: &'hir hir::FieldDef<'_>) { - self.visit_testable(f.ident.to_string(), f.def_id, f.span, |this| { - intravisit::walk_field_def(this, f); - }); - } -} - #[cfg(test)] mod tests; diff --git a/src/librustdoc/doctest/markdown.rs b/src/librustdoc/doctest/markdown.rs new file mode 100644 index 000000000000..a55b2941b87f --- /dev/null +++ b/src/librustdoc/doctest/markdown.rs @@ -0,0 +1 @@ +//! Doctest functionality used only for doctests in `.md` Markdown files. diff --git a/src/librustdoc/doctest/rust.rs b/src/librustdoc/doctest/rust.rs new file mode 100644 index 000000000000..e4527de77a02 --- /dev/null +++ b/src/librustdoc/doctest/rust.rs @@ -0,0 +1,126 @@ +//! Doctest functionality used only for doctests in `.rs` source files. + +use rustc_data_structures::fx::FxHashSet; +use rustc_hir::def_id::LocalDefId; +use rustc_hir::{self as hir, intravisit}; +use rustc_middle::hir::map::Map; +use rustc_middle::hir::nested_filter; +use rustc_middle::ty::TyCtxt; +use rustc_resolve::rustdoc::span_of_fragments; +use rustc_session::Session; +use rustc_span::{Span, DUMMY_SP}; + +use super::Collector; +use crate::clean::{types::AttributesExt, Attributes}; +use crate::html::markdown::{self, ErrorCodes}; + +pub(super) struct HirCollector<'a, 'hir, 'tcx> { + pub(super) sess: &'a Session, + pub(super) collector: &'a mut Collector, + pub(super) map: Map<'hir>, + pub(super) codes: ErrorCodes, + pub(super) tcx: TyCtxt<'tcx>, +} + +impl<'a, 'hir, 'tcx> HirCollector<'a, 'hir, 'tcx> { + pub(super) fn visit_testable( + &mut self, + name: String, + def_id: LocalDefId, + sp: Span, + nested: F, + ) { + let ast_attrs = self.tcx.hir().attrs(self.tcx.local_def_id_to_hir_id(def_id)); + if let Some(ref cfg) = ast_attrs.cfg(self.tcx, &FxHashSet::default()) { + if !cfg.matches(&self.sess.psess, Some(self.tcx.features())) { + return; + } + } + + let has_name = !name.is_empty(); + if has_name { + self.collector.names.push(name); + } + + // The collapse-docs pass won't combine sugared/raw doc attributes, or included files with + // anything else, this will combine them for us. + let attrs = Attributes::from_ast(ast_attrs); + if let Some(doc) = attrs.opt_doc_value() { + // Use the outermost invocation, so that doctest names come from where the docs were written. + let span = ast_attrs + .iter() + .find(|attr| attr.doc_str().is_some()) + .map(|attr| attr.span.ctxt().outer_expn().expansion_cause().unwrap_or(attr.span)) + .unwrap_or(DUMMY_SP); + self.collector.set_position(span); + markdown::find_testable_code( + &doc, + self.collector, + self.codes, + self.collector.enable_per_target_ignores, + Some(&crate::html::markdown::ExtraInfo::new( + self.tcx, + def_id.to_def_id(), + span_of_fragments(&attrs.doc_strings).unwrap_or(sp), + )), + ); + } + + nested(self); + + if has_name { + self.collector.names.pop(); + } + } +} + +impl<'a, 'hir, 'tcx> intravisit::Visitor<'hir> for HirCollector<'a, 'hir, 'tcx> { + type NestedFilter = nested_filter::All; + + fn nested_visit_map(&mut self) -> Self::Map { + self.map + } + + fn visit_item(&mut self, item: &'hir hir::Item<'_>) { + let name = match &item.kind { + hir::ItemKind::Impl(impl_) => { + rustc_hir_pretty::id_to_string(&self.map, impl_.self_ty.hir_id) + } + _ => item.ident.to_string(), + }; + + self.visit_testable(name, item.owner_id.def_id, item.span, |this| { + intravisit::walk_item(this, item); + }); + } + + fn visit_trait_item(&mut self, item: &'hir hir::TraitItem<'_>) { + self.visit_testable(item.ident.to_string(), item.owner_id.def_id, item.span, |this| { + intravisit::walk_trait_item(this, item); + }); + } + + fn visit_impl_item(&mut self, item: &'hir hir::ImplItem<'_>) { + self.visit_testable(item.ident.to_string(), item.owner_id.def_id, item.span, |this| { + intravisit::walk_impl_item(this, item); + }); + } + + fn visit_foreign_item(&mut self, item: &'hir hir::ForeignItem<'_>) { + self.visit_testable(item.ident.to_string(), item.owner_id.def_id, item.span, |this| { + intravisit::walk_foreign_item(this, item); + }); + } + + fn visit_variant(&mut self, v: &'hir hir::Variant<'_>) { + self.visit_testable(v.ident.to_string(), v.def_id, v.span, |this| { + intravisit::walk_variant(this, v); + }); + } + + fn visit_field_def(&mut self, f: &'hir hir::FieldDef<'_>) { + self.visit_testable(f.ident.to_string(), f.def_id, f.span, |this| { + intravisit::walk_field_def(this, f); + }); + } +} From 16db1a1bd0ca3f82a470363b8e6f95a8d03f63c5 Mon Sep 17 00:00:00 2001 From: Noah Lev Date: Thu, 30 May 2024 22:42:32 -0700 Subject: [PATCH 71/92] Move Markdown-specific doctest code into submodule --- src/librustdoc/doctest.rs | 2 ++ src/librustdoc/doctest/markdown.rs | 46 ++++++++++++++++++++++++++++ src/librustdoc/lib.rs | 2 +- src/librustdoc/markdown.rs | 48 ++---------------------------- 4 files changed, 51 insertions(+), 47 deletions(-) diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 58f65450e69c..22b5fa0951a4 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -1,6 +1,8 @@ mod markdown; mod rust; +pub(crate) use markdown::test as test_markdown; + use rustc_ast as ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::sync::Lrc; diff --git a/src/librustdoc/doctest/markdown.rs b/src/librustdoc/doctest/markdown.rs index a55b2941b87f..13239b9a5172 100644 --- a/src/librustdoc/doctest/markdown.rs +++ b/src/librustdoc/doctest/markdown.rs @@ -1 +1,47 @@ //! Doctest functionality used only for doctests in `.md` Markdown files. + +use std::fs::read_to_string; + +use rustc_span::DUMMY_SP; +use tempfile::tempdir; + +use super::{generate_args_file, Collector, GlobalTestOptions}; +use crate::config::Options; +use crate::html::markdown::{find_testable_code, ErrorCodes}; + +/// Runs any tests/code examples in the markdown file `input`. +pub(crate) fn test(options: Options) -> Result<(), String> { + use rustc_session::config::Input; + let input_str = match &options.input { + Input::File(path) => { + read_to_string(&path).map_err(|err| format!("{}: {err}", path.display()))? + } + Input::Str { name: _, input } => input.clone(), + }; + + let mut opts = GlobalTestOptions::default(); + opts.no_crate_inject = true; + + let temp_dir = + tempdir().map_err(|error| format!("failed to create temporary directory: {error:?}"))?; + let file_path = temp_dir.path().join("rustdoc-cfgs"); + generate_args_file(&file_path, &options)?; + + let mut collector = Collector::new( + options.input.filestem().to_string(), + options.clone(), + true, + opts, + None, + options.input.opt_path().map(ToOwned::to_owned), + options.enable_per_target_ignores, + file_path, + ); + collector.set_position(DUMMY_SP); + let codes = ErrorCodes::from(options.unstable_features.is_nightly_build()); + + find_testable_code(&input_str, &mut collector, codes, options.enable_per_target_ignores, None); + + crate::doctest::run_tests(options.test_args, options.nocapture, collector.tests); + Ok(()) +} diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index c0d2f9cfaf95..3b6bddf263a9 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -728,7 +728,7 @@ fn main_args( core::new_dcx(options.error_format, None, options.diagnostic_width, &options.unstable_opts); match (options.should_test, options.markdown_input()) { - (true, Some(_)) => return wrap_return(&diag, markdown::test(options)), + (true, Some(_)) => return wrap_return(&diag, doctest::test_markdown(options)), (true, None) => return doctest::run(&diag, options), (false, Some(input)) => { let input = input.to_owned(); diff --git a/src/librustdoc/markdown.rs b/src/librustdoc/markdown.rs index bcc5a37618a4..a98f81d011e8 100644 --- a/src/librustdoc/markdown.rs +++ b/src/librustdoc/markdown.rs @@ -3,18 +3,12 @@ use std::fs::{create_dir_all, read_to_string, File}; use std::io::prelude::*; use std::path::Path; -use tempfile::tempdir; - use rustc_span::edition::Edition; -use rustc_span::DUMMY_SP; -use crate::config::{Options, RenderOptions}; -use crate::doctest::{generate_args_file, Collector, GlobalTestOptions}; +use crate::config::RenderOptions; use crate::html::escape::Escape; use crate::html::markdown; -use crate::html::markdown::{ - find_testable_code, ErrorCodes, HeadingOffset, IdMap, Markdown, MarkdownWithToc, -}; +use crate::html::markdown::{ErrorCodes, HeadingOffset, IdMap, Markdown, MarkdownWithToc}; /// Separate any lines at the start of the file that begin with `# ` or `%`. fn extract_leading_metadata(s: &str) -> (Vec<&str>, &str) { @@ -137,41 +131,3 @@ pub(crate) fn render>( Ok(_) => Ok(()), } } - -/// Runs any tests/code examples in the markdown file `input`. -pub(crate) fn test(options: Options) -> Result<(), String> { - use rustc_session::config::Input; - let input_str = match &options.input { - Input::File(path) => { - read_to_string(&path).map_err(|err| format!("{}: {err}", path.display()))? - } - Input::Str { name: _, input } => input.clone(), - }; - - let mut opts = GlobalTestOptions::default(); - opts.no_crate_inject = true; - - let temp_dir = - tempdir().map_err(|error| format!("failed to create temporary directory: {error:?}"))?; - let file_path = temp_dir.path().join("rustdoc-cfgs"); - generate_args_file(&file_path, &options)?; - - let mut collector = Collector::new( - options.input.filestem().to_string(), - options.clone(), - true, - opts, - None, - options.input.opt_path().map(ToOwned::to_owned), - options.enable_per_target_ignores, - file_path, - ); - collector.set_position(DUMMY_SP); - let codes = ErrorCodes::from(options.unstable_features.is_nightly_build()); - - // For markdown files, custom code classes will be disabled until the feature is enabled by default. - find_testable_code(&input_str, &mut collector, codes, options.enable_per_target_ignores, None); - - crate::doctest::run_tests(options.test_args, options.nocapture, collector.tests); - Ok(()) -} From 85499ebf13d0c3c731ac5d69f02ed8e7eb2735bb Mon Sep 17 00:00:00 2001 From: Noah Lev Date: Fri, 31 May 2024 00:31:26 -0700 Subject: [PATCH 72/92] Separate doctest collection from running --- src/librustdoc/doctest.rs | 182 ++++++----------------------- src/librustdoc/doctest/markdown.rs | 110 ++++++++++++++--- src/librustdoc/doctest/rust.rs | 131 ++++++++++++++++----- 3 files changed, 236 insertions(+), 187 deletions(-) diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 22b5fa0951a4..8088b57dd76b 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -8,7 +8,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::sync::Lrc; use rustc_errors::emitter::stderr_destination; use rustc_errors::{ColorConfig, ErrorGuaranteed, FatalError}; -use rustc_hir::def_id::{CRATE_DEF_ID, LOCAL_CRATE}; +use rustc_hir::def_id::LOCAL_CRATE; use rustc_hir::CRATE_HIR_ID; use rustc_interface::interface; use rustc_parse::new_parser_from_source_str; @@ -19,10 +19,9 @@ use rustc_session::parse::ParseSess; use rustc_span::edition::Edition; use rustc_span::source_map::SourceMap; use rustc_span::symbol::sym; -use rustc_span::{BytePos, FileName, Pos, Span, DUMMY_SP}; +use rustc_span::FileName; use rustc_target::spec::{Target, TargetTriple}; -use std::env; use std::fs::File; use std::io::{self, Write}; use std::panic; @@ -38,7 +37,8 @@ use crate::config::Options as RustdocOptions; use crate::html::markdown::{ErrorCodes, Ignore, LangString}; use crate::lint::init_lints; -use self::rust::HirCollector; +use self::markdown::MdDoctest; +use self::rust::{HirCollector, RustDoctest}; /// Options that apply to all doctests in a crate or Markdown file (for `rustdoc foo.md`). #[derive(Clone, Default)] @@ -182,29 +182,19 @@ pub(crate) fn run( let mut collector = Collector::new( tcx.crate_name(LOCAL_CRATE).to_string(), options, - false, opts, - Some(compiler.sess.psess.clone_source_map()), - None, - enable_per_target_ignores, file_path, ); - let mut hir_collector = HirCollector { - sess: &compiler.sess, - collector: &mut collector, - map: tcx.hir(), - codes: ErrorCodes::from( - compiler.sess.opts.unstable_features.is_nightly_build(), - ), + let hir_collector = HirCollector::new( + &compiler.sess, + tcx.hir(), + ErrorCodes::from(compiler.sess.opts.unstable_features.is_nightly_build()), + enable_per_target_ignores, tcx, - }; - hir_collector.visit_testable( - "".to_string(), - CRATE_DEF_ID, - tcx.hir().span(CRATE_HIR_ID), - |this| tcx.hir().walk_toplevel_module(this), ); + let tests = hir_collector.collect_crate(); + tests.into_iter().for_each(|t| collector.add_test(ScrapedDoctest::Rust(t))); collector }); @@ -985,6 +975,12 @@ impl IndividualTestOptions { } } +/// A doctest scraped from the code, ready to be turned into a runnable test. +enum ScrapedDoctest { + Rust(RustDoctest), + Markdown(MdDoctest), +} + pub(crate) trait DoctestVisitor { fn visit_test(&mut self, test: String, config: LangString, line: usize); fn get_line(&self) -> usize { @@ -996,36 +992,9 @@ pub(crate) trait DoctestVisitor { pub(crate) struct Collector { pub(crate) tests: Vec, - // The name of the test displayed to the user, separated by `::`. - // - // In tests from Rust source, this is the path to the item - // e.g., `["std", "vec", "Vec", "push"]`. - // - // In tests from a markdown file, this is the titles of all headers (h1~h6) - // of the sections that contain the code block, e.g., if the markdown file is - // written as: - // - // ``````markdown - // # Title - // - // ## Subtitle - // - // ```rust - // assert!(true); - // ``` - // `````` - // - // the `names` vector of that test will be `["Title", "Subtitle"]`. - names: Vec, - rustdoc_options: RustdocOptions, - use_headers: bool, - enable_per_target_ignores: bool, crate_name: String, opts: GlobalTestOptions, - position: Span, - source_map: Option>, - filename: Option, visited_tests: FxHashMap<(String, usize), usize>, unused_extern_reports: Arc>>, compiling_test_count: AtomicUsize, @@ -1036,24 +1005,14 @@ impl Collector { pub(crate) fn new( crate_name: String, rustdoc_options: RustdocOptions, - use_headers: bool, opts: GlobalTestOptions, - source_map: Option>, - filename: Option, - enable_per_target_ignores: bool, arg_file: PathBuf, ) -> Collector { Collector { tests: Vec::new(), - names: Vec::new(), rustdoc_options, - use_headers, - enable_per_target_ignores, crate_name, opts, - position: DUMMY_SP, - source_map, - filename, visited_tests: FxHashMap::default(), unused_extern_reports: Default::default(), compiling_test_count: AtomicUsize::new(0), @@ -1061,8 +1020,8 @@ impl Collector { } } - fn generate_name(&self, line: usize, filename: &FileName) -> String { - let mut item_path = self.names.join("::"); + fn generate_name(&self, filename: &FileName, line: usize, logical_path: &[String]) -> String { + let mut item_path = logical_path.join("::"); item_path.retain(|c| c != ' '); if !item_path.is_empty() { item_path.push(' '); @@ -1070,40 +1029,24 @@ impl Collector { format!("{} - {item_path}(line {line})", filename.prefer_local()) } - pub(crate) fn set_position(&mut self, position: Span) { - self.position = position; - } - - fn get_filename(&self) -> FileName { - if let Some(ref source_map) = self.source_map { - let filename = source_map.span_to_filename(self.position); - if let FileName::Real(ref filename) = filename - && let Ok(cur_dir) = env::current_dir() - && let Some(local_path) = filename.local_path() - && let Ok(path) = local_path.strip_prefix(&cur_dir) - { - return path.to_owned().into(); + fn add_test(&mut self, test: ScrapedDoctest) { + let (filename, line, logical_path, langstr, text) = match test { + ScrapedDoctest::Rust(RustDoctest { filename, line, logical_path, langstr, text }) => { + (filename, line, logical_path, langstr, text) } - filename - } else if let Some(ref filename) = self.filename { - filename.clone().into() - } else { - FileName::Custom("input".to_owned()) - } - } -} + ScrapedDoctest::Markdown(MdDoctest { filename, line, logical_path, langstr, text }) => { + (filename, line, logical_path, langstr, text) + } + }; -impl DoctestVisitor for Collector { - fn visit_test(&mut self, test: String, config: LangString, line: usize) { - let filename = self.get_filename(); - let name = self.generate_name(line, &filename); + let name = self.generate_name(&filename, line, &logical_path); let crate_name = self.crate_name.clone(); let opts = self.opts.clone(); - let edition = config.edition.unwrap_or(self.rustdoc_options.edition); + let edition = langstr.edition.unwrap_or(self.rustdoc_options.edition); let target_str = self.rustdoc_options.target.to_string(); let unused_externs = self.unused_extern_reports.clone(); - let no_run = config.no_run || self.rustdoc_options.no_run; - if !config.compile_fail { + let no_run = langstr.no_run || self.rustdoc_options.no_run; + if !langstr.compile_fail { self.compiling_test_count.fetch_add(1, Ordering::SeqCst); } @@ -1140,11 +1083,11 @@ impl DoctestVisitor for Collector { let rustdoc_test_options = IndividualTestOptions::new(&self.rustdoc_options, &self.arg_file, test_id); - debug!("creating test {name}: {test}"); + debug!("creating test {name}: {text}"); self.tests.push(test::TestDescAndFn { desc: test::TestDesc { name: test::DynTestName(name), - ignore: match config.ignore { + ignore: match langstr.ignore { Ignore::All => true, Ignore::None => false, Ignore::Some(ref ignores) => ignores.iter().any(|s| target_str.contains(s)), @@ -1157,7 +1100,7 @@ impl DoctestVisitor for Collector { end_col: 0, // compiler failures are test failures should_panic: test::ShouldPanic::No, - compile_fail: config.compile_fail, + compile_fail: langstr.compile_fail, no_run, test_type: test::TestType::DocTest, }, @@ -1166,11 +1109,11 @@ impl DoctestVisitor for Collector { unused_externs.lock().unwrap().push(uext); }; let res = run_test( - &test, + &text, &crate_name, line, rustdoc_test_options, - config, + langstr, no_run, &opts, edition, @@ -1233,59 +1176,6 @@ impl DoctestVisitor for Collector { })), }); } - - fn get_line(&self) -> usize { - if let Some(ref source_map) = self.source_map { - let line = self.position.lo().to_usize(); - let line = source_map.lookup_char_pos(BytePos(line as u32)).line; - if line > 0 { line - 1 } else { line } - } else { - 0 - } - } - - fn visit_header(&mut self, name: &str, level: u32) { - if self.use_headers { - // We use these headings as test names, so it's good if - // they're valid identifiers. - let name = name - .chars() - .enumerate() - .map(|(i, c)| { - if (i == 0 && rustc_lexer::is_id_start(c)) - || (i != 0 && rustc_lexer::is_id_continue(c)) - { - c - } else { - '_' - } - }) - .collect::(); - - // Here we try to efficiently assemble the header titles into the - // test name in the form of `h1::h2::h3::h4::h5::h6`. - // - // Suppose that originally `self.names` contains `[h1, h2, h3]`... - let level = level as usize; - if level <= self.names.len() { - // ... Consider `level == 2`. All headers in the lower levels - // are irrelevant in this new level. So we should reset - // `self.names` to contain headers until