diff --git a/compiler/rustc_attr_parsing/src/attributes/stability.rs b/compiler/rustc_attr_parsing/src/attributes/stability.rs index d8dbe167ed175..0cfb68d523f9b 100644 --- a/compiler/rustc_attr_parsing/src/attributes/stability.rs +++ b/compiler/rustc_attr_parsing/src/attributes/stability.rs @@ -40,6 +40,7 @@ const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ Allow(Target::Static), Allow(Target::ForeignFn), Allow(Target::ForeignStatic), + Allow(Target::ForeignTy), Allow(Target::ExternCrate), ]); diff --git a/compiler/rustc_codegen_ssa/src/back/archive.rs b/compiler/rustc_codegen_ssa/src/back/archive.rs index bdf346d8e69d2..4fc516b244857 100644 --- a/compiler/rustc_codegen_ssa/src/back/archive.rs +++ b/compiler/rustc_codegen_ssa/src/back/archive.rs @@ -22,7 +22,7 @@ use rustc_target::spec::Arch; use tracing::trace; use super::metadata::{create_compressed_metadata_file, search_for_section}; -use super::rmeta_link; +use super::rmeta_link::{self, RmetaLinkCache}; use super::symbol_edit::{apply_edits, collect_internal_names}; use crate::common; // Public for ArchiveBuilderBuilder::extract_bundled_libs @@ -311,7 +311,7 @@ fn find_binutils_dlltool(sess: &Session) -> OsString { } pub enum AddArchiveKind<'a> { - Rlib(/*skip*/ &'a dyn Fn(&str, ArchiveEntryKind) -> bool), + Rlib(&'a mut RmetaLinkCache, /*skip*/ &'a dyn Fn(&str, ArchiveEntryKind) -> bool), Other, } @@ -466,7 +466,11 @@ pub fn try_extract_macho_fat_archive( } impl<'a> ArchiveBuilder for ArArchiveBuilder<'a> { - fn add_archive(&mut self, archive_path: &Path, ar_kind: AddArchiveKind<'_>) -> io::Result<()> { + fn add_archive( + &mut self, + archive_path: &Path, + mut ar_kind: AddArchiveKind<'_>, + ) -> io::Result<()> { let mut archive_path = archive_path.to_path_buf(); if self.sess.target.llvm_target.contains("-apple-macosx") && let Some(new_archive_path) = try_extract_macho_fat_archive(self.sess, &archive_path)? @@ -481,8 +485,14 @@ impl<'a> ArchiveBuilder for ArArchiveBuilder<'a> { let archive_map = unsafe { Mmap::map(File::open(&archive_path)?)? }; let archive = ArchiveFile::parse(&*archive_map) .map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?; - let metadata_link = match ar_kind { - AddArchiveKind::Rlib(..) => rmeta_link::read(&archive, &archive_map, &archive_path), + let skip = match &ar_kind { + AddArchiveKind::Rlib(_, skip) => Some(*skip), + AddArchiveKind::Other => None, + }; + let metadata_link = match &mut ar_kind { + AddArchiveKind::Rlib(cache, _) => cache.get_or_insert_with(&archive_path, || { + rmeta_link::read(&archive, &archive_map, &archive_path) + }), AddArchiveKind::Other => None, }; let archive_index = self.src_archives.len(); @@ -512,9 +522,9 @@ impl<'a> ArchiveBuilder for ArArchiveBuilder<'a> { } else { ArchiveEntryKind::Other }; - let drop = match ar_kind { - AddArchiveKind::Rlib(skip) => skip(&file_name, kind), - AddArchiveKind::Other => false, + let drop = match skip { + Some(skip) => skip(&file_name, kind), + None => false, }; if !drop { let source = if entry.is_thin() { diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index b7e153f47b024..07c713a006994 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -59,6 +59,7 @@ use super::archive::{ use super::command::Command; use super::linker::{self, Linker}; use super::metadata::{MetadataPosition, create_wrapper_file}; +use super::rmeta_link::RmetaLinkCache; use super::rpath::{self, RPathConfig}; use super::{apple, rmeta_link, versioned_llvm_target}; use crate::base::needs_allocator_shim_for_linking; @@ -86,6 +87,7 @@ pub fn link_binary( let _timer = sess.timer("link_binary"); let output_metadata = sess.opts.output_types.contains_key(&OutputType::Metadata); let mut tempfiles_for_stdout_output: Vec = Vec::new(); + let mut rmeta_link_cache = RmetaLinkCache::default(); for &crate_type in &crate_info.crate_types { // Ignore executable crates if we have -Z no-codegen, as they will error. if (sess.opts.unstable_opts.no_codegen || !sess.opts.output_types.should_codegen()) @@ -139,6 +141,7 @@ pub fn link_binary( link_staticlib( sess, archive_builder_builder, + &mut rmeta_link_cache, &compiled_modules, &crate_info, &metadata, @@ -150,6 +153,7 @@ pub fn link_binary( link_natively( sess, archive_builder_builder, + &mut rmeta_link_cache, crate_type, &out_filename, &compiled_modules, @@ -502,6 +506,7 @@ fn link_rlib<'a>( fn link_staticlib( sess: &Session, archive_builder_builder: &dyn ArchiveBuilderBuilder, + rmeta_link_cache: &mut RmetaLinkCache, compiled_modules: &CompiledModules, crate_info: &CrateInfo, metadata: &EncodedMetadata, @@ -531,7 +536,7 @@ fn link_staticlib( let bundled_libs: FxIndexSet<_> = native_libs.filter_map(|lib| lib.filename).collect(); ab.add_archive( path, - AddArchiveKind::Rlib(&|fname: &str, entry_kind| { + AddArchiveKind::Rlib(rmeta_link_cache, &|fname: &str, entry_kind| { // Ignore metadata and rmeta-link files. if fname == METADATA_FILENAME || fname == rmeta_link::FILENAME { return true; @@ -939,6 +944,7 @@ fn report_linker_output( fn link_natively( sess: &Session, archive_builder_builder: &dyn ArchiveBuilderBuilder, + rmeta_link_cache: &mut RmetaLinkCache, crate_type: CrateType, out_filename: &Path, compiled_modules: &CompiledModules, @@ -965,6 +971,7 @@ fn link_natively( flavor, sess, archive_builder_builder, + rmeta_link_cache, crate_type, tmpdir, temp_filename, @@ -2562,6 +2569,7 @@ fn linker_with_args( flavor: LinkerFlavor, sess: &Session, archive_builder_builder: &dyn ArchiveBuilderBuilder, + rmeta_link_cache: &mut RmetaLinkCache, crate_type: CrateType, tmpdir: &Path, out_filename: &Path, @@ -2690,6 +2698,7 @@ fn linker_with_args( cmd, sess, archive_builder_builder, + rmeta_link_cache, crate_info, crate_type, tmpdir, @@ -3126,6 +3135,7 @@ fn add_upstream_rust_crates( cmd: &mut dyn Linker, sess: &Session, archive_builder_builder: &dyn ArchiveBuilderBuilder, + rmeta_link_cache: &mut RmetaLinkCache, crate_info: &CrateInfo, crate_type: CrateType, tmpdir: &Path, @@ -3178,6 +3188,7 @@ fn add_upstream_rust_crates( cmd, sess, archive_builder_builder, + rmeta_link_cache, crate_info, tmpdir, cnum, @@ -3309,6 +3320,7 @@ fn add_static_crate( cmd: &mut dyn Linker, sess: &Session, archive_builder_builder: &dyn ArchiveBuilderBuilder, + rmeta_link_cache: &mut RmetaLinkCache, crate_info: &CrateInfo, tmpdir: &Path, cnum: CrateNum, @@ -3339,7 +3351,7 @@ fn add_static_crate( let mut archive = archive_builder_builder.new_archive_builder(sess); if let Err(error) = archive.add_archive( cratepath, - AddArchiveKind::Rlib(&|f, entry_kind| { + AddArchiveKind::Rlib(rmeta_link_cache, &|f, entry_kind| { if f == METADATA_FILENAME || f == rmeta_link::FILENAME { return true; } diff --git a/compiler/rustc_codegen_ssa/src/back/rmeta_link.rs b/compiler/rustc_codegen_ssa/src/back/rmeta_link.rs index 68b23ca9ac5cb..58da783277dfe 100644 --- a/compiler/rustc_codegen_ssa/src/back/rmeta_link.rs +++ b/compiler/rustc_codegen_ssa/src/back/rmeta_link.rs @@ -2,9 +2,10 @@ //! and potentially other data collected and used when building or linking a rlib. //! See . -use std::path::Path; +use std::path::{Path, PathBuf}; use object::read::archive::ArchiveFile; +use rustc_data_structures::fx::FxHashMap; use rustc_serialize::opaque::mem_encoder::MemEncoder; use rustc_serialize::opaque::{MAGIC_END_BYTES, MemDecoder}; use rustc_serialize::{Decodable, Encodable}; @@ -54,3 +55,18 @@ pub fn read_from_data(archive_data: &[u8], rlib_path: &Path) -> Option>, +} + +impl RmetaLinkCache { + pub fn get_or_insert_with( + &mut self, + rlib_path: &Path, + load: impl FnOnce() -> Option, + ) -> Option<&RmetaLink> { + self.cache.entry(rlib_path.to_path_buf()).or_insert_with(load).as_ref() + } +} diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs index 39402cfc35dce..32678e8a5a12f 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs @@ -409,9 +409,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { pub(super) fn report_ambiguous_assoc_item( &self, - bound1: ty::PolyTraitRef<'tcx>, - bound2: ty::PolyTraitRef<'tcx>, - matching_candidates: impl Iterator>, + matching_candidates: &[ty::PolyTraitRef<'tcx>], qself: AssocItemQSelf, assoc_tag: ty::AssocTag, assoc_ident: Ident, @@ -443,7 +441,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // predicates!). // FIXME: Turn this into a structured, translatable & more actionable suggestion. let mut where_bounds = vec![]; - for bound in [bound1, bound2].into_iter().chain(matching_candidates) { + for &bound in matching_candidates { let bound_id = bound.def_id(); let assoc_item = tcx.associated_items(bound_id).find_by_ident_and_kind( tcx, diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 4781525ad686e..e8a7156b41fe4 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -24,6 +24,7 @@ use std::{assert_matches, slice}; use rustc_abi::FIRST_VARIANT; use rustc_ast::LitKind; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; +use rustc_data_structures::sso::SsoHashSet; use rustc_errors::codes::*; use rustc_errors::{ Applicability, Diag, DiagCtxtHandle, ErrorGuaranteed, FatalError, StashKey, @@ -1262,6 +1263,74 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ) } + /// When there are multiple traits which contain an identically named + /// associated item, this function eliminates any traits which are a + /// supertrait of another candidate trait. + /// + /// This is the type-level analogue of + /// `rustc_hir_typeck::method::probe::ProbeContext::collapse_candidates_to_subtrait_pick`; + /// keep both implementations in sync. + /// + /// This implements RFC #3624. + fn collapse_candidates_to_subtrait_pick( + &self, + matching_candidates: &[ty::PolyTraitRef<'tcx>], + ) -> Option> { + if !self.tcx().features().supertrait_item_shadowing() { + return None; + } + + let mut child_trait = matching_candidates[0]; + let mut supertraits: SsoHashSet<_> = + traits::supertrait_def_ids(self.tcx(), child_trait.def_id()).collect(); + + let mut remaining_candidates: Vec<_> = matching_candidates[1..].iter().copied().collect(); + while !remaining_candidates.is_empty() { + let mut made_progress = false; + let mut next_round = vec![]; + + for remaining_trait in remaining_candidates { + if supertraits.contains(&remaining_trait.def_id()) { + made_progress = true; + continue; + } + + // This candidate is not a supertrait of the `child_trait`. + // Check if it's a subtrait of the `child_trait`, instead. + // If it is, then it must have been a subtrait of every + // other pick we've eliminated at this point. It will + // take over at this point. + let remaining_trait_supertraits: SsoHashSet<_> = + traits::supertrait_def_ids(self.tcx(), remaining_trait.def_id()).collect(); + if remaining_trait_supertraits.contains(&child_trait.def_id()) { + child_trait = remaining_trait; + supertraits = remaining_trait_supertraits; + made_progress = true; + continue; + } + + // Neither `child_trait` or the current candidate are + // supertraits of each other. + // Don't bail here, since we may be comparing two supertraits + // of a common subtrait. These two supertraits won't be related + // at all, but we will pick them up next round when we find their + // child as we continue iterating in this round. + next_round.push(remaining_trait); + } + + if made_progress { + // If we've made progress, iterate again. + remaining_candidates = next_round; + } else { + // Otherwise, we must have at least two candidates which + // are not related to each other at all. + return None; + } + } + + Some(child_trait) + } + /// Search for a single trait bound whose trait defines the associated item given by /// `assoc_ident`. /// @@ -1296,10 +1365,15 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { }; if let Some(bound2) = matching_candidates.next() { + let all_matching_candidates: Vec<_> = + [bound1, bound2].into_iter().chain(matching_candidates).collect(); + if let Some(bound) = self.collapse_candidates_to_subtrait_pick(&all_matching_candidates) + { + return Ok(bound); + } + return Err(self.report_ambiguous_assoc_item( - bound1, - bound2, - matching_candidates, + &all_matching_candidates, qself, assoc_tag, assoc_ident, diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index e8921841c4405..ce187aa1aff14 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -2359,6 +2359,10 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { /// multiple conflicting picks if there is one pick whose trait container is a subtrait /// of the trait containers of all of the other picks. /// + /// This is the method-probe analogue of + /// `rustc_hir_analysis::hir_ty_lowering::HirTyLowerer::collapse_candidates_to_subtrait_pick`; + /// keep both implementations in sync. + /// /// This implements RFC #3624. fn collapse_candidates_to_subtrait_pick( &self, diff --git a/compiler/rustc_query_impl/src/handle_cycle_error.rs b/compiler/rustc_query_impl/src/handle_cycle_error.rs index d4156ade79bd7..7e7ed3ab3b525 100644 --- a/compiler/rustc_query_impl/src/handle_cycle_error.rs +++ b/compiler/rustc_query_impl/src/handle_cycle_error.rs @@ -105,7 +105,7 @@ pub(crate) fn variances_of<'tcx>( err: Diag<'_>, ) -> &'tcx [ty::Variance] { let _guar = err.delay_as_bug(); - let n = tcx.generics_of(def_id).own_params.len(); + let n = tcx.generics_of(def_id).count(); tcx.arena.alloc_from_iter(iter::repeat_n(ty::Bivariant, n)) } diff --git a/library/core/src/option.rs b/library/core/src/option.rs index c11b2354d1638..163e8bf714b1d 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -581,6 +581,7 @@ use crate::clone::TrivialClone; use crate::iter::{self, FusedIterator, TrustedLen}; use crate::marker::Destruct; +use crate::num::NonZero; use crate::ops::{self, ControlFlow, Deref, DerefMut, Residual, Try}; use crate::panicking::{panic, panic_display}; use crate::pin::Pin; @@ -2666,18 +2667,111 @@ impl Iterator for OptionFlatten { type Item = A::Item; fn next(&mut self) -> Option { - self.iter.as_mut()?.next() + match &mut self.iter { + Some(iter) => iter.next(), + None => None, + } } fn size_hint(&self) -> (usize, Option) { - self.iter.as_ref().map(|i| i.size_hint()).unwrap_or((0, Some(0))) + match &self.iter { + Some(iter) => iter.size_hint(), + None => (0, Some(0)), + } + } + + fn advance_by(&mut self, n: usize) -> Result<(), NonZero> { + match &mut self.iter { + Some(iter) => iter.advance_by(n), + None => NonZero::new(n).map_or(Ok(()), Err), + } + } + + fn nth(&mut self, n: usize) -> Option { + match &mut self.iter { + Some(iter) => iter.nth(n), + None => None, + } + } + + fn fold(self, init: Acc, fold: Fold) -> Acc + where + Fold: FnMut(Acc, Self::Item) -> Acc, + { + match self.iter { + Some(iter) => iter.fold(init, fold), + None => init, + } + } + + fn try_fold(&mut self, init: Acc, fold: Fold) -> R + where + Fold: FnMut(Acc, Self::Item) -> R, + R: Try, + { + match &mut self.iter { + Some(iter) => iter.try_fold(init, fold), + None => try { init }, + } + } + + fn count(self) -> usize { + match self.iter { + Some(iter) => iter.count(), + None => 0, + } + } + + fn last(self) -> Option { + match self.iter { + Some(iter) => iter.last(), + None => None, + } } } #[unstable(feature = "option_into_flat_iter", issue = "148441")] impl DoubleEndedIterator for OptionFlatten { fn next_back(&mut self) -> Option { - self.iter.as_mut()?.next_back() + match &mut self.iter { + Some(iter) => iter.next_back(), + None => None, + } + } + + fn advance_back_by(&mut self, n: usize) -> Result<(), NonZero> { + match &mut self.iter { + Some(iter) => iter.advance_back_by(n), + None => NonZero::new(n).map_or(Ok(()), Err), + } + } + + fn nth_back(&mut self, n: usize) -> Option { + match &mut self.iter { + Some(iter) => iter.nth_back(n), + None => None, + } + } + + fn rfold(self, init: Acc, fold: Fold) -> Acc + where + Fold: FnMut(Acc, Self::Item) -> Acc, + { + match self.iter { + Some(iter) => iter.rfold(init, fold), + None => init, + } + } + + fn try_rfold(&mut self, init: Acc, fold: Fold) -> R + where + Fold: FnMut(Acc, Self::Item) -> R, + R: Try, + { + match &mut self.iter { + Some(iter) => iter.try_rfold(init, fold), + None => try { init }, + } } } diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs index fee783133133f..ccee062584e01 100644 --- a/src/librustdoc/formats/cache.rs +++ b/src/librustdoc/formats/cache.rs @@ -233,6 +233,30 @@ impl Cache { } } +impl CacheBuilder<'_, '_> { + /// Extends `dids` with ones that an impl should be associated with for a type appearing in its + /// `Self` type or trait generic arguments, accounting for references and `#[fundamental]` + /// wrappers. + /// + /// This ensures that impls like `impl Trait> for Foreign`, `impl Trait for + /// Box`, and other variations of these, are documented on `Local`'s page. + fn extend_with_fundamental_dids(&self, ty: &clean::Type, dids: &mut FxIndexSet) { + dids.extend(ty.def_id(self.cache)); + // without_borrowed_ref allows cases like `impl Trait<&Box> for Foreign` to be + // handled by this function. (This is rare in practice, but easy to handle here.) + if let clean::Type::Path { path } = ty.without_borrowed_ref() + && let Some(generics) = path.generics() + && let ty::Adt(adt, _) = + self.tcx.type_of(path.def_id()).instantiate_identity().skip_norm_wip().kind() + && adt.is_fundamental() + { + for inner in generics { + self.extend_with_fundamental_dids(inner, dids); + } + } + } +} + impl DocFolder for CacheBuilder<'_, '_> { fn fold_item(&mut self, item: clean::Item) -> Option { if item.item_id.is_local() { @@ -418,22 +442,9 @@ impl DocFolder for CacheBuilder<'_, '_> { // Note: matching twice to restrict the lifetime of the `i` borrow. let mut dids = FxIndexSet::default(); match i.for_ { - clean::Type::Path { ref path } - | clean::BorrowedRef { type_: clean::Type::Path { ref path }, .. } => { - dids.insert(path.def_id()); - if let Some(generics) = path.generics() - && let ty::Adt(adt, _) = self - .tcx - .type_of(path.def_id()) - .instantiate_identity() - .skip_norm_wip() - .kind() - && adt.is_fundamental() - { - for ty in generics { - dids.extend(ty.def_id(self.cache)); - } - } + clean::Type::Path { .. } + | clean::BorrowedRef { type_: clean::Type::Path { .. }, .. } => { + self.extend_with_fundamental_dids(&i.for_, &mut dids); } clean::DynTrait(ref bounds, _) | clean::BorrowedRef { type_: clean::DynTrait(ref bounds, _), .. } => { @@ -452,7 +463,7 @@ impl DocFolder for CacheBuilder<'_, '_> { && let Some(generics) = trait_.generics() { for bound in generics { - dids.extend(bound.def_id(self.cache)); + self.extend_with_fundamental_dids(bound, &mut dids); } } let impl_item = Impl { impl_item: item }; diff --git a/src/rustdoc-json-types/Cargo.toml b/src/rustdoc-json-types/Cargo.toml index 9e18691a9605a..ba6dc2d69e1ff 100644 --- a/src/rustdoc-json-types/Cargo.toml +++ b/src/rustdoc-json-types/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "rustdoc-json-types" version = "0.1.0" -edition = "2021" +edition = "2024" [lib] path = "lib.rs" diff --git a/src/tools/jsondocck/Cargo.toml b/src/tools/jsondocck/Cargo.toml index 80fc26cbe6680..f2ae68f7fbd69 100644 --- a/src/tools/jsondocck/Cargo.toml +++ b/src/tools/jsondocck/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "jsondocck" version = "0.1.0" -edition = "2021" +edition = "2024" [dependencies] jsonpath-rust = "1.0.0" diff --git a/src/tools/jsondoclint/Cargo.toml b/src/tools/jsondoclint/Cargo.toml index cc8ecefd530b4..848c0b37ae94e 100644 --- a/src/tools/jsondoclint/Cargo.toml +++ b/src/tools/jsondoclint/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "jsondoclint" version = "0.1.0" -edition = "2021" +edition = "2024" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/src/tools/miri/README.md b/src/tools/miri/README.md index 6b8d8ed1f8018..fdc746ead51f5 100644 --- a/src/tools/miri/README.md +++ b/src/tools/miri/README.md @@ -171,6 +171,38 @@ available (which affects `cfg(target_feature)`), and it tells Miri to consider t that the interpreted program runs on as having the feature available (meaning the code is allowed to invoke the corresponding intrinsics). +### Nextest integration + +Miri can be combined with [`cargo-nextest`](https://nexte.st): + +``` +cargo install --locked cargo-nextest +cargo miri nextest run +``` + +Nextest spawns a separate instance of Miri for each test, which has several advantages: +- Tests can run in parallel. Miri itself only uses a single thread per interpreter so this can + give a massive speedup (but see the caveat below). +- Tests do not stop when a single problem is found. Miri aborts execution when it encounters + Undefined Behavior or an unsupported operation (there is often not really any way to continue), + so once a single test fails, the remaining tests cannot be executed. Nextest's process-per-test + model means that you end up with a full list of which tests worked in Miri and which tests had a + problem. + +However, there is also a big caveat: Miri will [re-compile the test crate every time it is +invoked](https://github.com/rust-lang/miri/issues/5013), which means a crate with N tests will be +compiled N+1 times. If the test crate takes a long time to build, this can outweigh the benefits of +parallelization. + +For more information about nextest, see the [`cargo-nextest` Miri +documentation](https://nexte.st/book/miri.html). + +Note: Nextest's one-test-per-process model means that `cargo miri test` is able to detect data +races where two tests race on a shared resource, but `cargo miri nextest run` will not detect +such races. + +Note: `cargo-nextest` [does not support doctests](https://github.com/nextest-rs/nextest/issues/16). + ### Testing multiple different executions Certain parts of the execution are picked randomly by Miri, such as the exact base address @@ -184,6 +216,7 @@ MIRIFLAGS="-Zmiri-many-seeds" cargo miri test # tries the seeds in 0..64 MIRIFLAGS="-Zmiri-many-seeds=0..16" cargo miri test ``` +Miri will test the given range of seeds with parallel interpreter instances. The default of 64 different seeds can be quite slow, so you often want to specify a smaller range. ### Running Miri on CI @@ -243,26 +276,6 @@ However, even for targets that we do support, the degree of support for accessin (such as the file system) differs between targets: generally, Linux targets have the best support, and macOS targets are usually on par. Windows is supported less well. -### Running tests in parallel - -Though it implements Rust threading, Miri itself is a single-threaded interpreter -(it works like a multi-threaded OS on a single-core CPU). -This means that when running `cargo miri test`, you will probably see a dramatic -increase in the amount of time it takes to run your whole test suite due to the -inherent interpreter slowdown and a loss of parallelism. - -You can get your test suite's parallelism back by running `cargo miri nextest run -jN` -(note that you will need [`cargo-nextest`](https://nexte.st) installed). -This works because `cargo-nextest` collects a list of all tests then launches a -separate `cargo miri run` for each test. For more information about nextest, see the -[`cargo-nextest` Miri documentation](https://nexte.st/book/miri.html). - -Note: This one-test-per-process model means that `cargo miri test` is able to detect data -races where two tests race on a shared resource, but `cargo miri nextest run` will not detect -such races. - -Note: `cargo-nextest` does not support doctests, see https://github.com/nextest-rs/nextest/issues/16 - ### Directly invoking the `miri` driver The recommended way to invoke Miri is via `cargo miri`. Directly invoking the underlying `miri` diff --git a/src/tools/miri/cargo-miri/src/phases.rs b/src/tools/miri/cargo-miri/src/phases.rs index f58cec827cf5d..caf6987291970 100644 --- a/src/tools/miri/cargo-miri/src/phases.rs +++ b/src/tools/miri/cargo-miri/src/phases.rs @@ -36,6 +36,9 @@ Examples: This will print the path to the generated sysroot (and nothing else) on stdout. stderr will still contain progress information about how the build is doing. +For documentation on `-Zmiri-...` flags, see Miri's README.md, available at: +- $(rustc --print sysroot)/share/doc/miri/README.md +- https://github.com/rust-lang/miri/blob/master/README.md "; fn show_help() { diff --git a/src/tools/miri/ci/ci.sh b/src/tools/miri/ci/ci.sh index f7c0b739c1dd4..42107adfe566a 100755 --- a/src/tools/miri/ci/ci.sh +++ b/src/tools/miri/ci/ci.sh @@ -168,6 +168,7 @@ case $HOST_TARGET in MANY_SEEDS=16 TEST_TARGET=x86_64-unknown-freebsd run_tests MANY_SEEDS=16 TEST_TARGET=i686-unknown-freebsd run_tests MANY_SEEDS=16 TEST_TARGET=x86_64-unknown-illumos run_tests + MANY_SEEDS=16 TEST_TARGET=x86_64-unknown-netbsd run_tests_minimal hello ;; armv7-unknown-linux-gnueabihf) # Host diff --git a/src/tools/miri/priroda/src/main.rs b/src/tools/miri/priroda/src/main.rs index 2739b041b0b48..fe67b21d2c134 100644 --- a/src/tools/miri/priroda/src/main.rs +++ b/src/tools/miri/priroda/src/main.rs @@ -6,6 +6,7 @@ extern crate rustc_data_structures; extern crate rustc_driver; extern crate rustc_hir; extern crate rustc_hir_analysis; +extern crate rustc_index; extern crate rustc_interface; extern crate rustc_log; extern crate rustc_middle; @@ -19,13 +20,15 @@ use std::path::PathBuf; use miri::*; use rustc_driver::Compilation; use rustc_hir::attrs::CrateType; +use rustc_index::IndexVec; use rustc_interface::interface; use rustc_middle::mir; +use rustc_middle::mir::{Local, VarDebugInfoContents}; use rustc_middle::ty::TyCtxt; use rustc_session::EarlyDiagCtxt; use rustc_session::config::ErrorOutputType; -use rustc_span::Span; use rustc_span::source_map::SourceMap; +use rustc_span::{Span, Symbol}; fn find_sysroot() -> String { std::env::var("MIRI_SYSROOT") @@ -129,6 +132,11 @@ struct PrirodaContext<'tcx> { last_location: Option, } +struct LocalDesc { + name: Option, + local: Local, + ty: String, +} /// Controls when execution returns to the frontend. enum ResumeMode { /// Stop at the next visible MIR instruction. @@ -336,15 +344,49 @@ impl<'tcx> PrirodaContext<'tcx> { } } - /// Returns the names of all user-visible locals in the innermost stack frame. + /// Returns structured descriptions for locals in the innermost stack frame. /// - /// Uses `var_debug_info` from the MIR body, which is the same source that - /// DWARF debug info is built from, so the names match what the user wrote. - fn list_locals(&self) -> Vec { + /// Starts from all MIR locals, then enriches them with source names from + /// `var_debug_info` when a debug entry maps directly to a whole local. + fn list_locals(&self) -> Vec { let Some(frame) = self.ecx.active_thread_stack().last() else { return Vec::new(); }; - frame.body().var_debug_info.iter().map(|info| info.name.to_string()).collect() + + self.local_desc_map(frame).into_iter().collect() + } + + fn local_desc_map( + &self, + frame: &Frame<'tcx, Provenance, FrameExtra<'tcx>>, + ) -> IndexVec { + // Initialize one description per MIR local so the table can be indexed by Local. + let mut locals: IndexVec = frame + .body() + .local_decls + .iter_enumerated() + .map(|(id, local_decl)| { + LocalDesc { name: None, local: id, ty: local_decl.ty.to_string() } + }) + .collect(); + + // FIXME: Some debug-info entries do not have a backing MIR local, for example + // because the source variable was optimized out or is represented as a + // projection. This local-indexed table cannot represent those entries yet; + // the final locals list should become a `Vec` with `id : Option`, `id` + // could be renamed to `local`. + + // Attach source names from debug info when the debug entry maps directly to a whole MIR local. + for var_debug_info in &frame.body().var_debug_info { + if let VarDebugInfoContents::Place(place) = var_debug_info.value + && let Some(local) = place.as_local() + && locals[local].name.is_none() + { + locals[local].name = Some(var_debug_info.name); + } + } + + locals } } @@ -366,7 +408,7 @@ enum BreakpointSetResult { enum CommandResult { ExecutionStopped(StepResult), BreakpointResult(BreakpointSetResult), - Locals(Vec), + Locals(Vec), // FIXME: distinguish terminating the debugger session from disconnecting a // frontend and terminating the interpreted program once multiple frontends exist. TerminateSession, @@ -403,12 +445,21 @@ impl Cli { BreakpointSetResult::Duplicate => println!("Duplicate breakpoint"), }, - CommandResult::Locals(names) => - if names.is_empty() { + CommandResult::Locals(locals_desc) => + if locals_desc.is_empty() { println!("no locals"); } else { - for name in &names { - println!("{name}"); + for local_desc in &locals_desc { + let mut name_str = "None".to_string(); + if let Some(name) = local_desc.name { + name_str = name.to_string(); + } + println!( + "Name: {}, Id: _{}, Ty: {}", + name_str, + local_desc.local.index(), + local_desc.ty, + ); } }, CommandResult::TerminateSession => { diff --git a/src/tools/miri/priroda/tests/ui/locals_in_function.stdout b/src/tools/miri/priroda/tests/ui/locals_in_function.stdout index ed5889f5836e7..4f2786d3e17b7 100644 --- a/src/tools/miri/priroda/tests/ui/locals_in_function.stdout +++ b/src/tools/miri/priroda/tests/ui/locals_in_function.stdout @@ -1,6 +1,10 @@ (priroda) breakpoint added: {MANIFEST_DIR}/tests/ui/locals_in_function.rs:5 (priroda) Hit breakpoint {MANIFEST_DIR}/tests/ui/locals_in_function.rs:5 -(priroda) x -y +(priroda) Name: None, Id: _0, Ty: () +Name: x, Id: _1, Ty: i32 +Name: y, Id: _2, Ty: bool +Name: None, Id: _3, Ty: (i32, bool) +Name: None, Id: _4, Ty: i32 +Name: None, Id: _5, Ty: bool (priroda) quitting diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 4a2bfdb2cd558..d7711535dbf29 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -01f54e80e888b66d6486a3a95d481b87353016df +16761606d606b6ec4d0c88fc9251670742ad9fd2 diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs index 296de803d5828..b58a7cfeba92e 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs @@ -574,7 +574,9 @@ impl<'tcx> Tree { // Don't check for protector if it is a Cell (see `unsafe_cell_deallocate` in `interior_mutability.rs`). // Related to https://github.com/rust-lang/rust/issues/55005. && !perm.permission.is_cell() - // Only trigger UB if the accessed bit is set, i.e. if the protector is actually protecting this offset. See #4579. + // Only trigger UB if the accessed bit is set, i.e. if the protector + // is actually protecting this offset. See #4579. Note that this + // takes into account the access we just did above! && perm.accessed { Err(TbError { diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs index 58fe7dc541fc3..f5761f4712470 100644 --- a/src/tools/miri/src/helpers.rs +++ b/src/tools/miri/src/helpers.rs @@ -129,7 +129,13 @@ pub fn iter_exported_symbols<'tcx>( || codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_COMPILER) || codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER) }; - if exported { + // FIXME: `#[no_mangle]` makes no sense on a generic item, but still causes it to be + // considered "extern". Remove this once `no_mangle_generic_items` is a hard error. + let exported_mono = exported && { + let generics = tcx.generics_of(def_id); + !generics.requires_monomorphization(tcx) + }; + if exported_mono { f(LOCAL_CRATE, def_id.into())?; } } diff --git a/src/tools/miri/src/shims/aarch64.rs b/src/tools/miri/src/shims/aarch64.rs index deab856b3e24f..3d8946fb36720 100644 --- a/src/tools/miri/src/shims/aarch64.rs +++ b/src/tools/miri/src/shims/aarch64.rs @@ -146,6 +146,45 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } } + // Signed saturating doubling multiply returning the high half. + // + // Used by the `vqdmulh*` functions. + // + // This LLVM intrinsic multiplies the values of corresponding elements of the two source + // vector registers (which are signed integers), doubles the results, places the most significant half of the + // final results (using a saturating cast to fit the element type) into a vector, and writes the vector to the destination register. + // + // https://developer.arm.com/architectures/instruction-sets/intrinsics#f:@navigationhierarchiessimdisa=[Neon]&q=vqdmulh + name if name.starts_with("neon.sqdmulh.") => { + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + + let (left, left_len) = this.project_to_simd(left)?; + let (right, right_len) = this.project_to_simd(right)?; + let (dest, dest_len) = this.project_to_simd(dest)?; + assert_eq!(left_len, right_len); + assert_eq!(left_len, dest_len); + + let elem_size = dest.layout.field(this, 0).size; + let bits = elem_size.bits(); + let min = elem_size.signed_int_min(); + let max = elem_size.signed_int_max(); + + for i in 0..dest_len { + let a = this.read_scalar(&this.project_index(&left, i)?)?.to_int(elem_size)?; + let b = this.read_scalar(&this.project_index(&right, i)?)?.to_int(elem_size)?; + + // Uses i128 arithmetic, which cannot overflow because the intrinsic takes at most i32. + let doubled = a.strict_mul(b).strict_mul(2); + let res = (doubled >> bits).clamp(min, max); + + this.write_scalar( + Scalar::from_int(res, elem_size), + &this.project_index(&dest, i)?, + )?; + } + } + // Vector table lookup: each index selects a byte from the 16-byte table, out-of-range -> 0. // Used to implement vtbl1_u8 function. // LLVM does not have a portable shuffle that takes non-const indices diff --git a/src/tools/miri/src/shims/unix/foreign_items.rs b/src/tools/miri/src/shims/unix/foreign_items.rs index c2ae65091ee2c..14eb7af4b52df 100644 --- a/src/tools/miri/src/shims/unix/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/foreign_items.rs @@ -840,18 +840,46 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "mmap" => { - let [addr, length, prot, flags, fd, offset] = - this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [addr, length, prot, flags, fd, offset] = this.check_shim_sig( + shim_sig!(extern "C" fn(*mut _, usize, i32, i32, i32, libc::off_t) -> *mut _), + link_name, + abi, + args, + )?; let offset = this.read_scalar(offset)?.to_int(this.libc_ty_layout("off_t").size)?; let ptr = this.mmap(addr, length, prot, flags, fd, offset)?; this.write_scalar(ptr, dest)?; } "munmap" => { - let [addr, length] = - this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [addr, length] = this.check_shim_sig( + shim_sig!(extern "C" fn(*mut _, usize) -> i32), + link_name, + abi, + args, + )?; let result = this.munmap(addr, length)?; this.write_scalar(result, dest)?; } + "mprotect" => { + let [addr, length, prot] = this.check_shim_sig( + shim_sig!(extern "C" fn(*mut _, usize, i32) -> i32), + link_name, + abi, + args, + )?; + let result = this.mprotect(addr, length, prot)?; + this.write_scalar(result, dest)?; + } + "madvise" => { + let [addr, length, advice] = this.check_shim_sig( + shim_sig!(extern "C" fn(*mut _, usize, i32) -> i32), + link_name, + abi, + args, + )?; + let result = this.madvise(addr, length, advice)?; + this.write_scalar(result, dest)?; + } "reallocarray" => { // Currently this function does not exist on all Unixes, e.g. on macOS. @@ -1410,7 +1438,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let [_, _] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.write_null(dest)?; } - "sigaction" | "mprotect" if this.frame_in_std() => { + "sigaction" if this.frame_in_std() => { let [_, _, _] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.write_null(dest)?; } diff --git a/src/tools/miri/src/shims/unix/mem.rs b/src/tools/miri/src/shims/unix/mem.rs index c2ad7c0e9d0ab..50be3c7ae15e6 100644 --- a/src/tools/miri/src/shims/unix/mem.rs +++ b/src/tools/miri/src/shims/unix/mem.rs @@ -53,9 +53,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { return interp_ok(Scalar::from_maybe_pointer(Pointer::without_provenance(addr), this)); } - let prot_read = this.eval_libc_i32("PROT_READ"); - let prot_write = this.eval_libc_i32("PROT_WRITE"); - // First, we do some basic argument validation as required by mmap if (flags & (map_private | map_shared)).count_ones() != 1 { this.set_last_error(LibcError("EINVAL"))?; @@ -80,13 +77,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ); } - // Miri doesn't support protections other than PROT_READ|PROT_WRITE. - if prot != prot_read | prot_write { - throw_unsup_format!( - "Miri does not support calls to mmap with protections other than \ - PROT_READ|PROT_WRITE", - ); - } + verify_prot(this, prot)?; // Miri does not support shared mappings, or any of the other extensions that for example // Linux has added to the flags arguments. @@ -103,14 +94,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } let align = this.machine.page_align(); - let Some(map_length) = length.checked_next_multiple_of(this.machine.page_size) else { + let Some(map_length) = round_up_to_page_size(this, length) else { this.set_last_error(LibcError("EINVAL"))?; return interp_ok(this.eval_libc("MAP_FAILED")); }; - if map_length > this.target_usize_max() { - this.set_last_error(LibcError("EINVAL"))?; - return interp_ok(this.eval_libc("MAP_FAILED")); - } let ptr = this.allocate_ptr( Size::from_bytes(map_length), @@ -135,13 +122,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { return this.set_errno_and_return_neg1_i32(LibcError("EINVAL")); } - let Some(length) = length.checked_next_multiple_of(this.machine.page_size) else { + let Some(length) = round_up_to_page_size(this, length) else { return this.set_errno_and_return_neg1_i32(LibcError("EINVAL")); }; - if length > this.target_usize_max() { - this.set_last_error(LibcError("EINVAL"))?; - return interp_ok(this.eval_libc("MAP_FAILED")); - } let length = Size::from_bytes(length); this.deallocate_ptr( @@ -152,4 +135,106 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { interp_ok(Scalar::from_i32(0)) } + + fn mprotect( + &mut self, + addr: &OpTy<'tcx>, + length: &OpTy<'tcx>, + prot: &OpTy<'tcx>, + ) -> InterpResult<'tcx, Scalar> { + let this = self.eval_context_mut(); + + let addr = this.read_pointer(addr)?; + let length = this.read_target_usize(length)?; + let prot = this.read_scalar(prot)?.to_i32()?; + + // addr must be a multiple of the page size. + if !addr.addr().bytes().is_multiple_of(this.machine.page_size) { + return this.set_errno_and_return_neg1_i32(LibcError("EINVAL")); + } + + verify_prot(this, prot)?; + + // The pages from `[addr, addr + length)` must be mapped, so length definitely must not overflow. + let Some(length) = round_up_to_page_size(this, length) else { + return this.set_errno_and_return_neg1_i32(LibcError("ENOMEM")); + }; + // Ensure this is actually allocated memory we can access. + this.check_ptr_access(addr, Size::from_bytes(length), CheckInAllocMsg::MemoryAccess) + .map_err_kind(|_| err_ub_format!("`mprotect` called on out-of-bounds memory"))?; + + // If the memory comes from memory the Rust program has allocated with mmap, we only support + // `PROT_READ|PROT_WRITE`, so this `mprotect` is a no-op. If the memory was mmaped by the + // runtime (e.g. if it's the stack, executable memory, or static memory), POSIX also allows + // us to remap it. In those cases, such a call to `PROT_READ|PROT_WRITE` might actually change the permissions, + // but treating them as the new permissions is still UB. Therefore, we just pretend that we + // did the permission change by returning success, and will still reject if you try to use + // it with the "new" permissions. + interp_ok(Scalar::from_i32(0)) + } + + fn madvise( + &mut self, + addr: &OpTy<'tcx>, + length: &OpTy<'tcx>, + advice: &OpTy<'tcx>, + ) -> InterpResult<'tcx, Scalar> { + let this = self.eval_context_mut(); + + let addr = this.read_pointer(addr)?; + let length = this.read_target_usize(length)?; + let advise = this.read_scalar(advice)?.to_i32()?; + + // addr must be a multiple of the page size. + if !addr.addr().bytes().is_multiple_of(this.machine.page_size) { + return this.set_errno_and_return_neg1_i32(LibcError("EINVAL")); + } + + // advise must be supported. + let madv_normal = this.eval_libc_i32("MADV_NORMAL"); + let madv_random = this.eval_libc_i32("MADV_RANDOM"); + let madv_sequential = this.eval_libc_i32("MADV_SEQUENTIAL"); + let madv_willneed = this.eval_libc_i32("MADV_WILLNEED"); + if advise != madv_normal + && advise != madv_random + && advise != madv_sequential + && advise != madv_willneed + { + throw_unsup_format!( + "Miri does not support calls to madvise with advice other than MADV_NORMAL, MADV_RANDOM, MADV_SEQUENTIAL, or MADV_WILLNEED", + ); + } + + // The pages from `[addr, addr + length)` must be mapped, so length definitely must not overflow. + let Some(length) = round_up_to_page_size(this, length) else { + return this.set_errno_and_return_neg1_i32(LibcError("ENOMEM")); + }; + // Ensure this is actually allocated memory we can access. + this.check_ptr_access(addr, Size::from_bytes(length), CheckInAllocMsg::MemoryAccess) + .map_err_kind(|_| err_ub_format!("`madvise` called on out-of-bounds memory"))?; + + // All advises we support are no-ops. + interp_ok(Scalar::from_i32(0)) + } +} + +fn round_up_to_page_size(this: &MiriInterpCx<'_>, length: u64) -> Option { + length + .checked_next_multiple_of(this.machine.page_size) + .filter(|length| *length <= this.target_isize_max().try_into().unwrap()) +} + +fn verify_prot<'tcx>(this: &mut MiriInterpCx<'tcx>, prot: i32) -> InterpResult<'tcx> { + let prot_read = this.eval_libc_i32("PROT_READ"); + let prot_write = this.eval_libc_i32("PROT_WRITE"); + + // Miri doesn't support protections other than PROT_READ|PROT_WRITE. + if prot != prot_read | prot_write { + throw_unsup_format!( + "Miri does not support calls to mmap/mprotect with protections other than \ + PROT_READ|PROT_WRITE", + ); + } + + interp_ok(()) } diff --git a/src/tools/miri/src/shims/unix/socket.rs b/src/tools/miri/src/shims/unix/socket.rs index ae882f8ff3a40..72b614f58d0a4 100644 --- a/src/tools/miri/src/shims/unix/socket.rs +++ b/src/tools/miri/src/shims/unix/socket.rs @@ -1487,9 +1487,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Only shutting down the write end doesn't cause an EPOLLHUP event // and thus we won't set the `write_closed` readiness for it here. readiness.write_closed |= is_read_write_shutdown; - // The Linux kernel also sets EPOLLIN when both ends of a socket are closed: + // The Linux kernel also sets EPOLLIN when the read end of a socket is closed: // - readiness.readable |= is_read_write_shutdown; + readiness.readable |= is_read_shutdown || is_read_write_shutdown; drop(readiness); @@ -1697,12 +1697,16 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ) -> InterpResult<'tcx, Result> { let this = self.eval_context_mut(); - let SocketState::Connected(stream) = &mut *socket.state.borrow_mut() else { + let mut state = socket.state.borrow_mut(); + let SocketState::Connected(stream) = &mut *state else { panic!("try_non_block_send must only be called when the socket is connected") }; // This is a *non-blocking* write. let result = this.write_to_host(stream, length, buffer_ptr)?; + + drop(state); + match result { Err(IoError::HostError(e)) if matches!(e.kind(), io::ErrorKind::NotConnected | io::ErrorKind::WouldBlock) => @@ -1715,8 +1719,13 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // would be returned on UNIX-like systems. We thus remap this error to an EWOULDBLOCK. interp_ok(Err(IoError::HostError(io::ErrorKind::WouldBlock.into()))) } - Ok(bytes_written) if bytes_written < length => { - // We had a short write. On Unix hosts using the `epoll` and `kqueue` backends, a + Ok(bytes_written) + if bytes_written < length && !socket.io_readiness.borrow().write_closed => + { + // We had a short write. (Note that we don't want to clear the writable readiness for + // sockets whose write end has already been closed as those never block a write, i.e., + // they are always write-ready.) + // On Unix hosts using the `epoll` and `kqueue` backends, a // short write means that the write buffer is full. We update the readiness // accordingly, which means that next time we see "writable" we will report an epoll // edge. Some applications (e.g. tokio) rely on this behavior; see @@ -1820,7 +1829,8 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ) -> InterpResult<'tcx, Result> { let this = self.eval_context_mut(); - let SocketState::Connected(stream) = &mut *socket.state.borrow_mut() else { + let mut state = socket.state.borrow_mut(); + let SocketState::Connected(stream) = &mut *state else { panic!("try_non_block_recv must only be called when the socket is connected") }; @@ -1832,6 +1842,9 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { length, buffer_ptr, )?; + + drop(state); + match result { Err(IoError::HostError(e)) if matches!(e.kind(), io::ErrorKind::NotConnected | io::ErrorKind::WouldBlock) => @@ -1844,9 +1857,16 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // would be returned on UNIX-like systems. We thus remap this error to an EWOULDBLOCK. interp_ok(Err(IoError::HostError(io::ErrorKind::WouldBlock.into()))) } - Ok(bytes_read) if !should_peek && bytes_read < length && bytes_read > 0 => { + Ok(bytes_read) + if !should_peek + && bytes_read < length + && bytes_read > 0 + && !socket.io_readiness.borrow().read_closed => + { // We had a short read (and were not peeking). (Note that reading 0 bytes is guaranteed - // to indicate EOF, and can never happen spuriously, so we have to exclude that case.) + // to indicate EOF, and can never happen spuriously, so we have to exclude that case. + // We also don't want to clear the readable readiness for sockets whose read end has + // already been closed as those never block a read, i.e., they are always read-ready.) // On Unix hosts using the `epoll` and `kqueue` backends, a short read means that the // read buffer is empty. We update the readiness accordingly, which means that next time // we see "readable" we will report an epoll edge. Some applications (e.g. tokio) rely on diff --git a/src/tools/miri/src/shims/unix/sync.rs b/src/tools/miri/src/shims/unix/sync.rs index 4e351c1571218..a34314f3fbea6 100644 --- a/src/tools/miri/src/shims/unix/sync.rs +++ b/src/tools/miri/src/shims/unix/sync.rs @@ -35,7 +35,13 @@ const PTHREAD_INIT: u8 = 1; #[inline] fn mutexattr_kind_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> { interp_ok(match &ecx.tcx.sess.target.os { - Os::Linux | Os::Illumos | Os::Solaris | Os::MacOs | Os::FreeBsd | Os::Android => 0, + Os::Linux + | Os::Illumos + | Os::Solaris + | Os::MacOs + | Os::FreeBsd + | Os::Android + | Os::NetBsd => 0, os => throw_unsup_format!("`pthread_mutexattr` is not supported on {os}"), }) } @@ -135,8 +141,8 @@ impl SyncObj for PthreadMutex { fn mutex_init_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, Size> { let offset = match &ecx.tcx.sess.target.os { Os::Linux | Os::Illumos | Os::Solaris | Os::FreeBsd | Os::Android => 0, - // macOS stores a signature in the first bytes, so we move to offset 4. - Os::MacOs => 4, + // macOS and NetBSD store a signature in the first bytes, so we move to offset 4. + Os::MacOs | Os::NetBsd => 4, os => throw_unsup_format!("`pthread_mutex` is not supported on {os}"), }; let offset = Size::from_bytes(offset); @@ -163,7 +169,7 @@ fn mutex_init_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, Size> check_static_initializer("PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP"); check_static_initializer("PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP"); } - Os::Illumos | Os::Solaris | Os::MacOs | Os::FreeBsd | Os::Android => { + Os::Illumos | Os::Solaris | Os::MacOs | Os::FreeBsd | Os::Android | Os::NetBsd => { // No non-standard initializers. } os => throw_unsup_format!("`pthread_mutex` is not supported on {os}"), diff --git a/src/tools/miri/src/shims/x86/mod.rs b/src/tools/miri/src/shims/x86/mod.rs index 64b5b786c6c08..b3a8291b9361b 100644 --- a/src/tools/miri/src/shims/x86/mod.rs +++ b/src/tools/miri/src/shims/x86/mod.rs @@ -879,33 +879,39 @@ fn mpsadbw<'tcx>( assert_eq!(left.layout.size, dest.layout.size); let (num_chunks, op_items_per_chunk, left) = split_simd_to_128bit_chunks(ecx, left)?; + assert!(num_chunks <= 2); + let (_, _, right) = split_simd_to_128bit_chunks(ecx, right)?; let (_, dest_items_per_chunk, dest) = split_simd_to_128bit_chunks(ecx, dest)?; assert_eq!(op_items_per_chunk, dest_items_per_chunk.strict_mul(2)); let imm = ecx.read_scalar(imm)?.to_uint(imm.layout.size)?; - // Bit 2 of `imm` specifies the offset for indices of `left`. - // The offset is 0 when the bit is 0 or 4 when the bit is 1. - let left_offset = u64::try_from((imm >> 2) & 1).unwrap().strict_mul(4); - // Bits 0..=1 of `imm` specify the offset for indices of - // `right` in blocks of 4 elements. - let right_offset = u64::try_from(imm & 0b11).unwrap().strict_mul(4); for i in 0..num_chunks { let left = ecx.project_index(&left, i)?; let right = ecx.project_index(&right, i)?; let dest = ecx.project_index(&dest, i)?; + // The first 128-bit chunk uses the low 3 bits of IMM, the second chunk uses bits 3..6. + let lane_imm = imm.strict_shr(i.strict_mul(3).try_into().unwrap()); + + // Bit 2 of `lane_imm` specifies the offset for indices of `left`. + // The offset is 0 when the bit is 0 or 4 when the bit is 1. + let left_base = u64::try_from((lane_imm >> 2) & 1).unwrap().strict_mul(4); + // Bits 0..=1 of `lane_imm` specify the offset for indices of + // `right` in blocks of 4 elements. + let right_base = u64::try_from(lane_imm & 0b11).unwrap().strict_mul(4); + for j in 0..dest_items_per_chunk { - let left_offset = left_offset.strict_add(j); + let left_offset = left_base.strict_add(j); let mut res: u16 = 0; for k in 0..4 { let left = ecx .read_scalar(&ecx.project_index(&left, left_offset.strict_add(k))?)? .to_u8()?; let right = ecx - .read_scalar(&ecx.project_index(&right, right_offset.strict_add(k))?)? + .read_scalar(&ecx.project_index(&right, right_base.strict_add(k))?)? .to_u8()?; res = res.strict_add(left.abs_diff(right).into()); } diff --git a/src/tools/miri/test-cargo-miri/run-test.py b/src/tools/miri/test-cargo-miri/run-test.py index 60c7e192f4805..164b61f597065 100755 --- a/src/tools/miri/test-cargo-miri/run-test.py +++ b/src/tools/miri/test-cargo-miri/run-test.py @@ -170,9 +170,10 @@ def test_cargo_miri_test(): "test.empty.ref", env={'MIRIFLAGS': "-Zmiri-disable-isolation"}, ) - test("`cargo miri test` (proc-macro crate)", - cargo_miri("test") + ["-p", "proc_macro_crate"], - "test.proc-macro.stdout.ref", "test.proc-macro.stderr.ref", + test("`cargo miri test` (entire workspace, no isolation)", + cargo_miri("test") + ["--workspace"], + "test.workspace.stdout.ref", "test.workspace.stderr.ref", + env={'MIRIFLAGS': "-Zmiri-disable-isolation"}, ) test("`cargo miri test` (custom target dir)", cargo_miri("test") + ["--target-dir=custom-test"], diff --git a/src/tools/miri/test-cargo-miri/test.proc-macro.stdout.ref b/src/tools/miri/test-cargo-miri/test.proc-macro.stdout.ref deleted file mode 100644 index 7326c0a25a069..0000000000000 --- a/src/tools/miri/test-cargo-miri/test.proc-macro.stdout.ref +++ /dev/null @@ -1,5 +0,0 @@ - -running 0 tests - -test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME - diff --git a/src/tools/miri/test-cargo-miri/test.proc-macro.stderr.ref b/src/tools/miri/test-cargo-miri/test.workspace.stderr.ref similarity index 100% rename from src/tools/miri/test-cargo-miri/test.proc-macro.stderr.ref rename to src/tools/miri/test-cargo-miri/test.workspace.stderr.ref diff --git a/src/tools/miri/test-cargo-miri/test.workspace.stdout.ref b/src/tools/miri/test-cargo-miri/test.workspace.stdout.ref new file mode 100644 index 0000000000000..a4e224a329797 --- /dev/null +++ b/src/tools/miri/test-cargo-miri/test.workspace.stdout.ref @@ -0,0 +1,103 @@ + +running 2 tests +.. +test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + +imported main + +running 6 tests +...i.. +test result: ok. 5 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in $TIME + + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + +subcrate testing + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + + +running 5 tests +..... +test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + +all doctests ran in $TIME; merged doctests compilation took $TIME + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + + +running 1 test +. +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/src/tools/miri/tests/deps/Cargo.lock b/src/tools/miri/tests/deps/Cargo.lock index cbd2aa22b5236..4691588eefb40 100644 --- a/src/tools/miri/tests/deps/Cargo.lock +++ b/src/tools/miri/tests/deps/Cargo.lock @@ -290,7 +290,6 @@ dependencies = [ name = "miri-test-deps" version = "0.1.0" dependencies = [ - "cfg-if", "futures", "getrandom 0.1.16", "getrandom 0.2.17", diff --git a/src/tools/miri/tests/deps/Cargo.toml b/src/tools/miri/tests/deps/Cargo.toml index a4d06b628081c..bbbcc316f31c2 100644 --- a/src/tools/miri/tests/deps/Cargo.toml +++ b/src/tools/miri/tests/deps/Cargo.toml @@ -10,7 +10,6 @@ edition = "2021" # all dependencies (and their transitive ones) listed here can be used in `tests/*-dep`. libc = "0.2" num_cpus = "1.10.1" -cfg-if = "1" getrandom_01 = { package = "getrandom", version = "0.1" } getrandom_02 = { package = "getrandom", version = "0.2", features = ["js"] } diff --git a/src/tools/miri/tests/fail-dep/libc/eventfd_block_read_twice.rs b/src/tools/miri/tests/fail-dep/libc/eventfd_block_read_twice.rs index 98cc80b6b4ea2..d4d14d449aa68 100644 --- a/src/tools/miri/tests/fail-dep/libc/eventfd_block_read_twice.rs +++ b/src/tools/miri/tests/fail-dep/libc/eventfd_block_read_twice.rs @@ -4,6 +4,10 @@ use std::thread; +#[path = "../../utils/libc.rs"] +mod libc_utils; +use libc_utils::*; + // Test the behaviour of a thread being blocked on an eventfd read, get unblocked, and then // get blocked again. @@ -18,7 +22,7 @@ fn main() { // eventfd write will block when EFD_NONBLOCK flag is clear // and the addition caused counter to exceed u64::MAX - 1. let flags = libc::EFD_CLOEXEC; - let fd = unsafe { libc::eventfd(0, flags) }; + let fd = errno_result(unsafe { libc::eventfd(0, flags) }).unwrap(); let thread1 = thread::spawn(move || { let mut buf: [u8; 8] = [0; 8]; diff --git a/src/tools/miri/tests/fail-dep/libc/eventfd_block_write_twice.rs b/src/tools/miri/tests/fail-dep/libc/eventfd_block_write_twice.rs index 1a1d76eda2003..39d00a7522761 100644 --- a/src/tools/miri/tests/fail-dep/libc/eventfd_block_write_twice.rs +++ b/src/tools/miri/tests/fail-dep/libc/eventfd_block_write_twice.rs @@ -4,6 +4,10 @@ use std::thread; +#[path = "../../utils/libc.rs"] +mod libc_utils; +use libc_utils::*; + // Test the behaviour of a thread being blocked on an eventfd `write`, get unblocked, and then // get blocked again. @@ -17,7 +21,7 @@ fn main() { // eventfd write will block when EFD_NONBLOCK flag is clear // and the addition caused counter to exceed u64::MAX - 1. let flags = libc::EFD_CLOEXEC; - let fd = unsafe { libc::eventfd(0, flags) }; + let fd = errno_result(unsafe { libc::eventfd(0, flags) }).unwrap(); // Write u64 - 1, so the all subsequent write will block. let sized_8_data: [u8; 8] = (u64::MAX - 1).to_ne_bytes(); let res: i64 = unsafe { diff --git a/src/tools/miri/tests/fail-dep/libc/libc-epoll-data-race.rs b/src/tools/miri/tests/fail-dep/libc/libc-epoll-data-race.rs index eecf6abb9379f..6a6d363eea6bb 100644 --- a/src/tools/miri/tests/fail-dep/libc/libc-epoll-data-race.rs +++ b/src/tools/miri/tests/fail-dep/libc/libc-epoll-data-race.rs @@ -16,8 +16,7 @@ use libc_utils::*; fn main() { // Create an epoll instance. - let epfd = unsafe { libc::epoll_create1(0) }; - assert_ne!(epfd, -1); + let epfd = errno_result(unsafe { libc::epoll_create1(0) }).unwrap(); // Create two socketpair instances. let mut fds_a = [-1, -1]; diff --git a/src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.rs b/src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.rs index 3eb79121a2f8d..22fe014d5831c 100644 --- a/src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.rs +++ b/src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.rs @@ -7,6 +7,7 @@ use std::thread; #[path = "../../utils/libc.rs"] mod libc_utils; use libc_utils::epoll::*; +use libc_utils::*; // Test if only one thread is unblocked if multiple threads blocked on same epfd. // Expected execution: @@ -16,14 +17,13 @@ use libc_utils::epoll::*; // 4. Thread 1 deadlocks. fn main() { // Create an epoll instance. - let epfd = unsafe { libc::epoll_create1(0) }; - assert_ne!(epfd, -1); + let epfd = errno_result(unsafe { libc::epoll_create1(0) }).unwrap(); // Create an eventfd instance. let flags = libc::EFD_NONBLOCK | libc::EFD_CLOEXEC; - let fd1 = unsafe { libc::eventfd(0, flags) }; + let fd1 = errno_result(unsafe { libc::eventfd(0, flags) }).unwrap(); // Make a duplicate so that we have two file descriptors for the same file description. - let fd2 = unsafe { libc::dup(fd1) }; + let fd2 = errno_result(unsafe { libc::dup(fd1) }).unwrap(); // Register both with epoll. epoll_ctl_add(epfd, fd1, EPOLLIN | EPOLLOUT | EPOLLET).unwrap(); diff --git a/src/tools/miri/tests/fail-dep/libc/libc_epoll_unsupported_fd.rs b/src/tools/miri/tests/fail-dep/libc/libc_epoll_unsupported_fd.rs index 59cf0fc2ba026..d611feb30a703 100644 --- a/src/tools/miri/tests/fail-dep/libc/libc_epoll_unsupported_fd.rs +++ b/src/tools/miri/tests/fail-dep/libc/libc_epoll_unsupported_fd.rs @@ -1,13 +1,15 @@ //@only-target: linux android illumos +#[path = "../../utils/libc.rs"] +mod libc_utils; +use libc_utils::*; + // This is a test for registering unsupported fd with epoll. // Register epoll fd with epoll is allowed in real system, but we do not support this. fn main() { // Create two epoll instance. - let epfd0 = unsafe { libc::epoll_create1(0) }; - assert_ne!(epfd0, -1); - let epfd1 = unsafe { libc::epoll_create1(0) }; - assert_ne!(epfd1, -1); + let epfd0 = errno_result(unsafe { libc::epoll_create1(0) }).unwrap(); + let epfd1 = errno_result(unsafe { libc::epoll_create1(0) }).unwrap(); // Register epoll with epoll. let mut ev = diff --git a/src/tools/miri/tests/fail-dep/libc/madvise_out_of_bounds.rs b/src/tools/miri/tests/fail-dep/libc/madvise_out_of_bounds.rs new file mode 100644 index 0000000000000..29d804fd0d99a --- /dev/null +++ b/src/tools/miri/tests/fail-dep/libc/madvise_out_of_bounds.rs @@ -0,0 +1,20 @@ +//@compile-flags: -Zmiri-disable-isolation +//@ignore-target: windows # No mmap on Windows +//@normalize-stderr-test: "only .*? bytes" -> "only SIZE bytes" + +fn main() { + unsafe { + let page_size = page_size::get(); + let ptr = libc::mmap( + std::ptr::null_mut(), + page_size, + libc::PROT_READ | libc::PROT_WRITE, + libc::MAP_PRIVATE | libc::MAP_ANONYMOUS, + -1, + 0, + ); + assert!(!ptr.is_null()); + + libc::madvise(ptr, page_size + 1, libc::MADV_NORMAL); //~ ERROR: `madvise` called on out-of-bounds memory + } +} diff --git a/src/tools/miri/tests/fail-dep/libc/madvise_out_of_bounds.stderr b/src/tools/miri/tests/fail-dep/libc/madvise_out_of_bounds.stderr new file mode 100644 index 0000000000000..dbf38c7e4dd0f --- /dev/null +++ b/src/tools/miri/tests/fail-dep/libc/madvise_out_of_bounds.stderr @@ -0,0 +1,13 @@ +error: Undefined Behavior: `madvise` called on out-of-bounds memory + --> tests/fail-dep/libc/madvise_out_of_bounds.rs:LL:CC + | +LL | libc::madvise(ptr, page_size + 1, libc::MADV_NORMAL); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail-dep/libc/mprotect_out_of_bounds.rs b/src/tools/miri/tests/fail-dep/libc/mprotect_out_of_bounds.rs new file mode 100644 index 0000000000000..1e6868b714a81 --- /dev/null +++ b/src/tools/miri/tests/fail-dep/libc/mprotect_out_of_bounds.rs @@ -0,0 +1,20 @@ +//@compile-flags: -Zmiri-disable-isolation +//@ignore-target: windows # No mmap on Windows +//@normalize-stderr-test: "only .*? bytes" -> "only SIZE bytes" + +fn main() { + unsafe { + let page_size = page_size::get(); + let ptr = libc::mmap( + std::ptr::null_mut(), + page_size, + libc::PROT_READ | libc::PROT_WRITE, + libc::MAP_PRIVATE | libc::MAP_ANONYMOUS, + -1, + 0, + ); + assert!(!ptr.is_null()); + + libc::mprotect(ptr, page_size + 1, libc::PROT_READ | libc::PROT_WRITE); //~ ERROR: `mprotect` called on out-of-bounds memory + } +} diff --git a/src/tools/miri/tests/fail-dep/libc/mprotect_out_of_bounds.stderr b/src/tools/miri/tests/fail-dep/libc/mprotect_out_of_bounds.stderr new file mode 100644 index 0000000000000..ac48880150486 --- /dev/null +++ b/src/tools/miri/tests/fail-dep/libc/mprotect_out_of_bounds.stderr @@ -0,0 +1,13 @@ +error: Undefined Behavior: `mprotect` called on out-of-bounds memory + --> tests/fail-dep/libc/mprotect_out_of_bounds.rs:LL:CC + | +LL | libc::mprotect(ptr, page_size + 1, libc::PROT_READ | libc::PROT_WRITE); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail-dep/libc/socketpair_block_read_twice.rs b/src/tools/miri/tests/fail-dep/libc/socketpair_block_read_twice.rs index 37aa4590647b4..edf3f405056f4 100644 --- a/src/tools/miri/tests/fail-dep/libc/socketpair_block_read_twice.rs +++ b/src/tools/miri/tests/fail-dep/libc/socketpair_block_read_twice.rs @@ -1,5 +1,4 @@ //@ignore-target: windows # No libc socketpair on Windows -// test_race depends on a deterministic schedule. //@compile-flags: -Zmiri-deterministic-concurrency //@error-in-other-file: deadlock //@require-annotations-for-level: error diff --git a/src/tools/miri/tests/fail-dep/libc/socketpair_block_write_twice.rs b/src/tools/miri/tests/fail-dep/libc/socketpair_block_write_twice.rs index 2f8b5be0c0c57..d062aeaf34873 100644 --- a/src/tools/miri/tests/fail-dep/libc/socketpair_block_write_twice.rs +++ b/src/tools/miri/tests/fail-dep/libc/socketpair_block_write_twice.rs @@ -1,5 +1,4 @@ //@ignore-target: windows # No libc socketpair on Windows -// test_race depends on a deterministic schedule. //@compile-flags: -Zmiri-deterministic-concurrency //@error-in-other-file: deadlock //@require-annotations-for-level: error diff --git a/src/tools/miri/tests/fail/stacked_borrows/deallocate_against_protector1.rs b/src/tools/miri/tests/fail/both_borrows/deallocate_against_protector1.rs similarity index 50% rename from src/tools/miri/tests/fail/stacked_borrows/deallocate_against_protector1.rs rename to src/tools/miri/tests/fail/both_borrows/deallocate_against_protector1.rs index a34df7c7fe3ae..211d74caadd8a 100644 --- a/src/tools/miri/tests/fail/stacked_borrows/deallocate_against_protector1.rs +++ b/src/tools/miri/tests/fail/both_borrows/deallocate_against_protector1.rs @@ -1,4 +1,8 @@ -//@error-in-other-file: /deallocating while item \[Unique for .*\] is strongly protected/ +//@revisions: stack tree +//@[tree]compile-flags: -Zmiri-tree-borrows + +//@[stack]error-in-other-file: /deallocating while item \[Unique for .*\] is strongly protected/ +//@[tree]error-in-other-file: /deallocation through .* is forbidden/ fn inner(x: &mut i32, f: fn(&mut i32)) { // `f` may mutate, but it may not deallocate! diff --git a/src/tools/miri/tests/fail/stacked_borrows/deallocate_against_protector1.stderr b/src/tools/miri/tests/fail/both_borrows/deallocate_against_protector1.stack.stderr similarity index 76% rename from src/tools/miri/tests/fail/stacked_borrows/deallocate_against_protector1.stderr rename to src/tools/miri/tests/fail/both_borrows/deallocate_against_protector1.stack.stderr index 9f0df14ee4ddd..b8a763a5fc3f4 100644 --- a/src/tools/miri/tests/fail/stacked_borrows/deallocate_against_protector1.stderr +++ b/src/tools/miri/tests/fail/both_borrows/deallocate_against_protector1.stack.stderr @@ -14,13 +14,13 @@ LL | self.1.deallocate(From::from(ptr.cast()), layout); 2: std::mem::drop::> at RUSTLIB/core/src/mem/mod.rs:LL:CC 3: main::{closure#0} - at tests/fail/stacked_borrows/deallocate_against_protector1.rs:LL:CC - 4: <{closure@tests/fail/stacked_borrows/deallocate_against_protector1.rs:LL:CC} as std::ops::FnOnce<(&mut i32,)>>::call_once - shim + at tests/fail/both_borrows/deallocate_against_protector1.rs:LL:CC + 4: <{closure@tests/fail/both_borrows/deallocate_against_protector1.rs:LL:CC} as std::ops::FnOnce<(&mut i32,)>>::call_once - shim at RUSTLIB/core/src/ops/function.rs:LL:CC 5: inner - at tests/fail/stacked_borrows/deallocate_against_protector1.rs:LL:CC + at tests/fail/both_borrows/deallocate_against_protector1.rs:LL:CC 6: main - at tests/fail/stacked_borrows/deallocate_against_protector1.rs:LL:CC + at tests/fail/both_borrows/deallocate_against_protector1.rs:LL:CC note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/both_borrows/deallocate_against_protector1.tree.stderr b/src/tools/miri/tests/fail/both_borrows/deallocate_against_protector1.tree.stderr new file mode 100644 index 0000000000000..3e97a15333ac6 --- /dev/null +++ b/src/tools/miri/tests/fail/both_borrows/deallocate_against_protector1.tree.stderr @@ -0,0 +1,40 @@ +error: Undefined Behavior: deallocation through at ALLOC[0x0] is forbidden + --> RUSTLIB/alloc/src/boxed.rs:LL:CC + | +LL | self.1.deallocate(From::from(ptr.cast()), layout); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here + | + = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental + = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information + = help: the allocation of the accessed tag also contains the strongly protected tag + = help: the strongly protected tag disallows deallocations +help: the accessed tag was created here + --> tests/fail/both_borrows/deallocate_against_protector1.rs:LL:CC + | +LL | drop(unsafe { Box::from_raw(raw) }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: the strongly protected tag was created here, in the initial state Reserved + --> tests/fail/both_borrows/deallocate_against_protector1.rs:LL:CC + | +LL | inner(Box::leak(Box::new(0)), |x| { + | ^ + = note: stack backtrace: + 0: as std::ops::Drop>::drop + at RUSTLIB/alloc/src/boxed.rs:LL:CC + 1: std::ptr::drop_glue::> - shim(Some(std::boxed::Box)) + at RUSTLIB/core/src/ptr/mod.rs:LL:CC + 2: std::mem::drop::> + at RUSTLIB/core/src/mem/mod.rs:LL:CC + 3: main::{closure#0} + at tests/fail/both_borrows/deallocate_against_protector1.rs:LL:CC + 4: <{closure@tests/fail/both_borrows/deallocate_against_protector1.rs:LL:CC} as std::ops::FnOnce<(&mut i32,)>>::call_once - shim + at RUSTLIB/core/src/ops/function.rs:LL:CC + 5: inner + at tests/fail/both_borrows/deallocate_against_protector1.rs:LL:CC + 6: main + at tests/fail/both_borrows/deallocate_against_protector1.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/both_borrows/deallocate_against_protector2.rs b/src/tools/miri/tests/fail/both_borrows/deallocate_against_protector2.rs new file mode 100644 index 0000000000000..4cac0cdd03f8b --- /dev/null +++ b/src/tools/miri/tests/fail/both_borrows/deallocate_against_protector2.rs @@ -0,0 +1,23 @@ +//@revisions: stack tree +//@[tree]compile-flags: -Zmiri-tree-borrows + +// Ensure that even a ZST prevents the reference from being used for deallocation. +// The `nofree` attributes we add in LLVM IR rely on this. + +use std::alloc::Layout; + +fn inner(x: &mut (), f: fn(&mut ())) { + // `f` may mutate, but it may not deallocate! + f(x) +} + +fn main() { + let ptr = Box::leak(Box::new(0i32)) as *mut i32; + inner(unsafe { &mut *(ptr as *mut ()) }, |x| unsafe { + let raw = x as *mut _ as *mut i32; + // Avoid ever creating a `Box`, we don't want any implicit accesses. + std::alloc::dealloc(raw.cast(), Layout::new::()); + //~[tree]^ERROR: /deallocation through .* is forbidden/ + //~[stack]|ERROR: tag does not exist in the borrow stack for this location + }); +} diff --git a/src/tools/miri/tests/fail/both_borrows/deallocate_against_protector2.stack.stderr b/src/tools/miri/tests/fail/both_borrows/deallocate_against_protector2.stack.stderr new file mode 100644 index 0000000000000..20993ef5072b1 --- /dev/null +++ b/src/tools/miri/tests/fail/both_borrows/deallocate_against_protector2.stack.stderr @@ -0,0 +1,27 @@ +error: Undefined Behavior: attempting deallocation using at ALLOC, but that tag does not exist in the borrow stack for this location + --> tests/fail/both_borrows/deallocate_against_protector2.rs:LL:CC + | +LL | std::alloc::dealloc(raw.cast(), Layout::new::()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here + | + = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental + = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information +help: would have been created here, but this is a zero-size retag ([0x0..0x0]) so the tag in question does not exist anywhere + --> tests/fail/both_borrows/deallocate_against_protector2.rs:LL:CC + | +LL | let raw = x as *mut _ as *mut i32; + | ^ + = note: stack backtrace: + 0: main::{closure#0} + at tests/fail/both_borrows/deallocate_against_protector2.rs:LL:CC + 1: <{closure@tests/fail/both_borrows/deallocate_against_protector2.rs:LL:CC} as std::ops::FnOnce<(&mut (),)>>::call_once - shim + at RUSTLIB/core/src/ops/function.rs:LL:CC + 2: inner + at tests/fail/both_borrows/deallocate_against_protector2.rs:LL:CC + 3: main + at tests/fail/both_borrows/deallocate_against_protector2.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/both_borrows/deallocate_against_protector2.tree.stderr b/src/tools/miri/tests/fail/both_borrows/deallocate_against_protector2.tree.stderr new file mode 100644 index 0000000000000..7fae0b6a07773 --- /dev/null +++ b/src/tools/miri/tests/fail/both_borrows/deallocate_against_protector2.tree.stderr @@ -0,0 +1,29 @@ +error: Undefined Behavior: deallocation through at ALLOC[0x0] is forbidden + --> tests/fail/both_borrows/deallocate_against_protector2.rs:LL:CC + | +LL | std::alloc::dealloc(raw.cast(), Layout::new::()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here + | + = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental + = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information + = help: the allocation of the accessed tag also contains the strongly protected tag + = help: the strongly protected tag disallows deallocations +help: the strongly protected tag was created here, in the initial state Reserved + --> tests/fail/both_borrows/deallocate_against_protector2.rs:LL:CC + | +LL | inner(unsafe { &mut *(ptr as *mut ()) }, |x| unsafe { + | ^ + = note: stack backtrace: + 0: main::{closure#0} + at tests/fail/both_borrows/deallocate_against_protector2.rs:LL:CC + 1: <{closure@tests/fail/both_borrows/deallocate_against_protector2.rs:LL:CC} as std::ops::FnOnce<(&mut (),)>>::call_once - shim + at RUSTLIB/core/src/ops/function.rs:LL:CC + 2: inner + at tests/fail/both_borrows/deallocate_against_protector2.rs:LL:CC + 3: main + at tests/fail/both_borrows/deallocate_against_protector2.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/pass-dep/libc/libc-epoll-no-blocking.rs b/src/tools/miri/tests/pass-dep/libc/libc-epoll-no-blocking.rs index 75eb06bc12cfb..90f70064932c1 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-epoll-no-blocking.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-epoll-no-blocking.rs @@ -146,7 +146,7 @@ fn test_epoll_ctl_del() { events: (EPOLLIN | EPOLLOUT | EPOLLET_OR_ZERO) as u32, u64: u64::try_from(fds[1]).unwrap(), }; - let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fds[1], &mut ev) }; + let res = unsafe { libc::epoll_ctl(epfd, EPOLL_CTL_ADD, fds[1], &mut ev) }; assert_eq!(res, 0); // Test EPOLL_CTL_DEL. @@ -158,10 +158,8 @@ fn test_epoll_ctl_del() { // This test is for one fd registered under two different epoll instance. fn test_two_epoll_instance() { // Create two epoll instance. - let epfd1 = unsafe { libc::epoll_create1(0) }; - assert_ne!(epfd1, -1); - let epfd2 = unsafe { libc::epoll_create1(0) }; - assert_ne!(epfd2, -1); + let epfd1 = errno_result(unsafe { libc::epoll_create1(0) }).unwrap(); + let epfd2 = errno_result(unsafe { libc::epoll_create1(0) }).unwrap(); // Create a socketpair instance. let mut fds = [-1, -1]; @@ -570,8 +568,7 @@ fn test_epoll_ctl_epfd_equal_fd() { // epfd that shouldn't receive a notification in edge-triggered mode. fn test_epoll_ctl_notification() { // Create an epoll instance. - let epfd0 = unsafe { libc::epoll_create1(0) }; - assert_ne!(epfd0, -1); + let epfd0 = errno_result(unsafe { libc::epoll_create1(0) }).unwrap(); // Create a socketpair instance. let mut fds = [-1, -1]; @@ -584,8 +581,7 @@ fn test_epoll_ctl_notification() { check_epoll_wait_noblock(epfd0, &[Ev { events: EPOLLOUT, data: fds[0] }]); // Create another epoll instance. - let epfd1 = unsafe { libc::epoll_create1(0) }; - assert_ne!(epfd1, -1); + let epfd1 = errno_result(unsafe { libc::epoll_create1(0) }).unwrap(); // Register the same file description for epfd1. epoll_ctl_add(epfd1, fds[0], EPOLLIN | EPOLLOUT | EPOLLET_OR_ZERO).unwrap(); @@ -692,8 +688,7 @@ fn test_issue_3858() { /// Ensure that if a socket becomes un-writable, we don't see it any more. fn test_issue_4374() { // Create an epoll instance. - let epfd0 = unsafe { libc::epoll_create1(0) }; - assert_ne!(epfd0, -1); + let epfd0 = errno_result(unsafe { libc::epoll_create1(0) }).unwrap(); // Create a socketpair instance, make it non-blocking. let mut fds = [-1, -1]; @@ -721,8 +716,7 @@ fn test_issue_4374() { /// Same as above, but for becoming un-readable. fn test_issue_4374_reads() { // Create an epoll instance. - let epfd0 = unsafe { libc::epoll_create1(0) }; - assert_ne!(epfd0, -1); + let epfd0 = errno_result(unsafe { libc::epoll_create1(0) }).unwrap(); // Create a socketpair instance, make it non-blocking. let mut fds = [-1, -1]; diff --git a/src/tools/miri/tests/pass-dep/libc/libc-eventfd.rs b/src/tools/miri/tests/pass-dep/libc/libc-eventfd.rs index e86c70b590b36..e753983344bd4 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-eventfd.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-eventfd.rs @@ -1,6 +1,7 @@ //@only-target: linux android illumos // test_race, test_blocking_read and test_blocking_write depend on a deterministic schedule. //@compile-flags: -Zmiri-deterministic-concurrency +//@run-native // FIXME(static_mut_refs): Do not allow `static_mut_refs` lint #![allow(static_mut_refs)] @@ -9,6 +10,7 @@ use std::{io, thread}; #[path = "../../utils/libc.rs"] mod libc_utils; +use libc_utils::*; fn main() { test_read_write(); @@ -35,8 +37,8 @@ fn write_bytes(fd: i32, data: [u8; N]) -> io::Result { } fn test_read_write() { - let flags = libc::EFD_NONBLOCK | libc::EFD_CLOEXEC; - let fd = unsafe { libc::eventfd(0, flags) }; + let fd = + errno_result(unsafe { libc::eventfd(0, libc::EFD_NONBLOCK | libc::EFD_CLOEXEC) }).unwrap(); let sized_8_data: [u8; 8] = 1_u64.to_ne_bytes(); // Write 1 to the counter. let res = write_bytes(fd, sized_8_data).unwrap(); @@ -97,9 +99,15 @@ fn test_read_write() { fn test_race() { static mut VAL: u8 = 0; - let flags = libc::EFD_NONBLOCK | libc::EFD_CLOEXEC; - let fd = unsafe { libc::eventfd(0, flags) }; + + let fd = + errno_result(unsafe { libc::eventfd(0, libc::EFD_NONBLOCK | libc::EFD_CLOEXEC) }).unwrap(); let thread1 = thread::spawn(move || { + if !cfg!(miri) { + // Make sure the write goes first. + thread::sleep(std::time::Duration::from_millis(10)); + } + let mut buf: [u8; 8] = [0; 8]; let res = read_bytes(fd, &mut buf).unwrap(); // read returns number of bytes has been read, which is always 8. @@ -130,8 +138,7 @@ fn test_syscall() { // This test will block on eventfd read then get unblocked by `write`. fn test_blocking_read() { // eventfd read will block when EFD_NONBLOCK flag is clear and counter = 0. - let flags = libc::EFD_CLOEXEC; - let fd = unsafe { libc::eventfd(0, flags) }; + let fd = errno_result(unsafe { libc::eventfd(0, libc::EFD_CLOEXEC) }).unwrap(); let thread1 = thread::spawn(move || { let mut buf: [u8; 8] = [0; 8]; // This will block. @@ -154,8 +161,7 @@ fn test_blocking_read() { fn test_blocking_write() { // eventfd write will block when EFD_NONBLOCK flag is clear // and the addition caused counter to exceed u64::MAX - 1. - let flags = libc::EFD_CLOEXEC; - let fd = unsafe { libc::eventfd(0, flags) }; + let fd = errno_result(unsafe { libc::eventfd(0, libc::EFD_CLOEXEC) }).unwrap(); // Write u64 - 1, so the all subsequent write will block. let sized_8_data: [u8; 8] = (u64::MAX - 1).to_ne_bytes(); let res: i64 = unsafe { @@ -192,8 +198,7 @@ fn test_blocking_write() { fn test_two_threads_blocked_on_eventfd() { // eventfd write will block when EFD_NONBLOCK flag is clear // and the addition caused counter to exceed u64::MAX - 1. - let flags = libc::EFD_CLOEXEC; - let fd = unsafe { libc::eventfd(0, flags) }; + let fd = errno_result(unsafe { libc::eventfd(0, libc::EFD_CLOEXEC) }).unwrap(); // Write u64 - 1, so the all subsequent write will block. let sized_8_data: [u8; 8] = (u64::MAX - 1).to_ne_bytes(); let res: i64 = unsafe { diff --git a/src/tools/miri/tests/pass-dep/libc/libc-fs.rs b/src/tools/miri/tests/pass-dep/libc/libc-fs.rs index a131112ee258d..d9d00d7ba239b 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-fs.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-fs.rs @@ -272,7 +272,7 @@ fn test_dup() { let name = CString::new(path.into_os_string().into_encoded_bytes()).unwrap(); unsafe { - let fd = libc::open(name.as_ptr(), libc::O_RDONLY); + let fd = errno_result(libc::open(name.as_ptr(), libc::O_RDONLY)).unwrap(); let new_fd = libc::dup(fd); let new_fd2 = libc::dup2(fd, 8); @@ -856,8 +856,8 @@ fn test_readdir() { assert!(!dirp.is_null()); let mut entries = Vec::new(); loop { - cfg_if::cfg_if! { - if #[cfg(target_os = "macos")] { + cfg_select! { + target_os = "macos" => { // On macos we only support readdir_r as that's what std uses there. use std::mem::MaybeUninit; use libc::dirent; @@ -866,7 +866,8 @@ fn test_readdir() { let ret = libc::readdir_r(dirp, entry.as_mut_ptr(), &mut result); assert_eq!(ret, 0); let entry_ptr = result; - } else { + } + _ => { let entry_ptr = libc::readdir(dirp); } } diff --git a/src/tools/miri/tests/pass-dep/libc/libc-pipe.rs b/src/tools/miri/tests/pass-dep/libc/libc-pipe.rs index 98d7340fa9db3..12b5ae5666cfd 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-pipe.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-pipe.rs @@ -1,6 +1,7 @@ //@ignore-target: windows # No libc pipe on Windows // test_race depends on a deterministic schedule. //@compile-flags: -Zmiri-deterministic-concurrency +//@run-native use std::thread; #[path = "../../utils/libc.rs"] @@ -39,7 +40,7 @@ fn test_pipe() { let data = b"123"; write_all(fds[1], data).unwrap(); let mut buf4: [u8; 5] = [0; 5]; - let (part1, rest) = read_split_slice(fds[0], &mut buf4).unwrap(); + let (part1, rest) = read_partial(fds[0], &mut buf4).unwrap(); assert_eq!(part1[..], data[..part1.len()]); // Write 2 more bytes so we can exactly fill the `rest`. write_all(fds[1], b"34").unwrap(); diff --git a/src/tools/miri/tests/pass-dep/libc/libc-socket-no-blocking-epoll.rs b/src/tools/miri/tests/pass-dep/libc/libc-socket-no-blocking-epoll.rs index 9ed0b9c735979..b7918581bc649 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-socket-no-blocking-epoll.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-socket-no-blocking-epoll.rs @@ -28,6 +28,8 @@ fn main() { test_readiness_after_short_read(); test_readiness_after_short_peek(); test_readiness_after_short_write(); + test_readable_after_read_shutdown_and_short_read(); + test_writable_after_write_shutdown_with_full_buffer(); } /// Test that connecting to a server socket works when the client @@ -359,7 +361,7 @@ fn test_shutdown_read_write() { let (server_sockfd, addr) = net::make_listener_ipv4().unwrap(); let client_sockfd = unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0)).unwrap() }; - let epfd = unsafe { libc::epoll_create1(0) }; + let epfd = errno_result(unsafe { libc::epoll_create1(0) }).unwrap(); // Spawn the server thread. let server_thread = thread::spawn(move || net::accept_ipv4(server_sockfd).unwrap()); @@ -387,7 +389,7 @@ fn test_shutdown_read() { let (server_sockfd, addr) = net::make_listener_ipv4().unwrap(); let client_sockfd = unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0)).unwrap() }; - let epfd = unsafe { libc::epoll_create1(0) }; + let epfd = errno_result(unsafe { libc::epoll_create1(0) }).unwrap(); // Spawn the server thread. let server_thread = thread::spawn(move || net::accept_ipv4(server_sockfd).unwrap()); @@ -411,7 +413,7 @@ fn test_shutdown_write() { let (server_sockfd, addr) = net::make_listener_ipv4().unwrap(); let client_sockfd = unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0)).unwrap() }; - let epfd = unsafe { libc::epoll_create1(0) }; + let epfd = errno_result(unsafe { libc::epoll_create1(0) }).unwrap(); // Spawn the server thread. let server_thread = thread::spawn(move || { @@ -562,24 +564,14 @@ fn test_readiness_after_short_write() { unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0)).unwrap() }; let epfd = errno_result(unsafe { libc::epoll_create1(0) }).unwrap(); - // Spawn the server thread. - let server_thread = thread::spawn(move || { - let (peerfd, _) = net::accept_ipv4(server_sockfd).unwrap(); - // Return the peer socket file descriptor such that we can use - // it after joining the server thread. - peerfd - }); - net::connect_ipv4(client_sockfd, addr).unwrap(); + let (peerfd, _) = net::accept_ipv4(server_sockfd).unwrap(); unsafe { // Change client socket to be non-blocking. errno_check(libc::fcntl(client_sockfd, libc::F_SETFL, libc::O_NONBLOCK)); } - // The peer socket is a blocking socket. - let peerfd = server_thread.join().unwrap(); - // Add client socket with writable interest to epoll. epoll_ctl_add(epfd, client_sockfd, EPOLLET | EPOLLOUT).unwrap(); @@ -636,3 +628,153 @@ fn test_readiness_after_short_write() { // We should again be able to write into the socket. libc_utils::write_all(client_sockfd, &buffer).unwrap(); } + +/// Test that Miri correctly keeps the readable readiness when the read end of the client +/// socket has been closed -- even after a short read. +fn test_readable_after_read_shutdown_and_short_read() { + let (server_sockfd, addr) = net::make_listener_ipv4().unwrap(); + let client_sockfd = + unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0)).unwrap() }; + let epfd = errno_result(unsafe { libc::epoll_create1(0) }).unwrap(); + + // Spawn the server thread. + let server_thread = thread::spawn(move || { + let (peerfd, _) = net::accept_ipv4(server_sockfd).unwrap(); + + // Write `TEST_BYTES` into the stream. + libc_utils::write_all(peerfd, TEST_BYTES).unwrap(); + + // Close the write end, so that the reader will get an EOF. + // (We could alternatively test this by closing the read end of the client socket, + // but Windows has some special behavior when closing a read end while there's still + // data coming in, so we avoid that.) + unsafe { errno_check(libc::shutdown(peerfd, libc::SHUT_WR)) }; + }); + + net::connect_ipv4(client_sockfd, addr).unwrap(); + + // Change client socket to be non-blocking. + unsafe { errno_check(libc::fcntl(client_sockfd, libc::F_SETFL, libc::O_NONBLOCK)) }; + + server_thread.join().unwrap(); + + // Add client socket with "read closed" and "readable" interest to epoll. + epoll_ctl_add(epfd, client_sockfd, EPOLLET | EPOLLIN | EPOLLRDHUP).unwrap(); + + // Ensure that the socket is readable and that its read end is closed. + check_epoll_wait(epfd, &[Ev { events: EPOLLIN | EPOLLRDHUP, data: client_sockfd }], -1); + + let mut buffer = [0u8; 1024]; + + // We want to read in chunks of 16 bytes. To ensure we get a short read, `TEST_BYTES.len()` + // must not be dividable by 16. + assert!(TEST_BYTES.len() % 16 != 0); + + let mut total_bytes_read = 0; + // Read everything from the socket until we get a short read. + // We don't want to provide `TEST_BYTES.len()` as `count` because then we won't trigger + // a short read. + loop { + let bytes_read = unsafe { + errno_result(libc::read( + client_sockfd, + buffer.as_mut_ptr().byte_add(total_bytes_read).cast(), + // Read a chunk of 16 bytes. + 16, + )) + .unwrap() + }; + + total_bytes_read += bytes_read as usize; + if bytes_read < 16 { + // We had a short read; we thus assume the read buffer is empty. + break; + } + } + assert_eq!(total_bytes_read, TEST_BYTES.len()); + + // We had a short read because `buffer` is bigger than `TEST_BYTES`. + // Because the read end of the socket is closed, we should still be able to + // read to detect EOFs. + + // Ensure that the "readable" and "read closed" readiness flags are still set. + assert_eq!( + current_epoll_readiness::<8>(client_sockfd, EPOLLIN | EPOLLET | EPOLLRDHUP), + EPOLLIN | EPOLLRDHUP + ); + + // A read should not block and return 0, indicating EOF. + let mut buffer = [1u8; 16]; + let bytes_read = unsafe { + errno_result(libc::read(client_sockfd, buffer.as_mut_ptr().cast(), buffer.len())).unwrap() + }; + assert_eq!(bytes_read, 0); +} + +/// Test that the writable readiness gets set when the write end of a socket +/// is closed -- even when the socket write buffer is full. +fn test_writable_after_write_shutdown_with_full_buffer() { + let (server_sockfd, addr) = net::make_listener_ipv4().unwrap(); + let client_sockfd = + unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0)).unwrap() }; + let epfd = errno_result(unsafe { libc::epoll_create1(0) }).unwrap(); + + net::connect_ipv4(client_sockfd, addr).unwrap(); + net::accept_ipv4(server_sockfd).unwrap(); + + unsafe { + // Change client socket to be non-blocking. + errno_check(libc::fcntl(client_sockfd, libc::F_SETFL, libc::O_NONBLOCK)); + } + + // Add client socket with level-triggered "writable" and "write closed" interest to epoll. + epoll_ctl_add(epfd, client_sockfd, EPOLLOUT | EPOLLHUP).unwrap(); + + // Wait until the socket becomes writable. + check_epoll_wait(epfd, &[Ev { events: EPOLLOUT, data: client_sockfd }], -1); + + // We now want to fill the write buffer of the socket by repeatedly writing + // `buffer` into it. The last write should then be a short write. + // We assume/hope that the write buffer length is not divisible by 1039. + let buffer = [123u8; 1039]; + + loop { + let result = unsafe { + errno_result(libc::write(client_sockfd, buffer.as_ptr().cast(), buffer.len())) + }; + + match result { + Ok(bytes_written) => { + if (bytes_written as usize) < buffer.len() { + // We had a short write; we thus assume the write buffer is full. + break; + } + } + Err(err) if err.kind() == ErrorKind::WouldBlock => { + // Windows and Apple hosts behave weirdly when attempting to fill up the write buffer. + // Instead of doing a short write to completely fill the buffer, they can return an + // EWOULDBLOCK when the next write wouldn't fit into the buffer. + // When we get such an error, we also assume the write buffer is full. + break; + } + Err(err) => panic!("unexpected error whilst filling up buffer: {err}"), + } + } + + // The write buffer is full; because this is a level-triggered interest, + // a readiness of 0 means that the socket would now block on writes. + check_epoll_wait(epfd, &[], 0); + + // Close the socket write end. + unsafe { + errno_check(libc::shutdown(client_sockfd, libc::SHUT_WR)); + } + + // The socket should no longer block on writes after its write end is closed. + check_epoll_wait(epfd, &[Ev { events: EPOLLOUT, data: client_sockfd }], -1); + + // A write should not block and return an error. + let result = + unsafe { errno_result(libc::write(client_sockfd, buffer.as_ptr().cast(), buffer.len())) }; + assert_eq!(result.unwrap_err().kind(), ErrorKind::BrokenPipe); +} diff --git a/src/tools/miri/tests/pass-dep/libc/libc-socketpair.rs b/src/tools/miri/tests/pass-dep/libc/libc-socketpair.rs index da521600d84a6..f994dc28a9349 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-socketpair.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-socketpair.rs @@ -1,6 +1,7 @@ //@ignore-target: windows # No libc socketpair on Windows // test_race depends on a deterministic schedule. //@compile-flags: -Zmiri-deterministic-concurrency +//@run-native // FIXME(static_mut_refs): Do not allow `static_mut_refs` lint #![allow(static_mut_refs)] @@ -34,7 +35,7 @@ fn test_socketpair() { let data = b"abc"; write_all(fds[0], data).unwrap(); let mut buf2: [u8; 5] = [0; 5]; - let (read, rest) = read_split_slice(fds[1], &mut buf2).unwrap(); + let (read, rest) = read_partial(fds[1], &mut buf2).unwrap(); assert_eq!(read[..], data[..read.len()]); // Write 2 more bytes so we can exactly fill the `rest`. write_all(fds[0], b"12").unwrap(); @@ -52,7 +53,7 @@ fn test_socketpair() { let data = b"abc"; write_all(fds[1], data).unwrap(); let mut buf4: [u8; 5] = [0; 5]; - let (read, rest) = read_split_slice(fds[0], &mut buf4).unwrap(); + let (read, rest) = read_partial(fds[0], &mut buf4).unwrap(); assert_eq!(read[..], data[..read.len()]); // Write 2 more bytes so we can exactly fill the `rest`. write_all(fds[1], b"12").unwrap(); @@ -64,9 +65,9 @@ fn test_socketpair() { errno_check(unsafe { libc::close(fds[0]) }); // Reading the other end should return that data, then EOF. let mut buf: [u8; 5] = [0; 5]; - let (read, _tail) = read_split_slice(fds[1], &mut buf).unwrap(); + let (read, _tail) = read_partial(fds[1], &mut buf).unwrap(); assert_eq!(read, data); - let (read, _tail) = read_split_slice(fds[1], &mut buf).unwrap(); + let (read, _tail) = read_partial(fds[1], &mut buf).unwrap(); assert_eq!(read, &[]); // Writing the other end should emit EPIPE. let err = write_all(fds[1], &mut buf).unwrap_err(); @@ -132,6 +133,11 @@ fn test_blocking_read() { // Test the behaviour of a socketpair getting blocked on write and subsequently unblocked. fn test_blocking_write() { + // The test uses Miri's exact buffer size. + if !cfg!(miri) { + return; + } + let mut fds = [-1, -1]; errno_check(unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) }); let arr1: [u8; 0x34000] = [1; 0x34000]; diff --git a/src/tools/miri/tests/pass-dep/libc/libc-time.rs b/src/tools/miri/tests/pass-dep/libc/libc-time.rs index f315d2ab117f9..3bfcfab099d7b 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-time.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-time.rs @@ -1,5 +1,6 @@ //@ignore-target: windows # no libc time APIs on Windows //@compile-flags: -Zmiri-disable-isolation +//@run-native #[path = "../../utils/libc.rs"] mod libc_utils; @@ -9,6 +10,17 @@ use std::{env, mem, ptr}; use libc_utils::errno_check; +fn set_tz(name: &str) { + extern "C" { + fn tzset(); + } + + env::set_var("TZ", name); + if !cfg!(miri) { + unsafe { tzset() }; // re-read TZ env var (natively, it may be cached) + } +} + fn main() { test_clocks(); test_posix_gettimeofday(); @@ -66,11 +78,13 @@ fn test_posix_gettimeofday() { assert!(tv.tv_sec > 0); assert!(tv.tv_usec >= 0); // Theoretically this could be 0. - // Test that non-null tz returns an error (because we don't support it). - let mut tz = mem::MaybeUninit::::uninit(); - let tz_ptr = tz.as_mut_ptr(); - let is_error = unsafe { libc::gettimeofday(tp.as_mut_ptr(), tz_ptr.cast()) }; - assert_eq!(is_error, -1); + if cfg!(miri) { + // Test that non-null tz returns an error (because we don't support it). + let mut tz = mem::MaybeUninit::::uninit(); + let tz_ptr = tz.as_mut_ptr(); + let is_error = unsafe { libc::gettimeofday(tp.as_mut_ptr(), tz_ptr.cast()) }; + assert_eq!(is_error, -1); + } } /// Helper function to create an empty tm struct. @@ -104,9 +118,8 @@ fn create_empty_tm() -> libc::tm { /// Original GMT test fn test_localtime_r_gmt() { - // Set timezone to GMT. - let key = "TZ"; - env::set_var(key, "GMT"); + set_tz("GMT"); + const TIME_SINCE_EPOCH: libc::time_t = 1712475836; // 2024-04-07 07:43:56 GMT let custom_time_ptr = &TIME_SINCE_EPOCH; let mut tm = create_empty_tm(); @@ -120,7 +133,9 @@ fn test_localtime_r_gmt() { assert_eq!(tm.tm_year, 124); assert_eq!(tm.tm_wday, 0); assert_eq!(tm.tm_yday, 97); - assert_eq!(tm.tm_isdst, -1); + if cfg!(miri) { + assert_eq!(tm.tm_isdst, -1); + } #[cfg(any( target_os = "linux", target_os = "macos", @@ -130,21 +145,21 @@ fn test_localtime_r_gmt() { { assert_eq!(tm.tm_gmtoff, 0); unsafe { - assert_eq!(std::ffi::CStr::from_ptr(tm.tm_zone).to_str().unwrap(), "+00"); + assert_eq!( + std::ffi::CStr::from_ptr(tm.tm_zone).to_str().unwrap(), + if cfg!(miri) { "+00" } else { "GMT" } + ); } } // The returned value is the pointer passed in. assert!(ptr::eq(res, &mut tm)); - - // Remove timezone setting. - env::remove_var(key); } /// PST timezone test (testing different timezone handling). fn test_localtime_r_pst() { - let key = "TZ"; - env::set_var(key, "PST8PDT"); + set_tz("PST8PDT"); + const TIME_SINCE_EPOCH: libc::time_t = 1712475836; // 2024-04-07 07:43:56 GMT let custom_time_ptr = &TIME_SINCE_EPOCH; let mut tm = create_empty_tm(); @@ -159,7 +174,9 @@ fn test_localtime_r_pst() { assert_eq!(tm.tm_year, 124); assert_eq!(tm.tm_wday, 0); assert_eq!(tm.tm_yday, 97); - assert_eq!(tm.tm_isdst, -1); // DST information unavailable + if cfg!(miri) { + assert_eq!(tm.tm_isdst, -1); // DST information unavailable + } #[cfg(any( target_os = "linux", @@ -170,18 +187,20 @@ fn test_localtime_r_pst() { { assert_eq!(tm.tm_gmtoff, -7 * 3600); // -7 hours in seconds unsafe { - assert_eq!(std::ffi::CStr::from_ptr(tm.tm_zone).to_str().unwrap(), "-07"); + assert_eq!( + std::ffi::CStr::from_ptr(tm.tm_zone).to_str().unwrap(), + if cfg!(miri) { "-07" } else { "PDT" } + ); } } assert!(ptr::eq(res, &mut tm)); - env::remove_var(key); } /// Unix epoch test (edge case testing). fn test_localtime_r_epoch() { - let key = "TZ"; - env::set_var(key, "GMT"); + set_tz("GMT"); + const TIME_SINCE_EPOCH: libc::time_t = 0; // 1970-01-01 00:00:00 let custom_time_ptr = &TIME_SINCE_EPOCH; let mut tm = create_empty_tm(); @@ -196,7 +215,9 @@ fn test_localtime_r_epoch() { assert_eq!(tm.tm_year, 70); assert_eq!(tm.tm_wday, 4); // Thursday assert_eq!(tm.tm_yday, 0); - assert_eq!(tm.tm_isdst, -1); + if cfg!(miri) { + assert_eq!(tm.tm_isdst, -1); + } #[cfg(any( target_os = "linux", @@ -207,19 +228,20 @@ fn test_localtime_r_epoch() { { assert_eq!(tm.tm_gmtoff, 0); unsafe { - assert_eq!(std::ffi::CStr::from_ptr(tm.tm_zone).to_str().unwrap(), "+00"); + assert_eq!( + std::ffi::CStr::from_ptr(tm.tm_zone).to_str().unwrap(), + if cfg!(miri) { "+00" } else { "GMT" } + ); } } assert!(ptr::eq(res, &mut tm)); - env::remove_var(key); } /// Future date test (testing large values). #[cfg(target_pointer_width = "64")] fn test_localtime_r_future_64b() { - let key = "TZ"; - env::set_var(key, "GMT"); + set_tz("GMT"); // Using 2050-01-01 00:00:00 for 64-bit systems // value that's safe for 64-bit time_t @@ -237,7 +259,9 @@ fn test_localtime_r_future_64b() { assert_eq!(tm.tm_year, 150); // 2050 - 1900 assert_eq!(tm.tm_wday, 6); // Saturday assert_eq!(tm.tm_yday, 0); - assert_eq!(tm.tm_isdst, -1); + if cfg!(miri) { + assert_eq!(tm.tm_isdst, -1); + } #[cfg(any( target_os = "linux", @@ -248,19 +272,20 @@ fn test_localtime_r_future_64b() { { assert_eq!(tm.tm_gmtoff, 0); unsafe { - assert_eq!(std::ffi::CStr::from_ptr(tm.tm_zone).to_str().unwrap(), "+00"); + assert_eq!( + std::ffi::CStr::from_ptr(tm.tm_zone).to_str().unwrap(), + if cfg!(miri) { "+00" } else { "GMT" } + ); } } assert!(ptr::eq(res, &mut tm)); - env::remove_var(key); } /// Future date test (testing large values for 32b target). #[cfg(target_pointer_width = "32")] fn test_localtime_r_future_32b() { - let key = "TZ"; - env::set_var(key, "GMT"); + set_tz("GMT"); // Using 2030-01-01 00:00:00 for 32-bit systems // Safe value within i32 range @@ -279,7 +304,9 @@ fn test_localtime_r_future_32b() { assert_eq!(tm.tm_year, 130); // 2030 - 1900 assert_eq!(tm.tm_wday, 2); // Tuesday assert_eq!(tm.tm_yday, 0); - assert_eq!(tm.tm_isdst, -1); + if cfg!(miri) { + assert_eq!(tm.tm_isdst, -1); + } #[cfg(any( target_os = "linux", @@ -290,19 +317,20 @@ fn test_localtime_r_future_32b() { { assert_eq!(tm.tm_gmtoff, 0); unsafe { - assert_eq!(std::ffi::CStr::from_ptr(tm.tm_zone).to_str().unwrap(), "+00"); + assert_eq!( + std::ffi::CStr::from_ptr(tm.tm_zone).to_str().unwrap(), + if cfg!(miri) { "+00" } else { "GMT" } + ); } } assert!(ptr::eq(res, &mut tm)); - env::remove_var(key); } /// Tests the behavior of `localtime_r` with multiple calls to ensure deduplication of `tm_zone` pointers. #[cfg(any(target_os = "linux", target_os = "macos", target_os = "freebsd", target_os = "android"))] fn test_localtime_r_multiple_calls_deduplication() { - let key = "TZ"; - env::set_var(key, "PST8PDT"); + set_tz("PST8PDT"); const TIME_SINCE_EPOCH_BASE: libc::time_t = 1712475836; // Base timestamp: 2024-04-07 07:43:56 GMT const NUM_CALLS: usize = 50; @@ -321,9 +349,11 @@ fn test_localtime_r_multiple_calls_deduplication() { let unique_count = unique_pointers.len(); + // Miri non-determinisitcally de-duplicates. Native always deduplicates. + let min = if cfg!(miri) { 2 } else { 1 }; assert!( - unique_count >= 2 && unique_count <= (NUM_CALLS - 1), - "Unexpected number of unique tm_zone pointers: {} (expected between 2 and {})", + unique_count >= min && unique_count <= (NUM_CALLS - 1), + "Unexpected number of unique tm_zone pointers: {} (expected between {min} and {})", unique_count, NUM_CALLS - 1 ); diff --git a/src/tools/miri/tests/pass-dep/libc/mmap.rs b/src/tools/miri/tests/pass-dep/libc/mmap.rs index bfd840d2fb89d..692b59d443ae8 100644 --- a/src/tools/miri/tests/pass-dep/libc/mmap.rs +++ b/src/tools/miri/tests/pass-dep/libc/mmap.rs @@ -94,6 +94,78 @@ fn test_mmap( assert_eq!(Error::last_os_error().raw_os_error().unwrap(), libc::EINVAL); } +fn test_mprotect() { + let page_size = page_size::get(); + let ptr = unsafe { + libc::mmap( + ptr::null_mut(), + 4 * page_size, + libc::PROT_READ | libc::PROT_WRITE, + libc::MAP_PRIVATE | libc::MAP_ANONYMOUS, + -1, + Default::default(), + ) + }; + assert!(!ptr.is_null()); + + // Protect part of it redundantly. + let res = unsafe { + libc::mprotect(ptr.byte_add(2 * page_size), 42, libc::PROT_READ | libc::PROT_WRITE) + }; + assert_eq!(res, 0i32); + + // Protect everything redundantly. + let res = unsafe { libc::mprotect(ptr, 4 * page_size, libc::PROT_READ | libc::PROT_WRITE) }; + assert_eq!(res, 0i32); + + // We report an error when the address is not a multiple of the page size. + let res = + unsafe { libc::mprotect(ptr.byte_add(11), page_size, libc::PROT_READ | libc::PROT_WRITE) }; + assert_eq!(res, -1); + assert_eq!(Error::last_os_error().raw_os_error().unwrap(), libc::EINVAL); + + // We report an error if the length cannot be rounded up to a multiple of the page size. + let res = unsafe { libc::mprotect(ptr, usize::MAX - 1, libc::PROT_READ | libc::PROT_WRITE) }; + assert_eq!(res, -1); + assert_eq!(Error::last_os_error().raw_os_error().unwrap(), libc::ENOMEM); +} + +fn test_madvise() { + let page_size = page_size::get(); + let ptr = unsafe { + libc::mmap( + ptr::null_mut(), + 4 * page_size, + libc::PROT_READ | libc::PROT_WRITE, + libc::MAP_PRIVATE | libc::MAP_ANONYMOUS, + -1, + Default::default(), + ) + }; + assert!(!ptr.is_null()); + + for advice in [libc::MADV_NORMAL, libc::MADV_RANDOM, libc::MADV_SEQUENTIAL, libc::MADV_WILLNEED] + { + // Advise part of it redundantly. + let res = unsafe { libc::madvise(ptr.byte_add(2 * page_size), 42, advice) }; + assert_eq!(res, 0i32); + + // Protect everything redundantly. + let res = unsafe { libc::madvise(ptr, 4 * page_size, advice) }; + assert_eq!(res, 0i32); + } + + // We report an error when the address is not a multiple of the page size. + let res = unsafe { libc::madvise(ptr.byte_add(11), page_size, libc::MADV_NORMAL) }; + assert_eq!(res, -1); + assert_eq!(Error::last_os_error().raw_os_error().unwrap(), libc::EINVAL); + + // We report an error if the length cannot be rounded up to a multiple of the page size. + let res = unsafe { libc::madvise(ptr, usize::MAX - 1, libc::MADV_NORMAL) }; + assert_eq!(res, -1); + assert_eq!(Error::last_os_error().raw_os_error().unwrap(), libc::ENOMEM); +} + #[cfg(target_os = "linux")] fn test_mremap() { let page_size = page_size::get(); @@ -145,6 +217,8 @@ fn main() { test_mmap(libc::mmap); #[cfg(target_os = "linux")] test_mmap(libc::mmap64); + test_mprotect(); + test_madvise(); #[cfg(target_os = "linux")] test_mremap(); } diff --git a/src/tools/miri/tests/pass-dep/libc/pthread-threadname.rs b/src/tools/miri/tests/pass-dep/libc/pthread-threadname.rs index cf7ea4c6abba1..6cba672c6569c 100644 --- a/src/tools/miri/tests/pass-dep/libc/pthread-threadname.rs +++ b/src/tools/miri/tests/pass-dep/libc/pthread-threadname.rs @@ -4,17 +4,21 @@ use std::ffi::{CStr, CString}; use std::thread; const MAX_THREAD_NAME_LEN: usize = { - cfg_if::cfg_if! { - if #[cfg(any(target_os = "linux"))] { + cfg_select! { + target_os = "linux" => { 16 - } else if #[cfg(any(target_os = "illumos", target_os = "solaris"))] { + } + any(target_os = "illumos", target_os = "solaris") => { 32 - } else if #[cfg(target_os = "macos")] { + } + target_os = "macos" => { libc::MAXTHREADNAMESIZE // 64, at the time of writing - } else if #[cfg(target_os = "freebsd")] { + } + target_os = "freebsd" => { usize::MAX // as far as I can tell - } else { - panic!() + } + _ => { + compile_error!("unsupported OS"); } } }; @@ -28,35 +32,38 @@ fn main() { .collect::(); fn set_thread_name(name: &CStr) -> i32 { - cfg_if::cfg_if! { - if #[cfg(any( + cfg_select! { + any( target_os = "linux", target_os = "freebsd", target_os = "illumos", target_os = "solaris" - ))] { + ) => { unsafe { libc::pthread_setname_np(libc::pthread_self(), name.as_ptr().cast()) } - } else if #[cfg(target_os = "macos")] { + } + target_os = "macos" => { unsafe { libc::pthread_setname_np(name.as_ptr().cast()) } - } else { + } + _ => { compile_error!("set_thread_name not supported for this OS") } } } fn get_thread_name(name: &mut [u8]) -> i32 { - cfg_if::cfg_if! { - if #[cfg(any( + cfg_select! { + any( target_os = "linux", target_os = "freebsd", target_os = "illumos", target_os = "solaris", target_os = "macos" - ))] { + ) => { unsafe { libc::pthread_getname_np(libc::pthread_self(), name.as_mut_ptr().cast(), name.len()) } - } else { + } + _ => { compile_error!("get_thread_name not supported for this OS") } } @@ -95,13 +102,14 @@ fn main() { // Test what happens when the buffer is shorter than 16, but still long enough. let res = get_thread_name(&mut buf[..15]); - cfg_if::cfg_if! { - if #[cfg(target_os = "linux")] { + cfg_select! { + target_os = "linux" => { // For glibc used by linux-gnu there should be a failue, // if a shorter than 16 bytes buffer is provided, even if that would be // large enough for the thread name. assert_eq!(res, libc::ERANGE); - } else { + } + _ => { // Everywhere else, this should work. assert_eq!(res, 0); // POSIX seems to promise at least 15 chars excluding a null terminator. @@ -112,15 +120,16 @@ fn main() { // Test what happens when the buffer is too short even for the short name. let res = get_thread_name(&mut buf[..4]); - cfg_if::cfg_if! { - if #[cfg(any(target_os = "freebsd", target_os = "macos"))] { + cfg_select! { + any(target_os = "freebsd", target_os = "macos") => { // On macOS and FreeBSD it's not an error for the buffer to be // too short for the thread name -- they truncate instead. assert_eq!(res, 0); let cstr = CStr::from_bytes_until_nul(&buf).unwrap(); assert_eq!(cstr.to_bytes_with_nul().len(), 4); assert!(short_name.as_bytes().starts_with(cstr.to_bytes())); - } else { + } + _ => { // The rest should give an error. assert_eq!(res, libc::ERANGE); } @@ -128,12 +137,13 @@ fn main() { // Test zero-sized buffer. let res = get_thread_name(&mut []); - cfg_if::cfg_if! { - if #[cfg(any(target_os = "freebsd", target_os = "macos"))] { + cfg_select! { + any(target_os = "freebsd", target_os = "macos") => { // On macOS and FreeBSD it's not an error for the buffer to be // too short for the thread name -- even with size 0. assert_eq!(res, 0); - } else { + } + _ => { // The rest should give an error. assert_eq!(res, libc::ERANGE); } @@ -149,16 +159,18 @@ fn main() { // Set full thread name. let cstr = CString::new(long_name.clone()).unwrap(); let res = set_thread_name(&cstr); - cfg_if::cfg_if! { - if #[cfg(target_os = "freebsd")] { + cfg_select! { + target_os = "freebsd" => { // Names of all size are supported. assert!(cstr.to_bytes_with_nul().len() <= MAX_THREAD_NAME_LEN); assert_eq!(res, 0); - } else if #[cfg(target_os = "macos")] { + } + target_os = "macos" => { // Name is too long. assert!(cstr.to_bytes_with_nul().len() > MAX_THREAD_NAME_LEN); assert_eq!(res, libc::ENAMETOOLONG); - } else { + } + _ => { // Name is too long. assert!(cstr.to_bytes_with_nul().len() > MAX_THREAD_NAME_LEN); assert_eq!(res, libc::ERANGE); @@ -179,14 +191,15 @@ fn main() { // Test what happens when our buffer is just one byte too small. let res = get_thread_name(&mut buf[..truncated_name.len()]); - cfg_if::cfg_if! { - if #[cfg(any(target_os = "freebsd", target_os = "macos"))] { + cfg_select! { + any(target_os = "freebsd", target_os = "macos") => { // On macOS and FreeBSD it's not an error for the buffer to be // too short for the thread name -- they truncate instead. assert_eq!(res, 0); let cstr = CStr::from_bytes_until_nul(&buf).unwrap(); assert_eq!(cstr.to_bytes(), &truncated_name.as_bytes()[..(truncated_name.len() - 1)]); - } else { + } + _ => { // The rest should give an error. assert_eq!(res, libc::ERANGE); } @@ -199,10 +212,11 @@ fn main() { // Now set the name for a non-existing thread and verify error codes. let invalid_thread = 0xdeadbeef; let error = { - cfg_if::cfg_if! { - if #[cfg(target_os = "linux")] { + cfg_select! { + target_os = "linux" => { libc::ENOENT - } else { + } + _ => { libc::ESRCH } } diff --git a/src/tools/miri/tests/pass-dep/shims/gettid.rs b/src/tools/miri/tests/pass-dep/shims/gettid.rs index 0dce7bdc0c52e..9b186699ce275 100644 --- a/src/tools/miri/tests/pass-dep/shims/gettid.rs +++ b/src/tools/miri/tests/pass-dep/shims/gettid.rs @@ -5,29 +5,37 @@ #![feature(linkage)] fn gettid() -> u64 { - cfg_if::cfg_if! { - if #[cfg(any(target_os = "android", target_os = "linux"))] { + cfg_select! { + any(target_os = "android", target_os = "linux") => { gettid_linux_like() - } else if #[cfg(target_os = "nto")] { + } + target_os = "nto" => { unsafe { libc::gettid() as u64 } - } else if #[cfg(target_os = "openbsd")] { + } + target_os = "openbsd" => { unsafe { libc::getthrid() as u64 } - } else if #[cfg(target_os = "freebsd")] { + } + target_os = "freebsd" => { unsafe { libc::pthread_getthreadid_np() as u64 } - } else if #[cfg(target_os = "netbsd")] { + } + target_os = "netbsd" => { unsafe { libc::_lwp_self() as u64 } - } else if #[cfg(any(target_os = "solaris", target_os = "illumos"))] { + } + any(target_os = "solaris", target_os = "illumos") => { // On Solaris and Illumos, the `pthread_t` is the OS TID. unsafe { libc::pthread_self() as u64 } - } else if #[cfg(target_vendor = "apple")] { + } + target_vendor = "apple" => { let mut id = 0u64; let status: libc::c_int = unsafe { libc::pthread_threadid_np(0, &mut id) }; assert_eq!(status, 0); id - } else if #[cfg(windows)] { + } + windows => { use windows_sys::Win32::System::Threading::GetCurrentThreadId; unsafe { GetCurrentThreadId() as u64 } - } else { + } + _ => { compile_error!("platform has no gettid") } } diff --git a/src/tools/miri/tests/pass/issues/issue-154385-no-mangle-generic.rs b/src/tools/miri/tests/pass/issues/issue-154385-no-mangle-generic.rs new file mode 100644 index 0000000000000..0989cdd86a362 --- /dev/null +++ b/src/tools/miri/tests/pass/issues/issue-154385-no-mangle-generic.rs @@ -0,0 +1,9 @@ +fn main() { + foo(1234); +} + +#[allow(no_mangle_generic_items)] +#[unsafe(no_mangle)] +fn foo(value: T) { + println!("{value:?}"); +} diff --git a/src/tools/miri/tests/pass/issues/issue-154385-no-mangle-generic.stdout b/src/tools/miri/tests/pass/issues/issue-154385-no-mangle-generic.stdout new file mode 100644 index 0000000000000..81c545efebe5f --- /dev/null +++ b/src/tools/miri/tests/pass/issues/issue-154385-no-mangle-generic.stdout @@ -0,0 +1 @@ +1234 diff --git a/src/tools/miri/tests/pass/shims/aarch64/intrinsics-aarch64-aes.rs b/src/tools/miri/tests/pass/shims/aarch64/intrinsics-aarch64-aes.rs index 1345924beecf0..c640dde5afaf5 100644 --- a/src/tools/miri/tests/pass/shims/aarch64/intrinsics-aarch64-aes.rs +++ b/src/tools/miri/tests/pass/shims/aarch64/intrinsics-aarch64-aes.rs @@ -1,6 +1,7 @@ // We're testing aarch64 AES target specific features. //@only-target: aarch64 //@compile-flags: -C target-feature=+neon,+aes +//@run-native use std::arch::aarch64::*; use std::arch::is_aarch64_feature_detected; diff --git a/src/tools/miri/tests/pass/shims/aarch64/intrinsics-aarch64-crc32.rs b/src/tools/miri/tests/pass/shims/aarch64/intrinsics-aarch64-crc32.rs index 849f99ee36cce..8d50d152f2ebe 100644 --- a/src/tools/miri/tests/pass/shims/aarch64/intrinsics-aarch64-crc32.rs +++ b/src/tools/miri/tests/pass/shims/aarch64/intrinsics-aarch64-crc32.rs @@ -1,6 +1,7 @@ // We're testing aarch64 CRC32 target specific features //@only-target: aarch64 //@compile-flags: -C target-feature=+crc +//@run-native use std::arch::aarch64::*; use std::arch::is_aarch64_feature_detected; diff --git a/src/tools/miri/tests/pass/shims/aarch64/intrinsics-aarch64-neon.rs b/src/tools/miri/tests/pass/shims/aarch64/intrinsics-aarch64-neon.rs index 884f8eff41bdb..fdd0ff6ca6dfb 100644 --- a/src/tools/miri/tests/pass/shims/aarch64/intrinsics-aarch64-neon.rs +++ b/src/tools/miri/tests/pass/shims/aarch64/intrinsics-aarch64-neon.rs @@ -1,6 +1,7 @@ // We're testing aarch64 target specific features //@only-target: aarch64 //@compile-flags: -C target-feature=+neon +//@run-native use std::arch::aarch64::*; use std::arch::is_aarch64_feature_detected; @@ -14,6 +15,7 @@ fn main() { test_tbl1_v16i8_basic(); test_vpadd(); test_vpaddl(); + test_vqdmulh(); } } @@ -157,3 +159,51 @@ unsafe fn test_vpaddl() { vst1q_u64(r.as_mut_ptr(), vpaddlq_u32(a)); assert_eq!(r, e); } + +#[target_feature(enable = "neon")] +unsafe fn test_vqdmulh() { + let a = vld1_s32([i32::MIN, i32::MAX].as_ptr()); + let r: [i32; 2] = transmute(vqdmulh_n_s32(a, i32::MIN)); + assert_eq!(r, [i32::MAX, -i32::MAX]); + + // This is the actual calculation that happens. + let product = i32::MIN as i128 * i32::MIN as i128 * 2; + assert_eq!(i32::MAX, (product >> 32).clamp(i32::MIN as i128, i32::MAX as i128) as i32); + + let product = i32::MAX as i128 * i32::MIN as i128 * 2; + assert_eq!(-i32::MAX, (product >> 32).clamp(i32::MIN as i128, i32::MAX as i128) as i32); + + let b = vld1_s32([123, i32::MIN].as_ptr()); + let r: [i32; 2] = transmute(vqdmulh_s32(a, b)); + assert_eq!(r, [-123, -i32::MAX]); + + // Wider 32-bit versions. + let a = vld1q_s32([0x4000_0000, -0x4000_0000, i32::MIN, i32::MAX].as_ptr()); + + let b = vld1q_s32([123, 456, 0x4000_0000, 789].as_ptr()); + let r: [i32; 4] = transmute(vqdmulhq_s32(a, b)); + assert_eq!(r, [61, -228, -1073741824, 788]); + + let r: [i32; 4] = transmute(vqdmulhq_n_s32(a, 0x4000_0000)); + assert_eq!(r, [536870912, -536870912, -1073741824, 1073741823]); + + // 16-bit versions. + + let a = vld1_s16([i16::MIN, i16::MAX, 0, 16384].as_ptr()); + let r: [i16; 4] = transmute(vqdmulh_n_s16(a, i16::MIN)); + assert_eq!(r, [i16::MAX, -i16::MAX, 0, -16384]); + + let b = vld1_s16([123, i16::MIN, 456, 789].as_ptr()); + let r: [i16; 4] = transmute(vqdmulh_s16(a, b)); + assert_eq!(r, [-123, -i16::MAX, 0, 394]); + + // Wider 16-bit versions. + + let a = vld1q_s16([i16::MIN, i16::MAX, 0, 16384, -16384, 8192, 1, -1].as_ptr()); + let b = vld1q_s16([123, 456, 789, i16::MIN, 1, 2, 3, 4].as_ptr()); + let r: [i16; 8] = transmute(vqdmulhq_s16(a, b)); + assert_eq!(r, [-123, 455, 0, -16384, -1, 0, 0, -1]); + + let r: [i16; 8] = transmute(vqdmulhq_n_s16(a, i16::MIN)); + assert_eq!(r, [32767, -32767, 0, -16384, 16384, -8192, -1, 1]); +} diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-sha.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-sha.rs index ae5731bc8a6a3..1e36867c4a0b6 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-sha.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-sha.rs @@ -1,6 +1,7 @@ // We're testing x86 target specific features //@only-target: x86_64 i686 //@compile-flags: -C target-feature=+sha,+sse2,+ssse3,+sse4.1 +//@run-native #[cfg(target_arch = "x86")] use std::arch::x86::*; diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-adx.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-adx.rs index baa984e68d83b..87b2f5d3d80b3 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-adx.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-adx.rs @@ -1,6 +1,7 @@ // We're testing x86 target specific features //@only-target: x86_64 i686 //@compile-flags: -C target-feature=+adx +//@run-native #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] mod x86 { diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-aes-vaes.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-aes-vaes.rs index 8936ae8e91268..ba51dcea35cc6 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-aes-vaes.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-aes-vaes.rs @@ -1,6 +1,7 @@ // We're testing x86 target specific features //@only-target: x86_64 i686 //@compile-flags: -C target-feature=+aes,+vaes,+avx512f +//@run-native use core::mem::transmute; #[cfg(target_arch = "x86")] @@ -11,7 +12,6 @@ use std::arch::x86_64::*; fn main() { assert!(is_x86_feature_detected!("aes")); assert!(is_x86_feature_detected!("vaes")); - assert!(is_x86_feature_detected!("avx512f")); unsafe { test_aes(); @@ -86,7 +86,7 @@ unsafe fn test_aes() { // be interpreted as integers; signedness does not make sense for them, but // __m128i happens to be defined in terms of signed integers. #[allow(overflowing_literals)] -#[target_feature(enable = "vaes,avx512f")] +#[target_feature(enable = "vaes")] unsafe fn test_vaes() { #[target_feature(enable = "avx")] unsafe fn get_a256() -> __m256i { @@ -177,6 +177,12 @@ unsafe fn test_vaes() { } test_mm256_aesenclast_epi128(); + // The tests below require avx512. + if !is_x86_feature_detected!("avx512f") { + println!("warning: skipping avx512 tests"); + return; + } + #[target_feature(enable = "avx512f")] unsafe fn get_a512() -> __m512i { // Constants are random diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx.rs index 9f7c12c4393b5..8709a17e8aeb4 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx.rs @@ -1,6 +1,7 @@ // We're testing x86 target specific features //@only-target: x86_64 i686 //@compile-flags: -C target-feature=+avx +//@run-native #[cfg(target_arch = "x86")] use std::arch::x86::*; diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx2.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx2.rs index de1abc818420c..7fe75254c2dd8 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx2.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx2.rs @@ -1,6 +1,7 @@ // We're testing x86 target specific features //@only-target: x86_64 i686 //@compile-flags: -C target-feature=+avx2 +//@run-native #[cfg(target_arch = "x86")] use std::arch::x86::*; @@ -1068,23 +1069,31 @@ unsafe fn test_avx2() { 18, 20, 22, 24, 26, 28, 30, ); - let r = _mm256_mpsadbw_epu8::<0b000>(a, a); + let r = _mm256_mpsadbw_epu8::<0b00000>(a, a); let e = _mm256_setr_epi16(0, 4, 8, 12, 16, 20, 24, 28, 0, 8, 16, 24, 32, 40, 48, 56); assert_eq_m256i(r, e); - let r = _mm256_mpsadbw_epu8::<0b001>(a, a); + let r = _mm256_mpsadbw_epu8::<0b001001>(a, a); let e = _mm256_setr_epi16(16, 12, 8, 4, 0, 4, 8, 12, 32, 24, 16, 8, 0, 8, 16, 24); assert_eq_m256i(r, e); - let r = _mm256_mpsadbw_epu8::<0b100>(a, a); + let r = _mm256_mpsadbw_epu8::<0b000001>(a, a); + let e = _mm256_setr_epi16(16, 12, 8, 4, 0, 4, 8, 12, 0, 8, 16, 24, 32, 40, 48, 56); + assert_eq_m256i(r, e); + + let r = _mm256_mpsadbw_epu8::<0b001000>(a, a); + let e = _mm256_setr_epi16(0, 4, 8, 12, 16, 20, 24, 28, 32, 24, 16, 8, 0, 8, 16, 24); + assert_eq_m256i(r, e); + + let r = _mm256_mpsadbw_epu8::<0b100100>(a, a); let e = _mm256_setr_epi16(16, 20, 24, 28, 32, 36, 40, 44, 32, 40, 48, 56, 64, 72, 80, 88); assert_eq_m256i(r, e); - let r = _mm256_mpsadbw_epu8::<0b101>(a, a); + let r = _mm256_mpsadbw_epu8::<0b101101>(a, a); let e = _mm256_setr_epi16(0, 4, 8, 12, 16, 20, 24, 28, 0, 8, 16, 24, 32, 40, 48, 56); assert_eq_m256i(r, e); - let r = _mm256_mpsadbw_epu8::<0b111>(a, a); + let r = _mm256_mpsadbw_epu8::<0b111111>(a, a); let e = _mm256_setr_epi16(32, 28, 24, 20, 16, 12, 8, 4, 64, 56, 48, 40, 32, 24, 16, 8); assert_eq_m256i(r, e); } diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx512.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx512.rs index 31f47b57fd285..0541e9a6cc6e0 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx512.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx512.rs @@ -1,6 +1,7 @@ // We're testing x86 target specific features //@only-target: x86_64 i686 //@compile-flags: -C target-feature=+avx512f,+avx512vl,+avx512bw,+avx512bitalg,+avx512vpopcntdq,+avx512vnni,+avx512vbmi +//@run-native #[cfg(target_arch = "x86")] use std::arch::x86::*; @@ -9,6 +10,13 @@ use std::arch::x86_64::*; use std::mem::transmute; fn main() { + if !is_x86_feature_detected!("avx512f") { + // GH runners don't have this, but we still want to run this natively if + // the machine happens to have gfni. So we bail out dynamically. + println!("warning: skipping AVX512 tests"); + return; + } + assert!(is_x86_feature_detected!("avx512f")); assert!(is_x86_feature_detected!("avx512vl")); assert!(is_x86_feature_detected!("avx512bw")); diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-bmi.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-bmi.rs index 030258f21fa51..d6c05b601f4eb 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-bmi.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-bmi.rs @@ -1,6 +1,7 @@ // We're testing x86 target specific features //@only-target: x86_64 i686 //@compile-flags: -C target-feature=+bmi1,+bmi2 +//@run-native #[cfg(target_arch = "x86")] use std::arch::x86::*; diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-gfni.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-gfni.rs index 48958ef581096..ef0fd62da5f3f 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-gfni.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-gfni.rs @@ -1,6 +1,7 @@ // We're testing x86 target specific features //@only-target: x86_64 i686 //@compile-flags: -C target-feature=+gfni,+avx512f +//@run-native // The constants in the tests below are just bit patterns. They should not // be interpreted as integers; signedness does not make sense for them, but @@ -20,8 +21,14 @@ const CONSTANT_BYTE: i32 = 0x63; fn main() { // Mostly copied from library/stdarch/crates/core_arch/src/x86/gfni.rs - assert!(is_x86_feature_detected!("avx512f")); - assert!(is_x86_feature_detected!("gfni")); + assert!(is_x86_feature_detected!("avx")); + + if !is_x86_feature_detected!("gfni") { + // GH runners don't have this, but we still want to run this natively if + // the machine happens to have gfni. So we bail out dynamically. + println!("warning: skipping gfni tests"); + return; + } unsafe { let byte_mul_test_data = generate_byte_mul_test_data(); @@ -29,15 +36,20 @@ fn main() { let affine_mul_test_data_constant = generate_affine_mul_test_data(CONSTANT_BYTE as u8); let inv_tests_data = generate_inv_tests_data(); - test_mm512_gf2p8mul_epi8(&byte_mul_test_data); test_mm256_gf2p8mul_epi8(&byte_mul_test_data); test_mm_gf2p8mul_epi8(&byte_mul_test_data); - test_mm512_gf2p8affine_epi64_epi8(&byte_mul_test_data, &affine_mul_test_data_identity); test_mm256_gf2p8affine_epi64_epi8(&byte_mul_test_data, &affine_mul_test_data_identity); test_mm_gf2p8affine_epi64_epi8(&byte_mul_test_data, &affine_mul_test_data_identity); - test_mm512_gf2p8affineinv_epi64_epi8(&inv_tests_data, &affine_mul_test_data_constant); test_mm256_gf2p8affineinv_epi64_epi8(&inv_tests_data, &affine_mul_test_data_constant); test_mm_gf2p8affineinv_epi64_epi8(&inv_tests_data, &affine_mul_test_data_constant); + + if is_x86_feature_detected!("avx512f") { + test_mm512_gf2p8mul_epi8(&byte_mul_test_data); + test_mm512_gf2p8affine_epi64_epi8(&byte_mul_test_data, &affine_mul_test_data_identity); + test_mm512_gf2p8affineinv_epi64_epi8(&inv_tests_data, &affine_mul_test_data_constant); + } else { + println!("warning: skipping avx512 tests"); + } } } diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-pclmulqdq.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-pclmulqdq.rs index 6051987f8d4c1..50ce9928ab7ed 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-pclmulqdq.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-pclmulqdq.rs @@ -1,6 +1,7 @@ // We're testing x86 target specific features //@only-target: x86_64 i686 //@compile-flags: -C target-feature=+pclmulqdq +//@run-native #[cfg(target_arch = "x86")] use std::arch::x86::*; diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse.rs index 9136b5eda3870..f42c6db676839 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse.rs @@ -1,5 +1,6 @@ // We're testing x86 target specific features //@only-target: x86_64 i686 +//@run-native #![allow(unnecessary_transmutes)] #[cfg(target_arch = "x86")] diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse2.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse2.rs index 570da30f0b622..8e4b284b1d150 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse2.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse2.rs @@ -1,5 +1,6 @@ // We're testing x86 target specific features //@only-target: x86_64 i686 +//@run-native #![allow(unnecessary_transmutes)] #[cfg(target_arch = "x86")] diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse3-ssse3.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse3-ssse3.rs index 10842160abdc6..e226f3512ac6e 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse3-ssse3.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse3-ssse3.rs @@ -2,6 +2,7 @@ //@only-target: x86_64 i686 // SSSE3 implicitly enables SSE3 //@compile-flags: -C target-feature=+ssse3 +//@run-native use core::mem::transmute; #[cfg(target_arch = "x86")] diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse41.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse41.rs index 7331c6ed0db33..48510b46ff327 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse41.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse41.rs @@ -1,6 +1,7 @@ // We're testing x86 target specific features //@only-target: x86_64 i686 //@compile-flags: -C target-feature=+sse4.1 +//@run-native #[cfg(target_arch = "x86")] use std::arch::x86::*; diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse42.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse42.rs index 30908baa6c15e..c998fe800df45 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse42.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse42.rs @@ -1,6 +1,7 @@ // We're testing x86 target specific features //@only-target: x86_64 i686 //@compile-flags: -C target-feature=+sse4.2 +//@run-native #[cfg(target_arch = "x86")] use std::arch::x86::*; diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-vpclmulqdq.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-vpclmulqdq.rs index e2a045bf81ff1..5ceaf405f4040 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-vpclmulqdq.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-vpclmulqdq.rs @@ -3,6 +3,7 @@ //@only-target: x86_64 i686 //@[avx512]compile-flags: -C target-feature=+vpclmulqdq,+avx512f //@[avx]compile-flags: -C target-feature=+vpclmulqdq,+avx2 +//@run-native // The constants in the tests below are just bit patterns. They should not // be interpreted as integers; signedness does not make sense for them, but diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86.rs index a18b6d01524e8..9745a437eccb6 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86.rs @@ -1,4 +1,6 @@ -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +//@only-target: x86_64 i686 +//@run-native + mod x86 { #[cfg(target_arch = "x86")] use core::arch::x86 as arch; @@ -84,7 +86,6 @@ mod x86_64 { } fn main() { - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] x86::main(); #[cfg(target_arch = "x86_64")] x86_64::main(); diff --git a/src/tools/miri/tests/utils/libc.rs b/src/tools/miri/tests/utils/libc.rs index f46e6aad01a5b..0f762ed916de5 100644 --- a/src/tools/miri/tests/utils/libc.rs +++ b/src/tools/miri/tests/utils/libc.rs @@ -86,7 +86,7 @@ pub fn read_exact_array(fd: libc::c_int) -> io::Result<[u8; N]> /// Do a single read from `fd` and return the part of the buffer that was written into, /// and the rest. #[track_caller] -pub fn read_split_slice(fd: libc::c_int, buf: &mut [u8]) -> io::Result<(&mut [u8], &mut [u8])> { +pub fn read_partial(fd: libc::c_int, buf: &mut [u8]) -> io::Result<(&mut [u8], &mut [u8])> { let res = errno_result(unsafe { libc::read(fd, buf.as_mut_ptr().cast(), buf.len()) })?; Ok(buf.split_at_mut(res as usize)) } diff --git a/tests/codegen-llvm/issues/issue-107681-unwrap_unchecked.rs b/tests/codegen-llvm/issues/issue-107681-unwrap_unchecked.rs index c594e187a0eb5..5834255f3d313 100644 --- a/tests/codegen-llvm/issues/issue-107681-unwrap_unchecked.rs +++ b/tests/codegen-llvm/issues/issue-107681-unwrap_unchecked.rs @@ -1,6 +1,4 @@ //@ compile-flags: -Copt-level=3 -//@ filecheck-flags: --implicit-check-not 'br {{.*}}' --implicit-check-not 'select' -//@ min-llvm-version: 22 // Test for #107681. // Make sure we don't create `br` or `select` instructions. @@ -13,8 +11,10 @@ use std::slice::Iter; #[no_mangle] pub unsafe fn foo(x: &mut Copied>) -> u32 { // CHECK-LABEL: @foo( - // CHECK: [[INNER:%.*]] = load ptr, ptr %x - // CHECK: [[RET:%.*]] = load i32, ptr [[INNER]] - // CHECK: ret i32 [[RET]] + // CHECK-NOT: br {{.*}} + // CHECK-NOT: select + // CHECK: [[RET:%.*]] = load i32, ptr + // CHECK-NEXT: assume + // CHECK-NEXT: ret i32 [[RET]] x.next().unwrap_unchecked() } diff --git a/tests/rustdoc-html/impl/impl-fundamental-nesting.rs b/tests/rustdoc-html/impl/impl-fundamental-nesting.rs new file mode 100644 index 0000000000000..25aed33e53514 --- /dev/null +++ b/tests/rustdoc-html/impl/impl-fundamental-nesting.rs @@ -0,0 +1,58 @@ +// Followup to https://github.com/rust-lang/rust/issues/92940 and impl-box.rs. +// +// Show traits implemented on fundamental types that wrap local ones: nested edition. + +#![crate_name = "foo"] + +use std::pin::Pin; + +pub struct Local; + +//@ has 'foo/struct.Local.html' + +// Nested fundamental + foreign Self. +//@ has '-' '//*[@id="impl-From%3CBox%3CLocal%3E%3E-for-String"]' 'impl From> for String' +impl From> for String { + fn from(_: Box) -> String { + String::new() + } +} + +// Also test with Pin. +//@ has '-' '//*[@id="impl-From%3CPin%3CLocal%3E%3E-for-u8"]' 'impl From> for u8' +impl From> for u8 { + fn from(_: Pin) -> u8 { + 0 + } +} + +// Reference to a fundamental wrapper. +//@ has '-' '//*[@id="impl-From%3C%26Box%3CLocal%3E%3E-for-u16"]' "impl<'a> From<&'a Box> for u16" +impl<'a> From<&'a Box> for u16 { + fn from(_: &'a Box) -> u16 { + 0 + } +} + +// Nested two levels deep in Self. +//@ has '-' '//*[@id="impl-From%3Cu32%3E-for-Box%3CBox%3CLocal%3E%3E"]' 'impl From for Box>' +impl From for Box> { + fn from(_: u32) -> Box> { + Box::new(Box::new(Local)) + } +} + +// Mixed fundamental wrappers in Self. +//@ has '-' '//*[@id="impl-From%3Cu64%3E-for-Pin%3CBox%3CLocal%3E%3E"]' 'impl From for Pin>' +impl From for Pin> { + fn from(_: u64) -> Pin> { + Pin::new(Box::new(Local)) + } +} + +// A non-fundamental wrapper must not show up on Local's page, but it should still be listed on the +// trait's own page. +pub trait Marker {} +//@ has 'foo/trait.Marker.html' '//*[@id="impl-Marker-for-Vec%3CLocal%3E"]' 'impl Marker for Vec' +//@ !has 'foo/struct.Local.html' '//*[@id="impl-Marker-for-Vec%3CLocal%3E"]' 'impl Marker for Vec' +impl Marker for Vec {} diff --git a/tests/rustdoc-json/impls/fundamental_nesting.rs b/tests/rustdoc-json/impls/fundamental_nesting.rs new file mode 100644 index 0000000000000..a6aefb94f961f --- /dev/null +++ b/tests/rustdoc-json/impls/fundamental_nesting.rs @@ -0,0 +1,57 @@ +// Companion to tests/rustdoc-html/impl/impl-fundamental-nesting.rs. +// +// Show traits implemented on fundamental types that wrap local ones: nested edition. + +use std::pin::Pin; + +pub struct Local; + +// Nested fundamental + foreign Self. +/// from box local +impl From> for String { + fn from(_: Box) -> String { + String::new() + } +} +//@ set from_box_local = "$.index[?(@.docs=='from box local')].id" +//@ has "$.index[?(@.name=='Local')].inner.struct.impls[*]" $from_box_local + +// Reference to a fundamental wrapper. +/// from ref box local +impl<'a> From<&'a Box> for u16 { + fn from(_: &'a Box) -> u16 { + 0 + } +} +//@ set from_ref_box_local = "$.index[?(@.docs=='from ref box local')].id" +//@ has "$.index[?(@.name=='Local')].inner.struct.impls[*]" $from_ref_box_local + +// Nested two levels deep in Self. +/// u32 for box box local +impl From for Box> { + fn from(_: u32) -> Box> { + Box::new(Box::new(Local)) + } +} +//@ set u32_for_box_box_local = "$.index[?(@.docs=='u32 for box box local')].id" +//@ has "$.index[?(@.name=='Local')].inner.struct.impls[*]" $u32_for_box_box_local + +// Mixed fundamental wrappers in Self. +/// u64 for pin box local +impl From for Pin> { + fn from(_: u64) -> Pin> { + Pin::new(Box::new(Local)) + } +} +//@ set u64_for_pin_box_local = "$.index[?(@.docs=='u64 for pin box local')].id" +//@ has "$.index[?(@.name=='Local')].inner.struct.impls[*]" $u64_for_pin_box_local + +// A non-fundamental wrapper must not associate the impl with Local, but the impl must still be +// listed on the trait itself. +pub trait Marker {} + +/// marker for vec local +impl Marker for Vec {} +//@ set marker_for_vec_local = "$.index[?(@.docs=='marker for vec local')].id" +//@ !has "$.index[?(@.name=='Local')].inner.struct.impls[*]" $marker_for_vec_local +//@ has "$.index[?(@.name=='Marker')].inner.trait.implementations[*]" $marker_for_vec_local diff --git a/tests/ui/README.md b/tests/ui/README.md index 6ffef1a1cd699..d342f393d0550 100644 --- a/tests/ui/README.md +++ b/tests/ui/README.md @@ -1351,6 +1351,10 @@ Generic collection of tests for suggestions, when no more specific directories a **FIXME**: Some overlap with `tests/ui/did_you_mean/`, that directory should probably be moved under here. +## `tests/ui/supertrait-shadowing/` + +Tests for supertrait item shadowing (RFC 3624). + ## `tests/ui/svh/`: Strict Version Hash Tests on the *Strict Version Hash* (SVH, also known as the "crate hash"). diff --git a/tests/ui/lint/auxiliary/lint_stability.rs b/tests/ui/lint/auxiliary/lint_stability.rs index 99c29dcdda677..bc2d47aebbc29 100644 --- a/tests/ui/lint/auxiliary/lint_stability.rs +++ b/tests/ui/lint/auxiliary/lint_stability.rs @@ -1,5 +1,6 @@ #![crate_name="lint_stability"] #![crate_type = "lib"] +#![feature(extern_types)] #![feature(staged_api)] #![feature(associated_type_defaults)] #![stable(feature = "lint_stability", since = "1.0.0")] @@ -186,3 +187,8 @@ macro_rules! macro_test_arg { macro_rules! macro_test_arg_nested { ($func:ident) => (macro_test_arg!($func())); } + +extern "C" { + #[unstable(feature = "unstable_test_feature", issue = "none")] + pub type UnstableForeignType; +} diff --git a/tests/ui/lint/lint-stability.rs b/tests/ui/lint/lint-stability.rs index f080b5e4bbeb1..c8712397189c7 100644 --- a/tests/ui/lint/lint-stability.rs +++ b/tests/ui/lint/lint-stability.rs @@ -6,7 +6,7 @@ #![allow(deprecated)] #![allow(dead_code)] #![feature(staged_api)] - +#![feature(extern_types)] #![stable(feature = "rust1", since = "1.0.0")] #[macro_use] @@ -18,6 +18,9 @@ mod cross_crate { use lint_stability::*; + fn test_foreign_type(_: &mut UnstableForeignType) { //~ ERROR use of unstable library feature + } + fn test() { type Foo = MethodTester; let foo = MethodTester; diff --git a/tests/ui/lint/lint-stability.stderr b/tests/ui/lint/lint-stability.stderr index 53d31b16811ab..6316f470349ad 100644 --- a/tests/ui/lint/lint-stability.stderr +++ b/tests/ui/lint/lint-stability.stderr @@ -8,7 +8,16 @@ LL | extern crate stability_cfg2; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:45:9 + --> $DIR/lint-stability.rs:21:34 + | +LL | fn test_foreign_type(_: &mut UnstableForeignType) { + | ^^^^^^^^^^^^^^^^^^^ + | + = help: add `#![feature(unstable_test_feature)]` 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[E0658]: use of unstable library feature `unstable_test_feature` + --> $DIR/lint-stability.rs:48:9 | LL | deprecated_unstable(); | ^^^^^^^^^^^^^^^^^^^ @@ -17,7 +26,7 @@ LL | deprecated_unstable(); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:47:9 + --> $DIR/lint-stability.rs:50:9 | LL | Trait::trait_deprecated_unstable(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -26,7 +35,7 @@ LL | Trait::trait_deprecated_unstable(&foo); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:49:9 + --> $DIR/lint-stability.rs:52:9 | LL | ::trait_deprecated_unstable(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -35,7 +44,7 @@ LL | ::trait_deprecated_unstable(&foo); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:52:9 + --> $DIR/lint-stability.rs:55:9 | LL | deprecated_unstable_text(); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -44,7 +53,7 @@ LL | deprecated_unstable_text(); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:54:9 + --> $DIR/lint-stability.rs:57:9 | LL | Trait::trait_deprecated_unstable_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -53,7 +62,7 @@ LL | Trait::trait_deprecated_unstable_text(&foo); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:56:9 + --> $DIR/lint-stability.rs:59:9 | LL | ::trait_deprecated_unstable_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -62,7 +71,7 @@ LL | ::trait_deprecated_unstable_text(&foo); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:59:9 + --> $DIR/lint-stability.rs:62:9 | LL | unstable(); | ^^^^^^^^ @@ -71,7 +80,7 @@ LL | unstable(); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:60:9 + --> $DIR/lint-stability.rs:63:9 | LL | Trait::trait_unstable(&foo); | ^^^^^^^^^^^^^^^^^^^^^ @@ -80,7 +89,7 @@ LL | Trait::trait_unstable(&foo); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:61:9 + --> $DIR/lint-stability.rs:64:9 | LL | ::trait_unstable(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -89,7 +98,7 @@ LL | ::trait_unstable(&foo); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature`: text - --> $DIR/lint-stability.rs:63:9 + --> $DIR/lint-stability.rs:66:9 | LL | unstable_text(); | ^^^^^^^^^^^^^ @@ -98,7 +107,7 @@ LL | unstable_text(); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature`: text - --> $DIR/lint-stability.rs:65:9 + --> $DIR/lint-stability.rs:68:9 | LL | Trait::trait_unstable_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -107,7 +116,7 @@ LL | Trait::trait_unstable_text(&foo); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature`: text - --> $DIR/lint-stability.rs:67:9 + --> $DIR/lint-stability.rs:70:9 | LL | ::trait_unstable_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -116,7 +125,7 @@ LL | ::trait_unstable_text(&foo); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:99:17 + --> $DIR/lint-stability.rs:102:17 | LL | let _ = DeprecatedUnstableStruct { | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -125,7 +134,7 @@ LL | let _ = DeprecatedUnstableStruct { = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:103:17 + --> $DIR/lint-stability.rs:106:17 | LL | let _ = UnstableStruct { i: 0 }; | ^^^^^^^^^^^^^^ @@ -134,7 +143,7 @@ LL | let _ = UnstableStruct { i: 0 }; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:107:17 + --> $DIR/lint-stability.rs:110:17 | LL | let _ = DeprecatedUnstableUnitStruct; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -143,7 +152,7 @@ LL | let _ = DeprecatedUnstableUnitStruct; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:109:17 + --> $DIR/lint-stability.rs:112:17 | LL | let _ = UnstableUnitStruct; | ^^^^^^^^^^^^^^^^^^ @@ -152,7 +161,7 @@ LL | let _ = UnstableUnitStruct; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:113:17 + --> $DIR/lint-stability.rs:116:17 | LL | let _ = Enum::DeprecatedUnstableVariant; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -161,7 +170,7 @@ LL | let _ = Enum::DeprecatedUnstableVariant; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:115:17 + --> $DIR/lint-stability.rs:118:17 | LL | let _ = Enum::UnstableVariant; | ^^^^^^^^^^^^^^^^^^^^^ @@ -170,7 +179,7 @@ LL | let _ = Enum::UnstableVariant; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:119:17 + --> $DIR/lint-stability.rs:122:17 | LL | let _ = DeprecatedUnstableTupleStruct (1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -179,7 +188,7 @@ LL | let _ = DeprecatedUnstableTupleStruct (1); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:121:17 + --> $DIR/lint-stability.rs:124:17 | LL | let _ = UnstableTupleStruct (1); | ^^^^^^^^^^^^^^^^^^^ @@ -188,7 +197,7 @@ LL | let _ = UnstableTupleStruct (1); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:130:25 + --> $DIR/lint-stability.rs:133:25 | LL | macro_test_arg!(deprecated_unstable_text()); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -197,7 +206,7 @@ LL | macro_test_arg!(deprecated_unstable_text()); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:144:9 + --> $DIR/lint-stability.rs:147:9 | LL | Trait::trait_deprecated_unstable(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -206,7 +215,7 @@ LL | Trait::trait_deprecated_unstable(&foo); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:146:9 + --> $DIR/lint-stability.rs:149:9 | LL | ::trait_deprecated_unstable(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -215,7 +224,7 @@ LL | ::trait_deprecated_unstable(&foo); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:148:9 + --> $DIR/lint-stability.rs:151:9 | LL | Trait::trait_deprecated_unstable_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -224,7 +233,7 @@ LL | Trait::trait_deprecated_unstable_text(&foo); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:150:9 + --> $DIR/lint-stability.rs:153:9 | LL | ::trait_deprecated_unstable_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -233,7 +242,7 @@ LL | ::trait_deprecated_unstable_text(&foo); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:152:9 + --> $DIR/lint-stability.rs:155:9 | LL | Trait::trait_unstable(&foo); | ^^^^^^^^^^^^^^^^^^^^^ @@ -242,7 +251,7 @@ LL | Trait::trait_unstable(&foo); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:153:9 + --> $DIR/lint-stability.rs:156:9 | LL | ::trait_unstable(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -251,7 +260,7 @@ LL | ::trait_unstable(&foo); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature`: text - --> $DIR/lint-stability.rs:154:9 + --> $DIR/lint-stability.rs:157:9 | LL | Trait::trait_unstable_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -260,7 +269,7 @@ LL | Trait::trait_unstable_text(&foo); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature`: text - --> $DIR/lint-stability.rs:156:9 + --> $DIR/lint-stability.rs:159:9 | LL | ::trait_unstable_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -269,7 +278,7 @@ LL | ::trait_unstable_text(&foo); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:172:10 + --> $DIR/lint-stability.rs:175:10 | LL | impl UnstableTrait for S { } | ^^^^^^^^^^^^^ @@ -278,7 +287,7 @@ LL | impl UnstableTrait for S { } = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:174:24 + --> $DIR/lint-stability.rs:177:24 | LL | trait LocalTrait : UnstableTrait { } | ^^^^^^^^^^^^^ @@ -287,7 +296,7 @@ LL | trait LocalTrait : UnstableTrait { } = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:179:9 + --> $DIR/lint-stability.rs:182:9 | LL | fn trait_unstable(&self) {} | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -296,7 +305,7 @@ LL | fn trait_unstable(&self) {} = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:184:5 + --> $DIR/lint-stability.rs:187:5 | LL | extern crate inherited_stability; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -305,7 +314,7 @@ LL | extern crate inherited_stability; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:185:9 + --> $DIR/lint-stability.rs:188:9 | LL | use self::inherited_stability::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -314,7 +323,7 @@ LL | use self::inherited_stability::*; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:188:9 + --> $DIR/lint-stability.rs:191:9 | LL | unstable(); | ^^^^^^^^ @@ -323,7 +332,7 @@ LL | unstable(); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:191:9 + --> $DIR/lint-stability.rs:194:9 | LL | stable_mod::unstable(); | ^^^^^^^^^^^^^^^^^^^^ @@ -332,7 +341,7 @@ LL | stable_mod::unstable(); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:194:9 + --> $DIR/lint-stability.rs:197:9 | LL | unstable_mod::deprecated(); | ^^^^^^^^^^^^ @@ -341,7 +350,7 @@ LL | unstable_mod::deprecated(); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:195:9 + --> $DIR/lint-stability.rs:198:9 | LL | unstable_mod::unstable(); | ^^^^^^^^^^^^^^^^^^^^^^ @@ -350,7 +359,7 @@ LL | unstable_mod::unstable(); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:197:17 + --> $DIR/lint-stability.rs:200:17 | LL | let _ = Unstable::UnstableVariant; | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -359,7 +368,7 @@ LL | let _ = Unstable::UnstableVariant; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:198:17 + --> $DIR/lint-stability.rs:201:17 | LL | let _ = Unstable::StableVariant; | ^^^^^^^^ @@ -368,7 +377,7 @@ LL | let _ = Unstable::StableVariant; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:88:48 + --> $DIR/lint-stability.rs:91:48 | LL | struct S1(T::TypeUnstable); | ^^^^^^^^^^^^^^^ @@ -377,7 +386,7 @@ LL | struct S1(T::TypeUnstable); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:92:13 + --> $DIR/lint-stability.rs:95:13 | LL | TypeUnstable = u8, | ^^^^^^^^^^^^^^^^^ @@ -385,6 +394,6 @@ LL | TypeUnstable = u8, = help: add `#![feature(unstable_test_feature)]` 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 43 previous errors +error: aborting due to 44 previous errors For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/methods/supertrait-shadowing/assoc-const.rs b/tests/ui/methods/supertrait-shadowing/assoc-const.rs deleted file mode 100644 index 01d045b9fb74f..0000000000000 --- a/tests/ui/methods/supertrait-shadowing/assoc-const.rs +++ /dev/null @@ -1,22 +0,0 @@ -//@ run-pass - -#![feature(supertrait_item_shadowing)] -#![allow(dead_code)] - -trait A { - const CONST: i32; -} -impl A for T { - const CONST: i32 = 1; -} - -trait B: A { - const CONST: i32; -} -impl B for T { - const CONST: i32 = 2; -} - -fn main() { - assert_eq!(i32::CONST, 2) -} diff --git a/tests/ui/methods/supertrait-shadowing/common-ancestor-2.rs b/tests/ui/methods/supertrait-shadowing/common-ancestor-2.rs deleted file mode 100644 index ce56c25df19ea..0000000000000 --- a/tests/ui/methods/supertrait-shadowing/common-ancestor-2.rs +++ /dev/null @@ -1,33 +0,0 @@ -//@ run-pass - -#![feature(supertrait_item_shadowing)] -#![warn(resolving_to_items_shadowing_supertrait_items)] -#![warn(shadowing_supertrait_items)] -#![allow(dead_code)] - -trait A { - fn hello(&self) -> &'static str { - "A" - } -} -impl A for T {} - -trait B { - fn hello(&self) -> &'static str { - "B" - } -} -impl B for T {} - -trait C: A + B { - fn hello(&self) -> &'static str { - //~^ WARN trait item `hello` from `C` shadows identically named item - "C" - } -} -impl C for T {} - -fn main() { - assert_eq!(().hello(), "C"); - //~^ WARN trait item `hello` from `C` shadows identically named item from supertrait -} diff --git a/tests/ui/methods/supertrait-shadowing/common-ancestor-3.rs b/tests/ui/methods/supertrait-shadowing/common-ancestor-3.rs deleted file mode 100644 index b29f3c8d014ec..0000000000000 --- a/tests/ui/methods/supertrait-shadowing/common-ancestor-3.rs +++ /dev/null @@ -1,43 +0,0 @@ -//@ run-pass - -#![feature(supertrait_item_shadowing)] -#![warn(resolving_to_items_shadowing_supertrait_items)] -#![warn(shadowing_supertrait_items)] -#![allow(dead_code)] - -trait A { - fn hello(&self) -> &'static str { - "A" - } -} -impl A for T {} - -trait B { - fn hello(&self) -> &'static str { - "B" - } -} -impl B for T {} - -trait C: A + B { - fn hello(&self) -> &'static str { - //~^ WARN trait item `hello` from `C` shadows identically named item - "C" - } -} -impl C for T {} - -// `D` extends `C` which extends `B` and `A` - -trait D: C { - fn hello(&self) -> &'static str { - //~^ WARN trait item `hello` from `D` shadows identically named item - "D" - } -} -impl D for T {} - -fn main() { - assert_eq!(().hello(), "D"); - //~^ WARN trait item `hello` from `D` shadows identically named item from supertrait -} diff --git a/tests/ui/methods/supertrait-shadowing/common-ancestor-3.stderr b/tests/ui/methods/supertrait-shadowing/common-ancestor-3.stderr deleted file mode 100644 index 28fe7f72f94c3..0000000000000 --- a/tests/ui/methods/supertrait-shadowing/common-ancestor-3.stderr +++ /dev/null @@ -1,68 +0,0 @@ -warning: trait item `hello` from `C` shadows identically named item from supertrait - --> $DIR/common-ancestor-3.rs:23:5 - | -LL | fn hello(&self) -> &'static str { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: items from several supertraits are shadowed: `B` and `A` - --> $DIR/common-ancestor-3.rs:9:5 - | -LL | fn hello(&self) -> &'static str { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -... -LL | fn hello(&self) -> &'static str { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: the lint level is defined here - --> $DIR/common-ancestor-3.rs:5:9 - | -LL | #![warn(shadowing_supertrait_items)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - -warning: trait item `hello` from `D` shadows identically named item from supertrait - --> $DIR/common-ancestor-3.rs:33:5 - | -LL | fn hello(&self) -> &'static str { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: items from several supertraits are shadowed: `C`, `B`, and `A` - --> $DIR/common-ancestor-3.rs:9:5 - | -LL | fn hello(&self) -> &'static str { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -... -LL | fn hello(&self) -> &'static str { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -... -LL | fn hello(&self) -> &'static str { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -warning: trait item `hello` from `D` shadows identically named item from supertrait - --> $DIR/common-ancestor-3.rs:41:19 - | -LL | assert_eq!(().hello(), "D"); - | ^^^^^ - | -note: item from `D` shadows a supertrait item - --> $DIR/common-ancestor-3.rs:33:5 - | -LL | fn hello(&self) -> &'static str { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: items from several supertraits are shadowed: `A`, `B`, and `C` - --> $DIR/common-ancestor-3.rs:9:5 - | -LL | fn hello(&self) -> &'static str { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -... -LL | fn hello(&self) -> &'static str { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -... -LL | fn hello(&self) -> &'static str { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: the lint level is defined here - --> $DIR/common-ancestor-3.rs:4:9 - | -LL | #![warn(resolving_to_items_shadowing_supertrait_items)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -warning: 3 warnings emitted - diff --git a/tests/ui/methods/supertrait-shadowing/common-ancestor.rs b/tests/ui/methods/supertrait-shadowing/common-ancestor.rs deleted file mode 100644 index b288d6e22b8c3..0000000000000 --- a/tests/ui/methods/supertrait-shadowing/common-ancestor.rs +++ /dev/null @@ -1,26 +0,0 @@ -//@ run-pass - -#![feature(supertrait_item_shadowing)] -#![warn(resolving_to_items_shadowing_supertrait_items)] -#![warn(shadowing_supertrait_items)] -#![allow(dead_code)] - -trait A { - fn hello(&self) -> &'static str { - "A" - } -} -impl A for T {} - -trait B: A { - fn hello(&self) -> &'static str { - //~^ WARN trait item `hello` from `B` shadows identically named item - "B" - } -} -impl B for T {} - -fn main() { - assert_eq!(().hello(), "B"); - //~^ WARN trait item `hello` from `B` shadows identically named item from supertrait -} diff --git a/tests/ui/methods/supertrait-shadowing/definition-site.rs b/tests/ui/methods/supertrait-shadowing/definition-site.rs deleted file mode 100644 index 248df032736f6..0000000000000 --- a/tests/ui/methods/supertrait-shadowing/definition-site.rs +++ /dev/null @@ -1,18 +0,0 @@ -#![feature(supertrait_item_shadowing)] -#![deny(shadowing_supertrait_items)] - -trait SuperSuper { - fn method(); -} - -trait Super: SuperSuper { - fn method(); - //~^ ERROR trait item `method` from `Super` shadows identically named item -} - -trait Sub: Super { - fn method(); - //~^ ERROR trait item `method` from `Sub` shadows identically named item -} - -fn main() {} diff --git a/tests/ui/methods/supertrait-shadowing/definition-site.stderr b/tests/ui/methods/supertrait-shadowing/definition-site.stderr deleted file mode 100644 index 1e35a753a9ebc..0000000000000 --- a/tests/ui/methods/supertrait-shadowing/definition-site.stderr +++ /dev/null @@ -1,34 +0,0 @@ -error: trait item `method` from `Super` shadows identically named item from supertrait - --> $DIR/definition-site.rs:9:5 - | -LL | fn method(); - | ^^^^^^^^^^^^ - | -note: item from `SuperSuper` is shadowed by a subtrait item - --> $DIR/definition-site.rs:5:5 - | -LL | fn method(); - | ^^^^^^^^^^^^ -note: the lint level is defined here - --> $DIR/definition-site.rs:2:9 - | -LL | #![deny(shadowing_supertrait_items)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: trait item `method` from `Sub` shadows identically named item from supertrait - --> $DIR/definition-site.rs:14:5 - | -LL | fn method(); - | ^^^^^^^^^^^^ - | -note: items from several supertraits are shadowed: `Super` and `SuperSuper` - --> $DIR/definition-site.rs:5:5 - | -LL | fn method(); - | ^^^^^^^^^^^^ -... -LL | fn method(); - | ^^^^^^^^^^^^ - -error: aborting due to 2 previous errors - diff --git a/tests/ui/methods/supertrait-shadowing/no-common-ancestor-2.rs b/tests/ui/methods/supertrait-shadowing/no-common-ancestor-2.rs deleted file mode 100644 index b49476c7a4f68..0000000000000 --- a/tests/ui/methods/supertrait-shadowing/no-common-ancestor-2.rs +++ /dev/null @@ -1,37 +0,0 @@ -#![feature(supertrait_item_shadowing)] - -trait A { - fn hello(&self) -> &'static str { - "A" - } -} -impl A for T {} - -trait B { - fn hello(&self) -> &'static str { - "B" - } -} -impl B for T {} - -trait C: A + B { - fn hello(&self) -> &'static str { - "C" - } -} -impl C for T {} - -// Since `D` is not a subtrait of `C`, -// we have no obvious lower bound. - -trait D: B { - fn hello(&self) -> &'static str { - "D" - } -} -impl D for T {} - -fn main() { - ().hello(); - //~^ ERROR multiple applicable items in scope -} diff --git a/tests/ui/methods/supertrait-shadowing/no-common-ancestor-2.stderr b/tests/ui/methods/supertrait-shadowing/no-common-ancestor-2.stderr deleted file mode 100644 index 745f61d00d660..0000000000000 --- a/tests/ui/methods/supertrait-shadowing/no-common-ancestor-2.stderr +++ /dev/null @@ -1,50 +0,0 @@ -error[E0034]: multiple applicable items in scope - --> $DIR/no-common-ancestor-2.rs:35:8 - | -LL | ().hello(); - | ^^^^^ multiple `hello` found - | -note: candidate #1 is defined in an impl of the trait `A` for the type `T` - --> $DIR/no-common-ancestor-2.rs:4:5 - | -LL | fn hello(&self) -> &'static str { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: candidate #2 is defined in an impl of the trait `B` for the type `T` - --> $DIR/no-common-ancestor-2.rs:11:5 - | -LL | fn hello(&self) -> &'static str { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: candidate #3 is defined in an impl of the trait `C` for the type `T` - --> $DIR/no-common-ancestor-2.rs:18:5 - | -LL | fn hello(&self) -> &'static str { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: candidate #4 is defined in an impl of the trait `D` for the type `T` - --> $DIR/no-common-ancestor-2.rs:28:5 - | -LL | fn hello(&self) -> &'static str { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: disambiguate the method for candidate #1 - | -LL - ().hello(); -LL + A::hello(&()); - | -help: disambiguate the method for candidate #2 - | -LL - ().hello(); -LL + B::hello(&()); - | -help: disambiguate the method for candidate #3 - | -LL - ().hello(); -LL + C::hello(&()); - | -help: disambiguate the method for candidate #4 - | -LL - ().hello(); -LL + D::hello(&()); - | - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0034`. diff --git a/tests/ui/methods/supertrait-shadowing/no-common-ancestor.rs b/tests/ui/methods/supertrait-shadowing/no-common-ancestor.rs deleted file mode 100644 index 03c822eb7375e..0000000000000 --- a/tests/ui/methods/supertrait-shadowing/no-common-ancestor.rs +++ /dev/null @@ -1,20 +0,0 @@ -#![feature(supertrait_item_shadowing)] - -trait A { - fn hello(&self) -> &'static str { - "A" - } -} -impl A for T {} - -trait B { - fn hello(&self) -> &'static str { - "B" - } -} -impl B for T {} - -fn main() { - ().hello(); - //~^ ERROR multiple applicable items in scope -} diff --git a/tests/ui/methods/supertrait-shadowing/no-common-ancestor.stderr b/tests/ui/methods/supertrait-shadowing/no-common-ancestor.stderr deleted file mode 100644 index 29cf7ff1dbb11..0000000000000 --- a/tests/ui/methods/supertrait-shadowing/no-common-ancestor.stderr +++ /dev/null @@ -1,30 +0,0 @@ -error[E0034]: multiple applicable items in scope - --> $DIR/no-common-ancestor.rs:18:8 - | -LL | ().hello(); - | ^^^^^ multiple `hello` found - | -note: candidate #1 is defined in an impl of the trait `A` for the type `T` - --> $DIR/no-common-ancestor.rs:4:5 - | -LL | fn hello(&self) -> &'static str { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: candidate #2 is defined in an impl of the trait `B` for the type `T` - --> $DIR/no-common-ancestor.rs:11:5 - | -LL | fn hello(&self) -> &'static str { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: disambiguate the method for candidate #1 - | -LL - ().hello(); -LL + A::hello(&()); - | -help: disambiguate the method for candidate #2 - | -LL - ().hello(); -LL + B::hello(&()); - | - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0034`. diff --git a/tests/ui/methods/supertrait-shadowing/out-of-scope.rs b/tests/ui/methods/supertrait-shadowing/out-of-scope.rs deleted file mode 100644 index 4112634399b78..0000000000000 --- a/tests/ui/methods/supertrait-shadowing/out-of-scope.rs +++ /dev/null @@ -1,23 +0,0 @@ -//@ run-pass - -#![allow(dead_code)] - -mod out_of_scope { - pub trait Subtrait: super::Supertrait { - fn hello(&self) -> &'static str { - "subtrait" - } - } - impl Subtrait for T {} -} - -trait Supertrait { - fn hello(&self) -> &'static str { - "supertrait" - } -} -impl Supertrait for T {} - -fn main() { - assert_eq!(().hello(), "supertrait"); -} diff --git a/tests/ui/methods/supertrait-shadowing/type-dependent.rs b/tests/ui/methods/supertrait-shadowing/type-dependent.rs deleted file mode 100644 index 4a5af1d598812..0000000000000 --- a/tests/ui/methods/supertrait-shadowing/type-dependent.rs +++ /dev/null @@ -1,28 +0,0 @@ -//@ run-pass - -// Makes sure we can shadow with type-dependent method syntax. - -#![feature(supertrait_item_shadowing)] -#![allow(dead_code)] - -trait A { - fn hello() -> &'static str { - "A" - } -} -impl A for T {} - -trait B: A { - fn hello() -> &'static str { - "B" - } -} -impl B for T {} - -fn foo() -> &'static str { - T::hello() -} - -fn main() { - assert_eq!(foo::<()>(), "B"); -} diff --git a/tests/ui/supertrait-shadowing/assoc-const.rs b/tests/ui/supertrait-shadowing/assoc-const.rs new file mode 100644 index 0000000000000..be0d990284b0c --- /dev/null +++ b/tests/ui/supertrait-shadowing/assoc-const.rs @@ -0,0 +1,31 @@ +//@ run-pass + +#![feature(supertrait_item_shadowing)] +#![feature(min_generic_const_args)] +#![allow(dead_code)] + +trait A { + const CONST: i32; +} +impl A for T { + const CONST: i32 = 1; +} + +trait B: A { + type const CONST: i32; +} +impl B for T { + type const CONST: i32 = 2; +} + +trait C: B {} +impl C for T {} + +fn main() { + assert_eq!(i32::CONST, 2); + generic::(); +} + +fn generic>() { + assert_eq!(T::CONST, 2); +} diff --git a/tests/ui/supertrait-shadowing/assoc-type-fail.rs b/tests/ui/supertrait-shadowing/assoc-type-fail.rs new file mode 100644 index 0000000000000..5df4edb7e96e2 --- /dev/null +++ b/tests/ui/supertrait-shadowing/assoc-type-fail.rs @@ -0,0 +1,47 @@ +#![feature(supertrait_item_shadowing)] +#![allow(dead_code)] + +use std::mem::size_of; + +trait A { + type Assoc; +} +impl A for X { + type Assoc = i8; +} + +trait B: A { + type Assoc; +} +impl B for X { + type Assoc = i16; +} + +trait C: B {} +impl C for X {} + +fn main() { + b_unbound::(); + c_unbound::(); + + b_assoc_is_a::(); + //~^ ERROR type mismatch resolving `::Assoc == i8` + c_assoc_is_a::(); + //~^ ERROR type mismatch resolving `::Assoc == i8` +} + +fn b_unbound() { + let _ = size_of::(); +} + +fn c_unbound() { + let _ = size_of::(); +} + +fn b_assoc_is_a>() { + let _ = size_of::(); +} + +fn c_assoc_is_a>() { + let _ = size_of::(); +} diff --git a/tests/ui/supertrait-shadowing/assoc-type-fail.stderr b/tests/ui/supertrait-shadowing/assoc-type-fail.stderr new file mode 100644 index 0000000000000..36a9bd082aaf6 --- /dev/null +++ b/tests/ui/supertrait-shadowing/assoc-type-fail.stderr @@ -0,0 +1,37 @@ +error[E0271]: type mismatch resolving `::Assoc == i8` + --> $DIR/assoc-type-fail.rs:27:20 + | +LL | b_assoc_is_a::(); + | ^^^ type mismatch resolving `::Assoc == i8` + | +note: expected this to be `i8` + --> $DIR/assoc-type-fail.rs:17:18 + | +LL | type Assoc = i16; + | ^^^ +note: required by a bound in `b_assoc_is_a` + --> $DIR/assoc-type-fail.rs:41:22 + | +LL | fn b_assoc_is_a>() { + | ^^^^^^^^^^ required by this bound in `b_assoc_is_a` + +error[E0271]: type mismatch resolving `::Assoc == i8` + --> $DIR/assoc-type-fail.rs:29:20 + | +LL | c_assoc_is_a::(); + | ^^^ type mismatch resolving `::Assoc == i8` + | +note: expected this to be `i8` + --> $DIR/assoc-type-fail.rs:17:18 + | +LL | type Assoc = i16; + | ^^^ +note: required by a bound in `c_assoc_is_a` + --> $DIR/assoc-type-fail.rs:45:22 + | +LL | fn c_assoc_is_a>() { + | ^^^^^^^^^^ required by this bound in `c_assoc_is_a` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0271`. diff --git a/tests/ui/supertrait-shadowing/assoc-type-predicates.rs b/tests/ui/supertrait-shadowing/assoc-type-predicates.rs new file mode 100644 index 0000000000000..55d2b84a50f9a --- /dev/null +++ b/tests/ui/supertrait-shadowing/assoc-type-predicates.rs @@ -0,0 +1,45 @@ +//@ normalize-stderr: "assoc_type_predicates\[[^\]]+\]" -> "assoc_type_predicates[HASH]" + +#![feature(rustc_attrs)] +#![feature(supertrait_item_shadowing)] +#![allow(dead_code)] + +trait A { + type Assoc; +} +impl A for T { + type Assoc = i8; +} + +trait B: A { + type Assoc; +} +impl B for T { + type Assoc = i16; +} + +trait C: B {} +impl C for T {} + +#[rustc_dump_predicates] +fn a_bound>() {} +//~^ ERROR rustc_dump_predicates +//~| NOTE TraitPredicate( +//~| NOTE TraitPredicate( +//~| NOTE A::Assoc + +#[rustc_dump_predicates] +fn b_bound>() {} +//~^ ERROR rustc_dump_predicates +//~| NOTE TraitPredicate( +//~| NOTE TraitPredicate( +//~| NOTE B::Assoc + +#[rustc_dump_predicates] +fn c_bound>() {} +//~^ ERROR rustc_dump_predicates +//~| NOTE TraitPredicate( +//~| NOTE TraitPredicate( +//~| NOTE B::Assoc + +fn main() {} diff --git a/tests/ui/supertrait-shadowing/assoc-type-predicates.stderr b/tests/ui/supertrait-shadowing/assoc-type-predicates.stderr new file mode 100644 index 0000000000000..be765e737a6e1 --- /dev/null +++ b/tests/ui/supertrait-shadowing/assoc-type-predicates.stderr @@ -0,0 +1,32 @@ +error: rustc_dump_predicates + --> $DIR/assoc-type-predicates.rs:25:1 + | +LL | fn a_bound>() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: Binder { value: TraitPredicate(, polarity:Positive), bound_vars: [] } + = note: Binder { value: TraitPredicate(, polarity:Positive), bound_vars: [] } + = note: Binder { value: ProjectionPredicate(Alias { kind: ProjectionTy { def_id: DefId(0:4 ~ assoc_type_predicates[HASH]::A::Assoc) }, args: [T/#0], .. }, Term::Ty(i8)), bound_vars: [] } + +error: rustc_dump_predicates + --> $DIR/assoc-type-predicates.rs:32:1 + | +LL | fn b_bound>() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: Binder { value: TraitPredicate(, polarity:Positive), bound_vars: [] } + = note: Binder { value: TraitPredicate(, polarity:Positive), bound_vars: [] } + = note: Binder { value: ProjectionPredicate(Alias { kind: ProjectionTy { def_id: DefId(0:9 ~ assoc_type_predicates[HASH]::B::Assoc) }, args: [T/#0], .. }, Term::Ty(i16)), bound_vars: [] } + +error: rustc_dump_predicates + --> $DIR/assoc-type-predicates.rs:39:1 + | +LL | fn c_bound>() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: Binder { value: TraitPredicate(, polarity:Positive), bound_vars: [] } + = note: Binder { value: TraitPredicate(, polarity:Positive), bound_vars: [] } + = note: Binder { value: ProjectionPredicate(Alias { kind: ProjectionTy { def_id: DefId(0:9 ~ assoc_type_predicates[HASH]::B::Assoc) }, args: [T/#0], .. }, Term::Ty(i16)), bound_vars: [] } + +error: aborting due to 3 previous errors + diff --git a/tests/ui/supertrait-shadowing/assoc-type.rs b/tests/ui/supertrait-shadowing/assoc-type.rs new file mode 100644 index 0000000000000..9e9799397c571 --- /dev/null +++ b/tests/ui/supertrait-shadowing/assoc-type.rs @@ -0,0 +1,53 @@ +//@ run-pass +//@ check-run-results + +#![feature(supertrait_item_shadowing)] +#![allow(dead_code)] + +use std::mem::size_of; + +trait A { + type Assoc; +} +impl A for T { + type Assoc = i8; +} + +trait B: A { + type Assoc; +} +impl B for T { + type Assoc = i16; +} + +trait C: B {} +impl C for T {} + +fn main() { + generic::(); + generic2::(); + generic3::(); + generic4::(); + generic5::(); +} + +fn generic() { + assert_eq!(size_of::(), 2); +} + +fn generic2>() { + assert_eq!(size_of::(), 1); +} + +fn generic3>() { + assert_eq!(size_of::(), 2); +} + +fn generic4>() { + assert_eq!(size_of::(), 2); +} + +fn generic5() { + assert_eq!(size_of::<::Assoc>(), 1); + assert_eq!(size_of::<::Assoc>(), 2); +} diff --git a/tests/ui/methods/supertrait-shadowing/auxiliary/shadowed_stability.rs b/tests/ui/supertrait-shadowing/auxiliary/shadowed_stability.rs similarity index 100% rename from tests/ui/methods/supertrait-shadowing/auxiliary/shadowed_stability.rs rename to tests/ui/supertrait-shadowing/auxiliary/shadowed_stability.rs diff --git a/tests/ui/supertrait-shadowing/common-ancestor-2.rs b/tests/ui/supertrait-shadowing/common-ancestor-2.rs new file mode 100644 index 0000000000000..908c5d2bf2199 --- /dev/null +++ b/tests/ui/supertrait-shadowing/common-ancestor-2.rs @@ -0,0 +1,59 @@ +//@ run-pass + +#![feature(supertrait_item_shadowing)] +#![feature(min_generic_const_args)] +#![warn(resolving_to_items_shadowing_supertrait_items)] +#![warn(shadowing_supertrait_items)] +#![allow(dead_code)] + +use std::mem::size_of; + +trait A { + fn hello(&self) -> &'static str { + "A" + } + type Assoc; + const CONST: i32; +} +impl A for T { + type Assoc = i8; + const CONST: i32 = 1; +} + +trait B { + fn hello(&self) -> &'static str { + "B" + } + type Assoc; + const CONST: i32; +} +impl B for T { + type Assoc = i16; + const CONST: i32 = 2; +} + +trait C: A + B { + fn hello(&self) -> &'static str { + //~^ WARN trait item `hello` from `C` shadows identically named item + "C" + } + type Assoc; + //~^ WARN trait item `Assoc` from `C` shadows identically named item + type const CONST: i32; + //~^ WARN trait item `CONST` from `C` shadows identically named item +} +impl C for T { + type Assoc = i32; + type const CONST: i32 = 3; +} + +fn main() { + assert_eq!(().hello(), "C"); + //~^ WARN trait item `hello` from `C` shadows identically named item from supertrait + check::<()>(); +} + +fn check() { + assert_eq!(size_of::(), 4); + assert_eq!(T::CONST, 3); +} diff --git a/tests/ui/methods/supertrait-shadowing/common-ancestor-2.stderr b/tests/ui/supertrait-shadowing/common-ancestor-2.stderr similarity index 55% rename from tests/ui/methods/supertrait-shadowing/common-ancestor-2.stderr rename to tests/ui/supertrait-shadowing/common-ancestor-2.stderr index b0f61b46b6911..646726b835041 100644 --- a/tests/ui/methods/supertrait-shadowing/common-ancestor-2.stderr +++ b/tests/ui/supertrait-shadowing/common-ancestor-2.stderr @@ -1,11 +1,11 @@ warning: trait item `hello` from `C` shadows identically named item from supertrait - --> $DIR/common-ancestor-2.rs:23:5 + --> $DIR/common-ancestor-2.rs:36:5 | LL | fn hello(&self) -> &'static str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: items from several supertraits are shadowed: `B` and `A` - --> $DIR/common-ancestor-2.rs:9:5 + --> $DIR/common-ancestor-2.rs:12:5 | LL | fn hello(&self) -> &'static str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -13,24 +13,54 @@ LL | fn hello(&self) -> &'static str { LL | fn hello(&self) -> &'static str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: the lint level is defined here - --> $DIR/common-ancestor-2.rs:5:9 + --> $DIR/common-ancestor-2.rs:6:9 | LL | #![warn(shadowing_supertrait_items)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +warning: trait item `Assoc` from `C` shadows identically named item from supertrait + --> $DIR/common-ancestor-2.rs:40:5 + | +LL | type Assoc; + | ^^^^^^^^^^ + | +note: items from several supertraits are shadowed: `B` and `A` + --> $DIR/common-ancestor-2.rs:15:5 + | +LL | type Assoc; + | ^^^^^^^^^^ +... +LL | type Assoc; + | ^^^^^^^^^^ + +warning: trait item `CONST` from `C` shadows identically named item from supertrait + --> $DIR/common-ancestor-2.rs:42:5 + | +LL | type const CONST: i32; + | ^^^^^^^^^^^^^^^^^^^^^ + | +note: items from several supertraits are shadowed: `B` and `A` + --> $DIR/common-ancestor-2.rs:16:5 + | +LL | const CONST: i32; + | ^^^^^^^^^^^^^^^^ +... +LL | const CONST: i32; + | ^^^^^^^^^^^^^^^^ + warning: trait item `hello` from `C` shadows identically named item from supertrait - --> $DIR/common-ancestor-2.rs:31:19 + --> $DIR/common-ancestor-2.rs:51:19 | LL | assert_eq!(().hello(), "C"); | ^^^^^ | note: item from `C` shadows a supertrait item - --> $DIR/common-ancestor-2.rs:23:5 + --> $DIR/common-ancestor-2.rs:36:5 | LL | fn hello(&self) -> &'static str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: items from several supertraits are shadowed: `A` and `B` - --> $DIR/common-ancestor-2.rs:9:5 + --> $DIR/common-ancestor-2.rs:12:5 | LL | fn hello(&self) -> &'static str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -38,10 +68,10 @@ LL | fn hello(&self) -> &'static str { LL | fn hello(&self) -> &'static str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: the lint level is defined here - --> $DIR/common-ancestor-2.rs:4:9 + --> $DIR/common-ancestor-2.rs:5:9 | LL | #![warn(resolving_to_items_shadowing_supertrait_items)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: 2 warnings emitted +warning: 4 warnings emitted diff --git a/tests/ui/supertrait-shadowing/common-ancestor-3.rs b/tests/ui/supertrait-shadowing/common-ancestor-3.rs new file mode 100644 index 0000000000000..ade23ca88aec2 --- /dev/null +++ b/tests/ui/supertrait-shadowing/common-ancestor-3.rs @@ -0,0 +1,76 @@ +//@ run-pass + +#![feature(supertrait_item_shadowing)] +#![feature(min_generic_const_args)] +#![warn(resolving_to_items_shadowing_supertrait_items)] +#![warn(shadowing_supertrait_items)] +#![allow(dead_code)] + +use std::mem::size_of; + +trait A { + fn hello(&self) -> &'static str { + "A" + } + type Assoc; + const CONST: i32; +} +impl A for T { + type Assoc = i8; + const CONST: i32 = 1; +} + +trait B { + fn hello(&self) -> &'static str { + "B" + } + type Assoc; + const CONST: i32; +} +impl B for T { + type Assoc = i16; + const CONST: i32 = 2; +} + +trait C: A + B { + fn hello(&self) -> &'static str { + //~^ WARN trait item `hello` from `C` shadows identically named item + "C" + } + type Assoc; + //~^ WARN trait item `Assoc` from `C` shadows identically named item + type const CONST: i32; + //~^ WARN trait item `CONST` from `C` shadows identically named item +} +impl C for T { + type Assoc = i32; + type const CONST: i32 = 3; +} + +// `D` extends `C` which extends `B` and `A` + +trait D: C { + fn hello(&self) -> &'static str { + //~^ WARN trait item `hello` from `D` shadows identically named item + "D" + } + type Assoc; + //~^ WARN trait item `Assoc` from `D` shadows identically named item + type const CONST: i32; + //~^ WARN trait item `CONST` from `D` shadows identically named item +} +impl D for T { + type Assoc = i64; + type const CONST: i32 = 4; +} + +fn main() { + assert_eq!(().hello(), "D"); + //~^ WARN trait item `hello` from `D` shadows identically named item from supertrait + check::<()>(); +} + +fn check() { + assert_eq!(size_of::(), 8); + assert_eq!(T::CONST, 4); +} diff --git a/tests/ui/supertrait-shadowing/common-ancestor-3.stderr b/tests/ui/supertrait-shadowing/common-ancestor-3.stderr new file mode 100644 index 0000000000000..62132832da4ba --- /dev/null +++ b/tests/ui/supertrait-shadowing/common-ancestor-3.stderr @@ -0,0 +1,134 @@ +warning: trait item `hello` from `C` shadows identically named item from supertrait + --> $DIR/common-ancestor-3.rs:36:5 + | +LL | fn hello(&self) -> &'static str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: items from several supertraits are shadowed: `B` and `A` + --> $DIR/common-ancestor-3.rs:12:5 + | +LL | fn hello(&self) -> &'static str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | fn hello(&self) -> &'static str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: the lint level is defined here + --> $DIR/common-ancestor-3.rs:6:9 + | +LL | #![warn(shadowing_supertrait_items)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: trait item `Assoc` from `C` shadows identically named item from supertrait + --> $DIR/common-ancestor-3.rs:40:5 + | +LL | type Assoc; + | ^^^^^^^^^^ + | +note: items from several supertraits are shadowed: `B` and `A` + --> $DIR/common-ancestor-3.rs:15:5 + | +LL | type Assoc; + | ^^^^^^^^^^ +... +LL | type Assoc; + | ^^^^^^^^^^ + +warning: trait item `CONST` from `C` shadows identically named item from supertrait + --> $DIR/common-ancestor-3.rs:42:5 + | +LL | type const CONST: i32; + | ^^^^^^^^^^^^^^^^^^^^^ + | +note: items from several supertraits are shadowed: `B` and `A` + --> $DIR/common-ancestor-3.rs:16:5 + | +LL | const CONST: i32; + | ^^^^^^^^^^^^^^^^ +... +LL | const CONST: i32; + | ^^^^^^^^^^^^^^^^ + +warning: trait item `hello` from `D` shadows identically named item from supertrait + --> $DIR/common-ancestor-3.rs:53:5 + | +LL | fn hello(&self) -> &'static str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: items from several supertraits are shadowed: `C`, `B`, and `A` + --> $DIR/common-ancestor-3.rs:12:5 + | +LL | fn hello(&self) -> &'static str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | fn hello(&self) -> &'static str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | fn hello(&self) -> &'static str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: trait item `Assoc` from `D` shadows identically named item from supertrait + --> $DIR/common-ancestor-3.rs:57:5 + | +LL | type Assoc; + | ^^^^^^^^^^ + | +note: items from several supertraits are shadowed: `C`, `B`, and `A` + --> $DIR/common-ancestor-3.rs:15:5 + | +LL | type Assoc; + | ^^^^^^^^^^ +... +LL | type Assoc; + | ^^^^^^^^^^ +... +LL | type Assoc; + | ^^^^^^^^^^ + +warning: trait item `CONST` from `D` shadows identically named item from supertrait + --> $DIR/common-ancestor-3.rs:59:5 + | +LL | type const CONST: i32; + | ^^^^^^^^^^^^^^^^^^^^^ + | +note: items from several supertraits are shadowed: `C`, `B`, and `A` + --> $DIR/common-ancestor-3.rs:16:5 + | +LL | const CONST: i32; + | ^^^^^^^^^^^^^^^^ +... +LL | const CONST: i32; + | ^^^^^^^^^^^^^^^^ +... +LL | type const CONST: i32; + | ^^^^^^^^^^^^^^^^^^^^^ + +warning: trait item `hello` from `D` shadows identically named item from supertrait + --> $DIR/common-ancestor-3.rs:68:19 + | +LL | assert_eq!(().hello(), "D"); + | ^^^^^ + | +note: item from `D` shadows a supertrait item + --> $DIR/common-ancestor-3.rs:53:5 + | +LL | fn hello(&self) -> &'static str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: items from several supertraits are shadowed: `A`, `B`, and `C` + --> $DIR/common-ancestor-3.rs:12:5 + | +LL | fn hello(&self) -> &'static str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | fn hello(&self) -> &'static str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | fn hello(&self) -> &'static str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: the lint level is defined here + --> $DIR/common-ancestor-3.rs:5:9 + | +LL | #![warn(resolving_to_items_shadowing_supertrait_items)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: 7 warnings emitted + diff --git a/tests/ui/supertrait-shadowing/common-ancestor.rs b/tests/ui/supertrait-shadowing/common-ancestor.rs new file mode 100644 index 0000000000000..903f85957870b --- /dev/null +++ b/tests/ui/supertrait-shadowing/common-ancestor.rs @@ -0,0 +1,47 @@ +//@ run-pass + +#![feature(supertrait_item_shadowing)] +#![feature(min_generic_const_args)] +#![warn(resolving_to_items_shadowing_supertrait_items)] +#![warn(shadowing_supertrait_items)] +#![allow(dead_code)] + +use std::mem::size_of; + +trait A { + fn hello(&self) -> &'static str { + "A" + } + type Assoc; + const CONST: i32; +} +impl A for T { + type Assoc = i8; + const CONST: i32 = 1; +} + +trait B: A { + fn hello(&self) -> &'static str { + //~^ WARN trait item `hello` from `B` shadows identically named item + "B" + } + type Assoc; + //~^ WARN trait item `Assoc` from `B` shadows identically named item + type const CONST: i32; + //~^ WARN trait item `CONST` from `B` shadows identically named item +} +impl B for T { + type Assoc = i16; + type const CONST: i32 = 2; +} + +fn main() { + assert_eq!(().hello(), "B"); + //~^ WARN trait item `hello` from `B` shadows identically named item from supertrait + check::<()>(); +} + +fn check() { + assert_eq!(size_of::(), 2); + assert_eq!(T::CONST, 2); +} diff --git a/tests/ui/methods/supertrait-shadowing/common-ancestor.stderr b/tests/ui/supertrait-shadowing/common-ancestor.stderr similarity index 56% rename from tests/ui/methods/supertrait-shadowing/common-ancestor.stderr rename to tests/ui/supertrait-shadowing/common-ancestor.stderr index 9afedeab5e2c2..9b13537cf800c 100644 --- a/tests/ui/methods/supertrait-shadowing/common-ancestor.stderr +++ b/tests/ui/supertrait-shadowing/common-ancestor.stderr @@ -1,41 +1,65 @@ warning: trait item `hello` from `B` shadows identically named item from supertrait - --> $DIR/common-ancestor.rs:16:5 + --> $DIR/common-ancestor.rs:24:5 | LL | fn hello(&self) -> &'static str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: item from `A` is shadowed by a subtrait item - --> $DIR/common-ancestor.rs:9:5 + --> $DIR/common-ancestor.rs:12:5 | LL | fn hello(&self) -> &'static str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: the lint level is defined here - --> $DIR/common-ancestor.rs:5:9 + --> $DIR/common-ancestor.rs:6:9 | LL | #![warn(shadowing_supertrait_items)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +warning: trait item `Assoc` from `B` shadows identically named item from supertrait + --> $DIR/common-ancestor.rs:28:5 + | +LL | type Assoc; + | ^^^^^^^^^^ + | +note: item from `A` is shadowed by a subtrait item + --> $DIR/common-ancestor.rs:15:5 + | +LL | type Assoc; + | ^^^^^^^^^^ + +warning: trait item `CONST` from `B` shadows identically named item from supertrait + --> $DIR/common-ancestor.rs:30:5 + | +LL | type const CONST: i32; + | ^^^^^^^^^^^^^^^^^^^^^ + | +note: item from `A` is shadowed by a subtrait item + --> $DIR/common-ancestor.rs:16:5 + | +LL | const CONST: i32; + | ^^^^^^^^^^^^^^^^ + warning: trait item `hello` from `B` shadows identically named item from supertrait - --> $DIR/common-ancestor.rs:24:19 + --> $DIR/common-ancestor.rs:39:19 | LL | assert_eq!(().hello(), "B"); | ^^^^^ | note: item from `B` shadows a supertrait item - --> $DIR/common-ancestor.rs:16:5 + --> $DIR/common-ancestor.rs:24:5 | LL | fn hello(&self) -> &'static str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: item from `A` is shadowed by a subtrait item - --> $DIR/common-ancestor.rs:9:5 + --> $DIR/common-ancestor.rs:12:5 | LL | fn hello(&self) -> &'static str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: the lint level is defined here - --> $DIR/common-ancestor.rs:4:9 + --> $DIR/common-ancestor.rs:5:9 | LL | #![warn(resolving_to_items_shadowing_supertrait_items)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: 2 warnings emitted +warning: 4 warnings emitted diff --git a/tests/ui/supertrait-shadowing/definition-site.rs b/tests/ui/supertrait-shadowing/definition-site.rs new file mode 100644 index 0000000000000..3309ee97e9f00 --- /dev/null +++ b/tests/ui/supertrait-shadowing/definition-site.rs @@ -0,0 +1,28 @@ +#![feature(supertrait_item_shadowing)] +#![deny(shadowing_supertrait_items)] + +trait SuperSuper { + fn method(); + const CONST: i32; + type Assoc; +} + +trait Super: SuperSuper { + fn method(); + //~^ ERROR trait item `method` from `Super` shadows identically named item + const CONST: i32; + //~^ ERROR trait item `CONST` from `Super` shadows identically named item + type Assoc; + //~^ ERROR trait item `Assoc` from `Super` shadows identically named item +} + +trait Sub: Super { + fn method(); + //~^ ERROR trait item `method` from `Sub` shadows identically named item + const CONST: i32; + //~^ ERROR trait item `CONST` from `Sub` shadows identically named item + type Assoc; + //~^ ERROR trait item `Assoc` from `Sub` shadows identically named item +} + +fn main() {} diff --git a/tests/ui/supertrait-shadowing/definition-site.stderr b/tests/ui/supertrait-shadowing/definition-site.stderr new file mode 100644 index 0000000000000..faf01cc711c6a --- /dev/null +++ b/tests/ui/supertrait-shadowing/definition-site.stderr @@ -0,0 +1,88 @@ +error: trait item `method` from `Super` shadows identically named item from supertrait + --> $DIR/definition-site.rs:11:5 + | +LL | fn method(); + | ^^^^^^^^^^^^ + | +note: item from `SuperSuper` is shadowed by a subtrait item + --> $DIR/definition-site.rs:5:5 + | +LL | fn method(); + | ^^^^^^^^^^^^ +note: the lint level is defined here + --> $DIR/definition-site.rs:2:9 + | +LL | #![deny(shadowing_supertrait_items)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: trait item `CONST` from `Super` shadows identically named item from supertrait + --> $DIR/definition-site.rs:13:5 + | +LL | const CONST: i32; + | ^^^^^^^^^^^^^^^^ + | +note: item from `SuperSuper` is shadowed by a subtrait item + --> $DIR/definition-site.rs:6:5 + | +LL | const CONST: i32; + | ^^^^^^^^^^^^^^^^ + +error: trait item `Assoc` from `Super` shadows identically named item from supertrait + --> $DIR/definition-site.rs:15:5 + | +LL | type Assoc; + | ^^^^^^^^^^ + | +note: item from `SuperSuper` is shadowed by a subtrait item + --> $DIR/definition-site.rs:7:5 + | +LL | type Assoc; + | ^^^^^^^^^^ + +error: trait item `method` from `Sub` shadows identically named item from supertrait + --> $DIR/definition-site.rs:20:5 + | +LL | fn method(); + | ^^^^^^^^^^^^ + | +note: items from several supertraits are shadowed: `Super` and `SuperSuper` + --> $DIR/definition-site.rs:5:5 + | +LL | fn method(); + | ^^^^^^^^^^^^ +... +LL | fn method(); + | ^^^^^^^^^^^^ + +error: trait item `CONST` from `Sub` shadows identically named item from supertrait + --> $DIR/definition-site.rs:22:5 + | +LL | const CONST: i32; + | ^^^^^^^^^^^^^^^^ + | +note: items from several supertraits are shadowed: `Super` and `SuperSuper` + --> $DIR/definition-site.rs:6:5 + | +LL | const CONST: i32; + | ^^^^^^^^^^^^^^^^ +... +LL | const CONST: i32; + | ^^^^^^^^^^^^^^^^ + +error: trait item `Assoc` from `Sub` shadows identically named item from supertrait + --> $DIR/definition-site.rs:24:5 + | +LL | type Assoc; + | ^^^^^^^^^^ + | +note: items from several supertraits are shadowed: `Super` and `SuperSuper` + --> $DIR/definition-site.rs:7:5 + | +LL | type Assoc; + | ^^^^^^^^^^ +... +LL | type Assoc; + | ^^^^^^^^^^ + +error: aborting due to 6 previous errors + diff --git a/tests/ui/methods/supertrait-shadowing/false-subtrait-after-inference.rs b/tests/ui/supertrait-shadowing/false-subtrait-after-inference.rs similarity index 100% rename from tests/ui/methods/supertrait-shadowing/false-subtrait-after-inference.rs rename to tests/ui/supertrait-shadowing/false-subtrait-after-inference.rs diff --git a/tests/ui/methods/supertrait-shadowing/false-subtrait-after-inference.stderr b/tests/ui/supertrait-shadowing/false-subtrait-after-inference.stderr similarity index 100% rename from tests/ui/methods/supertrait-shadowing/false-subtrait-after-inference.stderr rename to tests/ui/supertrait-shadowing/false-subtrait-after-inference.stderr diff --git a/tests/ui/supertrait-shadowing/no-common-ancestor-2.rs b/tests/ui/supertrait-shadowing/no-common-ancestor-2.rs new file mode 100644 index 0000000000000..957aabf5a51f8 --- /dev/null +++ b/tests/ui/supertrait-shadowing/no-common-ancestor-2.rs @@ -0,0 +1,68 @@ +#![feature(supertrait_item_shadowing)] +#![feature(min_generic_const_args)] + +use std::mem::size_of; + +trait A { + fn hello(&self) -> &'static str { + "A" + } + type Assoc; + const CONST: i32; +} +impl A for T { + type Assoc = i8; + const CONST: i32 = 1; +} + +trait B { + fn hello(&self) -> &'static str { + "B" + } + type Assoc; + const CONST: i32; +} +impl B for T { + type Assoc = i16; + const CONST: i32 = 2; +} + +trait C: A + B { + fn hello(&self) -> &'static str { + "C" + } + type Assoc; + type const CONST: i32; +} +impl C for T { + type Assoc = i32; + type const CONST: i32 = 3; +} + +// Since `D` is not a subtrait of `C`, +// we have no obvious lower bound. + +trait D: B { + fn hello(&self) -> &'static str { + "D" + } + type Assoc; + type const CONST: i32; +} +impl D for T { + type Assoc = i64; + type const CONST: i32 = 4; +} + +fn main() { + ().hello(); + //~^ ERROR multiple applicable items in scope + check::<()>(); +} + +fn check() { + let _ = size_of::(); + //~^ ERROR ambiguous associated type `Assoc` in bounds of `T` + let _ = T::CONST; + //~^ ERROR multiple applicable items in scope +} diff --git a/tests/ui/supertrait-shadowing/no-common-ancestor-2.stderr b/tests/ui/supertrait-shadowing/no-common-ancestor-2.stderr new file mode 100644 index 0000000000000..987adcfbbe959 --- /dev/null +++ b/tests/ui/supertrait-shadowing/no-common-ancestor-2.stderr @@ -0,0 +1,131 @@ +error[E0034]: multiple applicable items in scope + --> $DIR/no-common-ancestor-2.rs:58:8 + | +LL | ().hello(); + | ^^^^^ multiple `hello` found + | +note: candidate #1 is defined in an impl of the trait `A` for the type `T` + --> $DIR/no-common-ancestor-2.rs:7:5 + | +LL | fn hello(&self) -> &'static str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: candidate #2 is defined in an impl of the trait `B` for the type `T` + --> $DIR/no-common-ancestor-2.rs:19:5 + | +LL | fn hello(&self) -> &'static str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: candidate #3 is defined in an impl of the trait `C` for the type `T` + --> $DIR/no-common-ancestor-2.rs:31:5 + | +LL | fn hello(&self) -> &'static str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: candidate #4 is defined in an impl of the trait `D` for the type `T` + --> $DIR/no-common-ancestor-2.rs:46:5 + | +LL | fn hello(&self) -> &'static str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: disambiguate the method for candidate #1 + | +LL - ().hello(); +LL + A::hello(&()); + | +help: disambiguate the method for candidate #2 + | +LL - ().hello(); +LL + B::hello(&()); + | +help: disambiguate the method for candidate #3 + | +LL - ().hello(); +LL + C::hello(&()); + | +help: disambiguate the method for candidate #4 + | +LL - ().hello(); +LL + D::hello(&()); + | + +error[E0221]: ambiguous associated type `Assoc` in bounds of `T` + --> $DIR/no-common-ancestor-2.rs:64:23 + | +LL | type Assoc; + | ---------- ambiguous `Assoc` from `A` +... +LL | type Assoc; + | ---------- ambiguous `Assoc` from `B` +... +LL | type Assoc; + | ---------- ambiguous `Assoc` from `C` +... +LL | type Assoc; + | ---------- ambiguous `Assoc` from `D` +... +LL | let _ = size_of::(); + | ^^^^^^^^ ambiguous associated type `Assoc` + | +help: use fully-qualified syntax to disambiguate + | +LL - let _ = size_of::(); +LL + let _ = size_of::<::Assoc>(); + | +help: use fully-qualified syntax to disambiguate + | +LL - let _ = size_of::(); +LL + let _ = size_of::<::Assoc>(); + | +help: use fully-qualified syntax to disambiguate + | +LL - let _ = size_of::(); +LL + let _ = size_of::<::Assoc>(); + | +help: use fully-qualified syntax to disambiguate + | +LL - let _ = size_of::(); +LL + let _ = size_of::<::Assoc>(); + | + +error[E0034]: multiple applicable items in scope + --> $DIR/no-common-ancestor-2.rs:66:16 + | +LL | let _ = T::CONST; + | ^^^^^ multiple `CONST` found + | +note: candidate #1 is defined in the trait `A` + --> $DIR/no-common-ancestor-2.rs:11:5 + | +LL | const CONST: i32; + | ^^^^^^^^^^^^^^^^ +note: candidate #2 is defined in the trait `B` + --> $DIR/no-common-ancestor-2.rs:23:5 + | +LL | const CONST: i32; + | ^^^^^^^^^^^^^^^^ +note: candidate #3 is defined in the trait `C` + --> $DIR/no-common-ancestor-2.rs:35:5 + | +LL | type const CONST: i32; + | ^^^^^^^^^^^^^^^^^^^^^ +note: candidate #4 is defined in the trait `D` + --> $DIR/no-common-ancestor-2.rs:50:5 + | +LL | type const CONST: i32; + | ^^^^^^^^^^^^^^^^^^^^^ +help: use fully-qualified syntax to disambiguate + | +LL - let _ = T::CONST; +LL + let _ = ::CONST; + | +LL - let _ = T::CONST; +LL + let _ = ::CONST; + | +LL - let _ = T::CONST; +LL + let _ = ::CONST; + | +LL - let _ = T::CONST; +LL + let _ = ::CONST; + | + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0034, E0221. +For more information about an error, try `rustc --explain E0034`. diff --git a/tests/ui/supertrait-shadowing/no-common-ancestor.rs b/tests/ui/supertrait-shadowing/no-common-ancestor.rs new file mode 100644 index 0000000000000..73209c015f8f5 --- /dev/null +++ b/tests/ui/supertrait-shadowing/no-common-ancestor.rs @@ -0,0 +1,41 @@ +#![feature(supertrait_item_shadowing)] +#![feature(min_generic_const_args)] + +use std::mem::size_of; + +trait A { + fn hello(&self) -> &'static str { + "A" + } + type Assoc; + const CONST: i32; +} +impl A for T { + type Assoc = i8; + const CONST: i32 = 1; +} + +trait B { + fn hello(&self) -> &'static str { + "B" + } + type Assoc; + const CONST: i32; +} +impl B for T { + type Assoc = i16; + const CONST: i32 = 2; +} + +fn main() { + ().hello(); + //~^ ERROR multiple applicable items in scope + check::<()>(); +} + +fn check() { + let _ = size_of::(); + //~^ ERROR ambiguous associated type `Assoc` in bounds of `T` + let _ = T::CONST; + //~^ ERROR multiple applicable items in scope +} diff --git a/tests/ui/supertrait-shadowing/no-common-ancestor.stderr b/tests/ui/supertrait-shadowing/no-common-ancestor.stderr new file mode 100644 index 0000000000000..78fd8f4670577 --- /dev/null +++ b/tests/ui/supertrait-shadowing/no-common-ancestor.stderr @@ -0,0 +1,79 @@ +error[E0034]: multiple applicable items in scope + --> $DIR/no-common-ancestor.rs:31:8 + | +LL | ().hello(); + | ^^^^^ multiple `hello` found + | +note: candidate #1 is defined in an impl of the trait `A` for the type `T` + --> $DIR/no-common-ancestor.rs:7:5 + | +LL | fn hello(&self) -> &'static str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: candidate #2 is defined in an impl of the trait `B` for the type `T` + --> $DIR/no-common-ancestor.rs:19:5 + | +LL | fn hello(&self) -> &'static str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: disambiguate the method for candidate #1 + | +LL - ().hello(); +LL + A::hello(&()); + | +help: disambiguate the method for candidate #2 + | +LL - ().hello(); +LL + B::hello(&()); + | + +error[E0221]: ambiguous associated type `Assoc` in bounds of `T` + --> $DIR/no-common-ancestor.rs:37:23 + | +LL | type Assoc; + | ---------- ambiguous `Assoc` from `A` +... +LL | type Assoc; + | ---------- ambiguous `Assoc` from `B` +... +LL | let _ = size_of::(); + | ^^^^^^^^ ambiguous associated type `Assoc` + | +help: use fully-qualified syntax to disambiguate + | +LL - let _ = size_of::(); +LL + let _ = size_of::<::Assoc>(); + | +help: use fully-qualified syntax to disambiguate + | +LL - let _ = size_of::(); +LL + let _ = size_of::<::Assoc>(); + | + +error[E0034]: multiple applicable items in scope + --> $DIR/no-common-ancestor.rs:39:16 + | +LL | let _ = T::CONST; + | ^^^^^ multiple `CONST` found + | +note: candidate #1 is defined in the trait `A` + --> $DIR/no-common-ancestor.rs:11:5 + | +LL | const CONST: i32; + | ^^^^^^^^^^^^^^^^ +note: candidate #2 is defined in the trait `B` + --> $DIR/no-common-ancestor.rs:23:5 + | +LL | const CONST: i32; + | ^^^^^^^^^^^^^^^^ +help: use fully-qualified syntax to disambiguate + | +LL - let _ = T::CONST; +LL + let _ = ::CONST; + | +LL - let _ = T::CONST; +LL + let _ = ::CONST; + | + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0034, E0221. +For more information about an error, try `rustc --explain E0034`. diff --git a/tests/ui/supertrait-shadowing/out-of-scope.rs b/tests/ui/supertrait-shadowing/out-of-scope.rs new file mode 100644 index 0000000000000..7b2322843f1c3 --- /dev/null +++ b/tests/ui/supertrait-shadowing/out-of-scope.rs @@ -0,0 +1,42 @@ +//@ run-pass + +#![feature(min_generic_const_args)] +#![allow(dead_code)] + +use std::mem::size_of; + +mod out_of_scope { + pub trait Subtrait: super::Supertrait { + fn hello(&self) -> &'static str { + "subtrait" + } + type Assoc; + type const CONST: i32; + } + impl Subtrait for T { + type Assoc = i16; + type const CONST: i32 = 2; + } +} + +trait Supertrait { + fn hello(&self) -> &'static str { + "supertrait" + } + type Assoc; + const CONST: i32; +} +impl Supertrait for T { + type Assoc = i8; + const CONST: i32 = 1; +} + +fn main() { + assert_eq!(().hello(), "supertrait"); + check::<()>(); +} + +fn check() { + assert_eq!(size_of::(), 1); + assert_eq!(T::CONST, 1); +} diff --git a/tests/ui/methods/supertrait-shadowing/trivially-false-subtrait.rs b/tests/ui/supertrait-shadowing/trivially-false-subtrait.rs similarity index 100% rename from tests/ui/methods/supertrait-shadowing/trivially-false-subtrait.rs rename to tests/ui/supertrait-shadowing/trivially-false-subtrait.rs diff --git a/tests/ui/supertrait-shadowing/type-dependent.rs b/tests/ui/supertrait-shadowing/type-dependent.rs new file mode 100644 index 0000000000000..75272d101dd29 --- /dev/null +++ b/tests/ui/supertrait-shadowing/type-dependent.rs @@ -0,0 +1,51 @@ +//@ run-pass + +// Makes sure we can shadow with type-dependent associated item syntax. + +#![feature(min_generic_const_args)] +#![feature(supertrait_item_shadowing)] +#![allow(dead_code)] + +use std::mem::size_of; + +trait A { + fn hello() -> &'static str { + "A" + } + type Assoc; + const CONST: i32; +} +impl A for T { + type Assoc = i8; + const CONST: i32 = 1; +} + +trait B: A { + fn hello() -> &'static str { + "B" + } + type Assoc; + type const CONST: i32; +} +impl B for T { + type Assoc = i16; + type const CONST: i32 = 2; +} + +fn foo() -> &'static str { + T::hello() +} + +fn assoc() -> usize { + size_of::() +} + +fn konst() -> i32 { + T::CONST +} + +fn main() { + assert_eq!(foo::<()>(), "B"); + assert_eq!(assoc::<()>(), 2); + assert_eq!(konst::<()>(), 2); +} diff --git a/tests/ui/methods/supertrait-shadowing/unstable.off_normal.stderr b/tests/ui/supertrait-shadowing/unstable.off_normal.stderr similarity index 100% rename from tests/ui/methods/supertrait-shadowing/unstable.off_normal.stderr rename to tests/ui/supertrait-shadowing/unstable.off_normal.stderr diff --git a/tests/ui/methods/supertrait-shadowing/unstable.off_shadowing.stderr b/tests/ui/supertrait-shadowing/unstable.off_shadowing.stderr similarity index 100% rename from tests/ui/methods/supertrait-shadowing/unstable.off_shadowing.stderr rename to tests/ui/supertrait-shadowing/unstable.off_shadowing.stderr diff --git a/tests/ui/methods/supertrait-shadowing/unstable.on_normal.stderr b/tests/ui/supertrait-shadowing/unstable.on_normal.stderr similarity index 100% rename from tests/ui/methods/supertrait-shadowing/unstable.on_normal.stderr rename to tests/ui/supertrait-shadowing/unstable.on_normal.stderr diff --git a/tests/ui/methods/supertrait-shadowing/unstable.rs b/tests/ui/supertrait-shadowing/unstable.rs similarity index 100% rename from tests/ui/methods/supertrait-shadowing/unstable.rs rename to tests/ui/supertrait-shadowing/unstable.rs