diff --git a/Cargo.lock b/Cargo.lock index e0c72d6899e..bb37fee98e4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -741,7 +741,7 @@ dependencies = [ "tracing-subscriber", "unified-diff", "walkdir", - "windows 0.46.0", + "windows", ] [[package]] @@ -1647,7 +1647,7 @@ dependencies = [ "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "windows 0.48.0", + "windows", ] [[package]] @@ -3259,7 +3259,7 @@ dependencies = [ "tempfile", "thorin-dwp", "tracing", - "windows 0.46.0", + "windows", ] [[package]] @@ -3315,7 +3315,7 @@ dependencies = [ "tempfile", "thin-vec", "tracing", - "windows 0.46.0", + "windows", ] [[package]] @@ -3376,7 +3376,7 @@ dependencies = [ "rustc_ty_utils", "serde_json", "tracing", - "windows 0.46.0", + "windows", ] [[package]] @@ -3426,7 +3426,7 @@ dependencies = [ "termize", "tracing", "unicode-width", - "windows 0.46.0", + "windows", ] [[package]] @@ -4096,7 +4096,7 @@ dependencies = [ "smallvec", "termize", "tracing", - "windows 0.46.0", + "windows", ] [[package]] @@ -5498,15 +5498,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdacb41e6a96a052c6cb63a144f24900236121c6f63f4f8219fef5977ecb0c25" -dependencies = [ - "windows-targets 0.42.2", -] - [[package]] name = "windows" version = "0.48.0" diff --git a/compiler/rustc_borrowck/src/def_use.rs b/compiler/rustc_borrowck/src/def_use.rs index 74e6ce37e97..b775739fed2 100644 --- a/compiler/rustc_borrowck/src/def_use.rs +++ b/compiler/rustc_borrowck/src/def_use.rs @@ -55,7 +55,7 @@ pub fn categorize(context: PlaceContext) -> Option { // `PlaceMention` and `AscribeUserType` both evaluate the place, which must not // contain dangling references. PlaceContext::NonMutatingUse(NonMutatingUseContext::PlaceMention) | - PlaceContext::NonUse(NonUseContext::AscribeUserTy) | + PlaceContext::NonUse(NonUseContext::AscribeUserTy(_)) | PlaceContext::MutatingUse(MutatingUseContext::AddressOf) | PlaceContext::NonMutatingUse(NonMutatingUseContext::AddressOf) | diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index dcabeb792be..33b24b68f7c 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -777,7 +777,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { Inspect | Copy | Move | PlaceMention | SharedBorrow | ShallowBorrow | UniqueBorrow | AddressOf | Projection, ) => ty::Covariant, - PlaceContext::NonUse(AscribeUserTy) => ty::Covariant, + PlaceContext::NonUse(AscribeUserTy(variance)) => variance, } } diff --git a/compiler/rustc_builtin_macros/src/concat_idents.rs b/compiler/rustc_builtin_macros/src/concat_idents.rs index 8c737f04323..ee56d45c9c8 100644 --- a/compiler/rustc_builtin_macros/src/concat_idents.rs +++ b/compiler/rustc_builtin_macros/src/concat_idents.rs @@ -19,7 +19,7 @@ pub fn expand_concat_idents<'cx>( } let mut res_str = String::new(); - for (i, e) in tts.into_trees().enumerate() { + for (i, e) in tts.trees().enumerate() { if i & 1 == 1 { match e { TokenTree::Token(Token { kind: token::Comma, .. }, _) => {} diff --git a/compiler/rustc_builtin_macros/src/trace_macros.rs b/compiler/rustc_builtin_macros/src/trace_macros.rs index cc5ae6894e6..9c98723e1f4 100644 --- a/compiler/rustc_builtin_macros/src/trace_macros.rs +++ b/compiler/rustc_builtin_macros/src/trace_macros.rs @@ -8,7 +8,7 @@ pub fn expand_trace_macros( sp: Span, tt: TokenStream, ) -> Box { - let mut cursor = tt.into_trees(); + let mut cursor = tt.trees(); let mut err = false; let value = match &cursor.next() { Some(TokenTree::Token(token, _)) if token.is_keyword(kw::True) => true, diff --git a/compiler/rustc_codegen_llvm/src/callee.rs b/compiler/rustc_codegen_llvm/src/callee.rs index 30a0cf1d019..4b9ca2e7d19 100644 --- a/compiler/rustc_codegen_llvm/src/callee.rs +++ b/compiler/rustc_codegen_llvm/src/callee.rs @@ -94,11 +94,11 @@ pub fn get_fn<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, instance: Instance<'tcx>) -> // LLVM will prefix the name with `__imp_`. Ideally, we'd like the // existing logic below to set the Storage Class, but it has an // exemption for MinGW for backwards compatability. - let llfn = cx.declare_fn(&common::i686_decorated_name(&dllimport, common::is_mingw_gnu_toolchain(&tcx.sess.target), true), fn_abi); + let llfn = cx.declare_fn(&common::i686_decorated_name(&dllimport, common::is_mingw_gnu_toolchain(&tcx.sess.target), true), fn_abi, Some(instance)); unsafe { llvm::LLVMSetDLLStorageClass(llfn, llvm::DLLStorageClass::DllImport); } llfn } else { - cx.declare_fn(sym, fn_abi) + cx.declare_fn(sym, fn_abi, Some(instance)) }; debug!("get_fn: not casting pointer!"); diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs index 3dc0ac03312..cd261293e9b 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs @@ -207,6 +207,7 @@ fn declare_unused_fn<'tcx>(cx: &CodegenCx<'_, 'tcx>, def_id: DefId) -> Instance< )), ty::List::empty(), ), + None, ); llvm::set_linkage(llfn, llvm::Linkage::PrivateLinkage); diff --git a/compiler/rustc_codegen_llvm/src/declare.rs b/compiler/rustc_codegen_llvm/src/declare.rs index cc2a5d158be..164b12cf8d4 100644 --- a/compiler/rustc_codegen_llvm/src/declare.rs +++ b/compiler/rustc_codegen_llvm/src/declare.rs @@ -19,8 +19,11 @@ use crate::llvm::AttributePlace::Function; use crate::type_::Type; use crate::value::Value; use rustc_codegen_ssa::traits::TypeMembershipMethods; -use rustc_middle::ty::Ty; -use rustc_symbol_mangling::typeid::{kcfi_typeid_for_fnabi, typeid_for_fnabi, TypeIdOptions}; +use rustc_middle::ty::{Instance, Ty}; +use rustc_symbol_mangling::typeid::{ + kcfi_typeid_for_fnabi, kcfi_typeid_for_instance, typeid_for_fnabi, typeid_for_instance, + TypeIdOptions, +}; use smallvec::SmallVec; /// Declare a function. @@ -116,7 +119,12 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { /// /// If there’s a value with the same name already declared, the function will /// update the declaration and return existing Value instead. - pub fn declare_fn(&self, name: &str, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> &'ll Value { + pub fn declare_fn( + &self, + name: &str, + fn_abi: &FnAbi<'tcx, Ty<'tcx>>, + instance: Option>, + ) -> &'ll Value { debug!("declare_rust_fn(name={:?}, fn_abi={:?})", name, fn_abi); // Function addresses in Rust are never significant, allowing functions to @@ -132,18 +140,35 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { fn_abi.apply_attrs_llfn(self, llfn); if self.tcx.sess.is_sanitizer_cfi_enabled() { - let typeid = typeid_for_fnabi(self.tcx, fn_abi, TypeIdOptions::empty()); - self.set_type_metadata(llfn, typeid); - let typeid = typeid_for_fnabi(self.tcx, fn_abi, TypeIdOptions::GENERALIZE_POINTERS); - self.add_type_metadata(llfn, typeid); - let typeid = typeid_for_fnabi(self.tcx, fn_abi, TypeIdOptions::NORMALIZE_INTEGERS); - self.add_type_metadata(llfn, typeid); - let typeid = typeid_for_fnabi( - self.tcx, - fn_abi, - TypeIdOptions::GENERALIZE_POINTERS | TypeIdOptions::NORMALIZE_INTEGERS, - ); - self.add_type_metadata(llfn, typeid); + if let Some(instance) = instance { + let typeid = typeid_for_instance(self.tcx, &instance, TypeIdOptions::empty()); + self.set_type_metadata(llfn, typeid); + let typeid = + typeid_for_instance(self.tcx, &instance, TypeIdOptions::GENERALIZE_POINTERS); + self.add_type_metadata(llfn, typeid); + let typeid = + typeid_for_instance(self.tcx, &instance, TypeIdOptions::NORMALIZE_INTEGERS); + self.add_type_metadata(llfn, typeid); + let typeid = typeid_for_instance( + self.tcx, + &instance, + TypeIdOptions::GENERALIZE_POINTERS | TypeIdOptions::NORMALIZE_INTEGERS, + ); + self.add_type_metadata(llfn, typeid); + } else { + let typeid = typeid_for_fnabi(self.tcx, fn_abi, TypeIdOptions::empty()); + self.set_type_metadata(llfn, typeid); + let typeid = typeid_for_fnabi(self.tcx, fn_abi, TypeIdOptions::GENERALIZE_POINTERS); + self.add_type_metadata(llfn, typeid); + let typeid = typeid_for_fnabi(self.tcx, fn_abi, TypeIdOptions::NORMALIZE_INTEGERS); + self.add_type_metadata(llfn, typeid); + let typeid = typeid_for_fnabi( + self.tcx, + fn_abi, + TypeIdOptions::GENERALIZE_POINTERS | TypeIdOptions::NORMALIZE_INTEGERS, + ); + self.add_type_metadata(llfn, typeid); + } } if self.tcx.sess.is_sanitizer_kcfi_enabled() { @@ -156,8 +181,13 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { options.insert(TypeIdOptions::NORMALIZE_INTEGERS); } - let kcfi_typeid = kcfi_typeid_for_fnabi(self.tcx, fn_abi, options); - self.set_kcfi_type_metadata(llfn, kcfi_typeid); + if let Some(instance) = instance { + let kcfi_typeid = kcfi_typeid_for_instance(self.tcx, &instance, options); + self.set_kcfi_type_metadata(llfn, kcfi_typeid); + } else { + let kcfi_typeid = kcfi_typeid_for_fnabi(self.tcx, fn_abi, options); + self.set_kcfi_type_metadata(llfn, kcfi_typeid); + } } llfn diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 00d1796f210..4e28034a850 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -772,7 +772,7 @@ fn gen_fn<'ll, 'tcx>( ) -> (&'ll Type, &'ll Value) { let fn_abi = cx.fn_abi_of_fn_ptr(rust_fn_sig, ty::List::empty()); let llty = fn_abi.llvm_type(cx); - let llfn = cx.declare_fn(name, fn_abi); + let llfn = cx.declare_fn(name, fn_abi, None); cx.set_frame_pointer_type(llfn); cx.apply_target_cpu_attr(llfn); // FIXME(eddyb) find a nicer way to do this. diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 61365e6dc4b..aefd5b2a13c 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -680,7 +680,9 @@ pub type InlineAsmDiagHandlerTy = unsafe extern "C" fn(&SMDiagnostic, *const c_v pub mod coverageinfo { use super::coverage_map; - /// Aligns with [llvm::coverage::CounterMappingRegion::RegionKind](https://github.com/rust-lang/llvm-project/blob/rustc/13.0-2021-09-30/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h#L209-L230) + /// Corresponds to enum `llvm::coverage::CounterMappingRegion::RegionKind`. + /// + /// Must match the layout of `LLVMRustCounterMappingRegionKind`. #[derive(Copy, Clone, Debug)] #[repr(C)] pub enum RegionKind { @@ -714,7 +716,9 @@ pub mod coverageinfo { /// array", encoded separately), and source location (start and end positions of the represented /// code region). /// - /// Matches LLVMRustCounterMappingRegion. + /// Corresponds to struct `llvm::coverage::CounterMappingRegion`. + /// + /// Must match the layout of `LLVMRustCounterMappingRegion`. #[derive(Copy, Clone, Debug)] #[repr(C)] pub struct CounterMappingRegion { diff --git a/compiler/rustc_codegen_llvm/src/mono_item.rs b/compiler/rustc_codegen_llvm/src/mono_item.rs index 59bdc60830f..e8f8c321510 100644 --- a/compiler/rustc_codegen_llvm/src/mono_item.rs +++ b/compiler/rustc_codegen_llvm/src/mono_item.rs @@ -51,7 +51,7 @@ impl<'tcx> PreDefineMethods<'tcx> for CodegenCx<'_, 'tcx> { assert!(!instance.substs.has_infer()); let fn_abi = self.fn_abi_of_instance(instance, ty::List::empty()); - let lldecl = self.declare_fn(symbol_name, fn_abi); + let lldecl = self.declare_fn(symbol_name, fn_abi, Some(instance)); unsafe { llvm::LLVMRustSetLinkage(lldecl, base::linkage_to_llvm(linkage)) }; let attrs = self.tcx.codegen_fn_attrs(instance.def_id()); base::set_link_section(lldecl, attrs); diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml index 4f73b731f5a..02be88df103 100644 --- a/compiler/rustc_codegen_ssa/Cargo.toml +++ b/compiler/rustc_codegen_ssa/Cargo.toml @@ -51,5 +51,5 @@ default-features = false features = ["read_core", "elf", "macho", "pe", "unaligned", "archive", "write"] [target.'cfg(windows)'.dependencies.windows] -version = "0.46.0" +version = "0.48.0" features = ["Win32_Globalization"] diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 8a7809a1468..ea06cb02d8b 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -546,10 +546,36 @@ fn link_staticlib<'a>( ab.build(out_filename); - if !all_native_libs.is_empty() { - if sess.opts.prints.contains(&PrintRequest::NativeStaticLibs) { - print_native_static_libs(sess, &all_native_libs); + let crates = codegen_results.crate_info.used_crates.iter(); + + let fmts = codegen_results + .crate_info + .dependency_formats + .iter() + .find_map(|&(ty, ref list)| if ty == CrateType::Staticlib { Some(list) } else { None }) + .expect("no dependency formats for staticlib"); + + let mut all_rust_dylibs = vec![]; + for &cnum in crates { + match fmts.get(cnum.as_usize() - 1) { + Some(&Linkage::Dynamic) => {} + _ => continue, } + let crate_name = codegen_results.crate_info.crate_name[&cnum]; + let used_crate_source = &codegen_results.crate_info.used_crate_source[&cnum]; + if let Some((path, _)) = &used_crate_source.dylib { + all_rust_dylibs.push(&**path); + } else { + if used_crate_source.rmeta.is_some() { + sess.emit_fatal(errors::LinkRlibError::OnlyRmetaFound { crate_name }); + } else { + sess.emit_fatal(errors::LinkRlibError::NotFound { crate_name }); + } + } + } + + if sess.opts.prints.contains(&PrintRequest::NativeStaticLibs) { + print_native_static_libs(sess, &all_native_libs, &all_rust_dylibs); } Ok(()) @@ -1370,8 +1396,12 @@ enum RlibFlavor { StaticlibBase, } -fn print_native_static_libs(sess: &Session, all_native_libs: &[NativeLib]) { - let lib_args: Vec<_> = all_native_libs +fn print_native_static_libs( + sess: &Session, + all_native_libs: &[NativeLib], + all_rust_dylibs: &[&Path], +) { + let mut lib_args: Vec<_> = all_native_libs .iter() .filter(|l| relevant_lib(sess, l)) .filter_map(|lib| { @@ -1401,6 +1431,41 @@ fn print_native_static_libs(sess: &Session, all_native_libs: &[NativeLib]) { } }) .collect(); + for path in all_rust_dylibs { + // FIXME deduplicate with add_dynamic_crate + + // Just need to tell the linker about where the library lives and + // what its name is + let parent = path.parent(); + if let Some(dir) = parent { + let dir = fix_windows_verbatim_for_gcc(dir); + if sess.target.is_like_msvc { + let mut arg = String::from("/LIBPATH:"); + arg.push_str(&dir.display().to_string()); + lib_args.push(arg); + } else { + lib_args.push("-L".to_owned()); + lib_args.push(dir.display().to_string()); + } + } + let stem = path.file_stem().unwrap().to_str().unwrap(); + // Convert library file-stem into a cc -l argument. + let prefix = if stem.starts_with("lib") && !sess.target.is_like_windows { 3 } else { 0 }; + let lib = &stem[prefix..]; + let path = parent.unwrap_or_else(|| Path::new("")); + if sess.target.is_like_msvc { + // When producing a dll, the MSVC linker may not actually emit a + // `foo.lib` file if the dll doesn't actually export any symbols, so we + // check to see if the file is there and just omit linking to it if it's + // not present. + let name = format!("{}.dll.lib", lib); + if path.join(&name).exists() { + lib_args.push(name); + } + } else { + lib_args.push(format!("-l{}", lib)); + } + } if !lib_args.is_empty() { sess.emit_note(errors::StaticLibraryNativeArtifacts); // Prefix for greppability diff --git a/compiler/rustc_codegen_ssa/src/coverageinfo/ffi.rs b/compiler/rustc_codegen_ssa/src/coverageinfo/ffi.rs index e288760a02b..1791ce4b315 100644 --- a/compiler/rustc_codegen_ssa/src/coverageinfo/ffi.rs +++ b/compiler/rustc_codegen_ssa/src/coverageinfo/ffi.rs @@ -1,6 +1,6 @@ use rustc_middle::mir::coverage::{CounterValueReference, MappedExpressionIndex}; -/// Aligns with [llvm::coverage::Counter::CounterKind](https://github.com/rust-lang/llvm-project/blob/rustc/13.0-2021-09-30/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h#L95) +/// Must match the layout of `LLVMRustCounterKind`. #[derive(Copy, Clone, Debug)] #[repr(C)] pub enum CounterKind { @@ -17,8 +17,10 @@ pub enum CounterKind { /// `instrprof.increment()`) /// * For `CounterKind::Expression`, `id` is the index into the coverage map's array of /// counter expressions. -/// Aligns with [llvm::coverage::Counter](https://github.com/rust-lang/llvm-project/blob/rustc/13.0-2021-09-30/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h#L102-L103) -/// Important: The Rust struct layout (order and types of fields) must match its C++ counterpart. +/// +/// Corresponds to struct `llvm::coverage::Counter`. +/// +/// Must match the layout of `LLVMRustCounter`. #[derive(Copy, Clone, Debug)] #[repr(C)] pub struct Counter { @@ -59,7 +61,9 @@ impl Counter { } } -/// Aligns with [llvm::coverage::CounterExpression::ExprKind](https://github.com/rust-lang/llvm-project/blob/rustc/13.0-2021-09-30/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h#L150) +/// Corresponds to enum `llvm::coverage::CounterExpression::ExprKind`. +/// +/// Must match the layout of `LLVMRustCounterExprKind`. #[derive(Copy, Clone, Debug)] #[repr(C)] pub enum ExprKind { @@ -67,9 +71,9 @@ pub enum ExprKind { Add = 1, } -/// Aligns with [llvm::coverage::CounterExpression](https://github.com/rust-lang/llvm-project/blob/rustc/13.0-2021-09-30/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h#L151-L152) -/// Important: The Rust struct layout (order and types of fields) must match its C++ -/// counterpart. +/// Corresponds to struct `llvm::coverage::CounterExpression`. +/// +/// Must match the layout of `LLVMRustCounterExpression`. #[derive(Copy, Clone, Debug)] #[repr(C)] pub struct CounterExpression { diff --git a/compiler/rustc_data_structures/Cargo.toml b/compiler/rustc_data_structures/Cargo.toml index 5e05fe463ed..c815bb2d197 100644 --- a/compiler/rustc_data_structures/Cargo.toml +++ b/compiler/rustc_data_structures/Cargo.toml @@ -37,7 +37,7 @@ itertools = "0.10.1" version = "0.11" [target.'cfg(windows)'.dependencies.windows] -version = "0.46.0" +version = "0.48.0" features = [ "Win32_Foundation", "Win32_Storage_FileSystem", diff --git a/compiler/rustc_data_structures/src/profiling.rs b/compiler/rustc_data_structures/src/profiling.rs index 5e13e7c8aaf..3c76c2b7991 100644 --- a/compiler/rustc_data_structures/src/profiling.rs +++ b/compiler/rustc_data_structures/src/profiling.rs @@ -865,14 +865,16 @@ cfg_if! { use std::mem; use windows::{ - Win32::System::ProcessStatus::{K32GetProcessMemoryInfo, PROCESS_MEMORY_COUNTERS}, + // FIXME: change back to K32GetProcessMemoryInfo when windows crate + // updated to 0.49.0+ to drop dependency on psapi.dll + Win32::System::ProcessStatus::{GetProcessMemoryInfo, PROCESS_MEMORY_COUNTERS}, Win32::System::Threading::GetCurrentProcess, }; let mut pmc = PROCESS_MEMORY_COUNTERS::default(); let pmc_size = mem::size_of_val(&pmc); unsafe { - K32GetProcessMemoryInfo( + GetProcessMemoryInfo( GetCurrentProcess(), &mut pmc, pmc_size as u32, diff --git a/compiler/rustc_driver_impl/Cargo.toml b/compiler/rustc_driver_impl/Cargo.toml index d7d97fcc3e7..67352c55c90 100644 --- a/compiler/rustc_driver_impl/Cargo.toml +++ b/compiler/rustc_driver_impl/Cargo.toml @@ -57,7 +57,7 @@ rustc_mir_transform = { path = "../rustc_mir_transform" } libc = "0.2" [target.'cfg(windows)'.dependencies.windows] -version = "0.46.0" +version = "0.48.0" features = [ "Win32_System_Diagnostics_Debug", ] diff --git a/compiler/rustc_errors/Cargo.toml b/compiler/rustc_errors/Cargo.toml index 46ace8eb2dd..bd3033fcb3e 100644 --- a/compiler/rustc_errors/Cargo.toml +++ b/compiler/rustc_errors/Cargo.toml @@ -27,12 +27,11 @@ serde = { version = "1.0.125", features = [ "derive" ] } serde_json = "1.0.59" [target.'cfg(windows)'.dependencies.windows] -version = "0.46.0" +version = "0.48.0" features = [ "Win32_Foundation", "Win32_Security", "Win32_System_Threading", - "Win32_System_WindowsProgramming", ] [features] diff --git a/compiler/rustc_errors/src/lock.rs b/compiler/rustc_errors/src/lock.rs index 7db262abfde..bd5cf49b56b 100644 --- a/compiler/rustc_errors/src/lock.rs +++ b/compiler/rustc_errors/src/lock.rs @@ -19,8 +19,7 @@ pub fn acquire_global_lock(name: &str) -> Box { use windows::{ core::PCSTR, Win32::Foundation::{CloseHandle, HANDLE, WAIT_ABANDONED, WAIT_OBJECT_0}, - Win32::System::Threading::{CreateMutexA, ReleaseMutex, WaitForSingleObject}, - Win32::System::WindowsProgramming::INFINITE, + Win32::System::Threading::{CreateMutexA, ReleaseMutex, WaitForSingleObject, INFINITE}, }; struct Handle(HANDLE); diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index c4d4e0d6d78..5187e63f8e3 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -31,6 +31,7 @@ use rustc_target::abi::FieldIdx; use rustc_target::spec::abi::Abi; use rustc_trait_selection::traits::error_reporting::on_unimplemented::OnUnimplementedDirective; use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _; +use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _; use rustc_trait_selection::traits::{self, ObligationCtxt, TraitEngine, TraitEngineExt as _}; use std::ops::ControlFlow; @@ -222,7 +223,7 @@ fn check_opaque(tcx: TyCtxt<'_>, id: hir::ItemId) { if check_opaque_for_cycles(tcx, item.owner_id.def_id, substs, span, &origin).is_err() { return; } - check_opaque_meets_bounds(tcx, item.owner_id.def_id, substs, span, &origin); + check_opaque_meets_bounds(tcx, item.owner_id.def_id, span, &origin); } /// Checks that an opaque type does not use `Self` or `T::Foo` projections that would result @@ -391,7 +392,6 @@ pub(super) fn check_opaque_for_cycles<'tcx>( fn check_opaque_meets_bounds<'tcx>( tcx: TyCtxt<'tcx>, def_id: LocalDefId, - substs: SubstsRef<'tcx>, span: Span, origin: &hir::OpaqueTyOrigin, ) { @@ -406,6 +406,8 @@ fn check_opaque_meets_bounds<'tcx>( .with_opaque_type_inference(DefiningAnchor::Bind(defining_use_anchor)) .build(); let ocx = ObligationCtxt::new(&infcx); + + let substs = InternalSubsts::identity_for_item(tcx, def_id.to_def_id()); let opaque_ty = tcx.mk_opaque(def_id.to_def_id(), substs); // `ReErased` regions appear in the "parent_substs" of closures/generators. @@ -448,9 +450,18 @@ fn check_opaque_meets_bounds<'tcx>( match origin { // Checked when type checking the function containing them. hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..) => {} + // Nested opaque types occur only in associated types: + // ` type Opaque = impl Trait<&'static T, AssocTy = impl Nested>; ` + // They can only be referenced as ` as Trait<&'static T>>::AssocTy`. + // We don't have to check them here because their well-formedness follows from the WF of + // the projection input types in the defining- and use-sites. + hir::OpaqueTyOrigin::TyAlias + if tcx.def_kind(tcx.parent(def_id.to_def_id())) == DefKind::OpaqueTy => {} // Can have different predicates to their defining use hir::OpaqueTyOrigin::TyAlias => { - let outlives_env = OutlivesEnvironment::new(param_env); + let wf_tys = ocx.assumed_wf_types(param_env, span, def_id); + let implied_bounds = infcx.implied_bounds_tys(param_env, def_id, wf_tys); + let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds); let _ = ocx.resolve_regions_and_report_errors(defining_use_anchor, &outlives_env); } } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs index 05e5d850bf9..3efdab53438 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs @@ -278,9 +278,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span: Span, ) -> bool { if let traits::FulfillmentErrorCode::CodeSelectionError( - traits::SelectionError::OutputTypeParameterMismatch(_, expected, _), + traits::SelectionError::OutputTypeParameterMismatch(box traits::SelectionOutputTypeParameterMismatch{ + expected_trait_ref, .. + }), ) = error.code - && let ty::Closure(def_id, _) | ty::Generator(def_id, ..) = expected.skip_binder().self_ty().kind() + && let ty::Closure(def_id, _) | ty::Generator(def_id, ..) = expected_trait_ref.skip_binder().self_ty().kind() && span.overlaps(self.tcx.def_span(*def_id)) { true diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index a2a4362e2f5..dcc323493f4 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -2,6 +2,7 @@ #![feature(let_chains)] #![feature(try_blocks)] #![feature(never_type)] +#![feature(box_patterns)] #![feature(min_specialization)] #![feature(control_flow_enum)] #![feature(drain_filter)] diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index a89b9931599..9a95a9c8375 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -1530,7 +1530,7 @@ impl<'tcx> InferCtxt<'tcx> { // variables let tcx = self.tcx; if substs.has_non_region_infer() { - if let Some(ct) = tcx.bound_abstract_const(unevaluated.def)? { + if let Some(ct) = tcx.thir_abstract_const(unevaluated.def)? { let ct = tcx.expand_abstract_consts(ct.subst(tcx, substs)); if let Err(e) = ct.error_reported() { return Err(ErrorHandled::Reported(e)); diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index 9d9f4ee13f4..51354c2b127 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -173,12 +173,21 @@ pub fn parse_check_cfg(specs: Vec) -> CheckCfg { let expected_values = check_cfg .expecteds .entry(ident.name.to_string()) + .and_modify(|expected_values| match expected_values { + ExpectedValues::Some(_) => {} + ExpectedValues::Any => { + // handle the case where names(...) was done + // before values by changing to a list + *expected_values = + ExpectedValues::Some(FxHashSet::default()); + } + }) .or_insert_with(|| { ExpectedValues::Some(FxHashSet::default()) }); let ExpectedValues::Some(expected_values) = expected_values else { - bug!("shoudn't be possible") + bug!("`expected_values` should be a list a values") }; for val in values { diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 3025cce7ba7..b1c45eaf601 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -1882,8 +1882,8 @@ declare_lint_pass!( struct UnderMacro(bool); impl KeywordIdents { - fn check_tokens(&mut self, cx: &EarlyContext<'_>, tokens: TokenStream) { - for tt in tokens.into_trees() { + fn check_tokens(&mut self, cx: &EarlyContext<'_>, tokens: &TokenStream) { + for tt in tokens.trees() { match tt { // Only report non-raw idents. TokenTree::Token(token, _) => { @@ -1944,10 +1944,10 @@ impl KeywordIdents { impl EarlyLintPass for KeywordIdents { fn check_mac_def(&mut self, cx: &EarlyContext<'_>, mac_def: &ast::MacroDef) { - self.check_tokens(cx, mac_def.body.tokens.clone()); + self.check_tokens(cx, &mac_def.body.tokens); } fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &ast::MacCall) { - self.check_tokens(cx, mac.args.tokens.clone()); + self.check_tokens(cx, &mac.args.tokens); } fn check_ident(&mut self, cx: &EarlyContext<'_>, ident: Ident) { self.check_ident_token(cx, UnderMacro(false), ident); diff --git a/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp index 03e6d2149e9..87906dee4d3 100644 --- a/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp @@ -8,18 +8,100 @@ using namespace llvm; +// FFI equivalent of enum `llvm::coverage::Counter::CounterKind` +// https://github.com/rust-lang/llvm-project/blob/ea6fa9c2/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h#L97-L99 +enum class LLVMRustCounterKind { + Zero = 0, + CounterValueReference = 1, + Expression = 2, +}; + +// FFI equivalent of struct `llvm::coverage::Counter` +// https://github.com/rust-lang/llvm-project/blob/ea6fa9c2/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h#L94-L149 +struct LLVMRustCounter { + LLVMRustCounterKind CounterKind; + uint32_t ID; +}; + +static coverage::Counter fromRust(LLVMRustCounter Counter) { + switch (Counter.CounterKind) { + case LLVMRustCounterKind::Zero: + return coverage::Counter::getZero(); + case LLVMRustCounterKind::CounterValueReference: + return coverage::Counter::getCounter(Counter.ID); + case LLVMRustCounterKind::Expression: + return coverage::Counter::getExpression(Counter.ID); + } + report_fatal_error("Bad LLVMRustCounterKind!"); +} + +// FFI equivalent of enum `llvm::coverage::CounterMappingRegion::RegionKind` +// https://github.com/rust-lang/llvm-project/blob/ea6fa9c2/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h#L213-L234 +enum class LLVMRustCounterMappingRegionKind { + CodeRegion = 0, + ExpansionRegion = 1, + SkippedRegion = 2, + GapRegion = 3, + BranchRegion = 4, +}; + +static coverage::CounterMappingRegion::RegionKind +fromRust(LLVMRustCounterMappingRegionKind Kind) { + switch (Kind) { + case LLVMRustCounterMappingRegionKind::CodeRegion: + return coverage::CounterMappingRegion::CodeRegion; + case LLVMRustCounterMappingRegionKind::ExpansionRegion: + return coverage::CounterMappingRegion::ExpansionRegion; + case LLVMRustCounterMappingRegionKind::SkippedRegion: + return coverage::CounterMappingRegion::SkippedRegion; + case LLVMRustCounterMappingRegionKind::GapRegion: + return coverage::CounterMappingRegion::GapRegion; + case LLVMRustCounterMappingRegionKind::BranchRegion: + return coverage::CounterMappingRegion::BranchRegion; + } + report_fatal_error("Bad LLVMRustCounterMappingRegionKind!"); +} + +// FFI equivalent of struct `llvm::coverage::CounterMappingRegion` +// https://github.com/rust-lang/llvm-project/blob/ea6fa9c2/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h#L211-L304 struct LLVMRustCounterMappingRegion { - coverage::Counter Count; - coverage::Counter FalseCount; + LLVMRustCounter Count; + LLVMRustCounter FalseCount; uint32_t FileID; uint32_t ExpandedFileID; uint32_t LineStart; uint32_t ColumnStart; uint32_t LineEnd; uint32_t ColumnEnd; - coverage::CounterMappingRegion::RegionKind Kind; + LLVMRustCounterMappingRegionKind Kind; }; +// FFI equivalent of enum `llvm::coverage::CounterExpression::ExprKind` +// https://github.com/rust-lang/llvm-project/blob/ea6fa9c2/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h#L154 +enum class LLVMRustCounterExprKind { + Subtract = 0, + Add = 1, +}; + +// FFI equivalent of struct `llvm::coverage::CounterExpression` +// https://github.com/rust-lang/llvm-project/blob/ea6fa9c2/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h#L151-L160 +struct LLVMRustCounterExpression { + LLVMRustCounterExprKind Kind; + LLVMRustCounter LHS; + LLVMRustCounter RHS; +}; + +static coverage::CounterExpression::ExprKind +fromRust(LLVMRustCounterExprKind Kind) { + switch (Kind) { + case LLVMRustCounterExprKind::Subtract: + return coverage::CounterExpression::Subtract; + case LLVMRustCounterExprKind::Add: + return coverage::CounterExpression::Add; + } + report_fatal_error("Bad LLVMRustCounterExprKind!"); +} + extern "C" void LLVMRustCoverageWriteFilenamesSectionToBuffer( const char* const Filenames[], size_t FilenamesLen, @@ -37,9 +119,9 @@ extern "C" void LLVMRustCoverageWriteFilenamesSectionToBuffer( extern "C" void LLVMRustCoverageWriteMappingToBuffer( const unsigned *VirtualFileMappingIDs, unsigned NumVirtualFileMappingIDs, - const coverage::CounterExpression *Expressions, + const LLVMRustCounterExpression *RustExpressions, unsigned NumExpressions, - LLVMRustCounterMappingRegion *RustMappingRegions, + const LLVMRustCounterMappingRegion *RustMappingRegions, unsigned NumMappingRegions, RustStringRef BufferOut) { // Convert from FFI representation to LLVM representation. @@ -48,13 +130,24 @@ extern "C" void LLVMRustCoverageWriteMappingToBuffer( for (const auto &Region : ArrayRef( RustMappingRegions, NumMappingRegions)) { MappingRegions.emplace_back( - Region.Count, Region.FalseCount, Region.FileID, Region.ExpandedFileID, + fromRust(Region.Count), fromRust(Region.FalseCount), + Region.FileID, Region.ExpandedFileID, Region.LineStart, Region.ColumnStart, Region.LineEnd, Region.ColumnEnd, - Region.Kind); + fromRust(Region.Kind)); } + + std::vector Expressions; + Expressions.reserve(NumExpressions); + for (const auto &Expression : + ArrayRef(RustExpressions, NumExpressions)) { + Expressions.emplace_back(fromRust(Expression.Kind), + fromRust(Expression.LHS), + fromRust(Expression.RHS)); + } + auto CoverageMappingWriter = coverage::CoverageMappingWriter( ArrayRef(VirtualFileMappingIDs, NumVirtualFileMappingIDs), - ArrayRef(Expressions, NumExpressions), + Expressions, MappingRegions); RawRustStringOstream OS(BufferOut); CoverageMappingWriter.write(OS); diff --git a/compiler/rustc_metadata/src/dependency_format.rs b/compiler/rustc_metadata/src/dependency_format.rs index 39ef4276faf..72b208a7132 100644 --- a/compiler/rustc_metadata/src/dependency_format.rs +++ b/compiler/rustc_metadata/src/dependency_format.rs @@ -89,11 +89,25 @@ fn calculate_type(tcx: TyCtxt<'_>, ty: CrateType) -> DependencyList { // to try to eagerly statically link all dependencies. This is normally // done for end-product dylibs, not intermediate products. // - // Treat cdylibs similarly. If `-C prefer-dynamic` is set, the caller may - // be code-size conscious, but without it, it makes sense to statically - // link a cdylib. - CrateType::Dylib | CrateType::Cdylib if !sess.opts.cg.prefer_dynamic => Linkage::Static, - CrateType::Dylib | CrateType::Cdylib => Linkage::Dynamic, + // Treat cdylibs and staticlibs similarly. If `-C prefer-dynamic` is set, + // the caller may be code-size conscious, but without it, it makes sense + // to statically link a cdylib or staticlib. For staticlibs we use + // `-Z staticlib-prefer-dynamic` for now. This may be merged into + // `-C prefer-dynamic` in the future. + CrateType::Dylib | CrateType::Cdylib => { + if sess.opts.cg.prefer_dynamic { + Linkage::Dynamic + } else { + Linkage::Static + } + } + CrateType::Staticlib => { + if sess.opts.unstable_opts.staticlib_prefer_dynamic { + Linkage::Dynamic + } else { + Linkage::Static + } + } // If the global prefer_dynamic switch is turned off, or the final // executable will be statically linked, prefer static crate linkage. @@ -108,9 +122,6 @@ fn calculate_type(tcx: TyCtxt<'_>, ty: CrateType) -> DependencyList { // No linkage happens with rlibs, we just needed the metadata (which we // got long ago), so don't bother with anything. CrateType::Rlib => Linkage::NotLinked, - - // staticlibs must have all static dependencies. - CrateType::Staticlib => Linkage::Static, }; match preferred_linkage { @@ -123,9 +134,9 @@ fn calculate_type(tcx: TyCtxt<'_>, ty: CrateType) -> DependencyList { return v; } - // Staticlibs and static executables must have all static dependencies. + // Static executables must have all static dependencies. // If any are not found, generate some nice pretty errors. - if ty == CrateType::Staticlib + if (ty == CrateType::Staticlib && !sess.opts.unstable_opts.staticlib_allow_rdylib_deps) || (ty == CrateType::Executable && sess.crt_static(Some(ty)) && !sess.target.crt_static_allows_dylibs) diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 82c66b9dfb9..29cf432b8f9 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -862,6 +862,11 @@ fn should_encode_attrs(def_kind: DefKind) -> bool { | DefKind::Macro(_) | DefKind::Field | DefKind::Impl { .. } => true, + // Tools may want to be able to detect their tool lints on + // closures from upstream crates, too. This is used by + // https://github.com/model-checking/kani and is not a performance + // or maintenance issue for us. + DefKind::Closure => true, DefKind::TyParam | DefKind::ConstParam | DefKind::Ctor(..) @@ -874,7 +879,6 @@ fn should_encode_attrs(def_kind: DefKind) -> bool { | DefKind::ImplTraitPlaceholder | DefKind::LifetimeParam | DefKind::GlobalAsm - | DefKind::Closure | DefKind::Generator => false, } } diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 84f6b7f934d..e2f6acb186b 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -394,7 +394,7 @@ define_tables! { mir_for_ctfe: Table>>, mir_generator_witnesses: Table>>, promoted_mir: Table>>>, - thir_abstract_const: Table>>, + thir_abstract_const: Table>>>, impl_parent: Table, impl_polarity: Table, constness: Table, diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index 6718605ed0b..4b7014e3109 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -64,7 +64,7 @@ use crate::mir::*; use crate::ty::subst::SubstsRef; -use crate::ty::{CanonicalUserTypeAnnotation, Ty}; +use crate::ty::{self, CanonicalUserTypeAnnotation, Ty}; use rustc_span::Span; macro_rules! make_mir_visitor { @@ -782,12 +782,12 @@ macro_rules! make_mir_visitor { fn super_ascribe_user_ty(&mut self, place: & $($mutability)? Place<'tcx>, - _variance: $(& $mutability)? ty::Variance, + variance: $(& $mutability)? ty::Variance, user_ty: & $($mutability)? UserTypeProjection, location: Location) { self.visit_place( place, - PlaceContext::NonUse(NonUseContext::AscribeUserTy), + PlaceContext::NonUse(NonUseContext::AscribeUserTy($(* &$mutability *)? variance)), location ); self.visit_user_type_projection(user_ty); @@ -1320,7 +1320,7 @@ pub enum NonUseContext { /// Ending a storage live range. StorageDead, /// User type annotation assertions for NLL. - AscribeUserTy, + AscribeUserTy(ty::Variance), /// The data of a user variable, for debug info. VarDebugInfo, } diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs index 7d9aea02289..28a9c1eef1a 100644 --- a/compiler/rustc_middle/src/query/erase.rs +++ b/compiler/rustc_middle/src/query/erase.rs @@ -82,9 +82,10 @@ impl EraseType for Result>, rustc_errors::ErrorGuarantee [u8; size_of::>, rustc_errors::ErrorGuaranteed>>()]; } -impl EraseType for Result>, rustc_errors::ErrorGuaranteed> { - type Result = - [u8; size_of::>, rustc_errors::ErrorGuaranteed>>()]; +impl EraseType for Result>>, rustc_errors::ErrorGuaranteed> { + type Result = [u8; size_of::< + Result>>, rustc_errors::ErrorGuaranteed>, + >()]; } impl EraseType for Result, traits::query::NoSolution> { diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index d5b185e45d6..9fad2816b0d 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -402,7 +402,7 @@ rustc_queries! { /// Try to build an abstract representation of the given constant. query thir_abstract_const( key: DefId - ) -> Result>, ErrorGuaranteed> { + ) -> Result>>, ErrorGuaranteed> { desc { |tcx| "building an abstract representation for `{}`", tcx.def_path_str(key), } @@ -1016,7 +1016,7 @@ rustc_queries! { desc { "converting literal to mir constant" } } - query check_match(key: LocalDefId) { + query check_match(key: LocalDefId) -> Result<(), rustc_errors::ErrorGuaranteed> { desc { |tcx| "match-checking `{}`", tcx.def_path_str(key) } cache_on_disk_if { true } } diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index 8366567c2c3..449c453555e 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -580,11 +580,7 @@ pub enum SelectionError<'tcx> { /// After a closure impl has selected, its "outputs" were evaluated /// (which for closures includes the "input" type params) and they /// didn't resolve. See `confirm_poly_trait_refs` for more. - OutputTypeParameterMismatch( - ty::PolyTraitRef<'tcx>, - ty::PolyTraitRef<'tcx>, - ty::error::TypeError<'tcx>, - ), + OutputTypeParameterMismatch(Box>), /// The trait pointed by `DefId` is not object safe. TraitNotObjectSafe(DefId), /// A given constant couldn't be evaluated. @@ -596,6 +592,13 @@ pub enum SelectionError<'tcx> { ErrorReporting, } +#[derive(Clone, Debug, TypeVisitable, Lift)] +pub struct SelectionOutputTypeParameterMismatch<'tcx> { + pub found_trait_ref: ty::PolyTraitRef<'tcx>, + pub expected_trait_ref: ty::PolyTraitRef<'tcx>, + pub terr: ty::error::TypeError<'tcx>, +} + /// When performing resolution, it is typically the case that there /// can be one of three outcomes: /// diff --git a/compiler/rustc_middle/src/ty/abstract_const.rs b/compiler/rustc_middle/src/ty/abstract_const.rs index bfb740ab356..972c417cbba 100644 --- a/compiler/rustc_middle/src/ty/abstract_const.rs +++ b/compiler/rustc_middle/src/ty/abstract_const.rs @@ -4,7 +4,6 @@ use crate::ty::{ TypeVisitableExt, }; use rustc_errors::ErrorGuaranteed; -use rustc_hir::def_id::DefId; #[derive(Hash, Debug, Clone, Copy, Ord, PartialOrd, PartialEq, Eq)] #[derive(TyDecodable, TyEncodable, HashStable, TypeVisitable, TypeFoldable)] @@ -35,12 +34,6 @@ TrivialTypeTraversalAndLiftImpls! { pub type BoundAbstractConst<'tcx> = Result>>, ErrorGuaranteed>; impl<'tcx> TyCtxt<'tcx> { - /// Returns a const without substs applied - pub fn bound_abstract_const(self, uv: DefId) -> BoundAbstractConst<'tcx> { - let ac = self.thir_abstract_const(uv); - Ok(ac?.map(|ac| EarlyBinder(ac))) - } - pub fn expand_abstract_consts>>(self, ac: T) -> T { struct Expander<'tcx> { tcx: TyCtxt<'tcx>, @@ -59,7 +52,7 @@ impl<'tcx> TyCtxt<'tcx> { } fn fold_const(&mut self, c: Const<'tcx>) -> Const<'tcx> { let ct = match c.kind() { - ty::ConstKind::Unevaluated(uv) => match self.tcx.bound_abstract_const(uv.def) { + ty::ConstKind::Unevaluated(uv) => match self.tcx.thir_abstract_const(uv.def) { Err(e) => self.tcx.const_error_with_guaranteed(c.ty(), e), Ok(Some(bac)) => { let substs = self.tcx.erase_regions(uv.substs); diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 8d0737e1eee..488d83b5f67 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -2366,13 +2366,11 @@ impl<'tcx> Ty<'tcx> { ty::Adt(def, _substs) => def.sized_constraint(tcx).0.is_empty(), - ty::Alias(..) | ty::Param(_) => false, + ty::Alias(..) | ty::Param(_) | ty::Placeholder(..) => false, ty::Infer(ty::TyVar(_)) => false, - ty::Bound(..) - | ty::Placeholder(..) - | ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { + ty::Bound(..) | ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { bug!("`is_trivially_sized` applied to unexpected type: {:?}", self) } } diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index e5b2d342452..bcb51db9bcf 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -1253,7 +1253,7 @@ pub enum ExplicitSelf<'tcx> { impl<'tcx> ExplicitSelf<'tcx> { /// Categorizes an explicit self declaration like `self: SomeType` - /// into either `self`, `&self`, `&mut self`, `Box`, or + /// into either `self`, `&self`, `&mut self`, `Box`, or /// `Other`. /// This is mainly used to require the arbitrary_self_types feature /// in the case of `Other`, to improve error messages in the common cases, diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs index 3f006765a71..20d381eddb1 100644 --- a/compiler/rustc_mir_build/src/build/mod.rs +++ b/compiler/rustc_mir_build/src/build/mod.rs @@ -42,7 +42,9 @@ fn mir_build(tcx: TyCtxt<'_>, def: LocalDefId) -> Body<'_> { // Ensure unsafeck and abstract const building is ran before we steal the THIR. tcx.ensure_with_value().thir_check_unsafety(def); tcx.ensure_with_value().thir_abstract_const(def); - tcx.ensure_with_value().check_match(def); + if let Err(e) = tcx.check_match(def) { + return construct_error(tcx, def, e); + } let body = match tcx.thir_body(def) { Err(error_reported) => construct_error(tcx, def, error_reported), diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index 8cb3b00c9ad..ca25f83e643 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -26,8 +26,8 @@ use rustc_session::Session; use rustc_span::hygiene::DesugaringKind; use rustc_span::Span; -pub(crate) fn check_match(tcx: TyCtxt<'_>, def_id: LocalDefId) { - let Ok((thir, expr)) = tcx.thir_body(def_id) else { return }; +pub(crate) fn check_match(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), ErrorGuaranteed> { + let (thir, expr) = tcx.thir_body(def_id)?; let thir = thir.borrow(); let pattern_arena = TypedArena::default(); let mut visitor = MatchVisitor { @@ -37,13 +37,16 @@ pub(crate) fn check_match(tcx: TyCtxt<'_>, def_id: LocalDefId) { lint_level: tcx.hir().local_def_id_to_hir_id(def_id), let_source: LetSource::None, pattern_arena: &pattern_arena, + error: Ok(()), }; visitor.visit_expr(&thir[expr]); + for param in thir.params.iter() { if let Some(box ref pattern) = param.pat { visitor.check_irrefutable(pattern, "function argument", None); } } + visitor.error } fn create_e0004( @@ -77,6 +80,7 @@ struct MatchVisitor<'a, 'p, 'tcx> { lint_level: HirId, let_source: LetSource, pattern_arena: &'p TypedArena>, + error: Result<(), ErrorGuaranteed>, } impl<'a, 'tcx> Visitor<'a, 'tcx> for MatchVisitor<'a, '_, 'tcx> { @@ -276,9 +280,9 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { let [pat_field] = &subpatterns[..] else { bug!() }; self.check_irrefutable(&pat_field.pattern, "`for` loop binding", None); } else { - non_exhaustive_match( + self.error = Err(non_exhaustive_match( &cx, self.thir, scrut_ty, scrut.span, witnesses, arms, expr_span, - ); + )); } } } @@ -406,7 +410,7 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { } #[instrument(level = "trace", skip(self))] - fn check_irrefutable(&self, pat: &Pat<'tcx>, origin: &str, sp: Option) { + fn check_irrefutable(&mut self, pat: &Pat<'tcx>, origin: &str, sp: Option) { let mut cx = self.new_cx(self.lint_level, false); let pattern = self.lower_pattern(&mut cx, pat); @@ -475,7 +479,7 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { AdtDefinedHere { adt_def_span, ty, variants } }; - self.tcx.sess.emit_err(PatternNotCovered { + self.error = Err(self.tcx.sess.emit_err(PatternNotCovered { span: pat.span, origin, uncovered: Uncovered::new(pat.span, &cx, witnesses), @@ -486,7 +490,7 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { let_suggestion, misc_suggestion, adt_defined_here, - }); + })); } } @@ -628,7 +632,7 @@ fn non_exhaustive_match<'p, 'tcx>( witnesses: Vec>, arms: &[ArmId], expr_span: Span, -) { +) -> ErrorGuaranteed { let is_empty_match = arms.is_empty(); let non_empty_enum = match scrut_ty.kind() { ty::Adt(def, _) => def.is_enum() && !def.variants().is_empty(), @@ -640,13 +644,12 @@ fn non_exhaustive_match<'p, 'tcx>( let pattern; let patterns_len; if is_empty_match && !non_empty_enum { - cx.tcx.sess.emit_err(NonExhaustivePatternsTypeNotEmpty { + return cx.tcx.sess.emit_err(NonExhaustivePatternsTypeNotEmpty { cx, expr_span, span: sp, ty: scrut_ty, }); - return; } else { // FIXME: migration of this diagnostic will require list support let joined_patterns = joined_uncovered_patterns(cx, &witnesses); @@ -797,7 +800,7 @@ fn non_exhaustive_match<'p, 'tcx>( } else { err.help(msg); } - err.emit(); + err.emit() } pub(crate) fn joined_uncovered_patterns<'p, 'tcx>( diff --git a/compiler/rustc_mir_dataflow/src/framework/lattice.rs b/compiler/rustc_mir_dataflow/src/framework/lattice.rs index af44a4329bf..3952f44ad48 100644 --- a/compiler/rustc_mir_dataflow/src/framework/lattice.rs +++ b/compiler/rustc_mir_dataflow/src/framework/lattice.rs @@ -75,12 +75,12 @@ pub trait MeetSemiLattice: Eq { /// A set that has a "bottom" element, which is less than or equal to any other element. pub trait HasBottom { - fn bottom() -> Self; + const BOTTOM: Self; } /// A set that has a "top" element, which is greater than or equal to any other element. pub trait HasTop { - fn top() -> Self; + const TOP: Self; } /// A `bool` is a "two-point" lattice with `true` as the top element and `false` as the bottom: @@ -113,15 +113,11 @@ impl MeetSemiLattice for bool { } impl HasBottom for bool { - fn bottom() -> Self { - false - } + const BOTTOM: Self = false; } impl HasTop for bool { - fn top() -> Self { - true - } + const TOP: Self = true; } /// A tuple (or list) of lattices is itself a lattice whose least upper bound is the concatenation @@ -274,13 +270,9 @@ impl MeetSemiLattice for FlatSet { } impl HasBottom for FlatSet { - fn bottom() -> Self { - Self::Bottom - } + const BOTTOM: Self = Self::Bottom; } impl HasTop for FlatSet { - fn top() -> Self { - Self::Top - } + const TOP: Self = Self::Top; } diff --git a/compiler/rustc_mir_dataflow/src/value_analysis.rs b/compiler/rustc_mir_dataflow/src/value_analysis.rs index fdff1ec788d..b74d06e5ae8 100644 --- a/compiler/rustc_mir_dataflow/src/value_analysis.rs +++ b/compiler/rustc_mir_dataflow/src/value_analysis.rs @@ -32,9 +32,12 @@ //! Because of that, we can assume that the only way to change the value behind a tracked place is //! by direct assignment. +use std::collections::VecDeque; use std::fmt::{Debug, Formatter}; +use std::ops::Range; use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_index::bit_set::BitSet; use rustc_index::{IndexSlice, IndexVec}; use rustc_middle::mir::visit::{MutatingUseContext, PlaceContext, Visitor}; @@ -65,8 +68,8 @@ pub trait ValueAnalysis<'tcx> { StatementKind::Assign(box (place, rvalue)) => { self.handle_assign(*place, rvalue, state); } - StatementKind::SetDiscriminant { box ref place, .. } => { - state.flood_discr(place.as_ref(), self.map()); + StatementKind::SetDiscriminant { box place, variant_index } => { + self.handle_set_discriminant(*place, *variant_index, state); } StatementKind::Intrinsic(box intrinsic) => { self.handle_intrinsic(intrinsic, state); @@ -74,11 +77,11 @@ pub trait ValueAnalysis<'tcx> { StatementKind::StorageLive(local) | StatementKind::StorageDead(local) => { // StorageLive leaves the local in an uninitialized state. // StorageDead makes it UB to access the local afterwards. - state.flood_with(Place::from(*local).as_ref(), self.map(), Self::Value::bottom()); + state.flood_with(Place::from(*local).as_ref(), self.map(), Self::Value::BOTTOM); } StatementKind::Deinit(box place) => { // Deinit makes the place uninitialized. - state.flood_with(place.as_ref(), self.map(), Self::Value::bottom()); + state.flood_with(place.as_ref(), self.map(), Self::Value::BOTTOM); } StatementKind::Retag(..) => { // We don't track references. @@ -92,6 +95,24 @@ pub trait ValueAnalysis<'tcx> { } } + fn handle_set_discriminant( + &self, + place: Place<'tcx>, + variant_index: VariantIdx, + state: &mut State, + ) { + self.super_set_discriminant(place, variant_index, state) + } + + fn super_set_discriminant( + &self, + place: Place<'tcx>, + _variant_index: VariantIdx, + state: &mut State, + ) { + state.flood_discr(place.as_ref(), self.map()); + } + fn handle_intrinsic( &self, intrinsic: &NonDivergingIntrinsic<'tcx>, @@ -103,16 +124,18 @@ pub trait ValueAnalysis<'tcx> { fn super_intrinsic( &self, intrinsic: &NonDivergingIntrinsic<'tcx>, - state: &mut State, + _state: &mut State, ) { match intrinsic { NonDivergingIntrinsic::Assume(..) => { // Could use this, but ignoring it is sound. } - NonDivergingIntrinsic::CopyNonOverlapping(CopyNonOverlapping { dst, .. }) => { - if let Some(place) = dst.place() { - state.flood(place.as_ref(), self.map()); - } + NonDivergingIntrinsic::CopyNonOverlapping(CopyNonOverlapping { + dst: _, + src: _, + count: _, + }) => { + // This statement represents `*dst = *src`, `count` times. } } } @@ -154,7 +177,7 @@ pub trait ValueAnalysis<'tcx> { Rvalue::CopyForDeref(place) => self.handle_operand(&Operand::Copy(*place), state), Rvalue::Ref(..) | Rvalue::AddressOf(..) => { // We don't track such places. - ValueOrPlace::top() + ValueOrPlace::TOP } Rvalue::Repeat(..) | Rvalue::ThreadLocalRef(..) @@ -168,7 +191,7 @@ pub trait ValueAnalysis<'tcx> { | Rvalue::Aggregate(..) | Rvalue::ShallowInitBox(..) => { // No modification is possible through these r-values. - ValueOrPlace::top() + ValueOrPlace::TOP } } } @@ -196,7 +219,7 @@ pub trait ValueAnalysis<'tcx> { self.map() .find(place.as_ref()) .map(ValueOrPlace::Place) - .unwrap_or(ValueOrPlace::top()) + .unwrap_or(ValueOrPlace::TOP) } } } @@ -214,7 +237,7 @@ pub trait ValueAnalysis<'tcx> { _constant: &Constant<'tcx>, _state: &mut State, ) -> Self::Value { - Self::Value::top() + Self::Value::TOP } /// The effect of a successful function call return should not be @@ -229,7 +252,7 @@ pub trait ValueAnalysis<'tcx> { // Effect is applied by `handle_call_return`. } TerminatorKind::Drop { place, .. } => { - state.flood_with(place.as_ref(), self.map(), Self::Value::bottom()); + state.flood_with(place.as_ref(), self.map(), Self::Value::BOTTOM); } TerminatorKind::Yield { .. } => { // They would have an effect, but are not allowed in this phase. @@ -307,7 +330,7 @@ impl<'tcx, T: ValueAnalysis<'tcx>> AnalysisDomain<'tcx> for ValueAnalysisWrapper fn initialize_start_block(&self, body: &Body<'tcx>, state: &mut Self::Domain) { // The initial state maps all tracked places of argument projections to ⊤ and the rest to ⊥. assert!(matches!(state.0, StateData::Unreachable)); - let values = IndexVec::from_elem_n(T::Value::bottom(), self.0.map().value_count); + let values = IndexVec::from_elem_n(T::Value::BOTTOM, self.0.map().value_count); *state = State(StateData::Reachable(values)); for arg in body.args_iter() { state.flood(PlaceRef { local: arg, projection: &[] }, self.0.map()); @@ -437,7 +460,7 @@ impl State { } pub fn flood_all(&mut self) { - self.flood_all_with(V::top()) + self.flood_all_with(V::TOP) } pub fn flood_all_with(&mut self, value: V) { @@ -447,28 +470,24 @@ impl State { pub fn flood_with(&mut self, place: PlaceRef<'_>, map: &Map, value: V) { let StateData::Reachable(values) = &mut self.0 else { return }; - map.for_each_aliasing_place(place, None, &mut |place| { - if let Some(vi) = map.places[place].value_index { - values[vi] = value.clone(); - } + map.for_each_aliasing_place(place, None, &mut |vi| { + values[vi] = value.clone(); }); } pub fn flood(&mut self, place: PlaceRef<'_>, map: &Map) { - self.flood_with(place, map, V::top()) + self.flood_with(place, map, V::TOP) } pub fn flood_discr_with(&mut self, place: PlaceRef<'_>, map: &Map, value: V) { let StateData::Reachable(values) = &mut self.0 else { return }; - map.for_each_aliasing_place(place, Some(TrackElem::Discriminant), &mut |place| { - if let Some(vi) = map.places[place].value_index { - values[vi] = value.clone(); - } + map.for_each_aliasing_place(place, Some(TrackElem::Discriminant), &mut |vi| { + values[vi] = value.clone(); }); } pub fn flood_discr(&mut self, place: PlaceRef<'_>, map: &Map) { - self.flood_discr_with(place, map, V::top()) + self.flood_discr_with(place, map, V::TOP) } /// Low-level method that assigns to a place. @@ -538,14 +557,14 @@ impl State { /// Retrieve the value stored for a place, or ⊤ if it is not tracked. pub fn get(&self, place: PlaceRef<'_>, map: &Map) -> V { - map.find(place).map(|place| self.get_idx(place, map)).unwrap_or(V::top()) + map.find(place).map(|place| self.get_idx(place, map)).unwrap_or(V::TOP) } /// Retrieve the value stored for a place, or ⊤ if it is not tracked. pub fn get_discr(&self, place: PlaceRef<'_>, map: &Map) -> V { match map.find_discr(place) { Some(place) => self.get_idx(place, map), - None => V::top(), + None => V::TOP, } } @@ -553,11 +572,11 @@ impl State { pub fn get_idx(&self, place: PlaceIndex, map: &Map) -> V { match &self.0 { StateData::Reachable(values) => { - map.places[place].value_index.map(|v| values[v].clone()).unwrap_or(V::top()) + map.places[place].value_index.map(|v| values[v].clone()).unwrap_or(V::TOP) } StateData::Unreachable => { // Because this is unreachable, we can return any value we want. - V::bottom() + V::BOTTOM } } } @@ -588,6 +607,9 @@ pub struct Map { projections: FxHashMap<(PlaceIndex, TrackElem), PlaceIndex>, places: IndexVec, value_count: usize, + // The Range corresponds to a slice into `inner_values_buffer`. + inner_values: IndexVec>, + inner_values_buffer: Vec, } impl Map { @@ -597,6 +619,8 @@ impl Map { projections: FxHashMap::default(), places: IndexVec::new(), value_count: 0, + inner_values: IndexVec::new(), + inner_values_buffer: Vec::new(), } } @@ -608,12 +632,12 @@ impl Map { pub fn from_filter<'tcx>( tcx: TyCtxt<'tcx>, body: &Body<'tcx>, - filter: impl FnMut(Ty<'tcx>) -> bool, - place_limit: Option, + filter: impl Fn(Ty<'tcx>) -> bool, + value_limit: Option, ) -> Self { let mut map = Self::new(); let exclude = excluded_locals(body); - map.register_with_filter(tcx, body, filter, exclude, place_limit); + map.register_with_filter(tcx, body, filter, exclude, value_limit); debug!("registered {} places ({} nodes in total)", map.value_count, map.places.len()); map } @@ -623,51 +647,90 @@ impl Map { &mut self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>, - mut filter: impl FnMut(Ty<'tcx>) -> bool, + filter: impl Fn(Ty<'tcx>) -> bool, exclude: BitSet, - place_limit: Option, + value_limit: Option, ) { - // We use this vector as stack, pushing and popping projections. - let mut projection = Vec::new(); + let mut worklist = VecDeque::with_capacity(value_limit.unwrap_or(body.local_decls.len())); + + // Start by constructing the places for each bare local. + self.locals = IndexVec::from_elem(None, &body.local_decls); for (local, decl) in body.local_decls.iter_enumerated() { - if !exclude.contains(local) { - self.register_with_filter_rec( - tcx, - local, - &mut projection, - decl.ty, - &mut filter, - place_limit, - ); + if exclude.contains(local) { + continue; + } + + // Create a place for the local. + debug_assert!(self.locals[local].is_none()); + let place = self.places.push(PlaceInfo::new(None)); + self.locals[local] = Some(place); + + // And push the eventual children places to the worklist. + self.register_children(tcx, place, decl.ty, &filter, &mut worklist); + } + + // `place.elem1.elem2` with type `ty`. + // `elem1` is either `Some(Variant(i))` or `None`. + while let Some((mut place, elem1, elem2, ty)) = worklist.pop_front() { + // The user requires a bound on the number of created values. + if let Some(value_limit) = value_limit && self.value_count >= value_limit { + break + } + + // Create a place for this projection. + for elem in [elem1, Some(elem2)].into_iter().flatten() { + place = *self.projections.entry((place, elem)).or_insert_with(|| { + // Prepend new child to the linked list. + let next = self.places.push(PlaceInfo::new(Some(elem))); + self.places[next].next_sibling = self.places[place].first_child; + self.places[place].first_child = Some(next); + next + }); + } + + // And push the eventual children places to the worklist. + self.register_children(tcx, place, ty, &filter, &mut worklist); + } + + // Pre-compute the tree of ValueIndex nested in each PlaceIndex. + // `inner_values_buffer[inner_values[place]]` is the set of all the values + // reachable by projecting `place`. + self.inner_values_buffer = Vec::with_capacity(self.value_count); + self.inner_values = IndexVec::from_elem(0..0, &self.places); + for local in body.local_decls.indices() { + if let Some(place) = self.locals[local] { + self.cache_preorder_invoke(place); } } + + // Trim useless places. + for opt_place in self.locals.iter_mut() { + if let Some(place) = *opt_place && self.inner_values[place].is_empty() { + *opt_place = None; + } + } + #[allow(rustc::potential_query_instability)] + self.projections.retain(|_, child| !self.inner_values[*child].is_empty()); } /// Potentially register the (local, projection) place and its fields, recursively. /// /// Invariant: The projection must only contain trackable elements. - fn register_with_filter_rec<'tcx>( + fn register_children<'tcx>( &mut self, tcx: TyCtxt<'tcx>, - local: Local, - projection: &mut Vec>, + place: PlaceIndex, ty: Ty<'tcx>, - filter: &mut impl FnMut(Ty<'tcx>) -> bool, - place_limit: Option, + filter: &impl Fn(Ty<'tcx>) -> bool, + worklist: &mut VecDeque<(PlaceIndex, Option, TrackElem, Ty<'tcx>)>, ) { - if let Some(place_limit) = place_limit && self.value_count >= place_limit { - return - } - - // We know that the projection only contains trackable elements. - let place = self.make_place(local, projection).unwrap(); - // Allocate a value slot if it doesn't have one, and the user requested one. if self.places[place].value_index.is_none() && filter(ty) { self.places[place].value_index = Some(self.value_count.into()); self.value_count += 1; } + // For enums, directly create the `Discriminant`, as that's their main use. if ty.is_enum() { let discr_ty = ty.discriminant_ty(tcx); if filter(discr_ty) { @@ -692,46 +755,32 @@ impl Map { // Recurse with all fields of this place. iter_fields(ty, tcx, ty::ParamEnv::reveal_all(), |variant, field, ty| { - if let Some(variant) = variant { - projection.push(PlaceElem::Downcast(None, variant)); - let _ = self.make_place(local, projection); - projection.push(PlaceElem::Field(field, ty)); - self.register_with_filter_rec(tcx, local, projection, ty, filter, place_limit); - projection.pop(); - projection.pop(); - return; - } - projection.push(PlaceElem::Field(field, ty)); - self.register_with_filter_rec(tcx, local, projection, ty, filter, place_limit); - projection.pop(); + worklist.push_back(( + place, + variant.map(TrackElem::Variant), + TrackElem::Field(field), + ty, + )) }); } - /// Tries to add the place to the map, without allocating a value slot. - /// - /// Can fail if the projection contains non-trackable elements. - fn make_place<'tcx>( - &mut self, - local: Local, - projection: &[PlaceElem<'tcx>], - ) -> Result { - // Get the base index of the local. - let mut index = - *self.locals.get_or_insert_with(local, || self.places.push(PlaceInfo::new(None))); - - // Apply the projection. - for &elem in projection { - let elem = elem.try_into()?; - index = *self.projections.entry((index, elem)).or_insert_with(|| { - // Prepend new child to the linked list. - let next = self.places.push(PlaceInfo::new(Some(elem))); - self.places[next].next_sibling = self.places[index].first_child; - self.places[index].first_child = Some(next); - next - }); + /// Precompute the list of values inside `root` and store it inside + /// as a slice within `inner_values_buffer`. + fn cache_preorder_invoke(&mut self, root: PlaceIndex) { + let start = self.inner_values_buffer.len(); + if let Some(vi) = self.places[root].value_index { + self.inner_values_buffer.push(vi); } - Ok(index) + // We manually iterate instead of using `children` as we need to mutate `self`. + let mut next_child = self.places[root].first_child; + while let Some(child) = next_child { + ensure_sufficient_stack(|| self.cache_preorder_invoke(child)); + next_child = self.places[child].next_sibling; + } + + let end = self.inner_values_buffer.len(); + self.inner_values[root] = start..end; } /// Returns the number of tracked places, i.e., those for which a value can be stored. @@ -750,7 +799,7 @@ impl Map { place: PlaceRef<'_>, extra: impl IntoIterator, ) -> Option { - let mut index = *self.locals.get(place.local)?.as_ref()?; + let mut index = *self.locals[place.local].as_ref()?; for &elem in place.projection { index = self.apply(index, elem.try_into().ok()?)?; @@ -784,17 +833,17 @@ impl Map { /// /// `tail_elem` allows to support discriminants that are not a place in MIR, but that we track /// as such. - pub fn for_each_aliasing_place( + fn for_each_aliasing_place( &self, place: PlaceRef<'_>, tail_elem: Option, - f: &mut impl FnMut(PlaceIndex), + f: &mut impl FnMut(ValueIndex), ) { - if place.is_indirect() { + if place.has_deref() { // We do not track indirect places. return; } - let Some(&Some(mut index)) = self.locals.get(place.local) else { + let Some(mut index) = self.locals[place.local] else { // The local is not tracked at all, so it does not alias anything. return; }; @@ -805,7 +854,9 @@ impl Map { .chain(tail_elem.map(Ok).into_iter()); for elem in elems { // A field aliases the parent place. - f(index); + if let Some(vi) = self.places[index].value_index { + f(vi); + } let Ok(elem) = elem else { return }; let sub = self.apply(index, elem); @@ -819,7 +870,7 @@ impl Map { return; } } - self.preorder_invoke(index, f); + self.for_each_value_inside(index, f); } /// Invoke the given function on all the descendants of the given place, except one branch. @@ -827,7 +878,7 @@ impl Map { &self, parent: PlaceIndex, preserved_child: Option, - f: &mut impl FnMut(PlaceIndex), + f: &mut impl FnMut(ValueIndex), ) { for sibling in self.children(parent) { let elem = self.places[sibling].proj_elem; @@ -837,16 +888,17 @@ impl Map { // Only invalidate the other variants, the current one is fine. && Some(sibling) != preserved_child { - self.preorder_invoke(sibling, f); + self.for_each_value_inside(sibling, f); } } } - /// Invoke a function on the given place and all descendants. - fn preorder_invoke(&self, root: PlaceIndex, f: &mut impl FnMut(PlaceIndex)) { - f(root); - for child in self.children(root) { - self.preorder_invoke(child, f); + /// Invoke a function on each value in the given place and all descendants. + fn for_each_value_inside(&self, root: PlaceIndex, f: &mut impl FnMut(ValueIndex)) { + let range = self.inner_values[root].clone(); + let values = &self.inner_values_buffer[range]; + for &v in values { + f(v) } } } @@ -909,9 +961,7 @@ pub enum ValueOrPlace { } impl ValueOrPlace { - pub fn top() -> Self { - ValueOrPlace::Value(V::top()) - } + pub const TOP: Self = ValueOrPlace::Value(V::TOP); } /// The set of projection elements that can be used by a tracked place. diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs index 254b704f9fc..7adfc9dff2a 100644 --- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs +++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs @@ -79,22 +79,22 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> { &self.map } - fn handle_statement(&self, statement: &Statement<'tcx>, state: &mut State) { - match statement.kind { - StatementKind::SetDiscriminant { box ref place, variant_index } => { - state.flood_discr(place.as_ref(), &self.map); - if self.map.find_discr(place.as_ref()).is_some() { - let enum_ty = place.ty(self.local_decls, self.tcx).ty; - if let Some(discr) = self.eval_discriminant(enum_ty, variant_index) { - state.assign_discr( - place.as_ref(), - ValueOrPlace::Value(FlatSet::Elem(discr)), - &self.map, - ); - } - } + fn handle_set_discriminant( + &self, + place: Place<'tcx>, + variant_index: VariantIdx, + state: &mut State, + ) { + state.flood_discr(place.as_ref(), &self.map); + if self.map.find_discr(place.as_ref()).is_some() { + let enum_ty = place.ty(self.local_decls, self.tcx).ty; + if let Some(discr) = self.eval_discriminant(enum_ty, variant_index) { + state.assign_discr( + place.as_ref(), + ValueOrPlace::Value(FlatSet::Elem(discr)), + &self.map, + ); } - _ => self.super_statement(statement, state), } } @@ -208,8 +208,8 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> { _ => unreachable!(), } .map(|result| ValueOrPlace::Value(self.wrap_immediate(result, *ty))) - .unwrap_or(ValueOrPlace::top()), - _ => ValueOrPlace::top(), + .unwrap_or(ValueOrPlace::TOP), + _ => ValueOrPlace::TOP, }, Rvalue::BinaryOp(op, box (left, right)) => { // Overflows must be ignored here. diff --git a/compiler/rustc_mir_transform/src/ref_prop.rs b/compiler/rustc_mir_transform/src/ref_prop.rs index dafd2ae23a6..d1bc9ee9153 100644 --- a/compiler/rustc_mir_transform/src/ref_prop.rs +++ b/compiler/rustc_mir_transform/src/ref_prop.rs @@ -85,7 +85,9 @@ fn propagate_ssa<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let ssa = SsaLocals::new(body); let mut replacer = compute_replacement(tcx, body, &ssa); - debug!(?replacer.targets, ?replacer.allowed_replacements, ?replacer.storage_to_remove); + debug!(?replacer.targets); + debug!(?replacer.allowed_replacements); + debug!(?replacer.storage_to_remove); replacer.visit_body_preserves_cfg(body); @@ -190,8 +192,11 @@ fn compute_replacement<'tcx>( continue; } + // Whether the current local is subject to the uniqueness rule. + let needs_unique = ty.is_mutable_ptr(); + // If this a mutable reference that we cannot fully replace, mark it as unknown. - if ty.is_mutable_ptr() && !fully_replacable_locals.contains(local) { + if needs_unique && !fully_replacable_locals.contains(local) { debug!("not fully replaceable"); continue; } @@ -203,13 +208,14 @@ fn compute_replacement<'tcx>( // have been visited before. Rvalue::Use(Operand::Copy(place) | Operand::Move(place)) | Rvalue::CopyForDeref(place) => { - if let Some(rhs) = place.as_local() { + if let Some(rhs) = place.as_local() && ssa.is_ssa(rhs) { let target = targets[rhs]; - if matches!(target, Value::Pointer(..)) { + // Only see through immutable reference and pointers, as we do not know yet if + // mutable references are fully replaced. + if !needs_unique && matches!(target, Value::Pointer(..)) { targets[local] = target; - } else if ssa.is_ssa(rhs) { - let refmut = body.local_decls[rhs].ty.is_mutable_ptr(); - targets[local] = Value::Pointer(tcx.mk_place_deref(rhs.into()), refmut); + } else { + targets[local] = Value::Pointer(tcx.mk_place_deref(rhs.into()), needs_unique); } } } @@ -217,10 +223,10 @@ fn compute_replacement<'tcx>( let mut place = *place; // Try to see through `place` in order to collapse reborrow chains. if place.projection.first() == Some(&PlaceElem::Deref) - && let Value::Pointer(target, refmut) = targets[place.local] + && let Value::Pointer(target, inner_needs_unique) = targets[place.local] // Only see through immutable reference and pointers, as we do not know yet if // mutable references are fully replaced. - && !refmut + && !inner_needs_unique // Only collapse chain if the pointee is definitely live. && can_perform_opt(target, location) { @@ -228,7 +234,7 @@ fn compute_replacement<'tcx>( } assert_ne!(place.local, local); if is_constant_place(place) { - targets[local] = Value::Pointer(place, ty.is_mutable_ptr()); + targets[local] = Value::Pointer(place, needs_unique); } } // We do not know what to do, so keep as not-a-pointer. @@ -276,16 +282,35 @@ fn compute_replacement<'tcx>( return; } - if let Value::Pointer(target, refmut) = self.targets[place.local] - && place.projection.first() == Some(&PlaceElem::Deref) - { - let perform_opt = (self.can_perform_opt)(target, loc); - if perform_opt { - self.allowed_replacements.insert((target.local, loc)); - } else if refmut { - // This mutable reference is not fully replacable, so drop it. - self.targets[place.local] = Value::Unknown; + if place.projection.first() != Some(&PlaceElem::Deref) { + // This is not a dereference, nothing to do. + return; + } + + let mut place = place.as_ref(); + loop { + if let Value::Pointer(target, needs_unique) = self.targets[place.local] { + let perform_opt = (self.can_perform_opt)(target, loc); + debug!(?place, ?target, ?needs_unique, ?perform_opt); + + // This a reborrow chain, recursively allow the replacement. + // + // This also allows to detect cases where `target.local` is not replacable, + // and mark it as such. + if let &[PlaceElem::Deref] = &target.projection[..] { + assert!(perform_opt); + self.allowed_replacements.insert((target.local, loc)); + place.local = target.local; + continue; + } else if perform_opt { + self.allowed_replacements.insert((target.local, loc)); + } else if needs_unique { + // This mutable reference is not fully replacable, so drop it. + self.targets[place.local] = Value::Unknown; + } } + + break; } } } @@ -326,18 +351,23 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'tcx> { } fn visit_place(&mut self, place: &mut Place<'tcx>, ctxt: PlaceContext, loc: Location) { - if let Value::Pointer(target, _) = self.targets[place.local] - && place.projection.first() == Some(&PlaceElem::Deref) - { - let perform_opt = matches!(ctxt, PlaceContext::NonUse(_)) - || self.allowed_replacements.contains(&(target.local, loc)); + if place.projection.first() != Some(&PlaceElem::Deref) { + return; + } - if perform_opt { - *place = target.project_deeper(&place.projection[1..], self.tcx); - self.any_replacement = true; + loop { + if let Value::Pointer(target, _) = self.targets[place.local] { + let perform_opt = matches!(ctxt, PlaceContext::NonUse(_)) + || self.allowed_replacements.contains(&(target.local, loc)); + + if perform_opt { + *place = target.project_deeper(&place.projection[1..], self.tcx); + self.any_replacement = true; + continue; + } } - } else { - self.super_place(place, ctxt, loc); + + break; } } diff --git a/compiler/rustc_mir_transform/src/ssa.rs b/compiler/rustc_mir_transform/src/ssa.rs index 05a7b226f0c..a7b45366662 100644 --- a/compiler/rustc_mir_transform/src/ssa.rs +++ b/compiler/rustc_mir_transform/src/ssa.rs @@ -209,13 +209,6 @@ impl<'tcx> Visitor<'tcx> for SsaVisitor { match ctxt { PlaceContext::MutatingUse(MutatingUseContext::Projection) | PlaceContext::NonMutatingUse(NonMutatingUseContext::Projection) => bug!(), - PlaceContext::MutatingUse(MutatingUseContext::Store) => { - self.assignments[local].insert(LocationExtended::Plain(loc)); - if let Set1::One(_) = self.assignments[local] { - // Only record if SSA-like, to avoid growing the vector needlessly. - self.assignment_order.push(local); - } - } // Anything can happen with raw pointers, so remove them. // We do not verify that all uses of the borrow dominate the assignment to `local`, // so we have to remove them too. @@ -252,6 +245,19 @@ impl<'tcx> Visitor<'tcx> for SsaVisitor { self.visit_local(place.local, ctxt, loc); } } + + fn visit_assign(&mut self, place: &Place<'tcx>, rvalue: &Rvalue<'tcx>, loc: Location) { + if let Some(local) = place.as_local() { + self.assignments[local].insert(LocationExtended::Plain(loc)); + if let Set1::One(_) = self.assignments[local] { + // Only record if SSA-like, to avoid growing the vector needlessly. + self.assignment_order.push(local); + } + } else { + self.visit_place(place, PlaceContext::MutatingUse(MutatingUseContext::Store), loc); + } + self.visit_rvalue(rvalue, loc); + } } #[instrument(level = "trace", skip(ssa, body))] diff --git a/compiler/rustc_resolve/messages.ftl b/compiler/rustc_resolve/messages.ftl index ff8bd462dd8..345255c4c69 100644 --- a/compiler/rustc_resolve/messages.ftl +++ b/compiler/rustc_resolve/messages.ftl @@ -199,6 +199,10 @@ resolve_invalid_asm_sym = .label = is a local variable .help = `sym` operands must refer to either a function or a static +resolve_lowercase_self = + attempt to use a non-constant value in a constant + .suggestion = try using `Self` + resolve_trait_impl_duplicate = duplicate definitions with name `{$name}`: .label = duplicate definition diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 72cdce5c8f0..6675b8ed59b 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -948,6 +948,9 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { ResolutionError::InvalidAsmSym => { self.tcx.sess.create_err(errs::InvalidAsmSym { span }) } + ResolutionError::LowercaseSelf => { + self.tcx.sess.create_err(errs::LowercaseSelf { span }) + } } } diff --git a/compiler/rustc_resolve/src/errors.rs b/compiler/rustc_resolve/src/errors.rs index f6d7e8b4c87..2ab55f12637 100644 --- a/compiler/rustc_resolve/src/errors.rs +++ b/compiler/rustc_resolve/src/errors.rs @@ -442,6 +442,14 @@ pub(crate) struct InvalidAsmSym { pub(crate) span: Span, } +#[derive(Diagnostic)] +#[diag(resolve_lowercase_self)] +pub(crate) struct LowercaseSelf { + #[primary_span] + #[suggestion(code = "Self", applicability = "maybe-incorrect", style = "short")] + pub(crate) span: Span, +} + #[derive(Diagnostic)] #[diag(resolve_trait_impl_duplicate, code = "E0201")] pub(crate) struct TraitImplDuplicate { diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 5a3ae656ad4..755acdd81fe 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -15,8 +15,7 @@ use std::ptr; use crate::errors::{ParamKindInEnumDiscriminant, ParamKindInNonTrivialAnonConst}; use crate::late::{ - ConstantHasGenerics, ConstantItemKind, HasGenericParams, NoConstantGenericsReason, PathSource, - Rib, RibKind, + ConstantHasGenerics, HasGenericParams, NoConstantGenericsReason, PathSource, Rib, RibKind, }; use crate::macros::{sub_namespace_match, MacroRulesScope}; use crate::{errors, AmbiguityError, AmbiguityErrorMisc, AmbiguityKind, Determinacy, Finalize}; @@ -1127,28 +1126,25 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { RibKind::ConstantItem(_, item) => { // Still doesn't deal with upvars if let Some(span) = finalize { - let (span, resolution_error) = - if let Some((ident, constant_item_kind)) = item { - let kind_str = match constant_item_kind { - ConstantItemKind::Const => "const", - ConstantItemKind::Static => "static", - }; - ( - span, - AttemptToUseNonConstantValueInConstant( - ident, "let", kind_str, - ), - ) - } else { - ( - rib_ident.span, - AttemptToUseNonConstantValueInConstant( - original_rib_ident_def, - "const", - "let", - ), - ) - }; + let (span, resolution_error) = match item { + None if rib_ident.as_str() == "self" => (span, LowercaseSelf), + None => ( + rib_ident.span, + AttemptToUseNonConstantValueInConstant( + original_rib_ident_def, + "const", + "let", + ), + ), + Some((ident, kind)) => ( + span, + AttemptToUseNonConstantValueInConstant( + ident, + "let", + kind.as_str(), + ), + ), + }; self.report_error(span, resolution_error); } return Res::Err; diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 2a8287d5554..44e277c99b9 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -150,6 +150,15 @@ pub(crate) enum ConstantItemKind { Static, } +impl ConstantItemKind { + pub(crate) fn as_str(&self) -> &'static str { + match self { + Self::Const => "const", + Self::Static => "static", + } + } +} + #[derive(Debug, Copy, Clone, PartialEq, Eq)] enum RecordPartialRes { Yes, @@ -1482,7 +1491,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { if let Some(&(_, res)) = rib.bindings.get(&normalized_ident) { self.record_lifetime_res(lifetime.id, res, LifetimeElisionCandidate::Named); - if let LifetimeRes::Param { param, .. } = res { + if let LifetimeRes::Param { param, binder } = res { match self.lifetime_uses.entry(param) { Entry::Vacant(v) => { debug!("First use of {:?} at {:?}", res, ident.span); @@ -1496,10 +1505,16 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { LifetimeRibKind::Item | LifetimeRibKind::AnonymousReportError | LifetimeRibKind::ElisionFailure => Some(LifetimeUseSet::Many), - // An anonymous lifetime is legal here, go ahead. - LifetimeRibKind::AnonymousCreateParameter { .. } => { - Some(LifetimeUseSet::One { use_span: ident.span, use_ctxt }) - } + // An anonymous lifetime is legal here, and bound to the right + // place, go ahead. + LifetimeRibKind::AnonymousCreateParameter { + binder: anon_binder, + .. + } => Some(if binder == anon_binder { + LifetimeUseSet::One { use_span: ident.span, use_ctxt } + } else { + LifetimeUseSet::Many + }), // Only report if eliding the lifetime would have the same // semantics. LifetimeRibKind::Elided(r) => Some(if res == r { diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 590609f9ed3..c12dc2f5d92 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -251,6 +251,8 @@ enum ResolutionError<'a> { TraitImplDuplicate { name: Symbol, trait_item_span: Span, old_span: Span }, /// Inline asm `sym` operand must refer to a `fn` or `static`. InvalidAsmSym, + /// `self` used instead of `Self` in a generic parameter + LowercaseSelf, } enum VisResolutionError<'a> { diff --git a/compiler/rustc_session/Cargo.toml b/compiler/rustc_session/Cargo.toml index 3477b97438c..3af83aaaaa8 100644 --- a/compiler/rustc_session/Cargo.toml +++ b/compiler/rustc_session/Cargo.toml @@ -25,7 +25,7 @@ termize = "0.1.1" libc = "0.2" [target.'cfg(windows)'.dependencies.windows] -version = "0.46.0" +version = "0.48.0" features = [ "Win32_Foundation", "Win32_System_LibraryLoader", diff --git a/compiler/rustc_session/src/filesearch.rs b/compiler/rustc_session/src/filesearch.rs index 7fdbd48d563..3988416d0c7 100644 --- a/compiler/rustc_session/src/filesearch.rs +++ b/compiler/rustc_session/src/filesearch.rs @@ -135,13 +135,13 @@ fn current_dll_path() -> Result { use windows::{ core::PCWSTR, - Win32::Foundation::HINSTANCE, + Win32::Foundation::HMODULE, Win32::System::LibraryLoader::{ GetModuleFileNameW, GetModuleHandleExW, GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, }, }; - let mut module = HINSTANCE::default(); + let mut module = HMODULE::default(); unsafe { GetModuleHandleExW( GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 5cbd7f98b6b..5976b9aa3e7 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -1720,6 +1720,10 @@ options! { #[rustc_lint_opt_deny_field_access("use `Session::stack_protector` instead of this field")] stack_protector: StackProtector = (StackProtector::None, parse_stack_protector, [TRACKED], "control stack smash protection strategy (`rustc --print stack-protector-strategies` for details)"), + staticlib_allow_rdylib_deps: bool = (false, parse_bool, [TRACKED], + "allow staticlibs to have rust dylib dependencies"), + staticlib_prefer_dynamic: bool = (false, parse_bool, [TRACKED], + "prefer dynamic linking to static linking for staticlibs (default: no)"), strict_init_checks: bool = (false, parse_bool, [TRACKED], "control if mem::uninitialized and mem::zeroed panic on more UB"), strip: Strip = (Strip::None, parse_strip, [UNTRACKED], diff --git a/compiler/rustc_smir/src/lib.rs b/compiler/rustc_smir/src/lib.rs index 54d474db038..b00f0a1c153 100644 --- a/compiler/rustc_smir/src/lib.rs +++ b/compiler/rustc_smir/src/lib.rs @@ -11,6 +11,8 @@ test(attr(allow(unused_variables), deny(warnings))) )] #![cfg_attr(not(feature = "default"), feature(rustc_private))] +#![feature(local_key_cell_methods)] +#![feature(ptr_metadata)] pub mod rustc_internal; pub mod stable_mir; diff --git a/compiler/rustc_smir/src/rustc_internal/mod.rs b/compiler/rustc_smir/src/rustc_internal/mod.rs index 5998c8b6500..609a04d263c 100644 --- a/compiler/rustc_smir/src/rustc_internal/mod.rs +++ b/compiler/rustc_smir/src/rustc_internal/mod.rs @@ -3,30 +3,49 @@ //! For that, we define APIs that will temporarily be public to 3P that exposes rustc internal APIs //! until stable MIR is complete. -use std::sync::RwLock; - -use crate::stable_mir; +use crate::{ + rustc_smir::Tables, + stable_mir::{self, with}, +}; +use rustc_middle::ty::TyCtxt; pub use rustc_span::def_id::{CrateNum, DefId}; -static DEF_ID_MAP: RwLock> = RwLock::new(Vec::new()); +fn with_tables(mut f: impl FnMut(&mut Tables<'_>) -> R) -> R { + let mut ret = None; + with(|tables| tables.rustc_tables(&mut |t| ret = Some(f(t)))); + ret.unwrap() +} pub fn item_def_id(item: &stable_mir::CrateItem) -> DefId { - DEF_ID_MAP.read().unwrap()[item.0] + with_tables(|t| t.item_def_id(item)) } pub fn crate_item(did: DefId) -> stable_mir::CrateItem { - // FIXME: this becomes inefficient when we have too many ids - let mut map = DEF_ID_MAP.write().unwrap(); - for (i, &d) in map.iter().enumerate() { - if d == did { - return stable_mir::CrateItem(i); - } + with_tables(|t| t.crate_item(did)) +} + +impl<'tcx> Tables<'tcx> { + pub fn item_def_id(&self, item: &stable_mir::CrateItem) -> DefId { + self.def_ids[item.0] + } + + pub fn crate_item(&mut self, did: DefId) -> stable_mir::CrateItem { + // FIXME: this becomes inefficient when we have too many ids + for (i, &d) in self.def_ids.iter().enumerate() { + if d == did { + return stable_mir::CrateItem(i); + } + } + let id = self.def_ids.len(); + self.def_ids.push(did); + stable_mir::CrateItem(id) } - let id = map.len(); - map.push(did); - stable_mir::CrateItem(id) } pub fn crate_num(item: &stable_mir::Crate) -> CrateNum { item.id.into() } + +pub fn run(tcx: TyCtxt<'_>, f: impl FnOnce()) { + crate::stable_mir::run(Tables { tcx, def_ids: vec![], types: vec![] }, f); +} diff --git a/compiler/rustc_smir/src/rustc_smir/mod.rs b/compiler/rustc_smir/src/rustc_smir/mod.rs index 241cd182059..6af43f5d3f3 100644 --- a/compiler/rustc_smir/src/rustc_smir/mod.rs +++ b/compiler/rustc_smir/src/rustc_smir/mod.rs @@ -7,55 +7,36 @@ //! //! For now, we are developing everything inside `rustc`, thus, we keep this module private. -use crate::{ - rustc_internal::{crate_item, item_def_id}, - stable_mir::{self}, -}; -use rustc_middle::ty::{tls::with, TyCtxt}; -use rustc_span::def_id::{CrateNum, LOCAL_CRATE}; +use crate::stable_mir::{self, ty::TyKind, Context}; +use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_span::def_id::{CrateNum, DefId, LOCAL_CRATE}; use tracing::debug; -/// Get information about the local crate. -pub fn local_crate() -> stable_mir::Crate { - with(|tcx| smir_crate(tcx, LOCAL_CRATE)) -} +impl<'tcx> Context for Tables<'tcx> { + fn local_crate(&self) -> stable_mir::Crate { + smir_crate(self.tcx, LOCAL_CRATE) + } -/// Retrieve a list of all external crates. -pub fn external_crates() -> Vec { - with(|tcx| tcx.crates(()).iter().map(|crate_num| smir_crate(tcx, *crate_num)).collect()) -} + fn external_crates(&self) -> Vec { + self.tcx.crates(()).iter().map(|crate_num| smir_crate(self.tcx, *crate_num)).collect() + } -/// Find a crate with the given name. -pub fn find_crate(name: &str) -> Option { - with(|tcx| { - [LOCAL_CRATE].iter().chain(tcx.crates(()).iter()).find_map(|crate_num| { - let crate_name = tcx.crate_name(*crate_num).to_string(); - (name == crate_name).then(|| smir_crate(tcx, *crate_num)) + fn find_crate(&self, name: &str) -> Option { + [LOCAL_CRATE].iter().chain(self.tcx.crates(()).iter()).find_map(|crate_num| { + let crate_name = self.tcx.crate_name(*crate_num).to_string(); + (name == crate_name).then(|| smir_crate(self.tcx, *crate_num)) }) - }) -} + } -/// Retrieve all items of the local crate that have a MIR associated with them. -pub fn all_local_items() -> stable_mir::CrateItems { - with(|tcx| tcx.mir_keys(()).iter().map(|item| crate_item(item.to_def_id())).collect()) -} - -pub fn entry_fn() -> Option { - with(|tcx| Some(crate_item(tcx.entry_fn(())?.0))) -} - -/// Build a stable mir crate from a given crate number. -fn smir_crate(tcx: TyCtxt<'_>, crate_num: CrateNum) -> stable_mir::Crate { - let crate_name = tcx.crate_name(crate_num).to_string(); - let is_local = crate_num == LOCAL_CRATE; - debug!(?crate_name, ?crate_num, "smir_crate"); - stable_mir::Crate { id: crate_num.into(), name: crate_name, is_local } -} - -pub fn mir_body(item: &stable_mir::CrateItem) -> stable_mir::mir::Body { - with(|tcx| { - let def_id = item_def_id(item); - let mir = tcx.optimized_mir(def_id); + fn all_local_items(&mut self) -> stable_mir::CrateItems { + self.tcx.mir_keys(()).iter().map(|item| self.crate_item(item.to_def_id())).collect() + } + fn entry_fn(&mut self) -> Option { + Some(self.crate_item(self.tcx.entry_fn(())?.0)) + } + fn mir_body(&mut self, item: &stable_mir::CrateItem) -> stable_mir::mir::Body { + let def_id = self.item_def_id(item); + let mir = self.tcx.optimized_mir(def_id); stable_mir::mir::Body { blocks: mir .basic_blocks @@ -65,8 +46,76 @@ pub fn mir_body(item: &stable_mir::CrateItem) -> stable_mir::mir::Body { statements: block.statements.iter().map(rustc_statement_to_statement).collect(), }) .collect(), + locals: mir.local_decls.iter().map(|decl| self.intern_ty(decl.ty)).collect(), } - }) + } + + fn rustc_tables(&mut self, f: &mut dyn FnMut(&mut Tables<'_>)) { + f(self) + } + + fn ty_kind(&mut self, ty: crate::stable_mir::ty::Ty) -> TyKind { + self.rustc_ty_to_ty(self.types[ty.0]) + } +} + +pub struct Tables<'tcx> { + pub tcx: TyCtxt<'tcx>, + pub def_ids: Vec, + pub types: Vec>, +} + +impl<'tcx> Tables<'tcx> { + fn rustc_ty_to_ty(&mut self, ty: Ty<'tcx>) -> TyKind { + match ty.kind() { + ty::Bool => TyKind::Bool, + ty::Char => todo!(), + ty::Int(_) => todo!(), + ty::Uint(_) => todo!(), + ty::Float(_) => todo!(), + ty::Adt(_, _) => todo!(), + ty::Foreign(_) => todo!(), + ty::Str => todo!(), + ty::Array(_, _) => todo!(), + ty::Slice(_) => todo!(), + ty::RawPtr(_) => todo!(), + ty::Ref(_, _, _) => todo!(), + ty::FnDef(_, _) => todo!(), + ty::FnPtr(_) => todo!(), + ty::Placeholder(..) => todo!(), + ty::Dynamic(_, _, _) => todo!(), + ty::Closure(_, _) => todo!(), + ty::Generator(_, _, _) => todo!(), + ty::GeneratorWitness(_) => todo!(), + ty::GeneratorWitnessMIR(_, _) => todo!(), + ty::Never => todo!(), + ty::Tuple(fields) => { + TyKind::Tuple(fields.iter().map(|ty| self.intern_ty(ty)).collect()) + } + ty::Alias(_, _) => todo!(), + ty::Param(_) => todo!(), + ty::Bound(_, _) => todo!(), + ty::Infer(_) => todo!(), + ty::Error(_) => todo!(), + } + } + + fn intern_ty(&mut self, ty: Ty<'tcx>) -> stable_mir::ty::Ty { + if let Some(id) = self.types.iter().position(|&t| t == ty) { + return stable_mir::ty::Ty(id); + } + let id = self.types.len(); + self.types.push(ty); + stable_mir::ty::Ty(id) + } +} + +/// Build a stable mir crate from a given crate number. +fn smir_crate(tcx: TyCtxt<'_>, crate_num: CrateNum) -> stable_mir::Crate { + let crate_name = tcx.crate_name(crate_num).to_string(); + let is_local = crate_num == LOCAL_CRATE; + debug!(?crate_name, ?crate_num, "smir_crate"); + stable_mir::Crate { id: crate_num.into(), name: crate_name, is_local } } fn rustc_statement_to_statement( diff --git a/compiler/rustc_smir/src/stable_mir/mir/body.rs b/compiler/rustc_smir/src/stable_mir/mir/body.rs index 4baf3f1f75e..6328c35aa59 100644 --- a/compiler/rustc_smir/src/stable_mir/mir/body.rs +++ b/compiler/rustc_smir/src/stable_mir/mir/body.rs @@ -1,6 +1,9 @@ +use crate::stable_mir::ty::Ty; + #[derive(Clone, Debug)] pub struct Body { pub blocks: Vec, + pub locals: Vec, } #[derive(Clone, Debug)] diff --git a/compiler/rustc_smir/src/stable_mir/mod.rs b/compiler/rustc_smir/src/stable_mir/mod.rs index 1d2efb5eab9..612777b9c75 100644 --- a/compiler/rustc_smir/src/stable_mir/mod.rs +++ b/compiler/rustc_smir/src/stable_mir/mod.rs @@ -11,7 +11,14 @@ //! There shouldn't be any direct references to internal compiler constructs in this module. //! If you need an internal construct, consider using `rustc_internal` or `rustc_smir`. +use std::cell::Cell; + +use crate::rustc_smir::Tables; + +use self::ty::{Ty, TyKind}; + pub mod mir; +pub mod ty; /// Use String for now but we should replace it. pub type Symbol = String; @@ -41,7 +48,7 @@ pub struct CrateItem(pub(crate) DefId); impl CrateItem { pub fn body(&self) -> mir::Body { - crate::rustc_smir::mir_body(self) + with(|cx| cx.mir_body(self)) } } @@ -49,25 +56,72 @@ impl CrateItem { /// crate defines that. This is usually `main`, but could be /// `start` if the crate is a no-std crate. pub fn entry_fn() -> Option { - crate::rustc_smir::entry_fn() + with(|cx| cx.entry_fn()) } /// Access to the local crate. pub fn local_crate() -> Crate { - crate::rustc_smir::local_crate() + with(|cx| cx.local_crate()) } /// Try to find a crate with the given name. pub fn find_crate(name: &str) -> Option { - crate::rustc_smir::find_crate(name) + with(|cx| cx.find_crate(name)) } /// Try to find a crate with the given name. pub fn external_crates() -> Vec { - crate::rustc_smir::external_crates() + with(|cx| cx.external_crates()) } /// Retrieve all items in the local crate that have a MIR associated with them. pub fn all_local_items() -> CrateItems { - crate::rustc_smir::all_local_items() + with(|cx| cx.all_local_items()) +} + +pub trait Context { + fn entry_fn(&mut self) -> Option; + /// Retrieve all items of the local crate that have a MIR associated with them. + fn all_local_items(&mut self) -> CrateItems; + fn mir_body(&mut self, item: &CrateItem) -> mir::Body; + /// Get information about the local crate. + fn local_crate(&self) -> Crate; + /// Retrieve a list of all external crates. + fn external_crates(&self) -> Vec; + + /// Find a crate with the given name. + fn find_crate(&self, name: &str) -> Option; + + /// Obtain the representation of a type. + fn ty_kind(&mut self, ty: Ty) -> TyKind; + + /// HACK: Until we have fully stable consumers, we need an escape hatch + /// to get `DefId`s out of `CrateItem`s. + fn rustc_tables(&mut self, f: &mut dyn FnMut(&mut Tables<'_>)); +} + +thread_local! { + /// A thread local variable that stores a pointer to the tables mapping between TyCtxt + /// datastructures and stable MIR datastructures. + static TLV: Cell<*mut ()> = const { Cell::new(std::ptr::null_mut()) }; +} + +pub fn run(mut context: impl Context, f: impl FnOnce()) { + assert!(TLV.get().is_null()); + fn g<'a>(mut context: &mut (dyn Context + 'a), f: impl FnOnce()) { + TLV.set(&mut context as *mut &mut _ as _); + f(); + TLV.replace(std::ptr::null_mut()); + } + g(&mut context, f); +} + +/// Loads the current context and calls a function with it. +/// Do not nest these, as that will ICE. +pub(crate) fn with(f: impl FnOnce(&mut dyn Context) -> R) -> R { + let ptr = TLV.replace(std::ptr::null_mut()) as *mut &mut dyn Context; + assert!(!ptr.is_null()); + let ret = f(unsafe { *ptr }); + TLV.set(ptr as _); + ret } diff --git a/compiler/rustc_smir/src/stable_mir/ty.rs b/compiler/rustc_smir/src/stable_mir/ty.rs new file mode 100644 index 00000000000..f27801b0f6c --- /dev/null +++ b/compiler/rustc_smir/src/stable_mir/ty.rs @@ -0,0 +1,15 @@ +use super::with; + +#[derive(Copy, Clone, Debug)] +pub struct Ty(pub usize); + +impl Ty { + pub fn kind(&self) -> TyKind { + with(|context| context.ty_kind(*self)) + } +} + +pub enum TyKind { + Bool, + Tuple(Vec), +} diff --git a/compiler/rustc_symbol_mangling/src/typeid.rs b/compiler/rustc_symbol_mangling/src/typeid.rs index 81dbff9ea4e..cda16e3a3f5 100644 --- a/compiler/rustc_symbol_mangling/src/typeid.rs +++ b/compiler/rustc_symbol_mangling/src/typeid.rs @@ -4,7 +4,7 @@ /// For more information about LLVM CFI and cross-language LLVM CFI support for the Rust compiler, /// see design document in the tracking issue #89653. use bitflags::bitflags; -use rustc_middle::ty::{FnSig, Ty, TyCtxt}; +use rustc_middle::ty::{FnSig, Instance, Ty, TyCtxt}; use rustc_target::abi::call::FnAbi; use std::hash::Hasher; use twox_hash::XxHash64; @@ -38,6 +38,15 @@ pub fn typeid_for_fnsig<'tcx>( typeid_itanium_cxx_abi::typeid_for_fnsig(tcx, fn_sig, options) } +/// Returns a type metadata identifier for the specified Instance. +pub fn typeid_for_instance<'tcx>( + tcx: TyCtxt<'tcx>, + instance: &Instance<'tcx>, + options: TypeIdOptions, +) -> String { + typeid_itanium_cxx_abi::typeid_for_instance(tcx, instance, options) +} + /// Returns a KCFI type metadata identifier for the specified FnAbi. pub fn kcfi_typeid_for_fnabi<'tcx>( tcx: TyCtxt<'tcx>, @@ -63,3 +72,16 @@ pub fn kcfi_typeid_for_fnsig<'tcx>( hash.write(typeid_itanium_cxx_abi::typeid_for_fnsig(tcx, fn_sig, options).as_bytes()); hash.finish() as u32 } + +/// Returns a KCFI type metadata identifier for the specified Instance. +pub fn kcfi_typeid_for_instance<'tcx>( + tcx: TyCtxt<'tcx>, + instance: &Instance<'tcx>, + options: TypeIdOptions, +) -> u32 { + // A KCFI type metadata identifier is a 32-bit constant produced by taking the lower half of the + // xxHash64 of the type metadata identifier. (See llvm/llvm-project@cff5bef.) + let mut hash: XxHash64 = Default::default(); + hash.write(typeid_itanium_cxx_abi::typeid_for_instance(tcx, instance, options).as_bytes()); + hash.finish() as u32 +} diff --git a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs b/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs index 5310ef26da7..c281aa7e83a 100644 --- a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs +++ b/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs @@ -14,8 +14,8 @@ use rustc_errors::DiagnosticMessage; use rustc_hir as hir; use rustc_middle::ty::subst::{GenericArg, GenericArgKind, SubstsRef}; use rustc_middle::ty::{ - self, Const, ExistentialPredicate, FloatTy, FnSig, IntTy, List, Region, RegionKind, TermKind, - Ty, TyCtxt, UintTy, + self, Const, ExistentialPredicate, FloatTy, FnSig, Instance, IntTy, List, Region, RegionKind, + TermKind, Ty, TyCtxt, UintTy, }; use rustc_span::def_id::DefId; use rustc_span::sym; @@ -1010,3 +1010,56 @@ pub fn typeid_for_fnsig<'tcx>( typeid } + +/// Returns a type metadata identifier for the specified Instance using the Itanium C++ ABI with +/// vendor extended type qualifiers and types for Rust types that are not used at the FFI boundary. +pub fn typeid_for_instance<'tcx>( + tcx: TyCtxt<'tcx>, + instance: &Instance<'tcx>, + options: TypeIdOptions, +) -> String { + let fn_abi = tcx + .fn_abi_of_instance(tcx.param_env(instance.def_id()).and((*instance, ty::List::empty()))) + .unwrap_or_else(|instance| { + bug!("typeid_for_instance: couldn't get fn_abi of instance {:?}", instance) + }); + + // If this instance is a method and self is a reference, get the impl it belongs to + let impl_def_id = tcx.impl_of_method(instance.def_id()); + if impl_def_id.is_some() && !fn_abi.args.is_empty() && fn_abi.args[0].layout.ty.is_ref() { + // If this impl is not an inherent impl, get the trait it implements + if let Some(trait_ref) = tcx.impl_trait_ref(impl_def_id.unwrap()) { + // Transform the concrete self into a reference to a trait object + let existential_predicate = trait_ref.map_bound(|trait_ref| { + ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef::erase_self_ty( + tcx, trait_ref, + )) + }); + let existential_predicates = tcx.mk_poly_existential_predicates(&[ty::Binder::dummy( + existential_predicate.skip_binder(), + )]); + // Is the concrete self mutable? + let self_ty = if fn_abi.args[0].layout.ty.is_mutable_ptr() { + tcx.mk_mut_ref( + tcx.lifetimes.re_erased, + tcx.mk_dynamic(existential_predicates, tcx.lifetimes.re_erased, ty::Dyn), + ) + } else { + tcx.mk_imm_ref( + tcx.lifetimes.re_erased, + tcx.mk_dynamic(existential_predicates, tcx.lifetimes.re_erased, ty::Dyn), + ) + }; + + // Replace the concrete self in an fn_abi clone by the reference to a trait object + let mut fn_abi = fn_abi.clone(); + // HACK(rcvalle): It is okay to not replace or update the entire ArgAbi here because the + // other fields are never used. + fn_abi.args[0].layout.ty = self_ty; + + return typeid_for_fnabi(tcx, &fn_abi, options); + } + } + + typeid_for_fnabi(tcx, &fn_abi, options) +} diff --git a/compiler/rustc_target/src/abi/call/mod.rs b/compiler/rustc_target/src/abi/call/mod.rs index 52159a7b06a..1ae11f5671c 100644 --- a/compiler/rustc_target/src/abi/call/mod.rs +++ b/compiler/rustc_target/src/abi/call/mod.rs @@ -28,7 +28,7 @@ mod x86; mod x86_64; mod x86_win64; -#[derive(PartialEq, Eq, Hash, Debug, HashStable_Generic)] +#[derive(Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)] pub enum PassMode { /// Ignore the argument. /// @@ -211,7 +211,7 @@ impl Uniform { } } -#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, HashStable_Generic)] +#[derive(Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)] pub struct CastTarget { pub prefix: [Option; 8], pub rest: Uniform, @@ -458,7 +458,7 @@ impl<'a, Ty> TyAndLayout<'a, Ty> { /// Information about how to pass an argument to, /// or return a value from, a function, under some ABI. -#[derive(PartialEq, Eq, Hash, Debug, HashStable_Generic)] +#[derive(Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)] pub struct ArgAbi<'a, Ty> { pub layout: TyAndLayout<'a, Ty>, pub mode: PassMode, @@ -605,7 +605,7 @@ pub enum Conv { /// /// I will do my best to describe this structure, but these /// comments are reverse-engineered and may be inaccurate. -NDM -#[derive(PartialEq, Eq, Hash, Debug, HashStable_Generic)] +#[derive(Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)] pub struct FnAbi<'a, Ty> { /// The LLVM types of each argument. pub args: Box<[ArgAbi<'a, Ty>]>, diff --git a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs index bacb0e32efc..25cc82f01d5 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs @@ -8,6 +8,7 @@ use rustc_data_structures::fx::FxIndexSet; use rustc_hir::def_id::DefId; use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::util::elaborate; +use rustc_infer::traits::Reveal; use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, MaybeCause, QueryResult}; use rustc_middle::ty::fast_reject::TreatProjections; use rustc_middle::ty::TypeFoldable; @@ -87,7 +88,9 @@ pub(super) enum CandidateSource { } /// Methods used to assemble candidates for either trait or projection goals. -pub(super) trait GoalKind<'tcx>: TypeFoldable> + Copy + Eq { +pub(super) trait GoalKind<'tcx>: + TypeFoldable> + Copy + Eq + std::fmt::Display +{ fn self_ty(self) -> Ty<'tcx>; fn trait_ref(self, tcx: TyCtxt<'tcx>) -> ty::TraitRef<'tcx>; @@ -96,6 +99,17 @@ pub(super) trait GoalKind<'tcx>: TypeFoldable> + Copy + Eq { fn trait_def_id(self, tcx: TyCtxt<'tcx>) -> DefId; + // Try equating an assumption predicate against a goal's predicate. If it + // holds, then execute the `then` callback, which should do any additional + // work, then produce a response (typically by executing + // [`EvalCtxt::evaluate_added_goals_and_make_canonical_response`]). + fn probe_and_match_goal_against_assumption( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + assumption: ty::Predicate<'tcx>, + then: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> QueryResult<'tcx>, + ) -> QueryResult<'tcx>; + // Consider a clause, which consists of a "assumption" and some "requirements", // to satisfy a goal. If the requirements hold, then attempt to satisfy our // goal by equating it with the assumption. @@ -104,7 +118,26 @@ pub(super) trait GoalKind<'tcx>: TypeFoldable> + Copy + Eq { goal: Goal<'tcx, Self>, assumption: ty::Predicate<'tcx>, requirements: impl IntoIterator>>, - ) -> QueryResult<'tcx>; + ) -> QueryResult<'tcx> { + Self::probe_and_match_goal_against_assumption(ecx, goal, assumption, |ecx| { + ecx.add_goals(requirements); + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }) + } + + /// Consider a bound originating from the item bounds of an alias. For this we + /// require that the well-formed requirements of the self type of the goal + /// are "satisfied from the param-env". + /// See [`EvalCtxt::validate_alias_bound_self_from_param_env`]. + fn consider_alias_bound_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + assumption: ty::Predicate<'tcx>, + ) -> QueryResult<'tcx> { + Self::probe_and_match_goal_against_assumption(ecx, goal, assumption, |ecx| { + ecx.validate_alias_bound_self_from_param_env(goal) + }) + } // Consider a clause specifically for a `dyn Trait` self type. This requires // additionally checking all of the supertraits and object bounds to hold, @@ -113,7 +146,25 @@ pub(super) trait GoalKind<'tcx>: TypeFoldable> + Copy + Eq { ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, assumption: ty::Predicate<'tcx>, - ) -> QueryResult<'tcx>; + ) -> QueryResult<'tcx> { + Self::probe_and_match_goal_against_assumption(ecx, goal, assumption, |ecx| { + let tcx = ecx.tcx(); + let ty::Dynamic(bounds, _, _) = *goal.predicate.self_ty().kind() else { + bug!("expected object type in `consider_object_bound_candidate`"); + }; + ecx.add_goals( + structural_traits::predicates_for_object_candidate( + &ecx, + goal.param_env, + goal.predicate.trait_ref(tcx), + bounds, + ) + .into_iter() + .map(|pred| goal.with(tcx, pred)), + ); + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }) + } fn consider_impl_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, @@ -463,7 +514,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { for assumption in self.tcx().item_bounds(alias_ty.def_id).subst(self.tcx(), alias_ty.substs) { - match G::consider_implied_clause(self, goal, assumption, []) { + match G::consider_alias_bound_candidate(self, goal, assumption) { Ok(result) => { candidates.push(Candidate { source: CandidateSource::AliasBound, result }) } @@ -472,6 +523,105 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { } } + /// Check that we are allowed to use an alias bound originating from the self + /// type of this goal. This means something different depending on the self type's + /// alias kind. + /// + /// * Projection: Given a goal with a self type such as `::Assoc`, + /// we require that the bound `Ty: Trait` can be proven using either a nested alias + /// bound candidate, or a param-env candidate. + /// + /// * Opaque: The param-env must be in `Reveal::UserFacing` mode. Otherwise, + /// the goal should be proven by using the hidden type instead. + #[instrument(level = "debug", skip(self), ret)] + pub(super) fn validate_alias_bound_self_from_param_env>( + &mut self, + goal: Goal<'tcx, G>, + ) -> QueryResult<'tcx> { + match *goal.predicate.self_ty().kind() { + ty::Alias(ty::Projection, projection_ty) => { + let mut param_env_candidates = vec![]; + let self_trait_ref = projection_ty.trait_ref(self.tcx()); + + if self_trait_ref.self_ty().is_ty_var() { + return self + .evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS); + } + + let trait_goal: Goal<'_, ty::TraitPredicate<'tcx>> = goal.with( + self.tcx(), + ty::TraitPredicate { + trait_ref: self_trait_ref, + constness: ty::BoundConstness::NotConst, + polarity: ty::ImplPolarity::Positive, + }, + ); + + self.assemble_param_env_candidates(trait_goal, &mut param_env_candidates); + // FIXME: We probably need some sort of recursion depth check here. + // Can't come up with an example yet, though, and the worst case + // we can have is a compiler stack overflow... + self.assemble_alias_bound_candidates(trait_goal, &mut param_env_candidates); + + // FIXME: We must also consider alias-bound candidates for a peculiar + // class of built-in candidates that I'll call "defaulted" built-ins. + // + // For example, we always know that `T: Pointee` is implemented, but + // we do not always know what `::Metadata` actually is, + // similar to if we had a user-defined impl with a `default type ...`. + // For these traits, since we're not able to always normalize their + // associated types to a concrete type, we must consider their alias bounds + // instead, so we can prove bounds such as `::Metadata: Copy`. + self.assemble_alias_bound_candidates_for_builtin_impl_default_items( + trait_goal, + &mut param_env_candidates, + ); + + self.merge_candidates(param_env_candidates) + } + ty::Alias(ty::Opaque, _opaque_ty) => match goal.param_env.reveal() { + Reveal::UserFacing => { + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } + Reveal::All => return Err(NoSolution), + }, + _ => bug!("only expected to be called on alias tys"), + } + } + + /// Assemble a subset of builtin impl candidates for a class of candidates called + /// "defaulted" built-in traits. + /// + /// For example, we always know that `T: Pointee` is implemented, but we do not + /// always know what `::Metadata` actually is! See the comment in + /// [`EvalCtxt::validate_alias_bound_self_from_param_env`] for more detail. + #[instrument(level = "debug", skip_all)] + fn assemble_alias_bound_candidates_for_builtin_impl_default_items>( + &mut self, + goal: Goal<'tcx, G>, + candidates: &mut Vec>, + ) { + let lang_items = self.tcx().lang_items(); + let trait_def_id = goal.predicate.trait_def_id(self.tcx()); + + // You probably shouldn't add anything to this list unless you + // know what you're doing. + let result = if lang_items.pointee_trait() == Some(trait_def_id) { + G::consider_builtin_pointee_candidate(self, goal) + } else if lang_items.discriminant_kind_trait() == Some(trait_def_id) { + G::consider_builtin_discriminant_kind_candidate(self, goal) + } else { + Err(NoSolution) + }; + + match result { + Ok(result) => { + candidates.push(Candidate { source: CandidateSource::BuiltinImpl, result }) + } + Err(NoSolution) => (), + } + } + #[instrument(level = "debug", skip_all)] fn assemble_object_bound_candidates>( &mut self, diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs index e5d51064c8d..20ce2d9416e 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs @@ -56,11 +56,11 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { self.trait_def_id(tcx) } - fn consider_implied_clause( + fn probe_and_match_goal_against_assumption( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, assumption: ty::Predicate<'tcx>, - requirements: impl IntoIterator>>, + then: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> QueryResult<'tcx>, ) -> QueryResult<'tcx> { if let Some(poly_projection_pred) = assumption.to_opt_poly_projection_pred() && poly_projection_pred.projection_def_id() == goal.predicate.def_id() @@ -75,49 +75,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { )?; ecx.eq(goal.param_env, goal.predicate.term, assumption_projection_pred.term) .expect("expected goal term to be fully unconstrained"); - ecx.add_goals(requirements); - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - }) - } else { - Err(NoSolution) - } - } - - fn consider_object_bound_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - assumption: ty::Predicate<'tcx>, - ) -> QueryResult<'tcx> { - if let Some(poly_projection_pred) = assumption.to_opt_poly_projection_pred() - && poly_projection_pred.projection_def_id() == goal.predicate.def_id() - { - ecx.probe(|ecx| { - let tcx = ecx.tcx(); - - let assumption_projection_pred = - ecx.instantiate_binder_with_infer(poly_projection_pred); - ecx.eq( - goal.param_env, - goal.predicate.projection_ty, - assumption_projection_pred.projection_ty, - )?; - - let ty::Dynamic(bounds, _, _) = *goal.predicate.self_ty().kind() else { - bug!("expected object type in `consider_object_bound_candidate`"); - }; - ecx.add_goals( - structural_traits::predicates_for_object_candidate( - &ecx, - goal.param_env, - goal.predicate.projection_ty.trait_ref(tcx), - bounds, - ) - .into_iter() - .map(|pred| goal.with(tcx, pred)), - ); - ecx.eq(goal.param_env, goal.predicate.term, assumption_projection_pred.term) - .expect("expected goal term to be fully unconstrained"); - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + then(ecx) }) } else { Err(NoSolution) diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index 04b38edc126..dcfa33ae842 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -78,11 +78,11 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { }) } - fn consider_implied_clause( + fn probe_and_match_goal_against_assumption( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, assumption: ty::Predicate<'tcx>, - requirements: impl IntoIterator>>, + then: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> QueryResult<'tcx>, ) -> QueryResult<'tcx> { if let Some(poly_trait_pred) = assumption.to_opt_poly_trait_pred() && poly_trait_pred.def_id() == goal.predicate.def_id() @@ -97,48 +97,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { goal.predicate.trait_ref, assumption_trait_pred.trait_ref, )?; - ecx.add_goals(requirements); - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - }) - } else { - Err(NoSolution) - } - } - - fn consider_object_bound_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - assumption: ty::Predicate<'tcx>, - ) -> QueryResult<'tcx> { - if let Some(poly_trait_pred) = assumption.to_opt_poly_trait_pred() - && poly_trait_pred.def_id() == goal.predicate.def_id() - && poly_trait_pred.polarity() == goal.predicate.polarity - { - // FIXME: Constness and polarity - ecx.probe(|ecx| { - let assumption_trait_pred = - ecx.instantiate_binder_with_infer(poly_trait_pred); - ecx.eq( - goal.param_env, - goal.predicate.trait_ref, - assumption_trait_pred.trait_ref, - )?; - - let tcx = ecx.tcx(); - let ty::Dynamic(bounds, _, _) = *goal.predicate.self_ty().kind() else { - bug!("expected object type in `consider_object_bound_candidate`"); - }; - ecx.add_goals( - structural_traits::predicates_for_object_candidate( - &ecx, - goal.param_env, - goal.predicate.trait_ref, - bounds, - ) - .into_iter() - .map(|pred| goal.with(tcx, pred)), - ); - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + then(ecx) }) } else { Err(NoSolution) diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index c9e2ed092d1..98365223923 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -28,6 +28,7 @@ use rustc_hir::{GenericParam, Item, Node}; use rustc_infer::infer::error_reporting::TypeErrCtxt; use rustc_infer::infer::{InferOk, TypeTrace}; use rustc_middle::traits::select::OverflowError; +use rustc_middle::traits::SelectionOutputTypeParameterMismatch; use rustc_middle::ty::abstract_const::NotConstEvaluatable; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::fold::{TypeFolder, TypeSuperFoldable}; @@ -1087,17 +1088,21 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } } - OutputTypeParameterMismatch( + OutputTypeParameterMismatch(box SelectionOutputTypeParameterMismatch { found_trait_ref, expected_trait_ref, - terr @ TypeError::CyclicTy(_), - ) => self.report_type_parameter_mismatch_cyclic_type_error( + terr: terr @ TypeError::CyclicTy(_), + }) => self.report_type_parameter_mismatch_cyclic_type_error( &obligation, found_trait_ref, expected_trait_ref, terr, ), - OutputTypeParameterMismatch(found_trait_ref, expected_trait_ref, _) => { + OutputTypeParameterMismatch(box SelectionOutputTypeParameterMismatch { + found_trait_ref, + expected_trait_ref, + terr: _, + }) => { match self.report_type_parameter_mismatch_error( &obligation, span, diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 616187b69dd..4dc84e0ad10 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -10,6 +10,7 @@ use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_hir::lang_items::LangItem; use rustc_infer::infer::LateBoundRegionConversionTime::HigherRankedType; use rustc_infer::infer::{DefineOpaqueTypes, InferOk}; +use rustc_middle::traits::SelectionOutputTypeParameterMismatch; use rustc_middle::ty::{ self, Binder, GenericParamDefKind, InternalSubsts, SubstsRef, ToPolyTraitRef, ToPredicate, TraitRef, Ty, TyCtxt, TypeVisitableExt, @@ -811,7 +812,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { fn confirm_poly_trait_refs( &mut self, obligation: &TraitObligation<'tcx>, - expected_trait_ref: ty::PolyTraitRef<'tcx>, + self_ty_trait_ref: ty::PolyTraitRef<'tcx>, ) -> Result>, SelectionError<'tcx>> { let obligation_trait_ref = obligation.predicate.to_poly_trait_ref(); // Normalize the obligation and expected trait refs together, because why not @@ -822,7 +823,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligation.param_env, obligation.cause.clone(), obligation.recursion_depth + 1, - (obligation_trait_ref, expected_trait_ref), + (obligation_trait_ref, self_ty_trait_ref), ) }); @@ -834,7 +835,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligations.extend(nested); obligations }) - .map_err(|e| OutputTypeParameterMismatch(expected_trait_ref, obligation_trait_ref, e)) + .map_err(|terr| { + OutputTypeParameterMismatch(Box::new(SelectionOutputTypeParameterMismatch { + expected_trait_ref: obligation_trait_ref, + found_trait_ref: expected_trait_ref, + terr, + })) + }) } fn confirm_trait_upcasting_unsize_candidate( diff --git a/compiler/rustc_ty_utils/src/consts.rs b/compiler/rustc_ty_utils/src/consts.rs index b08a92570ed..3dd1d056be2 100644 --- a/compiler/rustc_ty_utils/src/consts.rs +++ b/compiler/rustc_ty_utils/src/consts.rs @@ -394,7 +394,7 @@ impl<'a, 'tcx> visit::Visitor<'a, 'tcx> for IsThirPolymorphic<'a, 'tcx> { pub fn thir_abstract_const( tcx: TyCtxt<'_>, def: LocalDefId, -) -> Result>, ErrorGuaranteed> { +) -> Result>>, ErrorGuaranteed> { if !tcx.features().generic_const_exprs { return Ok(None); } @@ -420,7 +420,7 @@ pub fn thir_abstract_const( let root_span = body.exprs[body_id].span; - Some(recurse_build(tcx, body, body_id, root_span)).transpose() + Ok(Some(ty::EarlyBinder(recurse_build(tcx, body, body_id, root_span)?))) } pub fn provide(providers: &mut ty::query::Providers) { diff --git a/compiler/rustc_ty_utils/src/implied_bounds.rs b/compiler/rustc_ty_utils/src/implied_bounds.rs index 56d6cc28bc8..5ca5d14337c 100644 --- a/compiler/rustc_ty_utils/src/implied_bounds.rs +++ b/compiler/rustc_ty_utils/src/implied_bounds.rs @@ -31,6 +31,18 @@ fn assumed_wf_types(tcx: TyCtxt<'_>, def_id: DefId) -> &ty::List> { } } DefKind::AssocConst | DefKind::AssocTy => tcx.assumed_wf_types(tcx.parent(def_id)), + DefKind::OpaqueTy => match tcx.def_kind(tcx.parent(def_id)) { + DefKind::TyAlias => ty::List::empty(), + DefKind::AssocTy => tcx.assumed_wf_types(tcx.parent(def_id)), + // Nested opaque types only occur in associated types: + // ` type Opaque = impl Trait<&'static T, AssocTy = impl Nested>; ` + // assumed_wf_types should include those of `Opaque`, `Opaque` itself + // and `&'static T`. + DefKind::OpaqueTy => bug!("unimplemented implied bounds for neseted opaque types"), + def_kind @ _ => { + bug!("unimplemented implied bounds for opaque types with parent {def_kind:?}") + } + }, DefKind::Mod | DefKind::Struct | DefKind::Union @@ -51,7 +63,6 @@ fn assumed_wf_types(tcx: TyCtxt<'_>, def_id: DefId) -> &ty::List> { | DefKind::ForeignMod | DefKind::AnonConst | DefKind::InlineConst - | DefKind::OpaqueTy | DefKind::ImplTraitPlaceholder | DefKind::Field | DefKind::LifetimeParam diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs index 7e5a4d1c735..f6b44bdf27e 100644 --- a/compiler/rustc_type_ir/src/lib.rs +++ b/compiler/rustc_type_ir/src/lib.rs @@ -643,7 +643,7 @@ impl UnifyKey for FloatVid { } } -#[derive(Copy, Clone, PartialEq, Decodable, Encodable, Hash, HashStable_Generic)] +#[derive(Copy, Clone, PartialEq, Eq, Decodable, Encodable, Hash, HashStable_Generic)] #[rustc_pass_by_value] pub enum Variance { Covariant, // T <: T iff A <: B -- e.g., function return type diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index 9c02029f935..23ded42fa66 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -1187,7 +1187,7 @@ extern "rust-intrinsic" { /// Below are common applications of `transmute` which can be replaced with safer /// constructs. /// - /// Turning raw bytes (`&[u8]`) into `u32`, `f64`, etc.: + /// Turning raw bytes (`[u8; SZ]`) into `u32`, `f64`, etc.: /// /// ``` /// let raw_bytes = [0x78, 0x56, 0x34, 0x12]; diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs index 61fcdf58b4f..b492d2f07bc 100644 --- a/library/core/src/ptr/non_null.rs +++ b/library/core/src/ptr/non_null.rs @@ -449,6 +449,19 @@ impl NonNull { // SAFETY: `self` is a `NonNull` pointer which is necessarily non-null unsafe { NonNull::new_unchecked(self.as_ptr() as *mut U) } } + + /// See [`pointer::add`] for semantics and safety requirements. + #[inline] + pub(crate) const unsafe fn add(self, delta: usize) -> Self + where + T: Sized, + { + // SAFETY: We require that the delta stays in-bounds of the object, and + // thus it cannot become null, as that would require wrapping the + // address space, which no legal objects are allowed to do. + // And the caller promised the `delta` is sound to add. + unsafe { NonNull { pointer: self.pointer.add(delta) } } + } } impl NonNull<[T]> { diff --git a/library/core/src/slice/iter.rs b/library/core/src/slice/iter.rs index 8629aab0070..67fcef0f466 100644 --- a/library/core/src/slice/iter.rs +++ b/library/core/src/slice/iter.rs @@ -13,7 +13,7 @@ use crate::iter::{ use crate::marker::{PhantomData, Send, Sized, Sync}; use crate::mem::{self, SizedTypeProperties}; use crate::num::NonZeroUsize; -use crate::ptr::NonNull; +use crate::ptr::{invalid, invalid_mut, NonNull}; use super::{from_raw_parts, from_raw_parts_mut}; @@ -67,9 +67,7 @@ pub struct Iter<'a, T: 'a> { ptr: NonNull, /// For non-ZSTs, the non-null pointer to the past-the-end element. /// - /// For ZSTs, this is `ptr.wrapping_byte_add(len)`. - /// - /// For all types, `ptr == end` tests whether the iterator is empty. + /// For ZSTs, this is `ptr::invalid(len)`. end: *const T, _marker: PhantomData<&'a T>, } @@ -94,8 +92,7 @@ impl<'a, T> Iter<'a, T> { unsafe { assume(!ptr.is_null()); - let end = - if T::IS_ZST { ptr.wrapping_byte_add(slice.len()) } else { ptr.add(slice.len()) }; + let end = if T::IS_ZST { invalid(slice.len()) } else { ptr.add(slice.len()) }; Self { ptr: NonNull::new_unchecked(ptr as *mut T), end, _marker: PhantomData } } @@ -193,9 +190,7 @@ pub struct IterMut<'a, T: 'a> { ptr: NonNull, /// For non-ZSTs, the non-null pointer to the past-the-end element. /// - /// For ZSTs, this is `ptr.wrapping_byte_add(len)`. - /// - /// For all types, `ptr == end` tests whether the iterator is empty. + /// For ZSTs, this is `ptr::invalid_mut(len)`. end: *mut T, _marker: PhantomData<&'a mut T>, } @@ -235,8 +230,7 @@ impl<'a, T> IterMut<'a, T> { unsafe { assume(!ptr.is_null()); - let end = - if T::IS_ZST { ptr.wrapping_byte_add(slice.len()) } else { ptr.add(slice.len()) }; + let end = if T::IS_ZST { invalid_mut(slice.len()) } else { ptr.add(slice.len()) }; Self { ptr: NonNull::new_unchecked(ptr), end, _marker: PhantomData } } diff --git a/library/core/src/slice/iter/macros.rs b/library/core/src/slice/iter/macros.rs index d2d0dd3387f..3462c0e020a 100644 --- a/library/core/src/slice/iter/macros.rs +++ b/library/core/src/slice/iter/macros.rs @@ -1,11 +1,30 @@ //! Macros used by iterators of slice. +// Shrinks the iterator when T is a ZST, setting the length to `new_len`. +// `new_len` must not exceed `self.len()`. +macro_rules! zst_set_len { + ($self: ident, $new_len: expr) => {{ + #![allow(unused_unsafe)] // we're sometimes used within an unsafe block + + // SAFETY: same as `invalid(_mut)`, but the macro doesn't know + // which versions of that function to call, so open-code it. + $self.end = unsafe { mem::transmute::($new_len) }; + }}; +} + +// Shrinks the iterator when T is a ZST, reducing the length by `n`. +// `n` must not exceed `self.len()`. +macro_rules! zst_shrink { + ($self: ident, $n: ident) => { + let new_len = $self.end.addr() - $n; + zst_set_len!($self, new_len); + }; +} + // Inlining is_empty and len makes a huge performance difference macro_rules! is_empty { - // The way we encode the length of a ZST iterator, this works both for ZST - // and non-ZST. ($self: ident) => { - $self.ptr.as_ptr() as *const T == $self.end + if T::IS_ZST { $self.end.addr() == 0 } else { $self.ptr.as_ptr() as *const _ == $self.end } }; } @@ -13,16 +32,13 @@ macro_rules! len { ($self: ident) => {{ #![allow(unused_unsafe)] // we're sometimes used within an unsafe block - let start = $self.ptr; if T::IS_ZST { - // This _cannot_ use `ptr_sub` because we depend on wrapping - // to represent the length of long ZST slice iterators. - $self.end.addr().wrapping_sub(start.as_ptr().addr()) + $self.end.addr() } else { // To get rid of some bounds checks (see `position`), we use ptr_sub instead of // offset_from (Tested by `codegen/slice-position-bounds-check`.) // SAFETY: by the type invariant pointers are aligned and `start <= end` - unsafe { $self.end.sub_ptr(start.as_ptr()) } + unsafe { $self.end.sub_ptr($self.ptr.as_ptr()) } } }}; } @@ -50,14 +66,6 @@ macro_rules! iterator { ($self: ident) => {& $( $mut_ )? *$self.pre_dec_end(1)} } - // Shrinks the iterator when T is a ZST, by moving the end of the iterator - // backwards by `n`. `n` must not exceed `self.len()`. - macro_rules! zst_shrink { - ($self: ident, $n: ident) => { - $self.end = $self.end.wrapping_byte_sub($n); - } - } - impl<'a, T> $name<'a, T> { // Helper function for creating a slice from the iterator. #[inline(always)] @@ -73,16 +81,15 @@ macro_rules! iterator { // Unsafe because the offset must not exceed `self.len()`. #[inline(always)] unsafe fn post_inc_start(&mut self, offset: usize) -> * $raw_mut T { + let old = self.ptr; if T::IS_ZST { zst_shrink!(self, offset); - self.ptr.as_ptr() } else { - let old = self.ptr.as_ptr(); // SAFETY: the caller guarantees that `offset` doesn't exceed `self.len()`, // so this new pointer is inside `self` and thus guaranteed to be non-null. - self.ptr = unsafe { NonNull::new_unchecked(self.ptr.as_ptr().add(offset)) }; - old + self.ptr = unsafe { self.ptr.add(offset) }; } + old.as_ptr() } // Helper function for moving the end of the iterator backwards by `offset` elements, @@ -155,9 +162,7 @@ macro_rules! iterator { if n >= len!(self) { // This iterator is now empty. if T::IS_ZST { - // We have to do it this way as `ptr` may never be 0, but `end` - // could be (due to wrapping). - self.end = self.ptr.as_ptr(); + zst_set_len!(self, 0); } else { // SAFETY: end can't be 0 if T isn't ZST because ptr isn't 0 and end >= ptr unsafe { @@ -356,7 +361,11 @@ macro_rules! iterator { fn nth_back(&mut self, n: usize) -> Option<$elem> { if n >= len!(self) { // This iterator is now empty. - self.end = self.ptr.as_ptr(); + if T::IS_ZST { + zst_set_len!(self, 0); + } else { + self.end = self.ptr.as_ptr(); + } return None; } // SAFETY: We are in bounds. `pre_dec_end` does the right thing even for ZSTs. diff --git a/library/portable-simd/.github/workflows/ci.yml b/library/portable-simd/.github/workflows/ci.yml index d50dfa1be4c..acd47a3da72 100644 --- a/library/portable-simd/.github/workflows/ci.yml +++ b/library/portable-simd/.github/workflows/ci.yml @@ -241,6 +241,10 @@ jobs: - "--features std" - "--features generic_const_exprs" - "--features std --features generic_const_exprs" + - "--features all_lane_counts" + - "--features all_lane_counts --features std" + - "--features all_lane_counts --features generic_const_exprs" + - "--features all_lane_counts --features std --features generic_const_exprs" steps: - uses: actions/checkout@v2 diff --git a/library/portable-simd/README.md b/library/portable-simd/README.md index db0af2da606..e8ac600debe 100644 --- a/library/portable-simd/README.md +++ b/library/portable-simd/README.md @@ -24,19 +24,10 @@ or by setting up `rustup default nightly` or else with `cargo +nightly {build,te ```bash cargo new hellosimd ``` -to create a new crate. Edit `hellosimd/Cargo.toml` to be -```toml -[package] -name = "hellosimd" -version = "0.1.0" -edition = "2018" -[dependencies] -core_simd = { git = "https://github.com/rust-lang/portable-simd" } -``` - -and finally write this in `src/main.rs`: +to create a new crate. Finally write this in `src/main.rs`: ```rust -use core_simd::*; +#![feature(portable_simd)] +use std::simd::f32x4; fn main() { let a = f32x4::splat(10.0); let b = f32x4::from_array([1.0, 2.0, 3.0, 4.0]); @@ -44,24 +35,23 @@ fn main() { } ``` -Explanation: We import all the bindings from the crate with the first line. Then, we construct our SIMD vectors with methods like `splat` or `from_array`. Finally, we can use operators on them like `+` and the appropriate SIMD instructions will be carried out. When we run `cargo run` you should get `[11.0, 12.0, 13.0, 14.0]`. +Explanation: We construct our SIMD vectors with methods like `splat` or `from_array`. Next, we can use operators like `+` on them, and the appropriate SIMD instructions will be carried out. When we run `cargo run` you should get `[11.0, 12.0, 13.0, 14.0]`. -## Code Organization +## Supported vectors -Currently the crate is organized so that each element type is a file, and then the 64-bit, 128-bit, 256-bit, and 512-bit vectors using those types are contained in said file. - -All types are then exported as a single, flat module. +Currently, vectors may have up to 64 elements, but aliases are provided only up to 512-bit vectors. Depending on the size of the primitive type, the number of lanes the vector will have varies. For example, 128-bit vectors have four `f32` lanes and two `f64` lanes. The supported element types are as follows: * **Floating Point:** `f32`, `f64` -* **Signed Integers:** `i8`, `i16`, `i32`, `i64`, `i128`, `isize` -* **Unsigned Integers:** `u8`, `u16`, `u32`, `u64`, `u128`, `usize` -* **Masks:** `mask8`, `mask16`, `mask32`, `mask64`, `mask128`, `masksize` +* **Signed Integers:** `i8`, `i16`, `i32`, `i64`, `isize` (`i128` excluded) +* **Unsigned Integers:** `u8`, `u16`, `u32`, `u64`, `usize` (`u128` excluded) +* **Pointers:** `*const T` and `*mut T` (zero-sized metadata only) +* **Masks:** 8-bit, 16-bit, 32-bit, 64-bit, and `usize`-sized masks -Floating point, signed integers, and unsigned integers are the [primitive types](https://doc.rust-lang.org/core/primitive/index.html) you're already used to. -The `mask` types are "truthy" values, but they use the number of bits in their name instead of just 1 bit like a normal `bool` uses. +Floating point, signed integers, unsigned integers, and pointers are the [primitive types](https://doc.rust-lang.org/core/primitive/index.html) you're already used to. +The mask types have elements that are "truthy" values, like `bool`, but have an unspecified layout because different architectures prefer different layouts for mask types. [simd-guide]: ./beginners-guide.md [zulip-project-portable-simd]: https://rust-lang.zulipchat.com/#narrow/stream/257879-project-portable-simd diff --git a/library/portable-simd/crates/core_simd/Cargo.toml b/library/portable-simd/crates/core_simd/Cargo.toml index 8a29cf15696..d1a3a515a7e 100644 --- a/library/portable-simd/crates/core_simd/Cargo.toml +++ b/library/portable-simd/crates/core_simd/Cargo.toml @@ -13,12 +13,11 @@ default = ["as_crate"] as_crate = [] std = [] generic_const_exprs = [] +all_lane_counts = [] -[target.'cfg(target_arch = "wasm32")'.dev-dependencies.wasm-bindgen] -version = "0.2" - -[dev-dependencies.wasm-bindgen-test] -version = "0.3" +[target.'cfg(target_arch = "wasm32")'.dev-dependencies] +wasm-bindgen = "0.2" +wasm-bindgen-test = "0.3" [dev-dependencies.proptest] version = "0.10" diff --git a/library/portable-simd/crates/core_simd/examples/README.md b/library/portable-simd/crates/core_simd/examples/README.md new file mode 100644 index 00000000000..82747f1b5a6 --- /dev/null +++ b/library/portable-simd/crates/core_simd/examples/README.md @@ -0,0 +1,13 @@ +### `stdsimd` examples + +This crate is a port of example uses of `stdsimd`, mostly taken from the `packed_simd` crate. + +The examples contain, as in the case of `dot_product.rs`, multiple ways of solving the problem, in order to show idiomatic uses of SIMD and iteration of performance designs. + +Run the tests with the command + +``` +cargo run --example dot_product +``` + +and verify the code for `dot_product.rs` on your machine. diff --git a/library/portable-simd/crates/core_simd/examples/dot_product.rs b/library/portable-simd/crates/core_simd/examples/dot_product.rs new file mode 100644 index 00000000000..391f08f55a0 --- /dev/null +++ b/library/portable-simd/crates/core_simd/examples/dot_product.rs @@ -0,0 +1,169 @@ +// Code taken from the `packed_simd` crate +// Run this code with `cargo test --example dot_product` +//use std::iter::zip; + +#![feature(array_chunks)] +#![feature(slice_as_chunks)] +// Add these imports to use the stdsimd library +#![feature(portable_simd)] +use core_simd::simd::*; + +// This is your barebones dot product implementation: +// Take 2 vectors, multiply them element wise and *then* +// go along the resulting array and add up the result. +// In the next example we will see if there +// is any difference to adding and multiplying in tandem. +pub fn dot_prod_scalar_0(a: &[f32], b: &[f32]) -> f32 { + assert_eq!(a.len(), b.len()); + + a.iter().zip(b.iter()).map(|(a, b)| a * b).sum() +} + +// When dealing with SIMD, it is very important to think about the amount +// of data movement and when it happens. We're going over simple computation examples here, and yet +// it is not trivial to understand what may or may not contribute to performance +// changes. Eventually, you will need tools to inspect the generated assembly and confirm your +// hypothesis and benchmarks - we will mention them later on. +// With the use of `fold`, we're doing a multiplication, +// and then adding it to the sum, one element from both vectors at a time. +pub fn dot_prod_scalar_1(a: &[f32], b: &[f32]) -> f32 { + assert_eq!(a.len(), b.len()); + a.iter() + .zip(b.iter()) + .fold(0.0, |a, zipped| a + zipped.0 * zipped.1) +} + +// We now move on to the SIMD implementations: notice the following constructs: +// `array_chunks::<4>`: mapping this over the vector will let use construct SIMD vectors +// `f32x4::from_array`: construct the SIMD vector from a slice +// `(a * b).reduce_sum()`: Multiply both f32x4 vectors together, and then reduce them. +// This approach essentially uses SIMD to produce a vector of length N/4 of all the products, +// and then add those with `sum()`. This is suboptimal. +// TODO: ASCII diagrams +pub fn dot_prod_simd_0(a: &[f32], b: &[f32]) -> f32 { + assert_eq!(a.len(), b.len()); + // TODO handle remainder when a.len() % 4 != 0 + a.array_chunks::<4>() + .map(|&a| f32x4::from_array(a)) + .zip(b.array_chunks::<4>().map(|&b| f32x4::from_array(b))) + .map(|(a, b)| (a * b).reduce_sum()) + .sum() +} + +// There's some simple ways to improve the previous code: +// 1. Make a `zero` `f32x4` SIMD vector that we will be accumulating into +// So that there is only one `sum()` reduction when the last `f32x4` has been processed +// 2. Exploit Fused Multiply Add so that the multiplication, addition and sinking into the reduciton +// happen in the same step. +// If the arrays are large, minimizing the data shuffling will lead to great perf. +// If the arrays are small, handling the remainder elements when the length isn't a multiple of 4 +// Can become a problem. +pub fn dot_prod_simd_1(a: &[f32], b: &[f32]) -> f32 { + assert_eq!(a.len(), b.len()); + // TODO handle remainder when a.len() % 4 != 0 + a.array_chunks::<4>() + .map(|&a| f32x4::from_array(a)) + .zip(b.array_chunks::<4>().map(|&b| f32x4::from_array(b))) + .fold(f32x4::splat(0.0), |acc, zipped| acc + zipped.0 * zipped.1) + .reduce_sum() +} + +// A lot of knowledgeable use of SIMD comes from knowing specific instructions that are +// available - let's try to use the `mul_add` instruction, which is the fused-multiply-add we were looking for. +use std_float::StdFloat; +pub fn dot_prod_simd_2(a: &[f32], b: &[f32]) -> f32 { + assert_eq!(a.len(), b.len()); + // TODO handle remainder when a.len() % 4 != 0 + let mut res = f32x4::splat(0.0); + a.array_chunks::<4>() + .map(|&a| f32x4::from_array(a)) + .zip(b.array_chunks::<4>().map(|&b| f32x4::from_array(b))) + .for_each(|(a, b)| { + res = a.mul_add(b, res); + }); + res.reduce_sum() +} + +// Finally, we will write the same operation but handling the loop remainder. +const LANES: usize = 4; +pub fn dot_prod_simd_3(a: &[f32], b: &[f32]) -> f32 { + assert_eq!(a.len(), b.len()); + + let (a_extra, a_chunks) = a.as_rchunks(); + let (b_extra, b_chunks) = b.as_rchunks(); + + // These are always true, but for emphasis: + assert_eq!(a_chunks.len(), b_chunks.len()); + assert_eq!(a_extra.len(), b_extra.len()); + + let mut sums = [0.0; LANES]; + for ((x, y), d) in std::iter::zip(a_extra, b_extra).zip(&mut sums) { + *d = x * y; + } + + let mut sums = f32x4::from_array(sums); + std::iter::zip(a_chunks, b_chunks).for_each(|(x, y)| { + sums += f32x4::from_array(*x) * f32x4::from_array(*y); + }); + + sums.reduce_sum() +} + +// Finally, we present an iterator version for handling remainders in a scalar fashion at the end of the loop. +// Unfortunately, this is allocating 1 `XMM` register on the order of `~len(a)` - we'll see how we can get around it in the +// next example. +pub fn dot_prod_simd_4(a: &[f32], b: &[f32]) -> f32 { + let mut sum = a + .array_chunks::<4>() + .map(|&a| f32x4::from_array(a)) + .zip(b.array_chunks::<4>().map(|&b| f32x4::from_array(b))) + .map(|(a, b)| a * b) + .fold(f32x4::splat(0.0), std::ops::Add::add) + .reduce_sum(); + let remain = a.len() - (a.len() % 4); + sum += a[remain..] + .iter() + .zip(&b[remain..]) + .map(|(a, b)| a * b) + .sum::(); + sum +} + +// This version allocates a single `XMM` register for accumulation, and the folds don't allocate on top of that. +// Notice the the use of `mul_add`, which can do a multiply and an add operation ber iteration. +pub fn dot_prod_simd_5(a: &[f32], b: &[f32]) -> f32 { + a.array_chunks::<4>() + .map(|&a| f32x4::from_array(a)) + .zip(b.array_chunks::<4>().map(|&b| f32x4::from_array(b))) + .fold(f32x4::splat(0.), |acc, (a, b)| a.mul_add(b, acc)) + .reduce_sum() +} + +fn main() { + // Empty main to make cargo happy +} + +#[cfg(test)] +mod tests { + #[test] + fn smoke_test() { + use super::*; + let a: Vec = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0]; + let b: Vec = vec![-8.0, -7.0, -6.0, -5.0, 4.0, 3.0, 2.0, 1.0]; + let x: Vec = [0.5; 1003].to_vec(); + let y: Vec = [2.0; 1003].to_vec(); + + // Basic check + assert_eq!(0.0, dot_prod_scalar_0(&a, &b)); + assert_eq!(0.0, dot_prod_scalar_1(&a, &b)); + assert_eq!(0.0, dot_prod_simd_0(&a, &b)); + assert_eq!(0.0, dot_prod_simd_1(&a, &b)); + assert_eq!(0.0, dot_prod_simd_2(&a, &b)); + assert_eq!(0.0, dot_prod_simd_3(&a, &b)); + assert_eq!(0.0, dot_prod_simd_4(&a, &b)); + assert_eq!(0.0, dot_prod_simd_5(&a, &b)); + + // We can handle vectors that are non-multiples of 4 + assert_eq!(1003.0, dot_prod_simd_3(&x, &y)); + } +} diff --git a/library/portable-simd/crates/core_simd/src/alias.rs b/library/portable-simd/crates/core_simd/src/alias.rs new file mode 100644 index 00000000000..23f121c4619 --- /dev/null +++ b/library/portable-simd/crates/core_simd/src/alias.rs @@ -0,0 +1,227 @@ +macro_rules! number { + { 1 } => { "one" }; + { 2 } => { "two" }; + { 4 } => { "four" }; + { 8 } => { "eight" }; + { $x:literal } => { stringify!($x) }; +} + +macro_rules! plural { + { 1 } => { "" }; + { $x:literal } => { "s" }; +} + +macro_rules! alias { + { + $( + $element_ty:ty = { + $($alias:ident $num_elements:tt)* + } + )* + } => { + $( + $( + #[doc = concat!("A SIMD vector with ", number!($num_elements), " element", plural!($num_elements), " of type [`", stringify!($element_ty), "`].")] + #[allow(non_camel_case_types)] + pub type $alias = $crate::simd::Simd<$element_ty, $num_elements>; + )* + )* + } +} + +macro_rules! mask_alias { + { + $( + $element_ty:ty : $size:literal = { + $($alias:ident $num_elements:tt)* + } + )* + } => { + $( + $( + #[doc = concat!("A SIMD mask with ", number!($num_elements), " element", plural!($num_elements), " for vectors with ", $size, " element types.")] + /// + #[doc = concat!( + "The layout of this type is unspecified, and may change between platforms and/or Rust versions, and code should not assume that it is equivalent to `[", + stringify!($element_ty), "; ", $num_elements, "]`." + )] + #[allow(non_camel_case_types)] + pub type $alias = $crate::simd::Mask<$element_ty, $num_elements>; + )* + )* + } +} + +alias! { + i8 = { + i8x1 1 + i8x2 2 + i8x4 4 + i8x8 8 + i8x16 16 + i8x32 32 + i8x64 64 + } + + i16 = { + i16x1 1 + i16x2 2 + i16x4 4 + i16x8 8 + i16x16 16 + i16x32 32 + i16x64 64 + } + + i32 = { + i32x1 1 + i32x2 2 + i32x4 4 + i32x8 8 + i32x16 16 + i32x32 32 + i32x64 64 + } + + i64 = { + i64x1 1 + i64x2 2 + i64x4 4 + i64x8 8 + i64x16 16 + i64x32 32 + i64x64 64 + } + + isize = { + isizex1 1 + isizex2 2 + isizex4 4 + isizex8 8 + isizex16 16 + isizex32 32 + isizex64 64 + } + + u8 = { + u8x1 1 + u8x2 2 + u8x4 4 + u8x8 8 + u8x16 16 + u8x32 32 + u8x64 64 + } + + u16 = { + u16x1 1 + u16x2 2 + u16x4 4 + u16x8 8 + u16x16 16 + u16x32 32 + u16x64 64 + } + + u32 = { + u32x1 1 + u32x2 2 + u32x4 4 + u32x8 8 + u32x16 16 + u32x32 32 + u32x64 64 + } + + u64 = { + u64x1 1 + u64x2 2 + u64x4 4 + u64x8 8 + u64x16 16 + u64x32 32 + u64x64 64 + } + + usize = { + usizex1 1 + usizex2 2 + usizex4 4 + usizex8 8 + usizex16 16 + usizex32 32 + usizex64 64 + } + + f32 = { + f32x1 1 + f32x2 2 + f32x4 4 + f32x8 8 + f32x16 16 + f32x32 32 + f32x64 64 + } + + f64 = { + f64x1 1 + f64x2 2 + f64x4 4 + f64x8 8 + f64x16 16 + f64x32 32 + f64x64 64 + } +} + +mask_alias! { + i8 : "8-bit" = { + mask8x1 1 + mask8x2 2 + mask8x4 4 + mask8x8 8 + mask8x16 16 + mask8x32 32 + mask8x64 64 + } + + i16 : "16-bit" = { + mask16x1 1 + mask16x2 2 + mask16x4 4 + mask16x8 8 + mask16x16 16 + mask16x32 32 + mask16x64 64 + } + + i32 : "32-bit" = { + mask32x1 1 + mask32x2 2 + mask32x4 4 + mask32x8 8 + mask32x16 16 + mask32x32 32 + mask32x64 64 + } + + i64 : "64-bit" = { + mask64x1 1 + mask64x2 2 + mask64x4 4 + mask64x8 8 + mask64x16 16 + mask64x32 32 + mask64x64 64 + } + + isize : "pointer-sized" = { + masksizex1 1 + masksizex2 2 + masksizex4 4 + masksizex8 8 + masksizex16 16 + masksizex32 32 + masksizex64 64 + } +} diff --git a/library/portable-simd/crates/core_simd/src/cast.rs b/library/portable-simd/crates/core_simd/src/cast.rs new file mode 100644 index 00000000000..65a3f845ffc --- /dev/null +++ b/library/portable-simd/crates/core_simd/src/cast.rs @@ -0,0 +1,55 @@ +use crate::simd::SimdElement; + +/// Supporting trait for `Simd::cast`. Typically doesn't need to be used directly. +/// +/// # Safety +/// Implementing this trait asserts that the type is a valid vector element for the `simd_cast` or +/// `simd_as` intrinsics. +pub unsafe trait SimdCast: SimdElement {} + +// Safety: primitive number types can be cast to other primitive number types +unsafe impl SimdCast for i8 {} +// Safety: primitive number types can be cast to other primitive number types +unsafe impl SimdCast for i16 {} +// Safety: primitive number types can be cast to other primitive number types +unsafe impl SimdCast for i32 {} +// Safety: primitive number types can be cast to other primitive number types +unsafe impl SimdCast for i64 {} +// Safety: primitive number types can be cast to other primitive number types +unsafe impl SimdCast for isize {} +// Safety: primitive number types can be cast to other primitive number types +unsafe impl SimdCast for u8 {} +// Safety: primitive number types can be cast to other primitive number types +unsafe impl SimdCast for u16 {} +// Safety: primitive number types can be cast to other primitive number types +unsafe impl SimdCast for u32 {} +// Safety: primitive number types can be cast to other primitive number types +unsafe impl SimdCast for u64 {} +// Safety: primitive number types can be cast to other primitive number types +unsafe impl SimdCast for usize {} +// Safety: primitive number types can be cast to other primitive number types +unsafe impl SimdCast for f32 {} +// Safety: primitive number types can be cast to other primitive number types +unsafe impl SimdCast for f64 {} + +/// Supporting trait for `Simd::cast_ptr`. Typically doesn't need to be used directly. +/// +/// # Safety +/// Implementing this trait asserts that the type is a valid vector element for the `simd_cast_ptr` +/// intrinsic. +pub unsafe trait SimdCastPtr {} + +// Safety: pointers can be cast to other pointer types +unsafe impl SimdCastPtr for *const U +where + U: core::ptr::Pointee, + T: core::ptr::Pointee, +{ +} +// Safety: pointers can be cast to other pointer types +unsafe impl SimdCastPtr for *mut U +where + U: core::ptr::Pointee, + T: core::ptr::Pointee, +{ +} diff --git a/library/portable-simd/crates/core_simd/src/elements.rs b/library/portable-simd/crates/core_simd/src/elements.rs index 701eb66b248..dc7f52a4d57 100644 --- a/library/portable-simd/crates/core_simd/src/elements.rs +++ b/library/portable-simd/crates/core_simd/src/elements.rs @@ -1,11 +1,15 @@ +mod const_ptr; mod float; mod int; +mod mut_ptr; mod uint; mod sealed { pub trait Sealed {} } +pub use const_ptr::*; pub use float::*; pub use int::*; +pub use mut_ptr::*; pub use uint::*; diff --git a/library/portable-simd/crates/core_simd/src/elements/const_ptr.rs b/library/portable-simd/crates/core_simd/src/elements/const_ptr.rs new file mode 100644 index 00000000000..0ef9802b5e2 --- /dev/null +++ b/library/portable-simd/crates/core_simd/src/elements/const_ptr.rs @@ -0,0 +1,141 @@ +use super::sealed::Sealed; +use crate::simd::{intrinsics, LaneCount, Mask, Simd, SimdPartialEq, SupportedLaneCount}; + +/// Operations on SIMD vectors of constant pointers. +pub trait SimdConstPtr: Copy + Sealed { + /// Vector of `usize` with the same number of lanes. + type Usize; + + /// Vector of `isize` with the same number of lanes. + type Isize; + + /// Vector of mutable pointers to the same type. + type MutPtr; + + /// Mask type used for manipulating this SIMD vector type. + type Mask; + + /// Returns `true` for each lane that is null. + fn is_null(self) -> Self::Mask; + + /// Changes constness without changing the type. + /// + /// Equivalent to calling [`pointer::cast_mut`] on each lane. + fn cast_mut(self) -> Self::MutPtr; + + /// Gets the "address" portion of the pointer. + /// + /// This method discards pointer semantic metadata, so the result cannot be + /// directly cast into a valid pointer. + /// + /// This method semantically discards *provenance* and + /// *address-space* information. To properly restore that information, use [`Self::with_addr`]. + /// + /// Equivalent to calling [`pointer::addr`] on each lane. + fn addr(self) -> Self::Usize; + + /// Creates a new pointer with the given address. + /// + /// This performs the same operation as a cast, but copies the *address-space* and + /// *provenance* of `self` to the new pointer. + /// + /// Equivalent to calling [`pointer::with_addr`] on each lane. + fn with_addr(self, addr: Self::Usize) -> Self; + + /// Gets the "address" portion of the pointer, and "exposes" the provenance part for future use + /// in [`Self::from_exposed_addr`]. + fn expose_addr(self) -> Self::Usize; + + /// Convert an address back to a pointer, picking up a previously "exposed" provenance. + /// + /// Equivalent to calling [`core::ptr::from_exposed_addr`] on each lane. + fn from_exposed_addr(addr: Self::Usize) -> Self; + + /// Calculates the offset from a pointer using wrapping arithmetic. + /// + /// Equivalent to calling [`pointer::wrapping_offset`] on each lane. + fn wrapping_offset(self, offset: Self::Isize) -> Self; + + /// Calculates the offset from a pointer using wrapping arithmetic. + /// + /// Equivalent to calling [`pointer::wrapping_add`] on each lane. + fn wrapping_add(self, count: Self::Usize) -> Self; + + /// Calculates the offset from a pointer using wrapping arithmetic. + /// + /// Equivalent to calling [`pointer::wrapping_sub`] on each lane. + fn wrapping_sub(self, count: Self::Usize) -> Self; +} + +impl Sealed for Simd<*const T, LANES> where + LaneCount: SupportedLaneCount +{ +} + +impl SimdConstPtr for Simd<*const T, LANES> +where + LaneCount: SupportedLaneCount, +{ + type Usize = Simd; + type Isize = Simd; + type MutPtr = Simd<*mut T, LANES>; + type Mask = Mask; + + #[inline] + fn is_null(self) -> Self::Mask { + Simd::splat(core::ptr::null()).simd_eq(self) + } + + #[inline] + fn cast_mut(self) -> Self::MutPtr { + self.cast_ptr() + } + + #[inline] + fn addr(self) -> Self::Usize { + // FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic. + // SAFETY: Pointer-to-integer transmutes are valid (if you are okay with losing the + // provenance). + unsafe { core::mem::transmute_copy(&self) } + } + + #[inline] + fn with_addr(self, addr: Self::Usize) -> Self { + // FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic. + // + // In the mean-time, this operation is defined to be "as if" it was + // a wrapping_offset, so we can emulate it as such. This should properly + // restore pointer provenance even under today's compiler. + self.cast_ptr::<*const u8>() + .wrapping_offset(addr.cast::() - self.addr().cast::()) + .cast_ptr() + } + + #[inline] + fn expose_addr(self) -> Self::Usize { + // Safety: `self` is a pointer vector + unsafe { intrinsics::simd_expose_addr(self) } + } + + #[inline] + fn from_exposed_addr(addr: Self::Usize) -> Self { + // Safety: `self` is a pointer vector + unsafe { intrinsics::simd_from_exposed_addr(addr) } + } + + #[inline] + fn wrapping_offset(self, count: Self::Isize) -> Self { + // Safety: simd_arith_offset takes a vector of pointers and a vector of offsets + unsafe { intrinsics::simd_arith_offset(self, count) } + } + + #[inline] + fn wrapping_add(self, count: Self::Usize) -> Self { + self.wrapping_offset(count.cast()) + } + + #[inline] + fn wrapping_sub(self, count: Self::Usize) -> Self { + self.wrapping_offset(-count.cast::()) + } +} diff --git a/library/portable-simd/crates/core_simd/src/elements/mut_ptr.rs b/library/portable-simd/crates/core_simd/src/elements/mut_ptr.rs new file mode 100644 index 00000000000..d87986b4a09 --- /dev/null +++ b/library/portable-simd/crates/core_simd/src/elements/mut_ptr.rs @@ -0,0 +1,136 @@ +use super::sealed::Sealed; +use crate::simd::{intrinsics, LaneCount, Mask, Simd, SimdPartialEq, SupportedLaneCount}; + +/// Operations on SIMD vectors of mutable pointers. +pub trait SimdMutPtr: Copy + Sealed { + /// Vector of `usize` with the same number of lanes. + type Usize; + + /// Vector of `isize` with the same number of lanes. + type Isize; + + /// Vector of constant pointers to the same type. + type ConstPtr; + + /// Mask type used for manipulating this SIMD vector type. + type Mask; + + /// Returns `true` for each lane that is null. + fn is_null(self) -> Self::Mask; + + /// Changes constness without changing the type. + /// + /// Equivalent to calling [`pointer::cast_const`] on each lane. + fn cast_const(self) -> Self::ConstPtr; + + /// Gets the "address" portion of the pointer. + /// + /// This method discards pointer semantic metadata, so the result cannot be + /// directly cast into a valid pointer. + /// + /// Equivalent to calling [`pointer::addr`] on each lane. + fn addr(self) -> Self::Usize; + + /// Creates a new pointer with the given address. + /// + /// This performs the same operation as a cast, but copies the *address-space* and + /// *provenance* of `self` to the new pointer. + /// + /// Equivalent to calling [`pointer::with_addr`] on each lane. + fn with_addr(self, addr: Self::Usize) -> Self; + + /// Gets the "address" portion of the pointer, and "exposes" the provenance part for future use + /// in [`Self::from_exposed_addr`]. + fn expose_addr(self) -> Self::Usize; + + /// Convert an address back to a pointer, picking up a previously "exposed" provenance. + /// + /// Equivalent to calling [`core::ptr::from_exposed_addr_mut`] on each lane. + fn from_exposed_addr(addr: Self::Usize) -> Self; + + /// Calculates the offset from a pointer using wrapping arithmetic. + /// + /// Equivalent to calling [`pointer::wrapping_offset`] on each lane. + fn wrapping_offset(self, offset: Self::Isize) -> Self; + + /// Calculates the offset from a pointer using wrapping arithmetic. + /// + /// Equivalent to calling [`pointer::wrapping_add`] on each lane. + fn wrapping_add(self, count: Self::Usize) -> Self; + + /// Calculates the offset from a pointer using wrapping arithmetic. + /// + /// Equivalent to calling [`pointer::wrapping_sub`] on each lane. + fn wrapping_sub(self, count: Self::Usize) -> Self; +} + +impl Sealed for Simd<*mut T, LANES> where LaneCount: SupportedLaneCount +{} + +impl SimdMutPtr for Simd<*mut T, LANES> +where + LaneCount: SupportedLaneCount, +{ + type Usize = Simd; + type Isize = Simd; + type ConstPtr = Simd<*const T, LANES>; + type Mask = Mask; + + #[inline] + fn is_null(self) -> Self::Mask { + Simd::splat(core::ptr::null_mut()).simd_eq(self) + } + + #[inline] + fn cast_const(self) -> Self::ConstPtr { + self.cast_ptr() + } + + #[inline] + fn addr(self) -> Self::Usize { + // FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic. + // SAFETY: Pointer-to-integer transmutes are valid (if you are okay with losing the + // provenance). + unsafe { core::mem::transmute_copy(&self) } + } + + #[inline] + fn with_addr(self, addr: Self::Usize) -> Self { + // FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic. + // + // In the mean-time, this operation is defined to be "as if" it was + // a wrapping_offset, so we can emulate it as such. This should properly + // restore pointer provenance even under today's compiler. + self.cast_ptr::<*mut u8>() + .wrapping_offset(addr.cast::() - self.addr().cast::()) + .cast_ptr() + } + + #[inline] + fn expose_addr(self) -> Self::Usize { + // Safety: `self` is a pointer vector + unsafe { intrinsics::simd_expose_addr(self) } + } + + #[inline] + fn from_exposed_addr(addr: Self::Usize) -> Self { + // Safety: `self` is a pointer vector + unsafe { intrinsics::simd_from_exposed_addr(addr) } + } + + #[inline] + fn wrapping_offset(self, count: Self::Isize) -> Self { + // Safety: simd_arith_offset takes a vector of pointers and a vector of offsets + unsafe { intrinsics::simd_arith_offset(self, count) } + } + + #[inline] + fn wrapping_add(self, count: Self::Usize) -> Self { + self.wrapping_offset(count.cast()) + } + + #[inline] + fn wrapping_sub(self, count: Self::Usize) -> Self { + self.wrapping_offset(-count.cast::()) + } +} diff --git a/library/portable-simd/crates/core_simd/src/eq.rs b/library/portable-simd/crates/core_simd/src/eq.rs index c7111f720a8..80763c07272 100644 --- a/library/portable-simd/crates/core_simd/src/eq.rs +++ b/library/portable-simd/crates/core_simd/src/eq.rs @@ -1,4 +1,6 @@ -use crate::simd::{intrinsics, LaneCount, Mask, Simd, SimdElement, SupportedLaneCount}; +use crate::simd::{ + intrinsics, LaneCount, Mask, Simd, SimdConstPtr, SimdElement, SimdMutPtr, SupportedLaneCount, +}; /// Parallel `PartialEq`. pub trait SimdPartialEq { @@ -71,3 +73,37 @@ macro_rules! impl_mask { } impl_mask! { i8, i16, i32, i64, isize } + +impl SimdPartialEq for Simd<*const T, LANES> +where + LaneCount: SupportedLaneCount, +{ + type Mask = Mask; + + #[inline] + fn simd_eq(self, other: Self) -> Self::Mask { + self.addr().simd_eq(other.addr()) + } + + #[inline] + fn simd_ne(self, other: Self) -> Self::Mask { + self.addr().simd_ne(other.addr()) + } +} + +impl SimdPartialEq for Simd<*mut T, LANES> +where + LaneCount: SupportedLaneCount, +{ + type Mask = Mask; + + #[inline] + fn simd_eq(self, other: Self) -> Self::Mask { + self.addr().simd_eq(other.addr()) + } + + #[inline] + fn simd_ne(self, other: Self) -> Self::Mask { + self.addr().simd_ne(other.addr()) + } +} diff --git a/library/portable-simd/crates/core_simd/src/fmt.rs b/library/portable-simd/crates/core_simd/src/fmt.rs index dbd9839c4bf..b7317969cbb 100644 --- a/library/portable-simd/crates/core_simd/src/fmt.rs +++ b/library/portable-simd/crates/core_simd/src/fmt.rs @@ -1,39 +1,21 @@ use crate::simd::{LaneCount, Simd, SimdElement, SupportedLaneCount}; use core::fmt; -macro_rules! impl_fmt_trait { - { $($trait:ident,)* } => { - $( - impl fmt::$trait for Simd - where - LaneCount: SupportedLaneCount, - T: SimdElement + fmt::$trait, - { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - #[repr(transparent)] - struct Wrapper<'a, T: fmt::$trait>(&'a T); - - impl fmt::Debug for Wrapper<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0.fmt(f) - } - } - - f.debug_list() - .entries(self.as_array().iter().map(|x| Wrapper(x))) - .finish() - } - } - )* +impl fmt::Debug for Simd +where + LaneCount: SupportedLaneCount, + T: SimdElement + fmt::Debug, +{ + /// A `Simd` has a debug format like the one for `[T]`: + /// ``` + /// # #![feature(portable_simd)] + /// # #[cfg(feature = "as_crate")] use core_simd::simd::Simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd::Simd; + /// let floats = Simd::::splat(-1.0); + /// assert_eq!(format!("{:?}", [-1.0; 4]), format!("{:?}", floats)); + /// ``` + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + <[T] as fmt::Debug>::fmt(self.as_array(), f) } } - -impl_fmt_trait! { - Debug, - Binary, - LowerExp, - UpperExp, - Octal, - LowerHex, - UpperHex, -} diff --git a/library/portable-simd/crates/core_simd/src/intrinsics.rs b/library/portable-simd/crates/core_simd/src/intrinsics.rs index 704e6ed0159..dd6698e2ba5 100644 --- a/library/portable-simd/crates/core_simd/src/intrinsics.rs +++ b/library/portable-simd/crates/core_simd/src/intrinsics.rs @@ -61,9 +61,6 @@ extern "platform-intrinsic" { /// xor pub(crate) fn simd_xor(x: T, y: T) -> T; - /// getelementptr (without inbounds) - pub(crate) fn simd_arith_offset(ptrs: T, offsets: U) -> T; - /// fptoui/fptosi/uitofp/sitofp /// casting floats to integers is truncating, so it is safe to convert values like e.g. 1.5 /// but the truncated value must fit in the target type or the result is poison. @@ -150,4 +147,17 @@ extern "platform-intrinsic" { pub(crate) fn simd_select(m: M, yes: T, no: T) -> T; #[allow(unused)] pub(crate) fn simd_select_bitmask(m: M, yes: T, no: T) -> T; + + /// getelementptr (without inbounds) + /// equivalent to wrapping_offset + pub(crate) fn simd_arith_offset(ptr: T, offset: U) -> T; + + /// equivalent to `T as U` semantics, specifically for pointers + pub(crate) fn simd_cast_ptr(ptr: T) -> U; + + /// expose a pointer as an address + pub(crate) fn simd_expose_addr(ptr: T) -> U; + + /// convert an exposed address back to a pointer + pub(crate) fn simd_from_exposed_addr(addr: T) -> U; } diff --git a/library/portable-simd/crates/core_simd/src/lane_count.rs b/library/portable-simd/crates/core_simd/src/lane_count.rs index 63723e2ec13..2b91eb9e800 100644 --- a/library/portable-simd/crates/core_simd/src/lane_count.rs +++ b/library/portable-simd/crates/core_simd/src/lane_count.rs @@ -23,24 +23,20 @@ pub trait SupportedLaneCount: Sealed { impl Sealed for LaneCount {} -impl SupportedLaneCount for LaneCount<1> { - type BitMask = [u8; 1]; -} -impl SupportedLaneCount for LaneCount<2> { - type BitMask = [u8; 1]; -} -impl SupportedLaneCount for LaneCount<4> { - type BitMask = [u8; 1]; -} -impl SupportedLaneCount for LaneCount<8> { - type BitMask = [u8; 1]; -} -impl SupportedLaneCount for LaneCount<16> { - type BitMask = [u8; 2]; -} -impl SupportedLaneCount for LaneCount<32> { - type BitMask = [u8; 4]; -} -impl SupportedLaneCount for LaneCount<64> { - type BitMask = [u8; 8]; +macro_rules! supported_lane_count { + ($($lanes:literal),+) => { + $( + impl SupportedLaneCount for LaneCount<$lanes> { + type BitMask = [u8; ($lanes + 7) / 8]; + } + )+ + }; } + +supported_lane_count!(1, 2, 4, 8, 16, 32, 64); +#[cfg(feature = "all_lane_counts")] +supported_lane_count!( + 3, 5, 6, 7, 9, 10, 11, 12, 13, 14, 15, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, + 31, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63 +); diff --git a/library/portable-simd/crates/core_simd/src/lib.rs b/library/portable-simd/crates/core_simd/src/lib.rs index 715f258f617..e5307de2155 100644 --- a/library/portable-simd/crates/core_simd/src/lib.rs +++ b/library/portable-simd/crates/core_simd/src/lib.rs @@ -1,5 +1,8 @@ #![no_std] #![feature( + const_refs_to_cell, + const_maybe_uninit_as_mut_ptr, + const_mut_refs, convert_float_to_int, decl_macro, intra_doc_pointers, @@ -7,7 +10,9 @@ repr_simd, simd_ffi, staged_api, - stdsimd + stdsimd, + strict_provenance, + ptr_metadata )] #![cfg_attr(feature = "generic_const_exprs", feature(generic_const_exprs))] #![cfg_attr(feature = "generic_const_exprs", allow(incomplete_features))] @@ -19,4 +24,3 @@ #[path = "mod.rs"] mod core_simd; pub use self::core_simd::simd; -pub use simd::*; diff --git a/library/portable-simd/crates/core_simd/src/masks.rs b/library/portable-simd/crates/core_simd/src/masks.rs index c36c336d8a2..e58df80fca8 100644 --- a/library/portable-simd/crates/core_simd/src/masks.rs +++ b/library/portable-simd/crates/core_simd/src/masks.rs @@ -55,6 +55,7 @@ pub unsafe trait MaskElement: SimdElement + Sealed {} macro_rules! impl_element { { $ty:ty } => { impl Sealed for $ty { + #[inline] fn valid(value: Simd) -> bool where LaneCount: SupportedLaneCount, @@ -62,6 +63,7 @@ macro_rules! impl_element { (value.simd_eq(Simd::splat(0 as _)) | value.simd_eq(Simd::splat(-1 as _))).all() } + #[inline] fn eq(self, other: Self) -> bool { self == other } const TRUE: Self = -1; @@ -83,7 +85,9 @@ impl_element! { isize } /// /// Masks represent boolean inclusion/exclusion on a per-lane basis. /// -/// The layout of this type is unspecified. +/// The layout of this type is unspecified, and may change between platforms +/// and/or Rust versions, and code should not assume that it is equivalent to +/// `[T; LANES]`. #[repr(transparent)] pub struct Mask(mask_impl::Mask) where @@ -102,6 +106,7 @@ where T: MaskElement, LaneCount: SupportedLaneCount, { + #[inline] fn clone(&self) -> Self { *self } @@ -113,11 +118,13 @@ where LaneCount: SupportedLaneCount, { /// Construct a mask by setting all lanes to the given value. + #[inline] pub fn splat(value: bool) -> Self { Self(mask_impl::Mask::splat(value)) } /// Converts an array of bools to a SIMD mask. + #[inline] pub fn from_array(array: [bool; LANES]) -> Self { // SAFETY: Rust's bool has a layout of 1 byte (u8) with a value of // true: 0b_0000_0001 @@ -134,6 +141,7 @@ where } /// Converts a SIMD mask to an array of bools. + #[inline] pub fn to_array(self) -> [bool; LANES] { // This follows mostly the same logic as from_array. // SAFETY: Rust's bool has a layout of 1 byte (u8) with a value of @@ -261,6 +269,7 @@ where T: MaskElement, LaneCount: SupportedLaneCount, { + #[inline] fn from(array: [bool; LANES]) -> Self { Self::from_array(array) } @@ -271,6 +280,7 @@ where T: MaskElement, LaneCount: SupportedLaneCount, { + #[inline] fn from(vector: Mask) -> Self { vector.to_array() } @@ -520,60 +530,6 @@ where } } -/// A mask for SIMD vectors with eight elements of 8 bits. -pub type mask8x8 = Mask; - -/// A mask for SIMD vectors with 16 elements of 8 bits. -pub type mask8x16 = Mask; - -/// A mask for SIMD vectors with 32 elements of 8 bits. -pub type mask8x32 = Mask; - -/// A mask for SIMD vectors with 64 elements of 8 bits. -pub type mask8x64 = Mask; - -/// A mask for SIMD vectors with four elements of 16 bits. -pub type mask16x4 = Mask; - -/// A mask for SIMD vectors with eight elements of 16 bits. -pub type mask16x8 = Mask; - -/// A mask for SIMD vectors with 16 elements of 16 bits. -pub type mask16x16 = Mask; - -/// A mask for SIMD vectors with 32 elements of 16 bits. -pub type mask16x32 = Mask; - -/// A mask for SIMD vectors with two elements of 32 bits. -pub type mask32x2 = Mask; - -/// A mask for SIMD vectors with four elements of 32 bits. -pub type mask32x4 = Mask; - -/// A mask for SIMD vectors with eight elements of 32 bits. -pub type mask32x8 = Mask; - -/// A mask for SIMD vectors with 16 elements of 32 bits. -pub type mask32x16 = Mask; - -/// A mask for SIMD vectors with two elements of 64 bits. -pub type mask64x2 = Mask; - -/// A mask for SIMD vectors with four elements of 64 bits. -pub type mask64x4 = Mask; - -/// A mask for SIMD vectors with eight elements of 64 bits. -pub type mask64x8 = Mask; - -/// A mask for SIMD vectors with two elements of pointer width. -pub type masksizex2 = Mask; - -/// A mask for SIMD vectors with four elements of pointer width. -pub type masksizex4 = Mask; - -/// A mask for SIMD vectors with eight elements of pointer width. -pub type masksizex8 = Mask; - macro_rules! impl_from { { $from:ty => $($to:ty),* } => { $( @@ -581,6 +537,7 @@ macro_rules! impl_from { where LaneCount: SupportedLaneCount, { + #[inline] fn from(value: Mask<$from, LANES>) -> Self { value.cast() } diff --git a/library/portable-simd/crates/core_simd/src/masks/bitmask.rs b/library/portable-simd/crates/core_simd/src/masks/bitmask.rs index 365ecc0a325..20465ba9b07 100644 --- a/library/portable-simd/crates/core_simd/src/masks/bitmask.rs +++ b/library/portable-simd/crates/core_simd/src/masks/bitmask.rs @@ -26,6 +26,7 @@ where T: MaskElement, LaneCount: SupportedLaneCount, { + #[inline] fn clone(&self) -> Self { *self } @@ -36,6 +37,7 @@ where T: MaskElement, LaneCount: SupportedLaneCount, { + #[inline] fn eq(&self, other: &Self) -> bool { self.0.as_ref() == other.0.as_ref() } @@ -46,6 +48,7 @@ where T: MaskElement, LaneCount: SupportedLaneCount, { + #[inline] fn partial_cmp(&self, other: &Self) -> Option { self.0.as_ref().partial_cmp(other.0.as_ref()) } @@ -63,6 +66,7 @@ where T: MaskElement, LaneCount: SupportedLaneCount, { + #[inline] fn cmp(&self, other: &Self) -> core::cmp::Ordering { self.0.as_ref().cmp(other.0.as_ref()) } diff --git a/library/portable-simd/crates/core_simd/src/masks/full_masks.rs b/library/portable-simd/crates/core_simd/src/masks/full_masks.rs index b5ba198e504..1d13c45b8e7 100644 --- a/library/portable-simd/crates/core_simd/src/masks/full_masks.rs +++ b/library/portable-simd/crates/core_simd/src/masks/full_masks.rs @@ -37,6 +37,7 @@ where T: MaskElement + PartialEq, LaneCount: SupportedLaneCount, { + #[inline] fn eq(&self, other: &Self) -> bool { self.0.eq(&other.0) } @@ -47,6 +48,7 @@ where T: MaskElement + PartialOrd, LaneCount: SupportedLaneCount, { + #[inline] fn partial_cmp(&self, other: &Self) -> Option { self.0.partial_cmp(&other.0) } @@ -64,6 +66,7 @@ where T: MaskElement + Ord, LaneCount: SupportedLaneCount, { + #[inline] fn cmp(&self, other: &Self) -> core::cmp::Ordering { self.0.cmp(&other.0) } @@ -262,6 +265,7 @@ where T: MaskElement, LaneCount: SupportedLaneCount, { + #[inline] fn from(value: Mask) -> Self { value.0 } diff --git a/library/portable-simd/crates/core_simd/src/masks/to_bitmask.rs b/library/portable-simd/crates/core_simd/src/masks/to_bitmask.rs index 2235f016c71..fc7d6b781f2 100644 --- a/library/portable-simd/crates/core_simd/src/masks/to_bitmask.rs +++ b/library/portable-simd/crates/core_simd/src/masks/to_bitmask.rs @@ -48,10 +48,12 @@ macro_rules! impl_integer_intrinsic { impl ToBitMask for Mask { type BitMask = $int; + #[inline] fn to_bitmask(self) -> $int { self.0.to_bitmask_integer() } + #[inline] fn from_bitmask(bitmask: $int) -> Self { Self(mask_impl::Mask::from_bitmask_integer(bitmask)) } @@ -83,10 +85,12 @@ where { const BYTES: usize = bitmask_len(LANES); + #[inline] fn to_bitmask_array(self) -> [u8; Self::BYTES] { self.0.to_bitmask_array() } + #[inline] fn from_bitmask_array(bitmask: [u8; Self::BYTES]) -> Self { Mask(mask_impl::Mask::from_bitmask_array(bitmask)) } diff --git a/library/portable-simd/crates/core_simd/src/mod.rs b/library/portable-simd/crates/core_simd/src/mod.rs index b472aa3abe2..35c659b7a42 100644 --- a/library/portable-simd/crates/core_simd/src/mod.rs +++ b/library/portable-simd/crates/core_simd/src/mod.rs @@ -6,6 +6,8 @@ pub(crate) mod intrinsics; #[cfg(feature = "generic_const_exprs")] mod to_bytes; +mod alias; +mod cast; mod elements; mod eq; mod fmt; @@ -15,6 +17,7 @@ mod masks; mod ops; mod ord; mod select; +mod swizzle_dyn; mod vector; mod vendor; @@ -22,11 +25,14 @@ mod vendor; pub mod simd { pub(crate) use crate::core_simd::intrinsics; + pub use crate::core_simd::alias::*; + pub use crate::core_simd::cast::*; pub use crate::core_simd::elements::*; pub use crate::core_simd::eq::*; pub use crate::core_simd::lane_count::{LaneCount, SupportedLaneCount}; pub use crate::core_simd::masks::*; pub use crate::core_simd::ord::*; pub use crate::core_simd::swizzle::*; + pub use crate::core_simd::swizzle_dyn::*; pub use crate::core_simd::vector::*; } diff --git a/library/portable-simd/crates/core_simd/src/ord.rs b/library/portable-simd/crates/core_simd/src/ord.rs index 9a87bc2e344..1ae9cd061fb 100644 --- a/library/portable-simd/crates/core_simd/src/ord.rs +++ b/library/portable-simd/crates/core_simd/src/ord.rs @@ -1,4 +1,6 @@ -use crate::simd::{intrinsics, LaneCount, Mask, Simd, SimdPartialEq, SupportedLaneCount}; +use crate::simd::{ + intrinsics, LaneCount, Mask, Simd, SimdConstPtr, SimdMutPtr, SimdPartialEq, SupportedLaneCount, +}; /// Parallel `PartialOrd`. pub trait SimdPartialOrd: SimdPartialEq { @@ -211,3 +213,101 @@ macro_rules! impl_mask { } impl_mask! { i8, i16, i32, i64, isize } + +impl SimdPartialOrd for Simd<*const T, LANES> +where + LaneCount: SupportedLaneCount, +{ + #[inline] + fn simd_lt(self, other: Self) -> Self::Mask { + self.addr().simd_lt(other.addr()) + } + + #[inline] + fn simd_le(self, other: Self) -> Self::Mask { + self.addr().simd_le(other.addr()) + } + + #[inline] + fn simd_gt(self, other: Self) -> Self::Mask { + self.addr().simd_gt(other.addr()) + } + + #[inline] + fn simd_ge(self, other: Self) -> Self::Mask { + self.addr().simd_ge(other.addr()) + } +} + +impl SimdOrd for Simd<*const T, LANES> +where + LaneCount: SupportedLaneCount, +{ + #[inline] + fn simd_max(self, other: Self) -> Self { + self.simd_lt(other).select(other, self) + } + + #[inline] + fn simd_min(self, other: Self) -> Self { + self.simd_gt(other).select(other, self) + } + + #[inline] + fn simd_clamp(self, min: Self, max: Self) -> Self { + assert!( + min.simd_le(max).all(), + "each lane in `min` must be less than or equal to the corresponding lane in `max`", + ); + self.simd_max(min).simd_min(max) + } +} + +impl SimdPartialOrd for Simd<*mut T, LANES> +where + LaneCount: SupportedLaneCount, +{ + #[inline] + fn simd_lt(self, other: Self) -> Self::Mask { + self.addr().simd_lt(other.addr()) + } + + #[inline] + fn simd_le(self, other: Self) -> Self::Mask { + self.addr().simd_le(other.addr()) + } + + #[inline] + fn simd_gt(self, other: Self) -> Self::Mask { + self.addr().simd_gt(other.addr()) + } + + #[inline] + fn simd_ge(self, other: Self) -> Self::Mask { + self.addr().simd_ge(other.addr()) + } +} + +impl SimdOrd for Simd<*mut T, LANES> +where + LaneCount: SupportedLaneCount, +{ + #[inline] + fn simd_max(self, other: Self) -> Self { + self.simd_lt(other).select(other, self) + } + + #[inline] + fn simd_min(self, other: Self) -> Self { + self.simd_gt(other).select(other, self) + } + + #[inline] + fn simd_clamp(self, min: Self, max: Self) -> Self { + assert!( + min.simd_le(max).all(), + "each lane in `min` must be less than or equal to the corresponding lane in `max`", + ); + self.simd_max(min).simd_min(max) + } +} diff --git a/library/portable-simd/crates/core_simd/src/swizzle.rs b/library/portable-simd/crates/core_simd/src/swizzle.rs index 22999d24950..68f20516cf5 100644 --- a/library/portable-simd/crates/core_simd/src/swizzle.rs +++ b/library/portable-simd/crates/core_simd/src/swizzle.rs @@ -265,16 +265,13 @@ where /// Interleave two vectors. /// - /// Produces two vectors with lanes taken alternately from `self` and `other`. + /// The resulting vectors contain lanes taken alternatively from `self` and `other`, first + /// filling the first result, and then the second. /// - /// The first result contains the first `LANES / 2` lanes from `self` and `other`, - /// alternating, starting with the first lane of `self`. - /// - /// The second result contains the last `LANES / 2` lanes from `self` and `other`, - /// alternating, starting with the lane `LANES / 2` from the start of `self`. + /// The reverse of this operation is [`Simd::deinterleave`]. /// /// ``` - /// #![feature(portable_simd)] + /// # #![feature(portable_simd)] /// # use core::simd::Simd; /// let a = Simd::from_array([0, 1, 2, 3]); /// let b = Simd::from_array([4, 5, 6, 7]); @@ -285,29 +282,17 @@ where #[inline] #[must_use = "method returns a new vector and does not mutate the original inputs"] pub fn interleave(self, other: Self) -> (Self, Self) { - const fn lo() -> [Which; LANES] { + const fn interleave(high: bool) -> [Which; LANES] { let mut idx = [Which::First(0); LANES]; let mut i = 0; while i < LANES { - let offset = i / 2; - idx[i] = if i % 2 == 0 { - Which::First(offset) + // Treat the source as a concatenated vector + let dst_index = if high { i + LANES } else { i }; + let src_index = dst_index / 2 + (dst_index % 2) * LANES; + idx[i] = if src_index < LANES { + Which::First(src_index) } else { - Which::Second(offset) - }; - i += 1; - } - idx - } - const fn hi() -> [Which; LANES] { - let mut idx = [Which::First(0); LANES]; - let mut i = 0; - while i < LANES { - let offset = (LANES + i) / 2; - idx[i] = if i % 2 == 0 { - Which::First(offset) - } else { - Which::Second(offset) + Which::Second(src_index % LANES) }; i += 1; } @@ -318,11 +303,11 @@ where struct Hi; impl Swizzle2 for Lo { - const INDEX: [Which; LANES] = lo::(); + const INDEX: [Which; LANES] = interleave::(false); } impl Swizzle2 for Hi { - const INDEX: [Which; LANES] = hi::(); + const INDEX: [Which; LANES] = interleave::(true); } (Lo::swizzle2(self, other), Hi::swizzle2(self, other)) @@ -336,8 +321,10 @@ where /// The second result takes every other lane of `self` and then `other`, starting with /// the second lane. /// + /// The reverse of this operation is [`Simd::interleave`]. + /// /// ``` - /// #![feature(portable_simd)] + /// # #![feature(portable_simd)] /// # use core::simd::Simd; /// let a = Simd::from_array([0, 4, 1, 5]); /// let b = Simd::from_array([2, 6, 3, 7]); @@ -348,22 +335,17 @@ where #[inline] #[must_use = "method returns a new vector and does not mutate the original inputs"] pub fn deinterleave(self, other: Self) -> (Self, Self) { - const fn even() -> [Which; LANES] { + const fn deinterleave(second: bool) -> [Which; LANES] { let mut idx = [Which::First(0); LANES]; let mut i = 0; - while i < LANES / 2 { - idx[i] = Which::First(2 * i); - idx[i + LANES / 2] = Which::Second(2 * i); - i += 1; - } - idx - } - const fn odd() -> [Which; LANES] { - let mut idx = [Which::First(0); LANES]; - let mut i = 0; - while i < LANES / 2 { - idx[i] = Which::First(2 * i + 1); - idx[i + LANES / 2] = Which::Second(2 * i + 1); + while i < LANES { + // Treat the source as a concatenated vector + let src_index = i * 2 + second as usize; + idx[i] = if src_index < LANES { + Which::First(src_index) + } else { + Which::Second(src_index % LANES) + }; i += 1; } idx @@ -373,11 +355,11 @@ where struct Odd; impl Swizzle2 for Even { - const INDEX: [Which; LANES] = even::(); + const INDEX: [Which; LANES] = deinterleave::(false); } impl Swizzle2 for Odd { - const INDEX: [Which; LANES] = odd::(); + const INDEX: [Which; LANES] = deinterleave::(true); } (Even::swizzle2(self, other), Odd::swizzle2(self, other)) diff --git a/library/portable-simd/crates/core_simd/src/swizzle_dyn.rs b/library/portable-simd/crates/core_simd/src/swizzle_dyn.rs new file mode 100644 index 00000000000..6065d645937 --- /dev/null +++ b/library/portable-simd/crates/core_simd/src/swizzle_dyn.rs @@ -0,0 +1,157 @@ +use crate::simd::{LaneCount, Simd, SupportedLaneCount}; +use core::mem; + +impl Simd +where + LaneCount: SupportedLaneCount, +{ + /// Swizzle a vector of bytes according to the index vector. + /// Indices within range select the appropriate byte. + /// Indices "out of bounds" instead select 0. + /// + /// Note that the current implementation is selected during build-time + /// of the standard library, so `cargo build -Zbuild-std` may be necessary + /// to unlock better performance, especially for larger vectors. + /// A planned compiler improvement will enable using `#[target_feature]` instead. + #[inline] + pub fn swizzle_dyn(self, idxs: Simd) -> Self { + #![allow(unused_imports, unused_unsafe)] + #[cfg(target_arch = "aarch64")] + use core::arch::aarch64::{uint8x8_t, vqtbl1q_u8, vtbl1_u8}; + #[cfg(all(target_arch = "arm", target_feature = "v7", target_feature = "neon"))] + use core::arch::arm::{uint8x8_t, vtbl1_u8}; + #[cfg(target_arch = "wasm32")] + use core::arch::wasm32 as wasm; + #[cfg(target_arch = "x86")] + use core::arch::x86; + #[cfg(target_arch = "x86_64")] + use core::arch::x86_64 as x86; + // SAFETY: Intrinsics covered by cfg + unsafe { + match N { + #[cfg(target_feature = "neon")] + 8 => transize(vtbl1_u8, self, idxs), + #[cfg(target_feature = "ssse3")] + 16 => transize(x86::_mm_shuffle_epi8, self, idxs), + #[cfg(target_feature = "simd128")] + 16 => transize(wasm::i8x16_swizzle, self, idxs), + #[cfg(all(target_arch = "aarch64", target_feature = "neon"))] + 16 => transize(vqtbl1q_u8, self, idxs), + #[cfg(all(target_feature = "avx2", not(target_feature = "avx512vbmi")))] + 32 => transize_raw(avx2_pshufb, self, idxs), + #[cfg(target_feature = "avx512vl,avx512vbmi")] + 32 => transize(x86::_mm256_permutexvar_epi8, self, idxs), + // Notable absence: avx512bw shuffle + // If avx512bw is available, odds of avx512vbmi are good + // FIXME: initial AVX512VBMI variant didn't actually pass muster + // #[cfg(target_feature = "avx512vbmi")] + // 64 => transize(x86::_mm512_permutexvar_epi8, self, idxs), + _ => { + let mut array = [0; N]; + for (i, k) in idxs.to_array().into_iter().enumerate() { + if (k as usize) < N { + array[i] = self[k as usize]; + }; + } + array.into() + } + } + } + } +} + +/// "vpshufb like it was meant to be" on AVX2 +/// +/// # Safety +/// This requires AVX2 to work +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +#[target_feature(enable = "avx2")] +#[allow(unused)] +#[inline] +#[allow(clippy::let_and_return)] +unsafe fn avx2_pshufb(bytes: Simd, idxs: Simd) -> Simd { + use crate::simd::SimdPartialOrd; + #[cfg(target_arch = "x86")] + use core::arch::x86; + #[cfg(target_arch = "x86_64")] + use core::arch::x86_64 as x86; + use x86::_mm256_permute2x128_si256 as avx2_cross_shuffle; + use x86::_mm256_shuffle_epi8 as avx2_half_pshufb; + let mid = Simd::splat(16u8); + let high = mid + mid; + // SAFETY: Caller promised AVX2 + unsafe { + // This is ordering sensitive, and LLVM will order these how you put them. + // Most AVX2 impls use ~5 "ports", and only 1 or 2 are capable of permutes. + // But the "compose" step will lower to ops that can also use at least 1 other port. + // So this tries to break up permutes so composition flows through "open" ports. + // Comparative benches should be done on multiple AVX2 CPUs before reordering this + + let hihi = avx2_cross_shuffle::<0x11>(bytes.into(), bytes.into()); + let hi_shuf = Simd::from(avx2_half_pshufb( + hihi, // duplicate the vector's top half + idxs.into(), // so that using only 4 bits of an index still picks bytes 16-31 + )); + // A zero-fill during the compose step gives the "all-Neon-like" OOB-is-0 semantics + let compose = idxs.simd_lt(high).select(hi_shuf, Simd::splat(0)); + let lolo = avx2_cross_shuffle::<0x00>(bytes.into(), bytes.into()); + let lo_shuf = Simd::from(avx2_half_pshufb(lolo, idxs.into())); + // Repeat, then pick indices < 16, overwriting indices 0-15 from previous compose step + let compose = idxs.simd_lt(mid).select(lo_shuf, compose); + compose + } +} + +/// This sets up a call to an architecture-specific function, and in doing so +/// it persuades rustc that everything is the correct size. Which it is. +/// This would not be needed if one could convince Rust that, by matching on N, +/// N is that value, and thus it would be valid to substitute e.g. 16. +/// +/// # Safety +/// The correctness of this function hinges on the sizes agreeing in actuality. +#[allow(dead_code)] +#[inline(always)] +unsafe fn transize( + f: unsafe fn(T, T) -> T, + bytes: Simd, + idxs: Simd, +) -> Simd +where + LaneCount: SupportedLaneCount, +{ + let idxs = zeroing_idxs(idxs); + // SAFETY: Same obligation to use this function as to use mem::transmute_copy. + unsafe { mem::transmute_copy(&f(mem::transmute_copy(&bytes), mem::transmute_copy(&idxs))) } +} + +/// Make indices that yield 0 for this architecture +#[inline(always)] +fn zeroing_idxs(idxs: Simd) -> Simd +where + LaneCount: SupportedLaneCount, +{ + // On x86, make sure the top bit is set. + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + let idxs = { + use crate::simd::SimdPartialOrd; + idxs.simd_lt(Simd::splat(N as u8)) + .select(idxs, Simd::splat(u8::MAX)) + }; + // Simply do nothing on most architectures. + idxs +} + +/// As transize but no implicit call to `zeroing_idxs`. +#[allow(dead_code)] +#[inline(always)] +unsafe fn transize_raw( + f: unsafe fn(T, T) -> T, + bytes: Simd, + idxs: Simd, +) -> Simd +where + LaneCount: SupportedLaneCount, +{ + // SAFETY: Same obligation to use this function as to use mem::transmute_copy. + unsafe { mem::transmute_copy(&f(mem::transmute_copy(&bytes), mem::transmute_copy(&idxs))) } +} diff --git a/library/portable-simd/crates/core_simd/src/vector.rs b/library/portable-simd/crates/core_simd/src/vector.rs index d52d1ac4d3a..3809cc96151 100644 --- a/library/portable-simd/crates/core_simd/src/vector.rs +++ b/library/portable-simd/crates/core_simd/src/vector.rs @@ -1,60 +1,63 @@ -mod float; -mod int; -mod uint; - -pub use float::*; -pub use int::*; -pub use uint::*; - -// Vectors of pointers are not for public use at the current time. -pub(crate) mod ptr; - use crate::simd::{ - intrinsics, LaneCount, Mask, MaskElement, SimdPartialOrd, SupportedLaneCount, Swizzle, + intrinsics, LaneCount, Mask, MaskElement, SimdCast, SimdCastPtr, SimdConstPtr, SimdMutPtr, + SimdPartialOrd, SupportedLaneCount, Swizzle, }; +use core::convert::{TryFrom, TryInto}; -/// A SIMD vector of `LANES` elements of type `T`. `Simd` has the same shape as [`[T; N]`](array), but operates like `T`. +/// A SIMD vector with the shape of `[T; N]` but the operations of `T`. /// -/// Two vectors of the same type and length will, by convention, support the operators (+, *, etc.) that `T` does. -/// These take the lanes at each index on the left-hand side and right-hand side, perform the operation, -/// and return the result in the same lane in a vector of equal size. For a given operator, this is equivalent to zipping -/// the two arrays together and mapping the operator over each lane. +/// `Simd` supports the operators (+, *, etc.) that `T` does in "elementwise" fashion. +/// These take the element at each index from the left-hand side and right-hand side, +/// perform the operation, then return the result in the same index in a vector of equal size. +/// However, `Simd` differs from normal iteration and normal arrays: +/// - `Simd` executes `N` operations in a single step with no `break`s +/// - `Simd` can have an alignment greater than `T`, for better mechanical sympathy +/// +/// By always imposing these constraints on `Simd`, it is easier to compile elementwise operations +/// into machine instructions that can themselves be executed in parallel. /// /// ```rust -/// # #![feature(array_zip, portable_simd)] +/// # #![feature(portable_simd)] /// # use core::simd::{Simd}; -/// let a0: [i32; 4] = [-2, 0, 2, 4]; -/// let a1 = [10, 9, 8, 7]; -/// let zm_add = a0.zip(a1).map(|(lhs, rhs)| lhs + rhs); -/// let zm_mul = a0.zip(a1).map(|(lhs, rhs)| lhs * rhs); +/// # use core::array; +/// let a: [i32; 4] = [-2, 0, 2, 4]; +/// let b = [10, 9, 8, 7]; +/// let sum = array::from_fn(|i| a[i] + b[i]); +/// let prod = array::from_fn(|i| a[i] * b[i]); /// /// // `Simd` implements `From<[T; N]>` -/// let (v0, v1) = (Simd::from(a0), Simd::from(a1)); +/// let (v, w) = (Simd::from(a), Simd::from(b)); /// // Which means arrays implement `Into>`. -/// assert_eq!(v0 + v1, zm_add.into()); -/// assert_eq!(v0 * v1, zm_mul.into()); +/// assert_eq!(v + w, sum.into()); +/// assert_eq!(v * w, prod.into()); /// ``` /// -/// `Simd` with integers has the quirk that these operations are also inherently wrapping, as if `T` was [`Wrapping`]. +/// +/// `Simd` with integer elements treats operators as wrapping, as if `T` was [`Wrapping`]. /// Thus, `Simd` does not implement `wrapping_add`, because that is the default behavior. /// This means there is no warning on overflows, even in "debug" builds. /// For most applications where `Simd` is appropriate, it is "not a bug" to wrap, /// and even "debug builds" are unlikely to tolerate the loss of performance. /// You may want to consider using explicitly checked arithmetic if such is required. -/// Division by zero still causes a panic, so you may want to consider using floating point numbers if that is unacceptable. +/// Division by zero on integers still causes a panic, so +/// you may want to consider using `f32` or `f64` if that is unacceptable. /// /// [`Wrapping`]: core::num::Wrapping /// /// # Layout -/// `Simd` has a layout similar to `[T; N]` (identical "shapes"), but with a greater alignment. +/// `Simd` has a layout similar to `[T; N]` (identical "shapes"), with a greater alignment. /// `[T; N]` is aligned to `T`, but `Simd` will have an alignment based on both `T` and `N`. -/// It is thus sound to [`transmute`] `Simd` to `[T; N]`, and will typically optimize to zero cost, -/// but the reverse transmutation is more likely to require a copy the compiler cannot simply elide. +/// Thus it is sound to [`transmute`] `Simd` to `[T; N]` and should optimize to "zero cost", +/// but the reverse transmutation may require a copy the compiler cannot simply elide. /// /// # ABI "Features" -/// Due to Rust's safety guarantees, `Simd` is currently passed to and from functions via memory, not SIMD registers, -/// except as an optimization. `#[inline]` hints are recommended on functions that accept `Simd` or return it. -/// The need for this may be corrected in the future. +/// Due to Rust's safety guarantees, `Simd` is currently passed and returned via memory, +/// not SIMD registers, except as an optimization. Using `#[inline]` on functions that accept +/// `Simd` or return it is recommended, at the cost of code generation time, as +/// inlining SIMD-using functions can omit a large function prolog or epilog and thus +/// improve both speed and code size. The need for this may be corrected in the future. +/// +/// Using `#[inline(always)]` still requires additional care. /// /// # Safe SIMD with Unsafe Rust /// @@ -65,18 +68,22 @@ use crate::simd::{ /// Thus, when using `unsafe` Rust to read and write `Simd` through [raw pointers], it is a good idea to first try with /// [`read_unaligned`] and [`write_unaligned`]. This is because: /// - [`read`] and [`write`] require full alignment (in this case, `Simd`'s alignment) -/// - the likely source for reading or destination for writing `Simd` is [`[T]`](slice) and similar types, aligned to `T` -/// - combining these actions would violate the `unsafe` contract and explode the program into a puff of **undefined behavior** -/// - the compiler can implicitly adjust layouts to make unaligned reads or writes fully aligned if it sees the optimization -/// - most contemporary processors suffer no performance penalty for "unaligned" reads and writes that are aligned at runtime +/// - `Simd` is often read from or written to [`[T]`](slice) and other types aligned to `T` +/// - combining these actions violates the `unsafe` contract and explodes the program into +/// a puff of **undefined behavior** +/// - the compiler can implicitly adjust layouts to make unaligned reads or writes fully aligned +/// if it sees the optimization +/// - most contemporary processors with "aligned" and "unaligned" read and write instructions +/// exhibit no performance difference if the "unaligned" variant is aligned at runtime /// -/// By imposing less obligations, unaligned functions are less likely to make the program unsound, +/// Less obligations mean unaligned reads and writes are less likely to make the program unsound, /// and may be just as fast as stricter alternatives. -/// When trying to guarantee alignment, [`[T]::as_simd`][as_simd] is an option for converting `[T]` to `[Simd]`, -/// and allows soundly operating on an aligned SIMD body, but it may cost more time when handling the scalar head and tail. -/// If these are not sufficient, then it is most ideal to design data structures to be already aligned -/// to the `Simd` you wish to use before using `unsafe` Rust to read or write. -/// More conventional ways to compensate for these facts, like materializing `Simd` to or from an array first, +/// When trying to guarantee alignment, [`[T]::as_simd`][as_simd] is an option for +/// converting `[T]` to `[Simd]`, and allows soundly operating on an aligned SIMD body, +/// but it may cost more time when handling the scalar head and tail. +/// If these are not enough, it is most ideal to design data structures to be already aligned +/// to `mem::align_of::>()` before using `unsafe` Rust to read or write. +/// Other ways to compensate for these facts, like materializing `Simd` to or from an array first, /// are handled by safe methods like [`Simd::from_array`] and [`Simd::from_slice`]. /// /// [`transmute`]: core::mem::transmute @@ -86,21 +93,26 @@ use crate::simd::{ /// [`read`]: pointer::read /// [`write`]: pointer::write /// [as_simd]: slice::as_simd +// +// NOTE: Accessing the inner array directly in any way (e.g. by using the `.0` field syntax) or +// directly constructing an instance of the type (i.e. `let vector = Simd(array)`) should be +// avoided, as it will likely become illegal on `#[repr(simd)]` structs in the future. It also +// causes rustc to emit illegal LLVM IR in some cases. #[repr(simd)] -pub struct Simd([T; LANES]) +pub struct Simd([T; N]) where - T: SimdElement, - LaneCount: SupportedLaneCount; + LaneCount: SupportedLaneCount, + T: SimdElement; -impl Simd +impl Simd where - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, T: SimdElement, { - /// Number of lanes in this vector. - pub const LANES: usize = LANES; + /// Number of elements in this vector. + pub const LANES: usize = N; - /// Returns the number of lanes in this SIMD vector. + /// Returns the number of elements in this SIMD vector. /// /// # Examples /// @@ -111,10 +123,10 @@ where /// assert_eq!(v.lanes(), 4); /// ``` pub const fn lanes(&self) -> usize { - LANES + Self::LANES } - /// Constructs a new SIMD vector with all lanes set to the given value. + /// Constructs a new SIMD vector with all elements set to the given value. /// /// # Examples /// @@ -125,11 +137,11 @@ where /// assert_eq!(v.as_array(), &[8, 8, 8, 8]); /// ``` pub fn splat(value: T) -> Self { - // This is preferred over `[value; LANES]`, since it's explicitly a splat: + // This is preferred over `[value; N]`, since it's explicitly a splat: // https://github.com/rust-lang/rust/issues/97804 struct Splat; - impl Swizzle<1, LANES> for Splat { - const INDEX: [usize; LANES] = [0; LANES]; + impl Swizzle<1, N> for Splat { + const INDEX: [usize; N] = [0; N]; } Splat::swizzle(Simd::::from([value])) } @@ -144,32 +156,100 @@ where /// let v: u64x4 = Simd::from_array([0, 1, 2, 3]); /// assert_eq!(v.as_array(), &[0, 1, 2, 3]); /// ``` - pub const fn as_array(&self) -> &[T; LANES] { - &self.0 + pub const fn as_array(&self) -> &[T; N] { + // SAFETY: `Simd` is just an overaligned `[T; N]` with + // potential padding at the end, so pointer casting to a + // `&[T; N]` is safe. + // + // NOTE: This deliberately doesn't just use `&self.0`, see the comment + // on the struct definition for details. + unsafe { &*(self as *const Self as *const [T; N]) } } /// Returns a mutable array reference containing the entire SIMD vector. - pub fn as_mut_array(&mut self) -> &mut [T; LANES] { - &mut self.0 + pub fn as_mut_array(&mut self) -> &mut [T; N] { + // SAFETY: `Simd` is just an overaligned `[T; N]` with + // potential padding at the end, so pointer casting to a + // `&mut [T; N]` is safe. + // + // NOTE: This deliberately doesn't just use `&mut self.0`, see the comment + // on the struct definition for details. + unsafe { &mut *(self as *mut Self as *mut [T; N]) } + } + + /// Load a vector from an array of `T`. + /// + /// This function is necessary since `repr(simd)` has padding for non-power-of-2 vectors (at the time of writing). + /// With padding, `read_unaligned` will read past the end of an array of N elements. + /// + /// # Safety + /// Reading `ptr` must be safe, as if by `<*const [T; N]>::read_unaligned`. + const unsafe fn load(ptr: *const [T; N]) -> Self { + // There are potentially simpler ways to write this function, but this should result in + // LLVM `load ` + + let mut tmp = core::mem::MaybeUninit::::uninit(); + // SAFETY: `Simd` always contains `N` elements of type `T`. It may have padding + // which does not need to be initialized. The safety of reading `ptr` is ensured by the + // caller. + unsafe { + core::ptr::copy_nonoverlapping(ptr, tmp.as_mut_ptr().cast(), 1); + tmp.assume_init() + } + } + + /// Store a vector to an array of `T`. + /// + /// See `load` as to why this function is necessary. + /// + /// # Safety + /// Writing to `ptr` must be safe, as if by `<*mut [T; N]>::write_unaligned`. + const unsafe fn store(self, ptr: *mut [T; N]) { + // There are potentially simpler ways to write this function, but this should result in + // LLVM `store ` + + // Creating a temporary helps LLVM turn the memcpy into a store. + let tmp = self; + // SAFETY: `Simd` always contains `N` elements of type `T`. The safety of writing + // `ptr` is ensured by the caller. + unsafe { core::ptr::copy_nonoverlapping(tmp.as_array(), ptr, 1) } } /// Converts an array to a SIMD vector. - pub const fn from_array(array: [T; LANES]) -> Self { - Self(array) + pub const fn from_array(array: [T; N]) -> Self { + // SAFETY: `&array` is safe to read. + // + // FIXME: We currently use a pointer load instead of `transmute_copy` because `repr(simd)` + // results in padding for non-power-of-2 vectors (so vectors are larger than arrays). + // + // NOTE: This deliberately doesn't just use `Self(array)`, see the comment + // on the struct definition for details. + unsafe { Self::load(&array) } } /// Converts a SIMD vector to an array. - pub const fn to_array(self) -> [T; LANES] { - self.0 + pub const fn to_array(self) -> [T; N] { + let mut tmp = core::mem::MaybeUninit::uninit(); + // SAFETY: writing to `tmp` is safe and initializes it. + // + // FIXME: We currently use a pointer store instead of `transmute_copy` because `repr(simd)` + // results in padding for non-power-of-2 vectors (so vectors are larger than arrays). + // + // NOTE: This deliberately doesn't just use `self.0`, see the comment + // on the struct definition for details. + unsafe { + self.store(tmp.as_mut_ptr()); + tmp.assume_init() + } } - /// Converts a slice to a SIMD vector containing `slice[..LANES]`. + /// Converts a slice to a SIMD vector containing `slice[..N]`. /// /// # Panics /// - /// Panics if the slice's length is less than the vector's `Simd::LANES`. + /// Panics if the slice's length is less than the vector's `Simd::N`. /// - /// # Examples + /// # Example /// /// ``` /// # #![feature(portable_simd)] @@ -180,22 +260,49 @@ where /// ``` #[must_use] pub const fn from_slice(slice: &[T]) -> Self { - assert!(slice.len() >= LANES, "slice length must be at least the number of lanes"); - let mut array = [slice[0]; LANES]; - let mut i = 0; - while i < LANES { - array[i] = slice[i]; - i += 1; - } - Self(array) + assert!( + slice.len() >= Self::LANES, + "slice length must be at least the number of elements" + ); + // SAFETY: We just checked that the slice contains + // at least `N` elements. + unsafe { Self::load(slice.as_ptr().cast()) } } - /// Performs lanewise conversion of a SIMD vector's elements to another SIMD-valid type. + /// Writes a SIMD vector to the first `N` elements of a slice. /// - /// This follows the semantics of Rust's `as` conversion for casting - /// integers to unsigned integers (interpreting as the other type, so `-1` to `MAX`), - /// and from floats to integers (truncating, or saturating at the limits) for each lane, - /// or vice versa. + /// # Panics + /// + /// Panics if the slice's length is less than the vector's `Simd::N`. + /// + /// # Example + /// + /// ``` + /// # #![feature(portable_simd)] + /// # #[cfg(feature = "as_crate")] use core_simd::simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd; + /// # use simd::u32x4; + /// let mut dest = vec![0; 6]; + /// let v = u32x4::from_array([1, 2, 3, 4]); + /// v.copy_to_slice(&mut dest); + /// assert_eq!(&dest, &[1, 2, 3, 4, 0, 0]); + /// ``` + pub fn copy_to_slice(self, slice: &mut [T]) { + assert!( + slice.len() >= Self::LANES, + "slice length must be at least the number of elements" + ); + // SAFETY: We just checked that the slice contains + // at least `N` elements. + unsafe { self.store(slice.as_mut_ptr().cast()) } + } + + /// Performs elementwise conversion of a SIMD vector's elements to another SIMD-valid type. + /// + /// This follows the semantics of Rust's `as` conversion for casting integers between + /// signed and unsigned (interpreting integers as 2s complement, so `-1` to `U::MAX` and + /// `1 << (U::BITS -1)` becoming `I::MIN` ), and from floats to integers (truncating, + /// or saturating at the limits) for each element. /// /// # Examples /// ``` @@ -215,11 +322,26 @@ where /// ``` #[must_use] #[inline] - pub fn cast(self) -> Simd { - // Safety: The input argument is a vector of a valid SIMD element type. + pub fn cast(self) -> Simd + where + T: SimdCast, + { + // Safety: supported types are guaranteed by SimdCast unsafe { intrinsics::simd_as(self) } } + /// Casts a vector of pointers to another pointer type. + #[must_use] + #[inline] + pub fn cast_ptr(self) -> Simd + where + T: SimdCastPtr, + U: SimdElement, + { + // Safety: supported types are guaranteed by SimdCastPtr + unsafe { intrinsics::simd_cast_ptr(self) } + } + /// Rounds toward zero and converts to the same-width integer type, assuming that /// the value is finite and fits in that type. /// @@ -235,90 +357,90 @@ where /// /// [cast]: Simd::cast #[inline] - pub unsafe fn to_int_unchecked(self) -> Simd + #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + pub unsafe fn to_int_unchecked(self) -> Simd where - T: core::convert::FloatToInt, - I: SimdElement, + T: core::convert::FloatToInt + SimdCast, + I: SimdCast, { - // Safety: `self` is a vector, and `FloatToInt` ensures the type can be casted to - // an integer. + // Safety: supported types are guaranteed by SimdCast, the caller is responsible for the extra invariants unsafe { intrinsics::simd_cast(self) } } /// Reads from potentially discontiguous indices in `slice` to construct a SIMD vector. - /// If an index is out-of-bounds, the lane is instead selected from the `or` vector. + /// If an index is out-of-bounds, the element is instead selected from the `or` vector. /// /// # Examples /// ``` /// # #![feature(portable_simd)] /// # use core::simd::Simd; /// let vec: Vec = vec![10, 11, 12, 13, 14, 15, 16, 17, 18]; - /// let idxs = Simd::from_array([9, 3, 0, 5]); + /// let idxs = Simd::from_array([9, 3, 0, 5]); // Note the index that is out-of-bounds /// let alt = Simd::from_array([-5, -4, -3, -2]); /// - /// let result = Simd::gather_or(&vec, idxs, alt); // Note the lane that is out-of-bounds. + /// let result = Simd::gather_or(&vec, idxs, alt); /// assert_eq!(result, Simd::from_array([-5, 13, 10, 15])); /// ``` #[must_use] #[inline] - pub fn gather_or(slice: &[T], idxs: Simd, or: Self) -> Self { + pub fn gather_or(slice: &[T], idxs: Simd, or: Self) -> Self { Self::gather_select(slice, Mask::splat(true), idxs, or) } - /// Reads from potentially discontiguous indices in `slice` to construct a SIMD vector. - /// If an index is out-of-bounds, the lane is set to the default value for the type. + /// Reads from indices in `slice` to construct a SIMD vector. + /// If an index is out-of-bounds, the element is set to the default given by `T: Default`. /// /// # Examples /// ``` /// # #![feature(portable_simd)] /// # use core::simd::Simd; /// let vec: Vec = vec![10, 11, 12, 13, 14, 15, 16, 17, 18]; - /// let idxs = Simd::from_array([9, 3, 0, 5]); + /// let idxs = Simd::from_array([9, 3, 0, 5]); // Note the index that is out-of-bounds /// - /// let result = Simd::gather_or_default(&vec, idxs); // Note the lane that is out-of-bounds. + /// let result = Simd::gather_or_default(&vec, idxs); /// assert_eq!(result, Simd::from_array([0, 13, 10, 15])); /// ``` #[must_use] #[inline] - pub fn gather_or_default(slice: &[T], idxs: Simd) -> Self + pub fn gather_or_default(slice: &[T], idxs: Simd) -> Self where T: Default, { Self::gather_or(slice, idxs, Self::splat(T::default())) } - /// Reads from potentially discontiguous indices in `slice` to construct a SIMD vector. - /// The mask `enable`s all `true` lanes and disables all `false` lanes. - /// If an index is disabled or is out-of-bounds, the lane is selected from the `or` vector. + /// Reads from indices in `slice` to construct a SIMD vector. + /// The mask `enable`s all `true` indices and disables all `false` indices. + /// If an index is disabled or is out-of-bounds, the element is selected from the `or` vector. /// /// # Examples /// ``` /// # #![feature(portable_simd)] /// # use core::simd::{Simd, Mask}; /// let vec: Vec = vec![10, 11, 12, 13, 14, 15, 16, 17, 18]; - /// let idxs = Simd::from_array([9, 3, 0, 5]); + /// let idxs = Simd::from_array([9, 3, 0, 5]); // Includes an out-of-bounds index /// let alt = Simd::from_array([-5, -4, -3, -2]); - /// let enable = Mask::from_array([true, true, true, false]); // Note the mask of the last lane. + /// let enable = Mask::from_array([true, true, true, false]); // Includes a masked element /// - /// let result = Simd::gather_select(&vec, enable, idxs, alt); // Note the lane that is out-of-bounds. + /// let result = Simd::gather_select(&vec, enable, idxs, alt); /// assert_eq!(result, Simd::from_array([-5, 13, 10, -2])); /// ``` #[must_use] #[inline] pub fn gather_select( slice: &[T], - enable: Mask, - idxs: Simd, + enable: Mask, + idxs: Simd, or: Self, ) -> Self { - let enable: Mask = enable & idxs.simd_lt(Simd::splat(slice.len())); - // Safety: We have masked-off out-of-bounds lanes. + let enable: Mask = enable & idxs.simd_lt(Simd::splat(slice.len())); + // Safety: We have masked-off out-of-bounds indices. unsafe { Self::gather_select_unchecked(slice, enable, idxs, or) } } - /// Reads from potentially discontiguous indices in `slice` to construct a SIMD vector. - /// The mask `enable`s all `true` lanes and disables all `false` lanes. - /// If an index is disabled, the lane is selected from the `or` vector. + /// Reads from indices in `slice` to construct a SIMD vector. + /// The mask `enable`s all `true` indices and disables all `false` indices. + /// If an index is disabled, the element is selected from the `or` vector. /// /// # Safety /// @@ -332,57 +454,123 @@ where /// # #[cfg(not(feature = "as_crate"))] use core::simd; /// # use simd::{Simd, SimdPartialOrd, Mask}; /// let vec: Vec = vec![10, 11, 12, 13, 14, 15, 16, 17, 18]; - /// let idxs = Simd::from_array([9, 3, 0, 5]); + /// let idxs = Simd::from_array([9, 3, 0, 5]); // Includes an out-of-bounds index /// let alt = Simd::from_array([-5, -4, -3, -2]); - /// let enable = Mask::from_array([true, true, true, false]); // Note the final mask lane. + /// let enable = Mask::from_array([true, true, true, false]); // Includes a masked element /// // If this mask was used to gather, it would be unsound. Let's fix that. /// let enable = enable & idxs.simd_lt(Simd::splat(vec.len())); /// - /// // We have masked the OOB lane, so it's safe to gather now. + /// // The out-of-bounds index has been masked, so it's safe to gather now. /// let result = unsafe { Simd::gather_select_unchecked(&vec, enable, idxs, alt) }; /// assert_eq!(result, Simd::from_array([-5, 13, 10, -2])); /// ``` /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html #[must_use] #[inline] + #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub unsafe fn gather_select_unchecked( slice: &[T], - enable: Mask, - idxs: Simd, + enable: Mask, + idxs: Simd, or: Self, ) -> Self { - let base_ptr = crate::simd::ptr::SimdConstPtr::splat(slice.as_ptr()); + let base_ptr = Simd::<*const T, N>::splat(slice.as_ptr()); // Ferris forgive me, I have done pointer arithmetic here. let ptrs = base_ptr.wrapping_add(idxs); - // Safety: The ptrs have been bounds-masked to prevent memory-unsafe reads insha'allah - unsafe { intrinsics::simd_gather(or, ptrs, enable.to_int()) } + // Safety: The caller is responsible for determining the indices are okay to read + unsafe { Self::gather_select_ptr(ptrs, enable, or) } + } + + /// Read elementwise from pointers into a SIMD vector. + /// + /// # Safety + /// + /// Each read must satisfy the same conditions as [`core::ptr::read`]. + /// + /// # Example + /// ``` + /// # #![feature(portable_simd)] + /// # #[cfg(feature = "as_crate")] use core_simd::simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd; + /// # use simd::{Simd, SimdConstPtr}; + /// let values = [6, 2, 4, 9]; + /// let offsets = Simd::from_array([1, 0, 0, 3]); + /// let source = Simd::splat(values.as_ptr()).wrapping_add(offsets); + /// let gathered = unsafe { Simd::gather_ptr(source) }; + /// assert_eq!(gathered, Simd::from_array([2, 6, 6, 9])); + /// ``` + #[must_use] + #[inline] + #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + pub unsafe fn gather_ptr(source: Simd<*const T, N>) -> Self + where + T: Default, + { + // TODO: add an intrinsic that doesn't use a passthru vector, and remove the T: Default bound + // Safety: The caller is responsible for upholding all invariants + unsafe { Self::gather_select_ptr(source, Mask::splat(true), Self::default()) } + } + + /// Conditionally read elementwise from pointers into a SIMD vector. + /// The mask `enable`s all `true` pointers and disables all `false` pointers. + /// If a pointer is disabled, the element is selected from the `or` vector, + /// and no read is performed. + /// + /// # Safety + /// + /// Enabled elements must satisfy the same conditions as [`core::ptr::read`]. + /// + /// # Example + /// ``` + /// # #![feature(portable_simd)] + /// # #[cfg(feature = "as_crate")] use core_simd::simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd; + /// # use simd::{Mask, Simd, SimdConstPtr}; + /// let values = [6, 2, 4, 9]; + /// let enable = Mask::from_array([true, true, false, true]); + /// let offsets = Simd::from_array([1, 0, 0, 3]); + /// let source = Simd::splat(values.as_ptr()).wrapping_add(offsets); + /// let gathered = unsafe { Simd::gather_select_ptr(source, enable, Simd::splat(0)) }; + /// assert_eq!(gathered, Simd::from_array([2, 6, 0, 9])); + /// ``` + #[must_use] + #[inline] + #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + pub unsafe fn gather_select_ptr( + source: Simd<*const T, N>, + enable: Mask, + or: Self, + ) -> Self { + // Safety: The caller is responsible for upholding all invariants + unsafe { intrinsics::simd_gather(or, source, enable.to_int()) } } /// Writes the values in a SIMD vector to potentially discontiguous indices in `slice`. - /// If two lanes in the scattered vector would write to the same index - /// only the last lane is guaranteed to actually be written. + /// If an index is out-of-bounds, the write is suppressed without panicking. + /// If two elements in the scattered vector would write to the same index + /// only the last element is guaranteed to actually be written. /// /// # Examples /// ``` /// # #![feature(portable_simd)] /// # use core::simd::Simd; /// let mut vec: Vec = vec![10, 11, 12, 13, 14, 15, 16, 17, 18]; - /// let idxs = Simd::from_array([9, 3, 0, 0]); + /// let idxs = Simd::from_array([9, 3, 0, 0]); // Note the duplicate index. /// let vals = Simd::from_array([-27, 82, -41, 124]); /// - /// vals.scatter(&mut vec, idxs); // index 0 receives two writes. + /// vals.scatter(&mut vec, idxs); // two logical writes means the last wins. /// assert_eq!(vec, vec![124, 11, 12, 82, 14, 15, 16, 17, 18]); /// ``` #[inline] - pub fn scatter(self, slice: &mut [T], idxs: Simd) { + pub fn scatter(self, slice: &mut [T], idxs: Simd) { self.scatter_select(slice, Mask::splat(true), idxs) } - /// Writes the values in a SIMD vector to multiple potentially discontiguous indices in `slice`. - /// The mask `enable`s all `true` lanes and disables all `false` lanes. - /// If an enabled index is out-of-bounds, the lane is not written. - /// If two enabled lanes in the scattered vector would write to the same index, - /// only the last lane is guaranteed to actually be written. + /// Writes values from a SIMD vector to multiple potentially discontiguous indices in `slice`. + /// The mask `enable`s all `true` indices and disables all `false` indices. + /// If an enabled index is out-of-bounds, the write is suppressed without panicking. + /// If two enabled elements in the scattered vector would write to the same index, + /// only the last element is guaranteed to actually be written. /// /// # Examples /// ``` @@ -391,29 +579,24 @@ where /// # #[cfg(not(feature = "as_crate"))] use core::simd; /// # use simd::{Simd, Mask}; /// let mut vec: Vec = vec![10, 11, 12, 13, 14, 15, 16, 17, 18]; - /// let idxs = Simd::from_array([9, 3, 0, 0]); + /// let idxs = Simd::from_array([9, 3, 0, 0]); // Includes an out-of-bounds index /// let vals = Simd::from_array([-27, 82, -41, 124]); - /// let enable = Mask::from_array([true, true, true, false]); // Note the mask of the last lane. + /// let enable = Mask::from_array([true, true, true, false]); // Includes a masked element /// - /// vals.scatter_select(&mut vec, enable, idxs); // index 0's second write is masked, thus omitted. + /// vals.scatter_select(&mut vec, enable, idxs); // The last write is masked, thus omitted. /// assert_eq!(vec, vec![-41, 11, 12, 82, 14, 15, 16, 17, 18]); /// ``` #[inline] - pub fn scatter_select( - self, - slice: &mut [T], - enable: Mask, - idxs: Simd, - ) { - let enable: Mask = enable & idxs.simd_lt(Simd::splat(slice.len())); - // Safety: We have masked-off out-of-bounds lanes. + pub fn scatter_select(self, slice: &mut [T], enable: Mask, idxs: Simd) { + let enable: Mask = enable & idxs.simd_lt(Simd::splat(slice.len())); + // Safety: We have masked-off out-of-bounds indices. unsafe { self.scatter_select_unchecked(slice, enable, idxs) } } - /// Writes the values in a SIMD vector to multiple potentially discontiguous indices in `slice`. - /// The mask `enable`s all `true` lanes and disables all `false` lanes. - /// If two enabled lanes in the scattered vector would write to the same index, - /// only the last lane is guaranteed to actually be written. + /// Writes values from a SIMD vector to multiple potentially discontiguous indices in `slice`. + /// The mask `enable`s all `true` indices and disables all `false` indices. + /// If two enabled elements in the scattered vector would write to the same index, + /// only the last element is guaranteed to actually be written. /// /// # Safety /// @@ -429,22 +612,23 @@ where /// let mut vec: Vec = vec![10, 11, 12, 13, 14, 15, 16, 17, 18]; /// let idxs = Simd::from_array([9, 3, 0, 0]); /// let vals = Simd::from_array([-27, 82, -41, 124]); - /// let enable = Mask::from_array([true, true, true, false]); // Note the mask of the last lane. + /// let enable = Mask::from_array([true, true, true, false]); // Masks the final index /// // If this mask was used to scatter, it would be unsound. Let's fix that. /// let enable = enable & idxs.simd_lt(Simd::splat(vec.len())); /// - /// // We have masked the OOB lane, so it's safe to scatter now. + /// // We have masked the OOB index, so it's safe to scatter now. /// unsafe { vals.scatter_select_unchecked(&mut vec, enable, idxs); } - /// // index 0's second write is masked, thus was omitted. + /// // The second write to index 0 was masked, thus omitted. /// assert_eq!(vec, vec![-41, 11, 12, 82, 14, 15, 16, 17, 18]); /// ``` /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html #[inline] + #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub unsafe fn scatter_select_unchecked( self, slice: &mut [T], - enable: Mask, - idxs: Simd, + enable: Mask, + idxs: Simd, ) { // Safety: This block works with *mut T derived from &mut 'a [T], // which means it is delicate in Rust's borrowing model, circa 2021: @@ -458,36 +642,89 @@ where // 3. &mut [T] which will become our base ptr. unsafe { // Now Entering ☢️ *mut T Zone - let base_ptr = crate::simd::ptr::SimdMutPtr::splat(slice.as_mut_ptr()); + let base_ptr = Simd::<*mut T, N>::splat(slice.as_mut_ptr()); // Ferris forgive me, I have done pointer arithmetic here. let ptrs = base_ptr.wrapping_add(idxs); // The ptrs have been bounds-masked to prevent memory-unsafe writes insha'allah - intrinsics::simd_scatter(self, ptrs, enable.to_int()) + self.scatter_select_ptr(ptrs, enable); // Cleared ☢️ *mut T Zone } } + + /// Write pointers elementwise into a SIMD vector. + /// + /// # Safety + /// + /// Each write must satisfy the same conditions as [`core::ptr::write`]. + /// + /// # Example + /// ``` + /// # #![feature(portable_simd)] + /// # #[cfg(feature = "as_crate")] use core_simd::simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd; + /// # use simd::{Simd, SimdMutPtr}; + /// let mut values = [0; 4]; + /// let offset = Simd::from_array([3, 2, 1, 0]); + /// let ptrs = Simd::splat(values.as_mut_ptr()).wrapping_add(offset); + /// unsafe { Simd::from_array([6, 3, 5, 7]).scatter_ptr(ptrs); } + /// assert_eq!(values, [7, 5, 3, 6]); + /// ``` + #[inline] + #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + pub unsafe fn scatter_ptr(self, dest: Simd<*mut T, N>) { + // Safety: The caller is responsible for upholding all invariants + unsafe { self.scatter_select_ptr(dest, Mask::splat(true)) } + } + + /// Conditionally write pointers elementwise into a SIMD vector. + /// The mask `enable`s all `true` pointers and disables all `false` pointers. + /// If a pointer is disabled, the write to its pointee is skipped. + /// + /// # Safety + /// + /// Enabled pointers must satisfy the same conditions as [`core::ptr::write`]. + /// + /// # Example + /// ``` + /// # #![feature(portable_simd)] + /// # #[cfg(feature = "as_crate")] use core_simd::simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd; + /// # use simd::{Mask, Simd, SimdMutPtr}; + /// let mut values = [0; 4]; + /// let offset = Simd::from_array([3, 2, 1, 0]); + /// let ptrs = Simd::splat(values.as_mut_ptr()).wrapping_add(offset); + /// let enable = Mask::from_array([true, true, false, false]); + /// unsafe { Simd::from_array([6, 3, 5, 7]).scatter_select_ptr(ptrs, enable); } + /// assert_eq!(values, [0, 0, 3, 6]); + /// ``` + #[inline] + #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + pub unsafe fn scatter_select_ptr(self, dest: Simd<*mut T, N>, enable: Mask) { + // Safety: The caller is responsible for upholding all invariants + unsafe { intrinsics::simd_scatter(self, dest, enable.to_int()) } + } } -impl Copy for Simd +impl Copy for Simd where + LaneCount: SupportedLaneCount, T: SimdElement, - LaneCount: SupportedLaneCount, { } -impl Clone for Simd +impl Clone for Simd where + LaneCount: SupportedLaneCount, T: SimdElement, - LaneCount: SupportedLaneCount, { fn clone(&self) -> Self { *self } } -impl Default for Simd +impl Default for Simd where - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, T: SimdElement + Default, { #[inline] @@ -496,20 +733,20 @@ where } } -impl PartialEq for Simd +impl PartialEq for Simd where - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, T: SimdElement + PartialEq, { #[inline] fn eq(&self, other: &Self) -> bool { // Safety: All SIMD vectors are SimdPartialEq, and the comparison produces a valid mask. let mask = unsafe { - let tfvec: Simd<::Mask, LANES> = intrinsics::simd_eq(*self, *other); + let tfvec: Simd<::Mask, N> = intrinsics::simd_eq(*self, *other); Mask::from_int_unchecked(tfvec) }; - // Two vectors are equal if all lanes tested true for vertical equality. + // Two vectors are equal if all elements are equal when compared elementwise mask.all() } @@ -518,18 +755,18 @@ where fn ne(&self, other: &Self) -> bool { // Safety: All SIMD vectors are SimdPartialEq, and the comparison produces a valid mask. let mask = unsafe { - let tfvec: Simd<::Mask, LANES> = intrinsics::simd_ne(*self, *other); + let tfvec: Simd<::Mask, N> = intrinsics::simd_ne(*self, *other); Mask::from_int_unchecked(tfvec) }; - // Two vectors are non-equal if any lane tested true for vertical non-equality. + // Two vectors are non-equal if any elements are non-equal when compared elementwise mask.any() } } -impl PartialOrd for Simd +impl PartialOrd for Simd where - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, T: SimdElement + PartialOrd, { #[inline] @@ -539,16 +776,16 @@ where } } -impl Eq for Simd +impl Eq for Simd where - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, T: SimdElement + Eq, { } -impl Ord for Simd +impl Ord for Simd where - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, T: SimdElement + Ord, { #[inline] @@ -558,9 +795,9 @@ where } } -impl core::hash::Hash for Simd +impl core::hash::Hash for Simd where - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, T: SimdElement + core::hash::Hash, { #[inline] @@ -573,72 +810,96 @@ where } // array references -impl AsRef<[T; LANES]> for Simd +impl AsRef<[T; N]> for Simd where - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, T: SimdElement, { #[inline] - fn as_ref(&self) -> &[T; LANES] { - &self.0 + fn as_ref(&self) -> &[T; N] { + self.as_array() } } -impl AsMut<[T; LANES]> for Simd +impl AsMut<[T; N]> for Simd where - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, T: SimdElement, { #[inline] - fn as_mut(&mut self) -> &mut [T; LANES] { - &mut self.0 + fn as_mut(&mut self) -> &mut [T; N] { + self.as_mut_array() } } // slice references -impl AsRef<[T]> for Simd +impl AsRef<[T]> for Simd where - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, T: SimdElement, { #[inline] fn as_ref(&self) -> &[T] { - &self.0 + self.as_array() } } -impl AsMut<[T]> for Simd +impl AsMut<[T]> for Simd where - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, T: SimdElement, { #[inline] fn as_mut(&mut self) -> &mut [T] { - &mut self.0 + self.as_mut_array() } } // vector/array conversion -impl From<[T; LANES]> for Simd +impl From<[T; N]> for Simd where - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, T: SimdElement, { - fn from(array: [T; LANES]) -> Self { - Self(array) + fn from(array: [T; N]) -> Self { + Self::from_array(array) } } -impl From> for [T; LANES] +impl From> for [T; N] where - LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, T: SimdElement, { - fn from(vector: Simd) -> Self { + fn from(vector: Simd) -> Self { vector.to_array() } } +impl TryFrom<&[T]> for Simd +where + LaneCount: SupportedLaneCount, + T: SimdElement, +{ + type Error = core::array::TryFromSliceError; + + fn try_from(slice: &[T]) -> Result { + Ok(Self::from_array(slice.try_into()?)) + } +} + +impl TryFrom<&mut [T]> for Simd +where + LaneCount: SupportedLaneCount, + T: SimdElement, +{ + type Error = core::array::TryFromSliceError; + + fn try_from(slice: &mut [T]) -> Result { + Ok(Self::from_array(slice.try_into()?)) + } +} + mod sealed { pub trait Sealed {} } @@ -740,3 +1001,27 @@ impl Sealed for f64 {} unsafe impl SimdElement for f64 { type Mask = i64; } + +impl Sealed for *const T {} + +// Safety: (thin) const pointers are valid SIMD element types, and are supported by this API +// +// Fat pointers may be supported in the future. +unsafe impl SimdElement for *const T +where + T: core::ptr::Pointee, +{ + type Mask = isize; +} + +impl Sealed for *mut T {} + +// Safety: (thin) mut pointers are valid SIMD element types, and are supported by this API +// +// Fat pointers may be supported in the future. +unsafe impl SimdElement for *mut T +where + T: core::ptr::Pointee, +{ + type Mask = isize; +} diff --git a/library/portable-simd/crates/core_simd/src/vector/float.rs b/library/portable-simd/crates/core_simd/src/vector/float.rs deleted file mode 100644 index f836c99b1e2..00000000000 --- a/library/portable-simd/crates/core_simd/src/vector/float.rs +++ /dev/null @@ -1,24 +0,0 @@ -#![allow(non_camel_case_types)] - -use crate::simd::Simd; - -/// A 64-bit SIMD vector with two elements of type `f32`. -pub type f32x2 = Simd; - -/// A 128-bit SIMD vector with four elements of type `f32`. -pub type f32x4 = Simd; - -/// A 256-bit SIMD vector with eight elements of type `f32`. -pub type f32x8 = Simd; - -/// A 512-bit SIMD vector with 16 elements of type `f32`. -pub type f32x16 = Simd; - -/// A 128-bit SIMD vector with two elements of type `f64`. -pub type f64x2 = Simd; - -/// A 256-bit SIMD vector with four elements of type `f64`. -pub type f64x4 = Simd; - -/// A 512-bit SIMD vector with eight elements of type `f64`. -pub type f64x8 = Simd; diff --git a/library/portable-simd/crates/core_simd/src/vector/int.rs b/library/portable-simd/crates/core_simd/src/vector/int.rs deleted file mode 100644 index 20e56c7dc64..00000000000 --- a/library/portable-simd/crates/core_simd/src/vector/int.rs +++ /dev/null @@ -1,63 +0,0 @@ -#![allow(non_camel_case_types)] - -use crate::simd::Simd; - -/// A SIMD vector with two elements of type `isize`. -pub type isizex2 = Simd; - -/// A SIMD vector with four elements of type `isize`. -pub type isizex4 = Simd; - -/// A SIMD vector with eight elements of type `isize`. -pub type isizex8 = Simd; - -/// A 32-bit SIMD vector with two elements of type `i16`. -pub type i16x2 = Simd; - -/// A 64-bit SIMD vector with four elements of type `i16`. -pub type i16x4 = Simd; - -/// A 128-bit SIMD vector with eight elements of type `i16`. -pub type i16x8 = Simd; - -/// A 256-bit SIMD vector with 16 elements of type `i16`. -pub type i16x16 = Simd; - -/// A 512-bit SIMD vector with 32 elements of type `i16`. -pub type i16x32 = Simd; - -/// A 64-bit SIMD vector with two elements of type `i32`. -pub type i32x2 = Simd; - -/// A 128-bit SIMD vector with four elements of type `i32`. -pub type i32x4 = Simd; - -/// A 256-bit SIMD vector with eight elements of type `i32`. -pub type i32x8 = Simd; - -/// A 512-bit SIMD vector with 16 elements of type `i32`. -pub type i32x16 = Simd; - -/// A 128-bit SIMD vector with two elements of type `i64`. -pub type i64x2 = Simd; - -/// A 256-bit SIMD vector with four elements of type `i64`. -pub type i64x4 = Simd; - -/// A 512-bit SIMD vector with eight elements of type `i64`. -pub type i64x8 = Simd; - -/// A 32-bit SIMD vector with four elements of type `i8`. -pub type i8x4 = Simd; - -/// A 64-bit SIMD vector with eight elements of type `i8`. -pub type i8x8 = Simd; - -/// A 128-bit SIMD vector with 16 elements of type `i8`. -pub type i8x16 = Simd; - -/// A 256-bit SIMD vector with 32 elements of type `i8`. -pub type i8x32 = Simd; - -/// A 512-bit SIMD vector with 64 elements of type `i8`. -pub type i8x64 = Simd; diff --git a/library/portable-simd/crates/core_simd/src/vector/ptr.rs b/library/portable-simd/crates/core_simd/src/vector/ptr.rs deleted file mode 100644 index fa756344db9..00000000000 --- a/library/portable-simd/crates/core_simd/src/vector/ptr.rs +++ /dev/null @@ -1,51 +0,0 @@ -//! Private implementation details of public gather/scatter APIs. -use crate::simd::intrinsics; -use crate::simd::{LaneCount, Simd, SupportedLaneCount}; - -/// A vector of *const T. -#[derive(Debug, Copy, Clone)] -#[repr(simd)] -pub(crate) struct SimdConstPtr([*const T; LANES]); - -impl SimdConstPtr -where - LaneCount: SupportedLaneCount, - T: Sized, -{ - #[inline] - #[must_use] - pub fn splat(ptr: *const T) -> Self { - Self([ptr; LANES]) - } - - #[inline] - #[must_use] - pub fn wrapping_add(self, addend: Simd) -> Self { - // Safety: this intrinsic doesn't have a precondition - unsafe { intrinsics::simd_arith_offset(self, addend) } - } -} - -/// A vector of *mut T. Be very careful around potential aliasing. -#[derive(Debug, Copy, Clone)] -#[repr(simd)] -pub(crate) struct SimdMutPtr([*mut T; LANES]); - -impl SimdMutPtr -where - LaneCount: SupportedLaneCount, - T: Sized, -{ - #[inline] - #[must_use] - pub fn splat(ptr: *mut T) -> Self { - Self([ptr; LANES]) - } - - #[inline] - #[must_use] - pub fn wrapping_add(self, addend: Simd) -> Self { - // Safety: this intrinsic doesn't have a precondition - unsafe { intrinsics::simd_arith_offset(self, addend) } - } -} diff --git a/library/portable-simd/crates/core_simd/src/vector/uint.rs b/library/portable-simd/crates/core_simd/src/vector/uint.rs deleted file mode 100644 index b4a69c44363..00000000000 --- a/library/portable-simd/crates/core_simd/src/vector/uint.rs +++ /dev/null @@ -1,63 +0,0 @@ -#![allow(non_camel_case_types)] - -use crate::simd::Simd; - -/// A SIMD vector with two elements of type `usize`. -pub type usizex2 = Simd; - -/// A SIMD vector with four elements of type `usize`. -pub type usizex4 = Simd; - -/// A SIMD vector with eight elements of type `usize`. -pub type usizex8 = Simd; - -/// A 32-bit SIMD vector with two elements of type `u16`. -pub type u16x2 = Simd; - -/// A 64-bit SIMD vector with four elements of type `u16`. -pub type u16x4 = Simd; - -/// A 128-bit SIMD vector with eight elements of type `u16`. -pub type u16x8 = Simd; - -/// A 256-bit SIMD vector with 16 elements of type `u16`. -pub type u16x16 = Simd; - -/// A 512-bit SIMD vector with 32 elements of type `u16`. -pub type u16x32 = Simd; - -/// A 64-bit SIMD vector with two elements of type `u32`. -pub type u32x2 = Simd; - -/// A 128-bit SIMD vector with four elements of type `u32`. -pub type u32x4 = Simd; - -/// A 256-bit SIMD vector with eight elements of type `u32`. -pub type u32x8 = Simd; - -/// A 512-bit SIMD vector with 16 elements of type `u32`. -pub type u32x16 = Simd; - -/// A 128-bit SIMD vector with two elements of type `u64`. -pub type u64x2 = Simd; - -/// A 256-bit SIMD vector with four elements of type `u64`. -pub type u64x4 = Simd; - -/// A 512-bit SIMD vector with eight elements of type `u64`. -pub type u64x8 = Simd; - -/// A 32-bit SIMD vector with four elements of type `u8`. -pub type u8x4 = Simd; - -/// A 64-bit SIMD vector with eight elements of type `u8`. -pub type u8x8 = Simd; - -/// A 128-bit SIMD vector with 16 elements of type `u8`. -pub type u8x16 = Simd; - -/// A 256-bit SIMD vector with 32 elements of type `u8`. -pub type u8x32 = Simd; - -/// A 512-bit SIMD vector with 64 elements of type `u8`. -pub type u8x64 = Simd; diff --git a/library/portable-simd/crates/core_simd/tests/autoderef.rs b/library/portable-simd/crates/core_simd/tests/autoderef.rs index 9359da16ee5..3181826ef59 100644 --- a/library/portable-simd/crates/core_simd/tests/autoderef.rs +++ b/library/portable-simd/crates/core_simd/tests/autoderef.rs @@ -1,6 +1,6 @@ // Test that we handle all our "auto-deref" cases correctly. #![feature(portable_simd)] -use core_simd::f32x4; +use core_simd::simd::f32x4; #[cfg(target_arch = "wasm32")] use wasm_bindgen_test::*; diff --git a/library/portable-simd/crates/core_simd/tests/mask_ops_impl/mask_macros.rs b/library/portable-simd/crates/core_simd/tests/mask_ops_impl/mask_macros.rs index 795f9e27c44..faafa5fa51f 100644 --- a/library/portable-simd/crates/core_simd/tests/mask_ops_impl/mask_macros.rs +++ b/library/portable-simd/crates/core_simd/tests/mask_ops_impl/mask_macros.rs @@ -2,7 +2,7 @@ macro_rules! mask_tests { { $vector:ident, $lanes:literal } => { #[cfg(test)] mod $vector { - use core_simd::$vector as Vector; + use core_simd::simd::$vector as Vector; const LANES: usize = $lanes; #[cfg(target_arch = "wasm32")] diff --git a/library/portable-simd/crates/core_simd/tests/masks.rs b/library/portable-simd/crates/core_simd/tests/masks.rs index 673d0db93fe..9f8bad1c36c 100644 --- a/library/portable-simd/crates/core_simd/tests/masks.rs +++ b/library/portable-simd/crates/core_simd/tests/masks.rs @@ -13,11 +13,13 @@ macro_rules! test_mask_api { #[cfg(target_arch = "wasm32")] use wasm_bindgen_test::*; + use core_simd::simd::Mask; + #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn set_and_test() { let values = [true, false, false, true, false, false, true, false]; - let mut mask = core_simd::Mask::<$type, 8>::splat(false); + let mut mask = Mask::<$type, 8>::splat(false); for (lane, value) in values.iter().copied().enumerate() { mask.set(lane, value); } @@ -29,7 +31,7 @@ macro_rules! test_mask_api { #[test] #[should_panic] fn set_invalid_lane() { - let mut mask = core_simd::Mask::<$type, 8>::splat(false); + let mut mask = Mask::<$type, 8>::splat(false); mask.set(8, true); let _ = mask; } @@ -37,24 +39,24 @@ macro_rules! test_mask_api { #[test] #[should_panic] fn test_invalid_lane() { - let mask = core_simd::Mask::<$type, 8>::splat(false); + let mask = Mask::<$type, 8>::splat(false); let _ = mask.test(8); } #[test] fn any() { - assert!(!core_simd::Mask::<$type, 8>::splat(false).any()); - assert!(core_simd::Mask::<$type, 8>::splat(true).any()); - let mut v = core_simd::Mask::<$type, 8>::splat(false); + assert!(!Mask::<$type, 8>::splat(false).any()); + assert!(Mask::<$type, 8>::splat(true).any()); + let mut v = Mask::<$type, 8>::splat(false); v.set(2, true); assert!(v.any()); } #[test] fn all() { - assert!(!core_simd::Mask::<$type, 8>::splat(false).all()); - assert!(core_simd::Mask::<$type, 8>::splat(true).all()); - let mut v = core_simd::Mask::<$type, 8>::splat(false); + assert!(!Mask::<$type, 8>::splat(false).all()); + assert!(Mask::<$type, 8>::splat(true).all()); + let mut v = Mask::<$type, 8>::splat(false); v.set(2, true); assert!(!v.all()); } @@ -62,57 +64,57 @@ macro_rules! test_mask_api { #[test] fn roundtrip_int_conversion() { let values = [true, false, false, true, false, false, true, false]; - let mask = core_simd::Mask::<$type, 8>::from_array(values); + let mask = Mask::<$type, 8>::from_array(values); let int = mask.to_int(); assert_eq!(int.to_array(), [-1, 0, 0, -1, 0, 0, -1, 0]); - assert_eq!(core_simd::Mask::<$type, 8>::from_int(int), mask); + assert_eq!(Mask::<$type, 8>::from_int(int), mask); } #[test] fn roundtrip_bitmask_conversion() { - use core_simd::ToBitMask; + use core_simd::simd::ToBitMask; let values = [ true, false, false, true, false, false, true, false, true, true, false, false, false, false, false, true, ]; - let mask = core_simd::Mask::<$type, 16>::from_array(values); + let mask = Mask::<$type, 16>::from_array(values); let bitmask = mask.to_bitmask(); assert_eq!(bitmask, 0b1000001101001001); - assert_eq!(core_simd::Mask::<$type, 16>::from_bitmask(bitmask), mask); + assert_eq!(Mask::<$type, 16>::from_bitmask(bitmask), mask); } #[test] fn roundtrip_bitmask_conversion_short() { - use core_simd::ToBitMask; + use core_simd::simd::ToBitMask; let values = [ false, false, false, true, ]; - let mask = core_simd::Mask::<$type, 4>::from_array(values); + let mask = Mask::<$type, 4>::from_array(values); let bitmask = mask.to_bitmask(); assert_eq!(bitmask, 0b1000); - assert_eq!(core_simd::Mask::<$type, 4>::from_bitmask(bitmask), mask); + assert_eq!(Mask::<$type, 4>::from_bitmask(bitmask), mask); let values = [true, false]; - let mask = core_simd::Mask::<$type, 2>::from_array(values); + let mask = Mask::<$type, 2>::from_array(values); let bitmask = mask.to_bitmask(); assert_eq!(bitmask, 0b01); - assert_eq!(core_simd::Mask::<$type, 2>::from_bitmask(bitmask), mask); + assert_eq!(Mask::<$type, 2>::from_bitmask(bitmask), mask); } #[test] fn cast() { - fn cast_impl() + fn cast_impl() where - core_simd::Mask<$type, 8>: Into>, + Mask<$type, 8>: Into>, { let values = [true, false, false, true, false, false, true, false]; - let mask = core_simd::Mask::<$type, 8>::from_array(values); + let mask = Mask::<$type, 8>::from_array(values); let cast_mask = mask.cast::(); assert_eq!(values, cast_mask.to_array()); - let into_mask: core_simd::Mask = mask.into(); + let into_mask: Mask = mask.into(); assert_eq!(values, into_mask.to_array()); } @@ -126,15 +128,15 @@ macro_rules! test_mask_api { #[cfg(feature = "generic_const_exprs")] #[test] fn roundtrip_bitmask_array_conversion() { - use core_simd::ToBitMaskArray; + use core_simd::simd::ToBitMaskArray; let values = [ true, false, false, true, false, false, true, false, true, true, false, false, false, false, false, true, ]; - let mask = core_simd::Mask::<$type, 16>::from_array(values); + let mask = Mask::<$type, 16>::from_array(values); let bitmask = mask.to_bitmask_array(); assert_eq!(bitmask, [0b01001001, 0b10000011]); - assert_eq!(core_simd::Mask::<$type, 16>::from_bitmask_array(bitmask), mask); + assert_eq!(Mask::<$type, 16>::from_bitmask_array(bitmask), mask); } } } @@ -150,9 +152,10 @@ mod mask_api { #[test] fn convert() { + use core_simd::simd::Mask; let values = [true, false, false, true, false, false, true, false]; assert_eq!( - core_simd::Mask::::from_array(values), - core_simd::Mask::::from_array(values).into() + Mask::::from_array(values), + Mask::::from_array(values).into() ); } diff --git a/library/portable-simd/crates/core_simd/tests/ops_macros.rs b/library/portable-simd/crates/core_simd/tests/ops_macros.rs index f759394d075..3a02f3f01e1 100644 --- a/library/portable-simd/crates/core_simd/tests/ops_macros.rs +++ b/library/portable-simd/crates/core_simd/tests/ops_macros.rs @@ -7,7 +7,7 @@ macro_rules! impl_unary_op_test { test_helpers::test_lanes! { fn $fn() { test_helpers::test_unary_elementwise( - & as core::ops::$trait>::$fn, + & as core::ops::$trait>::$fn, &$scalar_fn, &|_| true, ); @@ -27,7 +27,7 @@ macro_rules! impl_binary_op_test { { $scalar:ty, $trait:ident :: $fn:ident, $trait_assign:ident :: $fn_assign:ident, $scalar_fn:expr } => { mod $fn { use super::*; - use core_simd::Simd; + use core_simd::simd::Simd; test_helpers::test_lanes! { fn normal() { @@ -64,7 +64,7 @@ macro_rules! impl_binary_checked_op_test { { $scalar:ty, $trait:ident :: $fn:ident, $trait_assign:ident :: $fn_assign:ident, $scalar_fn:expr, $check_fn:expr } => { mod $fn { use super::*; - use core_simd::Simd; + use core_simd::simd::Simd; test_helpers::test_lanes! { fn normal() { @@ -173,7 +173,7 @@ macro_rules! impl_signed_tests { { $scalar:tt } => { mod $scalar { use core_simd::simd::SimdInt; - type Vector = core_simd::Simd; + type Vector = core_simd::simd::Simd; type Scalar = $scalar; impl_common_integer_tests! { Vector, Scalar } @@ -314,7 +314,7 @@ macro_rules! impl_unsigned_tests { { $scalar:tt } => { mod $scalar { use core_simd::simd::SimdUint; - type Vector = core_simd::Simd; + type Vector = core_simd::simd::Simd; type Scalar = $scalar; impl_common_integer_tests! { Vector, Scalar } @@ -348,8 +348,8 @@ macro_rules! impl_unsigned_tests { macro_rules! impl_float_tests { { $scalar:tt, $int_scalar:tt } => { mod $scalar { - use core_simd::SimdFloat; - type Vector = core_simd::Simd; + use core_simd::simd::SimdFloat; + type Vector = core_simd::simd::Simd; type Scalar = $scalar; impl_unary_op_test!(Scalar, Neg::neg); diff --git a/library/portable-simd/crates/core_simd/tests/pointers.rs b/library/portable-simd/crates/core_simd/tests/pointers.rs new file mode 100644 index 00000000000..0ae8f83b8b9 --- /dev/null +++ b/library/portable-simd/crates/core_simd/tests/pointers.rs @@ -0,0 +1,111 @@ +#![feature(portable_simd, strict_provenance)] + +use core_simd::simd::{Simd, SimdConstPtr, SimdMutPtr}; + +macro_rules! common_tests { + { $constness:ident } => { + test_helpers::test_lanes! { + fn is_null() { + test_helpers::test_unary_mask_elementwise( + &Simd::<*$constness u32, LANES>::is_null, + &<*$constness u32>::is_null, + &|_| true, + ); + } + + fn addr() { + test_helpers::test_unary_elementwise( + &Simd::<*$constness u32, LANES>::addr, + &<*$constness u32>::addr, + &|_| true, + ); + } + + fn with_addr() { + test_helpers::test_binary_elementwise( + &Simd::<*$constness u32, LANES>::with_addr, + &<*$constness u32>::with_addr, + &|_, _| true, + ); + } + + fn expose_addr() { + test_helpers::test_unary_elementwise( + &Simd::<*$constness u32, LANES>::expose_addr, + &<*$constness u32>::expose_addr, + &|_| true, + ); + } + + fn wrapping_offset() { + test_helpers::test_binary_elementwise( + &Simd::<*$constness u32, LANES>::wrapping_offset, + &<*$constness u32>::wrapping_offset, + &|_, _| true, + ); + } + + fn wrapping_add() { + test_helpers::test_binary_elementwise( + &Simd::<*$constness u32, LANES>::wrapping_add, + &<*$constness u32>::wrapping_add, + &|_, _| true, + ); + } + + fn wrapping_sub() { + test_helpers::test_binary_elementwise( + &Simd::<*$constness u32, LANES>::wrapping_sub, + &<*$constness u32>::wrapping_sub, + &|_, _| true, + ); + } + } + } +} + +mod const_ptr { + use super::*; + common_tests! { const } + + test_helpers::test_lanes! { + fn cast_mut() { + test_helpers::test_unary_elementwise( + &Simd::<*const u32, LANES>::cast_mut, + &<*const u32>::cast_mut, + &|_| true, + ); + } + + fn from_exposed_addr() { + test_helpers::test_unary_elementwise( + &Simd::<*const u32, LANES>::from_exposed_addr, + &core::ptr::from_exposed_addr::, + &|_| true, + ); + } + } +} + +mod mut_ptr { + use super::*; + common_tests! { mut } + + test_helpers::test_lanes! { + fn cast_const() { + test_helpers::test_unary_elementwise( + &Simd::<*mut u32, LANES>::cast_const, + &<*mut u32>::cast_const, + &|_| true, + ); + } + + fn from_exposed_addr() { + test_helpers::test_unary_elementwise( + &Simd::<*mut u32, LANES>::from_exposed_addr, + &core::ptr::from_exposed_addr_mut::, + &|_| true, + ); + } + } +} diff --git a/library/portable-simd/crates/core_simd/tests/round.rs b/library/portable-simd/crates/core_simd/tests/round.rs index 484fd5bf47d..8b9638ad466 100644 --- a/library/portable-simd/crates/core_simd/tests/round.rs +++ b/library/portable-simd/crates/core_simd/tests/round.rs @@ -5,7 +5,7 @@ macro_rules! float_rounding_test { mod $scalar { use std_float::StdFloat; - type Vector = core_simd::Simd<$scalar, LANES>; + type Vector = core_simd::simd::Simd<$scalar, LANES>; type Scalar = $scalar; type IntScalar = $int_scalar; diff --git a/library/portable-simd/crates/core_simd/tests/swizzle.rs b/library/portable-simd/crates/core_simd/tests/swizzle.rs index 51c63611aba..8cd7c33e823 100644 --- a/library/portable-simd/crates/core_simd/tests/swizzle.rs +++ b/library/portable-simd/crates/core_simd/tests/swizzle.rs @@ -1,5 +1,5 @@ #![feature(portable_simd)] -use core_simd::{Simd, Swizzle}; +use core_simd::simd::{Simd, Swizzle}; #[cfg(target_arch = "wasm32")] use wasm_bindgen_test::*; @@ -60,3 +60,17 @@ fn interleave() { assert_eq!(even, a); assert_eq!(odd, b); } + +// portable-simd#298 +#[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +fn interleave_one() { + let a = Simd::from_array([0]); + let b = Simd::from_array([1]); + let (lo, hi) = a.interleave(b); + assert_eq!(lo.to_array(), [0]); + assert_eq!(hi.to_array(), [1]); + let (even, odd) = lo.deinterleave(hi); + assert_eq!(even, a); + assert_eq!(odd, b); +} diff --git a/library/portable-simd/crates/core_simd/tests/swizzle_dyn.rs b/library/portable-simd/crates/core_simd/tests/swizzle_dyn.rs new file mode 100644 index 00000000000..646cd5f3383 --- /dev/null +++ b/library/portable-simd/crates/core_simd/tests/swizzle_dyn.rs @@ -0,0 +1,74 @@ +#![feature(portable_simd)] +use core::{fmt, ops::RangeInclusive}; +use proptest; +use test_helpers::{self, biteq, make_runner, prop_assert_biteq}; + +fn swizzle_dyn_scalar_ver(values: [u8; N], idxs: [u8; N]) -> [u8; N] { + let mut array = [0; N]; + for (i, k) in idxs.into_iter().enumerate() { + if (k as usize) < N { + array[i] = values[k as usize]; + }; + } + array +} + +test_helpers::test_lanes! { + fn swizzle_dyn() { + match_simd_with_fallback( + &core_simd::simd::Simd::::swizzle_dyn, + &swizzle_dyn_scalar_ver, + &|_, _| true, + ); + } +} + +fn match_simd_with_fallback( + fv: &dyn Fn(Vector, Vector) -> VectorResult, + fs: &dyn Fn([Scalar; N], [Scalar; N]) -> [ScalarResult; N], + check: &dyn Fn([Scalar; N], [Scalar; N]) -> bool, +) where + Scalar: Copy + fmt::Debug + SwizzleStrategy, + ScalarResult: Copy + biteq::BitEq + fmt::Debug + SwizzleStrategy, + Vector: Into<[Scalar; N]> + From<[Scalar; N]> + Copy, + VectorResult: Into<[ScalarResult; N]> + From<[ScalarResult; N]> + Copy, +{ + test_swizzles_2(&|x: [Scalar; N], y: [Scalar; N]| { + proptest::prop_assume!(check(x, y)); + let result_v: [ScalarResult; N] = fv(x.into(), y.into()).into(); + let result_s: [ScalarResult; N] = fs(x, y); + crate::prop_assert_biteq!(result_v, result_s); + Ok(()) + }); +} + +fn test_swizzles_2( + f: &dyn Fn(A, B) -> proptest::test_runner::TestCaseResult, +) { + let mut runner = make_runner(); + runner + .run( + &(A::swizzled_strategy(), B::swizzled_strategy()), + |(a, b)| f(a, b), + ) + .unwrap(); +} + +pub trait SwizzleStrategy { + type Strategy: proptest::strategy::Strategy; + fn swizzled_strategy() -> Self::Strategy; +} + +impl SwizzleStrategy for u8 { + type Strategy = RangeInclusive; + fn swizzled_strategy() -> Self::Strategy { + 0..=64 + } +} + +impl SwizzleStrategy for [T; N] { + type Strategy = test_helpers::array::UniformArrayStrategy; + fn swizzled_strategy() -> Self::Strategy { + Self::Strategy::new(T::swizzled_strategy()) + } +} diff --git a/library/portable-simd/crates/core_simd/tests/to_bytes.rs b/library/portable-simd/crates/core_simd/tests/to_bytes.rs index debb4335e2c..be0ee4349c5 100644 --- a/library/portable-simd/crates/core_simd/tests/to_bytes.rs +++ b/library/portable-simd/crates/core_simd/tests/to_bytes.rs @@ -2,7 +2,7 @@ #![allow(incomplete_features)] #![cfg(feature = "generic_const_exprs")] -use core_simd::Simd; +use core_simd::simd::Simd; #[test] fn byte_convert() { diff --git a/library/portable-simd/crates/core_simd/tests/try_from_slice.rs b/library/portable-simd/crates/core_simd/tests/try_from_slice.rs new file mode 100644 index 00000000000..859e3b94f2c --- /dev/null +++ b/library/portable-simd/crates/core_simd/tests/try_from_slice.rs @@ -0,0 +1,25 @@ +#![feature(portable_simd)] + +#[cfg(target_arch = "wasm32")] +use wasm_bindgen_test::*; + +#[cfg(target_arch = "wasm32")] +wasm_bindgen_test_configure!(run_in_browser); + +use core_simd::simd::i32x4; + +#[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +fn try_from_slice() { + // Equal length + assert_eq!( + i32x4::try_from([1, 2, 3, 4].as_slice()).unwrap(), + i32x4::from_array([1, 2, 3, 4]) + ); + + // Slice length > vector length + assert!(i32x4::try_from([1, 2, 3, 4, 5].as_slice()).is_err()); + + // Slice length < vector length + assert!(i32x4::try_from([1, 2, 3].as_slice()).is_err()); +} diff --git a/library/portable-simd/crates/test_helpers/Cargo.toml b/library/portable-simd/crates/test_helpers/Cargo.toml index a04b0961d7f..1d2bc8b519a 100644 --- a/library/portable-simd/crates/test_helpers/Cargo.toml +++ b/library/portable-simd/crates/test_helpers/Cargo.toml @@ -8,3 +8,6 @@ publish = false version = "0.10" default-features = false features = ["alloc"] + +[features] +all_lane_counts = [] diff --git a/library/portable-simd/crates/test_helpers/src/array.rs b/library/portable-simd/crates/test_helpers/src/array.rs index 5ffc9226976..984a427320d 100644 --- a/library/portable-simd/crates/test_helpers/src/array.rs +++ b/library/portable-simd/crates/test_helpers/src/array.rs @@ -41,6 +41,7 @@ where fn new_tree(&self, runner: &mut TestRunner) -> NewTree { let tree: [S::Tree; LANES] = unsafe { + #[allow(clippy::uninit_assumed_init)] let mut tree: [MaybeUninit; LANES] = MaybeUninit::uninit().assume_init(); for t in tree.iter_mut() { *t = MaybeUninit::new(self.strategy.new_tree(runner)?) @@ -60,6 +61,7 @@ impl ValueTree for ArrayValueTree<[T; LANES]> fn current(&self) -> Self::Value { unsafe { + #[allow(clippy::uninit_assumed_init)] let mut value: [MaybeUninit; LANES] = MaybeUninit::uninit().assume_init(); for (tree_elem, value_elem) in self.tree.iter().zip(value.iter_mut()) { *value_elem = MaybeUninit::new(tree_elem.current()); diff --git a/library/portable-simd/crates/test_helpers/src/biteq.rs b/library/portable-simd/crates/test_helpers/src/biteq.rs index 00350e22418..7d91260d838 100644 --- a/library/portable-simd/crates/test_helpers/src/biteq.rs +++ b/library/portable-simd/crates/test_helpers/src/biteq.rs @@ -55,6 +55,26 @@ macro_rules! impl_float_biteq { impl_float_biteq! { f32, f64 } +impl BitEq for *const T { + fn biteq(&self, other: &Self) -> bool { + self == other + } + + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + write!(f, "{:?}", self) + } +} + +impl BitEq for *mut T { + fn biteq(&self, other: &Self) -> bool { + self == other + } + + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + write!(f, "{:?}", self) + } +} + impl BitEq for [T; N] { fn biteq(&self, other: &Self) -> bool { self.iter() diff --git a/library/portable-simd/crates/test_helpers/src/lib.rs b/library/portable-simd/crates/test_helpers/src/lib.rs index 141bee18a9a..b26cdc311a2 100644 --- a/library/portable-simd/crates/test_helpers/src/lib.rs +++ b/library/portable-simd/crates/test_helpers/src/lib.rs @@ -38,6 +38,28 @@ impl_num! { usize } impl_num! { f32 } impl_num! { f64 } +impl DefaultStrategy for *const T { + type Strategy = proptest::strategy::Map *const T>; + fn default_strategy() -> Self::Strategy { + fn map(x: isize) -> *const T { + x as _ + } + use proptest::strategy::Strategy; + proptest::num::isize::ANY.prop_map(map) + } +} + +impl DefaultStrategy for *mut T { + type Strategy = proptest::strategy::Map *mut T>; + fn default_strategy() -> Self::Strategy { + fn map(x: isize) -> *mut T { + x as _ + } + use proptest::strategy::Strategy; + proptest::num::isize::ANY.prop_map(map) + } +} + #[cfg(not(target_arch = "wasm32"))] impl DefaultStrategy for u128 { type Strategy = proptest::num::u128::Any; @@ -135,21 +157,21 @@ pub fn test_unary_elementwise ScalarResult, check: &dyn Fn([Scalar; LANES]) -> bool, ) where - Scalar: Copy + Default + core::fmt::Debug + DefaultStrategy, - ScalarResult: Copy + Default + biteq::BitEq + core::fmt::Debug + DefaultStrategy, + Scalar: Copy + core::fmt::Debug + DefaultStrategy, + ScalarResult: Copy + biteq::BitEq + core::fmt::Debug + DefaultStrategy, Vector: Into<[Scalar; LANES]> + From<[Scalar; LANES]> + Copy, VectorResult: Into<[ScalarResult; LANES]> + From<[ScalarResult; LANES]> + Copy, { test_1(&|x: [Scalar; LANES]| { proptest::prop_assume!(check(x)); let result_1: [ScalarResult; LANES] = fv(x.into()).into(); - let result_2: [ScalarResult; LANES] = { - let mut result = [ScalarResult::default(); LANES]; - for (i, o) in x.iter().zip(result.iter_mut()) { - *o = fs(*i); - } - result - }; + let result_2: [ScalarResult; LANES] = x + .iter() + .copied() + .map(fs) + .collect::>() + .try_into() + .unwrap(); crate::prop_assert_biteq!(result_1, result_2); Ok(()) }); @@ -162,7 +184,7 @@ pub fn test_unary_mask_elementwise( fs: &dyn Fn(Scalar) -> bool, check: &dyn Fn([Scalar; LANES]) -> bool, ) where - Scalar: Copy + Default + core::fmt::Debug + DefaultStrategy, + Scalar: Copy + core::fmt::Debug + DefaultStrategy, Vector: Into<[Scalar; LANES]> + From<[Scalar; LANES]> + Copy, Mask: Into<[bool; LANES]> + From<[bool; LANES]> + Copy, { @@ -196,9 +218,9 @@ pub fn test_binary_elementwise< fs: &dyn Fn(Scalar1, Scalar2) -> ScalarResult, check: &dyn Fn([Scalar1; LANES], [Scalar2; LANES]) -> bool, ) where - Scalar1: Copy + Default + core::fmt::Debug + DefaultStrategy, - Scalar2: Copy + Default + core::fmt::Debug + DefaultStrategy, - ScalarResult: Copy + Default + biteq::BitEq + core::fmt::Debug + DefaultStrategy, + Scalar1: Copy + core::fmt::Debug + DefaultStrategy, + Scalar2: Copy + core::fmt::Debug + DefaultStrategy, + ScalarResult: Copy + biteq::BitEq + core::fmt::Debug + DefaultStrategy, Vector1: Into<[Scalar1; LANES]> + From<[Scalar1; LANES]> + Copy, Vector2: Into<[Scalar2; LANES]> + From<[Scalar2; LANES]> + Copy, VectorResult: Into<[ScalarResult; LANES]> + From<[ScalarResult; LANES]> + Copy, @@ -206,13 +228,14 @@ pub fn test_binary_elementwise< test_2(&|x: [Scalar1; LANES], y: [Scalar2; LANES]| { proptest::prop_assume!(check(x, y)); let result_1: [ScalarResult; LANES] = fv(x.into(), y.into()).into(); - let result_2: [ScalarResult; LANES] = { - let mut result = [ScalarResult::default(); LANES]; - for ((i1, i2), o) in x.iter().zip(y.iter()).zip(result.iter_mut()) { - *o = fs(*i1, *i2); - } - result - }; + let result_2: [ScalarResult; LANES] = x + .iter() + .copied() + .zip(y.iter().copied()) + .map(|(x, y)| fs(x, y)) + .collect::>() + .try_into() + .unwrap(); crate::prop_assert_biteq!(result_1, result_2); Ok(()) }); @@ -333,6 +356,39 @@ pub fn test_ternary_elementwise< ); } +#[doc(hidden)] +#[macro_export] +macro_rules! test_lanes_helper { + ($($(#[$meta:meta])* $fn_name:ident $lanes:literal;)+) => { + $( + #[test] + $(#[$meta])* + fn $fn_name() { + implementation::<$lanes>(); + } + )+ + }; + ( + $(#[$meta:meta])+; + $($(#[$meta_before:meta])+ $fn_name_before:ident $lanes_before:literal;)* + $fn_name:ident $lanes:literal; + $($fn_name_rest:ident $lanes_rest:literal;)* + ) => { + $crate::test_lanes_helper!( + $(#[$meta])+; + $($(#[$meta_before])+ $fn_name_before $lanes_before;)* + $(#[$meta])+ $fn_name $lanes; + $($fn_name_rest $lanes_rest;)* + ); + }; + ( + $(#[$meta_ignored:meta])+; + $($(#[$meta:meta])+ $fn_name:ident $lanes:literal;)+ + ) => { + $crate::test_lanes_helper!($($(#[$meta])+ $fn_name $lanes;)+); + }; +} + /// Expand a const-generic test into separate tests for each possible lane count. #[macro_export] macro_rules! test_lanes { @@ -345,57 +401,96 @@ macro_rules! test_lanes { fn implementation() where - core_simd::LaneCount<$lanes>: core_simd::SupportedLaneCount, + core_simd::simd::LaneCount<$lanes>: core_simd::simd::SupportedLaneCount, $body #[cfg(target_arch = "wasm32")] wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); - #[test] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] - fn lanes_1() { - implementation::<1>(); - } + $crate::test_lanes_helper!( + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]; + lanes_1 1; + lanes_2 2; + lanes_4 4; + ); - #[test] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] - fn lanes_2() { - implementation::<2>(); - } - - #[test] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] - fn lanes_4() { - implementation::<4>(); - } - - #[test] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] #[cfg(not(miri))] // Miri intrinsic implementations are uniform and larger tests are sloooow - fn lanes_8() { - implementation::<8>(); - } + $crate::test_lanes_helper!( + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]; + lanes_8 8; + lanes_16 16; + lanes_32 32; + lanes_64 64; + ); - #[test] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] - #[cfg(not(miri))] // Miri intrinsic implementations are uniform and larger tests are sloooow - fn lanes_16() { - implementation::<16>(); - } + #[cfg(feature = "all_lane_counts")] + $crate::test_lanes_helper!( + // test some odd and even non-power-of-2 lengths on miri + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]; + lanes_3 3; + lanes_5 5; + lanes_6 6; + ); - #[test] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] + #[cfg(feature = "all_lane_counts")] #[cfg(not(miri))] // Miri intrinsic implementations are uniform and larger tests are sloooow - fn lanes_32() { - implementation::<32>(); - } - - #[test] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] - #[cfg(not(miri))] // Miri intrinsic implementations are uniform and larger tests are sloooow - fn lanes_64() { - implementation::<64>(); - } + $crate::test_lanes_helper!( + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]; + lanes_7 7; + lanes_9 9; + lanes_10 10; + lanes_11 11; + lanes_12 12; + lanes_13 13; + lanes_14 14; + lanes_15 15; + lanes_17 17; + lanes_18 18; + lanes_19 19; + lanes_20 20; + lanes_21 21; + lanes_22 22; + lanes_23 23; + lanes_24 24; + lanes_25 25; + lanes_26 26; + lanes_27 27; + lanes_28 28; + lanes_29 29; + lanes_30 30; + lanes_31 31; + lanes_33 33; + lanes_34 34; + lanes_35 35; + lanes_36 36; + lanes_37 37; + lanes_38 38; + lanes_39 39; + lanes_40 40; + lanes_41 41; + lanes_42 42; + lanes_43 43; + lanes_44 44; + lanes_45 45; + lanes_46 46; + lanes_47 47; + lanes_48 48; + lanes_49 49; + lanes_50 50; + lanes_51 51; + lanes_52 52; + lanes_53 53; + lanes_54 54; + lanes_55 55; + lanes_56 56; + lanes_57 57; + lanes_58 58; + lanes_59 59; + lanes_60 60; + lanes_61 61; + lanes_62 62; + lanes_63 63; + ); } )* } @@ -413,50 +508,93 @@ macro_rules! test_lanes_panic { fn implementation() where - core_simd::LaneCount<$lanes>: core_simd::SupportedLaneCount, + core_simd::simd::LaneCount<$lanes>: core_simd::simd::SupportedLaneCount, $body - #[test] - #[should_panic] - fn lanes_1() { - implementation::<1>(); - } + $crate::test_lanes_helper!( + #[should_panic]; + lanes_1 1; + lanes_2 2; + lanes_4 4; + ); - #[test] - #[should_panic] - fn lanes_2() { - implementation::<2>(); - } + #[cfg(not(miri))] // Miri intrinsic implementations are uniform and larger tests are sloooow + $crate::test_lanes_helper!( + #[should_panic]; + lanes_8 8; + lanes_16 16; + lanes_32 32; + lanes_64 64; + ); - #[test] - #[should_panic] - fn lanes_4() { - implementation::<4>(); - } + #[cfg(feature = "all_lane_counts")] + $crate::test_lanes_helper!( + // test some odd and even non-power-of-2 lengths on miri + #[should_panic]; + lanes_3 3; + lanes_5 5; + lanes_6 6; + ); - #[test] - #[should_panic] - fn lanes_8() { - implementation::<8>(); - } - - #[test] - #[should_panic] - fn lanes_16() { - implementation::<16>(); - } - - #[test] - #[should_panic] - fn lanes_32() { - implementation::<32>(); - } - - #[test] - #[should_panic] - fn lanes_64() { - implementation::<64>(); - } + #[cfg(feature = "all_lane_counts")] + #[cfg(not(miri))] // Miri intrinsic implementations are uniform and larger tests are sloooow + $crate::test_lanes_helper!( + #[should_panic]; + lanes_7 7; + lanes_9 9; + lanes_10 10; + lanes_11 11; + lanes_12 12; + lanes_13 13; + lanes_14 14; + lanes_15 15; + lanes_17 17; + lanes_18 18; + lanes_19 19; + lanes_20 20; + lanes_21 21; + lanes_22 22; + lanes_23 23; + lanes_24 24; + lanes_25 25; + lanes_26 26; + lanes_27 27; + lanes_28 28; + lanes_29 29; + lanes_30 30; + lanes_31 31; + lanes_33 33; + lanes_34 34; + lanes_35 35; + lanes_36 36; + lanes_37 37; + lanes_38 38; + lanes_39 39; + lanes_40 40; + lanes_41 41; + lanes_42 42; + lanes_43 43; + lanes_44 44; + lanes_45 45; + lanes_46 46; + lanes_47 47; + lanes_48 48; + lanes_49 49; + lanes_50 50; + lanes_51 51; + lanes_52 52; + lanes_53 53; + lanes_54 54; + lanes_55 55; + lanes_56 56; + lanes_57 57; + lanes_58 58; + lanes_59 59; + lanes_60 60; + lanes_61 61; + lanes_62 62; + lanes_63 63; + ); } )* } diff --git a/library/std/src/os/windows/io/handle.rs b/library/std/src/os/windows/io/handle.rs index 9b77cd8321b..280757a41a2 100644 --- a/library/std/src/os/windows/io/handle.rs +++ b/library/std/src/os/windows/io/handle.rs @@ -9,7 +9,7 @@ use crate::io; use crate::marker::PhantomData; use crate::mem::forget; use crate::ptr; -use crate::sys::c; +use crate::sys; use crate::sys::cvt; use crate::sys_common::{AsInner, FromInner, IntoInner}; @@ -190,14 +190,14 @@ impl BorrowedHandle<'_> { /// object as the existing `BorrowedHandle` instance. #[stable(feature = "io_safety", since = "1.63.0")] pub fn try_clone_to_owned(&self) -> crate::io::Result { - self.duplicate(0, false, c::DUPLICATE_SAME_ACCESS) + self.duplicate(0, false, sys::c::DUPLICATE_SAME_ACCESS) } pub(crate) fn duplicate( &self, - access: c::DWORD, + access: u32, inherit: bool, - options: c::DWORD, + options: u32, ) -> io::Result { let handle = self.as_raw_handle(); @@ -211,14 +211,14 @@ impl BorrowedHandle<'_> { let mut ret = ptr::null_mut(); cvt(unsafe { - let cur_proc = c::GetCurrentProcess(); - c::DuplicateHandle( + let cur_proc = sys::c::GetCurrentProcess(); + sys::c::DuplicateHandle( cur_proc, handle, cur_proc, &mut ret, access, - inherit as c::BOOL, + inherit as sys::c::BOOL, options, ) })?; @@ -233,7 +233,7 @@ impl TryFrom for OwnedHandle { #[inline] fn try_from(handle_or_invalid: HandleOrInvalid) -> Result { let owned_handle = handle_or_invalid.0; - if owned_handle.handle == c::INVALID_HANDLE_VALUE { + if owned_handle.handle == sys::c::INVALID_HANDLE_VALUE { // Don't call `CloseHandle`; it'd be harmless, except that it could // overwrite the `GetLastError` error. forget(owned_handle); @@ -365,7 +365,7 @@ impl Drop for OwnedHandle { #[inline] fn drop(&mut self) { unsafe { - let _ = c::CloseHandle(self.handle); + let _ = sys::c::CloseHandle(self.handle); } } } diff --git a/library/std/src/os/windows/io/raw.rs b/library/std/src/os/windows/io/raw.rs index 49e4f304f5d..1759e2e7f3f 100644 --- a/library/std/src/os/windows/io/raw.rs +++ b/library/std/src/os/windows/io/raw.rs @@ -11,7 +11,6 @@ use crate::os::windows::io::{OwnedHandle, OwnedSocket}; use crate::os::windows::raw; use crate::ptr; use crate::sys; -use crate::sys::c; use crate::sys_common::{self, AsInner, FromInner, IntoInner}; /// Raw HANDLEs. @@ -104,42 +103,42 @@ impl AsRawHandle for fs::File { #[stable(feature = "asraw_stdio", since = "1.21.0")] impl AsRawHandle for io::Stdin { fn as_raw_handle(&self) -> RawHandle { - stdio_handle(unsafe { c::GetStdHandle(c::STD_INPUT_HANDLE) as RawHandle }) + stdio_handle(unsafe { sys::c::GetStdHandle(sys::c::STD_INPUT_HANDLE) as RawHandle }) } } #[stable(feature = "asraw_stdio", since = "1.21.0")] impl AsRawHandle for io::Stdout { fn as_raw_handle(&self) -> RawHandle { - stdio_handle(unsafe { c::GetStdHandle(c::STD_OUTPUT_HANDLE) as RawHandle }) + stdio_handle(unsafe { sys::c::GetStdHandle(sys::c::STD_OUTPUT_HANDLE) as RawHandle }) } } #[stable(feature = "asraw_stdio", since = "1.21.0")] impl AsRawHandle for io::Stderr { fn as_raw_handle(&self) -> RawHandle { - stdio_handle(unsafe { c::GetStdHandle(c::STD_ERROR_HANDLE) as RawHandle }) + stdio_handle(unsafe { sys::c::GetStdHandle(sys::c::STD_ERROR_HANDLE) as RawHandle }) } } #[stable(feature = "asraw_stdio_locks", since = "1.35.0")] impl<'a> AsRawHandle for io::StdinLock<'a> { fn as_raw_handle(&self) -> RawHandle { - stdio_handle(unsafe { c::GetStdHandle(c::STD_INPUT_HANDLE) as RawHandle }) + stdio_handle(unsafe { sys::c::GetStdHandle(sys::c::STD_INPUT_HANDLE) as RawHandle }) } } #[stable(feature = "asraw_stdio_locks", since = "1.35.0")] impl<'a> AsRawHandle for io::StdoutLock<'a> { fn as_raw_handle(&self) -> RawHandle { - stdio_handle(unsafe { c::GetStdHandle(c::STD_OUTPUT_HANDLE) as RawHandle }) + stdio_handle(unsafe { sys::c::GetStdHandle(sys::c::STD_OUTPUT_HANDLE) as RawHandle }) } } #[stable(feature = "asraw_stdio_locks", since = "1.35.0")] impl<'a> AsRawHandle for io::StderrLock<'a> { fn as_raw_handle(&self) -> RawHandle { - stdio_handle(unsafe { c::GetStdHandle(c::STD_ERROR_HANDLE) as RawHandle }) + stdio_handle(unsafe { sys::c::GetStdHandle(sys::c::STD_ERROR_HANDLE) as RawHandle }) } } @@ -152,14 +151,14 @@ fn stdio_handle(raw: RawHandle) -> RawHandle { // console. In that case, return null to the user, which is consistent // with what they'd get in the parent, and which avoids the problem that // `INVALID_HANDLE_VALUE` aliases the current process handle. - if raw == c::INVALID_HANDLE_VALUE { ptr::null_mut() } else { raw } + if raw == sys::c::INVALID_HANDLE_VALUE { ptr::null_mut() } else { raw } } #[stable(feature = "from_raw_os", since = "1.1.0")] impl FromRawHandle for fs::File { #[inline] unsafe fn from_raw_handle(handle: RawHandle) -> fs::File { - let handle = handle as c::HANDLE; + let handle = handle as sys::c::HANDLE; fs::File::from_inner(sys::fs::File::from_inner(FromInner::from_inner( OwnedHandle::from_raw_handle(handle), ))) diff --git a/library/std/src/os/windows/io/socket.rs b/library/std/src/os/windows/io/socket.rs index ce34cd1a9bf..eb6097a89a6 100644 --- a/library/std/src/os/windows/io/socket.rs +++ b/library/std/src/os/windows/io/socket.rs @@ -9,7 +9,6 @@ use crate::marker::PhantomData; use crate::mem; use crate::mem::forget; use crate::sys; -use crate::sys::c; #[cfg(not(target_vendor = "uwp"))] use crate::sys::cvt; @@ -76,7 +75,7 @@ impl BorrowedSocket<'_> { #[rustc_const_stable(feature = "io_safety", since = "1.63.0")] #[stable(feature = "io_safety", since = "1.63.0")] pub const unsafe fn borrow_raw(socket: RawSocket) -> Self { - assert!(socket != c::INVALID_SOCKET as RawSocket); + assert!(socket != sys::c::INVALID_SOCKET as RawSocket); Self { socket, _phantom: PhantomData } } } @@ -94,7 +93,11 @@ impl OwnedSocket { #[cfg(not(target_vendor = "uwp"))] pub(crate) fn set_no_inherit(&self) -> io::Result<()> { cvt(unsafe { - c::SetHandleInformation(self.as_raw_socket() as c::HANDLE, c::HANDLE_FLAG_INHERIT, 0) + sys::c::SetHandleInformation( + self.as_raw_socket() as sys::c::HANDLE, + sys::c::HANDLE_FLAG_INHERIT, + 0, + ) }) .map(drop) } @@ -110,43 +113,47 @@ impl BorrowedSocket<'_> { /// object as the existing `BorrowedSocket` instance. #[stable(feature = "io_safety", since = "1.63.0")] pub fn try_clone_to_owned(&self) -> io::Result { - let mut info = unsafe { mem::zeroed::() }; + let mut info = unsafe { mem::zeroed::() }; let result = unsafe { - c::WSADuplicateSocketW(self.as_raw_socket(), c::GetCurrentProcessId(), &mut info) + sys::c::WSADuplicateSocketW( + self.as_raw_socket(), + sys::c::GetCurrentProcessId(), + &mut info, + ) }; sys::net::cvt(result)?; let socket = unsafe { - c::WSASocketW( + sys::c::WSASocketW( info.iAddressFamily, info.iSocketType, info.iProtocol, &mut info, 0, - c::WSA_FLAG_OVERLAPPED | c::WSA_FLAG_NO_HANDLE_INHERIT, + sys::c::WSA_FLAG_OVERLAPPED | sys::c::WSA_FLAG_NO_HANDLE_INHERIT, ) }; - if socket != c::INVALID_SOCKET { + if socket != sys::c::INVALID_SOCKET { unsafe { Ok(OwnedSocket::from_raw_socket(socket)) } } else { - let error = unsafe { c::WSAGetLastError() }; + let error = unsafe { sys::c::WSAGetLastError() }; - if error != c::WSAEPROTOTYPE && error != c::WSAEINVAL { + if error != sys::c::WSAEPROTOTYPE && error != sys::c::WSAEINVAL { return Err(io::Error::from_raw_os_error(error)); } let socket = unsafe { - c::WSASocketW( + sys::c::WSASocketW( info.iAddressFamily, info.iSocketType, info.iProtocol, &mut info, 0, - c::WSA_FLAG_OVERLAPPED, + sys::c::WSA_FLAG_OVERLAPPED, ) }; - if socket == c::INVALID_SOCKET { + if socket == sys::c::INVALID_SOCKET { return Err(last_error()); } @@ -161,7 +168,7 @@ impl BorrowedSocket<'_> { /// Returns the last error from the Windows socket interface. fn last_error() -> io::Error { - io::Error::from_raw_os_error(unsafe { c::WSAGetLastError() }) + io::Error::from_raw_os_error(unsafe { sys::c::WSAGetLastError() }) } #[stable(feature = "io_safety", since = "1.63.0")] @@ -194,7 +201,7 @@ impl IntoRawSocket for OwnedSocket { impl FromRawSocket for OwnedSocket { #[inline] unsafe fn from_raw_socket(socket: RawSocket) -> Self { - debug_assert_ne!(socket, c::INVALID_SOCKET as RawSocket); + debug_assert_ne!(socket, sys::c::INVALID_SOCKET as RawSocket); Self { socket } } } @@ -204,7 +211,7 @@ impl Drop for OwnedSocket { #[inline] fn drop(&mut self) { unsafe { - let _ = c::closesocket(self.socket); + let _ = sys::c::closesocket(self.socket); } } } diff --git a/library/std/src/sys/mod.rs b/library/std/src/sys/mod.rs index e767b2866cb..c72be13804d 100644 --- a/library/std/src/sys/mod.rs +++ b/library/std/src/sys/mod.rs @@ -52,31 +52,6 @@ cfg_if::cfg_if! { } } -// Import essential modules from platforms used in `std::os` when documenting. -// -// Note that on some platforms those modules don't compile -// (missing things in `libc` which is empty), so they are not included in `std::os` and can be -// omitted here as well. - -#[cfg(doc)] -#[cfg(not(any( - all(target_arch = "wasm32", not(target_os = "wasi")), - all(target_vendor = "fortanix", target_env = "sgx") -)))] -cfg_if::cfg_if! { - if #[cfg(not(windows))] { - // On non-Windows platforms (aka linux/osx/etc) pull in a "minimal" - // amount of windows goop which ends up compiling - - #[macro_use] - #[path = "windows/compat.rs"] - pub mod compat; - - #[path = "windows/c.rs"] - pub mod c; - } -} - cfg_if::cfg_if! { // Fuchsia components default to full backtrace. if #[cfg(target_os = "fuchsia")] { diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version b/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version index 7092c7c46f8..d183d4ace05 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version +++ b/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version @@ -1 +1 @@ -0.15.0 \ No newline at end of file +0.16.0 \ No newline at end of file diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md index 48efa67191a..73343ba9df5 100644 --- a/src/doc/rustc/src/SUMMARY.md +++ b/src/doc/rustc/src/SUMMARY.md @@ -27,6 +27,7 @@ - [armv7-unknown-linux-uclibceabihf](platform-support/armv7-unknown-linux-uclibceabihf.md) - [\*-android and \*-androideabi](platform-support/android.md) - [\*-linux-ohos](platform-support/openharmony.md) + - [\*-esp-espidf](platform-support/esp-idf.md) - [\*-unknown-fuchsia](platform-support/fuchsia.md) - [\*-kmc-solid_\*](platform-support/kmc-solid.md) - [loongarch\*-unknown-linux-\*](platform-support/loongarch-linux.md) diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index 75f97c1fc1e..d22e1cf7f68 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -297,7 +297,7 @@ target | std | host | notes `riscv32gc-unknown-linux-musl` | | | RISC-V Linux (kernel 5.4, musl + RISCV32 support patches) `riscv32im-unknown-none-elf` | * | | Bare RISC-V (RV32IM ISA) [`riscv32imac-unknown-xous-elf`](platform-support/riscv32imac-unknown-xous-elf.md) | ? | | RISC-V Xous (RV32IMAC ISA) -`riscv32imc-esp-espidf` | ✓ | | RISC-V ESP-IDF +[`riscv32imc-esp-espidf`](platform-support/esp-idf.md) | ✓ | | RISC-V ESP-IDF `riscv64gc-unknown-freebsd` | | | RISC-V FreeBSD `riscv64gc-unknown-fuchsia` | | | RISC-V Fuchsia `riscv64gc-unknown-linux-musl` | | | RISC-V Linux (kernel 4.20, musl 1.2.0) diff --git a/src/doc/rustc/src/platform-support/esp-idf.md b/src/doc/rustc/src/platform-support/esp-idf.md new file mode 100644 index 00000000000..8a4ca347e22 --- /dev/null +++ b/src/doc/rustc/src/platform-support/esp-idf.md @@ -0,0 +1,41 @@ +# `*-esp-espidf` + +**Tier: 3** + +Targets for the [ESP-IDF](https://github.com/espressif/esp-idf) development framework running on RISC-V and Xtensa CPUs. + +## Target maintainers + +- Ivan Markov [@ivmarkov](https://github.com/ivmarkov) +- Scott Mabin [@MabezDev](https://github.com/MabezDev) + +## Requirements + +The target names follow this format: `$ARCH-esp-espidf`, where `$ARCH` specifies the target processor architecture. The following targets are currently defined: + +| Target name | Target CPU(s) | +|--------------------------------|-----------------------| +| `riscv32imc-esp-espidf` | [ESP32-C3](https://www.espressif.com/en/products/socs/esp32-c3) | + +The minimum supported ESP-IDF version is `v4.3`, though it is recommended to use the latest stable release if possible. + +## Building the target + +The target can be built by enabling it for a `rustc` build. The `build-std` feature is required to build the standard library for ESP-IDF. `ldproxy` is also required for linking, it can be installed from crates.io. + +```toml +[build] +target = ["$ARCH-esp-espidf"] + +[target.$ARCH-esp-espidf] +linker = "ldproxy" + +[unstable] +build-std = ["std", "panic_abort"] +``` + +The `esp-idf-sys` crate will handle the compilation of ESP-IDF, including downloading the relevant toolchains for the build. + +## Cross-compilation toolchains and C code + +`esp-idf-sys` exposes the toolchain used in the compilation of ESP-IDF, see the crate [documentation for build output propagation](https://github.com/esp-rs/esp-idf-sys#conditional-compilation) for more information. diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 657f3c9ec45..59a3e631724 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -119,7 +119,39 @@ pub(crate) fn clean_doc_module<'tcx>(doc: &DocModule<'tcx>, cx: &mut DocContext< }); let kind = ModuleItem(Module { items, span }); - Item::from_def_id_and_parts(doc.def_id.to_def_id(), Some(doc.name), kind, cx) + generate_item_with_correct_attrs(cx, kind, doc.def_id, doc.name, doc.import_id, doc.renamed) +} + +fn generate_item_with_correct_attrs( + cx: &mut DocContext<'_>, + kind: ItemKind, + local_def_id: LocalDefId, + name: Symbol, + import_id: Option, + renamed: Option, +) -> Item { + let def_id = local_def_id.to_def_id(); + let target_attrs = inline::load_attrs(cx, def_id); + let attrs = if let Some(import_id) = import_id { + let is_inline = inline::load_attrs(cx, import_id.to_def_id()) + .lists(sym::doc) + .get_word_attr(sym::inline) + .is_some(); + let mut attrs = get_all_import_attributes(cx, import_id, local_def_id, is_inline); + add_without_unwanted_attributes(&mut attrs, target_attrs, is_inline, None); + attrs + } else { + // We only keep the item's attributes. + target_attrs.iter().map(|attr| (Cow::Borrowed(attr), None)).collect() + }; + + let cfg = attrs.cfg(cx.tcx, &cx.cache.hidden_cfg); + let attrs = Attributes::from_ast_iter(attrs.iter().map(|(attr, did)| (&**attr, *did)), false); + + let name = renamed.or(Some(name)); + let mut item = Item::from_def_id_and_attrs_and_parts(def_id, name, kind, Box::new(attrs), cfg); + item.inline_stmt_id = import_id.map(|local| local.to_def_id()); + item } fn clean_generic_bound<'tcx>( @@ -2345,29 +2377,14 @@ fn clean_maybe_renamed_item<'tcx>( _ => unreachable!("not yet converted"), }; - let target_attrs = inline::load_attrs(cx, def_id); - let attrs = if let Some(import_id) = import_id { - let is_inline = inline::load_attrs(cx, import_id.to_def_id()) - .lists(sym::doc) - .get_word_attr(sym::inline) - .is_some(); - let mut attrs = - get_all_import_attributes(cx, import_id, item.owner_id.def_id, is_inline); - add_without_unwanted_attributes(&mut attrs, target_attrs, is_inline, None); - attrs - } else { - // We only keep the item's attributes. - target_attrs.iter().map(|attr| (Cow::Borrowed(attr), None)).collect() - }; - - let cfg = attrs.cfg(cx.tcx, &cx.cache.hidden_cfg); - let attrs = - Attributes::from_ast_iter(attrs.iter().map(|(attr, did)| (&**attr, *did)), false); - - let mut item = - Item::from_def_id_and_attrs_and_parts(def_id, Some(name), kind, Box::new(attrs), cfg); - item.inline_stmt_id = import_id.map(|local| local.to_def_id()); - vec![item] + vec![generate_item_with_correct_attrs( + cx, + kind, + item.owner_id.def_id, + name, + import_id, + renamed, + )] }) } @@ -2397,14 +2414,15 @@ fn clean_impl<'tcx>( } let for_ = clean_ty(impl_.self_ty, cx); - let type_alias = for_.def_id(&cx.cache).and_then(|did| match tcx.def_kind(did) { - DefKind::TyAlias => Some(clean_middle_ty( - ty::Binder::dummy(tcx.type_of(did).subst_identity()), - cx, - Some(did), - )), - _ => None, - }); + let type_alias = + for_.def_id(&cx.cache).and_then(|alias_def_id: DefId| match tcx.def_kind(alias_def_id) { + DefKind::TyAlias => Some(clean_middle_ty( + ty::Binder::dummy(tcx.type_of(def_id).subst_identity()), + cx, + Some(def_id.to_def_id()), + )), + _ => None, + }); let mut make_item = |trait_: Option, for_: Type, items: Vec| { let kind = ImplItem(Box::new(Impl { unsafety: impl_.unsafety, diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs index 841abfab666..c0730e90740 100644 --- a/src/librustdoc/formats/cache.rs +++ b/src/librustdoc/formats/cache.rs @@ -195,7 +195,13 @@ impl Cache { impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> { fn fold_item(&mut self, item: clean::Item) -> Option { if item.item_id.is_local() { - debug!("folding {} \"{:?}\", id {:?}", item.type_(), item.name, item.item_id); + let is_stripped = matches!(*item.kind, clean::ItemKind::StrippedItem(..)); + debug!( + "folding {} (stripped: {is_stripped:?}) \"{:?}\", id {:?}", + item.type_(), + item.name, + item.item_id + ); } // If this is a stripped module, diff --git a/src/librustdoc/passes/check_doc_test_visibility.rs b/src/librustdoc/passes/check_doc_test_visibility.rs index 6b13e6c9581..10295cbd189 100644 --- a/src/librustdoc/passes/check_doc_test_visibility.rs +++ b/src/librustdoc/passes/check_doc_test_visibility.rs @@ -95,7 +95,7 @@ pub(crate) fn should_have_doc_example(cx: &DocContext<'_>, item: &clean::Item) - } if cx.tcx.is_doc_hidden(def_id.to_def_id()) - || inherits_doc_hidden(cx.tcx, def_id) + || inherits_doc_hidden(cx.tcx, def_id, None) || cx.tcx.def_span(def_id.to_def_id()).in_derive_expansion() { return false; diff --git a/src/librustdoc/passes/strip_hidden.rs b/src/librustdoc/passes/strip_hidden.rs index a688aa14863..972b0c5ec19 100644 --- a/src/librustdoc/passes/strip_hidden.rs +++ b/src/librustdoc/passes/strip_hidden.rs @@ -1,5 +1,6 @@ //! Strip all doc(hidden) items from the output. +use rustc_hir::def_id::LocalDefId; use rustc_middle::ty::TyCtxt; use rustc_span::symbol::sym; use std::mem; @@ -29,6 +30,7 @@ pub(crate) fn strip_hidden(krate: clean::Crate, cx: &mut DocContext<'_>) -> clea update_retained: true, tcx: cx.tcx, is_in_hidden_item: false, + last_reexport: None, }; stripper.fold_crate(krate) }; @@ -49,13 +51,24 @@ struct Stripper<'a, 'tcx> { update_retained: bool, tcx: TyCtxt<'tcx>, is_in_hidden_item: bool, + last_reexport: Option, } impl<'a, 'tcx> Stripper<'a, 'tcx> { + fn set_last_reexport_then_fold_item(&mut self, i: Item) -> Item { + let prev_from_reexport = self.last_reexport; + if i.inline_stmt_id.is_some() { + self.last_reexport = i.item_id.as_def_id().and_then(|def_id| def_id.as_local()); + } + let ret = self.fold_item_recur(i); + self.last_reexport = prev_from_reexport; + ret + } + fn set_is_in_hidden_item_and_fold(&mut self, is_in_hidden_item: bool, i: Item) -> Item { let prev = self.is_in_hidden_item; self.is_in_hidden_item |= is_in_hidden_item; - let ret = self.fold_item_recur(i); + let ret = self.set_last_reexport_then_fold_item(i); self.is_in_hidden_item = prev; ret } @@ -64,7 +77,7 @@ impl<'a, 'tcx> Stripper<'a, 'tcx> { /// of `is_in_hidden_item` to `true` because the impl children inherit its visibility. fn recurse_in_impl_or_exported_macro(&mut self, i: Item) -> Item { let prev = mem::replace(&mut self.is_in_hidden_item, false); - let ret = self.fold_item_recur(i); + let ret = self.set_last_reexport_then_fold_item(i); self.is_in_hidden_item = prev; ret } @@ -86,13 +99,20 @@ impl<'a, 'tcx> DocFolder for Stripper<'a, 'tcx> { if !is_impl_or_exported_macro { is_hidden = self.is_in_hidden_item || has_doc_hidden; if !is_hidden && i.inline_stmt_id.is_none() { - // We don't need to check if it's coming from a reexport since the reexport itself was - // already checked. + // `i.inline_stmt_id` is `Some` if the item is directly reexported. If it is, we + // don't need to check it, because the reexport itself was already checked. + // + // If this item is the child of a reexported module, `self.last_reexport` will be + // `Some` even though `i.inline_stmt_id` is `None`. Hiddenness inheritance needs to + // account for the possibility that an item's true parent module is hidden, but it's + // inlined into a visible module true. This code shouldn't be reachable if the + // module's reexport is itself hidden, for the same reason it doesn't need to be + // checked if `i.inline_stmt_id` is Some: hidden reexports are never inlined. is_hidden = i .item_id .as_def_id() .and_then(|def_id| def_id.as_local()) - .map(|def_id| inherits_doc_hidden(self.tcx, def_id)) + .map(|def_id| inherits_doc_hidden(self.tcx, def_id, self.last_reexport)) .unwrap_or(false); } } diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index 841c7a78b2d..deb29b1e7a9 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -27,6 +27,8 @@ pub(crate) struct Module<'hir> { pub(crate) where_inner: Span, pub(crate) mods: Vec>, pub(crate) def_id: LocalDefId, + pub(crate) renamed: Option, + pub(crate) import_id: Option, /// The key is the item `ItemId` and the value is: (item, renamed, import_id). /// We use `FxIndexMap` to keep the insert order. pub(crate) items: FxIndexMap< @@ -37,11 +39,19 @@ pub(crate) struct Module<'hir> { } impl Module<'_> { - pub(crate) fn new(name: Symbol, def_id: LocalDefId, where_inner: Span) -> Self { + pub(crate) fn new( + name: Symbol, + def_id: LocalDefId, + where_inner: Span, + renamed: Option, + import_id: Option, + ) -> Self { Module { name, def_id, where_inner, + renamed, + import_id, mods: Vec::new(), items: FxIndexMap::default(), foreigns: Vec::new(), @@ -60,9 +70,16 @@ fn def_id_to_path(tcx: TyCtxt<'_>, did: DefId) -> Vec { std::iter::once(crate_name).chain(relative).collect() } -pub(crate) fn inherits_doc_hidden(tcx: TyCtxt<'_>, mut def_id: LocalDefId) -> bool { +pub(crate) fn inherits_doc_hidden( + tcx: TyCtxt<'_>, + mut def_id: LocalDefId, + stop_at: Option, +) -> bool { let hir = tcx.hir(); while let Some(id) = tcx.opt_local_parent(def_id) { + if let Some(stop_at) = stop_at && id == stop_at { + return false; + } def_id = id; if tcx.is_doc_hidden(def_id.to_def_id()) { return true; @@ -100,6 +117,8 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { cx.tcx.crate_name(LOCAL_CRATE), CRATE_DEF_ID, cx.tcx.hir().root_module().spans.inner_span, + None, + None, ); RustdocVisitor { @@ -261,7 +280,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { let is_private = !self.cx.cache.effective_visibilities.is_directly_public(self.cx.tcx, ori_res_did); - let is_hidden = inherits_doc_hidden(self.cx.tcx, res_did); + let is_hidden = inherits_doc_hidden(self.cx.tcx, res_did, None); // Only inline if requested or if the item would otherwise be stripped. if (!please_inline && !is_private && !is_hidden) || is_no_inline { @@ -278,7 +297,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { .cache .effective_visibilities .is_directly_public(self.cx.tcx, item_def_id.to_def_id()) && - !inherits_doc_hidden(self.cx.tcx, item_def_id) + !inherits_doc_hidden(self.cx.tcx, item_def_id, None) { // The imported item is public and not `doc(hidden)` so no need to inline it. return false; @@ -427,7 +446,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { } } hir::ItemKind::Mod(ref m) => { - self.enter_mod(item.owner_id.def_id, m, name); + self.enter_mod(item.owner_id.def_id, m, name, renamed, import_id); } hir::ItemKind::Fn(..) | hir::ItemKind::ExternCrate(..) @@ -480,8 +499,15 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { /// This method will create a new module and push it onto the "modules stack" then call /// `visit_mod_contents`. Once done, it'll remove it from the "modules stack" and instead /// add into the list of modules of the current module. - fn enter_mod(&mut self, id: LocalDefId, m: &'tcx hir::Mod<'tcx>, name: Symbol) { - self.modules.push(Module::new(name, id, m.spans.inner_span)); + fn enter_mod( + &mut self, + id: LocalDefId, + m: &'tcx hir::Mod<'tcx>, + name: Symbol, + renamed: Option, + import_id: Option, + ) { + self.modules.push(Module::new(name, id, m.spans.inner_span, renamed, import_id)); self.visit_mod_contents(id, m); @@ -501,19 +527,14 @@ impl<'a, 'tcx> Visitor<'tcx> for RustdocVisitor<'a, 'tcx> { fn visit_item(&mut self, i: &'tcx hir::Item<'tcx>) { self.visit_item_inner(i, None, None); - let new_value = if self.is_importable_from_parent { - matches!( + let new_value = self.is_importable_from_parent + && matches!( i.kind, hir::ItemKind::Mod(..) | hir::ItemKind::ForeignMod { .. } | hir::ItemKind::Impl(..) | hir::ItemKind::Trait(..) - ) - } else { - // Whatever the context, if it's an impl block, the items inside it can be used so they - // should be visible. - matches!(i.kind, hir::ItemKind::Impl(..)) - }; + ); let prev = mem::replace(&mut self.is_importable_from_parent, new_value); walk_item(self, i); self.is_importable_from_parent = prev; diff --git a/src/tools/cargo b/src/tools/cargo index 26b73d15a68..13413c64ff8 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit 26b73d15a68fb94579f6d3590585ec0e9d81d3d5 +Subproject commit 13413c64ff88dd6c2824e9eb9374fc5f10895d28 diff --git a/src/tools/compiletest/Cargo.toml b/src/tools/compiletest/Cargo.toml index 85fd6523c82..0d42504c7f4 100644 --- a/src/tools/compiletest/Cargo.toml +++ b/src/tools/compiletest/Cargo.toml @@ -28,7 +28,7 @@ libc = "0.2" miow = "0.5" [target.'cfg(windows)'.dependencies.windows] -version = "0.46.0" +version = "0.48.0" features = [ "Win32_Foundation", "Win32_System_Diagnostics_Debug", diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 25304fc19f7..6ff63155f71 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -65dfca8488d635552eb246eb8e15df646e987cff +699a862a3d4e4a2d5603c93297c0c44021ea72f5 diff --git a/src/tools/miri/tests/fail/deny_lint.rs b/src/tools/miri/tests/fail/deny_lint.rs index 217d5677584..f49fa49d09d 100644 --- a/src/tools/miri/tests/fail/deny_lint.rs +++ b/src/tools/miri/tests/fail/deny_lint.rs @@ -1,6 +1,6 @@ //@error-in-other-file: miri cannot be run on programs that fail compilation -#![deny(warnings)] +#![deny(warnings, unused)] struct Foo; //~^ ERROR: struct `Foo` is never constructed diff --git a/src/tools/miri/tests/fail/deny_lint.stderr b/src/tools/miri/tests/fail/deny_lint.stderr index bb48fcc2038..d1c9b481807 100644 --- a/src/tools/miri/tests/fail/deny_lint.stderr +++ b/src/tools/miri/tests/fail/deny_lint.stderr @@ -4,7 +4,12 @@ error: struct `Foo` is never constructed LL | struct Foo; | ^^^ | - = note: `-D dead-code` implied by `-D unused` +note: the lint level is defined here + --> $DIR/deny_lint.rs:LL:CC + | +LL | #![deny(warnings, unused)] + | ^^^^^^ + = note: `#[deny(dead_code)]` implied by `#[deny(unused)]` error: miri cannot be run on programs that fail compilation diff --git a/src/tools/miri/tests/fail/intrinsics/simd-float-to-int.rs b/src/tools/miri/tests/fail/intrinsics/simd-float-to-int.rs index 8905d739078..10939c0f1c3 100644 --- a/src/tools/miri/tests/fail/intrinsics/simd-float-to-int.rs +++ b/src/tools/miri/tests/fail/intrinsics/simd-float-to-int.rs @@ -1,9 +1,8 @@ -//@error-in-other-file: cannot be represented in target type `i32` #![feature(portable_simd)] use std::simd::*; fn main() { unsafe { - let _x: i32x2 = f32x2::from_array([f32::MAX, f32::MIN]).to_int_unchecked(); + let _x: i32x2 = f32x2::from_array([f32::MAX, f32::MIN]).to_int_unchecked(); //~ERROR: cannot be represented in target type `i32` } } diff --git a/src/tools/miri/tests/fail/intrinsics/simd-float-to-int.stderr b/src/tools/miri/tests/fail/intrinsics/simd-float-to-int.stderr index 5c73c76a161..ea5ad62aea9 100644 --- a/src/tools/miri/tests/fail/intrinsics/simd-float-to-int.stderr +++ b/src/tools/miri/tests/fail/intrinsics/simd-float-to-int.stderr @@ -1,18 +1,13 @@ error: Undefined Behavior: `float_to_int_unchecked` intrinsic called on 3.40282347E+38 which cannot be represented in target type `i32` - --> RUSTLIB/core/src/../../portable-simd/crates/core_simd/src/vector.rs:LL:CC + --> $DIR/simd-float-to-int.rs:LL:CC | -LL | unsafe { intrinsics::simd_cast(self) } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `float_to_int_unchecked` intrinsic called on 3.40282347E+38 which cannot be represented in target type `i32` +LL | let _x: i32x2 = f32x2::from_array([f32::MAX, f32::MIN]).to_int_unchecked(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `float_to_int_unchecked` intrinsic called on 3.40282347E+38 which cannot be represented in target type `i32` | = 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: BACKTRACE: - = note: inside `std::simd::Simd::::to_int_unchecked::` at RUSTLIB/core/src/../../portable-simd/crates/core_simd/src/vector.rs:LL:CC -note: inside `main` - --> $DIR/simd-float-to-int.rs:LL:CC - | -LL | let _x: i32x2 = f32x2::from_array([f32::MAX, f32::MIN]).to_int_unchecked(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: inside `main` at $DIR/simd-float-to-int.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/intrinsics/simd-gather.rs b/src/tools/miri/tests/fail/intrinsics/simd-gather.rs index 5dd9bd8a685..ceb7beebd8a 100644 --- a/src/tools/miri/tests/fail/intrinsics/simd-gather.rs +++ b/src/tools/miri/tests/fail/intrinsics/simd-gather.rs @@ -1,4 +1,3 @@ -//@error-in-other-file: pointer to 1 byte starting at offset 9 is out-of-bounds #![feature(portable_simd)] use std::simd::*; @@ -6,6 +5,6 @@ fn main() { unsafe { let vec: &[i8] = &[10, 11, 12, 13, 14, 15, 16, 17, 18]; let idxs = Simd::from_array([9, 3, 0, 17]); - let _result = Simd::gather_select_unchecked(&vec, Mask::splat(true), idxs, Simd::splat(0)); + let _result = Simd::gather_select_unchecked(&vec, Mask::splat(true), idxs, Simd::splat(0)); //~ERROR: pointer to 1 byte starting at offset 9 is out-of-bounds } } diff --git a/src/tools/miri/tests/fail/intrinsics/simd-gather.stderr b/src/tools/miri/tests/fail/intrinsics/simd-gather.stderr index 7512d57f672..f82b30a9633 100644 --- a/src/tools/miri/tests/fail/intrinsics/simd-gather.stderr +++ b/src/tools/miri/tests/fail/intrinsics/simd-gather.stderr @@ -1,18 +1,13 @@ error: Undefined Behavior: dereferencing pointer failed: ALLOC has size 9, so pointer to 1 byte starting at offset 9 is out-of-bounds - --> RUSTLIB/core/src/../../portable-simd/crates/core_simd/src/vector.rs:LL:CC + --> $DIR/simd-gather.rs:LL:CC | -LL | unsafe { intrinsics::simd_gather(or, ptrs, enable.to_int()) } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ dereferencing pointer failed: ALLOC has size 9, so pointer to 1 byte starting at offset 9 is out-of-bounds +LL | let _result = Simd::gather_select_unchecked(&vec, Mask::splat(true), idxs, Simd::splat(0)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ dereferencing pointer failed: ALLOC has size 9, so pointer to 1 byte starting at offset 9 is out-of-bounds | = 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: BACKTRACE: - = note: inside `std::simd::Simd::::gather_select_unchecked` at RUSTLIB/core/src/../../portable-simd/crates/core_simd/src/vector.rs:LL:CC -note: inside `main` - --> $DIR/simd-gather.rs:LL:CC - | -LL | let _result = Simd::gather_select_unchecked(&vec, Mask::splat(true), idxs, Simd::splat(0)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: inside `main` at $DIR/simd-gather.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/intrinsics/simd-scatter.rs b/src/tools/miri/tests/fail/intrinsics/simd-scatter.rs index 0a64478dc74..98b6749c584 100644 --- a/src/tools/miri/tests/fail/intrinsics/simd-scatter.rs +++ b/src/tools/miri/tests/fail/intrinsics/simd-scatter.rs @@ -1,4 +1,3 @@ -//@error-in-other-file: pointer to 1 byte starting at offset 9 is out-of-bounds #![feature(portable_simd)] use std::simd::*; @@ -7,6 +6,7 @@ fn main() { let mut vec: Vec = vec![10, 11, 12, 13, 14, 15, 16, 17, 18]; let idxs = Simd::from_array([9, 3, 0, 17]); Simd::from_array([-27, 82, -41, 124]).scatter_select_unchecked( + //~^ERROR: pointer to 1 byte starting at offset 9 is out-of-bounds &mut vec, Mask::splat(true), idxs, diff --git a/src/tools/miri/tests/fail/intrinsics/simd-scatter.stderr b/src/tools/miri/tests/fail/intrinsics/simd-scatter.stderr index a9ad60a0e5b..a745b61029d 100644 --- a/src/tools/miri/tests/fail/intrinsics/simd-scatter.stderr +++ b/src/tools/miri/tests/fail/intrinsics/simd-scatter.stderr @@ -1,22 +1,18 @@ error: Undefined Behavior: dereferencing pointer failed: ALLOC has size 9, so pointer to 1 byte starting at offset 9 is out-of-bounds - --> RUSTLIB/core/src/../../portable-simd/crates/core_simd/src/vector.rs:LL:CC - | -LL | intrinsics::simd_scatter(self, ptrs, enable.to_int()) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ dereferencing pointer failed: ALLOC has size 9, so pointer to 1 byte starting at offset 9 is out-of-bounds - | - = 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: BACKTRACE: - = note: inside `std::simd::Simd::::scatter_select_unchecked` at RUSTLIB/core/src/../../portable-simd/crates/core_simd/src/vector.rs:LL:CC -note: inside `main` --> $DIR/simd-scatter.rs:LL:CC | LL | / Simd::from_array([-27, 82, -41, 124]).scatter_select_unchecked( +LL | | LL | | &mut vec, LL | | Mask::splat(true), LL | | idxs, LL | | ); - | |_________^ + | |_________^ dereferencing pointer failed: ALLOC has size 9, so pointer to 1 byte starting at offset 9 is out-of-bounds + | + = 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: BACKTRACE: + = note: inside `main` at $DIR/simd-scatter.rs:LL:CC note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/rustdoc-gui/tester.js b/src/tools/rustdoc-gui/tester.js index 692d5e3fcef..b26480f668b 100644 --- a/src/tools/rustdoc-gui/tester.js +++ b/src/tools/rustdoc-gui/tester.js @@ -143,7 +143,7 @@ async function runTests(opts, framework_options, files, results, status_bar, sho const tests_queue = []; for (const testPath of files) { - const callback = runTest(testPath, framework_options) + const callback = runTest(testPath, {"options": framework_options}) .then(out => { const [output, nb_failures] = out; results[nb_failures === 0 ? "successful" : "failed"].push({ @@ -323,6 +323,7 @@ async function main(argv) { if (results.failed.length > 0 || results.errored.length > 0) { process.exit(1); } + process.exit(0); } main(process.argv); diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index baef4bb0140..afa6bce943f 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -6,47 +6,53 @@ use std::path::Path; /// These are licenses that are allowed for all crates, including the runtime, /// rustc, tools, etc. +#[rustfmt::skip] const LICENSES: &[&str] = &[ - "MIT/Apache-2.0", - "MIT / Apache-2.0", - "Apache-2.0/MIT", + // tidy-alphabetical-start + "(MIT OR Apache-2.0) AND Unicode-DFS-2016", // unicode_ident + "0BSD OR MIT OR Apache-2.0", // adler license + "0BSD", "Apache-2.0 / MIT", - "MIT OR Apache-2.0", "Apache-2.0 OR MIT", "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT", // wasi license - "MIT", + "Apache-2.0/MIT", "ISC", - "Unlicense/MIT", + "MIT / Apache-2.0", + "MIT OR Apache-2.0 OR Zlib", // tinyvec_macros + "MIT OR Apache-2.0", + "MIT OR Zlib OR Apache-2.0", // miniz_oxide + "MIT", + "MIT/Apache-2.0", + "Unicode-DFS-2016", // tinystr and icu4x "Unlicense OR MIT", - "0BSD", - "0BSD OR MIT OR Apache-2.0", // adler license - "Zlib OR Apache-2.0 OR MIT", // tinyvec - "MIT OR Apache-2.0 OR Zlib", // tinyvec_macros - "MIT OR Zlib OR Apache-2.0", // miniz_oxide - "(MIT OR Apache-2.0) AND Unicode-DFS-2016", // unicode_ident - "Unicode-DFS-2016", // tinystr and icu4x + "Unlicense/MIT", + "Zlib OR Apache-2.0 OR MIT", // tinyvec + // tidy-alphabetical-end ]; /// These are exceptions to Rust's permissive licensing policy, and /// should be considered bugs. Exceptions are only allowed in Rust /// tooling. It is _crucial_ that no exception crates be dependencies /// of the Rust runtime (std/test). +#[rustfmt::skip] const EXCEPTIONS: &[(&str, &str)] = &[ + // tidy-alphabetical-start ("ar_archive_writer", "Apache-2.0 WITH LLVM-exception"), // rustc - ("mdbook", "MPL-2.0"), // mdbook + ("codespan-reporting", "Apache-2.0"), // cxx via iana-time-zone-haiku via time, only on haiku ("colored", "MPL-2.0"), // rustfmt + ("dissimilar", "Apache-2.0"), // rustdoc, rustc_lexer (few tests) via expect-test, (dev deps) + ("fluent-langneg", "Apache-2.0"), // rustc (fluent translations) + ("fortanix-sgx-abi", "MPL-2.0"), // libstd but only for `sgx` target. FIXME: this dependency violates the documentation comment above. + ("instant", "BSD-3-Clause"), // rustc_driver/tracing-subscriber/parking_lot + ("mdbook", "MPL-2.0"), // mdbook ("ryu", "Apache-2.0 OR BSL-1.0"), // cargo/... (because of serde) - ("codespan-reporting", "Apache-2.0"), // cxx via iana-time-zone-haiku via time, only on haiku - ("instant", "BSD-3-Clause"), // rustc_driver/tracing-subscriber/parking_lot - ("snap", "BSD-3-Clause"), // rustc - ("fluent-langneg", "Apache-2.0"), // rustc (fluent translations) - ("self_cell", "Apache-2.0"), // rustc (fluent translations) - // FIXME: this dependency violates the documentation comment above: - ("fortanix-sgx-abi", "MPL-2.0"), // libstd but only for `sgx` target - ("dissimilar", "Apache-2.0"), // rustdoc, rustc_lexer (few tests) via expect-test, (dev deps) + ("self_cell", "Apache-2.0"), // rustc (fluent translations) + ("snap", "BSD-3-Clause"), // rustc + // tidy-alphabetical-end ]; const EXCEPTIONS_CARGO: &[(&str, &str)] = &[ + // tidy-alphabetical-start ("bitmaps", "MPL-2.0+"), ("bytesize", "Apache-2.0"), ("dunce", "CC0-1.0 OR MIT-0"), @@ -62,9 +68,11 @@ const EXCEPTIONS_CARGO: &[(&str, &str)] = &[ ("sized-chunks", "MPL-2.0+"), ("subtle", "BSD-3-Clause"), ("unicode-bom", "Apache-2.0"), + // tidy-alphabetical-end ]; const EXCEPTIONS_CRANELIFT: &[(&str, &str)] = &[ + // tidy-alphabetical-start ("cranelift-bforest", "Apache-2.0 WITH LLVM-exception"), ("cranelift-codegen", "Apache-2.0 WITH LLVM-exception"), ("cranelift-codegen-meta", "Apache-2.0 WITH LLVM-exception"), @@ -80,6 +88,7 @@ const EXCEPTIONS_CRANELIFT: &[(&str, &str)] = &[ ("regalloc2", "Apache-2.0 WITH LLVM-exception"), ("target-lexicon", "Apache-2.0 WITH LLVM-exception"), ("wasmtime-jit-icache-coherence", "Apache-2.0 WITH LLVM-exception"), + // tidy-alphabetical-end ]; const EXCEPTIONS_BOOTSTRAP: &[(&str, &str)] = &[ @@ -95,6 +104,7 @@ const RUNTIME_CRATES: &[&str] = &["std", "core", "alloc", "test", "panic_abort", /// This list is here to provide a speed-bump to adding a new dependency to /// rustc. Please check with the compiler team before adding an entry. const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ + // tidy-alphabetical-start "addr2line", "adler", "ahash", @@ -113,8 +123,8 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ "chalk-engine", "chalk-ir", "chalk-solve", - "convert_case", // dependency of derive_more "compiler_builtins", + "convert_case", // dependency of derive_more "cpufeatures", "crc32fast", "crossbeam-channel", @@ -187,8 +197,8 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ "ppv-lite86", "proc-macro-hack", "proc-macro2", - "pulldown-cmark", "psm", + "pulldown-cmark", "punycode", "quote", "rand", @@ -227,6 +237,7 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ "tempfile", "termcolor", "termize", + "thin-vec", "thiserror", "thiserror-impl", "thorin-dwp", @@ -234,7 +245,6 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ "tinystr", "tinyvec", "tinyvec_macros", - "thin-vec", "tracing", "tracing-attributes", "tracing-core", @@ -263,29 +273,37 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ "valuable", "version_check", "wasi", - "windows", "winapi", "winapi-i686-pc-windows-gnu", "winapi-util", "winapi-x86_64-pc-windows-gnu", + "windows", + "windows-targets", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", "writeable", - // this is a false-positive: it's only used by rustfmt, but because it's enabled through a - // feature, tidy thinks it's used by rustc as well. - "yansi-term", + "yansi-term", // this is a false-positive: it's only used by rustfmt, but because it's enabled through a feature, tidy thinks it's used by rustc as well. "yoke", "yoke-derive", "zerofrom", "zerofrom-derive", "zerovec", "zerovec-derive", + // tidy-alphabetical-end ]; const PERMITTED_CRANELIFT_DEPENDENCIES: &[&str] = &[ + // tidy-alphabetical-start "ahash", "anyhow", "autocfg", - "bumpalo", "bitflags", + "bumpalo", "byteorder", "cfg-if", "cranelift-bforest", @@ -324,6 +342,7 @@ const PERMITTED_CRANELIFT_DEPENDENCIES: &[&str] = &[ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", "windows-sys", + // tidy-alphabetical-end ]; /// Dependency checks. diff --git a/tests/codegen/sanitizer-cfi-emit-type-metadata-trait-objects.rs b/tests/codegen/sanitizer-cfi-emit-type-metadata-trait-objects.rs new file mode 100644 index 00000000000..ab5dcec7936 --- /dev/null +++ b/tests/codegen/sanitizer-cfi-emit-type-metadata-trait-objects.rs @@ -0,0 +1,44 @@ +// Verifies that type metadata identifiers for trait objects are emitted correctly. +// +// needs-sanitizer-cfi +// compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi + +#![crate_type="lib"] + +trait Trait1 { + fn foo(&self); +} + +struct Type1; + +impl Trait1 for Type1 { + fn foo(&self) { + } +} + +pub fn foo() { + let a = Type1; + a.foo(); + // CHECK-LABEL: define{{.*}}foo{{.*}}!type !{{[0-9]+}} + // CHECK: call ::foo +} + +pub fn bar() { + let a = Type1; + let b = &a as &dyn Trait1; + b.foo(); + // CHECK-LABEL: define{{.*}}bar{{.*}}!type !{{[0-9]+}} + // CHECK: call i1 @llvm.type.test({{i8\*|ptr}} {{%f|%0|%1}}, metadata !"[[TYPE1:[[:print:]]+]]") +} + +pub fn baz() { + let a = Type1; + let b = &a as &dyn Trait1; + a.foo(); + b.foo(); + // CHECK-LABEL: define{{.*}}baz{{.*}}!type !{{[0-9]+}} + // CHECK: call ::foo + // CHECK: call i1 @llvm.type.test({{i8\*|ptr}} {{%f|%0|%1}}, metadata !"[[TYPE1:[[:print:]]+]]") +} + +// CHECK: !{{[0-9]+}} = !{i64 0, !"[[TYPE1]]"} diff --git a/tests/codegen/sanitizer-kcfi-emit-type-metadata-trait-objects.rs b/tests/codegen/sanitizer-kcfi-emit-type-metadata-trait-objects.rs new file mode 100644 index 00000000000..81e0d9344f7 --- /dev/null +++ b/tests/codegen/sanitizer-kcfi-emit-type-metadata-trait-objects.rs @@ -0,0 +1,69 @@ +// Verifies that type metadata identifiers for trait objects are emitted correctly. +// +// revisions: aarch64 x86_64 +// [aarch64] compile-flags: --target aarch64-unknown-none +// [aarch64] needs-llvm-components: aarch64 +// [x86_64] compile-flags: --target x86_64-unknown-none +// [x86_64] needs-llvm-components: +// compile-flags: -Cno-prepopulate-passes -Zsanitizer=kcfi -Copt-level=0 + +#![crate_type="lib"] +#![feature(arbitrary_self_types, no_core, lang_items)] +#![no_core] + +#[lang="sized"] +trait Sized { } +#[lang="copy"] +trait Copy { } +#[lang="receiver"] +trait Receiver { } +#[lang="dispatch_from_dyn"] +trait DispatchFromDyn { } +impl<'a, T: ?Sized + Unsize, U: ?Sized> DispatchFromDyn<&'a U> for &'a T {} +#[lang = "unsize"] +trait Unsize { } +#[lang = "coerce_unsized"] +pub trait CoerceUnsized { } +impl<'a, 'b: 'a, T: ?Sized + Unsize, U: ?Sized> CoerceUnsized<&'a U> for &'b T {} +#[lang="freeze"] +trait Freeze { } +#[lang="drop_in_place"] +fn drop_in_place_fn() { } + +trait Trait1 { + fn foo(&self); +} + +struct Type1; + +impl Trait1 for Type1 { + fn foo(&self) { + } +} + +pub fn foo() { + let a = Type1; + a.foo(); + // CHECK-LABEL: define{{.*}}foo{{.*}}!{{|kcfi_type}} !{{[0-9]+}} + // CHECK: call ::foo +} + +pub fn bar() { + let a = Type1; + let b = &a as &dyn Trait1; + b.foo(); + // CHECK-LABEL: define{{.*}}bar{{.*}}!{{|kcfi_type}} !{{[0-9]+}} + // CHECK: call void %0({{\{\}\*|ptr}} align 1 {{%b\.0|%_1}}){{.*}}[ "kcfi"(i32 [[TYPE1:[[:print:]]+]]) ] +} + +pub fn baz() { + let a = Type1; + let b = &a as &dyn Trait1; + a.foo(); + b.foo(); + // CHECK-LABEL: define{{.*}}baz{{.*}}!{{|kcfi_type}} !{{[0-9]+}} + // CHECK: call ::foo + // CHECK: call void %0({{\{\}\*|ptr}} align 1 {{%b\.0|%_1}}){{.*}}[ "kcfi"(i32 [[TYPE1:[[:print:]]+]]) ] +} + +// CHECK: !{{[0-9]+}} = !{i32 [[TYPE1]]} diff --git a/tests/codegen/vec-shrink-panik.rs b/tests/codegen/vec-shrink-panik.rs index 4e5d8dc0632..88b7edff260 100644 --- a/tests/codegen/vec-shrink-panik.rs +++ b/tests/codegen/vec-shrink-panik.rs @@ -25,7 +25,7 @@ pub fn issue71861(vec: Vec) -> Box<[u32]> { // Call to panic_cannot_unwind in case of double-panic is expected // on LLVM 16 and older, but other panics are not. - // CHECK: filter + // old: filter // old-NEXT: ; call core::panicking::panic_cannot_unwind // old-NEXT: panic_cannot_unwind @@ -40,7 +40,7 @@ pub fn issue75636<'a>(iter: &[&'a str]) -> Box<[&'a str]> { // Call to panic_cannot_unwind in case of double-panic is expected, // on LLVM 16 and older, but other panics are not. - // CHECK: filter + // old: filter // old-NEXT: ; call core::panicking::panic_cannot_unwind // old-NEXT: panic_cannot_unwind diff --git a/tests/mir-opt/copy-prop/partial_init.main.CopyProp.diff b/tests/mir-opt/copy-prop/partial_init.main.CopyProp.diff new file mode 100644 index 00000000000..5866439055e --- /dev/null +++ b/tests/mir-opt/copy-prop/partial_init.main.CopyProp.diff @@ -0,0 +1,13 @@ +- // MIR for `main` before CopyProp ++ // MIR for `main` after CopyProp + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/partial_init.rs:+0:15: +0:15 + let mut _1: (isize,); // in scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL + + bb0: { + (_1.0: isize) = const 1_isize; // scope 0 at $DIR/partial_init.rs:+4:13: +4:20 + return; // scope 0 at $DIR/partial_init.rs:+5:13: +5:21 + } + } + diff --git a/tests/mir-opt/copy-prop/partial_init.rs b/tests/mir-opt/copy-prop/partial_init.rs new file mode 100644 index 00000000000..f5ab9974f71 --- /dev/null +++ b/tests/mir-opt/copy-prop/partial_init.rs @@ -0,0 +1,18 @@ +// unit-test: CopyProp +// Verify that we do not ICE on partial initializations. + +#![feature(custom_mir, core_intrinsics)] +extern crate core; +use core::intrinsics::mir::*; + +// EMIT_MIR partial_init.main.CopyProp.diff +#[custom_mir(dialect = "runtime", phase = "post-cleanup")] +pub fn main() { + mir! ( + let x: (isize, ); + { + x.0 = 1; + Return() + } + ) +} diff --git a/tests/mir-opt/lower_intrinsics.wrapping.LowerIntrinsics.diff b/tests/mir-opt/lower_intrinsics.wrapping.LowerIntrinsics.diff index 0bfb34acac2..217f27efe5c 100644 --- a/tests/mir-opt/lower_intrinsics.wrapping.LowerIntrinsics.diff +++ b/tests/mir-opt/lower_intrinsics.wrapping.LowerIntrinsics.diff @@ -30,10 +30,10 @@ _4 = _1; // scope 0 at $DIR/lower_intrinsics.rs:+1:45: +1:46 StorageLive(_5); // scope 0 at $DIR/lower_intrinsics.rs:+1:48: +1:49 _5 = _2; // scope 0 at $DIR/lower_intrinsics.rs:+1:48: +1:49 -- _3 = wrapping_add::(move _4, move _5) -> [return: bb1, unwind unreachable]; // scope 0 at $DIR/lower_intrinsics.rs:+1:14: +1:50 +- _3 = std::intrinsics::wrapping_add::(move _4, move _5) -> [return: bb1, unwind unreachable]; // scope 0 at $DIR/lower_intrinsics.rs:+1:14: +1:50 - // mir::Constant - // + span: $DIR/lower_intrinsics.rs:9:14: 9:44 -- // + literal: Const { ty: extern "rust-intrinsic" fn(i32, i32) -> i32 {wrapping_add::}, val: Value() } +- // + literal: Const { ty: extern "rust-intrinsic" fn(i32, i32) -> i32 {std::intrinsics::wrapping_add::}, val: Value() } + _3 = Add(move _4, move _5); // scope 0 at $DIR/lower_intrinsics.rs:+1:14: +1:50 + goto -> bb1; // scope 0 at $DIR/lower_intrinsics.rs:+1:14: +1:50 } @@ -46,10 +46,10 @@ _7 = _1; // scope 1 at $DIR/lower_intrinsics.rs:+2:45: +2:46 StorageLive(_8); // scope 1 at $DIR/lower_intrinsics.rs:+2:48: +2:49 _8 = _2; // scope 1 at $DIR/lower_intrinsics.rs:+2:48: +2:49 -- _6 = wrapping_sub::(move _7, move _8) -> [return: bb2, unwind unreachable]; // scope 1 at $DIR/lower_intrinsics.rs:+2:14: +2:50 +- _6 = std::intrinsics::wrapping_sub::(move _7, move _8) -> [return: bb2, unwind unreachable]; // scope 1 at $DIR/lower_intrinsics.rs:+2:14: +2:50 - // mir::Constant - // + span: $DIR/lower_intrinsics.rs:10:14: 10:44 -- // + literal: Const { ty: extern "rust-intrinsic" fn(i32, i32) -> i32 {wrapping_sub::}, val: Value() } +- // + literal: Const { ty: extern "rust-intrinsic" fn(i32, i32) -> i32 {std::intrinsics::wrapping_sub::}, val: Value() } + _6 = Sub(move _7, move _8); // scope 1 at $DIR/lower_intrinsics.rs:+2:14: +2:50 + goto -> bb2; // scope 1 at $DIR/lower_intrinsics.rs:+2:14: +2:50 } diff --git a/tests/mir-opt/reference_prop.mut_raw_then_mut_shr.ReferencePropagation.diff b/tests/mir-opt/reference_prop.mut_raw_then_mut_shr.ReferencePropagation.diff new file mode 100644 index 00000000000..af8ee2411d3 --- /dev/null +++ b/tests/mir-opt/reference_prop.mut_raw_then_mut_shr.ReferencePropagation.diff @@ -0,0 +1,74 @@ +- // MIR for `mut_raw_then_mut_shr` before ReferencePropagation ++ // MIR for `mut_raw_then_mut_shr` after ReferencePropagation + + fn mut_raw_then_mut_shr() -> (i32, i32) { + let mut _0: (i32, i32); // return place in scope 0 at $DIR/reference_prop.rs:+0:30: +0:40 + let mut _1: i32; // in scope 0 at $DIR/reference_prop.rs:+1:9: +1:14 + let mut _4: *mut i32; // in scope 0 at $DIR/reference_prop.rs:+3:16: +3:36 + let mut _5: &mut i32; // in scope 0 at $DIR/reference_prop.rs:+3:16: +3:26 + let _8: (); // in scope 0 at $DIR/reference_prop.rs:+7:5: +7:26 + let mut _9: i32; // in scope 0 at $DIR/reference_prop.rs:+8:6: +8:7 + let mut _10: i32; // in scope 0 at $DIR/reference_prop.rs:+8:9: +8:10 + scope 1 { + debug x => _1; // in scope 1 at $DIR/reference_prop.rs:+1:9: +1:14 + let _2: &mut i32; // in scope 1 at $DIR/reference_prop.rs:+2:9: +2:13 + scope 2 { + debug xref => _2; // in scope 2 at $DIR/reference_prop.rs:+2:9: +2:13 + let _3: *mut i32; // in scope 2 at $DIR/reference_prop.rs:+3:9: +3:13 + scope 3 { + debug xraw => _3; // in scope 3 at $DIR/reference_prop.rs:+3:9: +3:13 + let _6: &i32; // in scope 3 at $DIR/reference_prop.rs:+4:9: +4:13 + scope 4 { + debug xshr => _6; // in scope 4 at $DIR/reference_prop.rs:+4:9: +4:13 + let _7: i32; // in scope 4 at $DIR/reference_prop.rs:+6:9: +6:10 + scope 5 { + debug a => _7; // in scope 5 at $DIR/reference_prop.rs:+6:9: +6:10 + scope 6 { + } + } + } + } + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/reference_prop.rs:+1:9: +1:14 + _1 = const 2_i32; // scope 0 at $DIR/reference_prop.rs:+1:17: +1:18 +- StorageLive(_2); // scope 1 at $DIR/reference_prop.rs:+2:9: +2:13 + _2 = &mut _1; // scope 1 at $DIR/reference_prop.rs:+2:16: +2:22 + StorageLive(_3); // scope 2 at $DIR/reference_prop.rs:+3:9: +3:13 +- StorageLive(_4); // scope 2 at $DIR/reference_prop.rs:+3:16: +3:36 +- StorageLive(_5); // scope 2 at $DIR/reference_prop.rs:+3:16: +3:26 +- _5 = &mut (*_2); // scope 2 at $DIR/reference_prop.rs:+3:16: +3:26 +- _4 = &raw mut (*_5); // scope 2 at $DIR/reference_prop.rs:+3:16: +3:26 ++ _4 = &raw mut _1; // scope 2 at $DIR/reference_prop.rs:+3:16: +3:26 + _3 = _4; // scope 2 at $DIR/reference_prop.rs:+3:16: +3:36 +- StorageDead(_5); // scope 2 at $DIR/reference_prop.rs:+3:36: +3:37 +- StorageDead(_4); // scope 2 at $DIR/reference_prop.rs:+3:36: +3:37 + StorageLive(_6); // scope 3 at $DIR/reference_prop.rs:+4:9: +4:13 +- _6 = &(*_2); // scope 3 at $DIR/reference_prop.rs:+4:16: +4:22 ++ _6 = &_1; // scope 3 at $DIR/reference_prop.rs:+4:16: +4:22 + StorageLive(_7); // scope 4 at $DIR/reference_prop.rs:+6:9: +6:10 +- _7 = (*_6); // scope 4 at $DIR/reference_prop.rs:+6:13: +6:18 +- StorageLive(_8); // scope 5 at $DIR/reference_prop.rs:+7:5: +7:26 +- (*_3) = const 4_i32; // scope 6 at $DIR/reference_prop.rs:+7:14: +7:23 +- _8 = const (); // scope 6 at $DIR/reference_prop.rs:+7:5: +7:26 +- StorageDead(_8); // scope 5 at $DIR/reference_prop.rs:+7:25: +7:26 ++ _7 = _1; // scope 4 at $DIR/reference_prop.rs:+6:13: +6:18 ++ _1 = const 4_i32; // scope 6 at $DIR/reference_prop.rs:+7:14: +7:23 + StorageLive(_9); // scope 5 at $DIR/reference_prop.rs:+8:6: +8:7 + _9 = _7; // scope 5 at $DIR/reference_prop.rs:+8:6: +8:7 + StorageLive(_10); // scope 5 at $DIR/reference_prop.rs:+8:9: +8:10 + _10 = _1; // scope 5 at $DIR/reference_prop.rs:+8:9: +8:10 + _0 = (move _9, move _10); // scope 5 at $DIR/reference_prop.rs:+8:5: +8:11 + StorageDead(_10); // scope 5 at $DIR/reference_prop.rs:+8:10: +8:11 + StorageDead(_9); // scope 5 at $DIR/reference_prop.rs:+8:10: +8:11 + StorageDead(_7); // scope 4 at $DIR/reference_prop.rs:+9:1: +9:2 + StorageDead(_6); // scope 3 at $DIR/reference_prop.rs:+9:1: +9:2 + StorageDead(_3); // scope 2 at $DIR/reference_prop.rs:+9:1: +9:2 +- StorageDead(_2); // scope 1 at $DIR/reference_prop.rs:+9:1: +9:2 + StorageDead(_1); // scope 0 at $DIR/reference_prop.rs:+9:1: +9:2 + return; // scope 0 at $DIR/reference_prop.rs:+9:2: +9:2 + } + } + diff --git a/tests/mir-opt/reference_prop.read_through_raw.ReferencePropagation.diff b/tests/mir-opt/reference_prop.read_through_raw.ReferencePropagation.diff index a7d505c6906..75c1f8f57cc 100644 --- a/tests/mir-opt/reference_prop.read_through_raw.ReferencePropagation.diff +++ b/tests/mir-opt/reference_prop.read_through_raw.ReferencePropagation.diff @@ -9,15 +9,14 @@ let mut _5: *mut usize; // in scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL bb0: { - _2 = &mut (*_1); // scope 0 at $DIR/reference_prop.rs:+10:13: +10:25 +- _2 = &mut (*_1); // scope 0 at $DIR/reference_prop.rs:+10:13: +10:25 - _3 = &mut (*_2); // scope 0 at $DIR/reference_prop.rs:+11:13: +11:26 - _4 = &raw mut (*_2); // scope 0 at $DIR/reference_prop.rs:+12:13: +12:30 - _5 = &raw mut (*_3); // scope 0 at $DIR/reference_prop.rs:+13:13: +13:30 - _0 = (*_4); // scope 0 at $DIR/reference_prop.rs:+15:13: +15:22 - _0 = (*_5); // scope 0 at $DIR/reference_prop.rs:+16:13: +16:22 -+ _3 = &mut (*_1); // scope 0 at $DIR/reference_prop.rs:+11:13: +11:26 -+ _0 = (*_2); // scope 0 at $DIR/reference_prop.rs:+15:13: +15:22 -+ _0 = (*_3); // scope 0 at $DIR/reference_prop.rs:+16:13: +16:22 ++ _0 = (*_1); // scope 0 at $DIR/reference_prop.rs:+15:13: +15:22 ++ _0 = (*_1); // scope 0 at $DIR/reference_prop.rs:+16:13: +16:22 return; // scope 0 at $DIR/reference_prop.rs:+17:13: +17:21 } } diff --git a/tests/mir-opt/reference_prop.rs b/tests/mir-opt/reference_prop.rs index e3e5d791464..93f8d1df8e8 100644 --- a/tests/mir-opt/reference_prop.rs +++ b/tests/mir-opt/reference_prop.rs @@ -433,6 +433,29 @@ fn maybe_dead(m: bool) { ) } +fn mut_raw_then_mut_shr() -> (i32, i32) { + let mut x = 2; + let xref = &mut x; + let xraw = &mut *xref as *mut _; + let xshr = &*xref; + // Verify that we completely replace with `x` in both cases. + let a = *xshr; + unsafe { *xraw = 4; } + (a, x) +} + +fn unique_with_copies() { + let y = { + let mut a = 0; + let x = &raw mut a; + // `*y` is not replacable below, so we must not replace `*x`. + unsafe { opaque(*x) }; + x + }; + // But rewriting as `*x` is ok. + unsafe { opaque(*y) }; +} + fn main() { let mut x = 5_usize; let mut y = 7_usize; @@ -444,6 +467,8 @@ fn main() { multiple_storage(); dominate_storage(); maybe_dead(true); + mut_raw_then_mut_shr(); + unique_with_copies(); } // EMIT_MIR reference_prop.reference_propagation.ReferencePropagation.diff @@ -454,3 +479,5 @@ fn main() { // EMIT_MIR reference_prop.multiple_storage.ReferencePropagation.diff // EMIT_MIR reference_prop.dominate_storage.ReferencePropagation.diff // EMIT_MIR reference_prop.maybe_dead.ReferencePropagation.diff +// EMIT_MIR reference_prop.mut_raw_then_mut_shr.ReferencePropagation.diff +// EMIT_MIR reference_prop.unique_with_copies.ReferencePropagation.diff diff --git a/tests/mir-opt/reference_prop.unique_with_copies.ReferencePropagation.diff b/tests/mir-opt/reference_prop.unique_with_copies.ReferencePropagation.diff new file mode 100644 index 00000000000..2cda2409e80 --- /dev/null +++ b/tests/mir-opt/reference_prop.unique_with_copies.ReferencePropagation.diff @@ -0,0 +1,66 @@ +- // MIR for `unique_with_copies` before ReferencePropagation ++ // MIR for `unique_with_copies` after ReferencePropagation + + fn unique_with_copies() -> () { + let mut _0: (); // return place in scope 0 at $DIR/reference_prop.rs:+0:25: +0:25 + let _1: *mut i32; // in scope 0 at $DIR/reference_prop.rs:+1:9: +1:10 + let mut _2: i32; // in scope 0 at $DIR/reference_prop.rs:+2:13: +2:18 + let _4: (); // in scope 0 at $DIR/reference_prop.rs:+5:18: +5:28 + let mut _5: i32; // in scope 0 at $DIR/reference_prop.rs:+5:25: +5:27 + let _6: (); // in scope 0 at $DIR/reference_prop.rs:+9:14: +9:24 + let mut _7: i32; // in scope 0 at $DIR/reference_prop.rs:+9:21: +9:23 + scope 1 { + debug y => _1; // in scope 1 at $DIR/reference_prop.rs:+1:9: +1:10 + scope 5 { + } + } + scope 2 { + debug a => _2; // in scope 2 at $DIR/reference_prop.rs:+2:13: +2:18 + let _3: *mut i32; // in scope 2 at $DIR/reference_prop.rs:+3:13: +3:14 + scope 3 { + debug x => _3; // in scope 3 at $DIR/reference_prop.rs:+3:13: +3:14 + scope 4 { + } + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/reference_prop.rs:+1:9: +1:10 + StorageLive(_2); // scope 0 at $DIR/reference_prop.rs:+2:13: +2:18 + _2 = const 0_i32; // scope 0 at $DIR/reference_prop.rs:+2:21: +2:22 +- StorageLive(_3); // scope 2 at $DIR/reference_prop.rs:+3:13: +3:14 + _3 = &raw mut _2; // scope 2 at $DIR/reference_prop.rs:+3:17: +3:27 + StorageLive(_4); // scope 3 at $DIR/reference_prop.rs:+5:9: +5:30 + StorageLive(_5); // scope 4 at $DIR/reference_prop.rs:+5:25: +5:27 + _5 = (*_3); // scope 4 at $DIR/reference_prop.rs:+5:25: +5:27 + _4 = opaque::(move _5) -> bb1; // scope 4 at $DIR/reference_prop.rs:+5:18: +5:28 + // mir::Constant + // + span: $DIR/reference_prop.rs:452:18: 452:24 + // + literal: Const { ty: fn(i32) {opaque::}, val: Value() } + } + + bb1: { + StorageDead(_5); // scope 4 at $DIR/reference_prop.rs:+5:27: +5:28 + StorageDead(_4); // scope 3 at $DIR/reference_prop.rs:+5:30: +5:31 + _1 = _3; // scope 3 at $DIR/reference_prop.rs:+6:9: +6:10 +- StorageDead(_3); // scope 2 at $DIR/reference_prop.rs:+7:5: +7:6 + StorageDead(_2); // scope 0 at $DIR/reference_prop.rs:+7:5: +7:6 + StorageLive(_6); // scope 1 at $DIR/reference_prop.rs:+9:5: +9:26 + StorageLive(_7); // scope 5 at $DIR/reference_prop.rs:+9:21: +9:23 +- _7 = (*_1); // scope 5 at $DIR/reference_prop.rs:+9:21: +9:23 ++ _7 = (*_3); // scope 5 at $DIR/reference_prop.rs:+9:21: +9:23 + _6 = opaque::(move _7) -> bb2; // scope 5 at $DIR/reference_prop.rs:+9:14: +9:24 + // mir::Constant + // + span: $DIR/reference_prop.rs:456:14: 456:20 + // + literal: Const { ty: fn(i32) {opaque::}, val: Value() } + } + + bb2: { + StorageDead(_7); // scope 5 at $DIR/reference_prop.rs:+9:23: +9:24 + StorageDead(_6); // scope 1 at $DIR/reference_prop.rs:+9:26: +9:27 + _0 = const (); // scope 0 at $DIR/reference_prop.rs:+0:25: +10:2 + StorageDead(_1); // scope 0 at $DIR/reference_prop.rs:+10:1: +10:2 + return; // scope 0 at $DIR/reference_prop.rs:+10:2: +10:2 + } + } + diff --git a/tests/run-make/coverage-reports/Makefile b/tests/run-make/coverage-reports/Makefile index d4ae03e590a..0ae409c4119 100644 --- a/tests/run-make/coverage-reports/Makefile +++ b/tests/run-make/coverage-reports/Makefile @@ -138,6 +138,7 @@ endif ) \ 2> "$(TMPDIR)"/show_coverage_stderr.$@.txt \ | "$(PYTHON)" $(BASEDIR)/normalize_paths.py \ + | "$(PYTHON)" $(BASEDIR)/sort_subviews.py \ > "$(TMPDIR)"/actual_show_coverage.$@.txt || \ ( status=$$? ; \ >&2 cat "$(TMPDIR)"/show_coverage_stderr.$@.txt ; \ @@ -158,23 +159,15 @@ ifdef RUSTC_BLESS_TEST else # Compare the show coverage output (`--bless` refreshes `typical` files). # - # FIXME(richkadel): None of the Rust test source samples have the - # `// ignore-llvm-cov-show-diffs` anymore. This directive exists to work around a limitation - # with `llvm-cov show`. When reporting coverage for multiple instantiations of a generic function, - # with different type substitutions, `llvm-cov show` prints these in a non-deterministic order, - # breaking the `diff` comparison. + # `llvm-cov show` normally prints instantiation groups in an unpredictable + # order, but we have used `sort_subviews.py` to sort them, so we can still + # check the output directly with `diff`. # - # A partial workaround is implemented below, with `diff --ignore-matching-lines=RE` - # to ignore each line prefixing each generic instantiation coverage code region. - # - # This workaround only works if the coverage counts are identical across all reported - # instantiations. If there is no way to ensure this, you may need to apply the - # `// ignore-llvm-cov-show-diffs` directive, and check for differences using the - # `.json` files to validate that results have not changed. (Until then, the JSON - # files are redundant, so there is no need to generate `expected_*.json` files or - # compare actual JSON results.) + # Some of the test cases are currently not working (since #110393) and have + # been marked with `// ignore-llvm-cov-show-diffs` so that they don't fail + # the build. - $(DIFF) --ignore-matching-lines='^ \| .*::<.*>.*:$$' --ignore-matching-lines='^ \| <.*>::.*:$$' \ + $(DIFF) \ expected_show_coverage.$@.txt "$(TMPDIR)"/actual_show_coverage.$@.txt || \ ( grep -q '^\/\/ ignore-llvm-cov-show-diffs' $(SOURCEDIR)/$@.rs && \ >&2 echo 'diff failed, but suppressed with `// ignore-llvm-cov-show-diffs` in $(SOURCEDIR)/$@.rs' \ diff --git a/tests/run-make/coverage-reports/expected_show_coverage.async.txt b/tests/run-make/coverage-reports/expected_show_coverage.async.txt index 87ccb6c43ea..93c1535b06b 100644 --- a/tests/run-make/coverage-reports/expected_show_coverage.async.txt +++ b/tests/run-make/coverage-reports/expected_show_coverage.async.txt @@ -41,9 +41,9 @@ 41| 1| // executed asynchronously. 42| 1| match x { 43| 1| y if c(x).await == y + 1 => { d().await; } - ^0 ^0 ^0 ^0 + ^0 ^0 ^0 ^0 44| 1| y if f().await == y + 1 => (), - ^0 ^0 ^0 + ^0 ^0 ^0 45| 1| _ => (), 46| | } 47| 1|} diff --git a/tests/run-make/coverage-reports/expected_show_coverage.sort_groups.txt b/tests/run-make/coverage-reports/expected_show_coverage.sort_groups.txt new file mode 100644 index 00000000000..81468cb35da --- /dev/null +++ b/tests/run-make/coverage-reports/expected_show_coverage.sort_groups.txt @@ -0,0 +1,49 @@ + 1| |// compile-flags: --edition=2021 + 2| | + 3| |// Demonstrate that `sort_subviews.py` can sort instantiation groups into a + 4| |// predictable order, while preserving their heterogeneous contents. + 5| | + 6| 1|fn main() { + 7| 1| let cond = std::env::args().len() > 1; + 8| 1| generic_fn::<()>(cond); + 9| 1| generic_fn::<&'static str>(!cond); + 10| 1| if false { + 11| 0| generic_fn::(cond); + 12| 1| } + 13| 1| generic_fn::(cond); + 14| 1| other_fn(); + 15| 1|} + 16| | + 17| 3|fn generic_fn(cond: bool) { + 18| 3| if cond { + 19| 1| println!("{}", std::any::type_name::()); + 20| 2| } + 21| 3|} + ------------------ + | Unexecuted instantiation: sort_groups::generic_fn:: + ------------------ + | sort_groups::generic_fn::<&str>: + | 17| 1|fn generic_fn(cond: bool) { + | 18| 1| if cond { + | 19| 1| println!("{}", std::any::type_name::()); + | 20| 1| } + | ^0 + | 21| 1|} + ------------------ + | sort_groups::generic_fn::<()>: + | 17| 1|fn generic_fn(cond: bool) { + | 18| 1| if cond { + | 19| 0| println!("{}", std::any::type_name::()); + | 20| 1| } + | 21| 1|} + ------------------ + | sort_groups::generic_fn::: + | 17| 1|fn generic_fn(cond: bool) { + | 18| 1| if cond { + | 19| 0| println!("{}", std::any::type_name::()); + | 20| 1| } + | 21| 1|} + ------------------ + 22| | + 23| 1|fn other_fn() {} + diff --git a/tests/run-make/coverage-reports/expected_show_coverage.uses_crate.txt b/tests/run-make/coverage-reports/expected_show_coverage.uses_crate.txt index 65eb1008dd8..412f4a93b9c 100644 --- a/tests/run-make/coverage-reports/expected_show_coverage.uses_crate.txt +++ b/tests/run-make/coverage-reports/expected_show_coverage.uses_crate.txt @@ -19,29 +19,29 @@ 18| 2| println!("used_only_from_bin_crate_generic_function with {:?}", arg); 19| 2|} ------------------ - | used_crate::used_only_from_bin_crate_generic_function::<&str>: - | 17| 1|pub fn used_only_from_bin_crate_generic_function(arg: T) { - | 18| 1| println!("used_only_from_bin_crate_generic_function with {:?}", arg); - | 19| 1|} + | Unexecuted instantiation: used_crate::used_only_from_bin_crate_generic_function::<_> ------------------ | used_crate::used_only_from_bin_crate_generic_function::<&alloc::vec::Vec>: | 17| 1|pub fn used_only_from_bin_crate_generic_function(arg: T) { | 18| 1| println!("used_only_from_bin_crate_generic_function with {:?}", arg); | 19| 1|} ------------------ - | Unexecuted instantiation: used_crate::used_only_from_bin_crate_generic_function::<_> + | used_crate::used_only_from_bin_crate_generic_function::<&str>: + | 17| 1|pub fn used_only_from_bin_crate_generic_function(arg: T) { + | 18| 1| println!("used_only_from_bin_crate_generic_function with {:?}", arg); + | 19| 1|} ------------------ 20| |// Expect for above function: `Unexecuted instantiation` (see below) 21| 2|pub fn used_only_from_this_lib_crate_generic_function(arg: T) { 22| 2| println!("used_only_from_this_lib_crate_generic_function with {:?}", arg); 23| 2|} ------------------ - | used_crate::used_only_from_this_lib_crate_generic_function::>: + | used_crate::used_only_from_this_lib_crate_generic_function::<&str>: | 21| 1|pub fn used_only_from_this_lib_crate_generic_function(arg: T) { | 22| 1| println!("used_only_from_this_lib_crate_generic_function with {:?}", arg); | 23| 1|} ------------------ - | used_crate::used_only_from_this_lib_crate_generic_function::<&str>: + | used_crate::used_only_from_this_lib_crate_generic_function::>: | 21| 1|pub fn used_only_from_this_lib_crate_generic_function(arg: T) { | 22| 1| println!("used_only_from_this_lib_crate_generic_function with {:?}", arg); | 23| 1|} @@ -51,12 +51,12 @@ 26| 2| println!("used_from_bin_crate_and_lib_crate_generic_function with {:?}", arg); 27| 2|} ------------------ - | used_crate::used_from_bin_crate_and_lib_crate_generic_function::>: + | used_crate::used_from_bin_crate_and_lib_crate_generic_function::<&str>: | 25| 1|pub fn used_from_bin_crate_and_lib_crate_generic_function(arg: T) { | 26| 1| println!("used_from_bin_crate_and_lib_crate_generic_function with {:?}", arg); | 27| 1|} ------------------ - | used_crate::used_from_bin_crate_and_lib_crate_generic_function::<&str>: + | used_crate::used_from_bin_crate_and_lib_crate_generic_function::>: | 25| 1|pub fn used_from_bin_crate_and_lib_crate_generic_function(arg: T) { | 26| 1| println!("used_from_bin_crate_and_lib_crate_generic_function with {:?}", arg); | 27| 1|} diff --git a/tests/run-make/coverage-reports/expected_show_coverage.uses_inline_crate.txt b/tests/run-make/coverage-reports/expected_show_coverage.uses_inline_crate.txt index 748343885de..66ca9e80a32 100644 --- a/tests/run-make/coverage-reports/expected_show_coverage.uses_inline_crate.txt +++ b/tests/run-make/coverage-reports/expected_show_coverage.uses_inline_crate.txt @@ -42,6 +42,8 @@ 40| 2| println!("used_only_from_bin_crate_generic_function with {:?}", arg); 41| 2|} ------------------ + | Unexecuted instantiation: used_inline_crate::used_only_from_bin_crate_generic_function::<_> + ------------------ | used_inline_crate::used_only_from_bin_crate_generic_function::<&alloc::vec::Vec>: | 39| 1|pub fn used_only_from_bin_crate_generic_function(arg: T) { | 40| 1| println!("used_only_from_bin_crate_generic_function with {:?}", arg); @@ -51,8 +53,6 @@ | 39| 1|pub fn used_only_from_bin_crate_generic_function(arg: T) { | 40| 1| println!("used_only_from_bin_crate_generic_function with {:?}", arg); | 41| 1|} - ------------------ - | Unexecuted instantiation: used_inline_crate::used_only_from_bin_crate_generic_function::<_> ------------------ 42| |// Expect for above function: `Unexecuted instantiation` (see notes in `used_crate.rs`) 43| | @@ -77,15 +77,15 @@ 51| 3| println!("used_from_bin_crate_and_lib_crate_generic_function with {:?}", arg); 52| 3|} ------------------ - | used_inline_crate::used_from_bin_crate_and_lib_crate_generic_function::>: - | 50| 1|pub fn used_from_bin_crate_and_lib_crate_generic_function(arg: T) { - | 51| 1| println!("used_from_bin_crate_and_lib_crate_generic_function with {:?}", arg); - | 52| 1|} - ------------------ | used_inline_crate::used_from_bin_crate_and_lib_crate_generic_function::<&str>: | 50| 2|pub fn used_from_bin_crate_and_lib_crate_generic_function(arg: T) { | 51| 2| println!("used_from_bin_crate_and_lib_crate_generic_function with {:?}", arg); | 52| 2|} + ------------------ + | used_inline_crate::used_from_bin_crate_and_lib_crate_generic_function::>: + | 50| 1|pub fn used_from_bin_crate_and_lib_crate_generic_function(arg: T) { + | 51| 1| println!("used_from_bin_crate_and_lib_crate_generic_function with {:?}", arg); + | 52| 1|} ------------------ 53| | 54| |#[inline(always)] diff --git a/tests/run-make/coverage-reports/sort_subviews.py b/tests/run-make/coverage-reports/sort_subviews.py new file mode 100644 index 00000000000..10cfc51d447 --- /dev/null +++ b/tests/run-make/coverage-reports/sort_subviews.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 + +# `llvm-cov show` prints grouped subviews (e.g. for generic functions) in an +# unstable order, which is inconvenient when checking output snapshots with +# `diff`. To work around that, this script detects consecutive subviews in its +# piped input, and sorts them while preserving their contents. + +from __future__ import print_function + +import sys + + +def main(): + subviews = [] + + def flush_subviews(): + if not subviews: + return + + # The last "subview" should be just a boundary line on its own, so + # temporarily remove it before sorting the accumulated subviews. + terminator = subviews.pop() + subviews.sort() + subviews.append(terminator) + + for view in subviews: + for line in view: + print(line, end="") + + subviews.clear() + + for line in sys.stdin: + if line.startswith(" ------------------"): + # This is a subview boundary line, so start a new subview. + subviews.append([line]) + elif line.startswith(" |"): + # Add this line to the current subview. + subviews[-1].append(line) + else: + # This line is not part of a subview, so sort and print any + # accumulated subviews, and then print the line as-is. + flush_subviews() + print(line, end="") + + flush_subviews() + assert not subviews + + +if __name__ == "__main__": + main() diff --git a/tests/run-make/coverage/sort_groups.rs b/tests/run-make/coverage/sort_groups.rs new file mode 100644 index 00000000000..f89f9f3ec61 --- /dev/null +++ b/tests/run-make/coverage/sort_groups.rs @@ -0,0 +1,23 @@ +// compile-flags: --edition=2021 + +// Demonstrate that `sort_subviews.py` can sort instantiation groups into a +// predictable order, while preserving their heterogeneous contents. + +fn main() { + let cond = std::env::args().len() > 1; + generic_fn::<()>(cond); + generic_fn::<&'static str>(!cond); + if false { + generic_fn::(cond); + } + generic_fn::(cond); + other_fn(); +} + +fn generic_fn(cond: bool) { + if cond { + println!("{}", std::any::type_name::()); + } +} + +fn other_fn() {} diff --git a/tests/run-make/staticlib-dylib-linkage/Makefile b/tests/run-make/staticlib-dylib-linkage/Makefile new file mode 100644 index 00000000000..a1e86a7ce4b --- /dev/null +++ b/tests/run-make/staticlib-dylib-linkage/Makefile @@ -0,0 +1,21 @@ +include ../tools.mk + +# ignore-cross-compile +# ignore-msvc FIXME(bjorn3) can't figure out how to link with the MSVC toolchain +# ignore-wasm wasm doesn't support dynamic libraries + +all: + $(RUSTC) -C prefer-dynamic bar.rs + $(RUSTC) foo.rs --crate-type staticlib --print native-static-libs \ + -Z staticlib-allow-rdylib-deps 2>&1 | grep 'note: native-static-libs: ' \ + | sed 's/note: native-static-libs: \(.*\)/\1/' > $(TMPDIR)/libs.txt + cat $(TMPDIR)/libs.txt + +ifdef IS_MSVC + $(CC) $(CFLAGS) /c foo.c /Fo:$(TMPDIR)/foo.o + $(RUSTC_LINKER) $(TMPDIR)/foo.o $(TMPDIR)/foo.lib $$(cat $(TMPDIR)/libs.txt) $(call OUT_EXE,foo) +else + $(CC) $(CFLAGS) foo.c -L $(TMPDIR) -lfoo $$(cat $(TMPDIR)/libs.txt) -o $(call RUN_BINFILE,foo) +endif + + $(call RUN,foo) diff --git a/tests/run-make/staticlib-dylib-linkage/bar.rs b/tests/run-make/staticlib-dylib-linkage/bar.rs new file mode 100644 index 00000000000..b3a7539abae --- /dev/null +++ b/tests/run-make/staticlib-dylib-linkage/bar.rs @@ -0,0 +1,5 @@ +#![crate_type = "dylib"] + +pub fn bar() { + println!("hello!"); +} diff --git a/tests/run-make/staticlib-dylib-linkage/foo.c b/tests/run-make/staticlib-dylib-linkage/foo.c new file mode 100644 index 00000000000..154f9682ef8 --- /dev/null +++ b/tests/run-make/staticlib-dylib-linkage/foo.c @@ -0,0 +1,10 @@ +#include + +extern void foo(); +extern unsigned bar(unsigned a, unsigned b); + +int main() { + foo(); + assert(bar(1, 2) == 3); + return 0; +} diff --git a/tests/run-make/staticlib-dylib-linkage/foo.rs b/tests/run-make/staticlib-dylib-linkage/foo.rs new file mode 100644 index 00000000000..af439391c75 --- /dev/null +++ b/tests/run-make/staticlib-dylib-linkage/foo.rs @@ -0,0 +1,13 @@ +#![crate_type = "staticlib"] + +extern crate bar; + +#[no_mangle] +pub extern "C" fn foo() { + bar::bar(); +} + +#[no_mangle] +pub extern "C" fn bar(a: u32, b: u32) -> u32 { + a + b +} diff --git a/tests/rustdoc-gui/check-stab-in-docblock.goml b/tests/rustdoc-gui/check-stab-in-docblock.goml index 2f62636211b..f25c88690e5 100644 --- a/tests/rustdoc-gui/check-stab-in-docblock.goml +++ b/tests/rustdoc-gui/check-stab-in-docblock.goml @@ -7,20 +7,26 @@ set-window-size: (786, 600) // Confirms that there 3 paragraphs. assert-count: (".top-doc .docblock p", 3) // Checking that there is no scrollable content. -store-property: (clientHeight, ".top-doc .docblock p:nth-of-type(1)", "clientHeight") -store-property: (clientWidth, ".top-doc .docblock p:nth-of-type(1)", "clientWidth") +store-property: (".top-doc .docblock p:nth-of-type(1)", { + "clientHeight": clientHeight, + "clientWidth": clientWidth, +}) assert-property: ( ".top-doc .docblock p:nth-of-type(1)", {"scrollHeight": |clientHeight|, "scrollWidth": |clientWidth|}, ) -store-property: (clientHeight, ".top-doc .docblock p:nth-of-type(2)", "clientHeight") -store-property: (clientWidth, ".top-doc .docblock p:nth-of-type(2)", "clientWidth") +store-property: (".top-doc .docblock p:nth-of-type(2)", { + "clientHeight": clientHeight, + "clientWidth": clientWidth, +}) assert-property: ( ".top-doc .docblock p:nth-of-type(2)", {"scrollHeight": |clientHeight|, "scrollWidth": |clientWidth|}, ) -store-property: (clientHeight, ".top-doc .docblock p:nth-of-type(3)", "clientHeight") -store-property: (clientWidth, ".top-doc .docblock p:nth-of-type(3)", "clientWidth") +store-property: (".top-doc .docblock p:nth-of-type(3)", { + "clientHeight": clientHeight, + "clientWidth": clientWidth, +}) assert-property: ( ".top-doc .docblock p:nth-of-type(3)", {"scrollHeight": |clientHeight|, "scrollWidth": |clientWidth|}, diff --git a/tests/rustdoc-gui/codeblock-sub.goml b/tests/rustdoc-gui/codeblock-sub.goml index 03575cc6aaa..a4b0558765a 100644 --- a/tests/rustdoc-gui/codeblock-sub.goml +++ b/tests/rustdoc-gui/codeblock-sub.goml @@ -1,5 +1,5 @@ // Test that code blocks nested within do not have a line height of 0. go-to: "file://" + |DOC_PATH| + "/test_docs/codeblock_sub/index.html" -store-property: (codeblock_sub_1, "#codeblock-sub-1", "offsetHeight") +store-property: ("#codeblock-sub-1", {"offsetHeight": codeblock_sub_1}) assert-property-false: ("#codeblock-sub-3", { "offsetHeight": |codeblock_sub_1| }) diff --git a/tests/rustdoc-gui/docblock-details.goml b/tests/rustdoc-gui/docblock-details.goml index 58ff17619f6..8e6d2ba824f 100644 --- a/tests/rustdoc-gui/docblock-details.goml +++ b/tests/rustdoc-gui/docblock-details.goml @@ -9,7 +9,7 @@ reload: assert-text: (".top-doc .docblock > h3", "Hello") assert-css: ( ".top-doc .docblock > h3", - {"border-bottom": "1px solid rgb(210, 210, 210)"}, + {"border-bottom": "1px solid #d2d2d2"}, ) // We now check that the `` doesn't have a bottom border and has the correct display. assert-css: ( diff --git a/tests/rustdoc-gui/item-info.goml b/tests/rustdoc-gui/item-info.goml index 60fd7c4e198..030ff8f8a3e 100644 --- a/tests/rustdoc-gui/item-info.goml +++ b/tests/rustdoc-gui/item-info.goml @@ -4,8 +4,8 @@ go-to: "file://" + |DOC_PATH| + "/lib2/struct.Foo.html" // We set a fixed size so there is no chance of "random" resize. set-window-size: (1100, 800) // We check that ".item-info" is bigger than its content. -assert-css: (".item-info", {"width": "840px"}) -assert-css: (".item-info .stab", {"width": "289px"}) +assert-size: (".item-info", {"width": 840}) +assert-size: (".item-info .stab", {"width": 289}) assert-position: (".item-info .stab", {"x": 245}) // Now we ensure that they're not rendered on the same line. diff --git a/tests/rustdoc-gui/notable-trait.goml b/tests/rustdoc-gui/notable-trait.goml index f65da577478..ecb57c274a5 100644 --- a/tests/rustdoc-gui/notable-trait.goml +++ b/tests/rustdoc-gui/notable-trait.goml @@ -225,12 +225,12 @@ assert: "#method\.create_an_iterator_from_read .tooltip:focus" // Now we check that the focus isn't given back to the wrong item when opening // another popover. -store-window-property: (scroll, "scrollY") +store-window-property: {"scrollY": scroll} click: "#method\.create_an_iterator_from_read .fn" // We ensure that the scroll position changed. assert-window-property-false: {"scrollY": |scroll|} // Store the new position. -store-window-property: (scroll, "scrollY") +store-window-property: {"scrollY": scroll} click: "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']" wait-for: "//*[@class='tooltip popover']" click: "#settings-menu a" @@ -239,12 +239,12 @@ click: ".search-input" assert-window-property-false: {"scrollY": |scroll|} // Same but with Escape handling. -store-window-property: (scroll, "scrollY") +store-window-property: {"scrollY": scroll} click: "#method\.create_an_iterator_from_read .fn" // We ensure that the scroll position changed. assert-window-property-false: {"scrollY": |scroll|} // Store the new position. -store-window-property: (scroll, "scrollY") +store-window-property: {"scrollY": scroll} click: "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']" wait-for: "//*[@class='tooltip popover']" click: "#settings-menu a" diff --git a/tests/rustdoc-gui/scrape-examples-button-focus.goml b/tests/rustdoc-gui/scrape-examples-button-focus.goml index 77061ea2a3f..af4293dfc00 100644 --- a/tests/rustdoc-gui/scrape-examples-button-focus.goml +++ b/tests/rustdoc-gui/scrape-examples-button-focus.goml @@ -3,7 +3,7 @@ go-to: "file://" + |DOC_PATH| + "/scrape_examples/fn.test.html" // The next/prev buttons vertically scroll the code viewport between examples -store-property: (initialScrollTop, ".scraped-example-list > .scraped-example pre", "scrollTop") +store-property: (".scraped-example-list > .scraped-example pre", {"scrollTop": initialScrollTop}) focus: ".scraped-example-list > .scraped-example .next" press-key: "Enter" assert-property-false: (".scraped-example-list > .scraped-example pre", { @@ -16,7 +16,7 @@ assert-property: (".scraped-example-list > .scraped-example pre", { }, NEAR) // The expand button increases the scrollHeight of the minimized code viewport -store-property: (smallOffsetHeight, ".scraped-example-list > .scraped-example pre", "offsetHeight") +store-property: (".scraped-example-list > .scraped-example pre", {"offsetHeight": smallOffsetHeight}) assert-property-false: (".scraped-example-list > .scraped-example pre", { "scrollHeight": |smallOffsetHeight| }, NEAR) @@ -25,7 +25,7 @@ press-key: "Enter" assert-property-false: (".scraped-example-list > .scraped-example pre", { "offsetHeight": |smallOffsetHeight| }, NEAR) -store-property: (fullOffsetHeight, ".scraped-example-list > .scraped-example pre", "offsetHeight") +store-property: (".scraped-example-list > .scraped-example pre", {"offsetHeight": fullOffsetHeight}) assert-property: (".scraped-example-list > .scraped-example pre", { "scrollHeight": |fullOffsetHeight| }, NEAR) diff --git a/tests/rustdoc-gui/scrape-examples-layout.goml b/tests/rustdoc-gui/scrape-examples-layout.goml index 160056d6d05..4fc1c1ac065 100644 --- a/tests/rustdoc-gui/scrape-examples-layout.goml +++ b/tests/rustdoc-gui/scrape-examples-layout.goml @@ -9,9 +9,8 @@ assert-property-false: ( // Check that examples with very long lines have the same width as ones that don't. store-property: ( - clientWidth, ".more-scraped-examples .scraped-example:nth-child(2) .code-wrapper .src-line-numbers", - "clientWidth" + {"clientWidth": clientWidth}, ) assert-property: ( @@ -40,8 +39,8 @@ assert-property: ( store-value: (offset_y, 4) // First with desktop -assert-position: (".scraped-example .code-wrapper", {"y": 253}) -assert-position: (".scraped-example .code-wrapper .prev", {"y": 253 + |offset_y|}) +assert-position: (".scraped-example .code-wrapper", {"y": 226}) +assert-position: (".scraped-example .code-wrapper .prev", {"y": 226 + |offset_y|}) // Then with mobile set-window-size: (600, 600) diff --git a/tests/rustdoc-gui/search-result-display.goml b/tests/rustdoc-gui/search-result-display.goml index 93c71f23f24..ee5598e4b21 100644 --- a/tests/rustdoc-gui/search-result-display.goml +++ b/tests/rustdoc-gui/search-result-display.goml @@ -32,8 +32,8 @@ set-text: ( ) // Then we compare again to confirm the height didn't change. -assert-css: ("#crate-search", {"width": "527px"}) -assert-css: (".search-results-title", {"height": "50px", "width": "640px"}) +assert-size: ("#crate-search", {"width": 527}) +assert-size: (".search-results-title", {"height": 50, "width": 640}) // And we check that the `