From 42a901acd9c9d3a0c9ca7adf2470b789f9c81a5b Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sat, 24 Aug 2024 16:15:43 -0400 Subject: [PATCH 01/13] Don't use TyKind in lint --- compiler/rustc_lint/src/foreign_modules.rs | 25 +++++++++++----------- compiler/rustc_middle/src/ty/sty.rs | 2 +- compiler/rustc_type_ir/src/ty_kind.rs | 7 ------ 3 files changed, 14 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_lint/src/foreign_modules.rs b/compiler/rustc_lint/src/foreign_modules.rs index 5da1cbc2283b6..a60fc0ffbbb3b 100644 --- a/compiler/rustc_lint/src/foreign_modules.rs +++ b/compiler/rustc_lint/src/foreign_modules.rs @@ -265,8 +265,6 @@ fn structurally_same_type_impl<'tcx>( } else { // Do a full, depth-first comparison between the two. use rustc_type_ir::TyKind::*; - let a_kind = a.kind(); - let b_kind = b.kind(); let compare_layouts = |a, b| -> Result> { debug!("compare_layouts({:?}, {:?})", a, b); @@ -281,12 +279,11 @@ fn structurally_same_type_impl<'tcx>( Ok(a_layout == b_layout) }; - #[allow(rustc::usage_of_ty_tykind)] let is_primitive_or_pointer = - |kind: &ty::TyKind<'_>| kind.is_primitive() || matches!(kind, RawPtr(..) | Ref(..)); + |ty: Ty<'tcx>| ty.is_primitive() || matches!(ty.kind(), RawPtr(..) | Ref(..)); ensure_sufficient_stack(|| { - match (a_kind, b_kind) { + match (a.kind(), b.kind()) { (Adt(a_def, _), Adt(b_def, _)) => { // We can immediately rule out these types as structurally same if // their layouts differ. @@ -382,17 +379,21 @@ fn structurally_same_type_impl<'tcx>( // An Adt and a primitive or pointer type. This can be FFI-safe if non-null // enum layout optimisation is being applied. - (Adt(..), other_kind) | (other_kind, Adt(..)) - if is_primitive_or_pointer(other_kind) => - { - let (primitive, adt) = - if is_primitive_or_pointer(a.kind()) { (a, b) } else { (b, a) }; - if let Some(ty) = types::repr_nullable_ptr(tcx, param_env, adt, ckind) { - ty == primitive + (Adt(..), _) if is_primitive_or_pointer(b) => { + if let Some(ty) = types::repr_nullable_ptr(tcx, param_env, a, ckind) { + ty == b } else { compare_layouts(a, b).unwrap_or(false) } } + (_, Adt(..)) if is_primitive_or_pointer(a) => { + if let Some(ty) = types::repr_nullable_ptr(tcx, param_env, b, ckind) { + ty == a + } else { + compare_layouts(a, b).unwrap_or(false) + } + } + // Otherwise, just compare the layouts. This may fail to lint for some // incompatible types, but at the very least, will stop reads into // uninitialised memory. diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index d60bfb9faa1aa..c6621a7a64331 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -1000,7 +1000,7 @@ impl<'tcx> Ty<'tcx> { #[inline] pub fn is_primitive(self) -> bool { - self.kind().is_primitive() + matches!(self.kind(), Bool | Char | Int(_) | Uint(_) | Float(_)) } #[inline] diff --git a/compiler/rustc_type_ir/src/ty_kind.rs b/compiler/rustc_type_ir/src/ty_kind.rs index 328b6739d9756..80c3565911e9a 100644 --- a/compiler/rustc_type_ir/src/ty_kind.rs +++ b/compiler/rustc_type_ir/src/ty_kind.rs @@ -254,13 +254,6 @@ pub enum TyKind { Error(I::ErrorGuaranteed), } -impl TyKind { - #[inline] - pub fn is_primitive(&self) -> bool { - matches!(self, Bool | Char | Int(_) | Uint(_) | Float(_)) - } -} - // This is manually implemented because a derive would require `I: Debug` impl fmt::Debug for TyKind { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { From af05882eb5775d9bf8b2efea5598cd6305f6a33e Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Sat, 24 Aug 2024 15:14:19 -0700 Subject: [PATCH 02/13] Deny wasm_c_abi lint to nudge the last 25% This shouldn't affect projects indirectly depending on wasm-bindgen because cargo passes `--cap-lints=allow` when building dependencies. --- compiler/rustc_lint_defs/src/builtin.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index a65d30eb817a0..bcd00ac06ba4b 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -4806,7 +4806,7 @@ declare_lint! { /// version of Rust this will be fixed and therefore dependencies relying /// on the non-spec-compliant C ABI will stop functioning. pub WASM_C_ABI, - Warn, + Deny, "detects dependencies that are incompatible with the Wasm C ABI", @future_incompatible = FutureIncompatibleInfo { reason: FutureIncompatibilityReason::FutureReleaseErrorReportInDeps, From a1c36c6ae9e6036b89e4c4b9cec2ce2e375b2a85 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Wed, 14 Aug 2024 20:16:28 +0300 Subject: [PATCH 03/13] linker: Synchronize native library search in rustc and linker --- compiler/rustc_codegen_ssa/src/back/link.rs | 61 ++++---------- compiler/rustc_metadata/src/lib.rs | 5 +- compiler/rustc_metadata/src/native_libs.rs | 92 ++++++++++++++++++--- 3 files changed, 101 insertions(+), 57 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 4d19425255faf..e8143b9a5f38f 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -2,7 +2,7 @@ use std::collections::BTreeSet; use std::ffi::OsString; use std::fs::{read, File, OpenOptions}; use std::io::{BufWriter, Write}; -use std::ops::Deref; +use std::ops::{ControlFlow, Deref}; use std::path::{Path, PathBuf}; use std::process::{ExitStatus, Output, Stdio}; use std::{env, fmt, fs, io, mem, str}; @@ -18,8 +18,8 @@ use rustc_data_structures::temp_dir::MaybeTempDir; use rustc_errors::{DiagCtxtHandle, ErrorGuaranteed, FatalError}; use rustc_fs_util::{fix_windows_verbatim_for_gcc, try_canonicalize}; use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; -use rustc_metadata::find_native_static_library; use rustc_metadata::fs::{copy_to_stdout, emit_wrapper_file, METADATA_FILENAME}; +use rustc_metadata::{find_native_static_library, walk_native_lib_search_dirs}; use rustc_middle::bug; use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile; use rustc_middle::middle::dependency_format::Linkage; @@ -2110,50 +2110,19 @@ fn add_library_search_dirs( return; } - // Library search paths explicitly supplied by user (`-L` on the command line). - for search_path in sess.target_filesearch(PathKind::Native).cli_search_paths() { - cmd.include_path(&fix_windows_verbatim_for_gcc(&search_path.dir)); - } - for search_path in sess.target_filesearch(PathKind::Framework).cli_search_paths() { - // Contrary to the `-L` docs only framework-specific paths are considered here. - if search_path.kind != PathKind::All { - cmd.framework_path(&search_path.dir); - } - } - - // The toolchain ships some native library components and self-contained linking was enabled. - // Add the self-contained library directory to search paths. - if self_contained_components.intersects( - LinkSelfContainedComponents::LIBC - | LinkSelfContainedComponents::UNWIND - | LinkSelfContainedComponents::MINGW, - ) { - let lib_path = sess.target_tlib_path.dir.join("self-contained"); - cmd.include_path(&fix_windows_verbatim_for_gcc(&lib_path)); - } - - // Toolchains for some targets may ship `libunwind.a`, but place it into the main sysroot - // library directory instead of the self-contained directories. - // Sanitizer libraries have the same issue and are also linked by name on Apple targets. - // The targets here should be in sync with `copy_third_party_objects` in bootstrap. - // FIXME: implement `-Clink-self-contained=+/-unwind,+/-sanitizers`, move the shipped libunwind - // and sanitizers to self-contained directory, and stop adding this search path. - if sess.target.vendor == "fortanix" - || sess.target.os == "linux" - || sess.target.os == "fuchsia" - || sess.target.is_like_osx && !sess.opts.unstable_opts.sanitizer.is_empty() - { - cmd.include_path(&fix_windows_verbatim_for_gcc(&sess.target_tlib_path.dir)); - } - - // Mac Catalyst uses the macOS SDK, but to link to iOS-specific frameworks - // we must have the support library stubs in the library search path (#121430). - if let Some(sdk_root) = apple_sdk_root - && sess.target.llvm_target.contains("macabi") - { - cmd.include_path(&sdk_root.join("System/iOSSupport/usr/lib")); - cmd.framework_path(&sdk_root.join("System/iOSSupport/System/Library/Frameworks")); - } + walk_native_lib_search_dirs( + sess, + self_contained_components, + apple_sdk_root, + |dir, is_framework| { + if is_framework { + cmd.framework_path(dir); + } else { + cmd.include_path(&fix_windows_verbatim_for_gcc(dir)); + } + ControlFlow::<()>::Continue(()) + }, + ); } /// Add options making relocation sections in the produced ELF files read-only diff --git a/compiler/rustc_metadata/src/lib.rs b/compiler/rustc_metadata/src/lib.rs index acaf9fb0fc32a..d530a7cd9d4bc 100644 --- a/compiler/rustc_metadata/src/lib.rs +++ b/compiler/rustc_metadata/src/lib.rs @@ -3,6 +3,7 @@ #![allow(rustc::potential_query_instability)] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] +#![feature(control_flow_enum)] #![feature(coroutines)] #![feature(decl_macro)] #![feature(error_iter)] @@ -34,7 +35,9 @@ pub mod locator; pub use creader::{load_symbol_from_dylib, DylibError}; pub use fs::{emit_wrapper_file, METADATA_FILENAME}; -pub use native_libs::find_native_static_library; +pub use native_libs::{ + find_native_static_library, try_find_native_static_library, walk_native_lib_search_dirs, +}; pub use rmeta::{encode_metadata, rendered_const, EncodedMetadata, METADATA_HEADER}; rustc_fluent_macro::fluent_messages! { "../messages.ftl" } diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs index 34497f5ac53f8..a6ad449cb53e8 100644 --- a/compiler/rustc_metadata/src/native_libs.rs +++ b/compiler/rustc_metadata/src/native_libs.rs @@ -1,4 +1,5 @@ -use std::path::PathBuf; +use std::ops::ControlFlow; +use std::path::{Path, PathBuf}; use rustc_ast::{NestedMetaItem, CRATE_NODE_ID}; use rustc_attr as attr; @@ -16,10 +17,68 @@ use rustc_session::Session; use rustc_span::def_id::{DefId, LOCAL_CRATE}; use rustc_span::symbol::{sym, Symbol}; use rustc_target::spec::abi::Abi; +use rustc_target::spec::LinkSelfContainedComponents; use crate::{errors, fluent_generated}; -pub fn find_native_static_library(name: &str, verbatim: bool, sess: &Session) -> PathBuf { +pub fn walk_native_lib_search_dirs( + sess: &Session, + self_contained_components: LinkSelfContainedComponents, + apple_sdk_root: Option<&Path>, + mut f: impl FnMut(&Path, bool /*is_framework*/) -> ControlFlow, +) -> ControlFlow { + // Library search paths explicitly supplied by user (`-L` on the command line). + for search_path in sess.target_filesearch(PathKind::Native).cli_search_paths() { + f(&search_path.dir, false)?; + } + for search_path in sess.target_filesearch(PathKind::Framework).cli_search_paths() { + // Frameworks are looked up strictly in framework-specific paths. + if search_path.kind != PathKind::All { + f(&search_path.dir, true)?; + } + } + + // The toolchain ships some native library components and self-contained linking was enabled. + // Add the self-contained library directory to search paths. + if self_contained_components.intersects( + LinkSelfContainedComponents::LIBC + | LinkSelfContainedComponents::UNWIND + | LinkSelfContainedComponents::MINGW, + ) { + f(&sess.target_tlib_path.dir.join("self-contained"), false)?; + } + + // Toolchains for some targets may ship `libunwind.a`, but place it into the main sysroot + // library directory instead of the self-contained directories. + // Sanitizer libraries have the same issue and are also linked by name on Apple targets. + // The targets here should be in sync with `copy_third_party_objects` in bootstrap. + // FIXME: implement `-Clink-self-contained=+/-unwind,+/-sanitizers`, move the shipped libunwind + // and sanitizers to self-contained directory, and stop adding this search path. + if sess.target.vendor == "fortanix" + || sess.target.os == "linux" + || sess.target.os == "fuchsia" + || sess.target.is_like_osx && !sess.opts.unstable_opts.sanitizer.is_empty() + { + f(&sess.target_tlib_path.dir, false)?; + } + + // Mac Catalyst uses the macOS SDK, but to link to iOS-specific frameworks + // we must have the support library stubs in the library search path (#121430). + if let Some(sdk_root) = apple_sdk_root + && sess.target.llvm_target.contains("macabi") + { + f(&sdk_root.join("System/iOSSupport/usr/lib"), false)?; + f(&sdk_root.join("System/iOSSupport/System/Library/Frameworks"), true)?; + } + + ControlFlow::Continue(()) +} + +pub fn try_find_native_static_library( + sess: &Session, + name: &str, + verbatim: bool, +) -> Option { let formats = if verbatim { vec![("".into(), "".into())] } else { @@ -30,16 +89,29 @@ pub fn find_native_static_library(name: &str, verbatim: bool, sess: &Session) -> if os == unix { vec![os] } else { vec![os, unix] } }; - for path in sess.target_filesearch(PathKind::Native).search_paths() { - for (prefix, suffix) in &formats { - let test = path.dir.join(format!("{prefix}{name}{suffix}")); - if test.exists() { - return test; + // FIXME: Account for self-contained linking settings and Apple SDK. + walk_native_lib_search_dirs( + sess, + LinkSelfContainedComponents::empty(), + None, + |dir, is_framework| { + if !is_framework { + for (prefix, suffix) in &formats { + let test = dir.join(format!("{prefix}{name}{suffix}")); + if test.exists() { + return ControlFlow::Break(test); + } + } } - } - } + ControlFlow::Continue(()) + }, + ) + .break_value() +} - sess.dcx().emit_fatal(errors::MissingNativeLibrary::new(name, verbatim)); +pub fn find_native_static_library(name: &str, verbatim: bool, sess: &Session) -> PathBuf { + try_find_native_static_library(sess, name, verbatim) + .unwrap_or_else(|| sess.dcx().emit_fatal(errors::MissingNativeLibrary::new(name, verbatim))) } fn find_bundled_library( From 05bd36de5068c8c34a404594926af268e5f4cb13 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Thu, 22 Aug 2024 01:56:20 +0300 Subject: [PATCH 04/13] linker: Better support alternative static library naming on MSVC Previously `libname.a` naming was supported as a fallback when producing rlibs, but not when producing executables or dynamic libraries --- compiler/rustc_codegen_ssa/src/back/linker.rs | 14 ++++++++++---- tests/run-make/native-lib-alt-naming/native.rs | 2 ++ tests/run-make/native-lib-alt-naming/rmake.rs | 15 +++++++++++++++ tests/run-make/native-lib-alt-naming/rust.rs | 1 + 4 files changed, 28 insertions(+), 4 deletions(-) create mode 100644 tests/run-make/native-lib-alt-naming/native.rs create mode 100644 tests/run-make/native-lib-alt-naming/rmake.rs create mode 100644 tests/run-make/native-lib-alt-naming/rust.rs diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index fbab988a32b08..cb266247e0dde 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -7,7 +7,7 @@ use std::{env, iter, mem, str}; use cc::windows_registry; use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; -use rustc_metadata::find_native_static_library; +use rustc_metadata::{find_native_static_library, try_find_native_static_library}; use rustc_middle::bug; use rustc_middle::middle::dependency_format::Linkage; use rustc_middle::middle::exported_symbols; @@ -891,9 +891,15 @@ impl<'a> Linker for MsvcLinker<'a> { } fn link_staticlib_by_name(&mut self, name: &str, verbatim: bool, whole_archive: bool) { - let prefix = if whole_archive { "/WHOLEARCHIVE:" } else { "" }; - let suffix = if verbatim { "" } else { ".lib" }; - self.link_arg(format!("{prefix}{name}{suffix}")); + // On MSVC-like targets rustc supports static libraries using alternative naming + // scheme (`libfoo.a`) unsupported by linker, search for such libraries manually. + if let Some(path) = try_find_native_static_library(self.sess, name, verbatim) { + self.link_staticlib_by_path(&path, whole_archive); + } else { + let prefix = if whole_archive { "/WHOLEARCHIVE:" } else { "" }; + let suffix = if verbatim { "" } else { ".lib" }; + self.link_arg(format!("{prefix}{name}{suffix}")); + } } fn link_staticlib_by_path(&mut self, path: &Path, whole_archive: bool) { diff --git a/tests/run-make/native-lib-alt-naming/native.rs b/tests/run-make/native-lib-alt-naming/native.rs new file mode 100644 index 0000000000000..6c869e74cd269 --- /dev/null +++ b/tests/run-make/native-lib-alt-naming/native.rs @@ -0,0 +1,2 @@ +#[no_mangle] +pub extern "C" fn native_lib_alt_naming() {} diff --git a/tests/run-make/native-lib-alt-naming/rmake.rs b/tests/run-make/native-lib-alt-naming/rmake.rs new file mode 100644 index 0000000000000..d1ea0fc868767 --- /dev/null +++ b/tests/run-make/native-lib-alt-naming/rmake.rs @@ -0,0 +1,15 @@ +// On MSVC the alternative naming format for static libraries (`libfoo.a`) is accepted in addition +// to the default format (`foo.lib`). + +//REMOVE@ only-msvc + +use run_make_support::rustc; + +fn main() { + // Prepare the native library. + rustc().input("native.rs").crate_type("staticlib").output("libnative.a").run(); + + // Try to link to it from both a rlib and a bin. + rustc().input("rust.rs").crate_type("rlib").arg("-lstatic=native").run(); + rustc().input("rust.rs").crate_type("bin").arg("-lstatic=native").run(); +} diff --git a/tests/run-make/native-lib-alt-naming/rust.rs b/tests/run-make/native-lib-alt-naming/rust.rs new file mode 100644 index 0000000000000..da0f5d925d107 --- /dev/null +++ b/tests/run-make/native-lib-alt-naming/rust.rs @@ -0,0 +1 @@ +pub fn main() {} From ac8f1320143baff8f2471985d77270f99ac42d0b Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Tue, 27 Aug 2024 22:12:18 +0300 Subject: [PATCH 05/13] docs: Update docs for the rustc's `-L` option --- src/doc/rustc/src/command-line-arguments.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc/src/command-line-arguments.md b/src/doc/rustc/src/command-line-arguments.md index fa23e3e414d90..e5631ba42741a 100644 --- a/src/doc/rustc/src/command-line-arguments.md +++ b/src/doc/rustc/src/command-line-arguments.md @@ -47,7 +47,7 @@ KIND=PATH` where `KIND` may be one of: directory. - `native` — Only search for native libraries in this directory. - `framework` — Only search for macOS frameworks in this directory. -- `all` — Search for all library kinds in this directory. This is the default +- `all` — Search for all library kinds in this directory, except frameworks. This is the default if `KIND` is not specified. From 6c54ab015f5d00c3941dd457bc79e1511aa5f3d7 Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Thu, 22 Aug 2024 01:28:20 -0700 Subject: [PATCH 06/13] Ban non-array SIMD --- .../src/error_codes/E0074.md | 4 +- .../src/error_codes/E0075.md | 18 +- .../src/error_codes/E0076.md | 10 +- .../src/error_codes/E0077.md | 4 +- .../src/error_codes/E0511.md | 4 +- .../rustc_hir_analysis/src/check/check.rs | 42 ++-- compiler/rustc_middle/src/ty/sty.rs | 38 ++-- library/alloc/benches/slice.rs | 4 +- tests/assembly/asm/aarch64-types.rs | 31 +-- tests/assembly/asm/arm-modifiers.rs | 5 +- tests/assembly/asm/arm-types.rs | 27 ++- tests/assembly/asm/x86-types.rs | 209 ++---------------- tests/codegen/align-byval-vector.rs | 8 +- tests/codegen/const-vector.rs | 36 +-- tests/codegen/repr/transparent.rs | 2 +- .../simd-intrinsic-float-abs.rs | 19 +- .../simd-intrinsic-float-ceil.rs | 19 +- .../simd-intrinsic-float-cos.rs | 19 +- .../simd-intrinsic-float-exp.rs | 19 +- .../simd-intrinsic-float-exp2.rs | 19 +- .../simd-intrinsic-float-floor.rs | 19 +- .../simd-intrinsic-float-fma.rs | 19 +- .../simd-intrinsic-float-fsqrt.rs | 19 +- .../simd-intrinsic-float-log.rs | 19 +- .../simd-intrinsic-float-log10.rs | 19 +- .../simd-intrinsic-float-log2.rs | 19 +- .../simd-intrinsic-float-minmax.rs | 2 +- .../simd-intrinsic-float-pow.rs | 19 +- .../simd-intrinsic-float-powi.rs | 19 +- .../simd-intrinsic-float-sin.rs | 19 +- ...intrinsic-generic-arithmetic-saturating.rs | 146 ++++-------- .../simd-intrinsic-generic-bitmask.rs | 10 +- .../simd-intrinsic-generic-gather.rs | 4 +- .../simd-intrinsic-generic-masked-load.rs | 4 +- .../simd-intrinsic-generic-masked-store.rs | 4 +- .../simd-intrinsic-generic-scatter.rs | 4 +- .../simd-intrinsic-generic-select.rs | 6 +- .../simd-intrinsic-transmute-array.rs | 25 +-- tests/codegen/simd/unpadded-simd.rs | 2 +- tests/codegen/union-abi.rs | 2 +- tests/codegen/zst-offset.rs | 2 +- tests/debuginfo/simd.rs | 64 +++--- tests/incremental/issue-61530.rs | 6 +- tests/run-make/simd-ffi/simd.rs | 6 +- tests/rustdoc/inline_cross/auxiliary/repr.rs | 2 +- tests/ui/abi/arm-unadjusted-intrinsic.rs | 9 +- .../homogenous-floats-target-feature-mixup.rs | 30 +-- tests/ui/asm/aarch64/type-check-2-2.rs | 4 +- tests/ui/asm/aarch64/type-check-2.rs | 8 +- tests/ui/asm/aarch64/type-check-2.stderr | 4 +- tests/ui/asm/aarch64/type-check-3.rs | 4 +- tests/ui/asm/aarch64/type-check-4.rs | 2 +- tests/ui/asm/x86_64/type-check-2.rs | 4 +- tests/ui/asm/x86_64/type-check-2.stderr | 4 +- tests/ui/asm/x86_64/type-check-5.rs | 5 +- tests/ui/asm/x86_64/type-check-5.stderr | 6 +- .../consts/const-eval/simd/insert_extract.rs | 23 +- tests/ui/error-codes/E0075.rs | 3 + tests/ui/error-codes/E0075.stderr | 8 +- tests/ui/error-codes/E0076.rs | 2 +- tests/ui/error-codes/E0076.stderr | 6 +- tests/ui/error-codes/E0077.rs | 2 +- tests/ui/error-codes/E0077.stderr | 2 +- .../feature-gates/feature-gate-repr-simd.rs | 4 +- .../ui/feature-gates/feature-gate-simd-ffi.rs | 2 +- tests/ui/feature-gates/feature-gate-simd.rs | 5 +- tests/ui/layout/debug.rs | 2 +- tests/ui/repr/attr-usage-repr.rs | 2 +- tests/ui/repr/explicit-rust-repr-conflicts.rs | 2 +- tests/ui/simd/const-err-trumps-simd-err.rs | 4 +- .../ui/simd/const-err-trumps-simd-err.stderr | 4 +- tests/ui/simd/generics.rs | 6 +- tests/ui/simd/intrinsic/float-math-pass.rs | 18 +- tests/ui/simd/intrinsic/float-minmax-pass.rs | 12 +- .../ui/simd/intrinsic/generic-arithmetic-2.rs | 12 +- .../simd/intrinsic/generic-arithmetic-pass.rs | 168 +++++++------- .../generic-arithmetic-saturating-2.rs | 12 +- .../generic-arithmetic-saturating-pass.rs | 12 +- .../ui/simd/intrinsic/generic-bitmask-pass.rs | 16 +- tests/ui/simd/intrinsic/generic-cast.rs | 12 +- tests/ui/simd/intrinsic/generic-cast.stderr | 8 +- .../simd/intrinsic/generic-comparison-pass.rs | 34 +-- tests/ui/simd/intrinsic/generic-comparison.rs | 7 +- .../simd/intrinsic/generic-comparison.stderr | 36 +-- .../simd/intrinsic/generic-elements-pass.rs | 65 +++--- tests/ui/simd/intrinsic/generic-elements.rs | 14 +- .../ui/simd/intrinsic/generic-gather-pass.rs | 40 ++-- .../simd/intrinsic/generic-reduction-pass.rs | 28 +-- tests/ui/simd/intrinsic/generic-reduction.rs | 8 +- .../ui/simd/intrinsic/generic-select-pass.rs | 80 +++---- tests/ui/simd/intrinsic/generic-select.rs | 16 +- .../simd/intrinsic/inlining-issue67557-ice.rs | 4 +- .../ui/simd/intrinsic/inlining-issue67557.rs | 10 +- tests/ui/simd/issue-17170.rs | 4 +- tests/ui/simd/issue-32947.rs | 4 +- tests/ui/simd/issue-39720.rs | 6 +- tests/ui/simd/issue-89193.rs | 20 +- tests/ui/simd/monomorphize-heterogeneous.rs | 6 +- .../ui/simd/monomorphize-heterogeneous.stderr | 15 +- tests/ui/simd/monomorphize-too-long.rs | 11 + tests/ui/simd/monomorphize-too-long.stderr | 4 + tests/ui/simd/monomorphize-zero-length.rs | 11 + tests/ui/simd/monomorphize-zero-length.stderr | 4 + tests/ui/simd/target-feature-mixup.rs | 30 +-- ...ric-monomorphisation-extern-nonnull-ptr.rs | 4 +- .../type-generic-monomorphisation-wide-ptr.rs | 6 +- ...e-generic-monomorphisation-wide-ptr.stderr | 2 +- .../ui/simd/type-generic-monomorphisation.rs | 4 +- tests/ui/simd/type-len.rs | 9 +- tests/ui/simd/type-len.stderr | 30 ++- tests/ui/span/gated-features-attr-spans.rs | 3 +- 111 files changed, 826 insertions(+), 1113 deletions(-) create mode 100644 tests/ui/simd/monomorphize-too-long.rs create mode 100644 tests/ui/simd/monomorphize-too-long.stderr create mode 100644 tests/ui/simd/monomorphize-zero-length.rs create mode 100644 tests/ui/simd/monomorphize-zero-length.stderr diff --git a/compiler/rustc_error_codes/src/error_codes/E0074.md b/compiler/rustc_error_codes/src/error_codes/E0074.md index 785d6de226d3d..71d89f5eb60a4 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0074.md +++ b/compiler/rustc_error_codes/src/error_codes/E0074.md @@ -11,7 +11,7 @@ This will cause an error: #![feature(repr_simd)] #[repr(simd)] -struct Bad(T, T, T, T); +struct Bad([T; 4]); ``` This will not: @@ -20,5 +20,5 @@ This will not: #![feature(repr_simd)] #[repr(simd)] -struct Good(u32, u32, u32, u32); +struct Good([u32; 4]); ``` diff --git a/compiler/rustc_error_codes/src/error_codes/E0075.md b/compiler/rustc_error_codes/src/error_codes/E0075.md index 969c1ee71313e..b58018eafc30c 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0075.md +++ b/compiler/rustc_error_codes/src/error_codes/E0075.md @@ -1,6 +1,6 @@ -A `#[simd]` attribute was applied to an empty tuple struct. +A `#[simd]` attribute was applied to an empty or multi-field struct. -Erroneous code example: +Erroneous code examples: ```compile_fail,E0075 #![feature(repr_simd)] @@ -9,9 +9,15 @@ Erroneous code example: struct Bad; // error! ``` -The `#[simd]` attribute can only be applied to non empty tuple structs, because -it doesn't make sense to try to use SIMD operations when there are no values to -operate on. +```compile_fail,E0075 +#![feature(repr_simd)] + +#[repr(simd)] +struct Bad([u32; 1], [u32; 1]); // error! +``` + +The `#[simd]` attribute can only be applied to a single-field struct, because +the one field must be the array of values in the vector. Fixed example: @@ -19,5 +25,5 @@ Fixed example: #![feature(repr_simd)] #[repr(simd)] -struct Good(u32); // ok! +struct Good([u32; 2]); // ok! ``` diff --git a/compiler/rustc_error_codes/src/error_codes/E0076.md b/compiler/rustc_error_codes/src/error_codes/E0076.md index 1da8caa9506d7..b1943de63e5d4 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0076.md +++ b/compiler/rustc_error_codes/src/error_codes/E0076.md @@ -1,4 +1,4 @@ -All types in a tuple struct aren't the same when using the `#[simd]` +The type of the field in a tuple struct isn't an array when using the `#[simd]` attribute. Erroneous code example: @@ -7,12 +7,12 @@ Erroneous code example: #![feature(repr_simd)] #[repr(simd)] -struct Bad(u16, u32, u32 u32); // error! +struct Bad(u16); // error! ``` When using the `#[simd]` attribute to automatically use SIMD operations in tuple -struct, the types in the struct must all be of the same type, or the compiler -will trigger this error. +structs, if you want a single-lane vector then the field must be a 1-element +array, or the compiler will trigger this error. Fixed example: @@ -20,5 +20,5 @@ Fixed example: #![feature(repr_simd)] #[repr(simd)] -struct Good(u32, u32, u32, u32); // ok! +struct Good([u16; 1]); // ok! ``` diff --git a/compiler/rustc_error_codes/src/error_codes/E0077.md b/compiler/rustc_error_codes/src/error_codes/E0077.md index 91aa24d1f52f4..688bfd3e72795 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0077.md +++ b/compiler/rustc_error_codes/src/error_codes/E0077.md @@ -7,7 +7,7 @@ Erroneous code example: #![feature(repr_simd)] #[repr(simd)] -struct Bad(String); // error! +struct Bad([String; 2]); // error! ``` When using the `#[simd]` attribute on a tuple struct, the elements in the tuple @@ -19,5 +19,5 @@ Fixed example: #![feature(repr_simd)] #[repr(simd)] -struct Good(u32, u32, u32, u32); // ok! +struct Good([u32; 4]); // ok! ``` diff --git a/compiler/rustc_error_codes/src/error_codes/E0511.md b/compiler/rustc_error_codes/src/error_codes/E0511.md index 681f4e611c32d..45ff49bdebb29 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0511.md +++ b/compiler/rustc_error_codes/src/error_codes/E0511.md @@ -23,11 +23,11 @@ The generic type has to be a SIMD type. Example: #[repr(simd)] #[derive(Copy, Clone)] -struct i32x2(i32, i32); +struct i32x2([i32; 2]); extern "rust-intrinsic" { fn simd_add(a: T, b: T) -> T; } -unsafe { simd_add(i32x2(0, 0), i32x2(1, 2)); } // ok! +unsafe { simd_add(i32x2([0, 0]), i32x2([1, 2])); } // ok! ``` diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index d414bcdb95b34..01a8512c985fc 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -1063,20 +1063,29 @@ fn check_simd(tcx: TyCtxt<'_>, sp: Span, def_id: LocalDefId) { struct_span_code_err!(tcx.dcx(), sp, E0075, "SIMD vector cannot be empty").emit(); return; } - let e = fields[FieldIdx::ZERO].ty(tcx, args); - if !fields.iter().all(|f| f.ty(tcx, args) == e) { - struct_span_code_err!(tcx.dcx(), sp, E0076, "SIMD vector should be homogeneous") - .with_span_label(sp, "SIMD elements must have the same type") + + let array_field = &fields[FieldIdx::ZERO]; + let array_ty = array_field.ty(tcx, args); + let ty::Array(element_ty, len_const) = array_ty.kind() else { + struct_span_code_err!( + tcx.dcx(), + sp, + E0076, + "SIMD vector's only field must be an array" + ) + .with_span_label(tcx.def_span(array_field.did), "not an array") + .emit(); + return; + }; + + if let Some(second_field) = fields.get(FieldIdx::from_u32(1)) { + struct_span_code_err!(tcx.dcx(), sp, E0075, "SIMD vector cannot have multiple fields") + .with_span_label(tcx.def_span(second_field.did), "excess field") .emit(); return; } - let len = if let ty::Array(_ty, c) = e.kind() { - c.try_eval_target_usize(tcx, tcx.param_env(def.did())) - } else { - Some(fields.len() as u64) - }; - if let Some(len) = len { + if let Some(len) = len_const.try_eval_target_usize(tcx, tcx.param_env(def.did())) { if len == 0 { struct_span_code_err!(tcx.dcx(), sp, E0075, "SIMD vector cannot be empty").emit(); return; @@ -1096,16 +1105,9 @@ fn check_simd(tcx: TyCtxt<'_>, sp: Span, def_id: LocalDefId) { // These are scalar types which directly match a "machine" type // Yes: Integers, floats, "thin" pointers // No: char, "fat" pointers, compound types - match e.kind() { - ty::Param(_) => (), // pass struct(T, T, T, T) through, let monomorphization catch errors - ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::RawPtr(_, _) => (), // struct(u8, u8, u8, u8) is ok - ty::Array(t, _) if matches!(t.kind(), ty::Param(_)) => (), // pass struct([T; N]) through, let monomorphization catch errors - ty::Array(t, _clen) - if matches!( - t.kind(), - ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::RawPtr(_, _) - ) => - { /* struct([f32; 4]) is ok */ } + match element_ty.kind() { + ty::Param(_) => (), // pass struct([T; 4]) through, let monomorphization catch errors + ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::RawPtr(_, _) => (), // struct([u8; 4]) is ok _ => { struct_span_code_err!( tcx.dcx(), diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index d60bfb9faa1aa..8af10247540ef 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -1091,29 +1091,21 @@ impl<'tcx> Ty<'tcx> { } pub fn simd_size_and_type(self, tcx: TyCtxt<'tcx>) -> (u64, Ty<'tcx>) { - match self.kind() { - Adt(def, args) => { - assert!(def.repr().simd(), "`simd_size_and_type` called on non-SIMD type"); - let variant = def.non_enum_variant(); - let f0_ty = variant.fields[FieldIdx::ZERO].ty(tcx, args); - - match f0_ty.kind() { - // If the first field is an array, we assume it is the only field and its - // elements are the SIMD components. - Array(f0_elem_ty, f0_len) => { - // FIXME(repr_simd): https://github.com/rust-lang/rust/pull/78863#discussion_r522784112 - // The way we evaluate the `N` in `[T; N]` here only works since we use - // `simd_size_and_type` post-monomorphization. It will probably start to ICE - // if we use it in generic code. See the `simd-array-trait` ui test. - (f0_len.eval_target_usize(tcx, ParamEnv::empty()), *f0_elem_ty) - } - // Otherwise, the fields of this Adt are the SIMD components (and we assume they - // all have the same type). - _ => (variant.fields.len() as u64, f0_ty), - } - } - _ => bug!("`simd_size_and_type` called on invalid type"), - } + let Adt(def, args) = self.kind() else { + bug!("`simd_size_and_type` called on invalid type") + }; + assert!(def.repr().simd(), "`simd_size_and_type` called on non-SIMD type"); + let variant = def.non_enum_variant(); + assert_eq!(variant.fields.len(), 1); + let field_ty = variant.fields[FieldIdx::ZERO].ty(tcx, args); + let Array(f0_elem_ty, f0_len) = field_ty.kind() else { + bug!("Simd type has non-array field type {field_ty:?}") + }; + // FIXME(repr_simd): https://github.com/rust-lang/rust/pull/78863#discussion_r522784112 + // The way we evaluate the `N` in `[T; N]` here only works since we use + // `simd_size_and_type` post-monomorphization. It will probably start to ICE + // if we use it in generic code. See the `simd-array-trait` ui test. + (f0_len.eval_target_usize(tcx, ParamEnv::empty()), *f0_elem_ty) } #[inline] diff --git a/library/alloc/benches/slice.rs b/library/alloc/benches/slice.rs index b62be9d39a1cd..ab46603d7739c 100644 --- a/library/alloc/benches/slice.rs +++ b/library/alloc/benches/slice.rs @@ -336,10 +336,10 @@ reverse!(reverse_u32, u32, |x| x as u32); reverse!(reverse_u64, u64, |x| x as u64); reverse!(reverse_u128, u128, |x| x as u128); #[repr(simd)] -struct F64x4(f64, f64, f64, f64); +struct F64x4([f64; 4]); reverse!(reverse_simd_f64x4, F64x4, |x| { let x = x as f64; - F64x4(x, x, x, x) + F64x4([x, x, x, x]) }); macro_rules! rotate { diff --git a/tests/assembly/asm/aarch64-types.rs b/tests/assembly/asm/aarch64-types.rs index f36345670e329..6d55e29fb578e 100644 --- a/tests/assembly/asm/aarch64-types.rs +++ b/tests/assembly/asm/aarch64-types.rs @@ -30,36 +30,39 @@ trait Sized {} #[lang = "copy"] trait Copy {} +// Do we really need to use no_core for this?!? +impl Copy for [T; N] {} + type ptr = *mut u8; #[repr(simd)] -pub struct i8x8(i8, i8, i8, i8, i8, i8, i8, i8); +pub struct i8x8([i8; 8]); #[repr(simd)] -pub struct i16x4(i16, i16, i16, i16); +pub struct i16x4([i16; 4]); #[repr(simd)] -pub struct i32x2(i32, i32); +pub struct i32x2([i32; 2]); #[repr(simd)] -pub struct i64x1(i64); +pub struct i64x1([i64; 1]); #[repr(simd)] -pub struct f16x4(f16, f16, f16, f16); +pub struct f16x4([f16; 4]); #[repr(simd)] -pub struct f32x2(f32, f32); +pub struct f32x2([f32; 2]); #[repr(simd)] -pub struct f64x1(f64); +pub struct f64x1([f64; 1]); #[repr(simd)] -pub struct i8x16(i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8); +pub struct i8x16([i8; 16]); #[repr(simd)] -pub struct i16x8(i16, i16, i16, i16, i16, i16, i16, i16); +pub struct i16x8([i16; 8]); #[repr(simd)] -pub struct i32x4(i32, i32, i32, i32); +pub struct i32x4([i32; 4]); #[repr(simd)] -pub struct i64x2(i64, i64); +pub struct i64x2([i64; 2]); #[repr(simd)] -pub struct f16x8(f16, f16, f16, f16, f16, f16, f16, f16); +pub struct f16x8([f16; 8]); #[repr(simd)] -pub struct f32x4(f32, f32, f32, f32); +pub struct f32x4([f32; 4]); #[repr(simd)] -pub struct f64x2(f64, f64); +pub struct f64x2([f64; 2]); impl Copy for i8 {} impl Copy for i16 {} diff --git a/tests/assembly/asm/arm-modifiers.rs b/tests/assembly/asm/arm-modifiers.rs index 0674e169d72e6..91d5b3f87f5c1 100644 --- a/tests/assembly/asm/arm-modifiers.rs +++ b/tests/assembly/asm/arm-modifiers.rs @@ -27,8 +27,11 @@ trait Sized {} #[lang = "copy"] trait Copy {} +// Do we really need to use no_core for this?!? +impl Copy for [T; N] {} + #[repr(simd)] -pub struct f32x4(f32, f32, f32, f32); +pub struct f32x4([f32; 4]); impl Copy for i32 {} impl Copy for f32 {} diff --git a/tests/assembly/asm/arm-types.rs b/tests/assembly/asm/arm-types.rs index eeff1a070b492..fb9bb0eba4760 100644 --- a/tests/assembly/asm/arm-types.rs +++ b/tests/assembly/asm/arm-types.rs @@ -30,32 +30,35 @@ trait Sized {} #[lang = "copy"] trait Copy {} +// Do we really need to use no_core for this?!? +impl Copy for [T; N] {} + type ptr = *mut u8; #[repr(simd)] -pub struct i8x8(i8, i8, i8, i8, i8, i8, i8, i8); +pub struct i8x8([i8; 8]); #[repr(simd)] -pub struct i16x4(i16, i16, i16, i16); +pub struct i16x4([i16; 4]); #[repr(simd)] -pub struct i32x2(i32, i32); +pub struct i32x2([i32; 2]); #[repr(simd)] -pub struct i64x1(i64); +pub struct i64x1([i64; 1]); #[repr(simd)] -pub struct f16x4(f16, f16, f16, f16); +pub struct f16x4([f16; 4]); #[repr(simd)] -pub struct f32x2(f32, f32); +pub struct f32x2([f32; 2]); #[repr(simd)] -pub struct i8x16(i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8); +pub struct i8x16([i8; 16]); #[repr(simd)] -pub struct i16x8(i16, i16, i16, i16, i16, i16, i16, i16); +pub struct i16x8([i16; 8]); #[repr(simd)] -pub struct i32x4(i32, i32, i32, i32); +pub struct i32x4([i32; 4]); #[repr(simd)] -pub struct i64x2(i64, i64); +pub struct i64x2([i64; 2]); #[repr(simd)] -pub struct f16x8(f16, f16, f16, f16, f16, f16, f16, f16); +pub struct f16x8([f16; 8]); #[repr(simd)] -pub struct f32x4(f32, f32, f32, f32); +pub struct f32x4([f32; 4]); impl Copy for i8 {} impl Copy for i16 {} diff --git a/tests/assembly/asm/x86-types.rs b/tests/assembly/asm/x86-types.rs index 8e229614420b6..5107c8b80a5b3 100644 --- a/tests/assembly/asm/x86-types.rs +++ b/tests/assembly/asm/x86-types.rs @@ -30,216 +30,55 @@ trait Sized {} #[lang = "copy"] trait Copy {} +// Do we really need to use no_core for this?!? +impl Copy for [T; N] {} + type ptr = *mut u8; #[repr(simd)] -pub struct i8x16(i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8); +pub struct i8x16([i8; 16]); #[repr(simd)] -pub struct i16x8(i16, i16, i16, i16, i16, i16, i16, i16); +pub struct i16x8([i16; 8]); #[repr(simd)] -pub struct i32x4(i32, i32, i32, i32); +pub struct i32x4([i32; 4]); #[repr(simd)] -pub struct i64x2(i64, i64); +pub struct i64x2([i64; 2]); #[repr(simd)] -pub struct f16x8(f16, f16, f16, f16, f16, f16, f16, f16); +pub struct f16x8([f16; 8]); #[repr(simd)] -pub struct f32x4(f32, f32, f32, f32); +pub struct f32x4([f32; 4]); #[repr(simd)] -pub struct f64x2(f64, f64); +pub struct f64x2([f64; 2]); #[repr(simd)] -pub struct i8x32( - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, -); +pub struct i8x32([i8; 32]); #[repr(simd)] -pub struct i16x16(i16, i16, i16, i16, i16, i16, i16, i16, i16, i16, i16, i16, i16, i16, i16, i16); +pub struct i16x16([i16; 16]); #[repr(simd)] -pub struct i32x8(i32, i32, i32, i32, i32, i32, i32, i32); +pub struct i32x8([i32; 8]); #[repr(simd)] -pub struct i64x4(i64, i64, i64, i64); +pub struct i64x4([i64; 4]); #[repr(simd)] -pub struct f16x16(f16, f16, f16, f16, f16, f16, f16, f16, f16, f16, f16, f16, f16, f16, f16, f16); +pub struct f16x16([f16; 16]); #[repr(simd)] -pub struct f32x8(f32, f32, f32, f32, f32, f32, f32, f32); +pub struct f32x8([f32; 8]); #[repr(simd)] -pub struct f64x4(f64, f64, f64, f64); +pub struct f64x4([f64; 4]); #[repr(simd)] -pub struct i8x64( - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, - i8, -); +pub struct i8x64([i8; 64]); #[repr(simd)] -pub struct i16x32( - i16, - i16, - i16, - i16, - i16, - i16, - i16, - i16, - i16, - i16, - i16, - i16, - i16, - i16, - i16, - i16, - i16, - i16, - i16, - i16, - i16, - i16, - i16, - i16, - i16, - i16, - i16, - i16, - i16, - i16, - i16, - i16, -); +pub struct i16x32([i16; 32]); #[repr(simd)] -pub struct i32x16(i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32); +pub struct i32x16([i32; 16]); #[repr(simd)] -pub struct i64x8(i64, i64, i64, i64, i64, i64, i64, i64); +pub struct i64x8([i64; 8]); #[repr(simd)] -pub struct f16x32( - f16, - f16, - f16, - f16, - f16, - f16, - f16, - f16, - f16, - f16, - f16, - f16, - f16, - f16, - f16, - f16, - f16, - f16, - f16, - f16, - f16, - f16, - f16, - f16, - f16, - f16, - f16, - f16, - f16, - f16, - f16, - f16, -); +pub struct f16x32([f16; 32]); #[repr(simd)] -pub struct f32x16(f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32); +pub struct f32x16([f32; 16]); #[repr(simd)] -pub struct f64x8(f64, f64, f64, f64, f64, f64, f64, f64); +pub struct f64x8([f64; 8]); macro_rules! impl_copy { ($($ty:ident)*) => { diff --git a/tests/codegen/align-byval-vector.rs b/tests/codegen/align-byval-vector.rs index 02b7d6b0c5e28..60d49f9308197 100644 --- a/tests/codegen/align-byval-vector.rs +++ b/tests/codegen/align-byval-vector.rs @@ -21,7 +21,7 @@ trait Freeze {} trait Copy {} #[repr(simd)] -pub struct i32x4(i32, i32, i32, i32); +pub struct i32x4([i32; 4]); #[repr(C)] pub struct Foo { @@ -47,12 +47,12 @@ extern "C" { } pub fn main() { - unsafe { f(Foo { a: i32x4(1, 2, 3, 4), b: 0 }) } + unsafe { f(Foo { a: i32x4([1, 2, 3, 4]), b: 0 }) } unsafe { g(DoubleFoo { - one: Foo { a: i32x4(1, 2, 3, 4), b: 0 }, - two: Foo { a: i32x4(1, 2, 3, 4), b: 0 }, + one: Foo { a: i32x4([1, 2, 3, 4]), b: 0 }, + two: Foo { a: i32x4([1, 2, 3, 4]), b: 0 }, }) } } diff --git a/tests/codegen/const-vector.rs b/tests/codegen/const-vector.rs index d368838201e2b..8343594e5d23c 100644 --- a/tests/codegen/const-vector.rs +++ b/tests/codegen/const-vector.rs @@ -13,19 +13,11 @@ // Setting up structs that can be used as const vectors #[repr(simd)] #[derive(Clone)] -pub struct i8x2(i8, i8); +pub struct i8x2([i8; 2]); #[repr(simd)] #[derive(Clone)] -pub struct i8x2_arr([i8; 2]); - -#[repr(simd)] -#[derive(Clone)] -pub struct f32x2(f32, f32); - -#[repr(simd)] -#[derive(Clone)] -pub struct f32x2_arr([f32; 2]); +pub struct f32x2([f32; 2]); #[repr(simd, packed)] #[derive(Copy, Clone)] @@ -35,42 +27,34 @@ pub struct Simd([T; N]); // that they are called with a const vector extern "unadjusted" { - #[no_mangle] fn test_i8x2(a: i8x2); } extern "unadjusted" { - #[no_mangle] fn test_i8x2_two_args(a: i8x2, b: i8x2); } extern "unadjusted" { - #[no_mangle] fn test_i8x2_mixed_args(a: i8x2, c: i32, b: i8x2); } extern "unadjusted" { - #[no_mangle] - fn test_i8x2_arr(a: i8x2_arr); + fn test_i8x2_arr(a: i8x2); } extern "unadjusted" { - #[no_mangle] fn test_f32x2(a: f32x2); } extern "unadjusted" { - #[no_mangle] - fn test_f32x2_arr(a: f32x2_arr); + fn test_f32x2_arr(a: f32x2); } extern "unadjusted" { - #[no_mangle] fn test_simd(a: Simd); } extern "unadjusted" { - #[no_mangle] fn test_simd_unaligned(a: Simd); } @@ -81,22 +65,22 @@ extern "unadjusted" { pub fn do_call() { unsafe { // CHECK: call void @test_i8x2(<2 x i8> - test_i8x2(const { i8x2(32, 64) }); + test_i8x2(const { i8x2([32, 64]) }); // CHECK: call void @test_i8x2_two_args(<2 x i8> , <2 x i8> - test_i8x2_two_args(const { i8x2(32, 64) }, const { i8x2(8, 16) }); + test_i8x2_two_args(const { i8x2([32, 64]) }, const { i8x2([8, 16]) }); // CHECK: call void @test_i8x2_mixed_args(<2 x i8> , i32 43, <2 x i8> - test_i8x2_mixed_args(const { i8x2(32, 64) }, 43, const { i8x2(8, 16) }); + test_i8x2_mixed_args(const { i8x2([32, 64]) }, 43, const { i8x2([8, 16]) }); // CHECK: call void @test_i8x2_arr(<2 x i8> - test_i8x2_arr(const { i8x2_arr([32, 64]) }); + test_i8x2_arr(const { i8x2([32, 64]) }); // CHECK: call void @test_f32x2(<2 x float> - test_f32x2(const { f32x2(0.32, 0.64) }); + test_f32x2(const { f32x2([0.32, 0.64]) }); // CHECK: void @test_f32x2_arr(<2 x float> - test_f32x2_arr(const { f32x2_arr([0.32, 0.64]) }); + test_f32x2_arr(const { f32x2([0.32, 0.64]) }); // CHECK: call void @test_simd(<4 x i32> test_simd(const { Simd::([2, 4, 6, 8]) }); diff --git a/tests/codegen/repr/transparent.rs b/tests/codegen/repr/transparent.rs index 9140b8542ecac..adcd3aacd2ace 100644 --- a/tests/codegen/repr/transparent.rs +++ b/tests/codegen/repr/transparent.rs @@ -132,7 +132,7 @@ pub extern "C" fn test_Nested2(_: Nested2) -> Nested2 { } #[repr(simd)] -struct f32x4(f32, f32, f32, f32); +struct f32x4([f32; 4]); #[repr(transparent)] pub struct Vector(f32x4); diff --git a/tests/codegen/simd-intrinsic/simd-intrinsic-float-abs.rs b/tests/codegen/simd-intrinsic/simd-intrinsic-float-abs.rs index f8efb678f76ef..4a5a6391c052f 100644 --- a/tests/codegen/simd-intrinsic/simd-intrinsic-float-abs.rs +++ b/tests/codegen/simd-intrinsic/simd-intrinsic-float-abs.rs @@ -7,23 +7,19 @@ #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f32x2(pub f32, pub f32); +pub struct f32x2(pub [f32; 2]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f32x4(pub f32, pub f32, pub f32, pub f32); +pub struct f32x4(pub [f32; 4]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f32x8(pub f32, pub f32, pub f32, pub f32, - pub f32, pub f32, pub f32, pub f32); +pub struct f32x8(pub [f32; 8]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f32x16(pub f32, pub f32, pub f32, pub f32, - pub f32, pub f32, pub f32, pub f32, - pub f32, pub f32, pub f32, pub f32, - pub f32, pub f32, pub f32, pub f32); +pub struct f32x16(pub [f32; 16]); extern "rust-intrinsic" { fn simd_fabs(x: T) -> T; @@ -59,16 +55,15 @@ pub unsafe fn fabs_32x16(a: f32x16) -> f32x16 { #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f64x2(pub f64, pub f64); +pub struct f64x2(pub [f64; 2]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f64x4(pub f64, pub f64, pub f64, pub f64); +pub struct f64x4(pub [f64; 4]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f64x8(pub f64, pub f64, pub f64, pub f64, - pub f64, pub f64, pub f64, pub f64); +pub struct f64x8(pub [f64; 8]); // CHECK-LABEL: @fabs_64x4 #[no_mangle] diff --git a/tests/codegen/simd-intrinsic/simd-intrinsic-float-ceil.rs b/tests/codegen/simd-intrinsic/simd-intrinsic-float-ceil.rs index a3ebec174b679..89e54f579ff7c 100644 --- a/tests/codegen/simd-intrinsic/simd-intrinsic-float-ceil.rs +++ b/tests/codegen/simd-intrinsic/simd-intrinsic-float-ceil.rs @@ -7,23 +7,19 @@ #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f32x2(pub f32, pub f32); +pub struct f32x2(pub [f32; 2]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f32x4(pub f32, pub f32, pub f32, pub f32); +pub struct f32x4(pub [f32; 4]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f32x8(pub f32, pub f32, pub f32, pub f32, - pub f32, pub f32, pub f32, pub f32); +pub struct f32x8(pub [f32; 8]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f32x16(pub f32, pub f32, pub f32, pub f32, - pub f32, pub f32, pub f32, pub f32, - pub f32, pub f32, pub f32, pub f32, - pub f32, pub f32, pub f32, pub f32); +pub struct f32x16(pub [f32; 16]); extern "rust-intrinsic" { fn simd_ceil(x: T) -> T; @@ -59,16 +55,15 @@ pub unsafe fn ceil_32x16(a: f32x16) -> f32x16 { #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f64x2(pub f64, pub f64); +pub struct f64x2(pub [f64; 2]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f64x4(pub f64, pub f64, pub f64, pub f64); +pub struct f64x4(pub [f64; 4]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f64x8(pub f64, pub f64, pub f64, pub f64, - pub f64, pub f64, pub f64, pub f64); +pub struct f64x8(pub [f64; 8]); // CHECK-LABEL: @ceil_64x4 #[no_mangle] diff --git a/tests/codegen/simd-intrinsic/simd-intrinsic-float-cos.rs b/tests/codegen/simd-intrinsic/simd-intrinsic-float-cos.rs index 00f97eef6f0cb..b40fd5365de57 100644 --- a/tests/codegen/simd-intrinsic/simd-intrinsic-float-cos.rs +++ b/tests/codegen/simd-intrinsic/simd-intrinsic-float-cos.rs @@ -7,23 +7,19 @@ #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f32x2(pub f32, pub f32); +pub struct f32x2(pub [f32; 2]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f32x4(pub f32, pub f32, pub f32, pub f32); +pub struct f32x4(pub [f32; 4]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f32x8(pub f32, pub f32, pub f32, pub f32, - pub f32, pub f32, pub f32, pub f32); +pub struct f32x8(pub [f32; 8]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f32x16(pub f32, pub f32, pub f32, pub f32, - pub f32, pub f32, pub f32, pub f32, - pub f32, pub f32, pub f32, pub f32, - pub f32, pub f32, pub f32, pub f32); +pub struct f32x16(pub [f32; 16]); extern "rust-intrinsic" { fn simd_fcos(x: T) -> T; @@ -59,16 +55,15 @@ pub unsafe fn fcos_32x16(a: f32x16) -> f32x16 { #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f64x2(pub f64, pub f64); +pub struct f64x2(pub [f64; 2]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f64x4(pub f64, pub f64, pub f64, pub f64); +pub struct f64x4(pub [f64; 4]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f64x8(pub f64, pub f64, pub f64, pub f64, - pub f64, pub f64, pub f64, pub f64); +pub struct f64x8(pub [f64; 8]); // CHECK-LABEL: @fcos_64x4 #[no_mangle] diff --git a/tests/codegen/simd-intrinsic/simd-intrinsic-float-exp.rs b/tests/codegen/simd-intrinsic/simd-intrinsic-float-exp.rs index 48c1a8ec489db..fef003dde5bb8 100644 --- a/tests/codegen/simd-intrinsic/simd-intrinsic-float-exp.rs +++ b/tests/codegen/simd-intrinsic/simd-intrinsic-float-exp.rs @@ -7,23 +7,19 @@ #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f32x2(pub f32, pub f32); +pub struct f32x2(pub [f32; 2]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f32x4(pub f32, pub f32, pub f32, pub f32); +pub struct f32x4(pub [f32; 4]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f32x8(pub f32, pub f32, pub f32, pub f32, - pub f32, pub f32, pub f32, pub f32); +pub struct f32x8(pub [f32; 8]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f32x16(pub f32, pub f32, pub f32, pub f32, - pub f32, pub f32, pub f32, pub f32, - pub f32, pub f32, pub f32, pub f32, - pub f32, pub f32, pub f32, pub f32); +pub struct f32x16(pub [f32; 16]); extern "rust-intrinsic" { fn simd_fexp(x: T) -> T; @@ -59,16 +55,15 @@ pub unsafe fn exp_32x16(a: f32x16) -> f32x16 { #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f64x2(pub f64, pub f64); +pub struct f64x2(pub [f64; 2]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f64x4(pub f64, pub f64, pub f64, pub f64); +pub struct f64x4(pub [f64; 4]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f64x8(pub f64, pub f64, pub f64, pub f64, - pub f64, pub f64, pub f64, pub f64); +pub struct f64x8(pub [f64; 8]); // CHECK-LABEL: @exp_64x4 #[no_mangle] diff --git a/tests/codegen/simd-intrinsic/simd-intrinsic-float-exp2.rs b/tests/codegen/simd-intrinsic/simd-intrinsic-float-exp2.rs index 23c38d8162157..779c0fc403a3d 100644 --- a/tests/codegen/simd-intrinsic/simd-intrinsic-float-exp2.rs +++ b/tests/codegen/simd-intrinsic/simd-intrinsic-float-exp2.rs @@ -7,23 +7,19 @@ #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f32x2(pub f32, pub f32); +pub struct f32x2(pub [f32; 2]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f32x4(pub f32, pub f32, pub f32, pub f32); +pub struct f32x4(pub [f32; 4]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f32x8(pub f32, pub f32, pub f32, pub f32, - pub f32, pub f32, pub f32, pub f32); +pub struct f32x8(pub [f32; 8]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f32x16(pub f32, pub f32, pub f32, pub f32, - pub f32, pub f32, pub f32, pub f32, - pub f32, pub f32, pub f32, pub f32, - pub f32, pub f32, pub f32, pub f32); +pub struct f32x16(pub [f32; 16]); extern "rust-intrinsic" { fn simd_fexp2(x: T) -> T; @@ -59,16 +55,15 @@ pub unsafe fn exp2_32x16(a: f32x16) -> f32x16 { #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f64x2(pub f64, pub f64); +pub struct f64x2(pub [f64; 2]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f64x4(pub f64, pub f64, pub f64, pub f64); +pub struct f64x4(pub [f64; 4]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f64x8(pub f64, pub f64, pub f64, pub f64, - pub f64, pub f64, pub f64, pub f64); +pub struct f64x8(pub [f64; 8]); // CHECK-LABEL: @exp2_64x4 #[no_mangle] diff --git a/tests/codegen/simd-intrinsic/simd-intrinsic-float-floor.rs b/tests/codegen/simd-intrinsic/simd-intrinsic-float-floor.rs index 978f263031ac3..b2bd27a5b75c2 100644 --- a/tests/codegen/simd-intrinsic/simd-intrinsic-float-floor.rs +++ b/tests/codegen/simd-intrinsic/simd-intrinsic-float-floor.rs @@ -7,23 +7,19 @@ #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f32x2(pub f32, pub f32); +pub struct f32x2(pub [f32; 2]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f32x4(pub f32, pub f32, pub f32, pub f32); +pub struct f32x4(pub [f32; 4]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f32x8(pub f32, pub f32, pub f32, pub f32, - pub f32, pub f32, pub f32, pub f32); +pub struct f32x8(pub [f32; 8]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f32x16(pub f32, pub f32, pub f32, pub f32, - pub f32, pub f32, pub f32, pub f32, - pub f32, pub f32, pub f32, pub f32, - pub f32, pub f32, pub f32, pub f32); +pub struct f32x16(pub [f32; 16]); extern "rust-intrinsic" { fn simd_floor(x: T) -> T; @@ -59,16 +55,15 @@ pub unsafe fn floor_32x16(a: f32x16) -> f32x16 { #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f64x2(pub f64, pub f64); +pub struct f64x2(pub [f64; 2]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f64x4(pub f64, pub f64, pub f64, pub f64); +pub struct f64x4(pub [f64; 4]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f64x8(pub f64, pub f64, pub f64, pub f64, - pub f64, pub f64, pub f64, pub f64); +pub struct f64x8(pub [f64; 8]); // CHECK-LABEL: @floor_64x4 #[no_mangle] diff --git a/tests/codegen/simd-intrinsic/simd-intrinsic-float-fma.rs b/tests/codegen/simd-intrinsic/simd-intrinsic-float-fma.rs index 200d67180265a..37f4782626add 100644 --- a/tests/codegen/simd-intrinsic/simd-intrinsic-float-fma.rs +++ b/tests/codegen/simd-intrinsic/simd-intrinsic-float-fma.rs @@ -7,23 +7,19 @@ #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f32x2(pub f32, pub f32); +pub struct f32x2(pub [f32; 2]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f32x4(pub f32, pub f32, pub f32, pub f32); +pub struct f32x4(pub [f32; 4]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f32x8(pub f32, pub f32, pub f32, pub f32, - pub f32, pub f32, pub f32, pub f32); +pub struct f32x8(pub [f32; 8]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f32x16(pub f32, pub f32, pub f32, pub f32, - pub f32, pub f32, pub f32, pub f32, - pub f32, pub f32, pub f32, pub f32, - pub f32, pub f32, pub f32, pub f32); +pub struct f32x16(pub [f32; 16]); extern "rust-intrinsic" { fn simd_fma(x: T, b: T, c: T) -> T; @@ -59,16 +55,15 @@ pub unsafe fn fma_32x16(a: f32x16, b: f32x16, c: f32x16) -> f32x16 { #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f64x2(pub f64, pub f64); +pub struct f64x2(pub [f64; 2]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f64x4(pub f64, pub f64, pub f64, pub f64); +pub struct f64x4(pub [f64; 4]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f64x8(pub f64, pub f64, pub f64, pub f64, - pub f64, pub f64, pub f64, pub f64); +pub struct f64x8(pub [f64; 8]); // CHECK-LABEL: @fma_64x4 #[no_mangle] diff --git a/tests/codegen/simd-intrinsic/simd-intrinsic-float-fsqrt.rs b/tests/codegen/simd-intrinsic/simd-intrinsic-float-fsqrt.rs index f70de3e27536e..336adf6db73f1 100644 --- a/tests/codegen/simd-intrinsic/simd-intrinsic-float-fsqrt.rs +++ b/tests/codegen/simd-intrinsic/simd-intrinsic-float-fsqrt.rs @@ -7,23 +7,19 @@ #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f32x2(pub f32, pub f32); +pub struct f32x2(pub [f32; 2]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f32x4(pub f32, pub f32, pub f32, pub f32); +pub struct f32x4(pub [f32; 4]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f32x8(pub f32, pub f32, pub f32, pub f32, - pub f32, pub f32, pub f32, pub f32); +pub struct f32x8(pub [f32; 8]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f32x16(pub f32, pub f32, pub f32, pub f32, - pub f32, pub f32, pub f32, pub f32, - pub f32, pub f32, pub f32, pub f32, - pub f32, pub f32, pub f32, pub f32); +pub struct f32x16(pub [f32; 16]); extern "rust-intrinsic" { fn simd_fsqrt(x: T) -> T; @@ -59,16 +55,15 @@ pub unsafe fn fsqrt_32x16(a: f32x16) -> f32x16 { #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f64x2(pub f64, pub f64); +pub struct f64x2(pub [f64; 2]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f64x4(pub f64, pub f64, pub f64, pub f64); +pub struct f64x4(pub [f64; 4]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f64x8(pub f64, pub f64, pub f64, pub f64, - pub f64, pub f64, pub f64, pub f64); +pub struct f64x8(pub [f64; 8]); // CHECK-LABEL: @fsqrt_64x4 #[no_mangle] diff --git a/tests/codegen/simd-intrinsic/simd-intrinsic-float-log.rs b/tests/codegen/simd-intrinsic/simd-intrinsic-float-log.rs index c0edd3ea48fc9..8e97abc3a6618 100644 --- a/tests/codegen/simd-intrinsic/simd-intrinsic-float-log.rs +++ b/tests/codegen/simd-intrinsic/simd-intrinsic-float-log.rs @@ -7,23 +7,19 @@ #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f32x2(pub f32, pub f32); +pub struct f32x2(pub [f32; 2]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f32x4(pub f32, pub f32, pub f32, pub f32); +pub struct f32x4(pub [f32; 4]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f32x8(pub f32, pub f32, pub f32, pub f32, - pub f32, pub f32, pub f32, pub f32); +pub struct f32x8(pub [f32; 8]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f32x16(pub f32, pub f32, pub f32, pub f32, - pub f32, pub f32, pub f32, pub f32, - pub f32, pub f32, pub f32, pub f32, - pub f32, pub f32, pub f32, pub f32); +pub struct f32x16(pub [f32; 16]); extern "rust-intrinsic" { fn simd_flog(x: T) -> T; @@ -59,16 +55,15 @@ pub unsafe fn log_32x16(a: f32x16) -> f32x16 { #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f64x2(pub f64, pub f64); +pub struct f64x2(pub [f64; 2]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f64x4(pub f64, pub f64, pub f64, pub f64); +pub struct f64x4(pub [f64; 4]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f64x8(pub f64, pub f64, pub f64, pub f64, - pub f64, pub f64, pub f64, pub f64); +pub struct f64x8(pub [f64; 8]); // CHECK-LABEL: @log_64x4 #[no_mangle] diff --git a/tests/codegen/simd-intrinsic/simd-intrinsic-float-log10.rs b/tests/codegen/simd-intrinsic/simd-intrinsic-float-log10.rs index 766307f47ed35..1d4d4dc24e9ad 100644 --- a/tests/codegen/simd-intrinsic/simd-intrinsic-float-log10.rs +++ b/tests/codegen/simd-intrinsic/simd-intrinsic-float-log10.rs @@ -7,23 +7,19 @@ #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f32x2(pub f32, pub f32); +pub struct f32x2(pub [f32; 2]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f32x4(pub f32, pub f32, pub f32, pub f32); +pub struct f32x4(pub [f32; 4]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f32x8(pub f32, pub f32, pub f32, pub f32, - pub f32, pub f32, pub f32, pub f32); +pub struct f32x8(pub [f32; 8]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f32x16(pub f32, pub f32, pub f32, pub f32, - pub f32, pub f32, pub f32, pub f32, - pub f32, pub f32, pub f32, pub f32, - pub f32, pub f32, pub f32, pub f32); +pub struct f32x16(pub [f32; 16]); extern "rust-intrinsic" { fn simd_flog10(x: T) -> T; @@ -59,16 +55,15 @@ pub unsafe fn log10_32x16(a: f32x16) -> f32x16 { #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f64x2(pub f64, pub f64); +pub struct f64x2(pub [f64; 2]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f64x4(pub f64, pub f64, pub f64, pub f64); +pub struct f64x4(pub [f64; 4]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f64x8(pub f64, pub f64, pub f64, pub f64, - pub f64, pub f64, pub f64, pub f64); +pub struct f64x8(pub [f64; 8]); // CHECK-LABEL: @log10_64x4 #[no_mangle] diff --git a/tests/codegen/simd-intrinsic/simd-intrinsic-float-log2.rs b/tests/codegen/simd-intrinsic/simd-intrinsic-float-log2.rs index 90c5918c33ea3..28f2f1516175c 100644 --- a/tests/codegen/simd-intrinsic/simd-intrinsic-float-log2.rs +++ b/tests/codegen/simd-intrinsic/simd-intrinsic-float-log2.rs @@ -7,23 +7,19 @@ #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f32x2(pub f32, pub f32); +pub struct f32x2(pub [f32; 2]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f32x4(pub f32, pub f32, pub f32, pub f32); +pub struct f32x4(pub [f32; 4]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f32x8(pub f32, pub f32, pub f32, pub f32, - pub f32, pub f32, pub f32, pub f32); +pub struct f32x8(pub [f32; 8]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f32x16(pub f32, pub f32, pub f32, pub f32, - pub f32, pub f32, pub f32, pub f32, - pub f32, pub f32, pub f32, pub f32, - pub f32, pub f32, pub f32, pub f32); +pub struct f32x16(pub [f32; 16]); extern "rust-intrinsic" { fn simd_flog2(x: T) -> T; @@ -59,16 +55,15 @@ pub unsafe fn log2_32x16(a: f32x16) -> f32x16 { #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f64x2(pub f64, pub f64); +pub struct f64x2(pub [f64; 2]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f64x4(pub f64, pub f64, pub f64, pub f64); +pub struct f64x4(pub [f64; 4]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f64x8(pub f64, pub f64, pub f64, pub f64, - pub f64, pub f64, pub f64, pub f64); +pub struct f64x8(pub [f64; 8]); // CHECK-LABEL: @log2_64x4 #[no_mangle] diff --git a/tests/codegen/simd-intrinsic/simd-intrinsic-float-minmax.rs b/tests/codegen/simd-intrinsic/simd-intrinsic-float-minmax.rs index d949112bae762..50c51bebe37ee 100644 --- a/tests/codegen/simd-intrinsic/simd-intrinsic-float-minmax.rs +++ b/tests/codegen/simd-intrinsic/simd-intrinsic-float-minmax.rs @@ -7,7 +7,7 @@ #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f32x4(pub f32, pub f32, pub f32, pub f32); +pub struct f32x4(pub [f32; 4]); extern "rust-intrinsic" { fn simd_fmin(x: T, y: T) -> T; diff --git a/tests/codegen/simd-intrinsic/simd-intrinsic-float-pow.rs b/tests/codegen/simd-intrinsic/simd-intrinsic-float-pow.rs index 21641c80d313c..3527f71c00b47 100644 --- a/tests/codegen/simd-intrinsic/simd-intrinsic-float-pow.rs +++ b/tests/codegen/simd-intrinsic/simd-intrinsic-float-pow.rs @@ -7,23 +7,19 @@ #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f32x2(pub f32, pub f32); +pub struct f32x2(pub [f32; 2]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f32x4(pub f32, pub f32, pub f32, pub f32); +pub struct f32x4(pub [f32; 4]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f32x8(pub f32, pub f32, pub f32, pub f32, - pub f32, pub f32, pub f32, pub f32); +pub struct f32x8(pub [f32; 8]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f32x16(pub f32, pub f32, pub f32, pub f32, - pub f32, pub f32, pub f32, pub f32, - pub f32, pub f32, pub f32, pub f32, - pub f32, pub f32, pub f32, pub f32); +pub struct f32x16(pub [f32; 16]); extern "rust-intrinsic" { fn simd_fpow(x: T, b: T) -> T; @@ -59,16 +55,15 @@ pub unsafe fn fpow_32x16(a: f32x16, b: f32x16) -> f32x16 { #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f64x2(pub f64, pub f64); +pub struct f64x2(pub [f64; 2]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f64x4(pub f64, pub f64, pub f64, pub f64); +pub struct f64x4(pub [f64; 4]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f64x8(pub f64, pub f64, pub f64, pub f64, - pub f64, pub f64, pub f64, pub f64); +pub struct f64x8(pub [f64; 8]); // CHECK-LABEL: @fpow_64x4 #[no_mangle] diff --git a/tests/codegen/simd-intrinsic/simd-intrinsic-float-powi.rs b/tests/codegen/simd-intrinsic/simd-intrinsic-float-powi.rs index 3985bdd50df70..4f0b5e4e01a3e 100644 --- a/tests/codegen/simd-intrinsic/simd-intrinsic-float-powi.rs +++ b/tests/codegen/simd-intrinsic/simd-intrinsic-float-powi.rs @@ -7,23 +7,19 @@ #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f32x2(pub f32, pub f32); +pub struct f32x2(pub [f32; 2]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f32x4(pub f32, pub f32, pub f32, pub f32); +pub struct f32x4(pub [f32; 4]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f32x8(pub f32, pub f32, pub f32, pub f32, - pub f32, pub f32, pub f32, pub f32); +pub struct f32x8(pub [f32; 8]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f32x16(pub f32, pub f32, pub f32, pub f32, - pub f32, pub f32, pub f32, pub f32, - pub f32, pub f32, pub f32, pub f32, - pub f32, pub f32, pub f32, pub f32); +pub struct f32x16(pub [f32; 16]); extern "rust-intrinsic" { fn simd_fpowi(x: T, b: i32) -> T; @@ -59,16 +55,15 @@ pub unsafe fn fpowi_32x16(a: f32x16, b: i32) -> f32x16 { #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f64x2(pub f64, pub f64); +pub struct f64x2(pub [f64; 2]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f64x4(pub f64, pub f64, pub f64, pub f64); +pub struct f64x4(pub [f64; 4]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f64x8(pub f64, pub f64, pub f64, pub f64, - pub f64, pub f64, pub f64, pub f64); +pub struct f64x8(pub [f64; 8]); // CHECK-LABEL: @fpowi_64x4 #[no_mangle] diff --git a/tests/codegen/simd-intrinsic/simd-intrinsic-float-sin.rs b/tests/codegen/simd-intrinsic/simd-intrinsic-float-sin.rs index f6978e32df724..4173809e3a9b3 100644 --- a/tests/codegen/simd-intrinsic/simd-intrinsic-float-sin.rs +++ b/tests/codegen/simd-intrinsic/simd-intrinsic-float-sin.rs @@ -7,23 +7,19 @@ #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f32x2(pub f32, pub f32); +pub struct f32x2(pub [f32; 2]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f32x4(pub f32, pub f32, pub f32, pub f32); +pub struct f32x4(pub [f32; 4]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f32x8(pub f32, pub f32, pub f32, pub f32, - pub f32, pub f32, pub f32, pub f32); +pub struct f32x8(pub [f32; 8]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f32x16(pub f32, pub f32, pub f32, pub f32, - pub f32, pub f32, pub f32, pub f32, - pub f32, pub f32, pub f32, pub f32, - pub f32, pub f32, pub f32, pub f32); +pub struct f32x16(pub [f32; 16]); extern "rust-intrinsic" { fn simd_fsin(x: T) -> T; @@ -59,16 +55,15 @@ pub unsafe fn fsin_32x16(a: f32x16) -> f32x16 { #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f64x2(pub f64, pub f64); +pub struct f64x2(pub [f64; 2]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f64x4(pub f64, pub f64, pub f64, pub f64); +pub struct f64x4(pub [f64; 4]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f64x8(pub f64, pub f64, pub f64, pub f64, - pub f64, pub f64, pub f64, pub f64); +pub struct f64x8(pub [f64; 8]); // CHECK-LABEL: @fsin_64x4 #[no_mangle] diff --git a/tests/codegen/simd-intrinsic/simd-intrinsic-generic-arithmetic-saturating.rs b/tests/codegen/simd-intrinsic/simd-intrinsic-generic-arithmetic-saturating.rs index 809f9a32226f4..a5afa27876a0f 100644 --- a/tests/codegen/simd-intrinsic/simd-intrinsic-generic-arithmetic-saturating.rs +++ b/tests/codegen/simd-intrinsic/simd-intrinsic-generic-arithmetic-saturating.rs @@ -9,107 +9,57 @@ // signed integer types -#[repr(simd)] #[derive(Copy, Clone)] pub struct i8x2(i8, i8); -#[repr(simd)] #[derive(Copy, Clone)] pub struct i8x4(i8, i8, i8, i8); -#[repr(simd)] #[derive(Copy, Clone)] pub struct i8x8( - i8, i8, i8, i8, i8, i8, i8, i8, -); -#[repr(simd)] #[derive(Copy, Clone)] pub struct i8x16( - i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, -); -#[repr(simd)] #[derive(Copy, Clone)] pub struct i8x32( - i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, - i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, -); -#[repr(simd)] #[derive(Copy, Clone)] pub struct i8x64( - i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, - i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, - i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, - i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, -); - -#[repr(simd)] #[derive(Copy, Clone)] pub struct i16x2(i16, i16); -#[repr(simd)] #[derive(Copy, Clone)] pub struct i16x4(i16, i16, i16, i16); -#[repr(simd)] #[derive(Copy, Clone)] pub struct i16x8( - i16, i16, i16, i16, i16, i16, i16, i16, -); -#[repr(simd)] #[derive(Copy, Clone)] pub struct i16x16( - i16, i16, i16, i16, i16, i16, i16, i16, i16, i16, i16, i16, i16, i16, i16, i16, -); -#[repr(simd)] #[derive(Copy, Clone)] pub struct i16x32( - i16, i16, i16, i16, i16, i16, i16, i16, i16, i16, i16, i16, i16, i16, i16, i16, - i16, i16, i16, i16, i16, i16, i16, i16, i16, i16, i16, i16, i16, i16, i16, i16, -); - -#[repr(simd)] #[derive(Copy, Clone)] pub struct i32x2(i32, i32); -#[repr(simd)] #[derive(Copy, Clone)] pub struct i32x4(i32, i32, i32, i32); -#[repr(simd)] #[derive(Copy, Clone)] pub struct i32x8( - i32, i32, i32, i32, i32, i32, i32, i32, -); -#[repr(simd)] #[derive(Copy, Clone)] pub struct i32x16( - i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, -); - -#[repr(simd)] #[derive(Copy, Clone)] pub struct i64x2(i64, i64); -#[repr(simd)] #[derive(Copy, Clone)] pub struct i64x4(i64, i64, i64, i64); -#[repr(simd)] #[derive(Copy, Clone)] pub struct i64x8( - i64, i64, i64, i64, i64, i64, i64, i64, -); - -#[repr(simd)] #[derive(Copy, Clone)] pub struct i128x2(i128, i128); -#[repr(simd)] #[derive(Copy, Clone)] pub struct i128x4(i128, i128, i128, i128); +#[repr(simd)] #[derive(Copy, Clone)] pub struct i8x2([i8; 2]); +#[repr(simd)] #[derive(Copy, Clone)] pub struct i8x4([i8; 4]); +#[repr(simd)] #[derive(Copy, Clone)] pub struct i8x8([i8; 8]); +#[repr(simd)] #[derive(Copy, Clone)] pub struct i8x16([i8; 16]); +#[repr(simd)] #[derive(Copy, Clone)] pub struct i8x32([i8; 32]); +#[repr(simd)] #[derive(Copy, Clone)] pub struct i8x64([i8; 64]); + +#[repr(simd)] #[derive(Copy, Clone)] pub struct i16x2([i16; 2]); +#[repr(simd)] #[derive(Copy, Clone)] pub struct i16x4([i16; 4]); +#[repr(simd)] #[derive(Copy, Clone)] pub struct i16x8([i16; 8]); +#[repr(simd)] #[derive(Copy, Clone)] pub struct i16x16([i16; 16]); +#[repr(simd)] #[derive(Copy, Clone)] pub struct i16x32([i16; 32]); + +#[repr(simd)] #[derive(Copy, Clone)] pub struct i32x2([i32; 2]); +#[repr(simd)] #[derive(Copy, Clone)] pub struct i32x4([i32; 4]); +#[repr(simd)] #[derive(Copy, Clone)] pub struct i32x8([i32; 8]); +#[repr(simd)] #[derive(Copy, Clone)] pub struct i32x16([i32; 16]); + +#[repr(simd)] #[derive(Copy, Clone)] pub struct i64x2([i64; 2]); +#[repr(simd)] #[derive(Copy, Clone)] pub struct i64x4([i64; 4]); +#[repr(simd)] #[derive(Copy, Clone)] pub struct i64x8([i64; 8]); + +#[repr(simd)] #[derive(Copy, Clone)] pub struct i128x2([i128; 2]); +#[repr(simd)] #[derive(Copy, Clone)] pub struct i128x4([i128; 4]); // unsigned integer types -#[repr(simd)] #[derive(Copy, Clone)] pub struct u8x2(u8, u8); -#[repr(simd)] #[derive(Copy, Clone)] pub struct u8x4(u8, u8, u8, u8); -#[repr(simd)] #[derive(Copy, Clone)] pub struct u8x8( - u8, u8, u8, u8, u8, u8, u8, u8, -); -#[repr(simd)] #[derive(Copy, Clone)] pub struct u8x16( - u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, -); -#[repr(simd)] #[derive(Copy, Clone)] pub struct u8x32( - u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, - u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, -); -#[repr(simd)] #[derive(Copy, Clone)] pub struct u8x64( - u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, - u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, - u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, - u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, -); - -#[repr(simd)] #[derive(Copy, Clone)] pub struct u16x2(u16, u16); -#[repr(simd)] #[derive(Copy, Clone)] pub struct u16x4(u16, u16, u16, u16); -#[repr(simd)] #[derive(Copy, Clone)] pub struct u16x8( - u16, u16, u16, u16, u16, u16, u16, u16, -); -#[repr(simd)] #[derive(Copy, Clone)] pub struct u16x16( - u16, u16, u16, u16, u16, u16, u16, u16, u16, u16, u16, u16, u16, u16, u16, u16, -); -#[repr(simd)] #[derive(Copy, Clone)] pub struct u16x32( - u16, u16, u16, u16, u16, u16, u16, u16, u16, u16, u16, u16, u16, u16, u16, u16, - u16, u16, u16, u16, u16, u16, u16, u16, u16, u16, u16, u16, u16, u16, u16, u16, -); - -#[repr(simd)] #[derive(Copy, Clone)] pub struct u32x2(u32, u32); -#[repr(simd)] #[derive(Copy, Clone)] pub struct u32x4(u32, u32, u32, u32); -#[repr(simd)] #[derive(Copy, Clone)] pub struct u32x8( - u32, u32, u32, u32, u32, u32, u32, u32, -); -#[repr(simd)] #[derive(Copy, Clone)] pub struct u32x16( - u32, u32, u32, u32, u32, u32, u32, u32, u32, u32, u32, u32, u32, u32, u32, u32, -); - -#[repr(simd)] #[derive(Copy, Clone)] pub struct u64x2(u64, u64); -#[repr(simd)] #[derive(Copy, Clone)] pub struct u64x4(u64, u64, u64, u64); -#[repr(simd)] #[derive(Copy, Clone)] pub struct u64x8( - u64, u64, u64, u64, u64, u64, u64, u64, -); - -#[repr(simd)] #[derive(Copy, Clone)] pub struct u128x2(u128, u128); -#[repr(simd)] #[derive(Copy, Clone)] pub struct u128x4(u128, u128, u128, u128); +#[repr(simd)] #[derive(Copy, Clone)] pub struct u8x2([u8; 2]); +#[repr(simd)] #[derive(Copy, Clone)] pub struct u8x4([u8; 4]); +#[repr(simd)] #[derive(Copy, Clone)] pub struct u8x8([u8; 8]); +#[repr(simd)] #[derive(Copy, Clone)] pub struct u8x16([u8; 16]); +#[repr(simd)] #[derive(Copy, Clone)] pub struct u8x32([u8; 32]); +#[repr(simd)] #[derive(Copy, Clone)] pub struct u8x64([u8; 64]); + +#[repr(simd)] #[derive(Copy, Clone)] pub struct u16x2([u16; 2]); +#[repr(simd)] #[derive(Copy, Clone)] pub struct u16x4([u16; 4]); +#[repr(simd)] #[derive(Copy, Clone)] pub struct u16x8([u16; 8]); +#[repr(simd)] #[derive(Copy, Clone)] pub struct u16x16([u16; 16]); +#[repr(simd)] #[derive(Copy, Clone)] pub struct u16x32([u16; 32]); + +#[repr(simd)] #[derive(Copy, Clone)] pub struct u32x2([u32; 2]); +#[repr(simd)] #[derive(Copy, Clone)] pub struct u32x4([u32; 4]); +#[repr(simd)] #[derive(Copy, Clone)] pub struct u32x8([u32; 8]); +#[repr(simd)] #[derive(Copy, Clone)] pub struct u32x16([u32; 16]); + +#[repr(simd)] #[derive(Copy, Clone)] pub struct u64x2([u64; 2]); +#[repr(simd)] #[derive(Copy, Clone)] pub struct u64x4([u64; 4]); +#[repr(simd)] #[derive(Copy, Clone)] pub struct u64x8([u64; 8]); + +#[repr(simd)] #[derive(Copy, Clone)] pub struct u128x2([u128; 2]); +#[repr(simd)] #[derive(Copy, Clone)] pub struct u128x4([u128; 4]); extern "rust-intrinsic" { fn simd_saturating_add(x: T, y: T) -> T; diff --git a/tests/codegen/simd-intrinsic/simd-intrinsic-generic-bitmask.rs b/tests/codegen/simd-intrinsic/simd-intrinsic-generic-bitmask.rs index 44a4c52d64a87..81ac90269b7df 100644 --- a/tests/codegen/simd-intrinsic/simd-intrinsic-generic-bitmask.rs +++ b/tests/codegen/simd-intrinsic/simd-intrinsic-generic-bitmask.rs @@ -8,19 +8,15 @@ #[repr(simd)] #[derive(Copy, Clone)] -pub struct u32x2(u32, u32); +pub struct u32x2([u32; 2]); #[repr(simd)] #[derive(Copy, Clone)] -pub struct i32x2(i32, i32); +pub struct i32x2([i32; 2]); #[repr(simd)] #[derive(Copy, Clone)] -pub struct i8x16( - i8, i8, i8, i8, i8, i8, i8, i8, - i8, i8, i8, i8, i8, i8, i8, i8, -); - +pub struct i8x16([i8; 16]); extern "rust-intrinsic" { fn simd_bitmask(x: T) -> U; diff --git a/tests/codegen/simd-intrinsic/simd-intrinsic-generic-gather.rs b/tests/codegen/simd-intrinsic/simd-intrinsic-generic-gather.rs index 863a9606c7e99..10ceeecf9007e 100644 --- a/tests/codegen/simd-intrinsic/simd-intrinsic-generic-gather.rs +++ b/tests/codegen/simd-intrinsic/simd-intrinsic-generic-gather.rs @@ -9,11 +9,11 @@ #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct Vec2(pub T, pub T); +pub struct Vec2(pub [T; 2]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct Vec4(pub T, pub T, pub T, pub T); +pub struct Vec4(pub [T; 4]); extern "rust-intrinsic" { fn simd_gather(value: T, pointers: P, mask: M) -> T; diff --git a/tests/codegen/simd-intrinsic/simd-intrinsic-generic-masked-load.rs b/tests/codegen/simd-intrinsic/simd-intrinsic-generic-masked-load.rs index b41c42810aafd..073dc0ac94d9e 100644 --- a/tests/codegen/simd-intrinsic/simd-intrinsic-generic-masked-load.rs +++ b/tests/codegen/simd-intrinsic/simd-intrinsic-generic-masked-load.rs @@ -7,11 +7,11 @@ #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct Vec2(pub T, pub T); +pub struct Vec2(pub [T; 2]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct Vec4(pub T, pub T, pub T, pub T); +pub struct Vec4(pub [T; 4]); extern "rust-intrinsic" { fn simd_masked_load(mask: M, pointer: P, values: T) -> T; diff --git a/tests/codegen/simd-intrinsic/simd-intrinsic-generic-masked-store.rs b/tests/codegen/simd-intrinsic/simd-intrinsic-generic-masked-store.rs index 066392bcde682..7c3393e6f2e7b 100644 --- a/tests/codegen/simd-intrinsic/simd-intrinsic-generic-masked-store.rs +++ b/tests/codegen/simd-intrinsic/simd-intrinsic-generic-masked-store.rs @@ -7,11 +7,11 @@ #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct Vec2(pub T, pub T); +pub struct Vec2(pub [T; 2]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct Vec4(pub T, pub T, pub T, pub T); +pub struct Vec4(pub [T; 4]); extern "rust-intrinsic" { fn simd_masked_store(mask: M, pointer: P, values: T) -> (); diff --git a/tests/codegen/simd-intrinsic/simd-intrinsic-generic-scatter.rs b/tests/codegen/simd-intrinsic/simd-intrinsic-generic-scatter.rs index e85bd61c7f83b..3c75ef5be40be 100644 --- a/tests/codegen/simd-intrinsic/simd-intrinsic-generic-scatter.rs +++ b/tests/codegen/simd-intrinsic/simd-intrinsic-generic-scatter.rs @@ -9,11 +9,11 @@ #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct Vec2(pub T, pub T); +pub struct Vec2(pub [T; 2]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct Vec4(pub T, pub T, pub T, pub T); +pub struct Vec4(pub [T; 4]); extern "rust-intrinsic" { fn simd_scatter(value: T, pointers: P, mask: M); diff --git a/tests/codegen/simd-intrinsic/simd-intrinsic-generic-select.rs b/tests/codegen/simd-intrinsic/simd-intrinsic-generic-select.rs index 05d2bf627ef12..c12fefa413b24 100644 --- a/tests/codegen/simd-intrinsic/simd-intrinsic-generic-select.rs +++ b/tests/codegen/simd-intrinsic/simd-intrinsic-generic-select.rs @@ -7,15 +7,15 @@ #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f32x4(pub f32, pub f32, pub f32, pub f32); +pub struct f32x4(pub [f32; 4]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct f32x8(f32, f32, f32, f32, f32, f32, f32, f32); +pub struct f32x8([f32; 8]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -pub struct b8x4(pub i8, pub i8, pub i8, pub i8); +pub struct b8x4(pub [i8; 4]); extern "rust-intrinsic" { fn simd_select(x: T, a: U, b: U) -> U; diff --git a/tests/codegen/simd-intrinsic/simd-intrinsic-transmute-array.rs b/tests/codegen/simd-intrinsic/simd-intrinsic-transmute-array.rs index c416f4d28bb53..75f989d6e12c4 100644 --- a/tests/codegen/simd-intrinsic/simd-intrinsic-transmute-array.rs +++ b/tests/codegen/simd-intrinsic/simd-intrinsic-transmute-array.rs @@ -13,10 +13,6 @@ pub struct S([f32; N]); #[derive(Copy, Clone)] pub struct T([f32; 4]); -#[repr(simd)] -#[derive(Copy, Clone)] -pub struct U(f32, f32, f32, f32); - // CHECK-LABEL: @array_align( #[no_mangle] pub fn array_align() -> usize { @@ -28,7 +24,7 @@ pub fn array_align() -> usize { #[no_mangle] pub fn vector_align() -> usize { // CHECK: ret [[USIZE]] [[VECTOR_ALIGN:[0-9]+]] - const { std::mem::align_of::() } + const { std::mem::align_of::() } } // CHECK-LABEL: @build_array_s @@ -60,22 +56,3 @@ pub fn build_array_transmute_t(x: [f32; 4]) -> T { // CHECK: store <4 x float> %[[VAL:.+]], ptr %_0, align [[VECTOR_ALIGN]] unsafe { std::mem::transmute(x) } } - -// CHECK-LABEL: @build_array_u -#[no_mangle] -pub fn build_array_u(x: [f32; 4]) -> U { - // CHECK: store float %a, {{.+}}, align [[VECTOR_ALIGN]] - // CHECK: store float %b, {{.+}}, align [[ARRAY_ALIGN]] - // CHECK: store float %c, {{.+}}, align - // CHECK: store float %d, {{.+}}, align [[ARRAY_ALIGN]] - let [a, b, c, d] = x; - U(a, b, c, d) -} - -// CHECK-LABEL: @build_array_transmute_u -#[no_mangle] -pub fn build_array_transmute_u(x: [f32; 4]) -> U { - // CHECK: %[[VAL:.+]] = load <4 x float>, ptr %x, align [[ARRAY_ALIGN]] - // CHECK: store <4 x float> %[[VAL:.+]], ptr %_0, align [[VECTOR_ALIGN]] - unsafe { std::mem::transmute(x) } -} diff --git a/tests/codegen/simd/unpadded-simd.rs b/tests/codegen/simd/unpadded-simd.rs index 66d9298c0068c..ef067a15702c3 100644 --- a/tests/codegen/simd/unpadded-simd.rs +++ b/tests/codegen/simd/unpadded-simd.rs @@ -7,7 +7,7 @@ #[derive(Copy, Clone)] #[repr(simd)] -pub struct int16x4_t(pub i16, pub i16, pub i16, pub i16); +pub struct int16x4_t(pub [i16; 4]); #[derive(Copy, Clone)] pub struct int16x4x2_t(pub int16x4_t, pub int16x4_t); diff --git a/tests/codegen/union-abi.rs b/tests/codegen/union-abi.rs index 08015014456ce..a1c081d7d61c2 100644 --- a/tests/codegen/union-abi.rs +++ b/tests/codegen/union-abi.rs @@ -16,7 +16,7 @@ pub enum Unhab {} #[repr(simd)] #[derive(Copy, Clone)] -pub struct i64x4(i64, i64, i64, i64); +pub struct i64x4([i64; 4]); #[derive(Copy, Clone)] pub union UnionI64x4 { diff --git a/tests/codegen/zst-offset.rs b/tests/codegen/zst-offset.rs index 14e97fd26ddf7..475394a881561 100644 --- a/tests/codegen/zst-offset.rs +++ b/tests/codegen/zst-offset.rs @@ -27,7 +27,7 @@ pub fn scalarpair_layout(s: &(u64, u32, ())) { } #[repr(simd)] -pub struct U64x4(u64, u64, u64, u64); +pub struct U64x4([u64; 4]); // Check that we correctly generate a GEP for a ZST that is not included in Vector layout // CHECK-LABEL: @vector_layout diff --git a/tests/debuginfo/simd.rs b/tests/debuginfo/simd.rs index e4fe262235bae..12675a71a5708 100644 --- a/tests/debuginfo/simd.rs +++ b/tests/debuginfo/simd.rs @@ -10,27 +10,27 @@ // gdb-command:run // gdb-command:print vi8x16 -// gdb-check:$1 = simd::i8x16 (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15) +// gdb-check:$1 = simd::i8x16 ([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]) // gdb-command:print vi16x8 -// gdb-check:$2 = simd::i16x8 (16, 17, 18, 19, 20, 21, 22, 23) +// gdb-check:$2 = simd::i16x8 ([16, 17, 18, 19, 20, 21, 22, 23]) // gdb-command:print vi32x4 -// gdb-check:$3 = simd::i32x4 (24, 25, 26, 27) +// gdb-check:$3 = simd::i32x4 ([24, 25, 26, 27]) // gdb-command:print vi64x2 -// gdb-check:$4 = simd::i64x2 (28, 29) +// gdb-check:$4 = simd::i64x2 ([28, 29]) // gdb-command:print vu8x16 -// gdb-check:$5 = simd::u8x16 (30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45) +// gdb-check:$5 = simd::u8x16 ([30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45]) // gdb-command:print vu16x8 -// gdb-check:$6 = simd::u16x8 (46, 47, 48, 49, 50, 51, 52, 53) +// gdb-check:$6 = simd::u16x8 ([46, 47, 48, 49, 50, 51, 52, 53]) // gdb-command:print vu32x4 -// gdb-check:$7 = simd::u32x4 (54, 55, 56, 57) +// gdb-check:$7 = simd::u32x4 ([54, 55, 56, 57]) // gdb-command:print vu64x2 -// gdb-check:$8 = simd::u64x2 (58, 59) +// gdb-check:$8 = simd::u64x2 ([58, 59]) // gdb-command:print vf32x4 -// gdb-check:$9 = simd::f32x4 (60.5, 61.5, 62.5, 63.5) +// gdb-check:$9 = simd::f32x4 ([60.5, 61.5, 62.5, 63.5]) // gdb-command:print vf64x2 -// gdb-check:$10 = simd::f64x2 (64.5, 65.5) +// gdb-check:$10 = simd::f64x2 ([64.5, 65.5]) // gdb-command:continue @@ -40,43 +40,43 @@ #![feature(repr_simd)] #[repr(simd)] -struct i8x16(i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8); +struct i8x16([i8; 16]); #[repr(simd)] -struct i16x8(i16, i16, i16, i16, i16, i16, i16, i16); +struct i16x8([i16; 8]); #[repr(simd)] -struct i32x4(i32, i32, i32, i32); +struct i32x4([i32; 4]); #[repr(simd)] -struct i64x2(i64, i64); +struct i64x2([i64; 2]); #[repr(simd)] -struct u8x16(u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8); +struct u8x16([u8; 16]); #[repr(simd)] -struct u16x8(u16, u16, u16, u16, u16, u16, u16, u16); +struct u16x8([u16; 8]); #[repr(simd)] -struct u32x4(u32, u32, u32, u32); +struct u32x4([u32; 4]); #[repr(simd)] -struct u64x2(u64, u64); +struct u64x2([u64; 2]); #[repr(simd)] -struct f32x4(f32, f32, f32, f32); +struct f32x4([f32; 4]); #[repr(simd)] -struct f64x2(f64, f64); +struct f64x2([f64; 2]); fn main() { - let vi8x16 = i8x16(0, 1, 2, 3, 4, 5, 6, 7, - 8, 9, 10, 11, 12, 13, 14, 15); + let vi8x16 = i8x16([0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15]); - let vi16x8 = i16x8(16, 17, 18, 19, 20, 21, 22, 23); - let vi32x4 = i32x4(24, 25, 26, 27); - let vi64x2 = i64x2(28, 29); + let vi16x8 = i16x8([16, 17, 18, 19, 20, 21, 22, 23]); + let vi32x4 = i32x4([24, 25, 26, 27]); + let vi64x2 = i64x2([28, 29]); - let vu8x16 = u8x16(30, 31, 32, 33, 34, 35, 36, 37, - 38, 39, 40, 41, 42, 43, 44, 45); - let vu16x8 = u16x8(46, 47, 48, 49, 50, 51, 52, 53); - let vu32x4 = u32x4(54, 55, 56, 57); - let vu64x2 = u64x2(58, 59); + let vu8x16 = u8x16([30, 31, 32, 33, 34, 35, 36, 37, + 38, 39, 40, 41, 42, 43, 44, 45]); + let vu16x8 = u16x8([46, 47, 48, 49, 50, 51, 52, 53]); + let vu32x4 = u32x4([54, 55, 56, 57]); + let vu64x2 = u64x2([58, 59]); - let vf32x4 = f32x4(60.5f32, 61.5f32, 62.5f32, 63.5f32); - let vf64x2 = f64x2(64.5f64, 65.5f64); + let vf32x4 = f32x4([60.5f32, 61.5f32, 62.5f32, 63.5f32]); + let vf64x2 = f64x2([64.5f64, 65.5f64]); zzz(); // #break } diff --git a/tests/incremental/issue-61530.rs b/tests/incremental/issue-61530.rs index e4ee8ccbc4bd2..b4914dda11ae1 100644 --- a/tests/incremental/issue-61530.rs +++ b/tests/incremental/issue-61530.rs @@ -3,7 +3,7 @@ //@ revisions:rpass1 rpass2 #[repr(simd)] -struct I32x2(i32, i32); +struct I32x2([i32; 2]); extern "rust-intrinsic" { fn simd_shuffle(x: T, y: T, idx: I) -> U; @@ -12,7 +12,7 @@ extern "rust-intrinsic" { fn main() { unsafe { const IDX: [u32; 2] = [0, 0]; - let _: I32x2 = simd_shuffle(I32x2(1, 2), I32x2(3, 4), IDX); - let _: I32x2 = simd_shuffle(I32x2(1, 2), I32x2(3, 4), IDX); + let _: I32x2 = simd_shuffle(I32x2([1, 2]), I32x2([3, 4]), IDX); + let _: I32x2 = simd_shuffle(I32x2([1, 2]), I32x2([3, 4]), IDX); } } diff --git a/tests/run-make/simd-ffi/simd.rs b/tests/run-make/simd-ffi/simd.rs index d11cfd77c5bf9..b72078faafa26 100644 --- a/tests/run-make/simd-ffi/simd.rs +++ b/tests/run-make/simd-ffi/simd.rs @@ -8,7 +8,7 @@ #[derive(Copy)] #[repr(simd)] -pub struct f32x4(f32, f32, f32, f32); +pub struct f32x4([f32; 4]); extern "C" { #[link_name = "llvm.sqrt.v4f32"] @@ -21,7 +21,7 @@ pub fn foo(x: f32x4) -> f32x4 { #[derive(Copy)] #[repr(simd)] -pub struct i32x4(i32, i32, i32, i32); +pub struct i32x4([i32; 4]); extern "C" { // _mm_sll_epi32 @@ -62,6 +62,8 @@ pub trait Copy {} impl Copy for f32 {} impl Copy for i32 {} +impl Copy for [f32; 4] {} +impl Copy for [i32; 4] {} pub mod marker { pub use Copy; diff --git a/tests/rustdoc/inline_cross/auxiliary/repr.rs b/tests/rustdoc/inline_cross/auxiliary/repr.rs index 35f08c11b7b3a..0211e1a86588f 100644 --- a/tests/rustdoc/inline_cross/auxiliary/repr.rs +++ b/tests/rustdoc/inline_cross/auxiliary/repr.rs @@ -6,7 +6,7 @@ pub struct ReprC { } #[repr(simd, packed(2))] pub struct ReprSimd { - field: u8, + field: [u8; 1], } #[repr(transparent)] pub struct ReprTransparent { diff --git a/tests/ui/abi/arm-unadjusted-intrinsic.rs b/tests/ui/abi/arm-unadjusted-intrinsic.rs index 7a728d4b241d9..533cd40b30a5c 100644 --- a/tests/ui/abi/arm-unadjusted-intrinsic.rs +++ b/tests/ui/abi/arm-unadjusted-intrinsic.rs @@ -25,16 +25,13 @@ impl Copy for i8 {} impl Copy for *const T {} impl Copy for *mut T {} +// I hate no_core tests! +impl Copy for [T; N] {} // Regression test for https://github.com/rust-lang/rust/issues/118124. #[repr(simd)] -pub struct int8x16_t( - pub(crate) i8, pub(crate) i8, pub(crate) i8, pub(crate) i8, - pub(crate) i8, pub(crate) i8, pub(crate) i8, pub(crate) i8, - pub(crate) i8, pub(crate) i8, pub(crate) i8, pub(crate) i8, - pub(crate) i8, pub(crate) i8, pub(crate) i8, pub(crate) i8, -); +pub struct int8x16_t(pub(crate) [i8; 16]); impl Copy for int8x16_t {} #[repr(C)] diff --git a/tests/ui/abi/homogenous-floats-target-feature-mixup.rs b/tests/ui/abi/homogenous-floats-target-feature-mixup.rs index 3a8540a825ca2..4afb710b193eb 100644 --- a/tests/ui/abi/homogenous-floats-target-feature-mixup.rs +++ b/tests/ui/abi/homogenous-floats-target-feature-mixup.rs @@ -65,13 +65,13 @@ fn is_sigill(status: ExitStatus) -> bool { #[allow(nonstandard_style)] mod test { #[derive(PartialEq, Debug, Clone, Copy)] - struct f32x2(f32, f32); + struct f32x2([f32; 2]); #[derive(PartialEq, Debug, Clone, Copy)] - struct f32x4(f32, f32, f32, f32); + struct f32x4([f32; 4]); #[derive(PartialEq, Debug, Clone, Copy)] - struct f32x8(f32, f32, f32, f32, f32, f32, f32, f32); + struct f32x8([f32; 8]); pub fn main(level: &str) { unsafe { @@ -97,9 +97,9 @@ mod test { )*) => ($( $(#[$attr])* unsafe fn $main(level: &str) { - let m128 = f32x2(1., 2.); - let m256 = f32x4(3., 4., 5., 6.); - let m512 = f32x8(7., 8., 9., 10., 11., 12., 13., 14.); + let m128 = f32x2([1., 2.]); + let m256 = f32x4([3., 4., 5., 6.]); + let m512 = f32x8([7., 8., 9., 10., 11., 12., 13., 14.]); assert_eq!(id_sse_128(m128), m128); assert_eq!(id_sse_256(m256), m256); assert_eq!(id_sse_512(m512), m512); @@ -133,55 +133,55 @@ mod test { #[target_feature(enable = "sse2")] unsafe fn id_sse_128(a: f32x2) -> f32x2 { - assert_eq!(a, f32x2(1., 2.)); + assert_eq!(a, f32x2([1., 2.])); a.clone() } #[target_feature(enable = "sse2")] unsafe fn id_sse_256(a: f32x4) -> f32x4 { - assert_eq!(a, f32x4(3., 4., 5., 6.)); + assert_eq!(a, f32x4([3., 4., 5., 6.])); a.clone() } #[target_feature(enable = "sse2")] unsafe fn id_sse_512(a: f32x8) -> f32x8 { - assert_eq!(a, f32x8(7., 8., 9., 10., 11., 12., 13., 14.)); + assert_eq!(a, f32x8([7., 8., 9., 10., 11., 12., 13., 14.])); a.clone() } #[target_feature(enable = "avx")] unsafe fn id_avx_128(a: f32x2) -> f32x2 { - assert_eq!(a, f32x2(1., 2.)); + assert_eq!(a, f32x2([1., 2.])); a.clone() } #[target_feature(enable = "avx")] unsafe fn id_avx_256(a: f32x4) -> f32x4 { - assert_eq!(a, f32x4(3., 4., 5., 6.)); + assert_eq!(a, f32x4([3., 4., 5., 6.])); a.clone() } #[target_feature(enable = "avx")] unsafe fn id_avx_512(a: f32x8) -> f32x8 { - assert_eq!(a, f32x8(7., 8., 9., 10., 11., 12., 13., 14.)); + assert_eq!(a, f32x8([7., 8., 9., 10., 11., 12., 13., 14.])); a.clone() } #[target_feature(enable = "avx512bw")] unsafe fn id_avx512_128(a: f32x2) -> f32x2 { - assert_eq!(a, f32x2(1., 2.)); + assert_eq!(a, f32x2([1., 2.])); a.clone() } #[target_feature(enable = "avx512bw")] unsafe fn id_avx512_256(a: f32x4) -> f32x4 { - assert_eq!(a, f32x4(3., 4., 5., 6.)); + assert_eq!(a, f32x4([3., 4., 5., 6.])); a.clone() } #[target_feature(enable = "avx512bw")] unsafe fn id_avx512_512(a: f32x8) -> f32x8 { - assert_eq!(a, f32x8(7., 8., 9., 10., 11., 12., 13., 14.)); + assert_eq!(a, f32x8([7., 8., 9., 10., 11., 12., 13., 14.])); a.clone() } } diff --git a/tests/ui/asm/aarch64/type-check-2-2.rs b/tests/ui/asm/aarch64/type-check-2-2.rs index f442ce81476ed..4b8ee1d922986 100644 --- a/tests/ui/asm/aarch64/type-check-2-2.rs +++ b/tests/ui/asm/aarch64/type-check-2-2.rs @@ -6,10 +6,10 @@ use std::arch::{asm, global_asm}; #[repr(simd)] #[derive(Clone, Copy)] -struct SimdType(f32, f32, f32, f32); +struct SimdType([f32; 4]); #[repr(simd)] -struct SimdNonCopy(f32, f32, f32, f32); +struct SimdNonCopy([f32; 4]); fn main() { unsafe { diff --git a/tests/ui/asm/aarch64/type-check-2.rs b/tests/ui/asm/aarch64/type-check-2.rs index 46667ae3a6565..ad223b799b786 100644 --- a/tests/ui/asm/aarch64/type-check-2.rs +++ b/tests/ui/asm/aarch64/type-check-2.rs @@ -6,10 +6,10 @@ use std::arch::{asm, global_asm}; #[repr(simd)] #[derive(Clone, Copy)] -struct SimdType(f32, f32, f32, f32); +struct SimdType([f32; 4]); #[repr(simd)] -struct SimdNonCopy(f32, f32, f32, f32); +struct SimdNonCopy([f32; 4]); fn main() { unsafe { @@ -17,7 +17,7 @@ fn main() { // Register operands must be Copy - asm!("{:v}", in(vreg) SimdNonCopy(0.0, 0.0, 0.0, 0.0)); + asm!("{:v}", in(vreg) SimdNonCopy([0.0, 0.0, 0.0, 0.0])); //~^ ERROR arguments for inline assembly must be copyable // Register operands must be integers, floats, SIMD vectors, pointers or @@ -25,7 +25,7 @@ fn main() { asm!("{}", in(reg) 0i64); asm!("{}", in(reg) 0f64); - asm!("{:v}", in(vreg) SimdType(0.0, 0.0, 0.0, 0.0)); + asm!("{:v}", in(vreg) SimdType([0.0, 0.0, 0.0, 0.0])); asm!("{}", in(reg) 0 as *const u8); asm!("{}", in(reg) 0 as *mut u8); asm!("{}", in(reg) main as fn()); diff --git a/tests/ui/asm/aarch64/type-check-2.stderr b/tests/ui/asm/aarch64/type-check-2.stderr index b7723fc74d4b6..84bc5f08b4ed1 100644 --- a/tests/ui/asm/aarch64/type-check-2.stderr +++ b/tests/ui/asm/aarch64/type-check-2.stderr @@ -1,8 +1,8 @@ error: arguments for inline assembly must be copyable --> $DIR/type-check-2.rs:20:31 | -LL | asm!("{:v}", in(vreg) SimdNonCopy(0.0, 0.0, 0.0, 0.0)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | asm!("{:v}", in(vreg) SimdNonCopy([0.0, 0.0, 0.0, 0.0])); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `SimdNonCopy` does not implement the Copy trait diff --git a/tests/ui/asm/aarch64/type-check-3.rs b/tests/ui/asm/aarch64/type-check-3.rs index b64473f98c090..2f8439d0a0f9e 100644 --- a/tests/ui/asm/aarch64/type-check-3.rs +++ b/tests/ui/asm/aarch64/type-check-3.rs @@ -8,11 +8,11 @@ use std::arch::{asm, global_asm}; #[repr(simd)] #[derive(Copy, Clone)] -struct Simd256bit(f64, f64, f64, f64); +struct Simd256bit([f64; 4]); fn main() { let f64x2: float64x2_t = unsafe { std::mem::transmute(0i128) }; - let f64x4 = Simd256bit(0.0, 0.0, 0.0, 0.0); + let f64x4 = Simd256bit([0.0, 0.0, 0.0, 0.0]); unsafe { // Types must be listed in the register class. diff --git a/tests/ui/asm/aarch64/type-check-4.rs b/tests/ui/asm/aarch64/type-check-4.rs index 41eb9de5669f6..1169c3dcfa843 100644 --- a/tests/ui/asm/aarch64/type-check-4.rs +++ b/tests/ui/asm/aarch64/type-check-4.rs @@ -8,7 +8,7 @@ use std::arch::{asm, global_asm}; #[repr(simd)] #[derive(Copy, Clone)] -struct Simd256bit(f64, f64, f64, f64); +struct Simd256bit([f64; 4]); fn main() {} diff --git a/tests/ui/asm/x86_64/type-check-2.rs b/tests/ui/asm/x86_64/type-check-2.rs index ff811961462df..1650c595faebb 100644 --- a/tests/ui/asm/x86_64/type-check-2.rs +++ b/tests/ui/asm/x86_64/type-check-2.rs @@ -5,7 +5,7 @@ use std::arch::{asm, global_asm}; #[repr(simd)] -struct SimdNonCopy(f32, f32, f32, f32); +struct SimdNonCopy([f32; 4]); fn main() { unsafe { @@ -29,7 +29,7 @@ fn main() { // Register operands must be Copy - asm!("{}", in(xmm_reg) SimdNonCopy(0.0, 0.0, 0.0, 0.0)); + asm!("{}", in(xmm_reg) SimdNonCopy([0.0, 0.0, 0.0, 0.0])); //~^ ERROR arguments for inline assembly must be copyable // Register operands must be integers, floats, SIMD vectors, pointers or diff --git a/tests/ui/asm/x86_64/type-check-2.stderr b/tests/ui/asm/x86_64/type-check-2.stderr index c72e695aefb83..8b1bfa85fa2c4 100644 --- a/tests/ui/asm/x86_64/type-check-2.stderr +++ b/tests/ui/asm/x86_64/type-check-2.stderr @@ -1,8 +1,8 @@ error: arguments for inline assembly must be copyable --> $DIR/type-check-2.rs:32:32 | -LL | asm!("{}", in(xmm_reg) SimdNonCopy(0.0, 0.0, 0.0, 0.0)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | asm!("{}", in(xmm_reg) SimdNonCopy([0.0, 0.0, 0.0, 0.0])); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `SimdNonCopy` does not implement the Copy trait diff --git a/tests/ui/asm/x86_64/type-check-5.rs b/tests/ui/asm/x86_64/type-check-5.rs index b5b51f6116890..81e096a72300a 100644 --- a/tests/ui/asm/x86_64/type-check-5.rs +++ b/tests/ui/asm/x86_64/type-check-5.rs @@ -1,12 +1,9 @@ //@ only-x86_64 -#![feature(repr_simd, never_type)] +#![feature(never_type)] use std::arch::asm; -#[repr(simd)] -struct SimdNonCopy(f32, f32, f32, f32); - fn main() { unsafe { // Inputs must be initialized diff --git a/tests/ui/asm/x86_64/type-check-5.stderr b/tests/ui/asm/x86_64/type-check-5.stderr index 4fb759934636d..377e1d19f6c5c 100644 --- a/tests/ui/asm/x86_64/type-check-5.stderr +++ b/tests/ui/asm/x86_64/type-check-5.stderr @@ -1,5 +1,5 @@ error[E0381]: used binding `x` isn't initialized - --> $DIR/type-check-5.rs:15:28 + --> $DIR/type-check-5.rs:12:28 | LL | let x: u64; | - binding declared here but left uninitialized @@ -12,7 +12,7 @@ LL | let x: u64 = 42; | ++++ error[E0381]: used binding `y` isn't initialized - --> $DIR/type-check-5.rs:18:9 + --> $DIR/type-check-5.rs:15:9 | LL | let mut y: u64; | ----- binding declared here but left uninitialized @@ -25,7 +25,7 @@ LL | let mut y: u64 = 42; | ++++ error[E0596]: cannot borrow `v` as mutable, as it is not declared as mutable - --> $DIR/type-check-5.rs:24:13 + --> $DIR/type-check-5.rs:21:13 | LL | let v: Vec = vec![0, 1, 2]; | ^ not mutable diff --git a/tests/ui/consts/const-eval/simd/insert_extract.rs b/tests/ui/consts/const-eval/simd/insert_extract.rs index fc7dbd5a41c34..f4f25327aaf11 100644 --- a/tests/ui/consts/const-eval/simd/insert_extract.rs +++ b/tests/ui/consts/const-eval/simd/insert_extract.rs @@ -5,10 +5,9 @@ #![stable(feature = "foo", since = "1.3.37")] #![allow(non_camel_case_types)] -#[repr(simd)] struct i8x1(i8); -#[repr(simd)] struct u16x2(u16, u16); -// Make some of them array types to ensure those also work. -#[repr(simd)] struct i8x1_arr([i8; 1]); +// repr(simd) now only supports array types +#[repr(simd)] struct i8x1([i8; 1]); +#[repr(simd)] struct u16x2([u16; 2]); #[repr(simd)] struct f32x4([f32; 4]); extern "rust-intrinsic" { @@ -20,26 +19,18 @@ extern "rust-intrinsic" { fn main() { { - const U: i8x1 = i8x1(13); + const U: i8x1 = i8x1([13]); const V: i8x1 = unsafe { simd_insert(U, 0_u32, 42_i8) }; - const X0: i8 = V.0; - const Y0: i8 = unsafe { simd_extract(V, 0) }; - assert_eq!(X0, 42); - assert_eq!(Y0, 42); - } - { - const U: i8x1_arr = i8x1_arr([13]); - const V: i8x1_arr = unsafe { simd_insert(U, 0_u32, 42_i8) }; const X0: i8 = V.0[0]; const Y0: i8 = unsafe { simd_extract(V, 0) }; assert_eq!(X0, 42); assert_eq!(Y0, 42); } { - const U: u16x2 = u16x2(13, 14); + const U: u16x2 = u16x2([13, 14]); const V: u16x2 = unsafe { simd_insert(U, 1_u32, 42_u16) }; - const X0: u16 = V.0; - const X1: u16 = V.1; + const X0: u16 = V.0[0]; + const X1: u16 = V.0[1]; const Y0: u16 = unsafe { simd_extract(V, 0) }; const Y1: u16 = unsafe { simd_extract(V, 1) }; assert_eq!(X0, 13); diff --git a/tests/ui/error-codes/E0075.rs b/tests/ui/error-codes/E0075.rs index 7feab0a8bd7a2..2c610d9186b64 100644 --- a/tests/ui/error-codes/E0075.rs +++ b/tests/ui/error-codes/E0075.rs @@ -3,5 +3,8 @@ #[repr(simd)] struct Bad; //~ ERROR E0075 +#[repr(simd)] +struct AlsoBad([i32; 1], [i32; 1]); //~ ERROR E0075 + fn main() { } diff --git a/tests/ui/error-codes/E0075.stderr b/tests/ui/error-codes/E0075.stderr index 43e9971e3095d..0a44a2eadebd1 100644 --- a/tests/ui/error-codes/E0075.stderr +++ b/tests/ui/error-codes/E0075.stderr @@ -4,6 +4,12 @@ error[E0075]: SIMD vector cannot be empty LL | struct Bad; | ^^^^^^^^^^ -error: aborting due to 1 previous error +error[E0075]: SIMD vector cannot have multiple fields + --> $DIR/E0075.rs:7:1 + | +LL | struct AlsoBad([i32; 1], [i32; 1]); + | ^^^^^^^^^^^^^^ -------- excess field + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0075`. diff --git a/tests/ui/error-codes/E0076.rs b/tests/ui/error-codes/E0076.rs index a27072eb71e68..2c0376fe7e0bd 100644 --- a/tests/ui/error-codes/E0076.rs +++ b/tests/ui/error-codes/E0076.rs @@ -1,7 +1,7 @@ #![feature(repr_simd)] #[repr(simd)] -struct Bad(u16, u32, u32); +struct Bad(u32); //~^ ERROR E0076 fn main() { diff --git a/tests/ui/error-codes/E0076.stderr b/tests/ui/error-codes/E0076.stderr index ea3bbf094976f..8bf46cf38e338 100644 --- a/tests/ui/error-codes/E0076.stderr +++ b/tests/ui/error-codes/E0076.stderr @@ -1,8 +1,8 @@ -error[E0076]: SIMD vector should be homogeneous +error[E0076]: SIMD vector's only field must be an array --> $DIR/E0076.rs:4:1 | -LL | struct Bad(u16, u32, u32); - | ^^^^^^^^^^ SIMD elements must have the same type +LL | struct Bad(u32); + | ^^^^^^^^^^ --- not an array error: aborting due to 1 previous error diff --git a/tests/ui/error-codes/E0077.rs b/tests/ui/error-codes/E0077.rs index fa2d5e24fa3ca..2704bbeb4ea17 100644 --- a/tests/ui/error-codes/E0077.rs +++ b/tests/ui/error-codes/E0077.rs @@ -1,7 +1,7 @@ #![feature(repr_simd)] #[repr(simd)] -struct Bad(String); //~ ERROR E0077 +struct Bad([String; 2]); //~ ERROR E0077 fn main() { } diff --git a/tests/ui/error-codes/E0077.stderr b/tests/ui/error-codes/E0077.stderr index aae4b3f2c2927..063b40a147c57 100644 --- a/tests/ui/error-codes/E0077.stderr +++ b/tests/ui/error-codes/E0077.stderr @@ -1,7 +1,7 @@ error[E0077]: SIMD vector element type should be a primitive scalar (integer/float/pointer) type --> $DIR/E0077.rs:4:1 | -LL | struct Bad(String); +LL | struct Bad([String; 2]); | ^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/feature-gates/feature-gate-repr-simd.rs b/tests/ui/feature-gates/feature-gate-repr-simd.rs index c527404f57270..65ade97c7e1c3 100644 --- a/tests/ui/feature-gates/feature-gate-repr-simd.rs +++ b/tests/ui/feature-gates/feature-gate-repr-simd.rs @@ -1,9 +1,9 @@ #[repr(simd)] //~ error: SIMD types are experimental -struct Foo(u64, u64); +struct Foo([u64; 2]); #[repr(C)] //~ ERROR conflicting representation hints //~^ WARN this was previously accepted #[repr(simd)] //~ error: SIMD types are experimental -struct Bar(u64, u64); +struct Bar([u64; 2]); fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-simd-ffi.rs b/tests/ui/feature-gates/feature-gate-simd-ffi.rs index abffa4a10010d..2ee935e07ffa4 100644 --- a/tests/ui/feature-gates/feature-gate-simd-ffi.rs +++ b/tests/ui/feature-gates/feature-gate-simd-ffi.rs @@ -3,7 +3,7 @@ #[repr(simd)] #[derive(Copy, Clone)] -struct LocalSimd(u8, u8); +struct LocalSimd([u8; 2]); extern "C" { fn baz() -> LocalSimd; //~ ERROR use of SIMD type diff --git a/tests/ui/feature-gates/feature-gate-simd.rs b/tests/ui/feature-gates/feature-gate-simd.rs index de5f645e6fd0b..e7aef5a97f2ee 100644 --- a/tests/ui/feature-gates/feature-gate-simd.rs +++ b/tests/ui/feature-gates/feature-gate-simd.rs @@ -2,10 +2,7 @@ #[repr(simd)] //~ ERROR SIMD types are experimental struct RGBA { - r: f32, - g: f32, - b: f32, - a: f32 + rgba: [f32; 4], } pub fn main() {} diff --git a/tests/ui/layout/debug.rs b/tests/ui/layout/debug.rs index aeacbc784462d..91e96d78ff556 100644 --- a/tests/ui/layout/debug.rs +++ b/tests/ui/layout/debug.rs @@ -49,7 +49,7 @@ union P2 { x: (u32, u32) } //~ ERROR: layout_of #[repr(simd)] #[derive(Copy, Clone)] -struct F32x4(f32, f32, f32, f32); +struct F32x4([f32; 4]); #[rustc_layout(debug)] #[repr(packed(1))] diff --git a/tests/ui/repr/attr-usage-repr.rs b/tests/ui/repr/attr-usage-repr.rs index 8965decc37989..dc0d4f5be2e30 100644 --- a/tests/ui/repr/attr-usage-repr.rs +++ b/tests/ui/repr/attr-usage-repr.rs @@ -10,7 +10,7 @@ struct SExtern(f64, f64); struct SPacked(f64, f64); #[repr(simd)] -struct SSimd(f64, f64); +struct SSimd([f64; 2]); #[repr(i8)] //~ ERROR: attribute should be applied to an enum struct SInt(f64, f64); diff --git a/tests/ui/repr/explicit-rust-repr-conflicts.rs b/tests/ui/repr/explicit-rust-repr-conflicts.rs index 22dd12d316ac5..5278f5e6ae0cc 100644 --- a/tests/ui/repr/explicit-rust-repr-conflicts.rs +++ b/tests/ui/repr/explicit-rust-repr-conflicts.rs @@ -18,6 +18,6 @@ enum U { #[repr(Rust, simd)] //~^ ERROR conflicting representation hints //~| ERROR SIMD types are experimental and possibly buggy -struct F32x4(f32, f32, f32, f32); +struct F32x4([f32; 4]); fn main() {} diff --git a/tests/ui/simd/const-err-trumps-simd-err.rs b/tests/ui/simd/const-err-trumps-simd-err.rs index fda044344517d..fb87fe1cbca83 100644 --- a/tests/ui/simd/const-err-trumps-simd-err.rs +++ b/tests/ui/simd/const-err-trumps-simd-err.rs @@ -10,7 +10,7 @@ use std::intrinsics::simd::*; #[repr(simd)] #[allow(non_camel_case_types)] -struct int8x4_t(u8,u8,u8,u8); +struct int8x4_t([u8; 4]); fn get_elem(a: int8x4_t) -> u8 { const { assert!(LANE < 4); } // the error should be here... @@ -20,5 +20,5 @@ fn get_elem(a: int8x4_t) -> u8 { } fn main() { - get_elem::<4>(int8x4_t(0,0,0,0)); + get_elem::<4>(int8x4_t([0, 0, 0, 0])); } diff --git a/tests/ui/simd/const-err-trumps-simd-err.stderr b/tests/ui/simd/const-err-trumps-simd-err.stderr index 1e46667cf4e2a..11cbcad227ff8 100644 --- a/tests/ui/simd/const-err-trumps-simd-err.stderr +++ b/tests/ui/simd/const-err-trumps-simd-err.stderr @@ -15,8 +15,8 @@ LL | const { assert!(LANE < 4); } // the error should be here... note: the above error was encountered while instantiating `fn get_elem::<4>` --> $DIR/const-err-trumps-simd-err.rs:23:5 | -LL | get_elem::<4>(int8x4_t(0,0,0,0)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | get_elem::<4>(int8x4_t([0, 0, 0, 0])); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/simd/generics.rs b/tests/ui/simd/generics.rs index bd048b19ca886..f96a7cd75e933 100644 --- a/tests/ui/simd/generics.rs +++ b/tests/ui/simd/generics.rs @@ -6,7 +6,7 @@ use std::ops; #[repr(simd)] #[derive(Copy, Clone)] -struct f32x4(f32, f32, f32, f32); +struct f32x4([f32; 4]); #[repr(simd)] #[derive(Copy, Clone)] @@ -67,8 +67,8 @@ pub fn main() { let y = [2.0f32, 4.0f32, 6.0f32, 8.0f32]; // lame-o - let a = f32x4(1.0f32, 2.0f32, 3.0f32, 4.0f32); - let f32x4(a0, a1, a2, a3) = add(a, a); + let a = f32x4([1.0f32, 2.0f32, 3.0f32, 4.0f32]); + let f32x4([a0, a1, a2, a3]) = add(a, a); assert_eq!(a0, 2.0f32); assert_eq!(a1, 4.0f32); assert_eq!(a2, 6.0f32); diff --git a/tests/ui/simd/intrinsic/float-math-pass.rs b/tests/ui/simd/intrinsic/float-math-pass.rs index ea31e2a7c570e..9b14d410acbe3 100644 --- a/tests/ui/simd/intrinsic/float-math-pass.rs +++ b/tests/ui/simd/intrinsic/float-math-pass.rs @@ -13,7 +13,7 @@ #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -struct f32x4(pub f32, pub f32, pub f32, pub f32); +struct f32x4(pub [f32; 4]); extern "rust-intrinsic" { fn simd_fsqrt(x: T) -> T; @@ -47,19 +47,19 @@ macro_rules! assert_approx_eq { ($a:expr, $b:expr) => ({ let a = $a; let b = $b; - assert_approx_eq_f32!(a.0, b.0); - assert_approx_eq_f32!(a.1, b.1); - assert_approx_eq_f32!(a.2, b.2); - assert_approx_eq_f32!(a.3, b.3); + assert_approx_eq_f32!(a.0[0], b.0[0]); + assert_approx_eq_f32!(a.0[1], b.0[1]); + assert_approx_eq_f32!(a.0[2], b.0[2]); + assert_approx_eq_f32!(a.0[3], b.0[3]); }) } fn main() { - let x = f32x4(1.0, 1.0, 1.0, 1.0); - let y = f32x4(-1.0, -1.0, -1.0, -1.0); - let z = f32x4(0.0, 0.0, 0.0, 0.0); + let x = f32x4([1.0, 1.0, 1.0, 1.0]); + let y = f32x4([-1.0, -1.0, -1.0, -1.0]); + let z = f32x4([0.0, 0.0, 0.0, 0.0]); - let h = f32x4(0.5, 0.5, 0.5, 0.5); + let h = f32x4([0.5, 0.5, 0.5, 0.5]); unsafe { let r = simd_fabs(y); diff --git a/tests/ui/simd/intrinsic/float-minmax-pass.rs b/tests/ui/simd/intrinsic/float-minmax-pass.rs index d6cbcd4e05a63..00c0d8cea3fa5 100644 --- a/tests/ui/simd/intrinsic/float-minmax-pass.rs +++ b/tests/ui/simd/intrinsic/float-minmax-pass.rs @@ -8,13 +8,13 @@ #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -struct f32x4(pub f32, pub f32, pub f32, pub f32); +struct f32x4(pub [f32; 4]); use std::intrinsics::simd::*; fn main() { - let x = f32x4(1.0, 2.0, 3.0, 4.0); - let y = f32x4(2.0, 1.0, 4.0, 3.0); + let x = f32x4([1.0, 2.0, 3.0, 4.0]); + let y = f32x4([2.0, 1.0, 4.0, 3.0]); #[cfg(not(any(target_arch = "mips", target_arch = "mips64")))] let nan = f32::NAN; @@ -23,13 +23,13 @@ fn main() { #[cfg(any(target_arch = "mips", target_arch = "mips64"))] let nan = f32::from_bits(f32::NAN.to_bits() - 1); - let n = f32x4(nan, nan, nan, nan); + let n = f32x4([nan, nan, nan, nan]); unsafe { let min0 = simd_fmin(x, y); let min1 = simd_fmin(y, x); assert_eq!(min0, min1); - let e = f32x4(1.0, 1.0, 3.0, 3.0); + let e = f32x4([1.0, 1.0, 3.0, 3.0]); assert_eq!(min0, e); let minn = simd_fmin(x, n); assert_eq!(minn, x); @@ -39,7 +39,7 @@ fn main() { let max0 = simd_fmax(x, y); let max1 = simd_fmax(y, x); assert_eq!(max0, max1); - let e = f32x4(2.0, 2.0, 4.0, 4.0); + let e = f32x4([2.0, 2.0, 4.0, 4.0]); assert_eq!(max0, e); let maxn = simd_fmax(x, n); assert_eq!(maxn, x); diff --git a/tests/ui/simd/intrinsic/generic-arithmetic-2.rs b/tests/ui/simd/intrinsic/generic-arithmetic-2.rs index fc3087cbf7542..663bcdf198193 100644 --- a/tests/ui/simd/intrinsic/generic-arithmetic-2.rs +++ b/tests/ui/simd/intrinsic/generic-arithmetic-2.rs @@ -4,15 +4,15 @@ #![allow(non_camel_case_types)] #[repr(simd)] #[derive(Copy, Clone)] -pub struct i32x4(pub i32, pub i32, pub i32, pub i32); +pub struct i32x4(pub [i32; 4]); #[repr(simd)] #[derive(Copy, Clone)] -pub struct u32x4(pub u32, pub u32, pub u32, pub u32); +pub struct u32x4(pub [u32; 4]); #[repr(simd)] #[derive(Copy, Clone)] -pub struct f32x4(pub f32, pub f32, pub f32, pub f32); +pub struct f32x4(pub [f32; 4]); extern "rust-intrinsic" { fn simd_add(x: T, y: T) -> T; @@ -35,9 +35,9 @@ extern "rust-intrinsic" { } fn main() { - let x = i32x4(0, 0, 0, 0); - let y = u32x4(0, 0, 0, 0); - let z = f32x4(0.0, 0.0, 0.0, 0.0); + let x = i32x4([0, 0, 0, 0]); + let y = u32x4([0, 0, 0, 0]); + let z = f32x4([0.0, 0.0, 0.0, 0.0]); unsafe { simd_add(x, x); diff --git a/tests/ui/simd/intrinsic/generic-arithmetic-pass.rs b/tests/ui/simd/intrinsic/generic-arithmetic-pass.rs index 60dfa62741488..e4eb2a9da2718 100644 --- a/tests/ui/simd/intrinsic/generic-arithmetic-pass.rs +++ b/tests/ui/simd/intrinsic/generic-arithmetic-pass.rs @@ -5,7 +5,7 @@ #[repr(simd)] #[derive(Copy, Clone)] -struct i32x4(pub i32, pub i32, pub i32, pub i32); +struct i32x4(pub [i32; 4]); #[repr(simd)] #[derive(Copy, Clone)] @@ -13,17 +13,9 @@ struct U32([u32; N]); #[repr(simd)] #[derive(Copy, Clone)] -struct f32x4(pub f32, pub f32, pub f32, pub f32); +struct f32x4(pub [f32; 4]); macro_rules! all_eq { - ($a: expr, $b: expr) => {{ - let a = $a; - let b = $b; - assert!(a.0 == b.0 && a.1 == b.1 && a.2 == b.2 && a.3 == b.3); - }}; -} - -macro_rules! all_eq_ { ($a: expr, $b: expr) => {{ let a = $a; let b = $b; @@ -52,112 +44,112 @@ extern "rust-intrinsic" { } fn main() { - let x1 = i32x4(1, 2, 3, 4); + let x1 = i32x4([1, 2, 3, 4]); let y1 = U32::<4>([1, 2, 3, 4]); - let z1 = f32x4(1.0, 2.0, 3.0, 4.0); - let x2 = i32x4(2, 3, 4, 5); + let z1 = f32x4([1.0, 2.0, 3.0, 4.0]); + let x2 = i32x4([2, 3, 4, 5]); let y2 = U32::<4>([2, 3, 4, 5]); - let z2 = f32x4(2.0, 3.0, 4.0, 5.0); - let x3 = i32x4(0, i32::MAX, i32::MIN, -1_i32); + let z2 = f32x4([2.0, 3.0, 4.0, 5.0]); + let x3 = i32x4([0, i32::MAX, i32::MIN, -1_i32]); let y3 = U32::<4>([0, i32::MAX as _, i32::MIN as _, -1_i32 as _]); unsafe { - all_eq!(simd_add(x1, x2), i32x4(3, 5, 7, 9)); - all_eq!(simd_add(x2, x1), i32x4(3, 5, 7, 9)); - all_eq_!(simd_add(y1, y2), U32::<4>([3, 5, 7, 9])); - all_eq_!(simd_add(y2, y1), U32::<4>([3, 5, 7, 9])); - all_eq!(simd_add(z1, z2), f32x4(3.0, 5.0, 7.0, 9.0)); - all_eq!(simd_add(z2, z1), f32x4(3.0, 5.0, 7.0, 9.0)); - - all_eq!(simd_mul(x1, x2), i32x4(2, 6, 12, 20)); - all_eq!(simd_mul(x2, x1), i32x4(2, 6, 12, 20)); - all_eq_!(simd_mul(y1, y2), U32::<4>([2, 6, 12, 20])); - all_eq_!(simd_mul(y2, y1), U32::<4>([2, 6, 12, 20])); - all_eq!(simd_mul(z1, z2), f32x4(2.0, 6.0, 12.0, 20.0)); - all_eq!(simd_mul(z2, z1), f32x4(2.0, 6.0, 12.0, 20.0)); - - all_eq!(simd_sub(x2, x1), i32x4(1, 1, 1, 1)); - all_eq!(simd_sub(x1, x2), i32x4(-1, -1, -1, -1)); - all_eq_!(simd_sub(y2, y1), U32::<4>([1, 1, 1, 1])); - all_eq_!(simd_sub(y1, y2), U32::<4>([!0, !0, !0, !0])); - all_eq!(simd_sub(z2, z1), f32x4(1.0, 1.0, 1.0, 1.0)); - all_eq!(simd_sub(z1, z2), f32x4(-1.0, -1.0, -1.0, -1.0)); - - all_eq!(simd_div(x1, x1), i32x4(1, 1, 1, 1)); - all_eq!(simd_div(i32x4(2, 4, 6, 8), i32x4(2, 2, 2, 2)), x1); - all_eq_!(simd_div(y1, y1), U32::<4>([1, 1, 1, 1])); - all_eq_!(simd_div(U32::<4>([2, 4, 6, 8]), U32::<4>([2, 2, 2, 2])), y1); - all_eq!(simd_div(z1, z1), f32x4(1.0, 1.0, 1.0, 1.0)); - all_eq!(simd_div(z1, z2), f32x4(1.0 / 2.0, 2.0 / 3.0, 3.0 / 4.0, 4.0 / 5.0)); - all_eq!(simd_div(z2, z1), f32x4(2.0 / 1.0, 3.0 / 2.0, 4.0 / 3.0, 5.0 / 4.0)); - - all_eq!(simd_rem(x1, x1), i32x4(0, 0, 0, 0)); - all_eq!(simd_rem(x2, x1), i32x4(0, 1, 1, 1)); - all_eq_!(simd_rem(y1, y1), U32::<4>([0, 0, 0, 0])); - all_eq_!(simd_rem(y2, y1), U32::<4>([0, 1, 1, 1])); - all_eq!(simd_rem(z1, z1), f32x4(0.0, 0.0, 0.0, 0.0)); + all_eq!(simd_add(x1, x2), i32x4([3, 5, 7, 9])); + all_eq!(simd_add(x2, x1), i32x4([3, 5, 7, 9])); + all_eq!(simd_add(y1, y2), U32::<4>([3, 5, 7, 9])); + all_eq!(simd_add(y2, y1), U32::<4>([3, 5, 7, 9])); + all_eq!(simd_add(z1, z2), f32x4([3.0, 5.0, 7.0, 9.0])); + all_eq!(simd_add(z2, z1), f32x4([3.0, 5.0, 7.0, 9.0])); + + all_eq!(simd_mul(x1, x2), i32x4([2, 6, 12, 20])); + all_eq!(simd_mul(x2, x1), i32x4([2, 6, 12, 20])); + all_eq!(simd_mul(y1, y2), U32::<4>([2, 6, 12, 20])); + all_eq!(simd_mul(y2, y1), U32::<4>([2, 6, 12, 20])); + all_eq!(simd_mul(z1, z2), f32x4([2.0, 6.0, 12.0, 20.0])); + all_eq!(simd_mul(z2, z1), f32x4([2.0, 6.0, 12.0, 20.0])); + + all_eq!(simd_sub(x2, x1), i32x4([1, 1, 1, 1])); + all_eq!(simd_sub(x1, x2), i32x4([-1, -1, -1, -1])); + all_eq!(simd_sub(y2, y1), U32::<4>([1, 1, 1, 1])); + all_eq!(simd_sub(y1, y2), U32::<4>([!0, !0, !0, !0])); + all_eq!(simd_sub(z2, z1), f32x4([1.0, 1.0, 1.0, 1.0])); + all_eq!(simd_sub(z1, z2), f32x4([-1.0, -1.0, -1.0, -1.0])); + + all_eq!(simd_div(x1, x1), i32x4([1, 1, 1, 1])); + all_eq!(simd_div(i32x4([2, 4, 6, 8]), i32x4([2, 2, 2, 2])), x1); + all_eq!(simd_div(y1, y1), U32::<4>([1, 1, 1, 1])); + all_eq!(simd_div(U32::<4>([2, 4, 6, 8]), U32::<4>([2, 2, 2, 2])), y1); + all_eq!(simd_div(z1, z1), f32x4([1.0, 1.0, 1.0, 1.0])); + all_eq!(simd_div(z1, z2), f32x4([1.0 / 2.0, 2.0 / 3.0, 3.0 / 4.0, 4.0 / 5.0])); + all_eq!(simd_div(z2, z1), f32x4([2.0 / 1.0, 3.0 / 2.0, 4.0 / 3.0, 5.0 / 4.0])); + + all_eq!(simd_rem(x1, x1), i32x4([0, 0, 0, 0])); + all_eq!(simd_rem(x2, x1), i32x4([0, 1, 1, 1])); + all_eq!(simd_rem(y1, y1), U32::<4>([0, 0, 0, 0])); + all_eq!(simd_rem(y2, y1), U32::<4>([0, 1, 1, 1])); + all_eq!(simd_rem(z1, z1), f32x4([0.0, 0.0, 0.0, 0.0])); all_eq!(simd_rem(z1, z2), z1); - all_eq!(simd_rem(z2, z1), f32x4(0.0, 1.0, 1.0, 1.0)); + all_eq!(simd_rem(z2, z1), f32x4([0.0, 1.0, 1.0, 1.0])); - all_eq!(simd_shl(x1, x2), i32x4(1 << 2, 2 << 3, 3 << 4, 4 << 5)); - all_eq!(simd_shl(x2, x1), i32x4(2 << 1, 3 << 2, 4 << 3, 5 << 4)); - all_eq_!(simd_shl(y1, y2), U32::<4>([1 << 2, 2 << 3, 3 << 4, 4 << 5])); - all_eq_!(simd_shl(y2, y1), U32::<4>([2 << 1, 3 << 2, 4 << 3, 5 << 4])); + all_eq!(simd_shl(x1, x2), i32x4([1 << 2, 2 << 3, 3 << 4, 4 << 5])); + all_eq!(simd_shl(x2, x1), i32x4([2 << 1, 3 << 2, 4 << 3, 5 << 4])); + all_eq!(simd_shl(y1, y2), U32::<4>([1 << 2, 2 << 3, 3 << 4, 4 << 5])); + all_eq!(simd_shl(y2, y1), U32::<4>([2 << 1, 3 << 2, 4 << 3, 5 << 4])); // test right-shift by assuming left-shift is correct all_eq!(simd_shr(simd_shl(x1, x2), x2), x1); all_eq!(simd_shr(simd_shl(x2, x1), x1), x2); - all_eq_!(simd_shr(simd_shl(y1, y2), y2), y1); - all_eq_!(simd_shr(simd_shl(y2, y1), y1), y2); + all_eq!(simd_shr(simd_shl(y1, y2), y2), y1); + all_eq!(simd_shr(simd_shl(y2, y1), y1), y2); // ensure we get logical vs. arithmetic shifts correct let (a, b, c, d) = (-12, -123, -1234, -12345); - all_eq!(simd_shr(i32x4(a, b, c, d), x1), i32x4(a >> 1, b >> 2, c >> 3, d >> 4)); - all_eq_!( + all_eq!(simd_shr(i32x4([a, b, c, d]), x1), i32x4([a >> 1, b >> 2, c >> 3, d >> 4])); + all_eq!( simd_shr(U32::<4>([a as u32, b as u32, c as u32, d as u32]), y1), U32::<4>([(a as u32) >> 1, (b as u32) >> 2, (c as u32) >> 3, (d as u32) >> 4]) ); - all_eq!(simd_and(x1, x2), i32x4(0, 2, 0, 4)); - all_eq!(simd_and(x2, x1), i32x4(0, 2, 0, 4)); - all_eq_!(simd_and(y1, y2), U32::<4>([0, 2, 0, 4])); - all_eq_!(simd_and(y2, y1), U32::<4>([0, 2, 0, 4])); + all_eq!(simd_and(x1, x2), i32x4([0, 2, 0, 4])); + all_eq!(simd_and(x2, x1), i32x4([0, 2, 0, 4])); + all_eq!(simd_and(y1, y2), U32::<4>([0, 2, 0, 4])); + all_eq!(simd_and(y2, y1), U32::<4>([0, 2, 0, 4])); - all_eq!(simd_or(x1, x2), i32x4(3, 3, 7, 5)); - all_eq!(simd_or(x2, x1), i32x4(3, 3, 7, 5)); - all_eq_!(simd_or(y1, y2), U32::<4>([3, 3, 7, 5])); - all_eq_!(simd_or(y2, y1), U32::<4>([3, 3, 7, 5])); + all_eq!(simd_or(x1, x2), i32x4([3, 3, 7, 5])); + all_eq!(simd_or(x2, x1), i32x4([3, 3, 7, 5])); + all_eq!(simd_or(y1, y2), U32::<4>([3, 3, 7, 5])); + all_eq!(simd_or(y2, y1), U32::<4>([3, 3, 7, 5])); - all_eq!(simd_xor(x1, x2), i32x4(3, 1, 7, 1)); - all_eq!(simd_xor(x2, x1), i32x4(3, 1, 7, 1)); - all_eq_!(simd_xor(y1, y2), U32::<4>([3, 1, 7, 1])); - all_eq_!(simd_xor(y2, y1), U32::<4>([3, 1, 7, 1])); + all_eq!(simd_xor(x1, x2), i32x4([3, 1, 7, 1])); + all_eq!(simd_xor(x2, x1), i32x4([3, 1, 7, 1])); + all_eq!(simd_xor(y1, y2), U32::<4>([3, 1, 7, 1])); + all_eq!(simd_xor(y2, y1), U32::<4>([3, 1, 7, 1])); - all_eq!(simd_neg(x1), i32x4(-1, -2, -3, -4)); - all_eq!(simd_neg(x2), i32x4(-2, -3, -4, -5)); - all_eq!(simd_neg(z1), f32x4(-1.0, -2.0, -3.0, -4.0)); - all_eq!(simd_neg(z2), f32x4(-2.0, -3.0, -4.0, -5.0)); + all_eq!(simd_neg(x1), i32x4([-1, -2, -3, -4])); + all_eq!(simd_neg(x2), i32x4([-2, -3, -4, -5])); + all_eq!(simd_neg(z1), f32x4([-1.0, -2.0, -3.0, -4.0])); + all_eq!(simd_neg(z2), f32x4([-2.0, -3.0, -4.0, -5.0])); - all_eq!(simd_bswap(x1), i32x4(0x01000000, 0x02000000, 0x03000000, 0x04000000)); - all_eq_!(simd_bswap(y1), U32::<4>([0x01000000, 0x02000000, 0x03000000, 0x04000000])); + all_eq!(simd_bswap(x1), i32x4([0x01000000, 0x02000000, 0x03000000, 0x04000000])); + all_eq!(simd_bswap(y1), U32::<4>([0x01000000, 0x02000000, 0x03000000, 0x04000000])); all_eq!( simd_bitreverse(x1), - i32x4(0x80000000u32 as i32, 0x40000000, 0xc0000000u32 as i32, 0x20000000) + i32x4([0x80000000u32 as i32, 0x40000000, 0xc0000000u32 as i32, 0x20000000]) ); - all_eq_!(simd_bitreverse(y1), U32::<4>([0x80000000, 0x40000000, 0xc0000000, 0x20000000])); + all_eq!(simd_bitreverse(y1), U32::<4>([0x80000000, 0x40000000, 0xc0000000, 0x20000000])); - all_eq!(simd_ctlz(x1), i32x4(31, 30, 30, 29)); - all_eq_!(simd_ctlz(y1), U32::<4>([31, 30, 30, 29])); + all_eq!(simd_ctlz(x1), i32x4([31, 30, 30, 29])); + all_eq!(simd_ctlz(y1), U32::<4>([31, 30, 30, 29])); - all_eq!(simd_ctpop(x1), i32x4(1, 1, 2, 1)); - all_eq_!(simd_ctpop(y1), U32::<4>([1, 1, 2, 1])); - all_eq!(simd_ctpop(x2), i32x4(1, 2, 1, 2)); - all_eq_!(simd_ctpop(y2), U32::<4>([1, 2, 1, 2])); - all_eq!(simd_ctpop(x3), i32x4(0, 31, 1, 32)); - all_eq_!(simd_ctpop(y3), U32::<4>([0, 31, 1, 32])); + all_eq!(simd_ctpop(x1), i32x4([1, 1, 2, 1])); + all_eq!(simd_ctpop(y1), U32::<4>([1, 1, 2, 1])); + all_eq!(simd_ctpop(x2), i32x4([1, 2, 1, 2])); + all_eq!(simd_ctpop(y2), U32::<4>([1, 2, 1, 2])); + all_eq!(simd_ctpop(x3), i32x4([0, 31, 1, 32])); + all_eq!(simd_ctpop(y3), U32::<4>([0, 31, 1, 32])); - all_eq!(simd_cttz(x1), i32x4(0, 1, 0, 2)); - all_eq_!(simd_cttz(y1), U32::<4>([0, 1, 0, 2])); + all_eq!(simd_cttz(x1), i32x4([0, 1, 0, 2])); + all_eq!(simd_cttz(y1), U32::<4>([0, 1, 0, 2])); } } diff --git a/tests/ui/simd/intrinsic/generic-arithmetic-saturating-2.rs b/tests/ui/simd/intrinsic/generic-arithmetic-saturating-2.rs index 36be8cc62a8c8..ec6ac78df1a7a 100644 --- a/tests/ui/simd/intrinsic/generic-arithmetic-saturating-2.rs +++ b/tests/ui/simd/intrinsic/generic-arithmetic-saturating-2.rs @@ -4,15 +4,15 @@ #![allow(non_camel_case_types)] #[repr(simd)] #[derive(Copy, Clone)] -pub struct i32x4(pub i32, pub i32, pub i32, pub i32); +pub struct i32x4(pub [i32; 4]); #[repr(simd)] #[derive(Copy, Clone)] -pub struct x4(pub T, pub T, pub T, pub T); +pub struct x4(pub [T; 4]); #[repr(simd)] #[derive(Copy, Clone)] -pub struct f32x4(pub f32, pub f32, pub f32, pub f32); +pub struct f32x4(pub [f32; 4]); extern "rust-intrinsic" { fn simd_saturating_add(x: T, y: T) -> T; @@ -20,9 +20,9 @@ extern "rust-intrinsic" { } fn main() { - let x = i32x4(0, 0, 0, 0); - let y = x4(0_usize, 0, 0, 0); - let z = f32x4(0.0, 0.0, 0.0, 0.0); + let x = i32x4([0, 0, 0, 0]); + let y = x4([0_usize, 0, 0, 0]); + let z = f32x4([0.0, 0.0, 0.0, 0.0]); unsafe { simd_saturating_add(x, x); diff --git a/tests/ui/simd/intrinsic/generic-arithmetic-saturating-pass.rs b/tests/ui/simd/intrinsic/generic-arithmetic-saturating-pass.rs index deee9c2ac41e6..57bda5c2d624e 100644 --- a/tests/ui/simd/intrinsic/generic-arithmetic-saturating-pass.rs +++ b/tests/ui/simd/intrinsic/generic-arithmetic-saturating-pass.rs @@ -6,7 +6,7 @@ #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -struct u32x4(pub u32, pub u32, pub u32, pub u32); +struct u32x4(pub [u32; 4]); #[repr(simd)] #[derive(Copy, Clone)] @@ -22,11 +22,11 @@ fn main() { { const M: u32 = u32::MAX; - let a = u32x4(1, 2, 3, 4); - let b = u32x4(2, 4, 6, 8); - let m = u32x4(M, M, M, M); - let m1 = u32x4(M - 1, M - 1, M - 1, M - 1); - let z = u32x4(0, 0, 0, 0); + let a = u32x4([1, 2, 3, 4]); + let b = u32x4([2, 4, 6, 8]); + let m = u32x4([M, M, M, M]); + let m1 = u32x4([M - 1, M - 1, M - 1, M - 1]); + let z = u32x4([0, 0, 0, 0]); unsafe { assert_eq!(simd_saturating_add(z, z), z); diff --git a/tests/ui/simd/intrinsic/generic-bitmask-pass.rs b/tests/ui/simd/intrinsic/generic-bitmask-pass.rs index 5c6a07876e35e..db10020bd469b 100644 --- a/tests/ui/simd/intrinsic/generic-bitmask-pass.rs +++ b/tests/ui/simd/intrinsic/generic-bitmask-pass.rs @@ -11,36 +11,36 @@ #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -struct u32x4(pub u32, pub u32, pub u32, pub u32); +struct u32x4(pub [u32; 4]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -struct u8x4(pub u8, pub u8, pub u8, pub u8); +struct u8x4(pub [u8; 4]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -struct Tx4(pub T, pub T, pub T, pub T); +struct Tx4(pub [T; 4]); extern "rust-intrinsic" { fn simd_bitmask(x: T) -> U; } fn main() { - let z = u32x4(0, 0, 0, 0); + let z = u32x4([0, 0, 0, 0]); let ez = 0_u8; - let o = u32x4(!0, !0, !0, !0); + let o = u32x4([!0, !0, !0, !0]); let eo = 0b_1111_u8; - let m0 = u32x4(!0, 0, !0, 0); + let m0 = u32x4([!0, 0, !0, 0]); let e0 = 0b_0000_0101_u8; // Check that the MSB is extracted: - let m = u8x4(0b_1000_0000, 0b_0100_0001, 0b_1100_0001, 0b_1111_1111); + let m = u8x4([0b_1000_0000, 0b_0100_0001, 0b_1100_0001, 0b_1111_1111]); let e = 0b_1101; // Check usize / isize - let msize: Tx4 = Tx4(usize::MAX, 0, usize::MAX, usize::MAX); + let msize: Tx4 = Tx4([usize::MAX, 0, usize::MAX, usize::MAX]); unsafe { let r: u8 = simd_bitmask(z); diff --git a/tests/ui/simd/intrinsic/generic-cast.rs b/tests/ui/simd/intrinsic/generic-cast.rs index f3fdbe3403f79..33978a2f7395d 100644 --- a/tests/ui/simd/intrinsic/generic-cast.rs +++ b/tests/ui/simd/intrinsic/generic-cast.rs @@ -5,22 +5,20 @@ #[repr(simd)] #[derive(Copy, Clone)] #[allow(non_camel_case_types)] -struct i32x4(i32, i32, i32, i32); +struct i32x4([i32; 4]); #[repr(simd)] #[derive(Copy, Clone)] #[allow(non_camel_case_types)] -struct i32x8(i32, i32, i32, i32, - i32, i32, i32, i32); +struct i32x8([i32; 8]); #[repr(simd)] #[derive(Copy, Clone)] #[allow(non_camel_case_types)] -struct f32x4(f32, f32, f32, f32); +struct f32x4([f32; 4]); #[repr(simd)] #[derive(Copy, Clone)] #[allow(non_camel_case_types)] -struct f32x8(f32, f32, f32, f32, - f32, f32, f32, f32); +struct f32x8([f32; 8]); extern "rust-intrinsic" { @@ -28,7 +26,7 @@ extern "rust-intrinsic" { } fn main() { - let x = i32x4(0, 0, 0, 0); + let x = i32x4([0, 0, 0, 0]); unsafe { simd_cast::(0); diff --git a/tests/ui/simd/intrinsic/generic-cast.stderr b/tests/ui/simd/intrinsic/generic-cast.stderr index 2226bbbe1bd55..2f9d44037afb5 100644 --- a/tests/ui/simd/intrinsic/generic-cast.stderr +++ b/tests/ui/simd/intrinsic/generic-cast.stderr @@ -1,23 +1,23 @@ error[E0511]: invalid monomorphization of `simd_cast` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/generic-cast.rs:34:9 + --> $DIR/generic-cast.rs:32:9 | LL | simd_cast::(0); | ^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_cast` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/generic-cast.rs:36:9 + --> $DIR/generic-cast.rs:34:9 | LL | simd_cast::(0); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_cast` intrinsic: expected SIMD return type, found non-SIMD `i32` - --> $DIR/generic-cast.rs:38:9 + --> $DIR/generic-cast.rs:36:9 | LL | simd_cast::(x); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_cast` intrinsic: expected return type with length 4 (same as input type `i32x4`), found `i32x8` with length 8 - --> $DIR/generic-cast.rs:40:9 + --> $DIR/generic-cast.rs:38:9 | LL | simd_cast::<_, i32x8>(x); | ^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/simd/intrinsic/generic-comparison-pass.rs b/tests/ui/simd/intrinsic/generic-comparison-pass.rs index a4df836b6f82a..a4d84a4c53482 100644 --- a/tests/ui/simd/intrinsic/generic-comparison-pass.rs +++ b/tests/ui/simd/intrinsic/generic-comparison-pass.rs @@ -6,13 +6,13 @@ #[repr(simd)] #[derive(Copy, Clone)] -struct i32x4(i32, i32, i32, i32); +struct i32x4([i32; 4]); #[repr(simd)] #[derive(Copy, Clone)] -struct u32x4(pub u32, pub u32, pub u32, pub u32); +struct u32x4(pub [u32; 4]); #[repr(simd)] #[derive(Copy, Clone)] -struct f32x4(pub f32, pub f32, pub f32, pub f32); +struct f32x4(pub [f32; 4]); extern "rust-intrinsic" { fn simd_eq(x: T, y: T) -> U; @@ -29,10 +29,10 @@ macro_rules! cmp { let rhs = $rhs; let e: u32x4 = concat_idents!(simd_, $method)($lhs, $rhs); // assume the scalar version is correct/the behaviour we want. - assert!((e.0 != 0) == lhs.0 .$method(&rhs.0)); - assert!((e.1 != 0) == lhs.1 .$method(&rhs.1)); - assert!((e.2 != 0) == lhs.2 .$method(&rhs.2)); - assert!((e.3 != 0) == lhs.3 .$method(&rhs.3)); + assert!((e.0[0] != 0) == lhs.0[0] .$method(&rhs.0[0])); + assert!((e.0[1] != 0) == lhs.0[1] .$method(&rhs.0[1])); + assert!((e.0[2] != 0) == lhs.0[2] .$method(&rhs.0[2])); + assert!((e.0[3] != 0) == lhs.0[3] .$method(&rhs.0[3])); }} } macro_rules! tests { @@ -61,17 +61,17 @@ macro_rules! tests { fn main() { // 13 vs. -100 tests that we get signed vs. unsigned comparisons // correct (i32: 13 > -100, u32: 13 < -100). let i1 = i32x4(10, -11, 12, 13); - let i1 = i32x4(10, -11, 12, 13); - let i2 = i32x4(5, -5, 20, -100); - let i3 = i32x4(10, -11, 20, -100); + let i1 = i32x4([10, -11, 12, 13]); + let i2 = i32x4([5, -5, 20, -100]); + let i3 = i32x4([10, -11, 20, -100]); - let u1 = u32x4(10, !11+1, 12, 13); - let u2 = u32x4(5, !5+1, 20, !100+1); - let u3 = u32x4(10, !11+1, 20, !100+1); + let u1 = u32x4([10, !11+1, 12, 13]); + let u2 = u32x4([5, !5+1, 20, !100+1]); + let u3 = u32x4([10, !11+1, 20, !100+1]); - let f1 = f32x4(10.0, -11.0, 12.0, 13.0); - let f2 = f32x4(5.0, -5.0, 20.0, -100.0); - let f3 = f32x4(10.0, -11.0, 20.0, -100.0); + let f1 = f32x4([10.0, -11.0, 12.0, 13.0]); + let f2 = f32x4([5.0, -5.0, 20.0, -100.0]); + let f3 = f32x4([10.0, -11.0, 20.0, -100.0]); unsafe { tests! { @@ -92,7 +92,7 @@ fn main() { // NAN comparisons are special: // -11 (*) 13 // -5 -100 (*) - let f4 = f32x4(f32::NAN, f1.1, f32::NAN, f2.3); + let f4 = f32x4([f32::NAN, f1.0[1], f32::NAN, f2.0[3]]); unsafe { tests! { diff --git a/tests/ui/simd/intrinsic/generic-comparison.rs b/tests/ui/simd/intrinsic/generic-comparison.rs index bb2720f615fef..f7f0655f3d244 100644 --- a/tests/ui/simd/intrinsic/generic-comparison.rs +++ b/tests/ui/simd/intrinsic/generic-comparison.rs @@ -5,12 +5,11 @@ #[repr(simd)] #[derive(Copy, Clone)] #[allow(non_camel_case_types)] -struct i32x4(i32, i32, i32, i32); +struct i32x4([i32; 4]); #[repr(simd)] #[derive(Copy, Clone)] #[allow(non_camel_case_types)] -struct i16x8(i16, i16, i16, i16, - i16, i16, i16, i16); +struct i16x8([i16; 8]); extern "rust-intrinsic" { fn simd_eq(x: T, y: T) -> U; @@ -22,7 +21,7 @@ extern "rust-intrinsic" { } fn main() { - let x = i32x4(0, 0, 0, 0); + let x = i32x4([0, 0, 0, 0]); unsafe { simd_eq::(0, 0); diff --git a/tests/ui/simd/intrinsic/generic-comparison.stderr b/tests/ui/simd/intrinsic/generic-comparison.stderr index 0eae2688bced0..ac4d491882742 100644 --- a/tests/ui/simd/intrinsic/generic-comparison.stderr +++ b/tests/ui/simd/intrinsic/generic-comparison.stderr @@ -1,107 +1,107 @@ error[E0511]: invalid monomorphization of `simd_eq` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/generic-comparison.rs:28:9 + --> $DIR/generic-comparison.rs:27:9 | LL | simd_eq::(0, 0); | ^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_ne` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/generic-comparison.rs:30:9 + --> $DIR/generic-comparison.rs:29:9 | LL | simd_ne::(0, 0); | ^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_lt` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/generic-comparison.rs:32:9 + --> $DIR/generic-comparison.rs:31:9 | LL | simd_lt::(0, 0); | ^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_le` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/generic-comparison.rs:34:9 + --> $DIR/generic-comparison.rs:33:9 | LL | simd_le::(0, 0); | ^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_gt` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/generic-comparison.rs:36:9 + --> $DIR/generic-comparison.rs:35:9 | LL | simd_gt::(0, 0); | ^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_ge` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/generic-comparison.rs:38:9 + --> $DIR/generic-comparison.rs:37:9 | LL | simd_ge::(0, 0); | ^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_eq` intrinsic: expected SIMD return type, found non-SIMD `i32` - --> $DIR/generic-comparison.rs:41:9 + --> $DIR/generic-comparison.rs:40:9 | LL | simd_eq::<_, i32>(x, x); | ^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_ne` intrinsic: expected SIMD return type, found non-SIMD `i32` - --> $DIR/generic-comparison.rs:43:9 + --> $DIR/generic-comparison.rs:42:9 | LL | simd_ne::<_, i32>(x, x); | ^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_lt` intrinsic: expected SIMD return type, found non-SIMD `i32` - --> $DIR/generic-comparison.rs:45:9 + --> $DIR/generic-comparison.rs:44:9 | LL | simd_lt::<_, i32>(x, x); | ^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_le` intrinsic: expected SIMD return type, found non-SIMD `i32` - --> $DIR/generic-comparison.rs:47:9 + --> $DIR/generic-comparison.rs:46:9 | LL | simd_le::<_, i32>(x, x); | ^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_gt` intrinsic: expected SIMD return type, found non-SIMD `i32` - --> $DIR/generic-comparison.rs:49:9 + --> $DIR/generic-comparison.rs:48:9 | LL | simd_gt::<_, i32>(x, x); | ^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_ge` intrinsic: expected SIMD return type, found non-SIMD `i32` - --> $DIR/generic-comparison.rs:51:9 + --> $DIR/generic-comparison.rs:50:9 | LL | simd_ge::<_, i32>(x, x); | ^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_eq` intrinsic: expected return type with length 4 (same as input type `i32x4`), found `i16x8` with length 8 - --> $DIR/generic-comparison.rs:54:9 + --> $DIR/generic-comparison.rs:53:9 | LL | simd_eq::<_, i16x8>(x, x); | ^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_ne` intrinsic: expected return type with length 4 (same as input type `i32x4`), found `i16x8` with length 8 - --> $DIR/generic-comparison.rs:56:9 + --> $DIR/generic-comparison.rs:55:9 | LL | simd_ne::<_, i16x8>(x, x); | ^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_lt` intrinsic: expected return type with length 4 (same as input type `i32x4`), found `i16x8` with length 8 - --> $DIR/generic-comparison.rs:58:9 + --> $DIR/generic-comparison.rs:57:9 | LL | simd_lt::<_, i16x8>(x, x); | ^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_le` intrinsic: expected return type with length 4 (same as input type `i32x4`), found `i16x8` with length 8 - --> $DIR/generic-comparison.rs:60:9 + --> $DIR/generic-comparison.rs:59:9 | LL | simd_le::<_, i16x8>(x, x); | ^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_gt` intrinsic: expected return type with length 4 (same as input type `i32x4`), found `i16x8` with length 8 - --> $DIR/generic-comparison.rs:62:9 + --> $DIR/generic-comparison.rs:61:9 | LL | simd_gt::<_, i16x8>(x, x); | ^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_ge` intrinsic: expected return type with length 4 (same as input type `i32x4`), found `i16x8` with length 8 - --> $DIR/generic-comparison.rs:64:9 + --> $DIR/generic-comparison.rs:63:9 | LL | simd_ge::<_, i16x8>(x, x); | ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/simd/intrinsic/generic-elements-pass.rs b/tests/ui/simd/intrinsic/generic-elements-pass.rs index a81d8ebdf50c2..b159387ab62cc 100644 --- a/tests/ui/simd/intrinsic/generic-elements-pass.rs +++ b/tests/ui/simd/intrinsic/generic-elements-pass.rs @@ -6,16 +6,15 @@ #[repr(simd)] #[derive(Copy, Clone, Debug, PartialEq)] #[allow(non_camel_case_types)] -struct i32x2(i32, i32); +struct i32x2([i32; 2]); #[repr(simd)] #[derive(Copy, Clone, Debug, PartialEq)] #[allow(non_camel_case_types)] -struct i32x4(i32, i32, i32, i32); +struct i32x4([i32; 4]); #[repr(simd)] #[derive(Copy, Clone, Debug, PartialEq)] #[allow(non_camel_case_types)] -struct i32x8(i32, i32, i32, i32, - i32, i32, i32, i32); +struct i32x8([i32; 8]); extern "rust-intrinsic" { fn simd_insert(x: T, idx: u32, y: E) -> T; @@ -37,26 +36,26 @@ macro_rules! all_eq { } fn main() { - let x2 = i32x2(20, 21); - let x4 = i32x4(40, 41, 42, 43); - let x8 = i32x8(80, 81, 82, 83, 84, 85, 86, 87); + let x2 = i32x2([20, 21]); + let x4 = i32x4([40, 41, 42, 43]); + let x8 = i32x8([80, 81, 82, 83, 84, 85, 86, 87]); unsafe { - all_eq!(simd_insert(x2, 0, 100), i32x2(100, 21)); - all_eq!(simd_insert(x2, 1, 100), i32x2(20, 100)); + all_eq!(simd_insert(x2, 0, 100), i32x2([100, 21])); + all_eq!(simd_insert(x2, 1, 100), i32x2([20, 100])); - all_eq!(simd_insert(x4, 0, 100), i32x4(100, 41, 42, 43)); - all_eq!(simd_insert(x4, 1, 100), i32x4(40, 100, 42, 43)); - all_eq!(simd_insert(x4, 2, 100), i32x4(40, 41, 100, 43)); - all_eq!(simd_insert(x4, 3, 100), i32x4(40, 41, 42, 100)); + all_eq!(simd_insert(x4, 0, 100), i32x4([100, 41, 42, 43])); + all_eq!(simd_insert(x4, 1, 100), i32x4([40, 100, 42, 43])); + all_eq!(simd_insert(x4, 2, 100), i32x4([40, 41, 100, 43])); + all_eq!(simd_insert(x4, 3, 100), i32x4([40, 41, 42, 100])); - all_eq!(simd_insert(x8, 0, 100), i32x8(100, 81, 82, 83, 84, 85, 86, 87)); - all_eq!(simd_insert(x8, 1, 100), i32x8(80, 100, 82, 83, 84, 85, 86, 87)); - all_eq!(simd_insert(x8, 2, 100), i32x8(80, 81, 100, 83, 84, 85, 86, 87)); - all_eq!(simd_insert(x8, 3, 100), i32x8(80, 81, 82, 100, 84, 85, 86, 87)); - all_eq!(simd_insert(x8, 4, 100), i32x8(80, 81, 82, 83, 100, 85, 86, 87)); - all_eq!(simd_insert(x8, 5, 100), i32x8(80, 81, 82, 83, 84, 100, 86, 87)); - all_eq!(simd_insert(x8, 6, 100), i32x8(80, 81, 82, 83, 84, 85, 100, 87)); - all_eq!(simd_insert(x8, 7, 100), i32x8(80, 81, 82, 83, 84, 85, 86, 100)); + all_eq!(simd_insert(x8, 0, 100), i32x8([100, 81, 82, 83, 84, 85, 86, 87])); + all_eq!(simd_insert(x8, 1, 100), i32x8([80, 100, 82, 83, 84, 85, 86, 87])); + all_eq!(simd_insert(x8, 2, 100), i32x8([80, 81, 100, 83, 84, 85, 86, 87])); + all_eq!(simd_insert(x8, 3, 100), i32x8([80, 81, 82, 100, 84, 85, 86, 87])); + all_eq!(simd_insert(x8, 4, 100), i32x8([80, 81, 82, 83, 100, 85, 86, 87])); + all_eq!(simd_insert(x8, 5, 100), i32x8([80, 81, 82, 83, 84, 100, 86, 87])); + all_eq!(simd_insert(x8, 6, 100), i32x8([80, 81, 82, 83, 84, 85, 100, 87])); + all_eq!(simd_insert(x8, 7, 100), i32x8([80, 81, 82, 83, 84, 85, 86, 100])); all_eq!(simd_extract(x2, 0), 20); all_eq!(simd_extract(x2, 1), 21); @@ -76,24 +75,24 @@ fn main() { all_eq!(simd_extract(x8, 7), 87); } - let y2 = i32x2(120, 121); - let y4 = i32x4(140, 141, 142, 143); - let y8 = i32x8(180, 181, 182, 183, 184, 185, 186, 187); + let y2 = i32x2([120, 121]); + let y4 = i32x4([140, 141, 142, 143]); + let y8 = i32x8([180, 181, 182, 183, 184, 185, 186, 187]); unsafe { - all_eq!(simd_shuffle(x2, y2, const { [3u32, 0] }), i32x2(121, 20)); - all_eq!(simd_shuffle(x2, y2, const { [3u32, 0, 1, 2] }), i32x4(121, 20, 21, 120)); + all_eq!(simd_shuffle(x2, y2, const { [3u32, 0] }), i32x2([121, 20])); + all_eq!(simd_shuffle(x2, y2, const { [3u32, 0, 1, 2] }), i32x4([121, 20, 21, 120])); all_eq!(simd_shuffle(x2, y2, const { [3u32, 0, 1, 2, 1, 2, 3, 0] }), - i32x8(121, 20, 21, 120, 21, 120, 121, 20)); + i32x8([121, 20, 21, 120, 21, 120, 121, 20])); - all_eq!(simd_shuffle(x4, y4, const { [7u32, 2] }), i32x2(143, 42)); - all_eq!(simd_shuffle(x4, y4, const { [7u32, 2, 5, 0] }), i32x4(143, 42, 141, 40)); + all_eq!(simd_shuffle(x4, y4, const { [7u32, 2] }), i32x2([143, 42])); + all_eq!(simd_shuffle(x4, y4, const { [7u32, 2, 5, 0] }), i32x4([143, 42, 141, 40])); all_eq!(simd_shuffle(x4, y4, const { [7u32, 2, 5, 0, 3, 6, 4, 1] }), - i32x8(143, 42, 141, 40, 43, 142, 140, 41)); + i32x8([143, 42, 141, 40, 43, 142, 140, 41])); - all_eq!(simd_shuffle(x8, y8, const { [11u32, 5] }), i32x2(183, 85)); - all_eq!(simd_shuffle(x8, y8, const { [11u32, 5, 15, 0] }), i32x4(183, 85, 187, 80)); + all_eq!(simd_shuffle(x8, y8, const { [11u32, 5] }), i32x2([183, 85])); + all_eq!(simd_shuffle(x8, y8, const { [11u32, 5, 15, 0] }), i32x4([183, 85, 187, 80])); all_eq!(simd_shuffle(x8, y8, const { [11u32, 5, 15, 0, 3, 8, 12, 1] }), - i32x8(183, 85, 187, 80, 83, 180, 184, 81)); + i32x8([183, 85, 187, 80, 83, 180, 184, 81])); } } diff --git a/tests/ui/simd/intrinsic/generic-elements.rs b/tests/ui/simd/intrinsic/generic-elements.rs index aec75a673067e..4848fd1b803af 100644 --- a/tests/ui/simd/intrinsic/generic-elements.rs +++ b/tests/ui/simd/intrinsic/generic-elements.rs @@ -6,28 +6,28 @@ #[repr(simd)] #[derive(Copy, Clone)] #[allow(non_camel_case_types)] -struct i32x2(i32, i32); +struct i32x2([i32; 2]); #[repr(simd)] #[derive(Copy, Clone)] #[allow(non_camel_case_types)] -struct i32x4(i32, i32, i32, i32); +struct i32x4([i32; 4]); #[repr(simd)] #[derive(Copy, Clone)] #[allow(non_camel_case_types)] -struct i32x8(i32, i32, i32, i32, i32, i32, i32, i32); +struct i32x8([i32; 8]); #[repr(simd)] #[derive(Copy, Clone)] #[allow(non_camel_case_types)] -struct f32x2(f32, f32); +struct f32x2([f32; 2]); #[repr(simd)] #[derive(Copy, Clone)] #[allow(non_camel_case_types)] -struct f32x4(f32, f32, f32, f32); +struct f32x4([f32; 4]); #[repr(simd)] #[derive(Copy, Clone)] #[allow(non_camel_case_types)] -struct f32x8(f32, f32, f32, f32, f32, f32, f32, f32); +struct f32x8([f32; 8]); extern "rust-intrinsic" { fn simd_insert(x: T, idx: u32, y: E) -> T; @@ -38,7 +38,7 @@ extern "rust-intrinsic" { } fn main() { - let x = i32x4(0, 0, 0, 0); + let x = i32x4([0, 0, 0, 0]); unsafe { simd_insert(0, 0, 0); diff --git a/tests/ui/simd/intrinsic/generic-gather-pass.rs b/tests/ui/simd/intrinsic/generic-gather-pass.rs index a00bc67e73bd4..3315d1cdaa22d 100644 --- a/tests/ui/simd/intrinsic/generic-gather-pass.rs +++ b/tests/ui/simd/intrinsic/generic-gather-pass.rs @@ -8,7 +8,7 @@ #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -struct x4(pub T, pub T, pub T, pub T); +struct x4(pub [T; 4]); extern "rust-intrinsic" { fn simd_gather(x: T, y: U, z: V) -> T; @@ -18,19 +18,19 @@ extern "rust-intrinsic" { fn main() { let mut x = [0_f32, 1., 2., 3., 4., 5., 6., 7.]; - let default = x4(-3_f32, -3., -3., -3.); - let s_strided = x4(0_f32, 2., -3., 6.); - let mask = x4(-1_i32, -1, 0, -1); + let default = x4([-3_f32, -3., -3., -3.]); + let s_strided = x4([0_f32, 2., -3., 6.]); + let mask = x4([-1_i32, -1, 0, -1]); // reading from *const unsafe { let pointer = x.as_ptr(); - let pointers = x4( + let pointers = x4([ pointer.offset(0), pointer.offset(2), pointer.offset(4), pointer.offset(6) - ); + ]); let r_strided = simd_gather(default, pointers, mask); @@ -40,12 +40,12 @@ fn main() { // reading from *mut unsafe { let pointer = x.as_mut_ptr(); - let pointers = x4( + let pointers = x4([ pointer.offset(0), pointer.offset(2), pointer.offset(4), pointer.offset(6) - ); + ]); let r_strided = simd_gather(default, pointers, mask); @@ -55,14 +55,14 @@ fn main() { // writing to *mut unsafe { let pointer = x.as_mut_ptr(); - let pointers = x4( + let pointers = x4([ pointer.offset(0), pointer.offset(2), pointer.offset(4), pointer.offset(6) - ); + ]); - let values = x4(42_f32, 43_f32, 44_f32, 45_f32); + let values = x4([42_f32, 43_f32, 44_f32, 45_f32]); simd_scatter(values, pointers, mask); assert_eq!(x, [42., 1., 43., 3., 4., 5., 45., 7.]); @@ -80,18 +80,18 @@ fn main() { &x[7] as *const f32 ]; - let default = x4(y[0], y[0], y[0], y[0]); - let s_strided = x4(y[0], y[2], y[0], y[6]); + let default = x4([y[0], y[0], y[0], y[0]]); + let s_strided = x4([y[0], y[2], y[0], y[6]]); // reading from *const unsafe { let pointer = y.as_ptr(); - let pointers = x4( + let pointers = x4([ pointer.offset(0), pointer.offset(2), pointer.offset(4), pointer.offset(6) - ); + ]); let r_strided = simd_gather(default, pointers, mask); @@ -101,12 +101,12 @@ fn main() { // reading from *mut unsafe { let pointer = y.as_mut_ptr(); - let pointers = x4( + let pointers = x4([ pointer.offset(0), pointer.offset(2), pointer.offset(4), pointer.offset(6) - ); + ]); let r_strided = simd_gather(default, pointers, mask); @@ -116,14 +116,14 @@ fn main() { // writing to *mut unsafe { let pointer = y.as_mut_ptr(); - let pointers = x4( + let pointers = x4([ pointer.offset(0), pointer.offset(2), pointer.offset(4), pointer.offset(6) - ); + ]); - let values = x4(y[7], y[6], y[5], y[1]); + let values = x4([y[7], y[6], y[5], y[1]]); simd_scatter(values, pointers, mask); let s = [ diff --git a/tests/ui/simd/intrinsic/generic-reduction-pass.rs b/tests/ui/simd/intrinsic/generic-reduction-pass.rs index 64902788709eb..699fb396259dc 100644 --- a/tests/ui/simd/intrinsic/generic-reduction-pass.rs +++ b/tests/ui/simd/intrinsic/generic-reduction-pass.rs @@ -10,19 +10,19 @@ #[repr(simd)] #[derive(Copy, Clone)] -struct i32x4(pub i32, pub i32, pub i32, pub i32); +struct i32x4(pub [i32; 4]); #[repr(simd)] #[derive(Copy, Clone)] -struct u32x4(pub u32, pub u32, pub u32, pub u32); +struct u32x4(pub [u32; 4]); #[repr(simd)] #[derive(Copy, Clone)] -struct f32x4(pub f32, pub f32, pub f32, pub f32); +struct f32x4(pub [f32; 4]); #[repr(simd)] #[derive(Copy, Clone)] -struct b8x4(pub i8, pub i8, pub i8, pub i8); +struct b8x4(pub [i8; 4]); extern "rust-intrinsic" { fn simd_reduce_add_unordered(x: T) -> U; @@ -40,7 +40,7 @@ extern "rust-intrinsic" { fn main() { unsafe { - let x = i32x4(1, -2, 3, 4); + let x = i32x4([1, -2, 3, 4]); let r: i32 = simd_reduce_add_unordered(x); assert_eq!(r, 6_i32); let r: i32 = simd_reduce_mul_unordered(x); @@ -55,7 +55,7 @@ fn main() { let r: i32 = simd_reduce_max(x); assert_eq!(r, 4_i32); - let x = i32x4(-1, -1, -1, -1); + let x = i32x4([-1, -1, -1, -1]); let r: i32 = simd_reduce_and(x); assert_eq!(r, -1_i32); let r: i32 = simd_reduce_or(x); @@ -63,7 +63,7 @@ fn main() { let r: i32 = simd_reduce_xor(x); assert_eq!(r, 0_i32); - let x = i32x4(-1, -1, 0, -1); + let x = i32x4([-1, -1, 0, -1]); let r: i32 = simd_reduce_and(x); assert_eq!(r, 0_i32); let r: i32 = simd_reduce_or(x); @@ -73,7 +73,7 @@ fn main() { } unsafe { - let x = u32x4(1, 2, 3, 4); + let x = u32x4([1, 2, 3, 4]); let r: u32 = simd_reduce_add_unordered(x); assert_eq!(r, 10_u32); let r: u32 = simd_reduce_mul_unordered(x); @@ -89,7 +89,7 @@ fn main() { assert_eq!(r, 4_u32); let t = u32::MAX; - let x = u32x4(t, t, t, t); + let x = u32x4([t, t, t, t]); let r: u32 = simd_reduce_and(x); assert_eq!(r, t); let r: u32 = simd_reduce_or(x); @@ -97,7 +97,7 @@ fn main() { let r: u32 = simd_reduce_xor(x); assert_eq!(r, 0_u32); - let x = u32x4(t, t, 0, t); + let x = u32x4([t, t, 0, t]); let r: u32 = simd_reduce_and(x); assert_eq!(r, 0_u32); let r: u32 = simd_reduce_or(x); @@ -107,7 +107,7 @@ fn main() { } unsafe { - let x = f32x4(1., -2., 3., 4.); + let x = f32x4([1., -2., 3., 4.]); let r: f32 = simd_reduce_add_unordered(x); assert_eq!(r, 6_f32); let r: f32 = simd_reduce_mul_unordered(x); @@ -128,19 +128,19 @@ fn main() { } unsafe { - let x = b8x4(!0, !0, !0, !0); + let x = b8x4([!0, !0, !0, !0]); let r: bool = simd_reduce_all(x); assert_eq!(r, true); let r: bool = simd_reduce_any(x); assert_eq!(r, true); - let x = b8x4(!0, !0, 0, !0); + let x = b8x4([!0, !0, 0, !0]); let r: bool = simd_reduce_all(x); assert_eq!(r, false); let r: bool = simd_reduce_any(x); assert_eq!(r, true); - let x = b8x4(0, 0, 0, 0); + let x = b8x4([0, 0, 0, 0]); let r: bool = simd_reduce_all(x); assert_eq!(r, false); let r: bool = simd_reduce_any(x); diff --git a/tests/ui/simd/intrinsic/generic-reduction.rs b/tests/ui/simd/intrinsic/generic-reduction.rs index 96df73591693f..1986deafb6ab5 100644 --- a/tests/ui/simd/intrinsic/generic-reduction.rs +++ b/tests/ui/simd/intrinsic/generic-reduction.rs @@ -9,11 +9,11 @@ #[repr(simd)] #[derive(Copy, Clone)] -pub struct f32x4(pub f32, pub f32, pub f32, pub f32); +pub struct f32x4(pub [f32; 4]); #[repr(simd)] #[derive(Copy, Clone)] -pub struct u32x4(pub u32, pub u32, pub u32, pub u32); +pub struct u32x4(pub [u32; 4]); extern "rust-intrinsic" { @@ -27,8 +27,8 @@ extern "rust-intrinsic" { } fn main() { - let x = u32x4(0, 0, 0, 0); - let z = f32x4(0.0, 0.0, 0.0, 0.0); + let x = u32x4([0, 0, 0, 0]); + let z = f32x4([0.0, 0.0, 0.0, 0.0]); unsafe { simd_reduce_add_ordered(z, 0); diff --git a/tests/ui/simd/intrinsic/generic-select-pass.rs b/tests/ui/simd/intrinsic/generic-select-pass.rs index 98e1534e6e622..5690bad504844 100644 --- a/tests/ui/simd/intrinsic/generic-select-pass.rs +++ b/tests/ui/simd/intrinsic/generic-select-pass.rs @@ -11,23 +11,23 @@ #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -struct i32x4(pub i32, pub i32, pub i32, pub i32); +struct i32x4(pub [i32; 4]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -struct u32x4(pub u32, pub u32, pub u32, pub u32); +struct u32x4(pub [u32; 4]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -struct u32x8(u32, u32, u32, u32, u32, u32, u32, u32); +struct u32x8([u32; 8]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -struct f32x4(pub f32, pub f32, pub f32, pub f32); +struct f32x4(pub [f32; 4]); #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -struct b8x4(pub i8, pub i8, pub i8, pub i8); +struct b8x4(pub [i8; 4]); extern "rust-intrinsic" { fn simd_select(x: T, a: U, b: U) -> U; @@ -35,15 +35,15 @@ extern "rust-intrinsic" { } fn main() { - let m0 = b8x4(!0, !0, !0, !0); - let m1 = b8x4(0, 0, 0, 0); - let m2 = b8x4(!0, !0, 0, 0); - let m3 = b8x4(0, 0, !0, !0); - let m4 = b8x4(!0, 0, !0, 0); + let m0 = b8x4([!0, !0, !0, !0]); + let m1 = b8x4([0, 0, 0, 0]); + let m2 = b8x4([!0, !0, 0, 0]); + let m3 = b8x4([0, 0, !0, !0]); + let m4 = b8x4([!0, 0, !0, 0]); unsafe { - let a = i32x4(1, -2, 3, 4); - let b = i32x4(5, 6, -7, 8); + let a = i32x4([1, -2, 3, 4]); + let b = i32x4([5, 6, -7, 8]); let r: i32x4 = simd_select(m0, a, b); let e = a; @@ -54,21 +54,21 @@ fn main() { assert_eq!(r, e); let r: i32x4 = simd_select(m2, a, b); - let e = i32x4(1, -2, -7, 8); + let e = i32x4([1, -2, -7, 8]); assert_eq!(r, e); let r: i32x4 = simd_select(m3, a, b); - let e = i32x4(5, 6, 3, 4); + let e = i32x4([5, 6, 3, 4]); assert_eq!(r, e); let r: i32x4 = simd_select(m4, a, b); - let e = i32x4(1, 6, 3, 8); + let e = i32x4([1, 6, 3, 8]); assert_eq!(r, e); } unsafe { - let a = u32x4(1, 2, 3, 4); - let b = u32x4(5, 6, 7, 8); + let a = u32x4([1, 2, 3, 4]); + let b = u32x4([5, 6, 7, 8]); let r: u32x4 = simd_select(m0, a, b); let e = a; @@ -79,21 +79,21 @@ fn main() { assert_eq!(r, e); let r: u32x4 = simd_select(m2, a, b); - let e = u32x4(1, 2, 7, 8); + let e = u32x4([1, 2, 7, 8]); assert_eq!(r, e); let r: u32x4 = simd_select(m3, a, b); - let e = u32x4(5, 6, 3, 4); + let e = u32x4([5, 6, 3, 4]); assert_eq!(r, e); let r: u32x4 = simd_select(m4, a, b); - let e = u32x4(1, 6, 3, 8); + let e = u32x4([1, 6, 3, 8]); assert_eq!(r, e); } unsafe { - let a = f32x4(1., 2., 3., 4.); - let b = f32x4(5., 6., 7., 8.); + let a = f32x4([1., 2., 3., 4.]); + let b = f32x4([5., 6., 7., 8.]); let r: f32x4 = simd_select(m0, a, b); let e = a; @@ -104,23 +104,23 @@ fn main() { assert_eq!(r, e); let r: f32x4 = simd_select(m2, a, b); - let e = f32x4(1., 2., 7., 8.); + let e = f32x4([1., 2., 7., 8.]); assert_eq!(r, e); let r: f32x4 = simd_select(m3, a, b); - let e = f32x4(5., 6., 3., 4.); + let e = f32x4([5., 6., 3., 4.]); assert_eq!(r, e); let r: f32x4 = simd_select(m4, a, b); - let e = f32x4(1., 6., 3., 8.); + let e = f32x4([1., 6., 3., 8.]); assert_eq!(r, e); } unsafe { let t = !0 as i8; let f = 0 as i8; - let a = b8x4(t, f, t, f); - let b = b8x4(f, f, f, t); + let a = b8x4([t, f, t, f]); + let b = b8x4([f, f, f, t]); let r: b8x4 = simd_select(m0, a, b); let e = a; @@ -131,21 +131,21 @@ fn main() { assert_eq!(r, e); let r: b8x4 = simd_select(m2, a, b); - let e = b8x4(t, f, f, t); + let e = b8x4([t, f, f, t]); assert_eq!(r, e); let r: b8x4 = simd_select(m3, a, b); - let e = b8x4(f, f, t, f); + let e = b8x4([f, f, t, f]); assert_eq!(r, e); let r: b8x4 = simd_select(m4, a, b); - let e = b8x4(t, f, t, t); + let e = b8x4([t, f, t, t]); assert_eq!(r, e); } unsafe { - let a = u32x8(0, 1, 2, 3, 4, 5, 6, 7); - let b = u32x8(8, 9, 10, 11, 12, 13, 14, 15); + let a = u32x8([0, 1, 2, 3, 4, 5, 6, 7]); + let b = u32x8([8, 9, 10, 11, 12, 13, 14, 15]); let r: u32x8 = simd_select_bitmask(0u8, a, b); let e = b; @@ -156,21 +156,21 @@ fn main() { assert_eq!(r, e); let r: u32x8 = simd_select_bitmask(0b01010101u8, a, b); - let e = u32x8(0, 9, 2, 11, 4, 13, 6, 15); + let e = u32x8([0, 9, 2, 11, 4, 13, 6, 15]); assert_eq!(r, e); let r: u32x8 = simd_select_bitmask(0b10101010u8, a, b); - let e = u32x8(8, 1, 10, 3, 12, 5, 14, 7); + let e = u32x8([8, 1, 10, 3, 12, 5, 14, 7]); assert_eq!(r, e); let r: u32x8 = simd_select_bitmask(0b11110000u8, a, b); - let e = u32x8(8, 9, 10, 11, 4, 5, 6, 7); + let e = u32x8([8, 9, 10, 11, 4, 5, 6, 7]); assert_eq!(r, e); } unsafe { - let a = u32x4(0, 1, 2, 3); - let b = u32x4(4, 5, 6, 7); + let a = u32x4([0, 1, 2, 3]); + let b = u32x4([4, 5, 6, 7]); let r: u32x4 = simd_select_bitmask(0u8, a, b); let e = b; @@ -181,15 +181,15 @@ fn main() { assert_eq!(r, e); let r: u32x4 = simd_select_bitmask(0b0101u8, a, b); - let e = u32x4(0, 5, 2, 7); + let e = u32x4([0, 5, 2, 7]); assert_eq!(r, e); let r: u32x4 = simd_select_bitmask(0b1010u8, a, b); - let e = u32x4(4, 1, 6, 3); + let e = u32x4([4, 1, 6, 3]); assert_eq!(r, e); let r: u32x4 = simd_select_bitmask(0b1100u8, a, b); - let e = u32x4(4, 5, 2, 3); + let e = u32x4([4, 5, 2, 3]); assert_eq!(r, e); } } diff --git a/tests/ui/simd/intrinsic/generic-select.rs b/tests/ui/simd/intrinsic/generic-select.rs index 215ae405c37e2..52e02649590ab 100644 --- a/tests/ui/simd/intrinsic/generic-select.rs +++ b/tests/ui/simd/intrinsic/generic-select.rs @@ -8,19 +8,19 @@ #[repr(simd)] #[derive(Copy, Clone)] -pub struct f32x4(pub f32, pub f32, pub f32, pub f32); +pub struct f32x4(pub [f32; 4]); #[repr(simd)] #[derive(Copy, Clone)] -pub struct u32x4(pub u32, pub u32, pub u32, pub u32); +pub struct u32x4(pub [u32; 4]); #[repr(simd)] #[derive(Copy, Clone, PartialEq)] -struct b8x4(pub i8, pub i8, pub i8, pub i8); +struct b8x4(pub [i8; 4]); #[repr(simd)] #[derive(Copy, Clone, PartialEq)] -struct b8x8(pub i8, pub i8, pub i8, pub i8, pub i8, pub i8, pub i8, pub i8); +struct b8x8(pub [i8; 8]); extern "rust-intrinsic" { fn simd_select(x: T, a: U, b: U) -> U; @@ -28,10 +28,10 @@ extern "rust-intrinsic" { } fn main() { - let m4 = b8x4(0, 0, 0, 0); - let m8 = b8x8(0, 0, 0, 0, 0, 0, 0, 0); - let x = u32x4(0, 0, 0, 0); - let z = f32x4(0.0, 0.0, 0.0, 0.0); + let m4 = b8x4([0, 0, 0, 0]); + let m8 = b8x8([0, 0, 0, 0, 0, 0, 0, 0]); + let x = u32x4([0, 0, 0, 0]); + let z = f32x4([0.0, 0.0, 0.0, 0.0]); unsafe { simd_select(m4, x, x); diff --git a/tests/ui/simd/intrinsic/inlining-issue67557-ice.rs b/tests/ui/simd/intrinsic/inlining-issue67557-ice.rs index 928d3824703e7..a64a7c0b48a1e 100644 --- a/tests/ui/simd/intrinsic/inlining-issue67557-ice.rs +++ b/tests/ui/simd/intrinsic/inlining-issue67557-ice.rs @@ -11,7 +11,7 @@ extern "rust-intrinsic" { #[repr(simd)] #[derive(Debug, PartialEq)] -struct Simd2(u8, u8); +struct Simd2([u8; 2]); fn main() { unsafe { @@ -22,5 +22,5 @@ fn main() { #[inline(always)] unsafe fn inline_me() -> Simd2 { const IDX: [u32; 2] = [0, 3]; - simd_shuffle(Simd2(10, 11), Simd2(12, 13), IDX) + simd_shuffle(Simd2([10, 11]), Simd2([12, 13]), IDX) } diff --git a/tests/ui/simd/intrinsic/inlining-issue67557.rs b/tests/ui/simd/intrinsic/inlining-issue67557.rs index b8b8dbba547d1..cb80d65d46838 100644 --- a/tests/ui/simd/intrinsic/inlining-issue67557.rs +++ b/tests/ui/simd/intrinsic/inlining-issue67557.rs @@ -11,12 +11,12 @@ extern "rust-intrinsic" { #[repr(simd)] #[derive(Debug, PartialEq)] -struct Simd2(u8, u8); +struct Simd2([u8; 2]); fn main() { unsafe { const IDX: [u32; 2] = [0, 1]; - let p_res: Simd2 = simd_shuffle(Simd2(10, 11), Simd2(12, 13), IDX); + let p_res: Simd2 = simd_shuffle(Simd2([10, 11]), Simd2([12, 13]), IDX); let a_res: Simd2 = inline_me(); assert_10_11(p_res); @@ -26,17 +26,17 @@ fn main() { #[inline(never)] fn assert_10_11(x: Simd2) { - assert_eq!(x, Simd2(10, 11)); + assert_eq!(x, Simd2([10, 11])); } #[inline(never)] fn assert_10_13(x: Simd2) { - assert_eq!(x, Simd2(10, 13)); + assert_eq!(x, Simd2([10, 13])); } #[inline(always)] unsafe fn inline_me() -> Simd2 { const IDX: [u32; 2] = [0, 3]; - simd_shuffle(Simd2(10, 11), Simd2(12, 13), IDX) + simd_shuffle(Simd2([10, 11]), Simd2([12, 13]), IDX) } diff --git a/tests/ui/simd/issue-17170.rs b/tests/ui/simd/issue-17170.rs index abfc1c25ffb78..2d13962843c44 100644 --- a/tests/ui/simd/issue-17170.rs +++ b/tests/ui/simd/issue-17170.rs @@ -2,9 +2,9 @@ #![feature(repr_simd)] #[repr(simd)] -struct T(f64, f64, f64); +struct T([f64; 3]); -static X: T = T(0.0, 0.0, 0.0); +static X: T = T([0.0, 0.0, 0.0]); fn main() { let _ = X; diff --git a/tests/ui/simd/issue-32947.rs b/tests/ui/simd/issue-32947.rs index bccca25c52b1d..dc5e7a4ec9153 100644 --- a/tests/ui/simd/issue-32947.rs +++ b/tests/ui/simd/issue-32947.rs @@ -6,7 +6,7 @@ extern crate test; #[repr(simd)] -pub struct Mu64(pub u64, pub u64, pub u64, pub u64); +pub struct Mu64(pub [u64; 4]); fn main() { // This ensures an unaligned pointer even in optimized builds, though LLVM @@ -18,7 +18,7 @@ fn main() { let misaligned_ptr: &mut [u8; 32] = { std::mem::transmute(memory.offset(1)) }; - *misaligned_ptr = std::mem::transmute(Mu64(1, 1, 1, 1)); + *misaligned_ptr = std::mem::transmute(Mu64([1, 1, 1, 1])); test::black_box(memory); } } diff --git a/tests/ui/simd/issue-39720.rs b/tests/ui/simd/issue-39720.rs index 4610b1d50044b..2b51c0224c633 100644 --- a/tests/ui/simd/issue-39720.rs +++ b/tests/ui/simd/issue-39720.rs @@ -5,18 +5,18 @@ #[repr(simd)] #[derive(Copy, Clone, Debug)] -pub struct Char3(pub i8, pub i8, pub i8); +pub struct Char3(pub [i8; 3]); #[repr(simd)] #[derive(Copy, Clone, Debug)] -pub struct Short3(pub i16, pub i16, pub i16); +pub struct Short3(pub [i16; 3]); extern "rust-intrinsic" { fn simd_cast(x: T) -> U; } fn main() { - let cast: Short3 = unsafe { simd_cast(Char3(10, -3, -9)) }; + let cast: Short3 = unsafe { simd_cast(Char3([10, -3, -9])) }; println!("{:?}", cast); } diff --git a/tests/ui/simd/issue-89193.rs b/tests/ui/simd/issue-89193.rs index a4ed9be9962fb..9530124a7cc81 100644 --- a/tests/ui/simd/issue-89193.rs +++ b/tests/ui/simd/issue-89193.rs @@ -8,7 +8,7 @@ #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -struct x4(pub T, pub T, pub T, pub T); +struct x4(pub [T; 4]); extern "rust-intrinsic" { fn simd_gather(x: T, y: U, z: V) -> T; @@ -16,36 +16,36 @@ extern "rust-intrinsic" { fn main() { let x: [usize; 4] = [10, 11, 12, 13]; - let default = x4(0_usize, 1, 2, 3); + let default = x4([0_usize, 1, 2, 3]); let all_set = u8::MAX as i8; // aka -1 - let mask = x4(all_set, all_set, all_set, all_set); - let expected = x4(10_usize, 11, 12, 13); + let mask = x4([all_set, all_set, all_set, all_set]); + let expected = x4([10_usize, 11, 12, 13]); unsafe { let pointer = x.as_ptr(); - let pointers = x4( + let pointers = x4([ pointer.offset(0), pointer.offset(1), pointer.offset(2), pointer.offset(3) - ); + ]); let result = simd_gather(default, pointers, mask); assert_eq!(result, expected); } // and again for isize let x: [isize; 4] = [10, 11, 12, 13]; - let default = x4(0_isize, 1, 2, 3); - let expected = x4(10_isize, 11, 12, 13); + let default = x4([0_isize, 1, 2, 3]); + let expected = x4([10_isize, 11, 12, 13]); unsafe { let pointer = x.as_ptr(); - let pointers = x4( + let pointers = x4([ pointer.offset(0), pointer.offset(1), pointer.offset(2), pointer.offset(3) - ); + ]); let result = simd_gather(default, pointers, mask); assert_eq!(result, expected); } diff --git a/tests/ui/simd/monomorphize-heterogeneous.rs b/tests/ui/simd/monomorphize-heterogeneous.rs index 42e380dbb779b..326e52acc34d1 100644 --- a/tests/ui/simd/monomorphize-heterogeneous.rs +++ b/tests/ui/simd/monomorphize-heterogeneous.rs @@ -2,7 +2,11 @@ #[repr(simd)] struct I64F64(i64, f64); -//~^ ERROR SIMD vector should be homogeneous +//~^ ERROR SIMD vector's only field must be an array + +#[repr(simd)] +struct I64x4F64x0([i64; 4], [f64; 0]); +//~^ ERROR SIMD vector cannot have multiple fields static X: I64F64 = I64F64(1, 2.0); diff --git a/tests/ui/simd/monomorphize-heterogeneous.stderr b/tests/ui/simd/monomorphize-heterogeneous.stderr index 58e2b7c834766..610a1a49038aa 100644 --- a/tests/ui/simd/monomorphize-heterogeneous.stderr +++ b/tests/ui/simd/monomorphize-heterogeneous.stderr @@ -1,9 +1,16 @@ -error[E0076]: SIMD vector should be homogeneous +error[E0076]: SIMD vector's only field must be an array --> $DIR/monomorphize-heterogeneous.rs:4:1 | LL | struct I64F64(i64, f64); - | ^^^^^^^^^^^^^ SIMD elements must have the same type + | ^^^^^^^^^^^^^ --- not an array -error: aborting due to 1 previous error +error[E0075]: SIMD vector cannot have multiple fields + --> $DIR/monomorphize-heterogeneous.rs:8:1 + | +LL | struct I64x4F64x0([i64; 4], [f64; 0]); + | ^^^^^^^^^^^^^^^^^ -------- excess field + +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0076`. +Some errors have detailed explanations: E0075, E0076. +For more information about an error, try `rustc --explain E0075`. diff --git a/tests/ui/simd/monomorphize-too-long.rs b/tests/ui/simd/monomorphize-too-long.rs new file mode 100644 index 0000000000000..4bcde782292d6 --- /dev/null +++ b/tests/ui/simd/monomorphize-too-long.rs @@ -0,0 +1,11 @@ +//@ build-fail +//@ error-pattern: monomorphising SIMD type `Simd` of length greater than 32768 + +#![feature(repr_simd)] + +#[repr(simd)] +struct Simd([T; N]); + +fn main() { + let _too_big = Simd([1_u16; 54321]); +} diff --git a/tests/ui/simd/monomorphize-too-long.stderr b/tests/ui/simd/monomorphize-too-long.stderr new file mode 100644 index 0000000000000..978eef307abd1 --- /dev/null +++ b/tests/ui/simd/monomorphize-too-long.stderr @@ -0,0 +1,4 @@ +error: monomorphising SIMD type `Simd` of length greater than 32768 + +error: aborting due to 1 previous error + diff --git a/tests/ui/simd/monomorphize-zero-length.rs b/tests/ui/simd/monomorphize-zero-length.rs new file mode 100644 index 0000000000000..44b4cfc0bcfab --- /dev/null +++ b/tests/ui/simd/monomorphize-zero-length.rs @@ -0,0 +1,11 @@ +//@ build-fail +//@ error-pattern: monomorphising SIMD type `Simd` of zero length + +#![feature(repr_simd)] + +#[repr(simd)] +struct Simd([T; N]); + +fn main() { + let _empty = Simd([1.0; 0]); +} diff --git a/tests/ui/simd/monomorphize-zero-length.stderr b/tests/ui/simd/monomorphize-zero-length.stderr new file mode 100644 index 0000000000000..738c20fe51a46 --- /dev/null +++ b/tests/ui/simd/monomorphize-zero-length.stderr @@ -0,0 +1,4 @@ +error: monomorphising SIMD type `Simd` of zero length + +error: aborting due to 1 previous error + diff --git a/tests/ui/simd/target-feature-mixup.rs b/tests/ui/simd/target-feature-mixup.rs index 034cb867c95fc..62d87c3a6dc6c 100644 --- a/tests/ui/simd/target-feature-mixup.rs +++ b/tests/ui/simd/target-feature-mixup.rs @@ -54,17 +54,17 @@ mod test { // An SSE type #[repr(simd)] #[derive(PartialEq, Debug, Clone, Copy)] - struct __m128i(u64, u64); + struct __m128i([u64; 2]); // An AVX type #[repr(simd)] #[derive(PartialEq, Debug, Clone, Copy)] - struct __m256i(u64, u64, u64, u64); + struct __m256i([u64; 4]); // An AVX-512 type #[repr(simd)] #[derive(PartialEq, Debug, Clone, Copy)] - struct __m512i(u64, u64, u64, u64, u64, u64, u64, u64); + struct __m512i([u64; 8]); pub fn main(level: &str) { unsafe { @@ -90,9 +90,9 @@ mod test { )*) => ($( $(#[$attr])* unsafe fn $main(level: &str) { - let m128 = __m128i(1, 2); - let m256 = __m256i(3, 4, 5, 6); - let m512 = __m512i(7, 8, 9, 10, 11, 12, 13, 14); + let m128 = __m128i([1, 2]); + let m256 = __m256i([3, 4, 5, 6]); + let m512 = __m512i([7, 8, 9, 10, 11, 12, 13, 14]); assert_eq!(id_sse_128(m128), m128); assert_eq!(id_sse_256(m256), m256); assert_eq!(id_sse_512(m512), m512); @@ -127,55 +127,55 @@ mod test { #[target_feature(enable = "sse2")] unsafe fn id_sse_128(a: __m128i) -> __m128i { - assert_eq!(a, __m128i(1, 2)); + assert_eq!(a, __m128i([1, 2])); a.clone() } #[target_feature(enable = "sse2")] unsafe fn id_sse_256(a: __m256i) -> __m256i { - assert_eq!(a, __m256i(3, 4, 5, 6)); + assert_eq!(a, __m256i([3, 4, 5, 6])); a.clone() } #[target_feature(enable = "sse2")] unsafe fn id_sse_512(a: __m512i) -> __m512i { - assert_eq!(a, __m512i(7, 8, 9, 10, 11, 12, 13, 14)); + assert_eq!(a, __m512i([7, 8, 9, 10, 11, 12, 13, 14])); a.clone() } #[target_feature(enable = "avx")] unsafe fn id_avx_128(a: __m128i) -> __m128i { - assert_eq!(a, __m128i(1, 2)); + assert_eq!(a, __m128i([1, 2])); a.clone() } #[target_feature(enable = "avx")] unsafe fn id_avx_256(a: __m256i) -> __m256i { - assert_eq!(a, __m256i(3, 4, 5, 6)); + assert_eq!(a, __m256i([3, 4, 5, 6])); a.clone() } #[target_feature(enable = "avx")] unsafe fn id_avx_512(a: __m512i) -> __m512i { - assert_eq!(a, __m512i(7, 8, 9, 10, 11, 12, 13, 14)); + assert_eq!(a, __m512i([7, 8, 9, 10, 11, 12, 13, 14])); a.clone() } #[target_feature(enable = "avx512bw")] unsafe fn id_avx512_128(a: __m128i) -> __m128i { - assert_eq!(a, __m128i(1, 2)); + assert_eq!(a, __m128i([1, 2])); a.clone() } #[target_feature(enable = "avx512bw")] unsafe fn id_avx512_256(a: __m256i) -> __m256i { - assert_eq!(a, __m256i(3, 4, 5, 6)); + assert_eq!(a, __m256i([3, 4, 5, 6])); a.clone() } #[target_feature(enable = "avx512bw")] unsafe fn id_avx512_512(a: __m512i) -> __m512i { - assert_eq!(a, __m512i(7, 8, 9, 10, 11, 12, 13, 14)); + assert_eq!(a, __m512i([7, 8, 9, 10, 11, 12, 13, 14])); a.clone() } } diff --git a/tests/ui/simd/type-generic-monomorphisation-extern-nonnull-ptr.rs b/tests/ui/simd/type-generic-monomorphisation-extern-nonnull-ptr.rs index dbe2d9ddd54a7..a969295c9f9cd 100644 --- a/tests/ui/simd/type-generic-monomorphisation-extern-nonnull-ptr.rs +++ b/tests/ui/simd/type-generic-monomorphisation-extern-nonnull-ptr.rs @@ -11,7 +11,7 @@ extern { } #[repr(simd)] -struct S(T); +struct S([T; 4]); #[inline(never)] fn identity(v: T) -> T { @@ -19,5 +19,5 @@ fn identity(v: T) -> T { } fn main() { - let _v: S<[Option>; 4]> = identity(S([None; 4])); + let _v: S>> = identity(S([None; 4])); } diff --git a/tests/ui/simd/type-generic-monomorphisation-wide-ptr.rs b/tests/ui/simd/type-generic-monomorphisation-wide-ptr.rs index 564118e9b1343..18fc075343069 100644 --- a/tests/ui/simd/type-generic-monomorphisation-wide-ptr.rs +++ b/tests/ui/simd/type-generic-monomorphisation-wide-ptr.rs @@ -2,11 +2,11 @@ #![feature(repr_simd)] -//@ error-pattern:monomorphising SIMD type `S<[*mut [u8]; 4]>` with a non-primitive-scalar (integer/float/pointer) element type `*mut [u8]` +//@ error-pattern:monomorphising SIMD type `S<*mut [u8]>` with a non-primitive-scalar (integer/float/pointer) element type `*mut [u8]` #[repr(simd)] -struct S(T); +struct S([T; 4]); fn main() { - let _v: Option> = None; + let _v: Option> = None; } diff --git a/tests/ui/simd/type-generic-monomorphisation-wide-ptr.stderr b/tests/ui/simd/type-generic-monomorphisation-wide-ptr.stderr index 7ac8d71536034..13b8b0315ba75 100644 --- a/tests/ui/simd/type-generic-monomorphisation-wide-ptr.stderr +++ b/tests/ui/simd/type-generic-monomorphisation-wide-ptr.stderr @@ -1,4 +1,4 @@ -error: monomorphising SIMD type `S<[*mut [u8]; 4]>` with a non-primitive-scalar (integer/float/pointer) element type `*mut [u8]` +error: monomorphising SIMD type `S<*mut [u8]>` with a non-primitive-scalar (integer/float/pointer) element type `*mut [u8]` error: aborting due to 1 previous error diff --git a/tests/ui/simd/type-generic-monomorphisation.rs b/tests/ui/simd/type-generic-monomorphisation.rs index 2eeba55ef913a..8b8d645a2649b 100644 --- a/tests/ui/simd/type-generic-monomorphisation.rs +++ b/tests/ui/simd/type-generic-monomorphisation.rs @@ -7,8 +7,8 @@ struct X(Vec); #[repr(simd)] -struct Simd2(T, T); +struct Simd2([T; 2]); fn main() { - let _ = Simd2(X(vec![]), X(vec![])); + let _ = Simd2([X(vec![]), X(vec![])]); } diff --git a/tests/ui/simd/type-len.rs b/tests/ui/simd/type-len.rs index d82c70b8d8268..a971177e1543a 100644 --- a/tests/ui/simd/type-len.rs +++ b/tests/ui/simd/type-len.rs @@ -1,7 +1,6 @@ #![feature(repr_simd)] #![allow(non_camel_case_types)] - #[repr(simd)] struct empty; //~ ERROR SIMD vector cannot be empty @@ -12,12 +11,12 @@ struct empty2([f32; 0]); //~ ERROR SIMD vector cannot be empty struct pow2([f32; 7]); #[repr(simd)] -struct i64f64(i64, f64); //~ ERROR SIMD vector should be homogeneous +struct i64f64(i64, f64); //~ ERROR SIMD vector's only field must be an array struct Foo; #[repr(simd)] -struct FooV(Foo, Foo); //~ ERROR SIMD vector element type should be a primitive scalar (integer/float/pointer) type +struct FooV(Foo, Foo); //~ ERROR SIMD vector's only field must be an array #[repr(simd)] struct FooV2([Foo; 2]); //~ ERROR SIMD vector element type should be a primitive scalar (integer/float/pointer) type @@ -29,11 +28,11 @@ struct TooBig([f32; 65536]); //~ ERROR SIMD vector cannot have more than 32768 e struct JustRight([u128; 32768]); #[repr(simd)] -struct RGBA { +struct RGBA { //~ ERROR SIMD vector's only field must be an array r: f32, g: f32, b: f32, - a: f32 + a: f32, } fn main() {} diff --git a/tests/ui/simd/type-len.stderr b/tests/ui/simd/type-len.stderr index 2a6bd1b0fd58a..04c4ca7677cfc 100644 --- a/tests/ui/simd/type-len.stderr +++ b/tests/ui/simd/type-len.stderr @@ -1,40 +1,48 @@ error[E0075]: SIMD vector cannot be empty - --> $DIR/type-len.rs:6:1 + --> $DIR/type-len.rs:5:1 | LL | struct empty; | ^^^^^^^^^^^^ error[E0075]: SIMD vector cannot be empty - --> $DIR/type-len.rs:9:1 + --> $DIR/type-len.rs:8:1 | LL | struct empty2([f32; 0]); | ^^^^^^^^^^^^^ -error[E0076]: SIMD vector should be homogeneous - --> $DIR/type-len.rs:15:1 +error[E0076]: SIMD vector's only field must be an array + --> $DIR/type-len.rs:14:1 | LL | struct i64f64(i64, f64); - | ^^^^^^^^^^^^^ SIMD elements must have the same type + | ^^^^^^^^^^^^^ --- not an array -error[E0077]: SIMD vector element type should be a primitive scalar (integer/float/pointer) type - --> $DIR/type-len.rs:20:1 +error[E0076]: SIMD vector's only field must be an array + --> $DIR/type-len.rs:19:1 | LL | struct FooV(Foo, Foo); - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ --- not an array error[E0077]: SIMD vector element type should be a primitive scalar (integer/float/pointer) type - --> $DIR/type-len.rs:23:1 + --> $DIR/type-len.rs:22:1 | LL | struct FooV2([Foo; 2]); | ^^^^^^^^^^^^ error[E0075]: SIMD vector cannot have more than 32768 elements - --> $DIR/type-len.rs:26:1 + --> $DIR/type-len.rs:25:1 | LL | struct TooBig([f32; 65536]); | ^^^^^^^^^^^^^ -error: aborting due to 6 previous errors +error[E0076]: SIMD vector's only field must be an array + --> $DIR/type-len.rs:31:1 + | +LL | struct RGBA { + | ^^^^^^^^^^^ +LL | r: f32, + | ------ not an array + +error: aborting due to 7 previous errors Some errors have detailed explanations: E0075, E0076, E0077. For more information about an error, try `rustc --explain E0075`. diff --git a/tests/ui/span/gated-features-attr-spans.rs b/tests/ui/span/gated-features-attr-spans.rs index 69511ab8e1fc7..55527fa8add01 100644 --- a/tests/ui/span/gated-features-attr-spans.rs +++ b/tests/ui/span/gated-features-attr-spans.rs @@ -1,7 +1,6 @@ #[repr(simd)] //~ ERROR are experimental struct Coord { - x: u32, - y: u32, + v: [u32; 2], } fn main() {} From 076dcaa73efc851c2e05001d2a4b3da449c4f534 Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Fri, 23 Aug 2024 01:59:26 -0700 Subject: [PATCH 07/13] Fix the examples in cg_clif --- .../example/float-minmax-pass.rs | 12 ++++++------ .../rustc_codegen_cranelift/example/std_example.rs | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_codegen_cranelift/example/float-minmax-pass.rs b/compiler/rustc_codegen_cranelift/example/float-minmax-pass.rs index c54574d801d3a..ad46e18c11c0d 100644 --- a/compiler/rustc_codegen_cranelift/example/float-minmax-pass.rs +++ b/compiler/rustc_codegen_cranelift/example/float-minmax-pass.rs @@ -9,13 +9,13 @@ #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] -struct f32x4(pub f32, pub f32, pub f32, pub f32); +struct f32x4(pub [f32; 4]); use std::intrinsics::simd::*; fn main() { - let x = f32x4(1.0, 2.0, 3.0, 4.0); - let y = f32x4(2.0, 1.0, 4.0, 3.0); + let x = f32x4([1.0, 2.0, 3.0, 4.0]); + let y = f32x4([2.0, 1.0, 4.0, 3.0]); #[cfg(not(any(target_arch = "mips", target_arch = "mips64")))] let nan = f32::NAN; @@ -24,13 +24,13 @@ fn main() { #[cfg(any(target_arch = "mips", target_arch = "mips64"))] let nan = f32::from_bits(f32::NAN.to_bits() - 1); - let n = f32x4(nan, nan, nan, nan); + let n = f32x4([nan, nan, nan, nan]); unsafe { let min0 = simd_fmin(x, y); let min1 = simd_fmin(y, x); assert_eq!(min0, min1); - let e = f32x4(1.0, 1.0, 3.0, 3.0); + let e = f32x4([1.0, 1.0, 3.0, 3.0]); assert_eq!(min0, e); let minn = simd_fmin(x, n); assert_eq!(minn, x); @@ -40,7 +40,7 @@ fn main() { let max0 = simd_fmax(x, y); let max1 = simd_fmax(y, x); assert_eq!(max0, max1); - let e = f32x4(2.0, 2.0, 4.0, 4.0); + let e = f32x4([2.0, 2.0, 4.0, 4.0]); assert_eq!(max0, e); let maxn = simd_fmax(x, n); assert_eq!(maxn, x); diff --git a/compiler/rustc_codegen_cranelift/example/std_example.rs b/compiler/rustc_codegen_cranelift/example/std_example.rs index e99763e272233..f27d4ef57e0c0 100644 --- a/compiler/rustc_codegen_cranelift/example/std_example.rs +++ b/compiler/rustc_codegen_cranelift/example/std_example.rs @@ -166,7 +166,7 @@ fn main() { enum Never {} } - foo(I64X2(0, 0)); + foo(I64X2([0, 0])); transmute_fat_pointer(); @@ -204,7 +204,7 @@ fn rust_call_abi() { } #[repr(simd)] -struct I64X2(i64, i64); +struct I64X2([i64; 2]); #[allow(improper_ctypes_definitions)] extern "C" fn foo(_a: I64X2) {} From b0ac14d19315b89371a60a383761ab20205e5d25 Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Fri, 23 Aug 2024 12:21:20 -0700 Subject: [PATCH 08/13] Update the MIRI tests --- .../tests/fail/intrinsics/simd-div-by-zero.rs | 6 +++--- .../tests/fail/intrinsics/simd-div-overflow.rs | 6 +++--- .../intrinsics/simd-reduce-invalid-bool.rs | 4 ++-- .../tests/fail/intrinsics/simd-rem-by-zero.rs | 6 +++--- .../intrinsics/simd-select-bitmask-invalid.rs | 4 ++-- .../intrinsics/simd-select-invalid-bool.rs | 4 ++-- .../tests/fail/intrinsics/simd-shl-too-far.rs | 6 +++--- .../tests/fail/intrinsics/simd-shr-too-far.rs | 6 +++--- .../pass/simd-intrinsic-generic-elements.rs | 18 +++++++++--------- 9 files changed, 30 insertions(+), 30 deletions(-) diff --git a/src/tools/miri/tests/fail/intrinsics/simd-div-by-zero.rs b/src/tools/miri/tests/fail/intrinsics/simd-div-by-zero.rs index ba474332b811c..57a9b66d8ecde 100644 --- a/src/tools/miri/tests/fail/intrinsics/simd-div-by-zero.rs +++ b/src/tools/miri/tests/fail/intrinsics/simd-div-by-zero.rs @@ -4,12 +4,12 @@ use std::intrinsics::simd::simd_div; #[repr(simd)] #[allow(non_camel_case_types)] -struct i32x2(i32, i32); +struct i32x2([i32; 2]); fn main() { unsafe { - let x = i32x2(1, 1); - let y = i32x2(1, 0); + let x = i32x2([1, 1]); + let y = i32x2([1, 0]); simd_div(x, y); //~ERROR: Undefined Behavior: dividing by zero } } diff --git a/src/tools/miri/tests/fail/intrinsics/simd-div-overflow.rs b/src/tools/miri/tests/fail/intrinsics/simd-div-overflow.rs index d01e41de0e4be..8ffc2669828c4 100644 --- a/src/tools/miri/tests/fail/intrinsics/simd-div-overflow.rs +++ b/src/tools/miri/tests/fail/intrinsics/simd-div-overflow.rs @@ -4,12 +4,12 @@ use std::intrinsics::simd::simd_div; #[repr(simd)] #[allow(non_camel_case_types)] -struct i32x2(i32, i32); +struct i32x2([i32; 2]); fn main() { unsafe { - let x = i32x2(1, i32::MIN); - let y = i32x2(1, -1); + let x = i32x2([1, i32::MIN]); + let y = i32x2([1, -1]); simd_div(x, y); //~ERROR: Undefined Behavior: overflow in signed division } } diff --git a/src/tools/miri/tests/fail/intrinsics/simd-reduce-invalid-bool.rs b/src/tools/miri/tests/fail/intrinsics/simd-reduce-invalid-bool.rs index a194f0dd18aa8..ea0f908d996ff 100644 --- a/src/tools/miri/tests/fail/intrinsics/simd-reduce-invalid-bool.rs +++ b/src/tools/miri/tests/fail/intrinsics/simd-reduce-invalid-bool.rs @@ -4,11 +4,11 @@ use std::intrinsics::simd::simd_reduce_any; #[repr(simd)] #[allow(non_camel_case_types)] -struct i32x2(i32, i32); +struct i32x2([i32; 2]); fn main() { unsafe { - let x = i32x2(0, 1); + let x = i32x2([0, 1]); simd_reduce_any(x); //~ERROR: must be all-0-bits or all-1-bits } } diff --git a/src/tools/miri/tests/fail/intrinsics/simd-rem-by-zero.rs b/src/tools/miri/tests/fail/intrinsics/simd-rem-by-zero.rs index cd1e4b8162b36..21c9520efc48a 100644 --- a/src/tools/miri/tests/fail/intrinsics/simd-rem-by-zero.rs +++ b/src/tools/miri/tests/fail/intrinsics/simd-rem-by-zero.rs @@ -4,12 +4,12 @@ use std::intrinsics::simd::simd_rem; #[repr(simd)] #[allow(non_camel_case_types)] -struct i32x2(i32, i32); +struct i32x2([i32; 2]); fn main() { unsafe { - let x = i32x2(1, 1); - let y = i32x2(1, 0); + let x = i32x2([1, 1]); + let y = i32x2([1, 0]); simd_rem(x, y); //~ERROR: Undefined Behavior: calculating the remainder with a divisor of zero } } diff --git a/src/tools/miri/tests/fail/intrinsics/simd-select-bitmask-invalid.rs b/src/tools/miri/tests/fail/intrinsics/simd-select-bitmask-invalid.rs index 96802fae49c23..409098ac3b5df 100644 --- a/src/tools/miri/tests/fail/intrinsics/simd-select-bitmask-invalid.rs +++ b/src/tools/miri/tests/fail/intrinsics/simd-select-bitmask-invalid.rs @@ -5,11 +5,11 @@ use std::intrinsics::simd::simd_select_bitmask; #[repr(simd)] #[allow(non_camel_case_types)] #[derive(Copy, Clone)] -struct i32x2(i32, i32); +struct i32x2([i32; 2]); fn main() { unsafe { - let x = i32x2(0, 1); + let x = i32x2([0, 1]); simd_select_bitmask(0b11111111u8, x, x); //~ERROR: bitmask less than 8 bits long must be filled with 0s for the remaining bits } } diff --git a/src/tools/miri/tests/fail/intrinsics/simd-select-invalid-bool.rs b/src/tools/miri/tests/fail/intrinsics/simd-select-invalid-bool.rs index 388fb2e2a84ae..a81ce95ada62f 100644 --- a/src/tools/miri/tests/fail/intrinsics/simd-select-invalid-bool.rs +++ b/src/tools/miri/tests/fail/intrinsics/simd-select-invalid-bool.rs @@ -5,11 +5,11 @@ use std::intrinsics::simd::simd_select; #[repr(simd)] #[allow(non_camel_case_types)] #[derive(Copy, Clone)] -struct i32x2(i32, i32); +struct i32x2([i32; 2]); fn main() { unsafe { - let x = i32x2(0, 1); + let x = i32x2([0, 1]); simd_select(x, x, x); //~ERROR: must be all-0-bits or all-1-bits } } diff --git a/src/tools/miri/tests/fail/intrinsics/simd-shl-too-far.rs b/src/tools/miri/tests/fail/intrinsics/simd-shl-too-far.rs index 12aa7c10af4e4..ed317254ee66c 100644 --- a/src/tools/miri/tests/fail/intrinsics/simd-shl-too-far.rs +++ b/src/tools/miri/tests/fail/intrinsics/simd-shl-too-far.rs @@ -4,12 +4,12 @@ use std::intrinsics::simd::simd_shl; #[repr(simd)] #[allow(non_camel_case_types)] -struct i32x2(i32, i32); +struct i32x2([i32; 2]); fn main() { unsafe { - let x = i32x2(1, 1); - let y = i32x2(100, 0); + let x = i32x2([1, 1]); + let y = i32x2([100, 0]); simd_shl(x, y); //~ERROR: overflowing shift by 100 in `simd_shl` in lane 0 } } diff --git a/src/tools/miri/tests/fail/intrinsics/simd-shr-too-far.rs b/src/tools/miri/tests/fail/intrinsics/simd-shr-too-far.rs index ada7cf408c4ed..5d2ff1b82ed06 100644 --- a/src/tools/miri/tests/fail/intrinsics/simd-shr-too-far.rs +++ b/src/tools/miri/tests/fail/intrinsics/simd-shr-too-far.rs @@ -4,12 +4,12 @@ use std::intrinsics::simd::simd_shr; #[repr(simd)] #[allow(non_camel_case_types)] -struct i32x2(i32, i32); +struct i32x2([i32; 2]); fn main() { unsafe { - let x = i32x2(1, 1); - let y = i32x2(20, 40); + let x = i32x2([1, 1]); + let y = i32x2([20, 40]); simd_shr(x, y); //~ERROR: overflowing shift by 40 in `simd_shr` in lane 1 } } diff --git a/src/tools/miri/tests/pass/simd-intrinsic-generic-elements.rs b/src/tools/miri/tests/pass/simd-intrinsic-generic-elements.rs index 4a87f8c3ca720..9cf0c2ddef333 100644 --- a/src/tools/miri/tests/pass/simd-intrinsic-generic-elements.rs +++ b/src/tools/miri/tests/pass/simd-intrinsic-generic-elements.rs @@ -3,22 +3,22 @@ #[repr(simd)] #[derive(Copy, Clone, Debug, PartialEq)] #[allow(non_camel_case_types)] -struct i32x2(i32, i32); +struct i32x2([i32; 2]); #[repr(simd)] #[derive(Copy, Clone, Debug, PartialEq)] #[allow(non_camel_case_types)] -struct i32x4(i32, i32, i32, i32); +struct i32x4([i32; 4]); #[repr(simd)] #[derive(Copy, Clone, Debug, PartialEq)] #[allow(non_camel_case_types)] -struct i32x8(i32, i32, i32, i32, i32, i32, i32, i32); +struct i32x8([i32; 8]); fn main() { - let _x2 = i32x2(20, 21); - let _x4 = i32x4(40, 41, 42, 43); - let _x8 = i32x8(80, 81, 82, 83, 84, 85, 86, 87); + let _x2 = i32x2([20, 21]); + let _x4 = i32x4([40, 41, 42, 43]); + let _x8 = i32x8([80, 81, 82, 83, 84, 85, 86, 87]); - let _y2 = i32x2(120, 121); - let _y4 = i32x4(140, 141, 142, 143); - let _y8 = i32x8(180, 181, 182, 183, 184, 185, 186, 187); + let _y2 = i32x2([120, 121]); + let _y4 = i32x4([140, 141, 142, 143]); + let _y8 = i32x8([180, 181, 182, 183, 184, 185, 186, 187]); } From ee05de8e5ea88f2664b9bb414ecc045ecae90955 Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Mon, 26 Aug 2024 22:44:26 -0400 Subject: [PATCH 09/13] Re-enable android tests/benches in alloc --- library/alloc/benches/lib.rs | 3 --- library/alloc/tests/string.rs | 3 --- library/alloc/tests/vec.rs | 3 --- library/alloc/tests/vec_deque.rs | 3 --- 4 files changed, 12 deletions(-) diff --git a/library/alloc/benches/lib.rs b/library/alloc/benches/lib.rs index 0561f49c967e5..ae9608ec7bd5c 100644 --- a/library/alloc/benches/lib.rs +++ b/library/alloc/benches/lib.rs @@ -1,6 +1,3 @@ -// Disabling on android for the time being -// See https://github.com/rust-lang/rust/issues/73535#event-3477699747 -#![cfg(not(target_os = "android"))] // Disabling in Miri as these would take too long. #![cfg(not(miri))] #![feature(btree_extract_if)] diff --git a/library/alloc/tests/string.rs b/library/alloc/tests/string.rs index c5bc4185a3670..dc03c4860e84b 100644 --- a/library/alloc/tests/string.rs +++ b/library/alloc/tests/string.rs @@ -723,7 +723,6 @@ fn test_reserve_exact() { #[test] #[cfg_attr(miri, ignore)] // Miri does not support signalling OOM -#[cfg_attr(target_os = "android", ignore)] // Android used in CI has a broken dlmalloc fn test_try_with_capacity() { let string = String::try_with_capacity(1000).unwrap(); assert_eq!(0, string.len()); @@ -734,7 +733,6 @@ fn test_try_with_capacity() { #[test] #[cfg_attr(miri, ignore)] // Miri does not support signalling OOM -#[cfg_attr(target_os = "android", ignore)] // Android used in CI has a broken dlmalloc fn test_try_reserve() { // These are the interesting cases: // * exactly isize::MAX should never trigger a CapacityOverflow (can be OOM) @@ -803,7 +801,6 @@ fn test_try_reserve() { #[test] #[cfg_attr(miri, ignore)] // Miri does not support signalling OOM -#[cfg_attr(target_os = "android", ignore)] // Android used in CI has a broken dlmalloc fn test_try_reserve_exact() { // This is exactly the same as test_try_reserve with the method changed. // See that test for comments. diff --git a/library/alloc/tests/vec.rs b/library/alloc/tests/vec.rs index fd2ddbf59e42d..3722fb06a6a8a 100644 --- a/library/alloc/tests/vec.rs +++ b/library/alloc/tests/vec.rs @@ -1695,7 +1695,6 @@ fn test_reserve_exact() { #[test] #[cfg_attr(miri, ignore)] // Miri does not support signalling OOM -#[cfg_attr(target_os = "android", ignore)] // Android used in CI has a broken dlmalloc fn test_try_with_capacity() { let mut vec: Vec = Vec::try_with_capacity(5).unwrap(); assert_eq!(0, vec.len()); @@ -1707,7 +1706,6 @@ fn test_try_with_capacity() { #[test] #[cfg_attr(miri, ignore)] // Miri does not support signalling OOM -#[cfg_attr(target_os = "android", ignore)] // Android used in CI has a broken dlmalloc fn test_try_reserve() { // These are the interesting cases: // * exactly isize::MAX should never trigger a CapacityOverflow (can be OOM) @@ -1803,7 +1801,6 @@ fn test_try_reserve() { #[test] #[cfg_attr(miri, ignore)] // Miri does not support signalling OOM -#[cfg_attr(target_os = "android", ignore)] // Android used in CI has a broken dlmalloc fn test_try_reserve_exact() { // This is exactly the same as test_try_reserve with the method changed. // See that test for comments. diff --git a/library/alloc/tests/vec_deque.rs b/library/alloc/tests/vec_deque.rs index db972122fef2a..f32ba8d5aa461 100644 --- a/library/alloc/tests/vec_deque.rs +++ b/library/alloc/tests/vec_deque.rs @@ -1185,7 +1185,6 @@ fn test_reserve_exact_2() { #[test] #[cfg_attr(miri, ignore)] // Miri does not support signalling OOM -#[cfg_attr(target_os = "android", ignore)] // Android used in CI has a broken dlmalloc fn test_try_with_capacity() { let vec: VecDeque = VecDeque::try_with_capacity(5).unwrap(); assert_eq!(0, vec.len()); @@ -1196,7 +1195,6 @@ fn test_try_with_capacity() { #[test] #[cfg_attr(miri, ignore)] // Miri does not support signalling OOM -#[cfg_attr(target_os = "android", ignore)] // Android used in CI has a broken dlmalloc fn test_try_reserve() { // These are the interesting cases: // * exactly isize::MAX should never trigger a CapacityOverflow (can be OOM) @@ -1292,7 +1290,6 @@ fn test_try_reserve() { #[test] #[cfg_attr(miri, ignore)] // Miri does not support signalling OOM -#[cfg_attr(target_os = "android", ignore)] // Android used in CI has a broken dlmalloc fn test_try_reserve_exact() { // This is exactly the same as test_try_reserve with the method changed. // See that test for comments. From 83de14c4ffeaa58874b0bbdb2da1ce3b32798b90 Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Mon, 26 Aug 2024 23:00:21 -0400 Subject: [PATCH 10/13] Enable some ilog2 tests as well --- library/core/tests/num/int_log.rs | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/library/core/tests/num/int_log.rs b/library/core/tests/num/int_log.rs index 2320a7acc35ac..60902752dab64 100644 --- a/library/core/tests/num/int_log.rs +++ b/library/core/tests/num/int_log.rs @@ -1,7 +1,4 @@ -//! This tests the `Integer::{ilog,log2,log10}` methods. These tests are in a -//! separate file because there's both a large number of them, and not all tests -//! can be run on Android. This is because in Android `ilog2` uses an imprecise -//! approximation:https://github.com/rust-lang/rust/blob/4825e12fc9c79954aa0fe18f5521efa6c19c7539/src/libstd/sys/unix/android.rs#L27-L53 +//! Tests for the `Integer::{ilog,log2,log10}` methods. #[test] fn checked_ilog() { @@ -48,6 +45,10 @@ fn checked_ilog2() { assert_eq!(0i8.checked_ilog2(), None); assert_eq!(0i16.checked_ilog2(), None); + assert_eq!(8192u16.checked_ilog2(), Some((8192f32).log2() as u32)); + assert_eq!(32768u16.checked_ilog2(), Some((32768f32).log2() as u32)); + assert_eq!(8192i16.checked_ilog2(), Some((8192f32).log2() as u32)); + for i in 1..=u8::MAX { assert_eq!(i.checked_ilog2(), Some((i as f32).log2() as u32), "checking {i}"); } @@ -77,15 +78,6 @@ fn checked_ilog2() { } } -// Validate cases that fail on Android's imprecise float ilog2 implementation. -#[test] -#[cfg(not(target_os = "android"))] -fn checked_ilog2_not_android() { - assert_eq!(8192u16.checked_ilog2(), Some((8192f32).log2() as u32)); - assert_eq!(32768u16.checked_ilog2(), Some((32768f32).log2() as u32)); - assert_eq!(8192i16.checked_ilog2(), Some((8192f32).log2() as u32)); -} - #[test] fn checked_ilog10() { assert_eq!(0u8.checked_ilog10(), None); From 1eb4cb452b62f82037e192cfb201e8795ce342ff Mon Sep 17 00:00:00 2001 From: Folyd Date: Sat, 8 Jun 2024 23:59:28 -0700 Subject: [PATCH 11/13] Separate core search logic with search ui --- src/librustdoc/html/static/js/search.js | 6807 ++++++++++++----------- src/tools/rustdoc-js/tester.js | 7 +- 2 files changed, 3426 insertions(+), 3388 deletions(-) diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index be0ec425946c2..6f575e60ed42b 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -16,6 +16,7 @@ if (!Array.prototype.toSpliced) { } (function() { +// ==================== Core search logic begin ==================== // This mapping table should match the discriminants of // `rustdoc::formats::item_type::ItemType` type in Rust. const itemTypes = [ @@ -48,35 +49,6 @@ const itemTypes = [ "generic", ]; -const longItemTypes = [ - "keyword", - "primitive type", - "module", - "extern crate", - "re-export", - "struct", - "enum", - "function", - "type alias", - "static", - "trait", - "", - "trait method", - "method", - "struct field", - "enum variant", - "macro", - "assoc type", - "constant", - "assoc const", - "union", - "foreign type", - "existential type", - "attribute macro", - "derive macro", - "trait alias", -]; - // used for special search precedence const TY_GENERIC = itemTypes.indexOf("generic"); const TY_IMPORT = itemTypes.indexOf("import"); @@ -93,44 +65,8 @@ const UNBOXING_LIMIT = 5; const REGEX_IDENT = /\p{ID_Start}\p{ID_Continue}*|_\p{ID_Continue}+/uy; const REGEX_INVALID_TYPE_FILTER = /[^a-z]/ui; -// In the search display, allows to switch between tabs. -function printTab(nb) { - let iter = 0; - let foundCurrentTab = false; - let foundCurrentResultSet = false; - onEachLazy(document.getElementById("search-tabs").childNodes, elem => { - if (nb === iter) { - addClass(elem, "selected"); - foundCurrentTab = true; - } else { - removeClass(elem, "selected"); - } - iter += 1; - }); - const isTypeSearch = (nb > 0 || iter === 1); - iter = 0; - onEachLazy(document.getElementById("results").childNodes, elem => { - if (nb === iter) { - addClass(elem, "active"); - foundCurrentResultSet = true; - } else { - removeClass(elem, "active"); - } - iter += 1; - }); - if (foundCurrentTab && foundCurrentResultSet) { - searchState.currentTab = nb; - // Corrections only kick in on type-based searches. - const correctionsElem = document.getElementsByClassName("search-corrections"); - if (isTypeSearch) { - removeClass(correctionsElem[0], "hidden"); - } else { - addClass(correctionsElem[0], "hidden"); - } - } else if (nb !== 0) { - printTab(0); - } -} +const MAX_RESULTS = 200; +const NO_TYPE_FILTER = -1; /** * The [edit distance] is a metric for measuring the difference between two strings. @@ -240,3651 +176,3756 @@ function editDistance(a, b, limit) { return editDistanceState.calculate(a, b, limit); } -function initSearch(rawSearchIndex) { - const MAX_RESULTS = 200; - const NO_TYPE_FILTER = -1; - /** - * @type {Array} - */ - let searchIndex; - /** - * @type {Map} - */ - let searchIndexDeprecated; - /** - * @type {Map} - */ - let searchIndexEmptyDesc; - /** - * @type {Uint32Array} - */ - let functionTypeFingerprint; - let currentResults; - /** - * Map from normalized type names to integers. Used to make type search - * more efficient. - * - * @type {Map} - */ - const typeNameIdMap = new Map(); - const ALIASES = new Map(); - - /** - * Special type name IDs for searching by array. - */ - const typeNameIdOfArray = buildTypeMapIndex("array"); - /** - * Special type name IDs for searching by slice. - */ - const typeNameIdOfSlice = buildTypeMapIndex("slice"); - /** - * Special type name IDs for searching by both array and slice (`[]` syntax). - */ - const typeNameIdOfArrayOrSlice = buildTypeMapIndex("[]"); - /** - * Special type name IDs for searching by tuple. - */ - const typeNameIdOfTuple = buildTypeMapIndex("tuple"); - /** - * Special type name IDs for searching by unit. - */ - const typeNameIdOfUnit = buildTypeMapIndex("unit"); - /** - * Special type name IDs for searching by both tuple and unit (`()` syntax). - */ - const typeNameIdOfTupleOrUnit = buildTypeMapIndex("()"); - /** - * Special type name IDs for searching `fn`. - */ - const typeNameIdOfFn = buildTypeMapIndex("fn"); - /** - * Special type name IDs for searching `fnmut`. - */ - const typeNameIdOfFnMut = buildTypeMapIndex("fnmut"); - /** - * Special type name IDs for searching `fnonce`. - */ - const typeNameIdOfFnOnce = buildTypeMapIndex("fnonce"); - /** - * Special type name IDs for searching higher order functions (`->` syntax). - */ - const typeNameIdOfHof = buildTypeMapIndex("->"); - - /** - * Add an item to the type Name->ID map, or, if one already exists, use it. - * Returns the number. If name is "" or null, return null (pure generic). - * - * This is effectively string interning, so that function matching can be - * done more quickly. Two types with the same name but different item kinds - * get the same ID. - * - * @param {string} name - * @param {boolean} isAssocType - True if this is an assoc type - * - * @returns {integer} - */ - function buildTypeMapIndex(name, isAssocType) { - if (name === "" || name === null) { - return null; - } - - if (typeNameIdMap.has(name)) { - const obj = typeNameIdMap.get(name); - obj.assocOnly = isAssocType && obj.assocOnly; - return obj.id; - } else { - const id = typeNameIdMap.size; - typeNameIdMap.set(name, {id, assocOnly: isAssocType}); - return id; - } - } - - function isSpecialStartCharacter(c) { - return "<\"".indexOf(c) !== -1; - } +function isEndCharacter(c) { + return "=,>-])".indexOf(c) !== -1; +} - function isEndCharacter(c) { - return "=,>-])".indexOf(c) !== -1; - } +/** + * Returns `true` if the given `c` character is a separator. + * + * @param {string} c + * + * @return {boolean} + */ +function isSeparatorCharacter(c) { + return c === "," || c === "="; +} - function itemTypeFromName(typename) { - const index = itemTypes.findIndex(i => i === typename); - if (index < 0) { - throw ["Unknown type filter ", typename]; - } - return index; - } +/** + * Returns `true` if the current parser position is starting with "->". + * + * @param {ParserState} parserState + * + * @return {boolean} + */ +function isReturnArrow(parserState) { + return parserState.userQuery.slice(parserState.pos, parserState.pos + 2) === "->"; +} - /** - * If we encounter a `"`, then we try to extract the string from it until we find another `"`. - * - * This function will throw an error in the following cases: - * * There is already another string element. - * * We are parsing a generic argument. - * * There is more than one element. - * * There is no closing `"`. - * - * @param {ParsedQuery} query - * @param {ParserState} parserState - * @param {boolean} isInGenerics - */ - function getStringElem(query, parserState, isInGenerics) { - if (isInGenerics) { - throw ["Unexpected ", "\"", " in generics"]; - } else if (query.literalSearch) { - throw ["Cannot have more than one literal search element"]; - } else if (parserState.totalElems - parserState.genericsElems > 0) { - throw ["Cannot use literal search when there is more than one element"]; +/** + * Increase current parser position until it doesn't find a whitespace anymore. + * + * @param {ParserState} parserState + */ +function skipWhitespace(parserState) { + while (parserState.pos < parserState.userQuery.length) { + const c = parserState.userQuery[parserState.pos]; + if (c !== " ") { + break; } parserState.pos += 1; - const start = parserState.pos; - const end = getIdentEndPosition(parserState); - if (parserState.pos >= parserState.length) { - throw ["Unclosed ", "\""]; - } else if (parserState.userQuery[end] !== "\"") { - throw ["Unexpected ", parserState.userQuery[end], " in a string element"]; - } else if (start === end) { - throw ["Cannot have empty string element"]; - } - // To skip the quote at the end. - parserState.pos += 1; - query.literalSearch = true; - } - - /** - * Returns `true` if the current parser position is starting with "::". - * - * @param {ParserState} parserState - * - * @return {boolean} - */ - function isPathStart(parserState) { - return parserState.userQuery.slice(parserState.pos, parserState.pos + 2) === "::"; - } - - /** - * Returns `true` if the current parser position is starting with "->". - * - * @param {ParserState} parserState - * - * @return {boolean} - */ - function isReturnArrow(parserState) { - return parserState.userQuery.slice(parserState.pos, parserState.pos + 2) === "->"; } +} - /** - * If the current parser position is at the beginning of an identifier, - * move the position to the end of it and return `true`. Otherwise, return `false`. - * - * @param {ParserState} parserState - * - * @return {boolean} - */ - function consumeIdent(parserState) { - REGEX_IDENT.lastIndex = parserState.pos; - const match = parserState.userQuery.match(REGEX_IDENT); - if (match) { - parserState.pos += match[0].length; +/** + * Returns `true` if the previous character is `lookingFor`. + * + * @param {ParserState} parserState + * @param {String} lookingFor + * + * @return {boolean} + */ +function prevIs(parserState, lookingFor) { + let pos = parserState.pos; + while (pos > 0) { + const c = parserState.userQuery[pos - 1]; + if (c === lookingFor) { return true; + } else if (c !== " ") { + break; } - return false; + pos -= 1; } + return false; +} - /** - * Returns `true` if the given `c` character is a separator. - * - * @param {string} c - * - * @return {boolean} - */ - function isSeparatorCharacter(c) { - return c === "," || c === "="; - } +/** + * Returns `true` if the last element in the `elems` argument has generics. + * + * @param {Array} elems + * @param {ParserState} parserState + * + * @return {boolean} + */ +function isLastElemGeneric(elems, parserState) { + return (elems.length > 0 && elems[elems.length - 1].generics.length > 0) || + prevIs(parserState, ">"); +} - /** - * Returns `true` if the given `c` character is a path separator. For example - * `:` in `a::b` or a whitespace in `a b`. - * - * @param {string} c - * - * @return {boolean} - */ - function isPathSeparator(c) { - return c === ":" || c === " "; +function getFilteredNextElem(query, parserState, elems, isInGenerics) { + const start = parserState.pos; + if (parserState.userQuery[parserState.pos] === ":" && !isPathStart(parserState)) { + throw ["Expected type filter before ", ":"]; } - - /** - * Returns `true` if the previous character is `lookingFor`. - * - * @param {ParserState} parserState - * @param {String} lookingFor - * - * @return {boolean} - */ - function prevIs(parserState, lookingFor) { - let pos = parserState.pos; - while (pos > 0) { - const c = parserState.userQuery[pos - 1]; - if (c === lookingFor) { - return true; - } else if (c !== " ") { - break; - } - pos -= 1; + getNextElem(query, parserState, elems, isInGenerics); + if (parserState.userQuery[parserState.pos] === ":" && !isPathStart(parserState)) { + if (parserState.typeFilter !== null) { + throw [ + "Unexpected ", + ":", + " (expected path after type filter ", + parserState.typeFilter + ":", + ")", + ]; } - return false; + if (elems.length === 0) { + throw ["Expected type filter before ", ":"]; + } else if (query.literalSearch) { + throw ["Cannot use quotes on type filter"]; + } + // The type filter doesn't count as an element since it's a modifier. + const typeFilterElem = elems.pop(); + checkExtraTypeFilterCharacters(start, parserState); + parserState.typeFilter = typeFilterElem.name; + parserState.pos += 1; + parserState.totalElems -= 1; + query.literalSearch = false; + getNextElem(query, parserState, elems, isInGenerics); } +} - /** - * Returns `true` if the last element in the `elems` argument has generics. - * - * @param {Array} elems - * @param {ParserState} parserState - * - * @return {boolean} - */ - function isLastElemGeneric(elems, parserState) { - return (elems.length > 0 && elems[elems.length - 1].generics.length > 0) || - prevIs(parserState, ">"); +/** + * This function parses the next query element until it finds `endChar`, + * calling `getNextElem` to collect each element. + * + * If there is no `endChar`, this function will implicitly stop at the end + * without raising an error. + * + * @param {ParsedQuery} query + * @param {ParserState} parserState + * @param {Array} elems - This is where the new {QueryElement} will be added. + * @param {string} endChar - This function will stop when it'll encounter this + * character. + * @returns {{foundSeparator: bool}} + */ +function getItemsBefore(query, parserState, elems, endChar) { + let foundStopChar = true; + let foundSeparator = false; + + // If this is a generic, keep the outer item's type filter around. + const oldTypeFilter = parserState.typeFilter; + parserState.typeFilter = null; + const oldIsInBinding = parserState.isInBinding; + parserState.isInBinding = null; + + // ML-style Higher Order Function notation + // + // a way to search for any closure or fn pointer regardless of + // which closure trait is used + // + // Looks like this: + // + // `option, (t -> u) -> option` + // ^^^^^^ + // + // The Rust-style closure notation is implemented in getNextElem + let hofParameters = null; + + let extra = ""; + if (endChar === ">") { + extra = "<"; + } else if (endChar === "]") { + extra = "["; + } else if (endChar === ")") { + extra = "("; + } else if (endChar === "") { + extra = "->"; + } else { + extra = endChar; } - /** - * Increase current parser position until it doesn't find a whitespace anymore. - * - * @param {ParserState} parserState - */ - function skipWhitespace(parserState) { - while (parserState.pos < parserState.userQuery.length) { - const c = parserState.userQuery[parserState.pos]; - if (c !== " ") { - break; + while (parserState.pos < parserState.length) { + const c = parserState.userQuery[parserState.pos]; + if (c === endChar) { + if (parserState.isInBinding) { + throw ["Unexpected ", endChar, " after ", "="]; + } + break; + } else if (endChar !== "" && isReturnArrow(parserState)) { + // ML-style HOF notation only works when delimited in something, + // otherwise a function arrow starts the return type of the top + if (parserState.isInBinding) { + throw ["Unexpected ", "->", " after ", "="]; + } + hofParameters = [...elems]; + elems.length = 0; + parserState.pos += 2; + foundStopChar = true; + foundSeparator = false; + continue; + } else if (c === " ") { + parserState.pos += 1; + continue; + } else if (isSeparatorCharacter(c)) { + parserState.pos += 1; + foundStopChar = true; + foundSeparator = true; + continue; + } else if (c === ":" && isPathStart(parserState)) { + throw ["Unexpected ", "::", ": paths cannot start with ", "::"]; + } else if (isEndCharacter(c)) { + throw ["Unexpected ", c, " after ", extra]; + } + if (!foundStopChar) { + let extra = []; + if (isLastElemGeneric(query.elems, parserState)) { + extra = [" after ", ">"]; + } else if (prevIs(parserState, "\"")) { + throw ["Cannot have more than one element if you use quotes"]; + } + if (endChar !== "") { + throw [ + "Expected ", + ",", + ", ", + "=", + ", or ", + endChar, + ...extra, + ", found ", + c, + ]; } + throw [ + "Expected ", + ",", + " or ", + "=", + ...extra, + ", found ", + c, + ]; + } + const posBefore = parserState.pos; + getFilteredNextElem(query, parserState, elems, endChar !== ""); + if (endChar !== "" && parserState.pos >= parserState.length) { + throw ["Unclosed ", extra]; + } + // This case can be encountered if `getNextElem` encountered a "stop character" + // right from the start. For example if you have `,,` or `<>`. In this case, + // we simply move up the current position to continue the parsing. + if (posBefore === parserState.pos) { parserState.pos += 1; } + foundStopChar = false; } - - function makePrimitiveElement(name, extra) { - return Object.assign({ - name, - id: null, - fullPath: [name], - pathWithoutLast: [], - pathLast: name, - normalizedPathLast: name, - generics: [], - bindings: new Map(), - typeFilter: "primitive", - bindingName: null, - }, extra); + if (parserState.pos >= parserState.length && endChar !== "") { + throw ["Unclosed ", extra]; + } + // We are either at the end of the string or on the `endChar` character, let's move + // forward in any case. + parserState.pos += 1; + + if (hofParameters) { + // Commas in a HOF don't cause wrapping parens to become a tuple. + // If you want a one-tuple with a HOF in it, write `((a -> b),)`. + foundSeparator = false; + // HOFs can't have directly nested bindings. + if ([...elems, ...hofParameters].some(x => x.bindingName) + || parserState.isInBinding) { + throw ["Unexpected ", "=", " within ", "->"]; + } + // HOFs are represented the same way closures are. + // The arguments are wrapped in a tuple, and the output + // is a binding, even though the compiler doesn't technically + // represent fn pointers that way. + const hofElem = makePrimitiveElement("->", { + generics: hofParameters, + bindings: new Map([["output", [...elems]]]), + typeFilter: null, + }); + elems.length = 0; + elems[0] = hofElem; } - /** - * @param {ParsedQuery} query - * @param {ParserState} parserState - * @param {string} name - Name of the query element. - * @param {Array} generics - List of generics of this query element. - * - * @return {QueryElement} - The newly created `QueryElement`. - */ - function createQueryElement(query, parserState, name, generics, isInGenerics) { - const path = name.trim(); - if (path.length === 0 && generics.length === 0) { - throw ["Unexpected ", parserState.userQuery[parserState.pos]]; - } - if (query.literalSearch && parserState.totalElems - parserState.genericsElems > 0) { - throw ["Cannot have more than one element if you use quotes"]; + parserState.typeFilter = oldTypeFilter; + parserState.isInBinding = oldIsInBinding; + + return { foundSeparator }; +} + +/** + * @param {ParsedQuery} query + * @param {ParserState} parserState + * @param {Array} elems - This is where the new {QueryElement} will be added. + * @param {boolean} isInGenerics + */ +function getNextElem(query, parserState, elems, isInGenerics) { + const generics = []; + + skipWhitespace(parserState); + let start = parserState.pos; + let end; + if ("[(".indexOf(parserState.userQuery[parserState.pos]) !== -1) { + let endChar = ")"; + let name = "()"; + let friendlyName = "tuple"; + + if (parserState.userQuery[parserState.pos] === "[") { + endChar = "]"; + name = "[]"; + friendlyName = "slice"; } + parserState.pos += 1; + const { foundSeparator } = getItemsBefore(query, parserState, generics, endChar); const typeFilter = parserState.typeFilter; + const bindingName = parserState.isInBinding; parserState.typeFilter = null; - if (name === "!") { - if (typeFilter !== null && typeFilter !== "primitive") { + parserState.isInBinding = null; + for (const gen of generics) { + if (gen.bindingName !== null) { + throw ["Type parameter ", "=", ` cannot be within ${friendlyName} `, name]; + } + } + if (name === "()" && !foundSeparator && generics.length === 1 + && typeFilter === null) { + elems.push(generics[0]); + } else if (name === "()" && generics.length === 1 && generics[0].name === "->") { + // `primitive:(a -> b)` parser to `primitive:"->"` + // not `primitive:"()"<"->">` + generics[0].typeFilter = typeFilter; + elems.push(generics[0]); + } else { + if (typeFilter !== null && typeFilter !== "primitive") { throw [ - "Invalid search type: primitive never type ", - "!", + "Invalid search type: primitive ", + name, " and ", typeFilter, " both specified", ]; } - if (generics.length !== 0) { - throw [ - "Never type ", - "!", - " does not accept generic parameters", - ]; - } - const bindingName = parserState.isInBinding; - parserState.isInBinding = null; - return makePrimitiveElement("never", { bindingName }); - } - const quadcolon = /::\s*::/.exec(path); - if (path.startsWith("::")) { - throw ["Paths cannot start with ", "::"]; - } else if (path.endsWith("::")) { - throw ["Paths cannot end with ", "::"]; - } else if (quadcolon !== null) { - throw ["Unexpected ", quadcolon[0]]; - } - const pathSegments = path.split(/(?:::\s*)|(?:\s+(?:::\s*)?)/); - // In case we only have something like `

`, there is no name. - if (pathSegments.length === 0 || (pathSegments.length === 1 && pathSegments[0] === "")) { - if (generics.length > 0 || prevIs(parserState, ">")) { - throw ["Found generics without a path"]; - } else { - throw ["Unexpected ", parserState.userQuery[parserState.pos]]; - } - } - for (const [i, pathSegment] of pathSegments.entries()) { - if (pathSegment === "!") { - if (i !== 0) { - throw ["Never type ", "!", " is not associated item"]; - } - pathSegments[i] = "never"; + parserState.totalElems += 1; + if (isInGenerics) { + parserState.genericsElems += 1; } + elems.push(makePrimitiveElement(name, { bindingName, generics })); } - parserState.totalElems += 1; - if (isInGenerics) { - parserState.genericsElems += 1; + } else if (parserState.userQuery[parserState.pos] === "&") { + if (parserState.typeFilter !== null && parserState.typeFilter !== "primitive") { + throw [ + "Invalid search type: primitive ", + "&", + " and ", + parserState.typeFilter, + " both specified", + ]; } - const bindingName = parserState.isInBinding; - parserState.isInBinding = null; - const bindings = new Map(); - const pathLast = pathSegments[pathSegments.length - 1]; - return { - name: name.trim(), - id: null, - fullPath: pathSegments, - pathWithoutLast: pathSegments.slice(0, pathSegments.length - 1), - pathLast, - normalizedPathLast: pathLast.replace(/_/g, ""), - generics: generics.filter(gen => { - // Syntactically, bindings are parsed as generics, - // but the query engine treats them differently. - if (gen.bindingName !== null) { - if (gen.name !== null) { - gen.bindingName.generics.unshift(gen); - } - bindings.set(gen.bindingName.name, gen.bindingName.generics); - return false; - } - return true; - }), - bindings, - typeFilter, - bindingName, - }; - } - - /** - * This function goes through all characters until it reaches an invalid ident character or the - * end of the query. It returns the position of the last character of the ident. - * - * @param {ParserState} parserState - * - * @return {integer} - */ - function getIdentEndPosition(parserState) { - let afterIdent = consumeIdent(parserState); - let end = parserState.pos; - let macroExclamation = -1; - while (parserState.pos < parserState.length) { - const c = parserState.userQuery[parserState.pos]; - if (c === "!") { - if (macroExclamation !== -1) { - throw ["Cannot have more than one ", "!", " in an ident"]; - } else if (parserState.pos + 1 < parserState.length) { - const pos = parserState.pos; - parserState.pos++; - const beforeIdent = consumeIdent(parserState); - parserState.pos = pos; - if (beforeIdent) { - throw ["Unexpected ", "!", ": it can only be at the end of an ident"]; - } - } - if (afterIdent) macroExclamation = parserState.pos; - } else if (isPathSeparator(c)) { - if (c === ":") { - if (!isPathStart(parserState)) { - break; - } - // Skip current ":". - parserState.pos += 1; - } else { - while (parserState.pos + 1 < parserState.length) { - const next_c = parserState.userQuery[parserState.pos + 1]; - if (next_c !== " ") { - break; - } - parserState.pos += 1; - } - } - if (macroExclamation !== -1) { - throw ["Cannot have associated items in macros"]; - } - } else if ( - c === "[" || - c === "(" || - isEndCharacter(c) || - isSpecialStartCharacter(c) || - isSeparatorCharacter(c) - ) { - break; - } else if (parserState.pos > 0) { - throw ["Unexpected ", c, " after ", parserState.userQuery[parserState.pos - 1], - " (not a valid identifier)"]; - } else { - throw ["Unexpected ", c, " (not a valid identifier)"]; - } + parserState.typeFilter = null; + parserState.pos += 1; + let c = parserState.userQuery[parserState.pos]; + while (c === " " && parserState.pos < parserState.length) { parserState.pos += 1; - afterIdent = consumeIdent(parserState); - end = parserState.pos; + c = parserState.userQuery[parserState.pos]; } - if (macroExclamation !== -1) { - if (parserState.typeFilter === null) { - parserState.typeFilter = "macro"; - } else if (parserState.typeFilter !== "macro") { - throw [ - "Invalid search type: macro ", - "!", - " and ", - parserState.typeFilter, - " both specified", - ]; - } - end = macroExclamation; + const generics = []; + if (parserState.userQuery.slice(parserState.pos, parserState.pos + 3) === "mut") { + generics.push(makePrimitiveElement("mut", { typeFilter: "keyword" })); + parserState.pos += 3; + c = parserState.userQuery[parserState.pos]; } - return end; - } - - function getFilteredNextElem(query, parserState, elems, isInGenerics) { - const start = parserState.pos; - if (parserState.userQuery[parserState.pos] === ":" && !isPathStart(parserState)) { - throw ["Expected type filter before ", ":"]; + while (c === " " && parserState.pos < parserState.length) { + parserState.pos += 1; + c = parserState.userQuery[parserState.pos]; + } + if (!isEndCharacter(c) && parserState.pos < parserState.length) { + getFilteredNextElem(query, parserState, generics, isInGenerics); + } + elems.push(makePrimitiveElement("reference", { generics })); + } else { + const isStringElem = parserState.userQuery[start] === "\""; + // We handle the strings on their own mostly to make code easier to follow. + if (isStringElem) { + start += 1; + getStringElem(query, parserState, isInGenerics); + end = parserState.pos - 1; + } else { + end = getIdentEndPosition(parserState); } - getNextElem(query, parserState, elems, isInGenerics); - if (parserState.userQuery[parserState.pos] === ":" && !isPathStart(parserState)) { - if (parserState.typeFilter !== null) { - throw [ - "Unexpected ", - ":", - " (expected path after type filter ", - parserState.typeFilter + ":", - ")", - ]; - } - if (elems.length === 0) { - throw ["Expected type filter before ", ":"]; - } else if (query.literalSearch) { - throw ["Cannot use quotes on type filter"]; + if (parserState.pos < parserState.length && + parserState.userQuery[parserState.pos] === "<" + ) { + if (start >= end) { + throw ["Found generics without a path"]; } - // The type filter doesn't count as an element since it's a modifier. - const typeFilterElem = elems.pop(); - checkExtraTypeFilterCharacters(start, parserState); - parserState.typeFilter = typeFilterElem.name; parserState.pos += 1; - parserState.totalElems -= 1; - query.literalSearch = false; - getNextElem(query, parserState, elems, isInGenerics); - } - } - - /** - * @param {ParsedQuery} query - * @param {ParserState} parserState - * @param {Array} elems - This is where the new {QueryElement} will be added. - * @param {boolean} isInGenerics - */ - function getNextElem(query, parserState, elems, isInGenerics) { - const generics = []; - - skipWhitespace(parserState); - let start = parserState.pos; - let end; - if ("[(".indexOf(parserState.userQuery[parserState.pos]) !== -1) { - let endChar = ")"; - let name = "()"; - let friendlyName = "tuple"; - - if (parserState.userQuery[parserState.pos] === "[") { - endChar = "]"; - name = "[]"; - friendlyName = "slice"; + getItemsBefore(query, parserState, generics, ">"); + } else if (parserState.pos < parserState.length && + parserState.userQuery[parserState.pos] === "(" + ) { + if (start >= end) { + throw ["Found generics without a path"]; + } + if (parserState.isInBinding) { + throw ["Unexpected ", "(", " after ", "="]; } parserState.pos += 1; - const { foundSeparator } = getItemsBefore(query, parserState, generics, endChar); const typeFilter = parserState.typeFilter; - const bindingName = parserState.isInBinding; parserState.typeFilter = null; - parserState.isInBinding = null; - for (const gen of generics) { - if (gen.bindingName !== null) { - throw ["Type parameter ", "=", ` cannot be within ${friendlyName} `, name]; - } - } - if (name === "()" && !foundSeparator && generics.length === 1 && typeFilter === null) { - elems.push(generics[0]); - } else if (name === "()" && generics.length === 1 && generics[0].name === "->") { - // `primitive:(a -> b)` parser to `primitive:"->"` - // not `primitive:"()"<"->">` - generics[0].typeFilter = typeFilter; - elems.push(generics[0]); + getItemsBefore(query, parserState, generics, ")"); + skipWhitespace(parserState); + if (isReturnArrow(parserState)) { + parserState.pos += 2; + skipWhitespace(parserState); + getFilteredNextElem(query, parserState, generics, isInGenerics); + generics[generics.length - 1].bindingName = makePrimitiveElement("output"); } else { - if (typeFilter !== null && typeFilter !== "primitive") { - throw [ - "Invalid search type: primitive ", - name, - " and ", - typeFilter, - " both specified", - ]; - } - parserState.totalElems += 1; - if (isInGenerics) { - parserState.genericsElems += 1; - } - elems.push(makePrimitiveElement(name, { bindingName, generics })); - } - } else if (parserState.userQuery[parserState.pos] === "&") { - if (parserState.typeFilter !== null && parserState.typeFilter !== "primitive") { - throw [ - "Invalid search type: primitive ", - "&", - " and ", - parserState.typeFilter, - " both specified", - ]; - } - parserState.typeFilter = null; - parserState.pos += 1; - let c = parserState.userQuery[parserState.pos]; - while (c === " " && parserState.pos < parserState.length) { - parserState.pos += 1; - c = parserState.userQuery[parserState.pos]; - } - const generics = []; - if (parserState.userQuery.slice(parserState.pos, parserState.pos + 3) === "mut") { - generics.push(makePrimitiveElement("mut", { typeFilter: "keyword"})); - parserState.pos += 3; - c = parserState.userQuery[parserState.pos]; - } - while (c === " " && parserState.pos < parserState.length) { - parserState.pos += 1; - c = parserState.userQuery[parserState.pos]; + generics.push(makePrimitiveElement(null, { + bindingName: makePrimitiveElement("output"), + typeFilter: null, + })); } - if (!isEndCharacter(c) && parserState.pos < parserState.length) { - getFilteredNextElem(query, parserState, generics, isInGenerics); + parserState.typeFilter = typeFilter; + } + if (isStringElem) { + skipWhitespace(parserState); + } + if (start >= end && generics.length === 0) { + return; + } + if (parserState.userQuery[parserState.pos] === "=") { + if (parserState.isInBinding) { + throw ["Cannot write ", "=", " twice in a binding"]; } - elems.push(makePrimitiveElement("reference", { generics })); - } else { - const isStringElem = parserState.userQuery[start] === "\""; - // We handle the strings on their own mostly to make code easier to follow. - if (isStringElem) { - start += 1; - getStringElem(query, parserState, isInGenerics); - end = parserState.pos - 1; - } else { - end = getIdentEndPosition(parserState); + if (!isInGenerics) { + throw ["Type parameter ", "=", " must be within generics list"]; } - if (parserState.pos < parserState.length && - parserState.userQuery[parserState.pos] === "<" - ) { - if (start >= end) { - throw ["Found generics without a path"]; - } - parserState.pos += 1; - getItemsBefore(query, parserState, generics, ">"); - } else if (parserState.pos < parserState.length && - parserState.userQuery[parserState.pos] === "(" - ) { - if (start >= end) { - throw ["Found generics without a path"]; - } - if (parserState.isInBinding) { - throw ["Unexpected ", "(", " after ", "="]; - } - parserState.pos += 1; - const typeFilter = parserState.typeFilter; - parserState.typeFilter = null; - getItemsBefore(query, parserState, generics, ")"); - skipWhitespace(parserState); - if (isReturnArrow(parserState)) { - parserState.pos += 2; - skipWhitespace(parserState); - getFilteredNextElem(query, parserState, generics, isInGenerics); - generics[generics.length - 1].bindingName = makePrimitiveElement("output"); - } else { - generics.push(makePrimitiveElement(null, { - bindingName: makePrimitiveElement("output"), - typeFilter: null, - })); - } - parserState.typeFilter = typeFilter; + const name = parserState.userQuery.slice(start, end).trim(); + if (name === "!") { + throw ["Type parameter ", "=", " key cannot be ", "!", " never type"]; } - if (isStringElem) { - skipWhitespace(parserState); + if (name.includes("!")) { + throw ["Type parameter ", "=", " key cannot be ", "!", " macro"]; } - if (start >= end && generics.length === 0) { - return; + if (name.includes("::")) { + throw ["Type parameter ", "=", " key cannot contain ", "::", " path"]; } - if (parserState.userQuery[parserState.pos] === "=") { - if (parserState.isInBinding) { - throw ["Cannot write ", "=", " twice in a binding"]; - } - if (!isInGenerics) { - throw ["Type parameter ", "=", " must be within generics list"]; - } - const name = parserState.userQuery.slice(start, end).trim(); - if (name === "!") { - throw ["Type parameter ", "=", " key cannot be ", "!", " never type"]; - } - if (name.includes("!")) { - throw ["Type parameter ", "=", " key cannot be ", "!", " macro"]; - } - if (name.includes("::")) { - throw ["Type parameter ", "=", " key cannot contain ", "::", " path"]; - } - if (name.includes(":")) { - throw ["Type parameter ", "=", " key cannot contain ", ":", " type"]; - } - parserState.isInBinding = { name, generics }; - } else { - elems.push( - createQueryElement( - query, - parserState, - parserState.userQuery.slice(start, end), - generics, - isInGenerics, - ), - ); + if (name.includes(":")) { + throw ["Type parameter ", "=", " key cannot contain ", ":", " type"]; } + parserState.isInBinding = { name, generics }; + } else { + elems.push( + createQueryElement( + query, + parserState, + parserState.userQuery.slice(start, end), + generics, + isInGenerics, + ), + ); } } +} - /** - * This function parses the next query element until it finds `endChar`, calling `getNextElem` - * to collect each element. - * - * If there is no `endChar`, this function will implicitly stop at the end without raising an - * error. - * - * @param {ParsedQuery} query - * @param {ParserState} parserState - * @param {Array} elems - This is where the new {QueryElement} will be added. - * @param {string} endChar - This function will stop when it'll encounter this - * character. - * @returns {{foundSeparator: bool}} - */ - function getItemsBefore(query, parserState, elems, endChar) { - let foundStopChar = true; - let foundSeparator = false; +/** + * Checks that the type filter doesn't have unwanted characters like `<>` (which are ignored + * if empty). + * + * @param {ParserState} parserState + */ +function checkExtraTypeFilterCharacters(start, parserState) { + const query = parserState.userQuery.slice(start, parserState.pos).trim(); + + const match = query.match(REGEX_INVALID_TYPE_FILTER); + if (match) { + throw [ + "Unexpected ", + match[0], + " in type filter (before ", + ":", + ")", + ]; + } +} - // If this is a generic, keep the outer item's type filter around. - const oldTypeFilter = parserState.typeFilter; - parserState.typeFilter = null; - const oldIsInBinding = parserState.isInBinding; +/** + * @param {ParsedQuery} query + * @param {ParserState} parserState + * @param {string} name - Name of the query element. + * @param {Array} generics - List of generics of this query element. + * + * @return {QueryElement} - The newly created `QueryElement`. + */ +function createQueryElement(query, parserState, name, generics, isInGenerics) { + const path = name.trim(); + if (path.length === 0 && generics.length === 0) { + throw ["Unexpected ", parserState.userQuery[parserState.pos]]; + } + if (query.literalSearch && parserState.totalElems - parserState.genericsElems > 0) { + throw ["Cannot have more than one element if you use quotes"]; + } + const typeFilter = parserState.typeFilter; + parserState.typeFilter = null; + if (name === "!") { + if (typeFilter !== null && typeFilter !== "primitive") { + throw [ + "Invalid search type: primitive never type ", + "!", + " and ", + typeFilter, + " both specified", + ]; + } + if (generics.length !== 0) { + throw [ + "Never type ", + "!", + " does not accept generic parameters", + ]; + } + const bindingName = parserState.isInBinding; parserState.isInBinding = null; - - // ML-style Higher Order Function notation - // - // a way to search for any closure or fn pointer regardless of - // which closure trait is used - // - // Looks like this: - // - // `option, (t -> u) -> option` - // ^^^^^^ - // - // The Rust-style closure notation is implemented in getNextElem - let hofParameters = null; - - let extra = ""; - if (endChar === ">") { - extra = "<"; - } else if (endChar === "]") { - extra = "["; - } else if (endChar === ")") { - extra = "("; - } else if (endChar === "") { - extra = "->"; + return makePrimitiveElement("never", { bindingName }); + } + const quadcolon = /::\s*::/.exec(path); + if (path.startsWith("::")) { + throw ["Paths cannot start with ", "::"]; + } else if (path.endsWith("::")) { + throw ["Paths cannot end with ", "::"]; + } else if (quadcolon !== null) { + throw ["Unexpected ", quadcolon[0]]; + } + const pathSegments = path.split(/(?:::\s*)|(?:\s+(?:::\s*)?)/); + // In case we only have something like `

`, there is no name. + if (pathSegments.length === 0 + || (pathSegments.length === 1 && pathSegments[0] === "")) { + if (generics.length > 0 || prevIs(parserState, ">")) { + throw ["Found generics without a path"]; } else { - extra = endChar; + throw ["Unexpected ", parserState.userQuery[parserState.pos]]; + } + } + for (const [i, pathSegment] of pathSegments.entries()) { + if (pathSegment === "!") { + if (i !== 0) { + throw ["Never type ", "!", " is not associated item"]; + } + pathSegments[i] = "never"; } + } + parserState.totalElems += 1; + if (isInGenerics) { + parserState.genericsElems += 1; + } + const bindingName = parserState.isInBinding; + parserState.isInBinding = null; + const bindings = new Map(); + const pathLast = pathSegments[pathSegments.length - 1]; + return { + name: name.trim(), + id: null, + fullPath: pathSegments, + pathWithoutLast: pathSegments.slice(0, pathSegments.length - 1), + pathLast, + normalizedPathLast: pathLast.replace(/_/g, ""), + generics: generics.filter(gen => { + // Syntactically, bindings are parsed as generics, + // but the query engine treats them differently. + if (gen.bindingName !== null) { + if (gen.name !== null) { + gen.bindingName.generics.unshift(gen); + } + bindings.set(gen.bindingName.name, gen.bindingName.generics); + return false; + } + return true; + }), + bindings, + typeFilter, + bindingName, + }; +} - while (parserState.pos < parserState.length) { - const c = parserState.userQuery[parserState.pos]; - if (c === endChar) { - if (parserState.isInBinding) { - throw ["Unexpected ", endChar, " after ", "="]; - } - break; - } else if (endChar !== "" && isReturnArrow(parserState)) { - // ML-style HOF notation only works when delimited in something, - // otherwise a function arrow starts the return type of the top - if (parserState.isInBinding) { - throw ["Unexpected ", "->", " after ", "="]; - } - hofParameters = [...elems]; - elems.length = 0; - parserState.pos += 2; - foundStopChar = true; - foundSeparator = false; - continue; - } else if (c === " ") { - parserState.pos += 1; - continue; - } else if (isSeparatorCharacter(c)) { +function makePrimitiveElement(name, extra) { + return Object.assign({ + name, + id: null, + fullPath: [name], + pathWithoutLast: [], + pathLast: name, + normalizedPathLast: name, + generics: [], + bindings: new Map(), + typeFilter: "primitive", + bindingName: null, + }, extra); +} + +/** + * If we encounter a `"`, then we try to extract the string + * from it until we find another `"`. + * + * This function will throw an error in the following cases: + * * There is already another string element. + * * We are parsing a generic argument. + * * There is more than one element. + * * There is no closing `"`. + * + * @param {ParsedQuery} query + * @param {ParserState} parserState + * @param {boolean} isInGenerics + */ +function getStringElem(query, parserState, isInGenerics) { + if (isInGenerics) { + throw ["Unexpected ", "\"", " in generics"]; + } else if (query.literalSearch) { + throw ["Cannot have more than one literal search element"]; + } else if (parserState.totalElems - parserState.genericsElems > 0) { + throw ["Cannot use literal search when there is more than one element"]; + } + parserState.pos += 1; + const start = parserState.pos; + const end = getIdentEndPosition(parserState); + if (parserState.pos >= parserState.length) { + throw ["Unclosed ", "\""]; + } else if (parserState.userQuery[end] !== "\"") { + throw ["Unexpected ", parserState.userQuery[end], " in a string element"]; + } else if (start === end) { + throw ["Cannot have empty string element"]; + } + // To skip the quote at the end. + parserState.pos += 1; + query.literalSearch = true; +} + +/** + * This function goes through all characters until it reaches an invalid ident + * character or the end of the query. It returns the position of the last + * character of the ident. + * + * @param {ParserState} parserState + * + * @return {integer} + */ +function getIdentEndPosition(parserState) { + let afterIdent = consumeIdent(parserState); + let end = parserState.pos; + let macroExclamation = -1; + while (parserState.pos < parserState.length) { + const c = parserState.userQuery[parserState.pos]; + if (c === "!") { + if (macroExclamation !== -1) { + throw ["Cannot have more than one ", "!", " in an ident"]; + } else if (parserState.pos + 1 < parserState.length) { + const pos = parserState.pos; + parserState.pos++; + const beforeIdent = consumeIdent(parserState); + parserState.pos = pos; + if (beforeIdent) { + throw ["Unexpected ", "!", ": it can only be at the end of an ident"]; + } + } + if (afterIdent) macroExclamation = parserState.pos; + } else if (isPathSeparator(c)) { + if (c === ":") { + if (!isPathStart(parserState)) { + break; + } + // Skip current ":". parserState.pos += 1; - foundStopChar = true; - foundSeparator = true; - continue; - } else if (c === ":" && isPathStart(parserState)) { - throw ["Unexpected ", "::", ": paths cannot start with ", "::"]; - } else if (isEndCharacter(c)) { - throw ["Unexpected ", c, " after ", extra]; - } - if (!foundStopChar) { - let extra = []; - if (isLastElemGeneric(query.elems, parserState)) { - extra = [" after ", ">"]; - } else if (prevIs(parserState, "\"")) { - throw ["Cannot have more than one element if you use quotes"]; - } - if (endChar !== "") { - throw [ - "Expected ", - ",", - ", ", - "=", - ", or ", - endChar, - ...extra, - ", found ", - c, - ]; + } else { + while (parserState.pos + 1 < parserState.length) { + const next_c = parserState.userQuery[parserState.pos + 1]; + if (next_c !== " ") { + break; + } + parserState.pos += 1; } - throw [ - "Expected ", - ",", - " or ", - "=", - ...extra, - ", found ", - c, - ]; - } - const posBefore = parserState.pos; - getFilteredNextElem(query, parserState, elems, endChar !== ""); - if (endChar !== "" && parserState.pos >= parserState.length) { - throw ["Unclosed ", extra]; } - // This case can be encountered if `getNextElem` encountered a "stop character" right - // from the start. For example if you have `,,` or `<>`. In this case, we simply move up - // the current position to continue the parsing. - if (posBefore === parserState.pos) { - parserState.pos += 1; + if (macroExclamation !== -1) { + throw ["Cannot have associated items in macros"]; } - foundStopChar = false; - } - if (parserState.pos >= parserState.length && endChar !== "") { - throw ["Unclosed ", extra]; + } else if ( + c === "[" || + c === "(" || + isEndCharacter(c) || + isSpecialStartCharacter(c) || + isSeparatorCharacter(c) + ) { + break; + } else if (parserState.pos > 0) { + throw ["Unexpected ", c, " after ", parserState.userQuery[parserState.pos - 1], + " (not a valid identifier)"]; + } else { + throw ["Unexpected ", c, " (not a valid identifier)"]; } - // We are either at the end of the string or on the `endChar` character, let's move forward - // in any case. parserState.pos += 1; - - if (hofParameters) { - // Commas in a HOF don't cause wrapping parens to become a tuple. - // If you want a one-tuple with a HOF in it, write `((a -> b),)`. - foundSeparator = false; - // HOFs can't have directly nested bindings. - if ([...elems, ...hofParameters].some(x => x.bindingName) || parserState.isInBinding) { - throw ["Unexpected ", "=", " within ", "->"]; - } - // HOFs are represented the same way closures are. - // The arguments are wrapped in a tuple, and the output - // is a binding, even though the compiler doesn't technically - // represent fn pointers that way. - const hofElem = makePrimitiveElement("->", { - generics: hofParameters, - bindings: new Map([["output", [...elems]]]), - typeFilter: null, - }); - elems.length = 0; - elems[0] = hofElem; + afterIdent = consumeIdent(parserState); + end = parserState.pos; + } + if (macroExclamation !== -1) { + if (parserState.typeFilter === null) { + parserState.typeFilter = "macro"; + } else if (parserState.typeFilter !== "macro") { + throw [ + "Invalid search type: macro ", + "!", + " and ", + parserState.typeFilter, + " both specified", + ]; } + end = macroExclamation; + } + return end; +} - parserState.typeFilter = oldTypeFilter; - parserState.isInBinding = oldIsInBinding; +function isSpecialStartCharacter(c) { + return "<\"".indexOf(c) !== -1; +} + +/** + * Returns `true` if the current parser position is starting with "::". + * + * @param {ParserState} parserState + * + * @return {boolean} + */ +function isPathStart(parserState) { + return parserState.userQuery.slice(parserState.pos, parserState.pos + 2) === "::"; +} - return { foundSeparator }; +/** + * If the current parser position is at the beginning of an identifier, + * move the position to the end of it and return `true`. Otherwise, return `false`. + * + * @param {ParserState} parserState + * + * @return {boolean} + */ +function consumeIdent(parserState) { + REGEX_IDENT.lastIndex = parserState.pos; + const match = parserState.userQuery.match(REGEX_IDENT); + if (match) { + parserState.pos += match[0].length; + return true; } + return false; +} - /** - * Checks that the type filter doesn't have unwanted characters like `<>` (which are ignored - * if empty). - * - * @param {ParserState} parserState - */ - function checkExtraTypeFilterCharacters(start, parserState) { - const query = parserState.userQuery.slice(start, parserState.pos).trim(); +/** + * Returns `true` if the given `c` character is a path separator. For example + * `:` in `a::b` or a whitespace in `a b`. + * + * @param {string} c + * + * @return {boolean} + */ +function isPathSeparator(c) { + return c === ":" || c === " "; +} - const match = query.match(REGEX_INVALID_TYPE_FILTER); - if (match) { - throw [ - "Unexpected ", - match[0], - " in type filter (before ", - ":", - ")", - ]; - } +class VlqHexDecoder { + constructor(string, cons) { + this.string = string; + this.cons = cons; + this.offset = 0; + this.backrefQueue = []; } - - /** - * Parses the provided `query` input to fill `parserState`. If it encounters an error while - * parsing `query`, it'll throw an error. - * - * @param {ParsedQuery} query - * @param {ParserState} parserState - */ - function parseInput(query, parserState) { - let foundStopChar = true; - - while (parserState.pos < parserState.length) { - const c = parserState.userQuery[parserState.pos]; - if (isEndCharacter(c)) { - foundStopChar = true; - if (isSeparatorCharacter(c)) { - parserState.pos += 1; - continue; - } else if (c === "-" || c === ">") { - if (isReturnArrow(parserState)) { - break; - } - throw ["Unexpected ", c, " (did you mean ", "->", "?)"]; - } else if (parserState.pos > 0) { - throw ["Unexpected ", c, " after ", parserState.userQuery[parserState.pos - 1]]; - } - throw ["Unexpected ", c]; - } else if (c === " ") { - skipWhitespace(parserState); - continue; + // call after consuming `{` + decodeList() { + let c = this.string.charCodeAt(this.offset); + const ret = []; + while (c !== 125) { // 125 = "}" + ret.push(this.decode()); + c = this.string.charCodeAt(this.offset); + } + this.offset += 1; // eat cb + return ret; + } + // consumes and returns a list or integer + decode() { + let n = 0; + let c = this.string.charCodeAt(this.offset); + if (c === 123) { // 123 = "{" + this.offset += 1; + return this.decodeList(); + } + while (c < 96) { // 96 = "`" + n = (n << 4) | (c & 0xF); + this.offset += 1; + c = this.string.charCodeAt(this.offset); + } + // last character >= la + n = (n << 4) | (c & 0xF); + const [sign, value] = [n & 1, n >> 1]; + this.offset += 1; + return sign ? -value : value; + } + next() { + const c = this.string.charCodeAt(this.offset); + // sixteen characters after "0" are backref + if (c >= 48 && c < 64) { // 48 = "0", 64 = "@" + this.offset += 1; + return this.backrefQueue[c - 48]; + } + // special exception: 0 doesn't use backref encoding + // it's already one character, and it's always nullish + if (c === 96) { // 96 = "`" + this.offset += 1; + return this.cons(0); + } + const result = this.cons(this.decode()); + this.backrefQueue.unshift(result); + if (this.backrefQueue.length > 16) { + this.backrefQueue.pop(); + } + return result; + } +} +class RoaringBitmap { + constructor(str) { + const strdecoded = atob(str); + const u8array = new Uint8Array(strdecoded.length); + for (let j = 0; j < strdecoded.length; ++j) { + u8array[j] = strdecoded.charCodeAt(j); + } + const has_runs = u8array[0] === 0x3b; + const size = has_runs ? + ((u8array[2] | (u8array[3] << 8)) + 1) : + ((u8array[4] | (u8array[5] << 8) | (u8array[6] << 16) | (u8array[7] << 24))); + let i = has_runs ? 4 : 8; + let is_run; + if (has_runs) { + const is_run_len = Math.floor((size + 7) / 8); + is_run = u8array.slice(i, i + is_run_len); + i += is_run_len; + } else { + is_run = new Uint8Array(); + } + this.keys = []; + this.cardinalities = []; + for (let j = 0; j < size; ++j) { + this.keys.push(u8array[i] | (u8array[i + 1] << 8)); + i += 2; + this.cardinalities.push((u8array[i] | (u8array[i + 1] << 8)) + 1); + i += 2; + } + this.containers = []; + let offsets = null; + if (!has_runs || this.keys.length >= 4) { + offsets = []; + for (let j = 0; j < size; ++j) { + offsets.push(u8array[i] | (u8array[i + 1] << 8) | (u8array[i + 2] << 16) | + (u8array[i + 3] << 24)); + i += 4; } - if (!foundStopChar) { - let extra = ""; - if (isLastElemGeneric(query.elems, parserState)) { - extra = [" after ", ">"]; - } else if (prevIs(parserState, "\"")) { - throw ["Cannot have more than one element if you use quotes"]; - } - if (parserState.typeFilter !== null) { - throw [ - "Expected ", - ",", - " or ", - "->", - ...extra, - ", found ", - c, - ]; - } - throw [ - "Expected ", - ",", - ", ", - ":", - " or ", - "->", - ...extra, - ", found ", - c, - ]; + } + for (let j = 0; j < size; ++j) { + if (offsets && offsets[j] !== i) { + console.log(this.containers); + throw new Error(`corrupt bitmap ${j}: ${i} / ${offsets[j]}`); } - const before = query.elems.length; - getFilteredNextElem(query, parserState, query.elems, false); - if (query.elems.length === before) { - // Nothing was added, weird... Let's increase the position to not remain stuck. - parserState.pos += 1; + if (is_run[j >> 3] & (1 << (j & 0x7))) { + const runcount = (u8array[i] | (u8array[i + 1] << 8)); + i += 2; + this.containers.push(new RoaringBitmapRun( + runcount, + u8array.slice(i, i + (runcount * 4)), + )); + i += runcount * 4; + } else if (this.cardinalities[j] >= 4096) { + this.containers.push(new RoaringBitmapBits(u8array.slice(i, i + 8192))); + i += 8192; + } else { + const end = this.cardinalities[j] * 2; + this.containers.push(new RoaringBitmapArray( + this.cardinalities[j], + u8array.slice(i, i + end), + )); + i += end; } - foundStopChar = false; - } - if (parserState.typeFilter !== null) { - throw [ - "Unexpected ", - ":", - " (expected path after type filter ", - parserState.typeFilter + ":", - ")", - ]; } - while (parserState.pos < parserState.length) { - if (isReturnArrow(parserState)) { - parserState.pos += 2; - skipWhitespace(parserState); - // Get returned elements. - getItemsBefore(query, parserState, query.returned, ""); - // Nothing can come afterward! - if (query.returned.length === 0) { - throw ["Expected at least one item after ", "->"]; - } - break; - } else { - parserState.pos += 1; + } + contains(keyvalue) { + const key = keyvalue >> 16; + const value = keyvalue & 0xFFFF; + for (let i = 0; i < this.keys.length; ++i) { + if (this.keys[i] === key) { + return this.containers[i].contains(value); } } + return false; } +} - /** - * Takes the user search input and returns an empty `ParsedQuery`. - * - * @param {string} userQuery - * - * @return {ParsedQuery} - */ - function newParsedQuery(userQuery) { - return { - original: userQuery, - userQuery: userQuery.toLowerCase(), - elems: [], - returned: [], - // Total number of "top" elements (does not include generics). - foundElems: 0, - // Total number of elements (includes generics). - totalElems: 0, - literalSearch: false, - error: null, - correction: null, - proposeCorrectionFrom: null, - proposeCorrectionTo: null, - // bloom filter build from type ids - typeFingerprint: new Uint32Array(4), - }; +class RoaringBitmapRun { + constructor(runcount, array) { + this.runcount = runcount; + this.array = array; } - - /** - * Build an URL with search parameters. - * - * @param {string} search - The current search being performed. - * @param {string|null} filterCrates - The current filtering crate (if any). - * - * @return {string} - */ - function buildUrl(search, filterCrates) { - let extra = "?search=" + encodeURIComponent(search); - - if (filterCrates !== null) { - extra += "&filter-crate=" + encodeURIComponent(filterCrates); + contains(value) { + const l = this.runcount * 4; + for (let i = 0; i < l; i += 4) { + const start = this.array[i] | (this.array[i + 1] << 8); + const lenm1 = this.array[i + 2] | (this.array[i + 3] << 8); + if (value >= start && value <= (start + lenm1)) { + return true; + } + } + return false; + } +} +class RoaringBitmapArray { + constructor(cardinality, array) { + this.cardinality = cardinality; + this.array = array; + } + contains(value) { + const l = this.cardinality * 2; + for (let i = 0; i < l; i += 2) { + const start = this.array[i] | (this.array[i + 1] << 8); + if (value === start) { + return true; + } } - return getNakedUrl() + extra + window.location.hash; + return false; } +} +class RoaringBitmapBits { + constructor(array) { + this.array = array; + } + contains(value) { + return !!(this.array[value >> 3] & (1 << (value & 7))); + } +} - /** - * Return the filtering crate or `null` if there is none. - * - * @return {string|null} - */ - function getFilterCrates() { - const elem = document.getElementById("crate-search"); - if (elem && - elem.value !== "all crates" && - rawSearchIndex.has(elem.value) - ) { - return elem.value; - } - return null; +class DocSearch { + constructor(rawSearchIndex, rootPath, searchState) { + /** + * @type {Map} + */ + this.searchIndexDeprecated = new Map(); + /** + * @type {Map} + */ + this.searchIndexEmptyDesc = new Map(); + /** + * @type {Uint32Array} + */ + this.functionTypeFingerprint = null; + /** + * Map from normalized type names to integers. Used to make type search + * more efficient. + * + * @type {Map} + */ + this.typeNameIdMap = new Map(); + this.ALIASES = new Map(); + this.rootPath = rootPath; + this.searchState = searchState; + + /** + * Special type name IDs for searching by array. + */ + this.typeNameIdOfArray = this.buildTypeMapIndex("array"); + /** + * Special type name IDs for searching by slice. + */ + this.typeNameIdOfSlice = this.buildTypeMapIndex("slice"); + /** + * Special type name IDs for searching by both array and slice (`[]` syntax). + */ + this.typeNameIdOfArrayOrSlice = this.buildTypeMapIndex("[]"); + /** + * Special type name IDs for searching by tuple. + */ + this.typeNameIdOfTuple = this.buildTypeMapIndex("tuple"); + /** + * Special type name IDs for searching by unit. + */ + this.typeNameIdOfUnit = this.buildTypeMapIndex("unit"); + /** + * Special type name IDs for searching by both tuple and unit (`()` syntax). + */ + this.typeNameIdOfTupleOrUnit = this.buildTypeMapIndex("()"); + /** + * Special type name IDs for searching `fn`. + */ + this.typeNameIdOfFn = this.buildTypeMapIndex("fn"); + /** + * Special type name IDs for searching `fnmut`. + */ + this.typeNameIdOfFnMut = this.buildTypeMapIndex("fnmut"); + /** + * Special type name IDs for searching `fnonce`. + */ + this.typeNameIdOfFnOnce = this.buildTypeMapIndex("fnonce"); + /** + * Special type name IDs for searching higher order functions (`->` syntax). + */ + this.typeNameIdOfHof = this.buildTypeMapIndex("->"); + + /** + * Empty, immutable map used in item search types with no bindings. + * + * @type {Map>} + */ + this.EMPTY_BINDINGS_MAP = new Map(); + + /** + * Empty, immutable map used in item search types with no bindings. + * + * @type {Array} + */ + this.EMPTY_GENERICS_ARRAY = []; + + /** + * Object pool for function types with no bindings or generics. + * This is reset after loading the index. + * + * @type {Map} + */ + this.TYPES_POOL = new Map(); + + /** + * @type {Array} + */ + this.searchIndex = this.buildIndex(rawSearchIndex); } /** - * Parses the query. - * - * The supported syntax by this parser is given in the rustdoc book chapter - * /src/doc/rustdoc/src/read-documentation/search.md + * Add an item to the type Name->ID map, or, if one already exists, use it. + * Returns the number. If name is "" or null, return null (pure generic). * - * When adding new things to the parser, add them there, too! + * This is effectively string interning, so that function matching can be + * done more quickly. Two types with the same name but different item kinds + * get the same ID. * - * @param {string} val - The user query + * @param {string} name + * @param {boolean} isAssocType - True if this is an assoc type * - * @return {ParsedQuery} - The parsed query + * @returns {integer} */ - function parseQuery(userQuery) { - function convertTypeFilterOnElem(elem) { - if (elem.typeFilter !== null) { - let typeFilter = elem.typeFilter; - if (typeFilter === "const") { - typeFilter = "constant"; - } - elem.typeFilter = itemTypeFromName(typeFilter); - } else { - elem.typeFilter = NO_TYPE_FILTER; - } - for (const elem2 of elem.generics) { - convertTypeFilterOnElem(elem2); - } - for (const constraints of elem.bindings.values()) { - for (const constraint of constraints) { - convertTypeFilterOnElem(constraint); - } - } + buildTypeMapIndex(name, isAssocType) { + if (name === "" || name === null) { + return null; } - userQuery = userQuery.trim().replace(/\r|\n|\t/g, " "); - const parserState = { - length: userQuery.length, - pos: 0, - // Total number of elements (includes generics). - totalElems: 0, - genericsElems: 0, - typeFilter: null, - isInBinding: null, - userQuery: userQuery.toLowerCase(), - }; - let query = newParsedQuery(userQuery); - try { - parseInput(query, parserState); - for (const elem of query.elems) { - convertTypeFilterOnElem(elem); - } - for (const elem of query.returned) { - convertTypeFilterOnElem(elem); - } - } catch (err) { - query = newParsedQuery(userQuery); - query.error = err; - return query; - } - if (!query.literalSearch) { - // If there is more than one element in the query, we switch to literalSearch in any - // case. - query.literalSearch = parserState.totalElems > 1; + if (this.typeNameIdMap.has(name)) { + const obj = this.typeNameIdMap.get(name); + obj.assocOnly = isAssocType && obj.assocOnly; + return obj.id; + } else { + const id = this.typeNameIdMap.size; + this.typeNameIdMap.set(name, { id, assocOnly: isAssocType }); + return id; } - query.foundElems = query.elems.length + query.returned.length; - query.totalElems = parserState.totalElems; - return query; } /** - * Creates the query results. + * Convert a list of RawFunctionType / ID to object-based FunctionType. + * + * Crates often have lots of functions in them, and it's common to have a large number of + * functions that operate on a small set of data types, so the search index compresses them + * by encoding function parameter and return types as indexes into an array of names. * - * @param {Array} results_in_args - * @param {Array} results_returned - * @param {Array} results_others - * @param {ParsedQuery} parsedQuery + * Even when a general-purpose compression algorithm is used, this is still a win. + * I checked. https://github.com/rust-lang/rust/pull/98475#issue-1284395985 * - * @return {ResultsTable} + * The format for individual function types is encoded in + * librustdoc/html/render/mod.rs: impl Serialize for RenderType + * + * @param {null|Array} types + * @param {Array<{name: string, ty: number}>} lowercasePaths + * + * @return {Array} */ - function createQueryResults(results_in_args, results_returned, results_others, parsedQuery) { - return { - "in_args": results_in_args, - "returned": results_returned, - "others": results_others, - "query": parsedQuery, - }; + buildItemSearchTypeAll(types, lowercasePaths) { + return types.length > 0 ? + types.map(type => this.buildItemSearchType(type, lowercasePaths)) : + this.EMPTY_GENERICS_ARRAY; } /** - * Executes the parsed query and builds a {ResultsTable}. - * - * @param {ParsedQuery} parsedQuery - The parsed user query - * @param {Object} [filterCrates] - Crate to search in if defined - * @param {Object} [currentCrate] - Current crate, to rank results from this crate higher + * Converts a single type. * - * @return {ResultsTable} + * @param {RawFunctionType} type */ - async function execQuery(parsedQuery, filterCrates, currentCrate) { - const results_others = new Map(), results_in_args = new Map(), - results_returned = new Map(); - - /** - * Add extra data to result objects, and filter items that have been - * marked for removal. - * - * @param {[ResultObject]} results - * @returns {[ResultObject]} - */ - function transformResults(results) { - const duplicates = new Set(); - const out = []; - - for (const result of results) { - if (result.id !== -1) { - const obj = searchIndex[result.id]; - obj.dist = result.dist; - const res = buildHrefAndPath(obj); - obj.displayPath = pathSplitter(res[0]); - - // To be sure than it some items aren't considered as duplicate. - obj.fullPath = res[2] + "|" + obj.ty; - if (duplicates.has(obj.fullPath)) { - continue; - } - - // Exports are specifically not shown if the items they point at - // are already in the results. - if (obj.ty === TY_IMPORT && duplicates.has(res[2])) { - continue; - } - if (duplicates.has(res[2] + "|" + TY_IMPORT)) { - continue; + buildItemSearchType(type, lowercasePaths, isAssocType) { + const PATH_INDEX_DATA = 0; + const GENERICS_DATA = 1; + const BINDINGS_DATA = 2; + let pathIndex, generics, bindings; + if (typeof type === "number") { + pathIndex = type; + generics = this.EMPTY_GENERICS_ARRAY; + bindings = this.EMPTY_BINDINGS_MAP; + } else { + pathIndex = type[PATH_INDEX_DATA]; + generics = this.buildItemSearchTypeAll( + type[GENERICS_DATA], + lowercasePaths, + ); + if (type.length > BINDINGS_DATA && type[BINDINGS_DATA].length > 0) { + bindings = new Map(type[BINDINGS_DATA].map(binding => { + const [assocType, constraints] = binding; + // Associated type constructors are represented sloppily in rustdoc's + // type search, to make the engine simpler. + // + // MyType=Result> is equivalent to MyType>=T> + // and both are, essentially + // MyType)>, except the tuple isn't actually there. + // It's more like the value of a type binding is naturally an array, + // which rustdoc calls "constraints". + // + // As a result, the key should never have generics on it. + return [ + this.buildItemSearchType(assocType, lowercasePaths, true).id, + this.buildItemSearchTypeAll(constraints, lowercasePaths), + ]; + })); + } else { + bindings = this.EMPTY_BINDINGS_MAP; + } + } + /** + * @type {FunctionType} + */ + let result; + if (pathIndex < 0) { + // types less than 0 are generic parameters + // the actual names of generic parameters aren't stored, since they aren't API + result = { + id: pathIndex, + ty: TY_GENERIC, + path: null, + exactPath: null, + generics, + bindings, + }; + } else if (pathIndex === 0) { + // `0` is used as a sentinel because it's fewer bytes than `null` + result = { + id: null, + ty: null, + path: null, + exactPath: null, + generics, + bindings, + }; + } else { + const item = lowercasePaths[pathIndex - 1]; + result = { + id: this.buildTypeMapIndex(item.name, isAssocType), + ty: item.ty, + path: item.path, + exactPath: item.exactPath, + generics, + bindings, + }; + } + const cr = this.TYPES_POOL.get(result.id); + if (cr) { + // Shallow equality check. Since this function is used + // to construct every type object, this should be mostly + // equivalent to a deep equality check, except if there's + // a conflict, we don't keep the old one around, so it's + // not a fully precise implementation of hashcons. + if (cr.generics.length === result.generics.length && + cr.generics !== result.generics && + cr.generics.every((x, i) => result.generics[i] === x) + ) { + result.generics = cr.generics; + } + if (cr.bindings.size === result.bindings.size && cr.bindings !== result.bindings) { + let ok = true; + for (const [k, v] of cr.bindings.entries()) { + const v2 = result.bindings.get(v); + if (!v2) { + ok = false; + break; } - duplicates.add(obj.fullPath); - duplicates.add(res[2]); - - obj.href = res[1]; - out.push(obj); - if (out.length >= MAX_RESULTS) { + if (v !== v2 && v.length === v2.length && v.every((x, i) => v2[i] === x)) { + result.bindings.set(k, v); + } else if (v !== v2) { + ok = false; break; } } + if (ok) { + result.bindings = cr.bindings; + } } - return out; + if (cr.ty === result.ty && cr.path === result.path + && cr.bindings === result.bindings && cr.generics === result.generics + && cr.ty === result.ty + ) { + return cr; + } + } + this.TYPES_POOL.set(result.id, result); + return result; + } + + /** + * Type fingerprints allow fast, approximate matching of types. + * + * This algo creates a compact representation of the type set using a Bloom filter. + * This fingerprint is used three ways: + * + * - It accelerates the matching algorithm by checking the function fingerprint against the + * query fingerprint. If any bits are set in the query but not in the function, it can't + * match. + * + * - The fourth section has the number of distinct items in the set. + * This is the distance function, used for filtering and for sorting. + * + * [^1]: Distance is the relatively naive metric of counting the number of distinct items in + * the function that are not present in the query. + * + * @param {FunctionType|QueryElement} type - a single type + * @param {Uint32Array} output - write the fingerprint to this data structure: uses 128 bits + * @param {Set} fps - Set of distinct items + */ + buildFunctionTypeFingerprint(type, output, fps) { + let input = type.id; + // All forms of `[]`/`()`/`->` get collapsed down to one thing in the bloom filter. + // Differentiating between arrays and slices, if the user asks for it, is + // still done in the matching algorithm. + if (input === this.typeNameIdOfArray || input === this.typeNameIdOfSlice) { + input = this.typeNameIdOfArrayOrSlice; + } + if (input === this.typeNameIdOfTuple || input === this.typeNameIdOfUnit) { + input = this.typeNameIdOfTupleOrUnit; + } + if (input === this.typeNameIdOfFn || input === this.typeNameIdOfFnMut || + input === this.typeNameIdOfFnOnce) { + input = this.typeNameIdOfHof; + } + // http://burtleburtle.net/bob/hash/integer.html + // ~~ is toInt32. It's used before adding, so + // the number stays in safe integer range. + const hashint1 = k => { + k = (~~k + 0x7ed55d16) + (k << 12); + k = (k ^ 0xc761c23c) ^ (k >>> 19); + k = (~~k + 0x165667b1) + (k << 5); + k = (~~k + 0xd3a2646c) ^ (k << 9); + k = (~~k + 0xfd7046c5) + (k << 3); + return (k ^ 0xb55a4f09) ^ (k >>> 16); + }; + const hashint2 = k => { + k = ~k + (k << 15); + k ^= k >>> 12; + k += k << 2; + k ^= k >>> 4; + k = Math.imul(k, 2057); + return k ^ (k >> 16); + }; + if (input !== null) { + const h0a = hashint1(input); + const h0b = hashint2(input); + // Less Hashing, Same Performance: Building a Better Bloom Filter + // doi=10.1.1.72.2442 + const h1a = ~~(h0a + Math.imul(h0b, 2)); + const h1b = ~~(h0a + Math.imul(h0b, 3)); + const h2a = ~~(h0a + Math.imul(h0b, 4)); + const h2b = ~~(h0a + Math.imul(h0b, 5)); + output[0] |= (1 << (h0a % 32)) | (1 << (h1b % 32)); + output[1] |= (1 << (h1a % 32)) | (1 << (h2b % 32)); + output[2] |= (1 << (h2a % 32)) | (1 << (h0b % 32)); + fps.add(input); + } + for (const g of type.generics) { + this.buildFunctionTypeFingerprint(g, output, fps); + } + const fb = { + id: null, + ty: 0, + generics: this.EMPTY_GENERICS_ARRAY, + bindings: this.EMPTY_BINDINGS_MAP, + }; + for (const [k, v] of type.bindings.entries()) { + fb.id = k; + fb.generics = v; + this.buildFunctionTypeFingerprint(fb, output, fps); } + output[3] = fps.size; + } + /** + * Convert raw search index into in-memory search index. + * + * @param {[string, RawSearchIndexCrate][]} rawSearchIndex + */ + buildIndex(rawSearchIndex) { /** - * This function takes a result map, and sorts it by various criteria, including edit - * distance, substring match, and the crate it comes from. + * Convert from RawFunctionSearchType to FunctionSearchType. * - * @param {Results} results - * @param {boolean} isType - * @param {string} preferredCrate - * @returns {Promise<[ResultObject]>} + * Crates often have lots of functions in them, and function signatures are sometimes + * complex, so rustdoc uses a pretty tight encoding for them. This function converts it + * to a simpler, object-based encoding so that the actual search code is more readable + * and easier to debug. + * + * The raw function search type format is generated using serde in + * librustdoc/html/render/mod.rs: IndexItemFunctionType::write_to_string + * + * @param {Array<{name: string, ty: number}>} lowercasePaths + * + * @return {null|FunctionSearchType} */ - async function sortResults(results, isType, preferredCrate) { - const userQuery = parsedQuery.userQuery; - const casedUserQuery = parsedQuery.original; - const result_list = []; - for (const result of results.values()) { - result.item = searchIndex[result.id]; - result.word = searchIndex[result.id].word; - result_list.push(result); - } - - result_list.sort((aaa, bbb) => { - let a, b; - - // sort by exact case-sensitive match - a = (aaa.item.name !== casedUserQuery); - b = (bbb.item.name !== casedUserQuery); - if (a !== b) { - return a - b; + const buildFunctionSearchTypeCallback = lowercasePaths => { + return functionSearchType => { + if (functionSearchType === 0) { + return null; + } + const INPUTS_DATA = 0; + const OUTPUT_DATA = 1; + let inputs, output; + if (typeof functionSearchType[INPUTS_DATA] === "number") { + inputs = [ + this.buildItemSearchType(functionSearchType[INPUTS_DATA], lowercasePaths), + ]; + } else { + inputs = this.buildItemSearchTypeAll( + functionSearchType[INPUTS_DATA], + lowercasePaths, + ); } - - // sort by exact match with regard to the last word (mismatch goes later) - a = (aaa.word !== userQuery); - b = (bbb.word !== userQuery); - if (a !== b) { - return a - b; + if (functionSearchType.length > 1) { + if (typeof functionSearchType[OUTPUT_DATA] === "number") { + output = [ + this.buildItemSearchType( + functionSearchType[OUTPUT_DATA], + lowercasePaths, + ), + ]; + } else { + output = this.buildItemSearchTypeAll( + functionSearchType[OUTPUT_DATA], + lowercasePaths, + ); + } + } else { + output = []; } - - // sort by index of keyword in item name (no literal occurrence goes later) - a = (aaa.index < 0); - b = (bbb.index < 0); - if (a !== b) { - return a - b; + const where_clause = []; + const l = functionSearchType.length; + for (let i = 2; i < l; ++i) { + where_clause.push(typeof functionSearchType[i] === "number" + ? [this.buildItemSearchType(functionSearchType[i], lowercasePaths)] + : this.buildItemSearchTypeAll(functionSearchType[i], lowercasePaths)); } + return { + inputs, output, where_clause, + }; + }; + }; - // Sort by distance in the path part, if specified - // (less changes required to match means higher rankings) - a = aaa.path_dist; - b = bbb.path_dist; - if (a !== b) { - return a - b; - } + const searchIndex = []; + let currentIndex = 0; + let id = 0; - // (later literal occurrence, if any, goes later) - a = aaa.index; - b = bbb.index; - if (a !== b) { - return a - b; - } - - // Sort by distance in the name part, the last part of the path - // (less changes required to match means higher rankings) - a = (aaa.dist); - b = (bbb.dist); - if (a !== b) { - return a - b; - } + // Function type fingerprints are 128-bit bloom filters that are used to + // estimate the distance between function and query. + // This loop counts the number of items to allocate a fingerprint for. + for (const crate of rawSearchIndex.values()) { + // Each item gets an entry in the fingerprint array, and the crate + // does, too + id += crate.t.length + 1; + } + this.functionTypeFingerprint = new Uint32Array((id + 1) * 4); + // This loop actually generates the search item indexes, including + // normalized names, type signature objects and fingerprints, and aliases. + id = 0; - // sort deprecated items later - a = searchIndexDeprecated.get(aaa.item.crate).contains(aaa.item.bitIndex); - b = searchIndexDeprecated.get(bbb.item.crate).contains(bbb.item.bitIndex); - if (a !== b) { - return a - b; - } + for (const [crate, crateCorpus] of rawSearchIndex) { + // a string representing the lengths of each description shard + // a string representing the list of function types + const itemDescShardDecoder = new VlqHexDecoder(crateCorpus.D, noop => noop); + let descShard = { + crate, + shard: 0, + start: 0, + len: itemDescShardDecoder.next(), + promise: null, + resolve: null, + }; + const descShardList = [descShard]; - // sort by crate (current crate comes first) - a = (aaa.item.crate !== preferredCrate); - b = (bbb.item.crate !== preferredCrate); - if (a !== b) { - return a - b; - } + // Deprecated items and items with no description + this.searchIndexDeprecated.set(crate, new RoaringBitmap(crateCorpus.c)); + this.searchIndexEmptyDesc.set(crate, new RoaringBitmap(crateCorpus.e)); + let descIndex = 0; - // sort by item name length (longer goes later) - a = aaa.word.length; - b = bbb.word.length; - if (a !== b) { - return a - b; - } + // This object should have exactly the same set of fields as the "row" + // object defined below. Your JavaScript runtime will thank you. + // https://mathiasbynens.be/notes/shapes-ics + const crateRow = { + crate, + ty: 3, // == ExternCrate + name: crate, + path: "", + descShard, + descIndex, + exactPath: "", + desc: crateCorpus.doc, + parent: undefined, + type: null, + id, + word: crate, + normalizedName: crate.indexOf("_") === -1 ? crate : crate.replace(/_/g, ""), + bitIndex: 0, + implDisambiguator: null, + }; + id += 1; + searchIndex.push(crateRow); + currentIndex += 1; + if (!this.searchIndexEmptyDesc.get(crate).contains(0)) { + descIndex += 1; + } - // sort by item name (lexicographically larger goes later) - a = aaa.word; - b = bbb.word; - if (a !== b) { - return (a > b ? +1 : -1); - } + // a String of one character item type codes + const itemTypes = crateCorpus.t; + // an array of (String) item names + const itemNames = crateCorpus.n; + // an array of [(Number) item index, + // (String) full path] + // an item whose index is not present will fall back to the previous present path + // i.e. if indices 4 and 11 are present, but 5-10 and 12-13 are not present, + // 5-10 will fall back to the path for 4 and 12-13 will fall back to the path for 11 + const itemPaths = new Map(crateCorpus.q); + // An array of [(Number) item index, (Number) path index] + // Used to de-duplicate inlined and re-exported stuff + const itemReexports = new Map(crateCorpus.r); + // an array of (Number) the parent path index + 1 to `paths`, or 0 if none + const itemParentIdxDecoder = new VlqHexDecoder(crateCorpus.i, noop => noop); + // a map Number, string for impl disambiguators + const implDisambiguator = new Map(crateCorpus.b); + // an array of [(Number) item type, + // (String) name] + const paths = crateCorpus.p; + // an array of [(String) alias name + // [Number] index to items] + const aliases = crateCorpus.a; - // sort by description (no description goes later) - a = searchIndexEmptyDesc.get(aaa.item.crate).contains(aaa.item.bitIndex); - b = searchIndexEmptyDesc.get(bbb.item.crate).contains(bbb.item.bitIndex); - if (a !== b) { - return a - b; - } + // an array of [{name: String, ty: Number}] + const lowercasePaths = []; - // sort by type (later occurrence in `itemTypes` goes later) - a = aaa.item.ty; - b = bbb.item.ty; - if (a !== b) { - return a - b; - } + // a string representing the list of function types + const itemFunctionDecoder = new VlqHexDecoder( + crateCorpus.f, + buildFunctionSearchTypeCallback(lowercasePaths), + ); - // sort by path (lexicographically larger goes later) - a = aaa.item.path; - b = bbb.item.path; - if (a !== b) { - return (a > b ? +1 : -1); + // convert `rawPaths` entries into object form + // generate normalizedPaths for function search mode + let len = paths.length; + let lastPath = itemPaths.get(0); + for (let i = 0; i < len; ++i) { + const elem = paths[i]; + const ty = elem[0]; + const name = elem[1]; + let path = null; + if (elem.length > 2) { + path = itemPaths.has(elem[2]) ? itemPaths.get(elem[2]) : lastPath; + lastPath = path; } + const exactPath = elem.length > 3 ? itemPaths.get(elem[3]) : path; - // que sera, sera - return 0; - }); - - return transformResults(result_list); - } - - /** - * This function checks if a list of search query `queryElems` can all be found in the - * search index (`fnTypes`). - * - * This function returns `true` on a match, or `false` if none. If `solutionCb` is - * supplied, it will call that function with mgens, and that callback can accept or - * reject the result bu returning `true` or `false`. If the callback returns false, - * then this function will try with a different solution, or bail with false if it - * runs out of candidates. - * - * @param {Array} fnTypesIn - The objects to check. - * @param {Array} queryElems - The elements from the parsed query. - * @param {[FunctionType]} whereClause - Trait bounds for generic items. - * @param {Map|null} mgensIn - * - Map functions generics to query generics (never modified). - * @param {null|Map -> bool} solutionCb - Called for each `mgens` solution. - * @param {number} unboxingDepth - * - Limit checks that Ty matches Vec, - * but not Vec>>>> - * - * @return {boolean} - Returns true if a match, false otherwise. - */ - function unifyFunctionTypes( - fnTypesIn, - queryElems, - whereClause, - mgensIn, - solutionCb, - unboxingDepth, - ) { - if (unboxingDepth >= UNBOXING_LIMIT) { - return false; - } - /** - * @type Map|null - */ - const mgens = mgensIn === null ? null : new Map(mgensIn); - if (queryElems.length === 0) { - return !solutionCb || solutionCb(mgens); - } - if (!fnTypesIn || fnTypesIn.length === 0) { - return false; + lowercasePaths.push({ ty, name: name.toLowerCase(), path, exactPath }); + paths[i] = { ty, name, path, exactPath }; } - const ql = queryElems.length; - const fl = fnTypesIn.length; - // One element fast path / base case - if (ql === 1 && queryElems[0].generics.length === 0 - && queryElems[0].bindings.size === 0) { - const queryElem = queryElems[0]; - for (const fnType of fnTypesIn) { - if (!unifyFunctionTypeIsMatchCandidate(fnType, queryElem, mgens)) { - continue; - } - if (fnType.id < 0 && queryElem.id < 0) { - if (mgens && mgens.has(fnType.id) && - mgens.get(fnType.id) !== queryElem.id) { - continue; + // convert `item*` into an object form, and construct word indices. + // + // before any analysis is performed lets gather the search terms to + // search against apart from the rest of the data. This is a quick + // operation that is cached for the life of the page state so that + // all other search operations have access to this cached data for + // faster analysis operations + lastPath = ""; + len = itemTypes.length; + let lastName = ""; + let lastWord = ""; + for (let i = 0; i < len; ++i) { + const bitIndex = i + 1; + if (descIndex >= descShard.len && + !this.searchIndexEmptyDesc.get(crate).contains(bitIndex)) { + descShard = { + crate, + shard: descShard.shard + 1, + start: descShard.start + descShard.len, + len: itemDescShardDecoder.next(), + promise: null, + resolve: null, + }; + descIndex = 0; + descShardList.push(descShard); + } + const name = itemNames[i] === "" ? lastName : itemNames[i]; + const word = itemNames[i] === "" ? lastWord : itemNames[i].toLowerCase(); + const path = itemPaths.has(i) ? itemPaths.get(i) : lastPath; + const type = itemFunctionDecoder.next(); + if (type !== null) { + if (type) { + const fp = this.functionTypeFingerprint.subarray(id * 4, (id + 1) * 4); + const fps = new Set(); + for (const t of type.inputs) { + this.buildFunctionTypeFingerprint(t, fp, fps); } - const mgensScratch = new Map(mgens); - mgensScratch.set(fnType.id, queryElem.id); - if (!solutionCb || solutionCb(mgensScratch)) { - return true; + for (const t of type.output) { + this.buildFunctionTypeFingerprint(t, fp, fps); + } + for (const w of type.where_clause) { + for (const t of w) { + this.buildFunctionTypeFingerprint(t, fp, fps); + } } - } else if (!solutionCb || solutionCb(mgens ? new Map(mgens) : null)) { - // unifyFunctionTypeIsMatchCandidate already checks that ids match - return true; } } - for (const fnType of fnTypesIn) { - if (!unifyFunctionTypeIsUnboxCandidate( - fnType, - queryElem, - whereClause, - mgens, - unboxingDepth + 1, - )) { + // This object should have exactly the same set of fields as the "crateRow" + // object defined above. + const itemParentIdx = itemParentIdxDecoder.next(); + const row = { + crate, + ty: itemTypes.charCodeAt(i) - 65, // 65 = "A" + name, + path, + descShard, + descIndex, + exactPath: itemReexports.has(i) ? + itemPaths.get(itemReexports.get(i)) : path, + parent: itemParentIdx > 0 ? paths[itemParentIdx - 1] : undefined, + type, + id, + word, + normalizedName: word.indexOf("_") === -1 ? word : word.replace(/_/g, ""), + bitIndex, + implDisambiguator: implDisambiguator.has(i) ? + implDisambiguator.get(i) : null, + }; + id += 1; + searchIndex.push(row); + lastPath = row.path; + if (!this.searchIndexEmptyDesc.get(crate).contains(bitIndex)) { + descIndex += 1; + } + lastName = name; + lastWord = word; + } + + if (aliases) { + const currentCrateAliases = new Map(); + this.ALIASES.set(crate, currentCrateAliases); + for (const alias_name in aliases) { + if (!Object.prototype.hasOwnProperty.call(aliases, alias_name)) { continue; } - if (fnType.id < 0) { - if (mgens && mgens.has(fnType.id) && - mgens.get(fnType.id) !== 0) { - continue; - } - const mgensScratch = new Map(mgens); - mgensScratch.set(fnType.id, 0); - if (unifyFunctionTypes( - whereClause[(-fnType.id) - 1], - queryElems, - whereClause, - mgensScratch, - solutionCb, - unboxingDepth + 1, - )) { - return true; - } - } else if (unifyFunctionTypes( - [...fnType.generics, ...Array.from(fnType.bindings.values()).flat() ], - queryElems, - whereClause, - mgens ? new Map(mgens) : null, - solutionCb, - unboxingDepth + 1, - )) { - return true; + + let currentNameAliases; + if (currentCrateAliases.has(alias_name)) { + currentNameAliases = currentCrateAliases.get(alias_name); + } else { + currentNameAliases = []; + currentCrateAliases.set(alias_name, currentNameAliases); + } + for (const local_alias of aliases[alias_name]) { + currentNameAliases.push(local_alias + currentIndex); } } - return false; } + currentIndex += itemTypes.length; + this.searchState.descShards.set(crate, descShardList); + } + // Drop the (rather large) hash table used for reusing function items + this.TYPES_POOL = new Map(); + return searchIndex; + } - // Multiple element recursive case - /** - * @type Array - */ - const fnTypes = fnTypesIn.slice(); - /** - * Algorithm works by building up a solution set in the working arrays - * fnTypes gets mutated in place to make this work, while queryElems - * is left alone. - * - * It works backwards, because arrays can be cheaply truncated that way. - * - * vvvvvvv `queryElem` - * queryElems = [ unknown, unknown, good, good, good ] - * fnTypes = [ unknown, unknown, good, good, good ] - * ^^^^^^^^^^^^^^^^ loop over these elements to find candidates - * - * Everything in the current working solution is known to be a good - * match, but it might not be the match we wind up going with, because - * there might be more than one candidate match, and we need to try them all - * before giving up. So, to handle this, it backtracks on failure. - */ - const flast = fl - 1; - const qlast = ql - 1; - const queryElem = queryElems[qlast]; - let queryElemsTmp = null; - for (let i = flast; i >= 0; i -= 1) { - const fnType = fnTypes[i]; - if (!unifyFunctionTypeIsMatchCandidate(fnType, queryElem, mgens)) { - continue; + /** + * Parses the query. + * + * The supported syntax by this parser is given in the rustdoc book chapter + * /src/doc/rustdoc/src/read-documentation/search.md + * + * When adding new things to the parser, add them there, too! + * + * @param {string} val - The user query + * + * @return {ParsedQuery} - The parsed query + */ + static parseQuery(userQuery) { + function itemTypeFromName(typename) { + const index = itemTypes.findIndex(i => i === typename); + if (index < 0) { + throw ["Unknown type filter ", typename]; + } + return index; + } + + function convertTypeFilterOnElem(elem) { + if (elem.typeFilter !== null) { + let typeFilter = elem.typeFilter; + if (typeFilter === "const") { + typeFilter = "constant"; } - let mgensScratch; - if (fnType.id < 0) { - mgensScratch = new Map(mgens); - if (mgensScratch.has(fnType.id) - && mgensScratch.get(fnType.id) !== queryElem.id) { + elem.typeFilter = itemTypeFromName(typeFilter); + } else { + elem.typeFilter = NO_TYPE_FILTER; + } + for (const elem2 of elem.generics) { + convertTypeFilterOnElem(elem2); + } + for (const constraints of elem.bindings.values()) { + for (const constraint of constraints) { + convertTypeFilterOnElem(constraint); + } + } + } + + /** + * Takes the user search input and returns an empty `ParsedQuery`. + * + * @param {string} userQuery + * + * @return {ParsedQuery} + */ + function newParsedQuery(userQuery) { + return { + original: userQuery, + userQuery: userQuery.toLowerCase(), + elems: [], + returned: [], + // Total number of "top" elements (does not include generics). + foundElems: 0, + // Total number of elements (includes generics). + totalElems: 0, + literalSearch: false, + error: null, + correction: null, + proposeCorrectionFrom: null, + proposeCorrectionTo: null, + // bloom filter build from type ids + typeFingerprint: new Uint32Array(4), + }; + } + + /** + * Parses the provided `query` input to fill `parserState`. If it encounters an error while + * parsing `query`, it'll throw an error. + * + * @param {ParsedQuery} query + * @param {ParserState} parserState + */ + function parseInput(query, parserState) { + let foundStopChar = true; + + while (parserState.pos < parserState.length) { + const c = parserState.userQuery[parserState.pos]; + if (isEndCharacter(c)) { + foundStopChar = true; + if (isSeparatorCharacter(c)) { + parserState.pos += 1; continue; + } else if (c === "-" || c === ">") { + if (isReturnArrow(parserState)) { + break; + } + throw ["Unexpected ", c, " (did you mean ", "->", "?)"]; + } else if (parserState.pos > 0) { + throw ["Unexpected ", c, " after ", + parserState.userQuery[parserState.pos - 1]]; } - mgensScratch.set(fnType.id, queryElem.id); - } else { - mgensScratch = mgens; + throw ["Unexpected ", c]; + } else if (c === " ") { + skipWhitespace(parserState); + continue; } - // fnTypes[i] is a potential match - // fnTypes[flast] is the last item in the list - // swap them, and drop the potential match from the list - // check if the remaining function types also match - fnTypes[i] = fnTypes[flast]; - fnTypes.length = flast; - if (!queryElemsTmp) { - queryElemsTmp = queryElems.slice(0, qlast); + if (!foundStopChar) { + let extra = ""; + if (isLastElemGeneric(query.elems, parserState)) { + extra = [" after ", ">"]; + } else if (prevIs(parserState, "\"")) { + throw ["Cannot have more than one element if you use quotes"]; + } + if (parserState.typeFilter !== null) { + throw [ + "Expected ", + ",", + " or ", + "->", + ...extra, + ", found ", + c, + ]; + } + throw [ + "Expected ", + ",", + ", ", + ":", + " or ", + "->", + ...extra, + ", found ", + c, + ]; } - const passesUnification = unifyFunctionTypes( - fnTypes, - queryElemsTmp, - whereClause, - mgensScratch, - mgensScratch => { - if (fnType.generics.length === 0 && queryElem.generics.length === 0 - && fnType.bindings.size === 0 && queryElem.bindings.size === 0) { - return !solutionCb || solutionCb(mgensScratch); - } - const solution = unifyFunctionTypeCheckBindings( - fnType, - queryElem, - whereClause, - mgensScratch, - unboxingDepth, - ); - if (!solution) { - return false; - } - const simplifiedGenerics = solution.simplifiedGenerics; - for (const simplifiedMgens of solution.mgens) { - const passesUnification = unifyFunctionTypes( - simplifiedGenerics, - queryElem.generics, - whereClause, - simplifiedMgens, - solutionCb, - unboxingDepth, - ); - if (passesUnification) { - return true; - } - } - return false; - }, - unboxingDepth, - ); - if (passesUnification) { - return true; + const before = query.elems.length; + getFilteredNextElem(query, parserState, query.elems, false); + if (query.elems.length === before) { + // Nothing was added, weird... Let's increase the position to not remain stuck. + parserState.pos += 1; } - // backtrack - fnTypes[flast] = fnTypes[i]; - fnTypes[i] = fnType; - fnTypes.length = fl; + foundStopChar = false; } - for (let i = flast; i >= 0; i -= 1) { - const fnType = fnTypes[i]; - if (!unifyFunctionTypeIsUnboxCandidate( - fnType, - queryElem, - whereClause, - mgens, - unboxingDepth + 1, - )) { - continue; - } - let mgensScratch; - if (fnType.id < 0) { - mgensScratch = new Map(mgens); - if (mgensScratch.has(fnType.id) && mgensScratch.get(fnType.id) !== 0) { - continue; + if (parserState.typeFilter !== null) { + throw [ + "Unexpected ", + ":", + " (expected path after type filter ", + parserState.typeFilter + ":", + ")", + ]; + } + while (parserState.pos < parserState.length) { + if (isReturnArrow(parserState)) { + parserState.pos += 2; + skipWhitespace(parserState); + // Get returned elements. + getItemsBefore(query, parserState, query.returned, ""); + // Nothing can come afterward! + if (query.returned.length === 0) { + throw ["Expected at least one item after ", "->"]; } - mgensScratch.set(fnType.id, 0); + break; } else { - mgensScratch = mgens; - } - const generics = fnType.id < 0 ? - whereClause[(-fnType.id) - 1] : - fnType.generics; - const bindings = fnType.bindings ? - Array.from(fnType.bindings.values()).flat() : - []; - const passesUnification = unifyFunctionTypes( - fnTypes.toSpliced(i, 1, ...generics, ...bindings), - queryElems, - whereClause, - mgensScratch, - solutionCb, - unboxingDepth + 1, - ); - if (passesUnification) { - return true; + parserState.pos += 1; } } - return false; } + + + userQuery = userQuery.trim().replace(/\r|\n|\t/g, " "); + const parserState = { + length: userQuery.length, + pos: 0, + // Total number of elements (includes generics). + totalElems: 0, + genericsElems: 0, + typeFilter: null, + isInBinding: null, + userQuery: userQuery.toLowerCase(), + }; + let query = newParsedQuery(userQuery); + + try { + parseInput(query, parserState); + for (const elem of query.elems) { + convertTypeFilterOnElem(elem); + } + for (const elem of query.returned) { + convertTypeFilterOnElem(elem); + } + } catch (err) { + query = newParsedQuery(userQuery); + query.error = err; + return query; + } + if (!query.literalSearch) { + // If there is more than one element in the query, we switch to literalSearch in any + // case. + query.literalSearch = parserState.totalElems > 1; + } + query.foundElems = query.elems.length + query.returned.length; + query.totalElems = parserState.totalElems; + return query; + } + + /** + * Executes the parsed query and builds a {ResultsTable}. + * + * @param {ParsedQuery} parsedQuery - The parsed user query + * @param {Object} [filterCrates] - Crate to search in if defined + * @param {Object} [currentCrate] - Current crate, to rank results from this crate higher + * + * @return {ResultsTable} + */ + async execQuery(parsedQuery, filterCrates, currentCrate) { + const results_others = new Map(), results_in_args = new Map(), + results_returned = new Map(); + /** - * Check if this function is a match candidate. + * Creates the query results. * - * This function is all the fast checks that don't require backtracking. - * It checks that two items are not named differently, and is load-bearing for that. - * It also checks that, if the query has generics, the function type must have generics - * or associated type bindings: that's not load-bearing, but it prevents unnecessary - * backtracking later. + * @param {Array} results_in_args + * @param {Array} results_returned + * @param {Array} results_others + * @param {ParsedQuery} parsedQuery * - * @param {FunctionType} fnType - * @param {QueryElement} queryElem - * @param {Map|null} mgensIn - Map functions generics to query generics. - * @returns {boolean} + * @return {ResultsTable} */ - function unifyFunctionTypeIsMatchCandidate(fnType, queryElem, mgensIn) { - // type filters look like `trait:Read` or `enum:Result` - if (!typePassesFilter(queryElem.typeFilter, fnType.ty)) { - return false; - } - // fnType.id < 0 means generic - // queryElem.id < 0 does too - // mgensIn[fnType.id] = queryElem.id - // or, if mgensIn[fnType.id] = 0, then we've matched this generic with a bare trait - // and should make that same decision everywhere it appears - if (fnType.id < 0 && queryElem.id < 0) { - if (mgensIn) { - if (mgensIn.has(fnType.id) && mgensIn.get(fnType.id) !== queryElem.id) { - return false; - } - for (const [fid, qid] of mgensIn.entries()) { - if (fnType.id !== fid && queryElem.id === qid) { - return false; - } - if (fnType.id === fid && queryElem.id !== qid) { - return false; - } - } - } - return true; - } else { - if (queryElem.id === typeNameIdOfArrayOrSlice && - (fnType.id === typeNameIdOfSlice || fnType.id === typeNameIdOfArray) - ) { - // [] matches primitive:array or primitive:slice - // if it matches, then we're fine, and this is an appropriate match candidate - } else if (queryElem.id === typeNameIdOfTupleOrUnit && - (fnType.id === typeNameIdOfTuple || fnType.id === typeNameIdOfUnit) - ) { - // () matches primitive:tuple or primitive:unit - // if it matches, then we're fine, and this is an appropriate match candidate - } else if (queryElem.id === typeNameIdOfHof && - (fnType.id === typeNameIdOfFn || fnType.id === typeNameIdOfFnMut || - fnType.id === typeNameIdOfFnOnce) - ) { - // -> matches fn, fnonce, and fnmut - // if it matches, then we're fine, and this is an appropriate match candidate - } else if (fnType.id !== queryElem.id || queryElem.id === null) { - return false; - } - // If the query elem has generics, and the function doesn't, - // it can't match. - if ((fnType.generics.length + fnType.bindings.size) === 0 && - queryElem.generics.length !== 0 - ) { - return false; + function createQueryResults( + results_in_args, + results_returned, + results_others, + parsedQuery) { + return { + "in_args": results_in_args, + "returned": results_returned, + "others": results_others, + "query": parsedQuery, + }; + } + + const buildHrefAndPath = item => { + let displayPath; + let href; + const type = itemTypes[item.ty]; + const name = item.name; + let path = item.path; + let exactPath = item.exactPath; + + if (type === "mod") { + displayPath = path + "::"; + href = this.rootPath + path.replace(/::/g, "/") + "/" + + name + "/index.html"; + } else if (type === "import") { + displayPath = item.path + "::"; + href = this.rootPath + item.path.replace(/::/g, "/") + + "/index.html#reexport." + name; + } else if (type === "primitive" || type === "keyword") { + displayPath = ""; + href = this.rootPath + path.replace(/::/g, "/") + + "/" + type + "." + name + ".html"; + } else if (type === "externcrate") { + displayPath = ""; + href = this.rootPath + name + "/index.html"; + } else if (item.parent !== undefined) { + const myparent = item.parent; + let anchor = type + "." + name; + const parentType = itemTypes[myparent.ty]; + let pageType = parentType; + let pageName = myparent.name; + exactPath = `${myparent.exactPath}::${myparent.name}`; + + if (parentType === "primitive") { + displayPath = myparent.name + "::"; + } else if (type === "structfield" && parentType === "variant") { + // Structfields belonging to variants are special: the + // final path element is the enum name. + const enumNameIdx = item.path.lastIndexOf("::"); + const enumName = item.path.substr(enumNameIdx + 2); + path = item.path.substr(0, enumNameIdx); + displayPath = path + "::" + enumName + "::" + myparent.name + "::"; + anchor = "variant." + myparent.name + ".field." + name; + pageType = "enum"; + pageName = enumName; + } else { + displayPath = path + "::" + myparent.name + "::"; } - if (fnType.bindings.size < queryElem.bindings.size) { - return false; + if (item.implDisambiguator !== null) { + anchor = item.implDisambiguator + "/" + anchor; } - // If the query element is a path (it contains `::`), we need to check if this - // path is compatible with the target type. - const queryElemPathLength = queryElem.pathWithoutLast.length; - if (queryElemPathLength > 0) { - const fnTypePath = fnType.path !== undefined && fnType.path !== null ? - fnType.path.split("::") : []; - // If the path provided in the query element is longer than this type, - // no need to check it since it won't match in any case. - if (queryElemPathLength > fnTypePath.length) { - return false; + href = this.rootPath + path.replace(/::/g, "/") + + "/" + pageType + + "." + pageName + + ".html#" + anchor; + } else { + displayPath = item.path + "::"; + href = this.rootPath + item.path.replace(/::/g, "/") + + "/" + type + "." + name + ".html"; + } + return [displayPath, href, `${exactPath}::${name}`]; + }; + + function pathSplitter(path) { + const tmp = "" + path.replace(/::/g, "::"); + if (tmp.endsWith("")) { + return tmp.slice(0, tmp.length - 6); + } + return tmp; + } + + /** + * Add extra data to result objects, and filter items that have been + * marked for removal. + * + * @param {[ResultObject]} results + * @returns {[ResultObject]} + */ + const transformResults = results => { + const duplicates = new Set(); + const out = []; + + for (const result of results) { + if (result.id !== -1) { + const obj = this.searchIndex[result.id]; + obj.dist = result.dist; + const res = buildHrefAndPath(obj); + obj.displayPath = pathSplitter(res[0]); + + // To be sure than it some items aren't considered as duplicate. + obj.fullPath = res[2] + "|" + obj.ty; + if (duplicates.has(obj.fullPath)) { + continue; } - let i = 0; - for (const path of fnTypePath) { - if (path === queryElem.pathWithoutLast[i]) { - i += 1; - if (i >= queryElemPathLength) { - break; - } - } + + // Exports are specifically not shown if the items they point at + // are already in the results. + if (obj.ty === TY_IMPORT && duplicates.has(res[2])) { + continue; } - if (i < queryElemPathLength) { - // If we didn't find all parts of the path of the query element inside - // the fn type, then it's not the right one. - return false; + if (duplicates.has(res[2] + "|" + TY_IMPORT)) { + continue; + } + duplicates.add(obj.fullPath); + duplicates.add(res[2]); + + obj.href = res[1]; + out.push(obj); + if (out.length >= MAX_RESULTS) { + break; } } - return true; } - } + return out; + }; + /** - * This function checks the associated type bindings. Any that aren't matched get converted - * to generics, and this function returns an array of the function's generics with these - * simplified bindings added to them. That is, it takes a path like this: - * - * Iterator - * - * ... if queryElem itself has an `Item=` in it, then this function returns an empty array. - * But if queryElem contains no Item=, then this function returns a one-item array with the - * ID of u32 in it, and the rest of the matching engine acts as if `Iterator` were - * the type instead. + * This function takes a result map, and sorts it by various criteria, including edit + * distance, substring match, and the crate it comes from. * - * @param {FunctionType} fnType - * @param {QueryElement} queryElem - * @param {[FunctionType]} whereClause - Trait bounds for generic items. - * @param {Map} mgensIn - Map functions generics to query generics. - * Never modified. - * @param {number} unboxingDepth - * @returns {false|{mgens: [Map], simplifiedGenerics: [FunctionType]}} + * @param {Results} results + * @param {boolean} isType + * @param {string} preferredCrate + * @returns {Promise<[ResultObject]>} */ - function unifyFunctionTypeCheckBindings( - fnType, - queryElem, - whereClause, - mgensIn, - unboxingDepth, - ) { - if (fnType.bindings.size < queryElem.bindings.size) { - return false; + const sortResults = async(results, isType, preferredCrate) => { + const userQuery = parsedQuery.userQuery; + const casedUserQuery = parsedQuery.original; + const result_list = []; + for (const result of results.values()) { + result.item = this.searchIndex[result.id]; + result.word = this.searchIndex[result.id].word; + result_list.push(result); } - let simplifiedGenerics = fnType.generics || []; - if (fnType.bindings.size > 0) { - let mgensSolutionSet = [mgensIn]; - for (const [name, constraints] of queryElem.bindings.entries()) { - if (mgensSolutionSet.length === 0) { - return false; - } - if (!fnType.bindings.has(name)) { - return false; - } - const fnTypeBindings = fnType.bindings.get(name); - mgensSolutionSet = mgensSolutionSet.flatMap(mgens => { - const newSolutions = []; - unifyFunctionTypes( - fnTypeBindings, - constraints, - whereClause, - mgens, - newMgens => { - newSolutions.push(newMgens); - // return `false` makes unifyFunctionTypes return the full set of - // possible solutions - return false; - }, - unboxingDepth, - ); - return newSolutions; - }); + + result_list.sort((aaa, bbb) => { + let a, b; + + // sort by exact case-sensitive match + a = (aaa.item.name !== casedUserQuery); + b = (bbb.item.name !== casedUserQuery); + if (a !== b) { + return a - b; } - if (mgensSolutionSet.length === 0) { - return false; + + // sort by exact match with regard to the last word (mismatch goes later) + a = (aaa.word !== userQuery); + b = (bbb.word !== userQuery); + if (a !== b) { + return a - b; } - const binds = Array.from(fnType.bindings.entries()).flatMap(entry => { - const [name, constraints] = entry; - if (queryElem.bindings.has(name)) { - return []; - } else { - return constraints; - } - }); - if (simplifiedGenerics.length > 0) { - simplifiedGenerics = [...simplifiedGenerics, ...binds]; - } else { - simplifiedGenerics = binds; + + // sort by index of keyword in item name (no literal occurrence goes later) + a = (aaa.index < 0); + b = (bbb.index < 0); + if (a !== b) { + return a - b; } - return { simplifiedGenerics, mgens: mgensSolutionSet }; - } - return { simplifiedGenerics, mgens: [mgensIn] }; - } + + // Sort by distance in the path part, if specified + // (less changes required to match means higher rankings) + a = aaa.path_dist; + b = bbb.path_dist; + if (a !== b) { + return a - b; + } + + // (later literal occurrence, if any, goes later) + a = aaa.index; + b = bbb.index; + if (a !== b) { + return a - b; + } + + // Sort by distance in the name part, the last part of the path + // (less changes required to match means higher rankings) + a = (aaa.dist); + b = (bbb.dist); + if (a !== b) { + return a - b; + } + + // sort deprecated items later + a = this.searchIndexDeprecated.get(aaa.item.crate).contains(aaa.item.bitIndex); + b = this.searchIndexDeprecated.get(bbb.item.crate).contains(bbb.item.bitIndex); + if (a !== b) { + return a - b; + } + + // sort by crate (current crate comes first) + a = (aaa.item.crate !== preferredCrate); + b = (bbb.item.crate !== preferredCrate); + if (a !== b) { + return a - b; + } + + // sort by item name length (longer goes later) + a = aaa.word.length; + b = bbb.word.length; + if (a !== b) { + return a - b; + } + + // sort by item name (lexicographically larger goes later) + a = aaa.word; + b = bbb.word; + if (a !== b) { + return (a > b ? +1 : -1); + } + + // sort by description (no description goes later) + a = this.searchIndexEmptyDesc.get(aaa.item.crate).contains(aaa.item.bitIndex); + b = this.searchIndexEmptyDesc.get(bbb.item.crate).contains(bbb.item.bitIndex); + if (a !== b) { + return a - b; + } + + // sort by type (later occurrence in `itemTypes` goes later) + a = aaa.item.ty; + b = bbb.item.ty; + if (a !== b) { + return a - b; + } + + // sort by path (lexicographically larger goes later) + a = aaa.item.path; + b = bbb.item.path; + if (a !== b) { + return (a > b ? +1 : -1); + } + + // que sera, sera + return 0; + }); + + return transformResults(result_list); + }; + /** - * @param {FunctionType} fnType - * @param {QueryElement} queryElem + * This function checks if a list of search query `queryElems` can all be found in the + * search index (`fnTypes`). + * + * This function returns `true` on a match, or `false` if none. If `solutionCb` is + * supplied, it will call that function with mgens, and that callback can accept or + * reject the result bu returning `true` or `false`. If the callback returns false, + * then this function will try with a different solution, or bail with false if it + * runs out of candidates. + * + * @param {Array} fnTypesIn - The objects to check. + * @param {Array} queryElems - The elements from the parsed query. * @param {[FunctionType]} whereClause - Trait bounds for generic items. - * @param {Map|null} mgens - Map functions generics to query generics. + * @param {Map|null} mgensIn + * - Map functions generics to query generics (never modified). + * @param {null|Map -> bool} solutionCb - Called for each `mgens` solution. * @param {number} unboxingDepth - * @returns {boolean} + * - Limit checks that Ty matches Vec, + * but not Vec>>>> + * + * @return {boolean} - Returns true if a match, false otherwise. */ - function unifyFunctionTypeIsUnboxCandidate( - fnType, - queryElem, + function unifyFunctionTypes( + fnTypesIn, + queryElems, whereClause, - mgens, + mgensIn, + solutionCb, unboxingDepth, ) { if (unboxingDepth >= UNBOXING_LIMIT) { return false; } - if (fnType.id < 0 && queryElem.id >= 0) { - if (!whereClause) { - return false; - } - // mgens[fnType.id] === 0 indicates that we committed to unboxing this generic - // mgens[fnType.id] === null indicates that we haven't decided yet - if (mgens && mgens.has(fnType.id) && mgens.get(fnType.id) !== 0) { - return false; - } - // Where clauses can represent cyclical data. - // `null` prevents it from trying to unbox in an infinite loop - const mgensTmp = new Map(mgens); - mgensTmp.set(fnType.id, null); - // This is only a potential unbox if the search query appears in the where clause - // for example, searching `Read -> usize` should find - // `fn read_all(R) -> Result` - // generic `R` is considered "unboxed" - return checkIfInList( - whereClause[(-fnType.id) - 1], - queryElem, - whereClause, - mgensTmp, - unboxingDepth, - ); - } else if (fnType.generics.length > 0 || fnType.bindings.size > 0) { - const simplifiedGenerics = [ - ...fnType.generics, - ...Array.from(fnType.bindings.values()).flat(), - ]; - return checkIfInList( - simplifiedGenerics, - queryElem, - whereClause, - mgens, - unboxingDepth, - ); - } - return false; - } - - /** - * This function checks if the object (`row`) matches the given type (`elem`) and its - * generics (if any). - * - * @param {Array} list - * @param {QueryElement} elem - The element from the parsed query. - * @param {[FunctionType]} whereClause - Trait bounds for generic items. - * @param {Map|null} mgens - Map functions generics to query generics. - * @param {number} unboxingDepth - * - * @return {boolean} - Returns true if found, false otherwise. - */ - function checkIfInList(list, elem, whereClause, mgens, unboxingDepth) { - for (const entry of list) { - if (checkType(entry, elem, whereClause, mgens, unboxingDepth)) { - return true; - } + /** + * @type Map|null + */ + const mgens = mgensIn === null ? null : new Map(mgensIn); + if (queryElems.length === 0) { + return !solutionCb || solutionCb(mgens); } - return false; - } - - /** - * This function checks if the object (`row`) matches the given type (`elem`) and its - * generics (if any). - * - * @param {Row} row - * @param {QueryElement} elem - The element from the parsed query. - * @param {[FunctionType]} whereClause - Trait bounds for generic items. - * @param {Map|null} mgens - Map functions generics to query generics. - * - * @return {boolean} - Returns true if the type matches, false otherwise. - */ - function checkType(row, elem, whereClause, mgens, unboxingDepth) { - if (unboxingDepth >= UNBOXING_LIMIT) { + if (!fnTypesIn || fnTypesIn.length === 0) { return false; } - if (row.bindings.size === 0 && elem.bindings.size === 0) { - if (elem.id < 0 && mgens === null) { - return row.id < 0 || checkIfInList( - row.generics, - elem, + const ql = queryElems.length; + const fl = fnTypesIn.length; + + // One element fast path / base case + if (ql === 1 && queryElems[0].generics.length === 0 + && queryElems[0].bindings.size === 0) { + const queryElem = queryElems[0]; + for (const fnType of fnTypesIn) { + if (!unifyFunctionTypeIsMatchCandidate(fnType, queryElem, mgens)) { + continue; + } + if (fnType.id < 0 && queryElem.id < 0) { + if (mgens && mgens.has(fnType.id) && + mgens.get(fnType.id) !== queryElem.id) { + continue; + } + const mgensScratch = new Map(mgens); + mgensScratch.set(fnType.id, queryElem.id); + if (!solutionCb || solutionCb(mgensScratch)) { + return true; + } + } else if (!solutionCb || solutionCb(mgens ? new Map(mgens) : null)) { + // unifyFunctionTypeIsMatchCandidate already checks that ids match + return true; + } + } + for (const fnType of fnTypesIn) { + if (!unifyFunctionTypeIsUnboxCandidate( + fnType, + queryElem, whereClause, mgens, unboxingDepth + 1, - ); - } - if (row.id > 0 && elem.id > 0 && elem.pathWithoutLast.length === 0 && - typePassesFilter(elem.typeFilter, row.ty) && elem.generics.length === 0 && - // special case - elem.id !== typeNameIdOfArrayOrSlice && elem.id !== typeNameIdOfTupleOrUnit - && elem.id !== typeNameIdOfHof - ) { - return row.id === elem.id || checkIfInList( - row.generics, - elem, - whereClause, - mgens, - unboxingDepth, - ); - } - } - return unifyFunctionTypes([row], [elem], whereClause, mgens, null, unboxingDepth); - } - - /** - * Compute an "edit distance" that ignores missing path elements. - * @param {string[]} contains search query path - * @param {Row} ty indexed item - * @returns {null|number} edit distance - */ - function checkPath(contains, ty) { - if (contains.length === 0) { - return 0; - } - const maxPathEditDistance = Math.floor( - contains.reduce((acc, next) => acc + next.length, 0) / 3, - ); - let ret_dist = maxPathEditDistance + 1; - const path = ty.path.split("::"); - - if (ty.parent && ty.parent.name) { - path.push(ty.parent.name.toLowerCase()); - } - - const length = path.length; - const clength = contains.length; - pathiter: for (let i = length - clength; i >= 0; i -= 1) { - let dist_total = 0; - for (let x = 0; x < clength; ++x) { - const [p, c] = [path[i + x], contains[x]]; - if (Math.floor((p.length - c.length) / 3) <= maxPathEditDistance && - p.indexOf(c) !== -1 - ) { - // discount distance on substring match - dist_total += Math.floor((p.length - c.length) / 3); - } else { - const dist = editDistance(p, c, maxPathEditDistance); - if (dist > maxPathEditDistance) { - continue pathiter; + )) { + continue; + } + if (fnType.id < 0) { + if (mgens && mgens.has(fnType.id) && + mgens.get(fnType.id) !== 0) { + continue; } - dist_total += dist; + const mgensScratch = new Map(mgens); + mgensScratch.set(fnType.id, 0); + if (unifyFunctionTypes( + whereClause[(-fnType.id) - 1], + queryElems, + whereClause, + mgensScratch, + solutionCb, + unboxingDepth + 1, + )) { + return true; + } + } else if (unifyFunctionTypes( + [...fnType.generics, ...Array.from(fnType.bindings.values()).flat()], + queryElems, + whereClause, + mgens ? new Map(mgens) : null, + solutionCb, + unboxingDepth + 1, + )) { + return true; } } - ret_dist = Math.min(ret_dist, Math.round(dist_total / clength)); - } - return ret_dist > maxPathEditDistance ? null : ret_dist; - } - - function typePassesFilter(filter, type) { - // No filter or Exact mach - if (filter <= NO_TYPE_FILTER || filter === type) return true; - - // Match related items - const name = itemTypes[type]; - switch (itemTypes[filter]) { - case "constant": - return name === "associatedconstant"; - case "fn": - return name === "method" || name === "tymethod"; - case "type": - return name === "primitive" || name === "associatedtype"; - case "trait": - return name === "traitalias"; + return false; } - // No match - return false; - } - - function createAliasFromItem(item) { - return { - crate: item.crate, - name: item.name, - path: item.path, - descShard: item.descShard, - descIndex: item.descIndex, - exactPath: item.exactPath, - ty: item.ty, - parent: item.parent, - type: item.type, - is_alias: true, - bitIndex: item.bitIndex, - implDisambiguator: item.implDisambiguator, - }; - } - - async function handleAliases(ret, query, filterCrates, currentCrate) { - const lowerQuery = query.toLowerCase(); - // We separate aliases and crate aliases because we want to have current crate - // aliases to be before the others in the displayed results. - const aliases = []; - const crateAliases = []; - if (filterCrates !== null) { - if (ALIASES.has(filterCrates) && ALIASES.get(filterCrates).has(lowerQuery)) { - const query_aliases = ALIASES.get(filterCrates).get(lowerQuery); - for (const alias of query_aliases) { - aliases.push(createAliasFromItem(searchIndex[alias])); + // Multiple element recursive case + /** + * @type Array + */ + const fnTypes = fnTypesIn.slice(); + /** + * Algorithm works by building up a solution set in the working arrays + * fnTypes gets mutated in place to make this work, while queryElems + * is left alone. + * + * It works backwards, because arrays can be cheaply truncated that way. + * + * vvvvvvv `queryElem` + * queryElems = [ unknown, unknown, good, good, good ] + * fnTypes = [ unknown, unknown, good, good, good ] + * ^^^^^^^^^^^^^^^^ loop over these elements to find candidates + * + * Everything in the current working solution is known to be a good + * match, but it might not be the match we wind up going with, because + * there might be more than one candidate match, and we need to try them all + * before giving up. So, to handle this, it backtracks on failure. + */ + const flast = fl - 1; + const qlast = ql - 1; + const queryElem = queryElems[qlast]; + let queryElemsTmp = null; + for (let i = flast; i >= 0; i -= 1) { + const fnType = fnTypes[i]; + if (!unifyFunctionTypeIsMatchCandidate(fnType, queryElem, mgens)) { + continue; + } + let mgensScratch; + if (fnType.id < 0) { + mgensScratch = new Map(mgens); + if (mgensScratch.has(fnType.id) + && mgensScratch.get(fnType.id) !== queryElem.id) { + continue; } + mgensScratch.set(fnType.id, queryElem.id); + } else { + mgensScratch = mgens; } - } else { - for (const [crate, crateAliasesIndex] of ALIASES) { - if (crateAliasesIndex.has(lowerQuery)) { - const pushTo = crate === currentCrate ? crateAliases : aliases; - const query_aliases = crateAliasesIndex.get(lowerQuery); - for (const alias of query_aliases) { - pushTo.push(createAliasFromItem(searchIndex[alias])); + // fnTypes[i] is a potential match + // fnTypes[flast] is the last item in the list + // swap them, and drop the potential match from the list + // check if the remaining function types also match + fnTypes[i] = fnTypes[flast]; + fnTypes.length = flast; + if (!queryElemsTmp) { + queryElemsTmp = queryElems.slice(0, qlast); + } + const passesUnification = unifyFunctionTypes( + fnTypes, + queryElemsTmp, + whereClause, + mgensScratch, + mgensScratch => { + if (fnType.generics.length === 0 && queryElem.generics.length === 0 + && fnType.bindings.size === 0 && queryElem.bindings.size === 0) { + return !solutionCb || solutionCb(mgensScratch); } - } + const solution = unifyFunctionTypeCheckBindings( + fnType, + queryElem, + whereClause, + mgensScratch, + unboxingDepth, + ); + if (!solution) { + return false; + } + const simplifiedGenerics = solution.simplifiedGenerics; + for (const simplifiedMgens of solution.mgens) { + const passesUnification = unifyFunctionTypes( + simplifiedGenerics, + queryElem.generics, + whereClause, + simplifiedMgens, + solutionCb, + unboxingDepth, + ); + if (passesUnification) { + return true; + } + } + return false; + }, + unboxingDepth, + ); + if (passesUnification) { + return true; } + // backtrack + fnTypes[flast] = fnTypes[i]; + fnTypes[i] = fnType; + fnTypes.length = fl; } - - const sortFunc = (aaa, bbb) => { - if (aaa.path < bbb.path) { - return 1; - } else if (aaa.path === bbb.path) { - return 0; - } - return -1; - }; - crateAliases.sort(sortFunc); - aliases.sort(sortFunc); - - const fetchDesc = alias => { - return searchIndexEmptyDesc.get(alias.crate).contains(alias.bitIndex) ? - "" : searchState.loadDesc(alias); - }; - const [crateDescs, descs] = await Promise.all([ - Promise.all(crateAliases.map(fetchDesc)), - Promise.all(aliases.map(fetchDesc)), - ]); - - const pushFunc = alias => { - alias.alias = query; - const res = buildHrefAndPath(alias); - alias.displayPath = pathSplitter(res[0]); - alias.fullPath = alias.displayPath + alias.name; - alias.href = res[1]; - - ret.others.unshift(alias); - if (ret.others.length > MAX_RESULTS) { - ret.others.pop(); + for (let i = flast; i >= 0; i -= 1) { + const fnType = fnTypes[i]; + if (!unifyFunctionTypeIsUnboxCandidate( + fnType, + queryElem, + whereClause, + mgens, + unboxingDepth + 1, + )) { + continue; } - }; - - aliases.forEach((alias, i) => { - alias.desc = descs[i]; - }); - aliases.forEach(pushFunc); - crateAliases.forEach((alias, i) => { - alias.desc = crateDescs[i]; - }); - crateAliases.forEach(pushFunc); - } - - /** - * This function adds the given result into the provided `results` map if it matches the - * following condition: - * - * * If it is a "literal search" (`parsedQuery.literalSearch`), then `dist` must be 0. - * * If it is not a "literal search", `dist` must be <= `maxEditDistance`. - * - * The `results` map contains information which will be used to sort the search results: - * - * * `fullId` is a `string`` used as the key of the object we use for the `results` map. - * * `id` is the index in the `searchIndex` array for this element. - * * `index` is an `integer`` used to sort by the position of the word in the item's name. - * * `dist` is the main metric used to sort the search results. - * * `path_dist` is zero if a single-component search query is used, otherwise it's the - * distance computed for everything other than the last path component. - * - * @param {Results} results - * @param {string} fullId - * @param {integer} id - * @param {integer} index - * @param {integer} dist - * @param {integer} path_dist - */ - function addIntoResults(results, fullId, id, index, dist, path_dist, maxEditDistance) { - if (dist <= maxEditDistance || index !== -1) { - if (results.has(fullId)) { - const result = results.get(fullId); - if (result.dontValidate || result.dist <= dist) { - return; + let mgensScratch; + if (fnType.id < 0) { + mgensScratch = new Map(mgens); + if (mgensScratch.has(fnType.id) && mgensScratch.get(fnType.id) !== 0) { + continue; } + mgensScratch.set(fnType.id, 0); + } else { + mgensScratch = mgens; + } + const generics = fnType.id < 0 ? + whereClause[(-fnType.id) - 1] : + fnType.generics; + const bindings = fnType.bindings ? + Array.from(fnType.bindings.values()).flat() : + []; + const passesUnification = unifyFunctionTypes( + fnTypes.toSpliced(i, 1, ...generics, ...bindings), + queryElems, + whereClause, + mgensScratch, + solutionCb, + unboxingDepth + 1, + ); + if (passesUnification) { + return true; } - results.set(fullId, { - id: id, - index: index, - dontValidate: parsedQuery.literalSearch, - dist: dist, - path_dist: path_dist, - }); } + return false; } - /** - * This function is called in case the query is only one element (with or without generics). - * This element will be compared to arguments' and returned values' items and also to items. + * Check if this function is a match candidate. * - * Other important thing to note: since there is only one element, we use edit - * distance for name comparisons. + * This function is all the fast checks that don't require backtracking. + * It checks that two items are not named differently, and is load-bearing for that. + * It also checks that, if the query has generics, the function type must have generics + * or associated type bindings: that's not load-bearing, but it prevents unnecessary + * backtracking later. * - * @param {Row} row - * @param {integer} pos - Position in the `searchIndex`. - * @param {QueryElement} elem - The element from the parsed query. - * @param {Results} results_others - Unqualified results (not in arguments nor in - * returned values). - * @param {Results} results_in_args - Matching arguments results. - * @param {Results} results_returned - Matching returned arguments results. + * @param {FunctionType} fnType + * @param {QueryElement} queryElem + * @param {Map|null} mgensIn - Map functions generics to query generics. + * @returns {boolean} */ - function handleSingleArg( - row, - pos, - elem, - results_others, - results_in_args, - results_returned, - maxEditDistance, - ) { - if (!row || (filterCrates !== null && row.crate !== filterCrates)) { - return; + const unifyFunctionTypeIsMatchCandidate = (fnType, queryElem, mgensIn) => { + // type filters look like `trait:Read` or `enum:Result` + if (!typePassesFilter(queryElem.typeFilter, fnType.ty)) { + return false; } - let path_dist = 0; - const fullId = row.id; - - // fpDist is a minimum possible type distance, where "type distance" is the number of - // atoms in the function not present in the query - const tfpDist = compareTypeFingerprints( - fullId, - parsedQuery.typeFingerprint, - ); - if (tfpDist !== null) { - const in_args = row.type && row.type.inputs - && checkIfInList(row.type.inputs, elem, row.type.where_clause, null, 0); - const returned = row.type && row.type.output - && checkIfInList(row.type.output, elem, row.type.where_clause, null, 0); - if (in_args) { - results_in_args.max_dist = Math.max(results_in_args.max_dist || 0, tfpDist); - const maxDist = results_in_args.size < MAX_RESULTS ? - (tfpDist + 1) : - results_in_args.max_dist; - addIntoResults(results_in_args, fullId, pos, -1, tfpDist, 0, maxDist); + // fnType.id < 0 means generic + // queryElem.id < 0 does too + // mgensIn[fnType.id] = queryElem.id + // or, if mgensIn[fnType.id] = 0, then we've matched this generic with a bare trait + // and should make that same decision everywhere it appears + if (fnType.id < 0 && queryElem.id < 0) { + if (mgensIn) { + if (mgensIn.has(fnType.id) && mgensIn.get(fnType.id) !== queryElem.id) { + return false; + } + for (const [fid, qid] of mgensIn.entries()) { + if (fnType.id !== fid && queryElem.id === qid) { + return false; + } + if (fnType.id === fid && queryElem.id !== qid) { + return false; + } + } } - if (returned) { - results_returned.max_dist = Math.max(results_returned.max_dist || 0, tfpDist); - const maxDist = results_returned.size < MAX_RESULTS ? - (tfpDist + 1) : - results_returned.max_dist; - addIntoResults(results_returned, fullId, pos, -1, tfpDist, 0, maxDist); + return true; + } else { + if (queryElem.id === this.typeNameIdOfArrayOrSlice && + (fnType.id === this.typeNameIdOfSlice || fnType.id === this.typeNameIdOfArray) + ) { + // [] matches primitive:array or primitive:slice + // if it matches, then we're fine, and this is an appropriate match candidate + } else if (queryElem.id === this.typeNameIdOfTupleOrUnit && + (fnType.id === this.typeNameIdOfTuple || fnType.id === this.typeNameIdOfUnit) + ) { + // () matches primitive:tuple or primitive:unit + // if it matches, then we're fine, and this is an appropriate match candidate + } else if (queryElem.id === this.typeNameIdOfHof && + (fnType.id === this.typeNameIdOfFn || fnType.id === this.typeNameIdOfFnMut || + fnType.id === this.typeNameIdOfFnOnce) + ) { + // -> matches fn, fnonce, and fnmut + // if it matches, then we're fine, and this is an appropriate match candidate + } else if (fnType.id !== queryElem.id || queryElem.id === null) { + return false; } - } - - if (!typePassesFilter(elem.typeFilter, row.ty)) { - return; - } - - let index = row.word.indexOf(elem.pathLast); - const normalizedIndex = row.normalizedName.indexOf(elem.pathLast); - if (index === -1 || (index > normalizedIndex && normalizedIndex !== -1)) { - index = normalizedIndex; - } - - if (elem.fullPath.length > 1) { - path_dist = checkPath(elem.pathWithoutLast, row); - if (path_dist === null) { - return; + // If the query elem has generics, and the function doesn't, + // it can't match. + if ((fnType.generics.length + fnType.bindings.size) === 0 && + queryElem.generics.length !== 0 + ) { + return false; } - } - - if (parsedQuery.literalSearch) { - if (row.word === elem.pathLast) { - addIntoResults(results_others, fullId, pos, index, 0, path_dist); + if (fnType.bindings.size < queryElem.bindings.size) { + return false; } - return; - } - - const dist = editDistance(row.normalizedName, elem.normalizedPathLast, maxEditDistance); - - if (index === -1 && dist > maxEditDistance) { - return; + // If the query element is a path (it contains `::`), we need to check if this + // path is compatible with the target type. + const queryElemPathLength = queryElem.pathWithoutLast.length; + if (queryElemPathLength > 0) { + const fnTypePath = fnType.path !== undefined && fnType.path !== null ? + fnType.path.split("::") : []; + // If the path provided in the query element is longer than this type, + // no need to check it since it won't match in any case. + if (queryElemPathLength > fnTypePath.length) { + return false; + } + let i = 0; + for (const path of fnTypePath) { + if (path === queryElem.pathWithoutLast[i]) { + i += 1; + if (i >= queryElemPathLength) { + break; + } + } + } + if (i < queryElemPathLength) { + // If we didn't find all parts of the path of the query element inside + // the fn type, then it's not the right one. + return false; + } + } + return true; } - - addIntoResults(results_others, fullId, pos, index, dist, path_dist, maxEditDistance); - } - - /** - * This function is called in case the query has more than one element. In this case, it'll - * try to match the items which validates all the elements. For `aa -> bb` will look for - * functions which have a parameter `aa` and has `bb` in its returned values. + }; + /** + * This function checks the associated type bindings. Any that aren't matched get converted + * to generics, and this function returns an array of the function's generics with these + * simplified bindings added to them. That is, it takes a path like this: * - * @param {Row} row - * @param {integer} pos - Position in the `searchIndex`. - * @param {Object} results + * Iterator + * + * ... if queryElem itself has an `Item=` in it, then this function returns an empty array. + * But if queryElem contains no Item=, then this function returns a one-item array with the + * ID of u32 in it, and the rest of the matching engine acts as if `Iterator` were + * the type instead. + * + * @param {FunctionType} fnType + * @param {QueryElement} queryElem + * @param {[FunctionType]} whereClause - Trait bounds for generic items. + * @param {Map} mgensIn - Map functions generics to query generics. + * Never modified. + * @param {number} unboxingDepth + * @returns {false|{mgens: [Map], simplifiedGenerics: [FunctionType]}} */ - function handleArgs(row, pos, results) { - if (!row || (filterCrates !== null && row.crate !== filterCrates) || !row.type) { - return; - } - - const tfpDist = compareTypeFingerprints( - row.id, - parsedQuery.typeFingerprint, - ); - if (tfpDist === null) { - return; - } - if (results.size >= MAX_RESULTS && tfpDist > results.max_dist) { - return; - } - - // If the result is too "bad", we return false and it ends this search. - if (!unifyFunctionTypes( - row.type.inputs, - parsedQuery.elems, - row.type.where_clause, - null, - mgens => { - return unifyFunctionTypes( - row.type.output, - parsedQuery.returned, - row.type.where_clause, - mgens, - null, - 0, // unboxing depth - ); - }, - 0, // unboxing depth - )) { - return; + function unifyFunctionTypeCheckBindings( + fnType, + queryElem, + whereClause, + mgensIn, + unboxingDepth, + ) { + if (fnType.bindings.size < queryElem.bindings.size) { + return false; } - - results.max_dist = Math.max(results.max_dist || 0, tfpDist); - addIntoResults(results, row.id, pos, 0, tfpDist, 0, Number.MAX_VALUE); - } - - function innerRunQuery() { - const queryLen = - parsedQuery.elems.reduce((acc, next) => acc + next.pathLast.length, 0) + - parsedQuery.returned.reduce((acc, next) => acc + next.pathLast.length, 0); - const maxEditDistance = Math.floor(queryLen / 3); - - /** - * @type {Map} - */ - const genericSymbols = new Map(); - - /** - * Convert names to ids in parsed query elements. - * This is not used for the "In Names" tab, but is used for the - * "In Params", "In Returns", and "In Function Signature" tabs. - * - * If there is no matching item, but a close-enough match, this - * function also that correction. - * - * See `buildTypeMapIndex` for more information. - * - * @param {QueryElement} elem - * @param {boolean} isAssocType - */ - function convertNameToId(elem, isAssocType) { - const loweredName = elem.pathLast.toLowerCase(); - if (typeNameIdMap.has(loweredName) && - (isAssocType || !typeNameIdMap.get(loweredName).assocOnly)) { - elem.id = typeNameIdMap.get(loweredName).id; - } else if (!parsedQuery.literalSearch) { - let match = null; - let matchDist = maxEditDistance + 1; - let matchName = ""; - for (const [name, {id, assocOnly}] of typeNameIdMap) { - const dist = Math.min( - editDistance(name, loweredName, maxEditDistance), - editDistance(name, elem.normalizedPathLast, maxEditDistance), - ); - if (dist <= matchDist && dist <= maxEditDistance && - (isAssocType || !assocOnly)) { - if (dist === matchDist && matchName > name) { - continue; - } - match = id; - matchDist = dist; - matchName = name; - } + let simplifiedGenerics = fnType.generics || []; + if (fnType.bindings.size > 0) { + let mgensSolutionSet = [mgensIn]; + for (const [name, constraints] of queryElem.bindings.entries()) { + if (mgensSolutionSet.length === 0) { + return false; } - if (match !== null) { - parsedQuery.correction = matchName; + if (!fnType.bindings.has(name)) { + return false; } - elem.id = match; + const fnTypeBindings = fnType.bindings.get(name); + mgensSolutionSet = mgensSolutionSet.flatMap(mgens => { + const newSolutions = []; + unifyFunctionTypes( + fnTypeBindings, + constraints, + whereClause, + mgens, + newMgens => { + newSolutions.push(newMgens); + // return `false` makes unifyFunctionTypes return the full set of + // possible solutions + return false; + }, + unboxingDepth, + ); + return newSolutions; + }); } - if ((elem.id === null && parsedQuery.totalElems > 1 && elem.typeFilter === -1 - && elem.generics.length === 0 && elem.bindings.size === 0) - || elem.typeFilter === TY_GENERIC) { - if (genericSymbols.has(elem.name)) { - elem.id = genericSymbols.get(elem.name); + if (mgensSolutionSet.length === 0) { + return false; + } + const binds = Array.from(fnType.bindings.entries()).flatMap(entry => { + const [name, constraints] = entry; + if (queryElem.bindings.has(name)) { + return []; } else { - elem.id = -(genericSymbols.size + 1); - genericSymbols.set(elem.name, elem.id); - } - if (elem.typeFilter === -1 && elem.name.length >= 3) { - // Silly heuristic to catch if the user probably meant - // to not write a generic parameter. We don't use it, - // just bring it up. - const maxPartDistance = Math.floor(elem.name.length / 3); - let matchDist = maxPartDistance + 1; - let matchName = ""; - for (const name of typeNameIdMap.keys()) { - const dist = editDistance(name, elem.name, maxPartDistance); - if (dist <= matchDist && dist <= maxPartDistance) { - if (dist === matchDist && matchName > name) { - continue; - } - matchDist = dist; - matchName = name; - } - } - if (matchName !== "") { - parsedQuery.proposeCorrectionFrom = elem.name; - parsedQuery.proposeCorrectionTo = matchName; - } + return constraints; } - elem.typeFilter = TY_GENERIC; + }); + if (simplifiedGenerics.length > 0) { + simplifiedGenerics = [...simplifiedGenerics, ...binds]; + } else { + simplifiedGenerics = binds; } - if (elem.generics.length > 0 && elem.typeFilter === TY_GENERIC) { - // Rust does not have HKT - parsedQuery.error = [ - "Generic type parameter ", - elem.name, - " does not accept generic parameters", - ]; + return { simplifiedGenerics, mgens: mgensSolutionSet }; + } + return { simplifiedGenerics, mgens: [mgensIn] }; + } + /** + * @param {FunctionType} fnType + * @param {QueryElement} queryElem + * @param {[FunctionType]} whereClause - Trait bounds for generic items. + * @param {Map|null} mgens - Map functions generics to query generics. + * @param {number} unboxingDepth + * @returns {boolean} + */ + function unifyFunctionTypeIsUnboxCandidate( + fnType, + queryElem, + whereClause, + mgens, + unboxingDepth, + ) { + if (unboxingDepth >= UNBOXING_LIMIT) { + return false; + } + if (fnType.id < 0 && queryElem.id >= 0) { + if (!whereClause) { + return false; } - for (const elem2 of elem.generics) { - convertNameToId(elem2); + // mgens[fnType.id] === 0 indicates that we committed to unboxing this generic + // mgens[fnType.id] === null indicates that we haven't decided yet + if (mgens && mgens.has(fnType.id) && mgens.get(fnType.id) !== 0) { + return false; } - elem.bindings = new Map(Array.from(elem.bindings.entries()) - .map(entry => { - const [name, constraints] = entry; - if (!typeNameIdMap.has(name)) { - parsedQuery.error = [ - "Type parameter ", - name, - " does not exist", - ]; - return [null, []]; - } - for (const elem2 of constraints) { - convertNameToId(elem2); - } - - return [typeNameIdMap.get(name).id, constraints]; - }), + // Where clauses can represent cyclical data. + // `null` prevents it from trying to unbox in an infinite loop + const mgensTmp = new Map(mgens); + mgensTmp.set(fnType.id, null); + // This is only a potential unbox if the search query appears in the where clause + // for example, searching `Read -> usize` should find + // `fn read_all(R) -> Result` + // generic `R` is considered "unboxed" + return checkIfInList( + whereClause[(-fnType.id) - 1], + queryElem, + whereClause, + mgensTmp, + unboxingDepth, + ); + } else if (fnType.generics.length > 0 || fnType.bindings.size > 0) { + const simplifiedGenerics = [ + ...fnType.generics, + ...Array.from(fnType.bindings.values()).flat(), + ]; + return checkIfInList( + simplifiedGenerics, + queryElem, + whereClause, + mgens, + unboxingDepth, ); } + return false; + } - const fps = new Set(); - for (const elem of parsedQuery.elems) { - convertNameToId(elem); - buildFunctionTypeFingerprint(elem, parsedQuery.typeFingerprint, fps); - } - for (const elem of parsedQuery.returned) { - convertNameToId(elem); - buildFunctionTypeFingerprint(elem, parsedQuery.typeFingerprint, fps); + /** + * This function checks if the object (`row`) matches the given type (`elem`) and its + * generics (if any). + * + * @param {Array} list + * @param {QueryElement} elem - The element from the parsed query. + * @param {[FunctionType]} whereClause - Trait bounds for generic items. + * @param {Map|null} mgens - Map functions generics to query generics. + * @param {number} unboxingDepth + * + * @return {boolean} - Returns true if found, false otherwise. + */ + function checkIfInList(list, elem, whereClause, mgens, unboxingDepth) { + for (const entry of list) { + if (checkType(entry, elem, whereClause, mgens, unboxingDepth)) { + return true; + } } + return false; + } - if (parsedQuery.foundElems === 1 && parsedQuery.returned.length === 0) { - if (parsedQuery.elems.length === 1) { - const elem = parsedQuery.elems[0]; - for (let i = 0, nSearchIndex = searchIndex.length; i < nSearchIndex; ++i) { - // It means we want to check for this element everywhere (in names, args and - // returned). - handleSingleArg( - searchIndex[i], - i, - elem, - results_others, - results_in_args, - results_returned, - maxEditDistance, - ); - } + /** + * This function checks if the object (`row`) matches the given type (`elem`) and its + * generics (if any). + * + * @param {Row} row + * @param {QueryElement} elem - The element from the parsed query. + * @param {[FunctionType]} whereClause - Trait bounds for generic items. + * @param {Map|null} mgens - Map functions generics to query generics. + * + * @return {boolean} - Returns true if the type matches, false otherwise. + */ + const checkType = (row, elem, whereClause, mgens, unboxingDepth) => { + if (unboxingDepth >= UNBOXING_LIMIT) { + return false; + } + if (row.bindings.size === 0 && elem.bindings.size === 0) { + if (elem.id < 0 && mgens === null) { + return row.id < 0 || checkIfInList( + row.generics, + elem, + whereClause, + mgens, + unboxingDepth + 1, + ); } - } else if (parsedQuery.foundElems > 0) { - // Sort input and output so that generic type variables go first and - // types with generic parameters go last. - // That's because of the way unification is structured: it eats off - // the end, and hits a fast path if the last item is a simple atom. - const sortQ = (a, b) => { - const ag = a.generics.length === 0 && a.bindings.size === 0; - const bg = b.generics.length === 0 && b.bindings.size === 0; - if (ag !== bg) { - return ag - bg; - } - const ai = a.id > 0; - const bi = b.id > 0; - return ai - bi; - }; - parsedQuery.elems.sort(sortQ); - parsedQuery.returned.sort(sortQ); - for (let i = 0, nSearchIndex = searchIndex.length; i < nSearchIndex; ++i) { - handleArgs(searchIndex[i], i, results_others); + if (row.id > 0 && elem.id > 0 && elem.pathWithoutLast.length === 0 && + typePassesFilter(elem.typeFilter, row.ty) && elem.generics.length === 0 && + // special case + elem.id !== this.typeNameIdOfArrayOrSlice + && elem.id !== this.typeNameIdOfTupleOrUnit + && elem.id !== this.typeNameIdOfHof + ) { + return row.id === elem.id || checkIfInList( + row.generics, + elem, + whereClause, + mgens, + unboxingDepth, + ); } } - } - - if (parsedQuery.error === null) { - innerRunQuery(); - } + return unifyFunctionTypes([row], [elem], whereClause, mgens, null, unboxingDepth); + }; - const [sorted_in_args, sorted_returned, sorted_others] = await Promise.all([ - sortResults(results_in_args, true, currentCrate), - sortResults(results_returned, true, currentCrate), - sortResults(results_others, false, currentCrate), - ]); - const ret = createQueryResults( - sorted_in_args, - sorted_returned, - sorted_others, - parsedQuery); - await handleAliases(ret, parsedQuery.original.replace(/"/g, ""), - filterCrates, currentCrate); - await Promise.all([ret.others, ret.returned, ret.in_args].map(async list => { - const descs = await Promise.all(list.map(result => { - return searchIndexEmptyDesc.get(result.crate).contains(result.bitIndex) ? - "" : - searchState.loadDesc(result); - })); - for (const [i, result] of list.entries()) { - result.desc = descs[i]; + /** + * Compute an "edit distance" that ignores missing path elements. + * @param {string[]} contains search query path + * @param {Row} ty indexed item + * @returns {null|number} edit distance + */ + function checkPath(contains, ty) { + if (contains.length === 0) { + return 0; } - })); - if (parsedQuery.error !== null && ret.others.length !== 0) { - // It means some doc aliases were found so let's "remove" the error! - ret.query.error = null; - } - return ret; - } + const maxPathEditDistance = Math.floor( + contains.reduce((acc, next) => acc + next.length, 0) / 3, + ); + let ret_dist = maxPathEditDistance + 1; + const path = ty.path.split("::"); - function nextTab(direction) { - const next = (searchState.currentTab + direction + 3) % searchState.focusedByTab.length; - searchState.focusedByTab[searchState.currentTab] = document.activeElement; - printTab(next); - focusSearchResult(); - } - - // Focus the first search result on the active tab, or the result that - // was focused last time this tab was active. - function focusSearchResult() { - const target = searchState.focusedByTab[searchState.currentTab] || - document.querySelectorAll(".search-results.active a").item(0) || - document.querySelectorAll("#search-tabs button").item(searchState.currentTab); - searchState.focusedByTab[searchState.currentTab] = null; - if (target) { - target.focus(); - } - } - - function buildHrefAndPath(item) { - let displayPath; - let href; - const type = itemTypes[item.ty]; - const name = item.name; - let path = item.path; - let exactPath = item.exactPath; - - if (type === "mod") { - displayPath = path + "::"; - href = ROOT_PATH + path.replace(/::/g, "/") + "/" + - name + "/index.html"; - } else if (type === "import") { - displayPath = item.path + "::"; - href = ROOT_PATH + item.path.replace(/::/g, "/") + "/index.html#reexport." + name; - } else if (type === "primitive" || type === "keyword") { - displayPath = ""; - href = ROOT_PATH + path.replace(/::/g, "/") + - "/" + type + "." + name + ".html"; - } else if (type === "externcrate") { - displayPath = ""; - href = ROOT_PATH + name + "/index.html"; - } else if (item.parent !== undefined) { - const myparent = item.parent; - let anchor = type + "." + name; - const parentType = itemTypes[myparent.ty]; - let pageType = parentType; - let pageName = myparent.name; - exactPath = `${myparent.exactPath}::${myparent.name}`; - - if (parentType === "primitive") { - displayPath = myparent.name + "::"; - } else if (type === "structfield" && parentType === "variant") { - // Structfields belonging to variants are special: the - // final path element is the enum name. - const enumNameIdx = item.path.lastIndexOf("::"); - const enumName = item.path.substr(enumNameIdx + 2); - path = item.path.substr(0, enumNameIdx); - displayPath = path + "::" + enumName + "::" + myparent.name + "::"; - anchor = "variant." + myparent.name + ".field." + name; - pageType = "enum"; - pageName = enumName; - } else { - displayPath = path + "::" + myparent.name + "::"; - } - if (item.implDisambiguator !== null) { - anchor = item.implDisambiguator + "/" + anchor; + if (ty.parent && ty.parent.name) { + path.push(ty.parent.name.toLowerCase()); } - href = ROOT_PATH + path.replace(/::/g, "/") + - "/" + pageType + - "." + pageName + - ".html#" + anchor; - } else { - displayPath = item.path + "::"; - href = ROOT_PATH + item.path.replace(/::/g, "/") + - "/" + type + "." + name + ".html"; - } - return [displayPath, href, `${exactPath}::${name}`]; - } - - function pathSplitter(path) { - const tmp = "" + path.replace(/::/g, "::"); - if (tmp.endsWith("")) { - return tmp.slice(0, tmp.length - 6); - } - return tmp; - } - - /** - * Render a set of search results for a single tab. - * @param {Array} array - The search results for this tab - * @param {ParsedQuery} query - * @param {boolean} display - True if this is the active tab - */ - async function addTab(array, query, display) { - const extraClass = display ? " active" : ""; - - const output = document.createElement("div"); - if (array.length > 0) { - output.className = "search-results " + extraClass; - - for (const item of array) { - const name = item.name; - const type = itemTypes[item.ty]; - const longType = longItemTypes[item.ty]; - const typeName = longType.length !== 0 ? `${longType}` : "?"; - - const link = document.createElement("a"); - link.className = "result-" + type; - link.href = item.href; - - const resultName = document.createElement("div"); - resultName.className = "result-name"; - - resultName.insertAdjacentHTML( - "beforeend", - `${typeName}`); - link.appendChild(resultName); - - let alias = " "; - if (item.is_alias) { - alias = `

\ -${item.alias} - see \ -
`; - } - resultName.insertAdjacentHTML( - "beforeend", - `
${alias}\ -${item.displayPath}${name}\ -
`); - - const description = document.createElement("div"); - description.className = "desc"; - description.insertAdjacentHTML("beforeend", item.desc); - - link.appendChild(description); - output.appendChild(link); - } - } else if (query.error === null) { - output.className = "search-failed" + extraClass; - output.innerHTML = "No results :(
" + - "Try on DuckDuckGo?

" + - "Or try looking in one of these:"; - } - return [output, array.length]; - } - - function makeTabHeader(tabNb, text, nbElems) { - // https://blog.horizon-eda.org/misc/2020/02/19/ui.html - // - // CSS runs with `font-variant-numeric: tabular-nums` to ensure all - // digits are the same width. \u{2007} is a Unicode space character - // that is defined to be the same width as a digit. - const fmtNbElems = - nbElems < 10 ? `\u{2007}(${nbElems})\u{2007}\u{2007}` : - nbElems < 100 ? `\u{2007}(${nbElems})\u{2007}` : - `\u{2007}(${nbElems})`; - if (searchState.currentTab === tabNb) { - return ""; - } - return ""; - } - - /** - * @param {ResultsTable} results - * @param {boolean} go_to_first - * @param {string} filterCrates - */ - async function showResults(results, go_to_first, filterCrates) { - const search = searchState.outputElement(); - if (go_to_first || (results.others.length === 1 - && getSettingValue("go-to-only-result") === "true") - ) { - // Needed to force re-execution of JS when coming back to a page. Let's take this - // scenario as example: - // - // 1. You have the "Directly go to item in search if there is only one result" option - // enabled. - // 2. You make a search which results only one result, leading you automatically to - // this result. - // 3. You go back to previous page. - // - // Now, without the call below, the JS will not be re-executed and the previous state - // will be used, starting search again since the search input is not empty, leading you - // back to the previous page again. - window.onunload = () => {}; - searchState.removeQueryParameters(); - const elem = document.createElement("a"); - elem.href = results.others[0].href; - removeClass(elem, "active"); - // For firefox, we need the element to be in the DOM so it can be clicked. - document.body.appendChild(elem); - elem.click(); - return; - } - if (results.query === undefined) { - results.query = parseQuery(searchState.input.value); - } - - currentResults = results.query.userQuery; - - const [ret_others, ret_in_args, ret_returned] = await Promise.all([ - addTab(results.others, results.query, true), - addTab(results.in_args, results.query, false), - addTab(results.returned, results.query, false), - ]); - // Navigate to the relevant tab if the current tab is empty, like in case users search - // for "-> String". If they had selected another tab previously, they have to click on - // it again. - let currentTab = searchState.currentTab; - if ((currentTab === 0 && ret_others[1] === 0) || - (currentTab === 1 && ret_in_args[1] === 0) || - (currentTab === 2 && ret_returned[1] === 0)) { - if (ret_others[1] !== 0) { - currentTab = 0; - } else if (ret_in_args[1] !== 0) { - currentTab = 1; - } else if (ret_returned[1] !== 0) { - currentTab = 2; - } - } - - let crates = ""; - if (rawSearchIndex.size > 1) { - crates = " in 
"; - } - - let output = `

Results${crates}

`; - if (results.query.error !== null) { - const error = results.query.error; - error.forEach((value, index) => { - value = value.split("<").join("<").split(">").join(">"); - if (index % 2 !== 0) { - error[index] = `${value.replaceAll(" ", " ")}`; - } else { - error[index] = value; + const length = path.length; + const clength = contains.length; + pathiter: for (let i = length - clength; i >= 0; i -= 1) { + let dist_total = 0; + for (let x = 0; x < clength; ++x) { + const [p, c] = [path[i + x], contains[x]]; + if (Math.floor((p.length - c.length) / 3) <= maxPathEditDistance && + p.indexOf(c) !== -1 + ) { + // discount distance on substring match + dist_total += Math.floor((p.length - c.length) / 3); + } else { + const dist = editDistance(p, c, maxPathEditDistance); + if (dist > maxPathEditDistance) { + continue pathiter; + } + dist_total += dist; + } } - }); - output += `

Query parser error: "${error.join("")}".

`; - output += "
" + - makeTabHeader(0, "In Names", ret_others[1]) + - "
"; - currentTab = 0; - } else if (results.query.foundElems <= 1 && results.query.returned.length === 0) { - output += "
" + - makeTabHeader(0, "In Names", ret_others[1]) + - makeTabHeader(1, "In Parameters", ret_in_args[1]) + - makeTabHeader(2, "In Return Types", ret_returned[1]) + - "
"; - } else { - const signatureTabTitle = - results.query.elems.length === 0 ? "In Function Return Types" : - results.query.returned.length === 0 ? "In Function Parameters" : - "In Function Signatures"; - output += "
" + - makeTabHeader(0, signatureTabTitle, ret_others[1]) + - "
"; - currentTab = 0; - } - - if (results.query.correction !== null) { - const orig = results.query.returned.length > 0 - ? results.query.returned[0].name - : results.query.elems[0].name; - output += "

" + - `Type "${orig}" not found. ` + - "Showing results for closest type name " + - `"${results.query.correction}" instead.

`; - } - if (results.query.proposeCorrectionFrom !== null) { - const orig = results.query.proposeCorrectionFrom; - const targ = results.query.proposeCorrectionTo; - output += "

" + - `Type "${orig}" not found and used as generic parameter. ` + - `Consider searching for "${targ}" instead.

`; - } - - const resultsElem = document.createElement("div"); - resultsElem.id = "results"; - resultsElem.appendChild(ret_others[0]); - resultsElem.appendChild(ret_in_args[0]); - resultsElem.appendChild(ret_returned[0]); - - search.innerHTML = output; - const crateSearch = document.getElementById("crate-search"); - if (crateSearch) { - crateSearch.addEventListener("input", updateCrate); - } - search.appendChild(resultsElem); - // Reset focused elements. - searchState.showResults(search); - const elems = document.getElementById("search-tabs").childNodes; - searchState.focusedByTab = []; - let i = 0; - for (const elem of elems) { - const j = i; - elem.onclick = () => printTab(j); - searchState.focusedByTab.push(null); - i += 1; - } - printTab(currentTab); - } - - function updateSearchHistory(url) { - if (!browserSupportsHistoryApi()) { - return; - } - const params = searchState.getQueryStringParams(); - if (!history.state && !params.search) { - history.pushState(null, "", url); - } else { - history.replaceState(null, "", url); - } - } - - /** - * Perform a search based on the current state of the search input element - * and display the results. - * @param {boolean} [forced] - */ - async function search(forced) { - const query = parseQuery(searchState.input.value.trim()); - let filterCrates = getFilterCrates(); - - if (!forced && query.userQuery === currentResults) { - if (query.userQuery.length > 0) { - putBackSearch(); + ret_dist = Math.min(ret_dist, Math.round(dist_total / clength)); } - return; + return ret_dist > maxPathEditDistance ? null : ret_dist; } - searchState.setLoadingSearch(); + function typePassesFilter(filter, type) { + // No filter or Exact mach + if (filter <= NO_TYPE_FILTER || filter === type) return true; - const params = searchState.getQueryStringParams(); + // Match related items + const name = itemTypes[type]; + switch (itemTypes[filter]) { + case "constant": + return name === "associatedconstant"; + case "fn": + return name === "method" || name === "tymethod"; + case "type": + return name === "primitive" || name === "associatedtype"; + case "trait": + return name === "traitalias"; + } - // In case we have no information about the saved crate and there is a URL query parameter, - // we override it with the URL query parameter. - if (filterCrates === null && params["filter-crate"] !== undefined) { - filterCrates = params["filter-crate"]; + // No match + return false; } - // Update document title to maintain a meaningful browser history - searchState.title = "\"" + query.original + "\" Search - Rust"; + function createAliasFromItem(item) { + return { + crate: item.crate, + name: item.name, + path: item.path, + descShard: item.descShard, + descIndex: item.descIndex, + exactPath: item.exactPath, + ty: item.ty, + parent: item.parent, + type: item.type, + is_alias: true, + bitIndex: item.bitIndex, + implDisambiguator: item.implDisambiguator, + }; + } - // Because searching is incremental by character, only the most - // recent search query is added to the browser history. - updateSearchHistory(buildUrl(query.original, filterCrates)); + const handleAliases = async(ret, query, filterCrates, currentCrate) => { + const lowerQuery = query.toLowerCase(); + // We separate aliases and crate aliases because we want to have current crate + // aliases to be before the others in the displayed results. + const aliases = []; + const crateAliases = []; + if (filterCrates !== null) { + if (this.ALIASES.has(filterCrates) + && this.ALIASES.get(filterCrates).has(lowerQuery)) { + const query_aliases = this.ALIASES.get(filterCrates).get(lowerQuery); + for (const alias of query_aliases) { + aliases.push(createAliasFromItem(this.searchIndex[alias])); + } + } + } else { + for (const [crate, crateAliasesIndex] of this.ALIASES) { + if (crateAliasesIndex.has(lowerQuery)) { + const pushTo = crate === currentCrate ? crateAliases : aliases; + const query_aliases = crateAliasesIndex.get(lowerQuery); + for (const alias of query_aliases) { + pushTo.push(createAliasFromItem(this.searchIndex[alias])); + } + } + } + } - await showResults( - await execQuery(query, filterCrates, window.currentCrate), - params.go_to_first, - filterCrates); - } + const sortFunc = (aaa, bbb) => { + if (aaa.path < bbb.path) { + return 1; + } else if (aaa.path === bbb.path) { + return 0; + } + return -1; + }; + crateAliases.sort(sortFunc); + aliases.sort(sortFunc); - /** - * Convert a list of RawFunctionType / ID to object-based FunctionType. - * - * Crates often have lots of functions in them, and it's common to have a large number of - * functions that operate on a small set of data types, so the search index compresses them - * by encoding function parameter and return types as indexes into an array of names. - * - * Even when a general-purpose compression algorithm is used, this is still a win. I checked. - * https://github.com/rust-lang/rust/pull/98475#issue-1284395985 - * - * The format for individual function types is encoded in - * librustdoc/html/render/mod.rs: impl Serialize for RenderType - * - * @param {null|Array} types - * @param {Array<{name: string, ty: number}>} lowercasePaths - * - * @return {Array} - */ - function buildItemSearchTypeAll(types, lowercasePaths) { - return types.length > 0 ? - types.map(type => buildItemSearchType(type, lowercasePaths)) : - EMPTY_GENERICS_ARRAY; - } + const fetchDesc = alias => { + return this.searchIndexEmptyDesc.get(alias.crate).contains(alias.bitIndex) ? + "" : this.searchState.loadDesc(alias); + }; + const [crateDescs, descs] = await Promise.all([ + Promise.all(crateAliases.map(fetchDesc)), + Promise.all(aliases.map(fetchDesc)), + ]); - /** - * Empty, immutable map used in item search types with no bindings. - * - * @type {Map>} - */ - const EMPTY_BINDINGS_MAP = new Map(); + const pushFunc = alias => { + alias.alias = query; + const res = buildHrefAndPath(alias); + alias.displayPath = pathSplitter(res[0]); + alias.fullPath = alias.displayPath + alias.name; + alias.href = res[1]; - /** - * Empty, immutable map used in item search types with no bindings. - * - * @type {Array} - */ - const EMPTY_GENERICS_ARRAY = []; + ret.others.unshift(alias); + if (ret.others.length > MAX_RESULTS) { + ret.others.pop(); + } + }; - /** - * Object pool for function types with no bindings or generics. - * This is reset after loading the index. - * - * @type {Map} - */ - let TYPES_POOL = new Map(); + aliases.forEach((alias, i) => { + alias.desc = descs[i]; + }); + aliases.forEach(pushFunc); + crateAliases.forEach((alias, i) => { + alias.desc = crateDescs[i]; + }); + crateAliases.forEach(pushFunc); + }; - /** - * Converts a single type. - * - * @param {RawFunctionType} type - */ - function buildItemSearchType(type, lowercasePaths, isAssocType) { - const PATH_INDEX_DATA = 0; - const GENERICS_DATA = 1; - const BINDINGS_DATA = 2; - let pathIndex, generics, bindings; - if (typeof type === "number") { - pathIndex = type; - generics = EMPTY_GENERICS_ARRAY; - bindings = EMPTY_BINDINGS_MAP; - } else { - pathIndex = type[PATH_INDEX_DATA]; - generics = buildItemSearchTypeAll( - type[GENERICS_DATA], - lowercasePaths, - ); - if (type.length > BINDINGS_DATA && type[BINDINGS_DATA].length > 0) { - bindings = new Map(type[BINDINGS_DATA].map(binding => { - const [assocType, constraints] = binding; - // Associated type constructors are represented sloppily in rustdoc's - // type search, to make the engine simpler. - // - // MyType=Result> is equivalent to MyType>=T> - // and both are, essentially - // MyType)>, except the tuple isn't actually there. - // It's more like the value of a type binding is naturally an array, - // which rustdoc calls "constraints". - // - // As a result, the key should never have generics on it. - return [ - buildItemSearchType(assocType, lowercasePaths, true).id, - buildItemSearchTypeAll(constraints, lowercasePaths), - ]; - })); - } else { - bindings = EMPTY_BINDINGS_MAP; + /** + * This function adds the given result into the provided `results` map if it matches the + * following condition: + * + * * If it is a "literal search" (`parsedQuery.literalSearch`), then `dist` must be 0. + * * If it is not a "literal search", `dist` must be <= `maxEditDistance`. + * + * The `results` map contains information which will be used to sort the search results: + * + * * `fullId` is a `string`` used as the key of the object we use for the `results` map. + * * `id` is the index in the `searchIndex` array for this element. + * * `index` is an `integer`` used to sort by the position of the word in the item's name. + * * `dist` is the main metric used to sort the search results. + * * `path_dist` is zero if a single-component search query is used, otherwise it's the + * distance computed for everything other than the last path component. + * + * @param {Results} results + * @param {string} fullId + * @param {integer} id + * @param {integer} index + * @param {integer} dist + * @param {integer} path_dist + */ + function addIntoResults(results, fullId, id, index, dist, path_dist, maxEditDistance) { + if (dist <= maxEditDistance || index !== -1) { + if (results.has(fullId)) { + const result = results.get(fullId); + if (result.dontValidate || result.dist <= dist) { + return; + } + } + results.set(fullId, { + id: id, + index: index, + dontValidate: parsedQuery.literalSearch, + dist: dist, + path_dist: path_dist, + }); } } + /** - * @type {FunctionType} + * This function is called in case the query is only one element (with or without generics). + * This element will be compared to arguments' and returned values' items and also to items. + * + * Other important thing to note: since there is only one element, we use edit + * distance for name comparisons. + * + * @param {Row} row + * @param {integer} pos - Position in the `searchIndex`. + * @param {QueryElement} elem - The element from the parsed query. + * @param {Results} results_others - Unqualified results (not in arguments nor in + * returned values). + * @param {Results} results_in_args - Matching arguments results. + * @param {Results} results_returned - Matching returned arguments results. */ - let result; - if (pathIndex < 0) { - // types less than 0 are generic parameters - // the actual names of generic parameters aren't stored, since they aren't API - result = { - id: pathIndex, - ty: TY_GENERIC, - path: null, - exactPath: null, - generics, - bindings, - }; - } else if (pathIndex === 0) { - // `0` is used as a sentinel because it's fewer bytes than `null` - result = { - id: null, - ty: null, - path: null, - exactPath: null, - generics, - bindings, - }; - } else { - const item = lowercasePaths[pathIndex - 1]; - result = { - id: buildTypeMapIndex(item.name, isAssocType), - ty: item.ty, - path: item.path, - exactPath: item.exactPath, - generics, - bindings, - }; - } - const cr = TYPES_POOL.get(result.id); - if (cr) { - // Shallow equality check. Since this function is used - // to construct every type object, this should be mostly - // equivalent to a deep equality check, except if there's - // a conflict, we don't keep the old one around, so it's - // not a fully precise implementation of hashcons. - if (cr.generics.length === result.generics.length && - cr.generics !== result.generics && - cr.generics.every((x, i) => result.generics[i] === x) - ) { - result.generics = cr.generics; + function handleSingleArg( + row, + pos, + elem, + results_others, + results_in_args, + results_returned, + maxEditDistance, + ) { + if (!row || (filterCrates !== null && row.crate !== filterCrates)) { + return; } - if (cr.bindings.size === result.bindings.size && cr.bindings !== result.bindings) { - let ok = true; - for (const [k, v] of cr.bindings.entries()) { - const v2 = result.bindings.get(v); - if (!v2) { - ok = false; - break; - } - if (v !== v2 && v.length === v2.length && v.every((x, i) => v2[i] === x)) { - result.bindings.set(k, v); - } else if (v !== v2) { - ok = false; - break; - } + let path_dist = 0; + const fullId = row.id; + + // fpDist is a minimum possible type distance, where "type distance" is the number of + // atoms in the function not present in the query + const tfpDist = compareTypeFingerprints( + fullId, + parsedQuery.typeFingerprint, + ); + if (tfpDist !== null) { + const in_args = row.type && row.type.inputs + && checkIfInList(row.type.inputs, elem, row.type.where_clause, null, 0); + const returned = row.type && row.type.output + && checkIfInList(row.type.output, elem, row.type.where_clause, null, 0); + if (in_args) { + results_in_args.max_dist = Math.max(results_in_args.max_dist || 0, tfpDist); + const maxDist = results_in_args.size < MAX_RESULTS ? + (tfpDist + 1) : + results_in_args.max_dist; + addIntoResults(results_in_args, fullId, pos, -1, tfpDist, 0, maxDist); + } + if (returned) { + results_returned.max_dist = Math.max(results_returned.max_dist || 0, tfpDist); + const maxDist = results_returned.size < MAX_RESULTS ? + (tfpDist + 1) : + results_returned.max_dist; + addIntoResults(results_returned, fullId, pos, -1, tfpDist, 0, maxDist); + } + } + + if (!typePassesFilter(elem.typeFilter, row.ty)) { + return; + } + + let index = row.word.indexOf(elem.pathLast); + const normalizedIndex = row.normalizedName.indexOf(elem.pathLast); + if (index === -1 || (index > normalizedIndex && normalizedIndex !== -1)) { + index = normalizedIndex; + } + + if (elem.fullPath.length > 1) { + path_dist = checkPath(elem.pathWithoutLast, row); + if (path_dist === null) { + return; } - if (ok) { - result.bindings = cr.bindings; + } + + if (parsedQuery.literalSearch) { + if (row.word === elem.pathLast) { + addIntoResults(results_others, fullId, pos, index, 0, path_dist); } + return; } - if (cr.ty === result.ty && cr.path === result.path - && cr.bindings === result.bindings && cr.generics === result.generics - && cr.ty === result.ty - ) { - return cr; + + const dist = editDistance(row.normalizedName, elem.normalizedPathLast, maxEditDistance); + + if (index === -1 && dist > maxEditDistance) { + return; } + + addIntoResults(results_others, fullId, pos, index, dist, path_dist, maxEditDistance); } - TYPES_POOL.set(result.id, result); - return result; - } - /** - * Convert from RawFunctionSearchType to FunctionSearchType. - * - * Crates often have lots of functions in them, and function signatures are sometimes complex, - * so rustdoc uses a pretty tight encoding for them. This function converts it to a simpler, - * object-based encoding so that the actual search code is more readable and easier to debug. - * - * The raw function search type format is generated using serde in - * librustdoc/html/render/mod.rs: IndexItemFunctionType::write_to_string - * - * @param {Array<{name: string, ty: number}>} lowercasePaths - * - * @return {null|FunctionSearchType} - */ - function buildFunctionSearchTypeCallback(lowercasePaths) { - return functionSearchType => { - if (functionSearchType === 0) { - return null; + /** + * This function is called in case the query has more than one element. In this case, it'll + * try to match the items which validates all the elements. For `aa -> bb` will look for + * functions which have a parameter `aa` and has `bb` in its returned values. + * + * @param {Row} row + * @param {integer} pos - Position in the `searchIndex`. + * @param {Object} results + */ + function handleArgs(row, pos, results) { + if (!row || (filterCrates !== null && row.crate !== filterCrates) || !row.type) { + return; } - const INPUTS_DATA = 0; - const OUTPUT_DATA = 1; - let inputs, output; - if (typeof functionSearchType[INPUTS_DATA] === "number") { - inputs = [buildItemSearchType(functionSearchType[INPUTS_DATA], lowercasePaths)]; - } else { - inputs = buildItemSearchTypeAll( - functionSearchType[INPUTS_DATA], - lowercasePaths, - ); + + const tfpDist = compareTypeFingerprints( + row.id, + parsedQuery.typeFingerprint, + ); + if (tfpDist === null) { + return; } - if (functionSearchType.length > 1) { - if (typeof functionSearchType[OUTPUT_DATA] === "number") { - output = [buildItemSearchType(functionSearchType[OUTPUT_DATA], lowercasePaths)]; - } else { - output = buildItemSearchTypeAll( - functionSearchType[OUTPUT_DATA], - lowercasePaths, - ); - } - } else { - output = []; + if (results.size >= MAX_RESULTS && tfpDist > results.max_dist) { + return; } - const where_clause = []; - const l = functionSearchType.length; - for (let i = 2; i < l; ++i) { - where_clause.push(typeof functionSearchType[i] === "number" - ? [buildItemSearchType(functionSearchType[i], lowercasePaths)] - : buildItemSearchTypeAll(functionSearchType[i], lowercasePaths)); + + // If the result is too "bad", we return false and it ends this search. + if (!unifyFunctionTypes( + row.type.inputs, + parsedQuery.elems, + row.type.where_clause, + null, + mgens => { + return unifyFunctionTypes( + row.type.output, + parsedQuery.returned, + row.type.where_clause, + mgens, + null, + 0, // unboxing depth + ); + }, + 0, // unboxing depth + )) { + return; } - return { - inputs, output, where_clause, - }; - }; - } - /** - * Type fingerprints allow fast, approximate matching of types. - * - * This algo creates a compact representation of the type set using a Bloom filter. - * This fingerprint is used three ways: - * - * - It accelerates the matching algorithm by checking the function fingerprint against the - * query fingerprint. If any bits are set in the query but not in the function, it can't - * match. - * - * - The fourth section has the number of distinct items in the set. - * This is the distance function, used for filtering and for sorting. - * - * [^1]: Distance is the relatively naive metric of counting the number of distinct items in - * the function that are not present in the query. - * - * @param {FunctionType|QueryElement} type - a single type - * @param {Uint32Array} output - write the fingerprint to this data structure: uses 128 bits - * @param {Set} fps - Set of distinct items - */ - function buildFunctionTypeFingerprint(type, output, fps) { - let input = type.id; - // All forms of `[]`/`()`/`->` get collapsed down to one thing in the bloom filter. - // Differentiating between arrays and slices, if the user asks for it, is - // still done in the matching algorithm. - if (input === typeNameIdOfArray || input === typeNameIdOfSlice) { - input = typeNameIdOfArrayOrSlice; - } - if (input === typeNameIdOfTuple || input === typeNameIdOfUnit) { - input = typeNameIdOfTupleOrUnit; - } - if (input === typeNameIdOfFn || input === typeNameIdOfFnMut || - input === typeNameIdOfFnOnce) { - input = typeNameIdOfHof; - } - // http://burtleburtle.net/bob/hash/integer.html - // ~~ is toInt32. It's used before adding, so - // the number stays in safe integer range. - const hashint1 = k => { - k = (~~k + 0x7ed55d16) + (k << 12); - k = (k ^ 0xc761c23c) ^ (k >>> 19); - k = (~~k + 0x165667b1) + (k << 5); - k = (~~k + 0xd3a2646c) ^ (k << 9); - k = (~~k + 0xfd7046c5) + (k << 3); - return (k ^ 0xb55a4f09) ^ (k >>> 16); - }; - const hashint2 = k => { - k = ~k + (k << 15); - k ^= k >>> 12; - k += k << 2; - k ^= k >>> 4; - k = Math.imul(k, 2057); - return k ^ (k >> 16); - }; - if (input !== null) { - const h0a = hashint1(input); - const h0b = hashint2(input); - // Less Hashing, Same Performance: Building a Better Bloom Filter - // doi=10.1.1.72.2442 - const h1a = ~~(h0a + Math.imul(h0b, 2)); - const h1b = ~~(h0a + Math.imul(h0b, 3)); - const h2a = ~~(h0a + Math.imul(h0b, 4)); - const h2b = ~~(h0a + Math.imul(h0b, 5)); - output[0] |= (1 << (h0a % 32)) | (1 << (h1b % 32)); - output[1] |= (1 << (h1a % 32)) | (1 << (h2b % 32)); - output[2] |= (1 << (h2a % 32)) | (1 << (h0b % 32)); - fps.add(input); - } - for (const g of type.generics) { - buildFunctionTypeFingerprint(g, output, fps); - } - const fb = { - id: null, - ty: 0, - generics: EMPTY_GENERICS_ARRAY, - bindings: EMPTY_BINDINGS_MAP, - }; - for (const [k, v] of type.bindings.entries()) { - fb.id = k; - fb.generics = v; - buildFunctionTypeFingerprint(fb, output, fps); + results.max_dist = Math.max(results.max_dist || 0, tfpDist); + addIntoResults(results, row.id, pos, 0, tfpDist, 0, Number.MAX_VALUE); } - output[3] = fps.size; - } - /** - * Compare the query fingerprint with the function fingerprint. - * - * @param {{number}} fullId - The function - * @param {{Uint32Array}} queryFingerprint - The query - * @returns {number|null} - Null if non-match, number if distance - * This function might return 0! - */ - function compareTypeFingerprints(fullId, queryFingerprint) { - const fh0 = functionTypeFingerprint[fullId * 4]; - const fh1 = functionTypeFingerprint[(fullId * 4) + 1]; - const fh2 = functionTypeFingerprint[(fullId * 4) + 2]; - const [qh0, qh1, qh2] = queryFingerprint; - // Approximate set intersection with bloom filters. - // This can be larger than reality, not smaller, because hashes have - // the property that if they've got the same value, they hash to the - // same thing. False positives exist, but not false negatives. - const [in0, in1, in2] = [fh0 & qh0, fh1 & qh1, fh2 & qh2]; - // Approximate the set of items in the query but not the function. - // This might be smaller than reality, but cannot be bigger. - // - // | in_ | qh_ | XOR | Meaning | - // | --- | --- | --- | ------------------------------------------------ | - // | 0 | 0 | 0 | Not present | - // | 1 | 0 | 1 | IMPOSSIBLE because `in_` is `fh_ & qh_` | - // | 1 | 1 | 0 | If one or both is false positive, false negative | - // | 0 | 1 | 1 | Since in_ has no false negatives, must be real | - if ((in0 ^ qh0) || (in1 ^ qh1) || (in2 ^ qh2)) { - return null; - } - return functionTypeFingerprint[(fullId * 4) + 3]; - } - - class VlqHexDecoder { - constructor(string, cons) { - this.string = string; - this.cons = cons; - this.offset = 0; - this.backrefQueue = []; - } - // call after consuming `{` - decodeList() { - let c = this.string.charCodeAt(this.offset); - const ret = []; - while (c !== 125) { // 125 = "}" - ret.push(this.decode()); - c = this.string.charCodeAt(this.offset); - } - this.offset += 1; // eat cb - return ret; - } - // consumes and returns a list or integer - decode() { - let n = 0; - let c = this.string.charCodeAt(this.offset); - if (c === 123) { // 123 = "{" - this.offset += 1; - return this.decodeList(); - } - while (c < 96) { // 96 = "`" - n = (n << 4) | (c & 0xF); - this.offset += 1; - c = this.string.charCodeAt(this.offset); - } - // last character >= la - n = (n << 4) | (c & 0xF); - const [sign, value] = [n & 1, n >> 1]; - this.offset += 1; - return sign ? -value : value; - } - next() { - const c = this.string.charCodeAt(this.offset); - // sixteen characters after "0" are backref - if (c >= 48 && c < 64) { // 48 = "0", 64 = "@" - this.offset += 1; - return this.backrefQueue[c - 48]; - } - // special exception: 0 doesn't use backref encoding - // it's already one character, and it's always nullish - if (c === 96) { // 96 = "`" - this.offset += 1; - return this.cons(0); - } - const result = this.cons(this.decode()); - this.backrefQueue.unshift(result); - if (this.backrefQueue.length > 16) { - this.backrefQueue.pop(); - } - return result; - } - } - class RoaringBitmap { - constructor(str) { - const strdecoded = atob(str); - const u8array = new Uint8Array(strdecoded.length); - for (let j = 0; j < strdecoded.length; ++j) { - u8array[j] = strdecoded.charCodeAt(j); - } - const has_runs = u8array[0] === 0x3b; - const size = has_runs ? - ((u8array[2] | (u8array[3] << 8)) + 1) : - ((u8array[4] | (u8array[5] << 8) | (u8array[6] << 16) | (u8array[7] << 24))); - let i = has_runs ? 4 : 8; - let is_run; - if (has_runs) { - const is_run_len = Math.floor((size + 7) / 8); - is_run = u8array.slice(i, i + is_run_len); - i += is_run_len; - } else { - is_run = new Uint8Array(); - } - this.keys = []; - this.cardinalities = []; - for (let j = 0; j < size; ++j) { - this.keys.push(u8array[i] | (u8array[i + 1] << 8)); - i += 2; - this.cardinalities.push((u8array[i] | (u8array[i + 1] << 8)) + 1); - i += 2; + /** + * Compare the query fingerprint with the function fingerprint. + * + * @param {{number}} fullId - The function + * @param {{Uint32Array}} queryFingerprint - The query + * @returns {number|null} - Null if non-match, number if distance + * This function might return 0! + */ + const compareTypeFingerprints = (fullId, queryFingerprint) => { + const fh0 = this.functionTypeFingerprint[fullId * 4]; + const fh1 = this.functionTypeFingerprint[(fullId * 4) + 1]; + const fh2 = this.functionTypeFingerprint[(fullId * 4) + 2]; + const [qh0, qh1, qh2] = queryFingerprint; + // Approximate set intersection with bloom filters. + // This can be larger than reality, not smaller, because hashes have + // the property that if they've got the same value, they hash to the + // same thing. False positives exist, but not false negatives. + const [in0, in1, in2] = [fh0 & qh0, fh1 & qh1, fh2 & qh2]; + // Approximate the set of items in the query but not the function. + // This might be smaller than reality, but cannot be bigger. + // + // | in_ | qh_ | XOR | Meaning | + // | --- | --- | --- | ------------------------------------------------ | + // | 0 | 0 | 0 | Not present | + // | 1 | 0 | 1 | IMPOSSIBLE because `in_` is `fh_ & qh_` | + // | 1 | 1 | 0 | If one or both is false positive, false negative | + // | 0 | 1 | 1 | Since in_ has no false negatives, must be real | + if ((in0 ^ qh0) || (in1 ^ qh1) || (in2 ^ qh2)) { + return null; } - this.containers = []; - let offsets = null; - if (!has_runs || this.keys.length >= 4) { - offsets = []; - for (let j = 0; j < size; ++j) { - offsets.push(u8array[i] | (u8array[i + 1] << 8) | (u8array[i + 2] << 16) | - (u8array[i + 3] << 24)); - i += 4; + return this.functionTypeFingerprint[(fullId * 4) + 3]; + }; + + + const innerRunQuery = () => { + const queryLen = + parsedQuery.elems.reduce((acc, next) => acc + next.pathLast.length, 0) + + parsedQuery.returned.reduce((acc, next) => acc + next.pathLast.length, 0); + const maxEditDistance = Math.floor(queryLen / 3); + + /** + * @type {Map} + */ + const genericSymbols = new Map(); + + /** + * Convert names to ids in parsed query elements. + * This is not used for the "In Names" tab, but is used for the + * "In Params", "In Returns", and "In Function Signature" tabs. + * + * If there is no matching item, but a close-enough match, this + * function also that correction. + * + * See `buildTypeMapIndex` for more information. + * + * @param {QueryElement} elem + * @param {boolean} isAssocType + */ + const convertNameToId = (elem, isAssocType) => { + const loweredName = elem.pathLast.toLowerCase(); + if (this.typeNameIdMap.has(loweredName) && + (isAssocType || !this.typeNameIdMap.get(loweredName).assocOnly)) { + elem.id = this.typeNameIdMap.get(loweredName).id; + } else if (!parsedQuery.literalSearch) { + let match = null; + let matchDist = maxEditDistance + 1; + let matchName = ""; + for (const [name, { id, assocOnly }] of this.typeNameIdMap) { + const dist = Math.min( + editDistance(name, loweredName, maxEditDistance), + editDistance(name, elem.normalizedPathLast, maxEditDistance), + ); + if (dist <= matchDist && dist <= maxEditDistance && + (isAssocType || !assocOnly)) { + if (dist === matchDist && matchName > name) { + continue; + } + match = id; + matchDist = dist; + matchName = name; + } + } + if (match !== null) { + parsedQuery.correction = matchName; + } + elem.id = match; + } + if ((elem.id === null && parsedQuery.totalElems > 1 && elem.typeFilter === -1 + && elem.generics.length === 0 && elem.bindings.size === 0) + || elem.typeFilter === TY_GENERIC) { + if (genericSymbols.has(elem.name)) { + elem.id = genericSymbols.get(elem.name); + } else { + elem.id = -(genericSymbols.size + 1); + genericSymbols.set(elem.name, elem.id); + } + if (elem.typeFilter === -1 && elem.name.length >= 3) { + // Silly heuristic to catch if the user probably meant + // to not write a generic parameter. We don't use it, + // just bring it up. + const maxPartDistance = Math.floor(elem.name.length / 3); + let matchDist = maxPartDistance + 1; + let matchName = ""; + for (const name of this.typeNameIdMap.keys()) { + const dist = editDistance(name, elem.name, maxPartDistance); + if (dist <= matchDist && dist <= maxPartDistance) { + if (dist === matchDist && matchName > name) { + continue; + } + matchDist = dist; + matchName = name; + } + } + if (matchName !== "") { + parsedQuery.proposeCorrectionFrom = elem.name; + parsedQuery.proposeCorrectionTo = matchName; + } + } + elem.typeFilter = TY_GENERIC; } - } - for (let j = 0; j < size; ++j) { - if (offsets && offsets[j] !== i) { - console.log(this.containers); - throw new Error(`corrupt bitmap ${j}: ${i} / ${offsets[j]}`); - } - if (is_run[j >> 3] & (1 << (j & 0x7))) { - const runcount = (u8array[i] | (u8array[i + 1] << 8)); - i += 2; - this.containers.push(new RoaringBitmapRun( - runcount, - u8array.slice(i, i + (runcount * 4)), - )); - i += runcount * 4; - } else if (this.cardinalities[j] >= 4096) { - this.containers.push(new RoaringBitmapBits(u8array.slice(i, i + 8192))); - i += 8192; - } else { - const end = this.cardinalities[j] * 2; - this.containers.push(new RoaringBitmapArray( - this.cardinalities[j], - u8array.slice(i, i + end), - )); - i += end; + if (elem.generics.length > 0 && elem.typeFilter === TY_GENERIC) { + // Rust does not have HKT + parsedQuery.error = [ + "Generic type parameter ", + elem.name, + " does not accept generic parameters", + ]; } - } - } - contains(keyvalue) { - const key = keyvalue >> 16; - const value = keyvalue & 0xFFFF; - for (let i = 0; i < this.keys.length; ++i) { - if (this.keys[i] === key) { - return this.containers[i].contains(value); + for (const elem2 of elem.generics) { + convertNameToId(elem2); } + elem.bindings = new Map(Array.from(elem.bindings.entries()) + .map(entry => { + const [name, constraints] = entry; + if (!this.typeNameIdMap.has(name)) { + parsedQuery.error = [ + "Type parameter ", + name, + " does not exist", + ]; + return [null, []]; + } + for (const elem2 of constraints) { + convertNameToId(elem2); + } + + return [this.typeNameIdMap.get(name).id, constraints]; + }), + ); + }; + + const fps = new Set(); + for (const elem of parsedQuery.elems) { + convertNameToId(elem); + this.buildFunctionTypeFingerprint(elem, parsedQuery.typeFingerprint, fps); + } + for (const elem of parsedQuery.returned) { + convertNameToId(elem); + this.buildFunctionTypeFingerprint(elem, parsedQuery.typeFingerprint, fps); } - return false; - } - } - class RoaringBitmapRun { - constructor(runcount, array) { - this.runcount = runcount; - this.array = array; - } - contains(value) { - const l = this.runcount * 4; - for (let i = 0; i < l; i += 4) { - const start = this.array[i] | (this.array[i + 1] << 8); - const lenm1 = this.array[i + 2] | (this.array[i + 3] << 8); - if (value >= start && value <= (start + lenm1)) { - return true; + if (parsedQuery.foundElems === 1 && parsedQuery.returned.length === 0) { + if (parsedQuery.elems.length === 1) { + const elem = parsedQuery.elems[0]; + const length = this.searchIndex.length; + for (let i = 0, nSearchIndex = length; i < nSearchIndex; ++i) { + // It means we want to check for this element everywhere (in names, args and + // returned). + handleSingleArg( + this.searchIndex[i], + i, + elem, + results_others, + results_in_args, + results_returned, + maxEditDistance, + ); + } } - } - return false; - } - } - class RoaringBitmapArray { - constructor(cardinality, array) { - this.cardinality = cardinality; - this.array = array; - } - contains(value) { - const l = this.cardinality * 2; - for (let i = 0; i < l; i += 2) { - const start = this.array[i] | (this.array[i + 1] << 8); - if (value === start) { - return true; + } else if (parsedQuery.foundElems > 0) { + // Sort input and output so that generic type variables go first and + // types with generic parameters go last. + // That's because of the way unification is structured: it eats off + // the end, and hits a fast path if the last item is a simple atom. + const sortQ = (a, b) => { + const ag = a.generics.length === 0 && a.bindings.size === 0; + const bg = b.generics.length === 0 && b.bindings.size === 0; + if (ag !== bg) { + return ag - bg; + } + const ai = a.id > 0; + const bi = b.id > 0; + return ai - bi; + }; + parsedQuery.elems.sort(sortQ); + parsedQuery.returned.sort(sortQ); + for (let i = 0, nSearchIndex = this.searchIndex.length; i < nSearchIndex; ++i) { + handleArgs(this.searchIndex[i], i, results_others); } } - return false; - } - } - class RoaringBitmapBits { - constructor(array) { - this.array = array; - } - contains(value) { - return !!(this.array[value >> 3] & (1 << (value & 7))); - } - } - - /** - * Convert raw search index into in-memory search index. - * - * @param {[string, RawSearchIndexCrate][]} rawSearchIndex - */ - function buildIndex(rawSearchIndex) { - searchIndex = []; - searchIndexDeprecated = new Map(); - searchIndexEmptyDesc = new Map(); - let currentIndex = 0; - let id = 0; + }; - // Function type fingerprints are 128-bit bloom filters that are used to - // estimate the distance between function and query. - // This loop counts the number of items to allocate a fingerprint for. - for (const crate of rawSearchIndex.values()) { - // Each item gets an entry in the fingerprint array, and the crate - // does, too - id += crate.t.length + 1; + if (parsedQuery.error === null) { + innerRunQuery(); } - functionTypeFingerprint = new Uint32Array((id + 1) * 4); - - // This loop actually generates the search item indexes, including - // normalized names, type signature objects and fingerprints, and aliases. - id = 0; - for (const [crate, crateCorpus] of rawSearchIndex) { - // a string representing the lengths of each description shard - // a string representing the list of function types - const itemDescShardDecoder = new VlqHexDecoder(crateCorpus.D, noop => noop); - let descShard = { - crate, - shard: 0, - start: 0, - len: itemDescShardDecoder.next(), - promise: null, - resolve: null, - }; - const descShardList = [ descShard ]; + const [sorted_in_args, sorted_returned, sorted_others] = await Promise.all([ + sortResults(results_in_args, true, currentCrate), + sortResults(results_returned, true, currentCrate), + sortResults(results_others, false, currentCrate), + ]); + const ret = createQueryResults( + sorted_in_args, + sorted_returned, + sorted_others, + parsedQuery); + await handleAliases(ret, parsedQuery.original.replace(/"/g, ""), + filterCrates, currentCrate); + await Promise.all([ret.others, ret.returned, ret.in_args].map(async list => { + const descs = await Promise.all(list.map(result => { + return this.searchIndexEmptyDesc.get(result.crate).contains(result.bitIndex) ? + "" : + this.searchState.loadDesc(result); + })); + for (const [i, result] of list.entries()) { + result.desc = descs[i]; + } + })); + if (parsedQuery.error !== null && ret.others.length !== 0) { + // It means some doc aliases were found so let's "remove" the error! + ret.query.error = null; + } + return ret; + } +} - // Deprecated items and items with no description - searchIndexDeprecated.set(crate, new RoaringBitmap(crateCorpus.c)); - searchIndexEmptyDesc.set(crate, new RoaringBitmap(crateCorpus.e)); - let descIndex = 0; +// ==================== Core search logic end ==================== - // This object should have exactly the same set of fields as the "row" - // object defined below. Your JavaScript runtime will thank you. - // https://mathiasbynens.be/notes/shapes-ics - const crateRow = { - crate, - ty: 3, // == ExternCrate - name: crate, - path: "", - descShard, - descIndex, - exactPath: "", - desc: crateCorpus.doc, - parent: undefined, - type: null, - id, - word: crate, - normalizedName: crate.indexOf("_") === -1 ? crate : crate.replace(/_/g, ""), - bitIndex: 0, - implDisambiguator: null, - }; - id += 1; - searchIndex.push(crateRow); - currentIndex += 1; - if (!searchIndexEmptyDesc.get(crate).contains(0)) { - descIndex += 1; - } +let rawSearchIndex; +let docSearch; +const longItemTypes = [ + "keyword", + "primitive type", + "module", + "extern crate", + "re-export", + "struct", + "enum", + "function", + "type alias", + "static", + "trait", + "", + "trait method", + "method", + "struct field", + "enum variant", + "macro", + "assoc type", + "constant", + "assoc const", + "union", + "foreign type", + "existential type", + "attribute macro", + "derive macro", + "trait alias", +]; +let currentResults; - // a String of one character item type codes - const itemTypes = crateCorpus.t; - // an array of (String) item names - const itemNames = crateCorpus.n; - // an array of [(Number) item index, - // (String) full path] - // an item whose index is not present will fall back to the previous present path - // i.e. if indices 4 and 11 are present, but 5-10 and 12-13 are not present, - // 5-10 will fall back to the path for 4 and 12-13 will fall back to the path for 11 - const itemPaths = new Map(crateCorpus.q); - // An array of [(Number) item index, (Number) path index] - // Used to de-duplicate inlined and re-exported stuff - const itemReexports = new Map(crateCorpus.r); - // an array of (Number) the parent path index + 1 to `paths`, or 0 if none - const itemParentIdxDecoder = new VlqHexDecoder(crateCorpus.i, noop => noop); - // a map Number, string for impl disambiguators - const implDisambiguator = new Map(crateCorpus.b); - // an array of [(Number) item type, - // (String) name] - const paths = crateCorpus.p; - // an array of [(String) alias name - // [Number] index to items] - const aliases = crateCorpus.a; +// In the search display, allows to switch between tabs. +function printTab(nb) { + let iter = 0; + let foundCurrentTab = false; + let foundCurrentResultSet = false; + onEachLazy(document.getElementById("search-tabs").childNodes, elem => { + if (nb === iter) { + addClass(elem, "selected"); + foundCurrentTab = true; + } else { + removeClass(elem, "selected"); + } + iter += 1; + }); + const isTypeSearch = (nb > 0 || iter === 1); + iter = 0; + onEachLazy(document.getElementById("results").childNodes, elem => { + if (nb === iter) { + addClass(elem, "active"); + foundCurrentResultSet = true; + } else { + removeClass(elem, "active"); + } + iter += 1; + }); + if (foundCurrentTab && foundCurrentResultSet) { + searchState.currentTab = nb; + // Corrections only kick in on type-based searches. + const correctionsElem = document.getElementsByClassName("search-corrections"); + if (isTypeSearch) { + removeClass(correctionsElem[0], "hidden"); + } else { + addClass(correctionsElem[0], "hidden"); + } + } else if (nb !== 0) { + printTab(0); + } +} - // an array of [{name: String, ty: Number}] - const lowercasePaths = []; +/** + * Build an URL with search parameters. + * + * @param {string} search - The current search being performed. + * @param {string|null} filterCrates - The current filtering crate (if any). + * + * @return {string} + */ +function buildUrl(search, filterCrates) { + let extra = "?search=" + encodeURIComponent(search); - // a string representing the list of function types - const itemFunctionDecoder = new VlqHexDecoder( - crateCorpus.f, - buildFunctionSearchTypeCallback(lowercasePaths), - ); + if (filterCrates !== null) { + extra += "&filter-crate=" + encodeURIComponent(filterCrates); + } + return getNakedUrl() + extra + window.location.hash; +} - // convert `rawPaths` entries into object form - // generate normalizedPaths for function search mode - let len = paths.length; - let lastPath = itemPaths.get(0); - for (let i = 0; i < len; ++i) { - const elem = paths[i]; - const ty = elem[0]; - const name = elem[1]; - let path = null; - if (elem.length > 2) { - path = itemPaths.has(elem[2]) ? itemPaths.get(elem[2]) : lastPath; - lastPath = path; - } - const exactPath = elem.length > 3 ? itemPaths.get(elem[3]) : path; +/** + * Return the filtering crate or `null` if there is none. + * + * @return {string|null} + */ +function getFilterCrates() { + const elem = document.getElementById("crate-search"); + + if (elem && + elem.value !== "all crates" && + window.searchIndex.has(elem.value) + ) { + return elem.value; + } + return null; +} - lowercasePaths.push({ty, name: name.toLowerCase(), path, exactPath}); - paths[i] = {ty, name, path, exactPath}; - } +function nextTab(direction) { + const next = (searchState.currentTab + direction + 3) % searchState.focusedByTab.length; + searchState.focusedByTab[searchState.currentTab] = document.activeElement; + printTab(next); + focusSearchResult(); +} - // convert `item*` into an object form, and construct word indices. - // - // before any analysis is performed lets gather the search terms to - // search against apart from the rest of the data. This is a quick - // operation that is cached for the life of the page state so that - // all other search operations have access to this cached data for - // faster analysis operations - lastPath = ""; - len = itemTypes.length; - let lastName = ""; - let lastWord = ""; - for (let i = 0; i < len; ++i) { - const bitIndex = i + 1; - if (descIndex >= descShard.len && - !searchIndexEmptyDesc.get(crate).contains(bitIndex)) { - descShard = { - crate, - shard: descShard.shard + 1, - start: descShard.start + descShard.len, - len: itemDescShardDecoder.next(), - promise: null, - resolve: null, - }; - descIndex = 0; - descShardList.push(descShard); - } - const name = itemNames[i] === "" ? lastName : itemNames[i]; - const word = itemNames[i] === "" ? lastWord : itemNames[i].toLowerCase(); - const path = itemPaths.has(i) ? itemPaths.get(i) : lastPath; - const type = itemFunctionDecoder.next(); - if (type !== null) { - if (type) { - const fp = functionTypeFingerprint.subarray(id * 4, (id + 1) * 4); - const fps = new Set(); - for (const t of type.inputs) { - buildFunctionTypeFingerprint(t, fp, fps); - } - for (const t of type.output) { - buildFunctionTypeFingerprint(t, fp, fps); - } - for (const w of type.where_clause) { - for (const t of w) { - buildFunctionTypeFingerprint(t, fp, fps); - } - } - } - } - // This object should have exactly the same set of fields as the "crateRow" - // object defined above. - const itemParentIdx = itemParentIdxDecoder.next(); - const row = { - crate, - ty: itemTypes.charCodeAt(i) - 65, // 65 = "A" - name, - path, - descShard, - descIndex, - exactPath: itemReexports.has(i) ? itemPaths.get(itemReexports.get(i)) : path, - parent: itemParentIdx > 0 ? paths[itemParentIdx - 1] : undefined, - type, - id, - word, - normalizedName: word.indexOf("_") === -1 ? word : word.replace(/_/g, ""), - bitIndex, - implDisambiguator: implDisambiguator.has(i) ? implDisambiguator.get(i) : null, - }; - id += 1; - searchIndex.push(row); - lastPath = row.path; - if (!searchIndexEmptyDesc.get(crate).contains(bitIndex)) { - descIndex += 1; - } - lastName = name; - lastWord = word; +// Focus the first search result on the active tab, or the result that +// was focused last time this tab was active. +function focusSearchResult() { + const target = searchState.focusedByTab[searchState.currentTab] || + document.querySelectorAll(".search-results.active a").item(0) || + document.querySelectorAll("#search-tabs button").item(searchState.currentTab); + searchState.focusedByTab[searchState.currentTab] = null; + if (target) { + target.focus(); + } +} + +/** + * Render a set of search results for a single tab. + * @param {Array} array - The search results for this tab + * @param {ParsedQuery} query + * @param {boolean} display - True if this is the active tab + */ +async function addTab(array, query, display) { + const extraClass = display ? " active" : ""; + + const output = document.createElement("div"); + if (array.length > 0) { + output.className = "search-results " + extraClass; + + for (const item of array) { + const name = item.name; + const type = itemTypes[item.ty]; + const longType = longItemTypes[item.ty]; + const typeName = longType.length !== 0 ? `${longType}` : "?"; + + const link = document.createElement("a"); + link.className = "result-" + type; + link.href = item.href; + + const resultName = document.createElement("div"); + resultName.className = "result-name"; + + resultName.insertAdjacentHTML( + "beforeend", + `${typeName}`); + link.appendChild(resultName); + + let alias = " "; + if (item.is_alias) { + alias = `
\ +${item.alias} - see \ +
`; } + resultName.insertAdjacentHTML( + "beforeend", + `
${alias}\ +${item.displayPath}${name}\ +
`); - if (aliases) { - const currentCrateAliases = new Map(); - ALIASES.set(crate, currentCrateAliases); - for (const alias_name in aliases) { - if (!Object.prototype.hasOwnProperty.call(aliases, alias_name)) { - continue; - } + const description = document.createElement("div"); + description.className = "desc"; + description.insertAdjacentHTML("beforeend", item.desc); + + link.appendChild(description); + output.appendChild(link); + } + } else if (query.error === null) { + output.className = "search-failed" + extraClass; + output.innerHTML = "No results :(
" + + "Try on DuckDuckGo?

" + + "Or try looking in one of these:"; + } + return [output, array.length]; +} - let currentNameAliases; - if (currentCrateAliases.has(alias_name)) { - currentNameAliases = currentCrateAliases.get(alias_name); - } else { - currentNameAliases = []; - currentCrateAliases.set(alias_name, currentNameAliases); - } - for (const local_alias of aliases[alias_name]) { - currentNameAliases.push(local_alias + currentIndex); - } - } - } - currentIndex += itemTypes.length; - searchState.descShards.set(crate, descShardList); - } - // Drop the (rather large) hash table used for reusing function items - TYPES_POOL = new Map(); +function makeTabHeader(tabNb, text, nbElems) { + // https://blog.horizon-eda.org/misc/2020/02/19/ui.html + // + // CSS runs with `font-variant-numeric: tabular-nums` to ensure all + // digits are the same width. \u{2007} is a Unicode space character + // that is defined to be the same width as a digit. + const fmtNbElems = + nbElems < 10 ? `\u{2007}(${nbElems})\u{2007}\u{2007}` : + nbElems < 100 ? `\u{2007}(${nbElems})\u{2007}` : `\u{2007}(${nbElems})`; + if (searchState.currentTab === tabNb) { + return ""; } + return ""; +} - /** - * Callback for when the search form is submitted. - * @param {Event} [e] - The event that triggered this call, if any - */ - function onSearchSubmit(e) { - e.preventDefault(); - searchState.clearInputTimeout(); - search(); +/** + * @param {ResultsTable} results + * @param {boolean} go_to_first + * @param {string} filterCrates + */ +async function showResults(results, go_to_first, filterCrates) { + const search = searchState.outputElement(); + if (go_to_first || (results.others.length === 1 + && getSettingValue("go-to-only-result") === "true") + ) { + // Needed to force re-execution of JS when coming back to a page. Let's take this + // scenario as example: + // + // 1. You have the "Directly go to item in search if there is only one result" option + // enabled. + // 2. You make a search which results only one result, leading you automatically to + // this result. + // 3. You go back to previous page. + // + // Now, without the call below, the JS will not be re-executed and the previous state + // will be used, starting search again since the search input is not empty, leading you + // back to the previous page again. + window.onunload = () => { }; + searchState.removeQueryParameters(); + const elem = document.createElement("a"); + elem.href = results.others[0].href; + removeClass(elem, "active"); + // For firefox, we need the element to be in the DOM so it can be clicked. + document.body.appendChild(elem); + elem.click(); + return; + } + if (results.query === undefined) { + results.query = DocSearch.parseQuery(searchState.input.value); } - function putBackSearch() { - const search_input = searchState.input; - if (!searchState.input) { - return; - } - if (search_input.value !== "" && !searchState.isDisplayed()) { - searchState.showResults(); - if (browserSupportsHistoryApi()) { - history.replaceState(null, "", - buildUrl(search_input.value, getFilterCrates())); - } - document.title = searchState.title; + currentResults = results.query.userQuery; + + const [ret_others, ret_in_args, ret_returned] = await Promise.all([ + addTab(results.others, results.query, true), + addTab(results.in_args, results.query, false), + addTab(results.returned, results.query, false), + ]); + + // Navigate to the relevant tab if the current tab is empty, like in case users search + // for "-> String". If they had selected another tab previously, they have to click on + // it again. + let currentTab = searchState.currentTab; + if ((currentTab === 0 && ret_others[1] === 0) || + (currentTab === 1 && ret_in_args[1] === 0) || + (currentTab === 2 && ret_returned[1] === 0)) { + if (ret_others[1] !== 0) { + currentTab = 0; + } else if (ret_in_args[1] !== 0) { + currentTab = 1; + } else if (ret_returned[1] !== 0) { + currentTab = 2; } } - function registerSearchEvents() { - const params = searchState.getQueryStringParams(); - - // Populate search bar with query string search term when provided, - // but only if the input bar is empty. This avoid the obnoxious issue - // where you start trying to do a search, and the index loads, and - // suddenly your search is gone! - if (searchState.input.value === "") { - searchState.input.value = params.search || ""; + let crates = ""; + if (rawSearchIndex.size > 1) { + crates = " in 
"; + } - const searchAfter500ms = () => { - searchState.clearInputTimeout(); - if (searchState.input.value.length === 0) { - searchState.hideResults(); + let output = `

Results${crates}

`; + if (results.query.error !== null) { + const error = results.query.error; + error.forEach((value, index) => { + value = value.split("<").join("<").split(">").join(">"); + if (index % 2 !== 0) { + error[index] = `${value.replaceAll(" ", " ")}`; } else { - searchState.timeout = setTimeout(search, 500); - } - }; - searchState.input.onkeyup = searchAfter500ms; - searchState.input.oninput = searchAfter500ms; - document.getElementsByClassName("search-form")[0].onsubmit = onSearchSubmit; - searchState.input.onchange = e => { - if (e.target !== document.activeElement) { - // To prevent doing anything when it's from a blur event. - return; - } - // Do NOT e.preventDefault() here. It will prevent pasting. - searchState.clearInputTimeout(); - // zero-timeout necessary here because at the time of event handler execution the - // pasted content is not in the input field yet. Shouldn’t make any difference for - // change, though. - setTimeout(search, 0); - }; - searchState.input.onpaste = searchState.input.onchange; - - searchState.outputElement().addEventListener("keydown", e => { - // We only handle unmodified keystrokes here. We don't want to interfere with, - // for instance, alt-left and alt-right for history navigation. - if (e.altKey || e.ctrlKey || e.shiftKey || e.metaKey) { - return; - } - // up and down arrow select next/previous search result, or the - // search box if we're already at the top. - if (e.which === 38) { // up - const previous = document.activeElement.previousElementSibling; - if (previous) { - previous.focus(); - } else { - searchState.focus(); - } - e.preventDefault(); - } else if (e.which === 40) { // down - const next = document.activeElement.nextElementSibling; - if (next) { - next.focus(); - } - const rect = document.activeElement.getBoundingClientRect(); - if (window.innerHeight - rect.bottom < rect.height) { - window.scrollBy(0, rect.height); - } - e.preventDefault(); - } else if (e.which === 37) { // left - nextTab(-1); - e.preventDefault(); - } else if (e.which === 39) { // right - nextTab(1); - e.preventDefault(); + error[index] = value; } }); + output += `

Query parser error: "${error.join("")}".

`; + output += "
" + + makeTabHeader(0, "In Names", ret_others[1]) + + "
"; + currentTab = 0; + } else if (results.query.foundElems <= 1 && results.query.returned.length === 0) { + output += "
" + + makeTabHeader(0, "In Names", ret_others[1]) + + makeTabHeader(1, "In Parameters", ret_in_args[1]) + + makeTabHeader(2, "In Return Types", ret_returned[1]) + + "
"; + } else { + const signatureTabTitle = + results.query.elems.length === 0 ? "In Function Return Types" : + results.query.returned.length === 0 ? "In Function Parameters" : + "In Function Signatures"; + output += "
" + + makeTabHeader(0, signatureTabTitle, ret_others[1]) + + "
"; + currentTab = 0; + } - searchState.input.addEventListener("keydown", e => { - if (e.which === 40) { // down - focusSearchResult(); - e.preventDefault(); - } - }); + if (results.query.correction !== null) { + const orig = results.query.returned.length > 0 + ? results.query.returned[0].name + : results.query.elems[0].name; + output += "

" + + `Type "${orig}" not found. ` + + "Showing results for closest type name " + + `"${results.query.correction}" instead.

`; + } + if (results.query.proposeCorrectionFrom !== null) { + const orig = results.query.proposeCorrectionFrom; + const targ = results.query.proposeCorrectionTo; + output += "

" + + `Type "${orig}" not found and used as generic parameter. ` + + `Consider searching for "${targ}" instead.

`; + } + + const resultsElem = document.createElement("div"); + resultsElem.id = "results"; + resultsElem.appendChild(ret_others[0]); + resultsElem.appendChild(ret_in_args[0]); + resultsElem.appendChild(ret_returned[0]); + + search.innerHTML = output; + const crateSearch = document.getElementById("crate-search"); + if (crateSearch) { + crateSearch.addEventListener("input", updateCrate); + } + search.appendChild(resultsElem); + // Reset focused elements. + searchState.showResults(search); + const elems = document.getElementById("search-tabs").childNodes; + searchState.focusedByTab = []; + let i = 0; + for (const elem of elems) { + const j = i; + elem.onclick = () => printTab(j); + searchState.focusedByTab.push(null); + i += 1; + } + printTab(currentTab); +} + +function updateSearchHistory(url) { + if (!browserSupportsHistoryApi()) { + return; + } + const params = searchState.getQueryStringParams(); + if (!history.state && !params.search) { + history.pushState(null, "", url); + } else { + history.replaceState(null, "", url); + } +} - searchState.input.addEventListener("focus", () => { +/** + * Perform a search based on the current state of the search input element + * and display the results. + * @param {boolean} [forced] + */ +async function search(forced) { + const query = DocSearch.parseQuery(searchState.input.value.trim()); + let filterCrates = getFilterCrates(); + + if (!forced && query.userQuery === currentResults) { + if (query.userQuery.length > 0) { putBackSearch(); - }); + } + return; + } - searchState.input.addEventListener("blur", () => { - searchState.input.placeholder = searchState.input.origPlaceholder; - }); + searchState.setLoadingSearch(); + + const params = searchState.getQueryStringParams(); + + // In case we have no information about the saved crate and there is a URL query parameter, + // we override it with the URL query parameter. + if (filterCrates === null && params["filter-crate"] !== undefined) { + filterCrates = params["filter-crate"]; + } + + // Update document title to maintain a meaningful browser history + searchState.title = "\"" + query.original + "\" Search - Rust"; + + // Because searching is incremental by character, only the most + // recent search query is added to the browser history. + updateSearchHistory(buildUrl(query.original, filterCrates)); + + await showResults( + await docSearch.execQuery(query, filterCrates, window.currentCrate), + params.go_to_first, + filterCrates); +} + +/** + * Callback for when the search form is submitted. + * @param {Event} [e] - The event that triggered this call, if any + */ +function onSearchSubmit(e) { + e.preventDefault(); + searchState.clearInputTimeout(); + search(); +} - // Push and pop states are used to add search results to the browser - // history. +function putBackSearch() { + const search_input = searchState.input; + if (!searchState.input) { + return; + } + if (search_input.value !== "" && !searchState.isDisplayed()) { + searchState.showResults(); if (browserSupportsHistoryApi()) { - // Store the previous so we can revert back to it later. - const previousTitle = document.title; - - window.addEventListener("popstate", e => { - const params = searchState.getQueryStringParams(); - // Revert to the previous title manually since the History - // API ignores the title parameter. - document.title = previousTitle; - // When browsing forward to search results the previous - // search will be repeated, so the currentResults are - // cleared to ensure the search is successful. - currentResults = null; - // Synchronize search bar with query string state and - // perform the search. This will empty the bar if there's - // nothing there, which lets you really go back to a - // previous state with nothing in the bar. - if (params.search && params.search.length > 0) { - searchState.input.value = params.search; - // Some browsers fire "onpopstate" for every page load - // (Chrome), while others fire the event only when actually - // popping a state (Firefox), which is why search() is - // called both here and at the end of the startSearch() - // function. - e.preventDefault(); - search(); - } else { - searchState.input.value = ""; - // When browsing back from search results the main page - // visibility must be reset. - searchState.hideResults(); - } - }); + history.replaceState(null, "", + buildUrl(search_input.value, getFilterCrates())); + } + document.title = searchState.title; + } +} + +function registerSearchEvents() { + const params = searchState.getQueryStringParams(); + + // Populate search bar with query string search term when provided, + // but only if the input bar is empty. This avoid the obnoxious issue + // where you start trying to do a search, and the index loads, and + // suddenly your search is gone! + if (searchState.input.value === "") { + searchState.input.value = params.search || ""; + } + + const searchAfter500ms = () => { + searchState.clearInputTimeout(); + if (searchState.input.value.length === 0) { + searchState.hideResults(); + } else { + searchState.timeout = setTimeout(search, 500); + } + }; + searchState.input.onkeyup = searchAfter500ms; + searchState.input.oninput = searchAfter500ms; + document.getElementsByClassName("search-form")[0].onsubmit = onSearchSubmit; + searchState.input.onchange = e => { + if (e.target !== document.activeElement) { + // To prevent doing anything when it's from a blur event. + return; + } + // Do NOT e.preventDefault() here. It will prevent pasting. + searchState.clearInputTimeout(); + // zero-timeout necessary here because at the time of event handler execution the + // pasted content is not in the input field yet. Shouldn’t make any difference for + // change, though. + setTimeout(search, 0); + }; + searchState.input.onpaste = searchState.input.onchange; + + searchState.outputElement().addEventListener("keydown", e => { + // We only handle unmodified keystrokes here. We don't want to interfere with, + // for instance, alt-left and alt-right for history navigation. + if (e.altKey || e.ctrlKey || e.shiftKey || e.metaKey) { + return; + } + // up and down arrow select next/previous search result, or the + // search box if we're already at the top. + if (e.which === 38) { // up + const previous = document.activeElement.previousElementSibling; + if (previous) { + previous.focus(); + } else { + searchState.focus(); + } + e.preventDefault(); + } else if (e.which === 40) { // down + const next = document.activeElement.nextElementSibling; + if (next) { + next.focus(); + } + const rect = document.activeElement.getBoundingClientRect(); + if (window.innerHeight - rect.bottom < rect.height) { + window.scrollBy(0, rect.height); + } + e.preventDefault(); + } else if (e.which === 37) { // left + nextTab(-1); + e.preventDefault(); + } else if (e.which === 39) { // right + nextTab(1); + e.preventDefault(); + } + }); + + searchState.input.addEventListener("keydown", e => { + if (e.which === 40) { // down + focusSearchResult(); + e.preventDefault(); } + }); + + searchState.input.addEventListener("focus", () => { + putBackSearch(); + }); + + searchState.input.addEventListener("blur", () => { + searchState.input.placeholder = searchState.input.origPlaceholder; + }); - // This is required in firefox to avoid this problem: Navigating to a search result - // with the keyboard, hitting enter, and then hitting back would take you back to - // the doc page, rather than the search that should overlay it. - // This was an interaction between the back-forward cache and our handlers - // that try to sync state between the URL and the search input. To work around it, - // do a small amount of re-init on page show. - window.onpageshow = () => { - const qSearch = searchState.getQueryStringParams().search; - if (searchState.input.value === "" && qSearch) { - searchState.input.value = qSearch; + // Push and pop states are used to add search results to the browser + // history. + if (browserSupportsHistoryApi()) { + // Store the previous <title> so we can revert back to it later. + const previousTitle = document.title; + + window.addEventListener("popstate", e => { + const params = searchState.getQueryStringParams(); + // Revert to the previous title manually since the History + // API ignores the title parameter. + document.title = previousTitle; + // When browsing forward to search results the previous + // search will be repeated, so the currentResults are + // cleared to ensure the search is successful. + currentResults = null; + // Synchronize search bar with query string state and + // perform the search. This will empty the bar if there's + // nothing there, which lets you really go back to a + // previous state with nothing in the bar. + if (params.search && params.search.length > 0) { + searchState.input.value = params.search; + // Some browsers fire "onpopstate" for every page load + // (Chrome), while others fire the event only when actually + // popping a state (Firefox), which is why search() is + // called both here and at the end of the startSearch() + // function. + e.preventDefault(); + search(); + } else { + searchState.input.value = ""; + // When browsing back from search results the main page + // visibility must be reset. + searchState.hideResults(); } - search(); - }; + }); } - function updateCrate(ev) { - if (ev.target.value === "all crates") { - // If we don't remove it from the URL, it'll be picked up again by the search. - const query = searchState.input.value.trim(); - updateSearchHistory(buildUrl(query, null)); + // This is required in firefox to avoid this problem: Navigating to a search result + // with the keyboard, hitting enter, and then hitting back would take you back to + // the doc page, rather than the search that should overlay it. + // This was an interaction between the back-forward cache and our handlers + // that try to sync state between the URL and the search input. To work around it, + // do a small amount of re-init on page show. + window.onpageshow = () => { + const qSearch = searchState.getQueryStringParams().search; + if (searchState.input.value === "" && qSearch) { + searchState.input.value = qSearch; } - // In case you "cut" the entry from the search input, then change the crate filter - // before paste back the previous search, you get the old search results without - // the filter. To prevent this, we need to remove the previous results. - currentResults = null; - search(true); + search(); + }; +} + +function updateCrate(ev) { + if (ev.target.value === "all crates") { + // If we don't remove it from the URL, it'll be picked up again by the search. + const query = searchState.input.value.trim(); + updateSearchHistory(buildUrl(query, null)); } + // In case you "cut" the entry from the search input, then change the crate filter + // before paste back the previous search, you get the old search results without + // the filter. To prevent this, we need to remove the previous results. + currentResults = null; + search(true); +} - buildIndex(rawSearchIndex); +function initSearch(searchIndx) { + rawSearchIndex = searchIndx; if (typeof window !== "undefined") { + docSearch = new DocSearch(rawSearchIndex, ROOT_PATH, searchState); registerSearchEvents(); // If there's a search term in the URL, execute the search now. if (window.searchState.getQueryStringParams().search) { search(); } + } else if (typeof exports !== "undefined") { + docSearch = new DocSearch(rawSearchIndex, ROOT_PATH, searchState); + exports.docSearch = docSearch; + exports.parseQuery = DocSearch.parseQuery; } +} - if (typeof exports !== "undefined") { - exports.initSearch = initSearch; - exports.execQuery = execQuery; - exports.parseQuery = parseQuery; - } +if (typeof exports !== "undefined") { + exports.initSearch = initSearch; } if (typeof window !== "undefined") { @@ -3897,6 +3938,4 @@ if (typeof window !== "undefined") { // exports. initSearch(new Map()); } - - })(); diff --git a/src/tools/rustdoc-js/tester.js b/src/tools/rustdoc-js/tester.js index 43a22f358c31f..e162ba033cc55 100644 --- a/src/tools/rustdoc-js/tester.js +++ b/src/tools/rustdoc-js/tester.js @@ -427,7 +427,6 @@ function loadSearchJS(doc_folder, resource_suffix) { return list[descIndex]; }, loadedDescShard: function(crate, shard, data) { - //console.log(this.descShards); this.descShards.get(crate)[shard].resolve(data.split("\n")); }, }; @@ -436,15 +435,15 @@ function loadSearchJS(doc_folder, resource_suffix) { const searchJs = fs.readdirSync(staticFiles).find(f => f.match(/search.*\.js$/)); const searchModule = require(path.join(staticFiles, searchJs)); searchModule.initSearch(searchIndex.searchIndex); - + const docSearch = searchModule.docSearch; return { doSearch: function(queryStr, filterCrate, currentCrate) { - return searchModule.execQuery(searchModule.parseQuery(queryStr), + return docSearch.execQuery(searchModule.parseQuery(queryStr), filterCrate, currentCrate); }, getCorrections: function(queryStr, filterCrate, currentCrate) { const parsedQuery = searchModule.parseQuery(queryStr); - searchModule.execQuery(parsedQuery, filterCrate, currentCrate); + docSearch.execQuery(parsedQuery, filterCrate, currentCrate); return parsedQuery.correction; }, parseQuery: searchModule.parseQuery, From e20a888f8cb91d36634942fe5c1d6d38167d8fa3 Mon Sep 17 00:00:00 2001 From: Lukas Wirth <lukastw97@gmail.com> Date: Thu, 29 Aug 2024 09:24:53 +0200 Subject: [PATCH 12/13] Allow running `./x.py test compiler` --- src/bootstrap/src/core/build_steps/test.rs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index cc01afd4c18c6..84a6b26a491ed 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -14,9 +14,8 @@ use crate::core::build_steps::synthetic_targets::MirOptPanicAbortSyntheticTarget use crate::core::build_steps::tool::{self, SourceType, Tool}; use crate::core::build_steps::toolstate::ToolState; use crate::core::build_steps::{compile, dist, llvm}; -use crate::core::builder; use crate::core::builder::{ - crate_description, Builder, Compiler, Kind, RunConfig, ShouldRun, Step, + self, crate_description, Alias, Builder, Compiler, Kind, RunConfig, ShouldRun, Step, }; use crate::core::config::flags::{get_completion, Subcommand}; use crate::core::config::TargetSelection; @@ -2435,18 +2434,14 @@ impl Step for CrateLibrustc { const ONLY_HOSTS: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - run.crate_or_deps("rustc-main") + run.crate_or_deps("rustc-main").path("compiler") } fn make_run(run: RunConfig<'_>) { let builder = run.builder; let host = run.build_triple(); let compiler = builder.compiler_for(builder.top_stage, host, host); - let crates = run - .paths - .iter() - .map(|p| builder.crate_paths[&p.assert_single_path().path].clone()) - .collect(); + let crates = run.make_run_crates(Alias::Compiler); builder.ensure(CrateLibrustc { compiler, target: run.target, crates }); } From c824c1ada7278ac508143b21dc23aeb1ed7e05a5 Mon Sep 17 00:00:00 2001 From: Alex Crichton <alex@alexcrichton.com> Date: Thu, 29 Aug 2024 10:31:17 -0700 Subject: [PATCH 13/13] wasi: Fix sleeping for `Duration::MAX` This commit fixes an assert in the WASI-specific implementation of thread sleep to ensure that sleeping for a very large period of time blocks instead of panicking. This can come up when testing programs that sleep "forever", for example. --- library/std/src/sys/pal/wasi/thread.rs | 61 +++++++++++++------------- 1 file changed, 31 insertions(+), 30 deletions(-) diff --git a/library/std/src/sys/pal/wasi/thread.rs b/library/std/src/sys/pal/wasi/thread.rs index 31c9cbd4699bd..4b83870fdea6c 100644 --- a/library/std/src/sys/pal/wasi/thread.rs +++ b/library/std/src/sys/pal/wasi/thread.rs @@ -136,36 +136,37 @@ impl Thread { } pub fn sleep(dur: Duration) { - let nanos = dur.as_nanos(); - assert!(nanos <= u64::MAX as u128); - - const USERDATA: wasi::Userdata = 0x0123_45678; - - let clock = wasi::SubscriptionClock { - id: wasi::CLOCKID_MONOTONIC, - timeout: nanos as u64, - precision: 0, - flags: 0, - }; - - let in_ = wasi::Subscription { - userdata: USERDATA, - u: wasi::SubscriptionU { tag: 0, u: wasi::SubscriptionUU { clock } }, - }; - unsafe { - let mut event: wasi::Event = mem::zeroed(); - let res = wasi::poll_oneoff(&in_, &mut event, 1); - match (res, event) { - ( - Ok(1), - wasi::Event { - userdata: USERDATA, - error: wasi::ERRNO_SUCCESS, - type_: wasi::EVENTTYPE_CLOCK, - .. - }, - ) => {} - _ => panic!("thread::sleep(): unexpected result of poll_oneoff"), + let mut nanos = dur.as_nanos(); + while nanos > 0 { + const USERDATA: wasi::Userdata = 0x0123_45678; + + let clock = wasi::SubscriptionClock { + id: wasi::CLOCKID_MONOTONIC, + timeout: u64::try_from(nanos).unwrap_or(u64::MAX), + precision: 0, + flags: 0, + }; + nanos -= u128::from(clock.timeout); + + let in_ = wasi::Subscription { + userdata: USERDATA, + u: wasi::SubscriptionU { tag: 0, u: wasi::SubscriptionUU { clock } }, + }; + unsafe { + let mut event: wasi::Event = mem::zeroed(); + let res = wasi::poll_oneoff(&in_, &mut event, 1); + match (res, event) { + ( + Ok(1), + wasi::Event { + userdata: USERDATA, + error: wasi::ERRNO_SUCCESS, + type_: wasi::EVENTTYPE_CLOCK, + .. + }, + ) => {} + _ => panic!("thread::sleep(): unexpected result of poll_oneoff"), + } } } }