Auto merge of #151816 - Zalathar:rollup-z5YytdB, r=Zalathar
Rollup of 5 pull requests Successful merges: - rust-lang/rust#151775 (Portable SIMD subtree update) - rust-lang/rust#151488 (Tweak E0599 to consolidate unsatisfied trait bound messages) - rust-lang/rust#149823 (fix(parser): Disallow CR in frontmatter ) - rust-lang/rust#151475 (add foregin type tests for issue 64458) - rust-lang/rust#151657 (Cleanup of `#[derive(Diagnostic)]` attribute parsers)
This commit is contained in:
commit
370143facf
60 changed files with 1562 additions and 1718 deletions
|
|
@ -13,7 +13,9 @@ use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
|
||||||
use rustc_data_structures::sorted_map::SortedMap;
|
use rustc_data_structures::sorted_map::SortedMap;
|
||||||
use rustc_data_structures::unord::UnordSet;
|
use rustc_data_structures::unord::UnordSet;
|
||||||
use rustc_errors::codes::*;
|
use rustc_errors::codes::*;
|
||||||
use rustc_errors::{Applicability, Diag, MultiSpan, StashKey, pluralize, struct_span_code_err};
|
use rustc_errors::{
|
||||||
|
Applicability, Diag, MultiSpan, StashKey, listify, pluralize, struct_span_code_err,
|
||||||
|
};
|
||||||
use rustc_hir::attrs::AttributeKind;
|
use rustc_hir::attrs::AttributeKind;
|
||||||
use rustc_hir::def::{CtorKind, DefKind, Res};
|
use rustc_hir::def::{CtorKind, DefKind, Res};
|
||||||
use rustc_hir::def_id::DefId;
|
use rustc_hir::def_id::DefId;
|
||||||
|
|
@ -50,6 +52,51 @@ use crate::errors::{self, CandidateTraitNote, NoAssociatedItem};
|
||||||
use crate::method::probe::UnsatisfiedPredicates;
|
use crate::method::probe::UnsatisfiedPredicates;
|
||||||
use crate::{Expectation, FnCtxt};
|
use crate::{Expectation, FnCtxt};
|
||||||
|
|
||||||
|
/// Tracks trait bounds and detects duplicates between ref and non-ref versions of self types.
|
||||||
|
/// This is used to condense error messages when the same trait bound appears for both
|
||||||
|
/// `T` and `&T` (or `&mut T`).
|
||||||
|
struct TraitBoundDuplicateTracker {
|
||||||
|
trait_def_ids: FxIndexSet<DefId>,
|
||||||
|
seen_ref: FxIndexSet<DefId>,
|
||||||
|
seen_non_ref: FxIndexSet<DefId>,
|
||||||
|
has_ref_dupes: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TraitBoundDuplicateTracker {
|
||||||
|
fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
trait_def_ids: FxIndexSet::default(),
|
||||||
|
seen_ref: FxIndexSet::default(),
|
||||||
|
seen_non_ref: FxIndexSet::default(),
|
||||||
|
has_ref_dupes: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Track a trait bound. `is_ref` indicates whether the self type is a reference.
|
||||||
|
fn track(&mut self, def_id: DefId, is_ref: bool) {
|
||||||
|
self.trait_def_ids.insert(def_id);
|
||||||
|
if is_ref {
|
||||||
|
if self.seen_non_ref.contains(&def_id) {
|
||||||
|
self.has_ref_dupes = true;
|
||||||
|
}
|
||||||
|
self.seen_ref.insert(def_id);
|
||||||
|
} else {
|
||||||
|
if self.seen_ref.contains(&def_id) {
|
||||||
|
self.has_ref_dupes = true;
|
||||||
|
}
|
||||||
|
self.seen_non_ref.insert(def_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn has_ref_dupes(&self) -> bool {
|
||||||
|
self.has_ref_dupes
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_trait_def_ids(self) -> FxIndexSet<DefId> {
|
||||||
|
self.trait_def_ids
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
fn is_slice_ty(&self, ty: Ty<'tcx>, span: Span) -> bool {
|
fn is_slice_ty(&self, ty: Ty<'tcx>, span: Span) -> bool {
|
||||||
self.autoderef(span, ty)
|
self.autoderef(span, ty)
|
||||||
|
|
@ -1004,6 +1051,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
item_ident: Ident,
|
item_ident: Ident,
|
||||||
item_kind: &str,
|
item_kind: &str,
|
||||||
bound_spans: SortedMap<Span, Vec<String>>,
|
bound_spans: SortedMap<Span, Vec<String>>,
|
||||||
|
unsatisfied_predicates: &UnsatisfiedPredicates<'tcx>,
|
||||||
) {
|
) {
|
||||||
let mut ty_span = match rcvr_ty.kind() {
|
let mut ty_span = match rcvr_ty.kind() {
|
||||||
ty::Param(param_type) => {
|
ty::Param(param_type) => {
|
||||||
|
|
@ -1012,13 +1060,61 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
ty::Adt(def, _) if def.did().is_local() => Some(self.tcx.def_span(def.did())),
|
ty::Adt(def, _) if def.did().is_local() => Some(self.tcx.def_span(def.did())),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
|
let rcvr_ty_str = self.tcx.short_string(rcvr_ty, err.long_ty_path());
|
||||||
|
let mut tracker = TraitBoundDuplicateTracker::new();
|
||||||
|
for (predicate, _parent_pred, _cause) in unsatisfied_predicates {
|
||||||
|
if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) =
|
||||||
|
predicate.kind().skip_binder()
|
||||||
|
&& let self_ty = pred.trait_ref.self_ty()
|
||||||
|
&& self_ty.peel_refs() == rcvr_ty
|
||||||
|
{
|
||||||
|
let is_ref = matches!(self_ty.kind(), ty::Ref(..));
|
||||||
|
tracker.track(pred.trait_ref.def_id, is_ref);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let has_ref_dupes = tracker.has_ref_dupes();
|
||||||
|
let mut missing_trait_names = tracker
|
||||||
|
.into_trait_def_ids()
|
||||||
|
.into_iter()
|
||||||
|
.map(|def_id| format!("`{}`", self.tcx.def_path_str(def_id)))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
missing_trait_names.sort();
|
||||||
|
let should_condense =
|
||||||
|
has_ref_dupes && missing_trait_names.len() > 1 && matches!(rcvr_ty.kind(), ty::Adt(..));
|
||||||
|
let missing_trait_list = if should_condense {
|
||||||
|
Some(match missing_trait_names.as_slice() {
|
||||||
|
[only] => only.clone(),
|
||||||
|
[first, second] => format!("{first} or {second}"),
|
||||||
|
[rest @ .., last] => format!("{} or {last}", rest.join(", ")),
|
||||||
|
[] => String::new(),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
for (span, mut bounds) in bound_spans {
|
for (span, mut bounds) in bound_spans {
|
||||||
if !self.tcx.sess.source_map().is_span_accessible(span) {
|
if !self.tcx.sess.source_map().is_span_accessible(span) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
bounds.sort();
|
bounds.sort();
|
||||||
bounds.dedup();
|
bounds.dedup();
|
||||||
let pre = if Some(span) == ty_span {
|
let is_ty_span = Some(span) == ty_span;
|
||||||
|
if is_ty_span && should_condense {
|
||||||
|
ty_span.take();
|
||||||
|
let label = if let Some(missing_trait_list) = &missing_trait_list {
|
||||||
|
format!(
|
||||||
|
"{item_kind} `{item_ident}` not found for this {} because `{rcvr_ty_str}` doesn't implement {missing_trait_list}",
|
||||||
|
rcvr_ty.prefix_string(self.tcx)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
format!(
|
||||||
|
"{item_kind} `{item_ident}` not found for this {}",
|
||||||
|
rcvr_ty.prefix_string(self.tcx)
|
||||||
|
)
|
||||||
|
};
|
||||||
|
err.span_label(span, label);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let pre = if is_ty_span {
|
||||||
ty_span.take();
|
ty_span.take();
|
||||||
format!(
|
format!(
|
||||||
"{item_kind} `{item_ident}` not found for this {} because it ",
|
"{item_kind} `{item_ident}` not found for this {} because it ",
|
||||||
|
|
@ -1248,6 +1344,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
item_ident,
|
item_ident,
|
||||||
item_kind,
|
item_kind,
|
||||||
bound_spans,
|
bound_spans,
|
||||||
|
unsatisfied_predicates,
|
||||||
);
|
);
|
||||||
|
|
||||||
self.note_derefed_ty_has_method(&mut err, source, rcvr_ty, item_ident, expected);
|
self.note_derefed_ty_has_method(&mut err, source, rcvr_ty, item_ident, expected);
|
||||||
|
|
@ -1507,6 +1604,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
bound_spans: &mut SortedMap<Span, Vec<String>>,
|
bound_spans: &mut SortedMap<Span, Vec<String>>,
|
||||||
) {
|
) {
|
||||||
let tcx = self.tcx;
|
let tcx = self.tcx;
|
||||||
|
let rcvr_ty_str = self.tcx.short_string(rcvr_ty, err.long_ty_path());
|
||||||
let mut type_params = FxIndexMap::default();
|
let mut type_params = FxIndexMap::default();
|
||||||
|
|
||||||
// Pick out the list of unimplemented traits on the receiver.
|
// Pick out the list of unimplemented traits on the receiver.
|
||||||
|
|
@ -1798,6 +1896,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
let mut spanned_predicates: Vec<_> = spanned_predicates.into_iter().collect();
|
let mut spanned_predicates: Vec<_> = spanned_predicates.into_iter().collect();
|
||||||
spanned_predicates.sort_by_key(|(span, _)| *span);
|
spanned_predicates.sort_by_key(|(span, _)| *span);
|
||||||
for (_, (primary_spans, span_labels, predicates)) in spanned_predicates {
|
for (_, (primary_spans, span_labels, predicates)) in spanned_predicates {
|
||||||
|
let mut tracker = TraitBoundDuplicateTracker::new();
|
||||||
|
let mut all_trait_bounds_for_rcvr = true;
|
||||||
|
for pred in &predicates {
|
||||||
|
match pred.kind().skip_binder() {
|
||||||
|
ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => {
|
||||||
|
let self_ty = pred.trait_ref.self_ty();
|
||||||
|
if self_ty.peel_refs() != rcvr_ty {
|
||||||
|
all_trait_bounds_for_rcvr = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let is_ref = matches!(self_ty.kind(), ty::Ref(..));
|
||||||
|
tracker.track(pred.trait_ref.def_id, is_ref);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
all_trait_bounds_for_rcvr = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let has_ref_dupes = tracker.has_ref_dupes();
|
||||||
|
let trait_def_ids = tracker.into_trait_def_ids();
|
||||||
let mut preds: Vec<_> = predicates
|
let mut preds: Vec<_> = predicates
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|pred| format_pred(**pred))
|
.filter_map(|pred| format_pred(**pred))
|
||||||
|
|
@ -1805,7 +1924,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
.collect();
|
.collect();
|
||||||
preds.sort();
|
preds.sort();
|
||||||
preds.dedup();
|
preds.dedup();
|
||||||
let msg = if let [pred] = &preds[..] {
|
let availability_note = if all_trait_bounds_for_rcvr
|
||||||
|
&& has_ref_dupes
|
||||||
|
&& trait_def_ids.len() > 1
|
||||||
|
&& matches!(rcvr_ty.kind(), ty::Adt(..))
|
||||||
|
{
|
||||||
|
let mut trait_names = trait_def_ids
|
||||||
|
.into_iter()
|
||||||
|
.map(|def_id| format!("`{}`", tcx.def_path_str(def_id)))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
trait_names.sort();
|
||||||
|
listify(&trait_names, |name| name.to_string()).map(|traits| {
|
||||||
|
format!(
|
||||||
|
"for `{item_ident}` to be available, `{rcvr_ty_str}` must implement {traits}"
|
||||||
|
)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
let msg = if let Some(availability_note) = availability_note {
|
||||||
|
availability_note
|
||||||
|
} else if let [pred] = &preds[..] {
|
||||||
format!("trait bound {pred} was not satisfied")
|
format!("trait bound {pred} was not satisfied")
|
||||||
} else {
|
} else {
|
||||||
format!("the following trait bounds were not satisfied:\n{}", preds.join("\n"),)
|
format!("the following trait bounds were not satisfied:\n{}", preds.join("\n"),)
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
use proc_macro2::{Ident, Span, TokenStream};
|
use proc_macro2::{Ident, Span, TokenStream};
|
||||||
use quote::{format_ident, quote, quote_spanned};
|
use quote::{format_ident, quote, quote_spanned};
|
||||||
|
use syn::parse::ParseStream;
|
||||||
use syn::spanned::Spanned;
|
use syn::spanned::Spanned;
|
||||||
use syn::{Attribute, Meta, Path, Token, Type, parse_quote};
|
use syn::{Attribute, Meta, Path, Token, Type, parse_quote};
|
||||||
use synstructure::{BindingInfo, Structure, VariantInfo};
|
use synstructure::{BindingInfo, Structure, VariantInfo};
|
||||||
|
|
@ -11,7 +12,7 @@ use crate::diagnostics::error::{
|
||||||
DiagnosticDeriveError, span_err, throw_invalid_attr, throw_span_err,
|
DiagnosticDeriveError, span_err, throw_invalid_attr, throw_span_err,
|
||||||
};
|
};
|
||||||
use crate::diagnostics::utils::{
|
use crate::diagnostics::utils::{
|
||||||
FieldInfo, FieldInnerTy, FieldMap, HasFieldMap, SetOnce, SpannedOption, SubdiagnosticKind,
|
FieldInfo, FieldInnerTy, FieldMap, SetOnce, SpannedOption, SubdiagnosticKind,
|
||||||
build_field_mapping, is_doc_comment, report_error_if_not_applied_to_span, report_type_error,
|
build_field_mapping, is_doc_comment, report_error_if_not_applied_to_span, report_type_error,
|
||||||
should_generate_arg, type_is_bool, type_is_unit, type_matches_path,
|
should_generate_arg, type_is_bool, type_is_unit, type_matches_path,
|
||||||
};
|
};
|
||||||
|
|
@ -42,19 +43,13 @@ pub(crate) struct DiagnosticDeriveVariantBuilder {
|
||||||
|
|
||||||
/// Slug is a mandatory part of the struct attribute as corresponds to the Fluent message that
|
/// Slug is a mandatory part of the struct attribute as corresponds to the Fluent message that
|
||||||
/// has the actual diagnostic message.
|
/// has the actual diagnostic message.
|
||||||
pub slug: SpannedOption<Path>,
|
pub slug: Option<Path>,
|
||||||
|
|
||||||
/// Error codes are a optional part of the struct attribute - this is only set to detect
|
/// Error codes are a optional part of the struct attribute - this is only set to detect
|
||||||
/// multiple specifications.
|
/// multiple specifications.
|
||||||
pub code: SpannedOption<()>,
|
pub code: SpannedOption<()>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HasFieldMap for DiagnosticDeriveVariantBuilder {
|
|
||||||
fn get_field_binding(&self, field: &String) -> Option<&TokenStream> {
|
|
||||||
self.field_map.get(field)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DiagnosticDeriveKind {
|
impl DiagnosticDeriveKind {
|
||||||
/// Call `f` for the struct or for each variant of the enum, returning a `TokenStream` with the
|
/// Call `f` for the struct or for each variant of the enum, returning a `TokenStream` with the
|
||||||
/// tokens from `f` wrapped in an `match` expression. Emits errors for use of derive on unions
|
/// tokens from `f` wrapped in an `match` expression. Emits errors for use of derive on unions
|
||||||
|
|
@ -111,7 +106,7 @@ impl DiagnosticDeriveKind {
|
||||||
|
|
||||||
impl DiagnosticDeriveVariantBuilder {
|
impl DiagnosticDeriveVariantBuilder {
|
||||||
pub(crate) fn primary_message(&self) -> Option<&Path> {
|
pub(crate) fn primary_message(&self) -> Option<&Path> {
|
||||||
match self.slug.value_ref() {
|
match self.slug.as_ref() {
|
||||||
None => {
|
None => {
|
||||||
span_err(self.span, "diagnostic slug not specified")
|
span_err(self.span, "diagnostic slug not specified")
|
||||||
.help(
|
.help(
|
||||||
|
|
@ -169,7 +164,7 @@ impl DiagnosticDeriveVariantBuilder {
|
||||||
&self,
|
&self,
|
||||||
attr: &Attribute,
|
attr: &Attribute,
|
||||||
) -> Result<Option<(SubdiagnosticKind, Path, bool)>, DiagnosticDeriveError> {
|
) -> Result<Option<(SubdiagnosticKind, Path, bool)>, DiagnosticDeriveError> {
|
||||||
let Some(subdiag) = SubdiagnosticVariant::from_attr(attr, self)? else {
|
let Some(subdiag) = SubdiagnosticVariant::from_attr(attr, &self.field_map)? else {
|
||||||
// Some attributes aren't errors - like documentation comments - but also aren't
|
// Some attributes aren't errors - like documentation comments - but also aren't
|
||||||
// subdiagnostics.
|
// subdiagnostics.
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
|
|
@ -191,7 +186,7 @@ impl DiagnosticDeriveVariantBuilder {
|
||||||
SubdiagnosticKind::MultipartSuggestion { .. } => unreachable!(),
|
SubdiagnosticKind::MultipartSuggestion { .. } => unreachable!(),
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(Some((subdiag.kind, slug, subdiag.no_span)))
|
Ok(Some((subdiag.kind, slug, false)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Establishes state in the `DiagnosticDeriveBuilder` resulting from the struct
|
/// Establishes state in the `DiagnosticDeriveBuilder` resulting from the struct
|
||||||
|
|
@ -209,47 +204,54 @@ impl DiagnosticDeriveVariantBuilder {
|
||||||
let name = attr.path().segments.last().unwrap().ident.to_string();
|
let name = attr.path().segments.last().unwrap().ident.to_string();
|
||||||
let name = name.as_str();
|
let name = name.as_str();
|
||||||
|
|
||||||
let mut first = true;
|
|
||||||
|
|
||||||
if name == "diag" {
|
if name == "diag" {
|
||||||
let mut tokens = TokenStream::new();
|
let mut tokens = TokenStream::new();
|
||||||
attr.parse_nested_meta(|nested| {
|
attr.parse_args_with(|input: ParseStream<'_>| {
|
||||||
let path = &nested.path;
|
let mut input = &*input;
|
||||||
|
let slug_recovery_point = input.fork();
|
||||||
|
|
||||||
if first && (nested.input.is_empty() || nested.input.peek(Token![,])) {
|
let slug = input.parse::<Path>()?;
|
||||||
self.slug.set_once(path.clone(), path.span().unwrap());
|
if input.is_empty() || input.peek(Token![,]) {
|
||||||
first = false;
|
self.slug = Some(slug);
|
||||||
return Ok(());
|
} else {
|
||||||
|
input = &slug_recovery_point;
|
||||||
}
|
}
|
||||||
|
|
||||||
first = false;
|
while !input.is_empty() {
|
||||||
|
input.parse::<Token![,]>()?;
|
||||||
let Ok(nested) = nested.value() else {
|
// Allow trailing comma
|
||||||
span_err(
|
if input.is_empty() {
|
||||||
nested.input.span().unwrap(),
|
break;
|
||||||
"diagnostic slug must be the first argument",
|
}
|
||||||
)
|
let arg_name: Path = input.parse::<Path>()?;
|
||||||
.emit();
|
if input.peek(Token![,]) {
|
||||||
return Ok(());
|
span_err(
|
||||||
};
|
arg_name.span().unwrap(),
|
||||||
|
"diagnostic slug must be the first argument",
|
||||||
if path.is_ident("code") {
|
)
|
||||||
self.code.set_once((), path.span().unwrap());
|
|
||||||
|
|
||||||
let code = nested.parse::<syn::Expr>()?;
|
|
||||||
tokens.extend(quote! {
|
|
||||||
diag.code(#code);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
span_err(path.span().unwrap(), "unknown argument")
|
|
||||||
.note("only the `code` parameter is valid after the slug")
|
|
||||||
.emit();
|
.emit();
|
||||||
|
continue;
|
||||||
// consume the buffer so we don't have syntax errors from syn
|
}
|
||||||
let _ = nested.parse::<TokenStream>();
|
let arg_name = arg_name.require_ident()?;
|
||||||
|
input.parse::<Token![=]>()?;
|
||||||
|
let arg_value = input.parse::<syn::Expr>()?;
|
||||||
|
match arg_name.to_string().as_str() {
|
||||||
|
"code" => {
|
||||||
|
self.code.set_once((), arg_name.span().unwrap());
|
||||||
|
tokens.extend(quote! {
|
||||||
|
diag.code(#arg_value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
span_err(arg_name.span().unwrap(), "unknown argument")
|
||||||
|
.note("only the `code` parameter is valid after the slug")
|
||||||
|
.emit();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
return Ok(tokens);
|
return Ok(tokens);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
#![deny(unused_must_use)]
|
#![deny(unused_must_use)]
|
||||||
|
|
||||||
use proc_macro2::TokenStream;
|
use proc_macro2::{Ident, TokenStream};
|
||||||
use quote::{format_ident, quote};
|
use quote::{format_ident, quote};
|
||||||
|
use syn::parse::ParseStream;
|
||||||
use syn::spanned::Spanned;
|
use syn::spanned::Spanned;
|
||||||
use syn::{Attribute, Meta, MetaList, Path};
|
use syn::{Attribute, Meta, MetaList, Path, Token};
|
||||||
use synstructure::{BindingInfo, Structure, VariantInfo};
|
use synstructure::{BindingInfo, Structure, VariantInfo};
|
||||||
|
|
||||||
use super::utils::SubdiagnosticVariant;
|
use super::utils::SubdiagnosticVariant;
|
||||||
|
|
@ -11,10 +12,10 @@ use crate::diagnostics::error::{
|
||||||
DiagnosticDeriveError, invalid_attr, span_err, throw_invalid_attr, throw_span_err,
|
DiagnosticDeriveError, invalid_attr, span_err, throw_invalid_attr, throw_span_err,
|
||||||
};
|
};
|
||||||
use crate::diagnostics::utils::{
|
use crate::diagnostics::utils::{
|
||||||
AllowMultipleAlternatives, FieldInfo, FieldInnerTy, FieldMap, HasFieldMap, SetOnce,
|
AllowMultipleAlternatives, FieldInfo, FieldInnerTy, FieldMap, SetOnce, SpannedOption,
|
||||||
SpannedOption, SubdiagnosticKind, build_field_mapping, build_suggestion_code, is_doc_comment,
|
SubdiagnosticKind, build_field_mapping, build_suggestion_code, is_doc_comment, new_code_ident,
|
||||||
new_code_ident, report_error_if_not_applied_to_applicability,
|
report_error_if_not_applied_to_applicability, report_error_if_not_applied_to_span,
|
||||||
report_error_if_not_applied_to_span, should_generate_arg,
|
should_generate_arg,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// The central struct for constructing the `add_to_diag` method from an annotated struct.
|
/// The central struct for constructing the `add_to_diag` method from an annotated struct.
|
||||||
|
|
@ -142,12 +143,6 @@ struct SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
|
||||||
is_enum: bool,
|
is_enum: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'parent, 'a> HasFieldMap for SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
|
|
||||||
fn get_field_binding(&self, field: &String) -> Option<&TokenStream> {
|
|
||||||
self.fields.get(field)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Provides frequently-needed information about the diagnostic kinds being derived for this type.
|
/// Provides frequently-needed information about the diagnostic kinds being derived for this type.
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
struct KindsStatistics {
|
struct KindsStatistics {
|
||||||
|
|
@ -187,14 +182,12 @@ impl<'a> FromIterator<&'a SubdiagnosticKind> for KindsStatistics {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
|
impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
|
||||||
fn identify_kind(
|
fn identify_kind(&mut self) -> Result<Vec<(SubdiagnosticKind, Path)>, DiagnosticDeriveError> {
|
||||||
&mut self,
|
|
||||||
) -> Result<Vec<(SubdiagnosticKind, Path, bool)>, DiagnosticDeriveError> {
|
|
||||||
let mut kind_slugs = vec![];
|
let mut kind_slugs = vec![];
|
||||||
|
|
||||||
for attr in self.variant.ast().attrs {
|
for attr in self.variant.ast().attrs {
|
||||||
let Some(SubdiagnosticVariant { kind, slug, no_span }) =
|
let Some(SubdiagnosticVariant { kind, slug }) =
|
||||||
SubdiagnosticVariant::from_attr(attr, self)?
|
SubdiagnosticVariant::from_attr(attr, &self.fields)?
|
||||||
else {
|
else {
|
||||||
// Some attributes aren't errors - like documentation comments - but also aren't
|
// Some attributes aren't errors - like documentation comments - but also aren't
|
||||||
// subdiagnostics.
|
// subdiagnostics.
|
||||||
|
|
@ -213,7 +206,7 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
kind_slugs.push((kind, slug, no_span));
|
kind_slugs.push((kind, slug));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(kind_slugs)
|
Ok(kind_slugs)
|
||||||
|
|
@ -437,23 +430,35 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
|
||||||
|
|
||||||
let mut code = None;
|
let mut code = None;
|
||||||
|
|
||||||
list.parse_nested_meta(|nested| {
|
list.parse_args_with(|input: ParseStream<'_>| {
|
||||||
if nested.path.is_ident("code") {
|
while !input.is_empty() {
|
||||||
let code_field = new_code_ident();
|
let arg_name = input.parse::<Ident>()?;
|
||||||
let span = nested.path.span().unwrap();
|
match arg_name.to_string().as_str() {
|
||||||
let formatting_init = build_suggestion_code(
|
"code" => {
|
||||||
&code_field,
|
let code_field = new_code_ident();
|
||||||
nested,
|
let formatting_init = build_suggestion_code(
|
||||||
self,
|
&code_field,
|
||||||
AllowMultipleAlternatives::No,
|
input,
|
||||||
);
|
&self.fields,
|
||||||
code.set_once((code_field, formatting_init), span);
|
AllowMultipleAlternatives::No,
|
||||||
} else {
|
)?;
|
||||||
span_err(
|
code.set_once(
|
||||||
nested.path.span().unwrap(),
|
(code_field, formatting_init),
|
||||||
"`code` is the only valid nested attribute",
|
arg_name.span().unwrap(),
|
||||||
)
|
);
|
||||||
.emit();
|
}
|
||||||
|
_ => {
|
||||||
|
span_err(
|
||||||
|
arg_name.span().unwrap(),
|
||||||
|
"`code` is the only valid nested attribute",
|
||||||
|
)
|
||||||
|
.emit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if input.is_empty() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
input.parse::<Token![,]>()?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
})?;
|
})?;
|
||||||
|
|
@ -492,8 +497,7 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
|
||||||
pub(crate) fn into_tokens(&mut self) -> Result<TokenStream, DiagnosticDeriveError> {
|
pub(crate) fn into_tokens(&mut self) -> Result<TokenStream, DiagnosticDeriveError> {
|
||||||
let kind_slugs = self.identify_kind()?;
|
let kind_slugs = self.identify_kind()?;
|
||||||
|
|
||||||
let kind_stats: KindsStatistics =
|
let kind_stats: KindsStatistics = kind_slugs.iter().map(|(kind, _slug)| kind).collect();
|
||||||
kind_slugs.iter().map(|(kind, _slug, _no_span)| kind).collect();
|
|
||||||
|
|
||||||
let init = if kind_stats.has_multipart_suggestion {
|
let init = if kind_stats.has_multipart_suggestion {
|
||||||
quote! { let mut suggestions = Vec::new(); }
|
quote! { let mut suggestions = Vec::new(); }
|
||||||
|
|
@ -526,17 +530,13 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
|
||||||
|
|
||||||
let diag = &self.parent.diag;
|
let diag = &self.parent.diag;
|
||||||
let mut calls = TokenStream::new();
|
let mut calls = TokenStream::new();
|
||||||
for (kind, slug, no_span) in kind_slugs {
|
for (kind, slug) in kind_slugs {
|
||||||
let message = format_ident!("__message");
|
let message = format_ident!("__message");
|
||||||
calls.extend(
|
calls.extend(
|
||||||
quote! { let #message = #diag.eagerly_translate(crate::fluent_generated::#slug); },
|
quote! { let #message = #diag.eagerly_translate(crate::fluent_generated::#slug); },
|
||||||
);
|
);
|
||||||
|
|
||||||
let name = format_ident!(
|
let name = format_ident!("{}{}", if span_field.is_some() { "span_" } else { "" }, kind);
|
||||||
"{}{}",
|
|
||||||
if span_field.is_some() && !no_span { "span_" } else { "" },
|
|
||||||
kind
|
|
||||||
);
|
|
||||||
let call = match kind {
|
let call = match kind {
|
||||||
SubdiagnosticKind::Suggestion {
|
SubdiagnosticKind::Suggestion {
|
||||||
suggestion_kind,
|
suggestion_kind,
|
||||||
|
|
@ -588,9 +588,7 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
if let Some(span) = span_field
|
if let Some(span) = span_field {
|
||||||
&& !no_span
|
|
||||||
{
|
|
||||||
quote! { #diag.#name(#span, #message); }
|
quote! { #diag.#name(#span, #message); }
|
||||||
} else {
|
} else {
|
||||||
quote! { #diag.#name(#message); }
|
quote! { #diag.#name(#message); }
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ use std::str::FromStr;
|
||||||
use proc_macro::Span;
|
use proc_macro::Span;
|
||||||
use proc_macro2::{Ident, TokenStream};
|
use proc_macro2::{Ident, TokenStream};
|
||||||
use quote::{ToTokens, format_ident, quote};
|
use quote::{ToTokens, format_ident, quote};
|
||||||
use syn::meta::ParseNestedMeta;
|
use syn::parse::ParseStream;
|
||||||
use syn::punctuated::Punctuated;
|
use syn::punctuated::Punctuated;
|
||||||
use syn::spanned::Spanned;
|
use syn::spanned::Spanned;
|
||||||
use syn::{Attribute, Field, LitStr, Meta, Path, Token, Type, TypeTuple, parenthesized};
|
use syn::{Attribute, Field, LitStr, Meta, Path, Token, Type, TypeTuple, parenthesized};
|
||||||
|
|
@ -261,108 +261,104 @@ impl<T> SetOnce<T> for SpannedOption<T> {
|
||||||
|
|
||||||
pub(super) type FieldMap = HashMap<String, TokenStream>;
|
pub(super) type FieldMap = HashMap<String, TokenStream>;
|
||||||
|
|
||||||
pub(crate) trait HasFieldMap {
|
/// In the strings in the attributes supplied to this macro, we want callers to be able to
|
||||||
/// Returns the binding for the field with the given name, if it exists on the type.
|
/// reference fields in the format string. For example:
|
||||||
fn get_field_binding(&self, field: &String) -> Option<&TokenStream>;
|
///
|
||||||
|
/// ```ignore (not-usage-example)
|
||||||
|
/// /// Suggest `==` when users wrote `===`.
|
||||||
|
/// #[suggestion(slug = "parser-not-javascript-eq", code = "{lhs} == {rhs}")]
|
||||||
|
/// struct NotJavaScriptEq {
|
||||||
|
/// #[primary_span]
|
||||||
|
/// span: Span,
|
||||||
|
/// lhs: Ident,
|
||||||
|
/// rhs: Ident,
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// We want to automatically pick up that `{lhs}` refers `self.lhs` and `{rhs}` refers to
|
||||||
|
/// `self.rhs`, then generate this call to `format!`:
|
||||||
|
///
|
||||||
|
/// ```ignore (not-usage-example)
|
||||||
|
/// format!("{lhs} == {rhs}", lhs = self.lhs, rhs = self.rhs)
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// This function builds the entire call to `format!`.
|
||||||
|
pub(super) fn build_format(
|
||||||
|
field_map: &FieldMap,
|
||||||
|
input: &str,
|
||||||
|
span: proc_macro2::Span,
|
||||||
|
) -> TokenStream {
|
||||||
|
// This set is used later to generate the final format string. To keep builds reproducible,
|
||||||
|
// the iteration order needs to be deterministic, hence why we use a `BTreeSet` here
|
||||||
|
// instead of a `HashSet`.
|
||||||
|
let mut referenced_fields: BTreeSet<String> = BTreeSet::new();
|
||||||
|
|
||||||
/// In the strings in the attributes supplied to this macro, we want callers to be able to
|
// At this point, we can start parsing the format string.
|
||||||
/// reference fields in the format string. For example:
|
let mut it = input.chars().peekable();
|
||||||
///
|
|
||||||
/// ```ignore (not-usage-example)
|
|
||||||
/// /// Suggest `==` when users wrote `===`.
|
|
||||||
/// #[suggestion(slug = "parser-not-javascript-eq", code = "{lhs} == {rhs}")]
|
|
||||||
/// struct NotJavaScriptEq {
|
|
||||||
/// #[primary_span]
|
|
||||||
/// span: Span,
|
|
||||||
/// lhs: Ident,
|
|
||||||
/// rhs: Ident,
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// We want to automatically pick up that `{lhs}` refers `self.lhs` and `{rhs}` refers to
|
|
||||||
/// `self.rhs`, then generate this call to `format!`:
|
|
||||||
///
|
|
||||||
/// ```ignore (not-usage-example)
|
|
||||||
/// format!("{lhs} == {rhs}", lhs = self.lhs, rhs = self.rhs)
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// This function builds the entire call to `format!`.
|
|
||||||
fn build_format(&self, input: &str, span: proc_macro2::Span) -> TokenStream {
|
|
||||||
// This set is used later to generate the final format string. To keep builds reproducible,
|
|
||||||
// the iteration order needs to be deterministic, hence why we use a `BTreeSet` here
|
|
||||||
// instead of a `HashSet`.
|
|
||||||
let mut referenced_fields: BTreeSet<String> = BTreeSet::new();
|
|
||||||
|
|
||||||
// At this point, we can start parsing the format string.
|
// Once the start of a format string has been found, process the format string and spit out
|
||||||
let mut it = input.chars().peekable();
|
// the referenced fields. Leaves `it` sitting on the closing brace of the format string, so
|
||||||
|
// the next call to `it.next()` retrieves the next character.
|
||||||
// Once the start of a format string has been found, process the format string and spit out
|
while let Some(c) = it.next() {
|
||||||
// the referenced fields. Leaves `it` sitting on the closing brace of the format string, so
|
if c != '{' {
|
||||||
// the next call to `it.next()` retrieves the next character.
|
continue;
|
||||||
while let Some(c) = it.next() {
|
|
||||||
if c != '{' {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if *it.peek().unwrap_or(&'\0') == '{' {
|
|
||||||
assert_eq!(it.next().unwrap(), '{');
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let mut eat_argument = || -> Option<String> {
|
|
||||||
let mut result = String::new();
|
|
||||||
// Format specifiers look like:
|
|
||||||
//
|
|
||||||
// format := '{' [ argument ] [ ':' format_spec ] '}' .
|
|
||||||
//
|
|
||||||
// Therefore, we only need to eat until ':' or '}' to find the argument.
|
|
||||||
while let Some(c) = it.next() {
|
|
||||||
result.push(c);
|
|
||||||
let next = *it.peek().unwrap_or(&'\0');
|
|
||||||
if next == '}' {
|
|
||||||
break;
|
|
||||||
} else if next == ':' {
|
|
||||||
// Eat the ':' character.
|
|
||||||
assert_eq!(it.next().unwrap(), ':');
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Eat until (and including) the matching '}'
|
|
||||||
while it.next()? != '}' {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
Some(result)
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(referenced_field) = eat_argument() {
|
|
||||||
referenced_fields.insert(referenced_field);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if *it.peek().unwrap_or(&'\0') == '{' {
|
||||||
|
assert_eq!(it.next().unwrap(), '{');
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let mut eat_argument = || -> Option<String> {
|
||||||
|
let mut result = String::new();
|
||||||
|
// Format specifiers look like:
|
||||||
|
//
|
||||||
|
// format := '{' [ argument ] [ ':' format_spec ] '}' .
|
||||||
|
//
|
||||||
|
// Therefore, we only need to eat until ':' or '}' to find the argument.
|
||||||
|
while let Some(c) = it.next() {
|
||||||
|
result.push(c);
|
||||||
|
let next = *it.peek().unwrap_or(&'\0');
|
||||||
|
if next == '}' {
|
||||||
|
break;
|
||||||
|
} else if next == ':' {
|
||||||
|
// Eat the ':' character.
|
||||||
|
assert_eq!(it.next().unwrap(), ':');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Eat until (and including) the matching '}'
|
||||||
|
while it.next()? != '}' {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Some(result)
|
||||||
|
};
|
||||||
|
|
||||||
// At this point, `referenced_fields` contains a set of the unique fields that were
|
if let Some(referenced_field) = eat_argument() {
|
||||||
// referenced in the format string. Generate the corresponding "x = self.x" format
|
referenced_fields.insert(referenced_field);
|
||||||
// string parameters:
|
}
|
||||||
let args = referenced_fields.into_iter().map(|field: String| {
|
}
|
||||||
let field_ident = format_ident!("{}", field);
|
|
||||||
let value = match self.get_field_binding(&field) {
|
// At this point, `referenced_fields` contains a set of the unique fields that were
|
||||||
Some(value) => value.clone(),
|
// referenced in the format string. Generate the corresponding "x = self.x" format
|
||||||
// This field doesn't exist. Emit a diagnostic.
|
// string parameters:
|
||||||
None => {
|
let args = referenced_fields.into_iter().map(|field: String| {
|
||||||
span_err(
|
let field_ident = format_ident!("{}", field);
|
||||||
span.unwrap(),
|
let value = match field_map.get(&field) {
|
||||||
format!("`{field}` doesn't refer to a field on this type"),
|
Some(value) => value.clone(),
|
||||||
)
|
// This field doesn't exist. Emit a diagnostic.
|
||||||
|
None => {
|
||||||
|
span_err(span.unwrap(), format!("`{field}` doesn't refer to a field on this type"))
|
||||||
.emit();
|
.emit();
|
||||||
quote! {
|
quote! {
|
||||||
"{#field}"
|
"{#field}"
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
quote! {
|
|
||||||
#field_ident = #value
|
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
quote! {
|
quote! {
|
||||||
format!(#input #(,#args)*)
|
#field_ident = #value
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
quote! {
|
||||||
|
format!(#input #(,#args)*)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -428,76 +424,63 @@ pub(super) enum AllowMultipleAlternatives {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_suggestion_values(
|
fn parse_suggestion_values(
|
||||||
nested: ParseNestedMeta<'_>,
|
nested: ParseStream<'_>,
|
||||||
allow_multiple: AllowMultipleAlternatives,
|
allow_multiple: AllowMultipleAlternatives,
|
||||||
) -> syn::Result<Vec<LitStr>> {
|
) -> syn::Result<Vec<LitStr>> {
|
||||||
let values = if let Ok(val) = nested.value() {
|
if nested.parse::<Token![=]>().is_ok() {
|
||||||
vec![val.parse()?]
|
return Ok(vec![nested.parse::<LitStr>()?]);
|
||||||
} else {
|
}
|
||||||
let content;
|
|
||||||
parenthesized!(content in nested.input);
|
|
||||||
|
|
||||||
if let AllowMultipleAlternatives::No = allow_multiple {
|
let content;
|
||||||
|
parenthesized!(content in nested);
|
||||||
|
if let AllowMultipleAlternatives::No = allow_multiple {
|
||||||
|
span_err(content.span().unwrap(), "expected exactly one string literal for `code = ...`")
|
||||||
|
.emit();
|
||||||
|
return Ok(vec![]);
|
||||||
|
}
|
||||||
|
|
||||||
|
let literals = Punctuated::<LitStr, Token![,]>::parse_terminated(&content);
|
||||||
|
Ok(match literals {
|
||||||
|
Ok(p) if p.is_empty() => {
|
||||||
span_err(
|
span_err(
|
||||||
nested.input.span().unwrap(),
|
content.span().unwrap(),
|
||||||
"expected exactly one string literal for `code = ...`",
|
"expected at least one string literal for `code(...)`",
|
||||||
)
|
)
|
||||||
.emit();
|
.emit();
|
||||||
vec![]
|
vec![]
|
||||||
} else {
|
|
||||||
let literals = Punctuated::<LitStr, Token![,]>::parse_terminated(&content);
|
|
||||||
|
|
||||||
match literals {
|
|
||||||
Ok(p) if p.is_empty() => {
|
|
||||||
span_err(
|
|
||||||
content.span().unwrap(),
|
|
||||||
"expected at least one string literal for `code(...)`",
|
|
||||||
)
|
|
||||||
.emit();
|
|
||||||
vec![]
|
|
||||||
}
|
|
||||||
Ok(p) => p.into_iter().collect(),
|
|
||||||
Err(_) => {
|
|
||||||
span_err(
|
|
||||||
content.span().unwrap(),
|
|
||||||
"`code(...)` must contain only string literals",
|
|
||||||
)
|
|
||||||
.emit();
|
|
||||||
vec![]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
Ok(p) => p.into_iter().collect(),
|
||||||
|
Err(_) => {
|
||||||
Ok(values)
|
span_err(content.span().unwrap(), "`code(...)` must contain only string literals")
|
||||||
|
.emit();
|
||||||
|
vec![]
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Constructs the `format!()` invocation(s) necessary for a `#[suggestion*(code = "foo")]` or
|
/// Constructs the `format!()` invocation(s) necessary for a `#[suggestion*(code = "foo")]` or
|
||||||
/// `#[suggestion*(code("foo", "bar"))]` attribute field
|
/// `#[suggestion*(code("foo", "bar"))]` attribute field
|
||||||
pub(super) fn build_suggestion_code(
|
pub(super) fn build_suggestion_code(
|
||||||
code_field: &Ident,
|
code_field: &Ident,
|
||||||
nested: ParseNestedMeta<'_>,
|
nested: ParseStream<'_>,
|
||||||
fields: &impl HasFieldMap,
|
fields: &FieldMap,
|
||||||
allow_multiple: AllowMultipleAlternatives,
|
allow_multiple: AllowMultipleAlternatives,
|
||||||
) -> TokenStream {
|
) -> Result<TokenStream, syn::Error> {
|
||||||
let values = match parse_suggestion_values(nested, allow_multiple) {
|
let values = parse_suggestion_values(nested, allow_multiple)?;
|
||||||
Ok(x) => x,
|
|
||||||
Err(e) => return e.into_compile_error(),
|
|
||||||
};
|
|
||||||
|
|
||||||
if let AllowMultipleAlternatives::Yes = allow_multiple {
|
Ok(if let AllowMultipleAlternatives::Yes = allow_multiple {
|
||||||
let formatted_strings: Vec<_> = values
|
let formatted_strings: Vec<_> = values
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|value| fields.build_format(&value.value(), value.span()))
|
.map(|value| build_format(fields, &value.value(), value.span()))
|
||||||
.collect();
|
.collect();
|
||||||
quote! { let #code_field = [#(#formatted_strings),*].into_iter(); }
|
quote! { let #code_field = [#(#formatted_strings),*].into_iter(); }
|
||||||
} else if let [value] = values.as_slice() {
|
} else if let [value] = values.as_slice() {
|
||||||
let formatted_str = fields.build_format(&value.value(), value.span());
|
let formatted_str = build_format(fields, &value.value(), value.span());
|
||||||
quote! { let #code_field = #formatted_str; }
|
quote! { let #code_field = #formatted_str; }
|
||||||
} else {
|
} else {
|
||||||
// error handled previously
|
// error handled previously
|
||||||
quote! { let #code_field = String::new(); }
|
quote! { let #code_field = String::new(); }
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Possible styles for suggestion subdiagnostics.
|
/// Possible styles for suggestion subdiagnostics.
|
||||||
|
|
@ -605,16 +588,15 @@ pub(super) enum SubdiagnosticKind {
|
||||||
pub(super) struct SubdiagnosticVariant {
|
pub(super) struct SubdiagnosticVariant {
|
||||||
pub(super) kind: SubdiagnosticKind,
|
pub(super) kind: SubdiagnosticKind,
|
||||||
pub(super) slug: Option<Path>,
|
pub(super) slug: Option<Path>,
|
||||||
pub(super) no_span: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SubdiagnosticVariant {
|
impl SubdiagnosticVariant {
|
||||||
/// Constructs a `SubdiagnosticVariant` from a field or type attribute such as `#[note]`,
|
/// Constructs a `SubdiagnosticVariant` from a field or type attribute such as `#[note]`,
|
||||||
/// `#[error(parser::add_paren, no_span)]` or `#[suggestion(code = "...")]`. Returns the
|
/// `#[error(parser::add_paren)]` or `#[suggestion(code = "...")]`. Returns the
|
||||||
/// `SubdiagnosticKind` and the diagnostic slug, if specified.
|
/// `SubdiagnosticKind` and the diagnostic slug, if specified.
|
||||||
pub(super) fn from_attr(
|
pub(super) fn from_attr(
|
||||||
attr: &Attribute,
|
attr: &Attribute,
|
||||||
fields: &impl HasFieldMap,
|
fields: &FieldMap,
|
||||||
) -> Result<Option<SubdiagnosticVariant>, DiagnosticDeriveError> {
|
) -> Result<Option<SubdiagnosticVariant>, DiagnosticDeriveError> {
|
||||||
// Always allow documentation comments.
|
// Always allow documentation comments.
|
||||||
if is_doc_comment(attr) {
|
if is_doc_comment(attr) {
|
||||||
|
|
@ -694,7 +676,7 @@ impl SubdiagnosticVariant {
|
||||||
| SubdiagnosticKind::HelpOnce
|
| SubdiagnosticKind::HelpOnce
|
||||||
| SubdiagnosticKind::Warn
|
| SubdiagnosticKind::Warn
|
||||||
| SubdiagnosticKind::MultipartSuggestion { .. } => {
|
| SubdiagnosticKind::MultipartSuggestion { .. } => {
|
||||||
return Ok(Some(SubdiagnosticVariant { kind, slug: None, no_span: false }));
|
return Ok(Some(SubdiagnosticVariant { kind, slug: None }));
|
||||||
}
|
}
|
||||||
SubdiagnosticKind::Suggestion { .. } => {
|
SubdiagnosticKind::Suggestion { .. } => {
|
||||||
throw_span_err!(span, "suggestion without `code = \"...\"`")
|
throw_span_err!(span, "suggestion without `code = \"...\"`")
|
||||||
|
|
@ -709,112 +691,93 @@ impl SubdiagnosticVariant {
|
||||||
let mut code = None;
|
let mut code = None;
|
||||||
let mut suggestion_kind = None;
|
let mut suggestion_kind = None;
|
||||||
|
|
||||||
let mut first = true;
|
|
||||||
let mut slug = None;
|
let mut slug = None;
|
||||||
let mut no_span = false;
|
|
||||||
|
|
||||||
list.parse_nested_meta(|nested| {
|
list.parse_args_with(|input: ParseStream<'_>| {
|
||||||
if nested.input.is_empty() || nested.input.peek(Token![,]) {
|
let mut is_first = true;
|
||||||
if first {
|
while !input.is_empty() {
|
||||||
slug = Some(nested.path);
|
let arg_name: Path = input.parse::<Path>()?;
|
||||||
} else if nested.path.is_ident("no_span") {
|
let arg_name_span = arg_name.span().unwrap();
|
||||||
no_span = true;
|
if input.is_empty() || input.parse::<Token![,]>().is_ok() {
|
||||||
} else {
|
if is_first {
|
||||||
span_err(nested.input.span().unwrap(), "a diagnostic slug must be the first argument to the attribute").emit();
|
slug = Some(arg_name);
|
||||||
|
is_first = false;
|
||||||
|
} else {
|
||||||
|
span_err(arg_name_span, "a diagnostic slug must be the first argument to the attribute").emit();
|
||||||
|
}
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
is_first = false;
|
||||||
|
|
||||||
first = false;
|
match (arg_name.require_ident()?.to_string().as_str(), &mut kind) {
|
||||||
return Ok(());
|
("code", SubdiagnosticKind::Suggestion { code_field, .. }) => {
|
||||||
}
|
let code_init = build_suggestion_code(
|
||||||
|
&code_field,
|
||||||
|
&input,
|
||||||
|
fields,
|
||||||
|
AllowMultipleAlternatives::Yes,
|
||||||
|
)?;
|
||||||
|
code.set_once(code_init, arg_name_span);
|
||||||
|
}
|
||||||
|
(
|
||||||
|
"applicability",
|
||||||
|
SubdiagnosticKind::Suggestion { applicability, .. }
|
||||||
|
| SubdiagnosticKind::MultipartSuggestion { applicability, .. },
|
||||||
|
) => {
|
||||||
|
input.parse::<Token![=]>()?;
|
||||||
|
let value = input.parse::<LitStr>()?;
|
||||||
|
let value = Applicability::from_str(&value.value()).unwrap_or_else(|()| {
|
||||||
|
span_err(value.span().unwrap(), "invalid applicability").emit();
|
||||||
|
Applicability::Unspecified
|
||||||
|
});
|
||||||
|
applicability.set_once(value, span);
|
||||||
|
}
|
||||||
|
(
|
||||||
|
"style",
|
||||||
|
SubdiagnosticKind::Suggestion { .. }
|
||||||
|
| SubdiagnosticKind::MultipartSuggestion { .. },
|
||||||
|
) => {
|
||||||
|
input.parse::<Token![=]>()?;
|
||||||
|
let value = input.parse::<LitStr>()?;
|
||||||
|
|
||||||
first = false;
|
let value = value.value().parse().unwrap_or_else(|()| {
|
||||||
|
span_err(value.span().unwrap(), "invalid suggestion style")
|
||||||
|
.help("valid styles are `normal`, `short`, `hidden`, `verbose` and `tool-only`")
|
||||||
|
.emit();
|
||||||
|
SuggestionKind::Normal
|
||||||
|
});
|
||||||
|
|
||||||
let nested_name = nested.path.segments.last().unwrap().ident.to_string();
|
suggestion_kind.set_once(value, span);
|
||||||
let nested_name = nested_name.as_str();
|
}
|
||||||
|
|
||||||
let path_span = nested.path.span().unwrap();
|
|
||||||
let val_span = nested.input.span().unwrap();
|
|
||||||
|
|
||||||
macro_rules! get_string {
|
// Invalid nested attribute
|
||||||
() => {{
|
(_, SubdiagnosticKind::Suggestion { .. }) => {
|
||||||
let Ok(value) = nested.value().and_then(|x| x.parse::<LitStr>()) else {
|
span_err(arg_name_span, "invalid nested attribute")
|
||||||
span_err(val_span, "expected `= \"xxx\"`").emit();
|
.help(
|
||||||
return Ok(());
|
"only `style`, `code` and `applicability` are valid nested attributes",
|
||||||
};
|
)
|
||||||
value
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut has_errors = false;
|
|
||||||
let input = nested.input;
|
|
||||||
|
|
||||||
match (nested_name, &mut kind) {
|
|
||||||
("code", SubdiagnosticKind::Suggestion { code_field, .. }) => {
|
|
||||||
let code_init = build_suggestion_code(
|
|
||||||
code_field,
|
|
||||||
nested,
|
|
||||||
fields,
|
|
||||||
AllowMultipleAlternatives::Yes,
|
|
||||||
);
|
|
||||||
code.set_once(code_init, path_span);
|
|
||||||
}
|
|
||||||
(
|
|
||||||
"applicability",
|
|
||||||
SubdiagnosticKind::Suggestion { applicability, .. }
|
|
||||||
| SubdiagnosticKind::MultipartSuggestion { applicability, .. },
|
|
||||||
) => {
|
|
||||||
let value = get_string!();
|
|
||||||
let value = Applicability::from_str(&value.value()).unwrap_or_else(|()| {
|
|
||||||
span_err(value.span().unwrap(), "invalid applicability").emit();
|
|
||||||
has_errors = true;
|
|
||||||
Applicability::Unspecified
|
|
||||||
});
|
|
||||||
applicability.set_once(value, span);
|
|
||||||
}
|
|
||||||
(
|
|
||||||
"style",
|
|
||||||
SubdiagnosticKind::Suggestion { .. }
|
|
||||||
| SubdiagnosticKind::MultipartSuggestion { .. },
|
|
||||||
) => {
|
|
||||||
let value = get_string!();
|
|
||||||
|
|
||||||
let value = value.value().parse().unwrap_or_else(|()| {
|
|
||||||
span_err(value.span().unwrap(), "invalid suggestion style")
|
|
||||||
.help("valid styles are `normal`, `short`, `hidden`, `verbose` and `tool-only`")
|
|
||||||
.emit();
|
.emit();
|
||||||
has_errors = true;
|
// Consume the rest of the input to avoid spamming errors
|
||||||
SuggestionKind::Normal
|
let _ = input.parse::<TokenStream>();
|
||||||
});
|
}
|
||||||
|
(_, SubdiagnosticKind::MultipartSuggestion { .. }) => {
|
||||||
suggestion_kind.set_once(value, span);
|
span_err(arg_name_span, "invalid nested attribute")
|
||||||
|
.help("only `style` and `applicability` are valid nested attributes")
|
||||||
|
.emit();
|
||||||
|
// Consume the rest of the input to avoid spamming errors
|
||||||
|
let _ = input.parse::<TokenStream>();
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
span_err(arg_name_span, "no nested attribute expected here").emit();
|
||||||
|
// Consume the rest of the input to avoid spamming errors
|
||||||
|
let _ = input.parse::<TokenStream>();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Invalid nested attribute
|
if input.is_empty() { break }
|
||||||
(_, SubdiagnosticKind::Suggestion { .. }) => {
|
input.parse::<Token![,]>()?;
|
||||||
span_err(path_span, "invalid nested attribute")
|
|
||||||
.help(
|
|
||||||
"only `no_span`, `style`, `code` and `applicability` are valid nested attributes",
|
|
||||||
)
|
|
||||||
.emit();
|
|
||||||
has_errors = true;
|
|
||||||
}
|
|
||||||
(_, SubdiagnosticKind::MultipartSuggestion { .. }) => {
|
|
||||||
span_err(path_span, "invalid nested attribute")
|
|
||||||
.help("only `no_span`, `style` and `applicability` are valid nested attributes")
|
|
||||||
.emit();
|
|
||||||
has_errors = true;
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
span_err(path_span, "only `no_span` is a valid nested attribute").emit();
|
|
||||||
has_errors = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if has_errors {
|
|
||||||
// Consume the rest of the input to avoid spamming errors
|
|
||||||
let _ = input.parse::<TokenStream>();
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
|
@ -851,7 +814,7 @@ impl SubdiagnosticVariant {
|
||||||
| SubdiagnosticKind::Warn => {}
|
| SubdiagnosticKind::Warn => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Some(SubdiagnosticVariant { kind, slug, no_span }))
|
Ok(Some(SubdiagnosticVariant { kind, slug }))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -98,6 +98,8 @@ parse_bare_cr = {$double_quotes ->
|
||||||
}
|
}
|
||||||
.escape = escape the character
|
.escape = escape the character
|
||||||
|
|
||||||
|
parse_bare_cr_in_frontmatter = bare CR not allowed in frontmatter
|
||||||
|
|
||||||
parse_bare_cr_in_raw_string = bare CR not allowed in raw string
|
parse_bare_cr_in_raw_string = bare CR not allowed in raw string
|
||||||
|
|
||||||
parse_binder_and_polarity = `for<...>` binder not allowed with `{$polarity}` trait polarity modifier
|
parse_binder_and_polarity = `for<...>` binder not allowed with `{$polarity}` trait polarity modifier
|
||||||
|
|
@ -352,7 +354,6 @@ parse_frontmatter_length_mismatch = frontmatter close does not match the opening
|
||||||
parse_frontmatter_too_many_dashes = too many `-` symbols: frontmatter openings may be delimited by up to 255 `-` symbols, but found {$len_opening}
|
parse_frontmatter_too_many_dashes = too many `-` symbols: frontmatter openings may be delimited by up to 255 `-` symbols, but found {$len_opening}
|
||||||
parse_frontmatter_unclosed = unclosed frontmatter
|
parse_frontmatter_unclosed = unclosed frontmatter
|
||||||
.note = frontmatter opening here was not closed
|
.note = frontmatter opening here was not closed
|
||||||
|
|
||||||
parse_function_body_equals_expr = function body cannot be `= expression;`
|
parse_function_body_equals_expr = function body cannot be `= expression;`
|
||||||
.suggestion = surround the expression with `{"{"}` and `{"}"}` instead of `=` and `;`
|
.suggestion = surround the expression with `{"{"}` and `{"}"}` instead of `=` and `;`
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -829,6 +829,13 @@ pub(crate) struct FrontmatterTooManyDashes {
|
||||||
pub len_opening: usize,
|
pub len_opening: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Diagnostic)]
|
||||||
|
#[diag(parse_bare_cr_in_frontmatter)]
|
||||||
|
pub(crate) struct BareCrFrontmatter {
|
||||||
|
#[primary_span]
|
||||||
|
pub span: Span,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Diagnostic)]
|
#[derive(Diagnostic)]
|
||||||
#[diag(parse_leading_plus_not_supported)]
|
#[diag(parse_leading_plus_not_supported)]
|
||||||
pub(crate) struct LeadingPlusNotSupported {
|
pub(crate) struct LeadingPlusNotSupported {
|
||||||
|
|
|
||||||
|
|
@ -598,9 +598,9 @@ impl<'psess, 'src> Lexer<'psess, 'src> {
|
||||||
let s = self.str_from(start);
|
let s = self.str_from(start);
|
||||||
let real_start = s.find("---").unwrap();
|
let real_start = s.find("---").unwrap();
|
||||||
let frontmatter_opening_pos = BytePos(real_start as u32) + start;
|
let frontmatter_opening_pos = BytePos(real_start as u32) + start;
|
||||||
let s_new = &s[real_start..];
|
let real_s = &s[real_start..];
|
||||||
let within = s_new.trim_start_matches('-');
|
let within = real_s.trim_start_matches('-');
|
||||||
let len_opening = s_new.len() - within.len();
|
let len_opening = real_s.len() - within.len();
|
||||||
|
|
||||||
let frontmatter_opening_end_pos = frontmatter_opening_pos + BytePos(len_opening as u32);
|
let frontmatter_opening_end_pos = frontmatter_opening_pos + BytePos(len_opening as u32);
|
||||||
if has_invalid_preceding_whitespace {
|
if has_invalid_preceding_whitespace {
|
||||||
|
|
@ -614,8 +614,8 @@ impl<'psess, 'src> Lexer<'psess, 'src> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let line_end = real_s.find('\n').unwrap_or(real_s.len());
|
||||||
if invalid_infostring {
|
if invalid_infostring {
|
||||||
let line_end = s[real_start..].find('\n').unwrap_or(s[real_start..].len());
|
|
||||||
let span = self.mk_sp(
|
let span = self.mk_sp(
|
||||||
frontmatter_opening_end_pos,
|
frontmatter_opening_end_pos,
|
||||||
frontmatter_opening_pos + BytePos(line_end as u32),
|
frontmatter_opening_pos + BytePos(line_end as u32),
|
||||||
|
|
@ -623,10 +623,18 @@ impl<'psess, 'src> Lexer<'psess, 'src> {
|
||||||
self.dcx().emit_err(errors::FrontmatterInvalidInfostring { span });
|
self.dcx().emit_err(errors::FrontmatterInvalidInfostring { span });
|
||||||
}
|
}
|
||||||
|
|
||||||
let last_line_start = within.rfind('\n').map_or(0, |i| i + 1);
|
let last_line_start = real_s.rfind('\n').map_or(0, |i| i + 1);
|
||||||
let last_line = &within[last_line_start..];
|
|
||||||
|
let content = &real_s[line_end..last_line_start];
|
||||||
|
if let Some(cr_offset) = content.find('\r') {
|
||||||
|
let cr_pos = start + BytePos((real_start + line_end + cr_offset) as u32);
|
||||||
|
let span = self.mk_sp(cr_pos, cr_pos + BytePos(1 as u32));
|
||||||
|
self.dcx().emit_err(errors::BareCrFrontmatter { span });
|
||||||
|
}
|
||||||
|
|
||||||
|
let last_line = &real_s[last_line_start..];
|
||||||
let last_line_trimmed = last_line.trim_start_matches(is_horizontal_whitespace);
|
let last_line_trimmed = last_line.trim_start_matches(is_horizontal_whitespace);
|
||||||
let last_line_start_pos = frontmatter_opening_end_pos + BytePos(last_line_start as u32);
|
let last_line_start_pos = frontmatter_opening_pos + BytePos(last_line_start as u32);
|
||||||
|
|
||||||
let frontmatter_span = self.mk_sp(frontmatter_opening_pos, self.pos);
|
let frontmatter_span = self.mk_sp(frontmatter_opening_pos, self.pos);
|
||||||
self.psess.gated_spans.gate(sym::frontmatter, frontmatter_span);
|
self.psess.gated_spans.gate(sym::frontmatter, frontmatter_span);
|
||||||
|
|
|
||||||
|
|
@ -4443,7 +4443,6 @@ impl<T> [T] {
|
||||||
where
|
where
|
||||||
Simd<T, LANES>: AsRef<[T; LANES]>,
|
Simd<T, LANES>: AsRef<[T; LANES]>,
|
||||||
T: simd::SimdElement,
|
T: simd::SimdElement,
|
||||||
simd::LaneCount<LANES>: simd::SupportedLaneCount,
|
|
||||||
{
|
{
|
||||||
// These are expected to always match, as vector types are laid out like
|
// These are expected to always match, as vector types are laid out like
|
||||||
// arrays per <https://llvm.org/docs/LangRef.html#vector-type>, but we
|
// arrays per <https://llvm.org/docs/LangRef.html#vector-type>, but we
|
||||||
|
|
@ -4479,7 +4478,6 @@ impl<T> [T] {
|
||||||
where
|
where
|
||||||
Simd<T, LANES>: AsMut<[T; LANES]>,
|
Simd<T, LANES>: AsMut<[T; LANES]>,
|
||||||
T: simd::SimdElement,
|
T: simd::SimdElement,
|
||||||
simd::LaneCount<LANES>: simd::SupportedLaneCount,
|
|
||||||
{
|
{
|
||||||
// These are expected to always match, as vector types are laid out like
|
// These are expected to always match, as vector types are laid out like
|
||||||
// arrays per <https://llvm.org/docs/LangRef.html#vector-type>, but we
|
// arrays per <https://llvm.org/docs/LangRef.html#vector-type>, but we
|
||||||
|
|
|
||||||
23
library/portable-simd/.github/workflows/ci.yml
vendored
23
library/portable-simd/.github/workflows/ci.yml
vendored
|
|
@ -59,7 +59,7 @@ jobs:
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
target: [x86_64-pc-windows-msvc, i686-pc-windows-msvc, i586-pc-windows-msvc, x86_64-unknown-linux-gnu]
|
target: [x86_64-pc-windows-msvc, i686-pc-windows-msvc, x86_64-unknown-linux-gnu]
|
||||||
# `default` means we use the default target config for the target,
|
# `default` means we use the default target config for the target,
|
||||||
# `native` means we run with `-Ctarget-cpu=native`, and anything else is
|
# `native` means we run with `-Ctarget-cpu=native`, and anything else is
|
||||||
# an arg to `-Ctarget-feature`
|
# an arg to `-Ctarget-feature`
|
||||||
|
|
@ -68,18 +68,12 @@ jobs:
|
||||||
exclude:
|
exclude:
|
||||||
# -Ctarget-cpu=native sounds like bad-news if target != host
|
# -Ctarget-cpu=native sounds like bad-news if target != host
|
||||||
- { target: i686-pc-windows-msvc, target_feature: native }
|
- { target: i686-pc-windows-msvc, target_feature: native }
|
||||||
- { target: i586-pc-windows-msvc, target_feature: native }
|
|
||||||
|
|
||||||
include:
|
include:
|
||||||
# Populate the `matrix.os` field
|
# Populate the `matrix.os` field
|
||||||
- { target: x86_64-unknown-linux-gnu, os: ubuntu-latest }
|
- { target: x86_64-unknown-linux-gnu, os: ubuntu-latest }
|
||||||
- { target: x86_64-pc-windows-msvc, os: windows-latest }
|
- { target: x86_64-pc-windows-msvc, os: windows-latest }
|
||||||
- { target: i686-pc-windows-msvc, os: windows-latest }
|
- { target: i686-pc-windows-msvc, os: windows-latest }
|
||||||
- { target: i586-pc-windows-msvc, os: windows-latest }
|
|
||||||
|
|
||||||
# These are globally available on all the other targets.
|
|
||||||
- { target: i586-pc-windows-msvc, target_feature: +sse, os: windows-latest }
|
|
||||||
- { target: i586-pc-windows-msvc, target_feature: +sse2, os: windows-latest }
|
|
||||||
|
|
||||||
# Annoyingly, the x86_64-unknown-linux-gnu runner *almost* always has
|
# Annoyingly, the x86_64-unknown-linux-gnu runner *almost* always has
|
||||||
# avx512vl, but occasionally doesn't. Maybe one day we can enable it.
|
# avx512vl, but occasionally doesn't. Maybe one day we can enable it.
|
||||||
|
|
@ -129,7 +123,7 @@ jobs:
|
||||||
run: cargo doc --verbose --target=${{ matrix.target }}
|
run: cargo doc --verbose --target=${{ matrix.target }}
|
||||||
env:
|
env:
|
||||||
RUSTDOCFLAGS: -Dwarnings
|
RUSTDOCFLAGS: -Dwarnings
|
||||||
|
|
||||||
macos-tests:
|
macos-tests:
|
||||||
name: ${{ matrix.target }}
|
name: ${{ matrix.target }}
|
||||||
runs-on: macos-latest
|
runs-on: macos-latest
|
||||||
|
|
@ -246,9 +240,18 @@ jobs:
|
||||||
|
|
||||||
miri:
|
miri:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
shard: [1, 2, 3, 4]
|
||||||
env:
|
env:
|
||||||
PROPTEST_CASES: 16
|
PROPTEST_CASES: 16
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Test (Miri)
|
|
||||||
run: cargo miri test
|
- name: Install cargo-nextest
|
||||||
|
uses: taiki-e/install-action@nextest
|
||||||
|
|
||||||
|
- name: Test (Miri) (partition ${{ matrix.shard }}/4)
|
||||||
|
run: |
|
||||||
|
cargo miri nextest run --partition count:${{ matrix.shard }}/4
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
# This file is automatically @generated by Cargo.
|
# This file is automatically @generated by Cargo.
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 3
|
version = 4
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.1.0"
|
version = "1.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
|
|
@ -16,31 +16,30 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bumpalo"
|
name = "bumpalo"
|
||||||
version = "3.13.0"
|
version = "3.19.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1"
|
checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "byteorder"
|
name = "byteorder"
|
||||||
version = "1.4.3"
|
version = "1.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cc"
|
||||||
|
version = "1.2.33"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3ee0f8803222ba5a7e2777dd72ca451868909b1ac410621b676adf07280e9b5f"
|
||||||
|
dependencies = [
|
||||||
|
"shlex",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
version = "1.0.0"
|
version = "1.0.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "console_error_panic_hook"
|
|
||||||
version = "0.1.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"wasm-bindgen",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "core_simd"
|
name = "core_simd"
|
||||||
|
|
@ -54,46 +53,69 @@ dependencies = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "js-sys"
|
name = "float-cmp"
|
||||||
version = "0.3.64"
|
version = "0.10.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a"
|
checksum = "b09cf3155332e944990140d967ff5eceb70df778b34f77d8075db46e4704e6d8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "js-sys"
|
||||||
|
version = "0.3.77"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f"
|
||||||
|
dependencies = [
|
||||||
|
"once_cell",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "log"
|
name = "log"
|
||||||
version = "0.4.20"
|
version = "0.4.27"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
|
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "minicov"
|
||||||
|
version = "0.3.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f27fe9f1cc3c22e1687f9446c2083c4c5fc7f0bcf1c7a86bdbded14985895b4b"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"walkdir",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-traits"
|
name = "num-traits"
|
||||||
version = "0.2.16"
|
version = "0.2.19"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2"
|
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "once_cell"
|
name = "once_cell"
|
||||||
version = "1.18.0"
|
version = "1.21.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
|
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ppv-lite86"
|
name = "ppv-lite86"
|
||||||
version = "0.2.17"
|
version = "0.2.21"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
|
||||||
|
dependencies = [
|
||||||
|
"zerocopy",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.66"
|
version = "1.0.101"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9"
|
checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
@ -114,9 +136,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.33"
|
version = "1.0.40"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
|
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
@ -167,10 +189,25 @@ dependencies = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "scoped-tls"
|
name = "rustversion"
|
||||||
version = "1.0.1"
|
version = "1.0.22"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
|
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "same-file"
|
||||||
|
version = "1.0.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "shlex"
|
||||||
|
version = "1.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "std_float"
|
name = "std_float"
|
||||||
|
|
@ -184,9 +221,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.29"
|
version = "2.0.106"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a"
|
checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
|
@ -197,34 +234,46 @@ dependencies = [
|
||||||
name = "test_helpers"
|
name = "test_helpers"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"float-cmp",
|
||||||
"proptest",
|
"proptest",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.11"
|
version = "1.0.18"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c"
|
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "walkdir"
|
||||||
|
version = "2.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
|
||||||
|
dependencies = [
|
||||||
|
"same-file",
|
||||||
|
"winapi-util",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen"
|
name = "wasm-bindgen"
|
||||||
version = "0.2.87"
|
version = "0.2.100"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342"
|
checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
|
"once_cell",
|
||||||
|
"rustversion",
|
||||||
"wasm-bindgen-macro",
|
"wasm-bindgen-macro",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-backend"
|
name = "wasm-bindgen-backend"
|
||||||
version = "0.2.87"
|
version = "0.2.100"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd"
|
checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
"log",
|
"log",
|
||||||
"once_cell",
|
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn",
|
||||||
|
|
@ -233,21 +282,22 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-futures"
|
name = "wasm-bindgen-futures"
|
||||||
version = "0.4.37"
|
version = "0.4.50"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03"
|
checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
|
"once_cell",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
"web-sys",
|
"web-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-macro"
|
name = "wasm-bindgen-macro"
|
||||||
version = "0.2.87"
|
version = "0.2.100"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d"
|
checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"quote",
|
"quote",
|
||||||
"wasm-bindgen-macro-support",
|
"wasm-bindgen-macro-support",
|
||||||
|
|
@ -255,9 +305,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-macro-support"
|
name = "wasm-bindgen-macro-support"
|
||||||
version = "0.2.87"
|
version = "0.2.100"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b"
|
checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
|
@ -268,19 +318,21 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-shared"
|
name = "wasm-bindgen-shared"
|
||||||
version = "0.2.87"
|
version = "0.2.100"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1"
|
checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-test"
|
name = "wasm-bindgen-test"
|
||||||
version = "0.3.37"
|
version = "0.3.50"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6e6e302a7ea94f83a6d09e78e7dc7d9ca7b186bc2829c24a22d0753efd680671"
|
checksum = "66c8d5e33ca3b6d9fa3b4676d774c5778031d27a578c2b007f905acf816152c3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"console_error_panic_hook",
|
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"scoped-tls",
|
"minicov",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
"wasm-bindgen-futures",
|
"wasm-bindgen-futures",
|
||||||
"wasm-bindgen-test-macro",
|
"wasm-bindgen-test-macro",
|
||||||
|
|
@ -288,20 +340,123 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-test-macro"
|
name = "wasm-bindgen-test-macro"
|
||||||
version = "0.3.37"
|
version = "0.3.50"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ecb993dd8c836930ed130e020e77d9b2e65dd0fbab1b67c790b0f5d80b11a575"
|
checksum = "17d5042cc5fa009658f9a7333ef24291b1291a25b6382dd68862a7f3b969f69b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "web-sys"
|
name = "web-sys"
|
||||||
version = "0.3.64"
|
version = "0.3.77"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b"
|
checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-util"
|
||||||
|
version = "0.1.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
||||||
|
dependencies = [
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.59.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-targets"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||||
|
dependencies = [
|
||||||
|
"windows_aarch64_gnullvm",
|
||||||
|
"windows_aarch64_msvc",
|
||||||
|
"windows_i686_gnu",
|
||||||
|
"windows_i686_gnullvm",
|
||||||
|
"windows_i686_msvc",
|
||||||
|
"windows_x86_64_gnu",
|
||||||
|
"windows_x86_64_gnullvm",
|
||||||
|
"windows_x86_64_msvc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_gnullvm"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_msvc"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnu"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnullvm"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_msvc"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnu"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnullvm"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_msvc"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerocopy"
|
||||||
|
version = "0.8.26"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f"
|
||||||
|
dependencies = [
|
||||||
|
"zerocopy-derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerocopy-derive"
|
||||||
|
version = "0.8.26"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ SIMD has a few special vocabulary terms you should know:
|
||||||
|
|
||||||
* **Scalar:** "Scalar" in mathematical contexts refers to values that can be represented as a single element, mostly numbers like 6, 3.14, or -2. It can also be used to describe "scalar operations" that use strictly scalar values, like addition. This term is mostly used to differentiate between vectorized operations that use SIMD instructions and scalar operations that don't.
|
* **Scalar:** "Scalar" in mathematical contexts refers to values that can be represented as a single element, mostly numbers like 6, 3.14, or -2. It can also be used to describe "scalar operations" that use strictly scalar values, like addition. This term is mostly used to differentiate between vectorized operations that use SIMD instructions and scalar operations that don't.
|
||||||
|
|
||||||
* **Lane:** A single element position within a vector is called a lane. If you have `N` lanes available then they're numbered from `0` to `N-1` when referring to them, again like an array. The biggest difference between an array element and a vector lane is that in general is *relatively costly* to access an individual lane value. On most architectures, the vector has to be pushed out of the SIMD register onto the stack, then an individual lane is accessed while it's on the stack (and possibly the stack value is read back into a register). For this reason, when working with SIMD you should avoid reading or writing the value of an individual lane during hot loops.
|
* **Lane:** A single element position within a vector is called a lane. If you have `N` lanes available then they're numbered from `0` to `N-1` when referring to them, again like an array. The biggest difference between an array element and a vector lane is that in general it is *relatively costly* to access an individual lane value. On most architectures, the vector has to be pushed out of the SIMD register onto the stack, then an individual lane is accessed while it's on the stack (and possibly the stack value is read back into a register). For this reason, when working with SIMD you should avoid reading or writing the value of an individual lane during hot loops.
|
||||||
|
|
||||||
* **Bit Widths:** When talking about SIMD, the bit widths used are the bit size of the vectors involved, *not* the individual elements. So "128-bit SIMD" has 128-bit vectors, and that might be `f32x4`, `i32x4`, `i16x8`, or other variations. While 128-bit SIMD is the most common, there's also 64-bit, 256-bit, and even 512-bit on the newest CPUs.
|
* **Bit Widths:** When talking about SIMD, the bit widths used are the bit size of the vectors involved, *not* the individual elements. So "128-bit SIMD" has 128-bit vectors, and that might be `f32x4`, `i32x4`, `i16x8`, or other variations. While 128-bit SIMD is the most common, there's also 64-bit, 256-bit, and even 512-bit on the newest CPUs.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,6 @@
|
||||||
//! Code taken from the `packed_simd` crate.
|
//! Code taken from the `packed_simd` crate.
|
||||||
//! Run this code with `cargo test --example dot_product`.
|
//! Run this code with `cargo test --example dot_product`.
|
||||||
|
|
||||||
#![feature(array_chunks)]
|
|
||||||
#![feature(slice_as_chunks)]
|
|
||||||
// Add these imports to use the stdsimd library
|
// Add these imports to use the stdsimd library
|
||||||
#![feature(portable_simd)]
|
#![feature(portable_simd)]
|
||||||
use core_simd::simd::prelude::*;
|
use core_simd::simd::prelude::*;
|
||||||
|
|
@ -33,7 +31,7 @@ pub fn dot_prod_scalar_1(a: &[f32], b: &[f32]) -> f32 {
|
||||||
}
|
}
|
||||||
|
|
||||||
// We now move on to the SIMD implementations: notice the following constructs:
|
// We now move on to the SIMD implementations: notice the following constructs:
|
||||||
// `array_chunks::<4>`: mapping this over the vector will let use construct SIMD vectors
|
// `as_chunks::<4>`: mapping this over the vector will let us construct SIMD vectors
|
||||||
// `f32x4::from_array`: construct the SIMD vector from a slice
|
// `f32x4::from_array`: construct the SIMD vector from a slice
|
||||||
// `(a * b).reduce_sum()`: Multiply both f32x4 vectors together, and then reduce them.
|
// `(a * b).reduce_sum()`: Multiply both f32x4 vectors together, and then reduce them.
|
||||||
// This approach essentially uses SIMD to produce a vector of length N/4 of all the products,
|
// This approach essentially uses SIMD to produce a vector of length N/4 of all the products,
|
||||||
|
|
@ -42,9 +40,11 @@ pub fn dot_prod_scalar_1(a: &[f32], b: &[f32]) -> f32 {
|
||||||
pub fn dot_prod_simd_0(a: &[f32], b: &[f32]) -> f32 {
|
pub fn dot_prod_simd_0(a: &[f32], b: &[f32]) -> f32 {
|
||||||
assert_eq!(a.len(), b.len());
|
assert_eq!(a.len(), b.len());
|
||||||
// TODO handle remainder when a.len() % 4 != 0
|
// TODO handle remainder when a.len() % 4 != 0
|
||||||
a.array_chunks::<4>()
|
a.as_chunks::<4>()
|
||||||
|
.0
|
||||||
|
.iter()
|
||||||
.map(|&a| f32x4::from_array(a))
|
.map(|&a| f32x4::from_array(a))
|
||||||
.zip(b.array_chunks::<4>().map(|&b| f32x4::from_array(b)))
|
.zip(b.as_chunks::<4>().0.iter().map(|&b| f32x4::from_array(b)))
|
||||||
.map(|(a, b)| (a * b).reduce_sum())
|
.map(|(a, b)| (a * b).reduce_sum())
|
||||||
.sum()
|
.sum()
|
||||||
}
|
}
|
||||||
|
|
@ -60,9 +60,11 @@ pub fn dot_prod_simd_0(a: &[f32], b: &[f32]) -> f32 {
|
||||||
pub fn dot_prod_simd_1(a: &[f32], b: &[f32]) -> f32 {
|
pub fn dot_prod_simd_1(a: &[f32], b: &[f32]) -> f32 {
|
||||||
assert_eq!(a.len(), b.len());
|
assert_eq!(a.len(), b.len());
|
||||||
// TODO handle remainder when a.len() % 4 != 0
|
// TODO handle remainder when a.len() % 4 != 0
|
||||||
a.array_chunks::<4>()
|
a.as_chunks::<4>()
|
||||||
|
.0
|
||||||
|
.iter()
|
||||||
.map(|&a| f32x4::from_array(a))
|
.map(|&a| f32x4::from_array(a))
|
||||||
.zip(b.array_chunks::<4>().map(|&b| f32x4::from_array(b)))
|
.zip(b.as_chunks::<4>().0.iter().map(|&b| f32x4::from_array(b)))
|
||||||
.fold(f32x4::splat(0.0), |acc, zipped| acc + zipped.0 * zipped.1)
|
.fold(f32x4::splat(0.0), |acc, zipped| acc + zipped.0 * zipped.1)
|
||||||
.reduce_sum()
|
.reduce_sum()
|
||||||
}
|
}
|
||||||
|
|
@ -74,9 +76,11 @@ pub fn dot_prod_simd_2(a: &[f32], b: &[f32]) -> f32 {
|
||||||
assert_eq!(a.len(), b.len());
|
assert_eq!(a.len(), b.len());
|
||||||
// TODO handle remainder when a.len() % 4 != 0
|
// TODO handle remainder when a.len() % 4 != 0
|
||||||
let mut res = f32x4::splat(0.0);
|
let mut res = f32x4::splat(0.0);
|
||||||
a.array_chunks::<4>()
|
a.as_chunks::<4>()
|
||||||
|
.0
|
||||||
|
.iter()
|
||||||
.map(|&a| f32x4::from_array(a))
|
.map(|&a| f32x4::from_array(a))
|
||||||
.zip(b.array_chunks::<4>().map(|&b| f32x4::from_array(b)))
|
.zip(b.as_chunks::<4>().0.iter().map(|&b| f32x4::from_array(b)))
|
||||||
.for_each(|(a, b)| {
|
.for_each(|(a, b)| {
|
||||||
res = a.mul_add(b, res);
|
res = a.mul_add(b, res);
|
||||||
});
|
});
|
||||||
|
|
@ -113,9 +117,11 @@ pub fn dot_prod_simd_3(a: &[f32], b: &[f32]) -> f32 {
|
||||||
// next example.
|
// next example.
|
||||||
pub fn dot_prod_simd_4(a: &[f32], b: &[f32]) -> f32 {
|
pub fn dot_prod_simd_4(a: &[f32], b: &[f32]) -> f32 {
|
||||||
let mut sum = a
|
let mut sum = a
|
||||||
.array_chunks::<4>()
|
.as_chunks::<4>()
|
||||||
|
.0
|
||||||
|
.iter()
|
||||||
.map(|&a| f32x4::from_array(a))
|
.map(|&a| f32x4::from_array(a))
|
||||||
.zip(b.array_chunks::<4>().map(|&b| f32x4::from_array(b)))
|
.zip(b.as_chunks::<4>().0.iter().map(|&b| f32x4::from_array(b)))
|
||||||
.map(|(a, b)| a * b)
|
.map(|(a, b)| a * b)
|
||||||
.fold(f32x4::splat(0.0), std::ops::Add::add)
|
.fold(f32x4::splat(0.0), std::ops::Add::add)
|
||||||
.reduce_sum();
|
.reduce_sum();
|
||||||
|
|
@ -131,9 +137,11 @@ pub fn dot_prod_simd_4(a: &[f32], b: &[f32]) -> f32 {
|
||||||
// This version allocates a single `XMM` register for accumulation, and the folds don't allocate on top of that.
|
// This version allocates a single `XMM` register for accumulation, and the folds don't allocate on top of that.
|
||||||
// Notice the use of `mul_add`, which can do a multiply and an add operation ber iteration.
|
// Notice the use of `mul_add`, which can do a multiply and an add operation ber iteration.
|
||||||
pub fn dot_prod_simd_5(a: &[f32], b: &[f32]) -> f32 {
|
pub fn dot_prod_simd_5(a: &[f32], b: &[f32]) -> f32 {
|
||||||
a.array_chunks::<4>()
|
a.as_chunks::<4>()
|
||||||
|
.0
|
||||||
|
.iter()
|
||||||
.map(|&a| f32x4::from_array(a))
|
.map(|&a| f32x4::from_array(a))
|
||||||
.zip(b.array_chunks::<4>().map(|&b| f32x4::from_array(b)))
|
.zip(b.as_chunks::<4>().0.iter().map(|&b| f32x4::from_array(b)))
|
||||||
.fold(f32x4::splat(0.), |acc, (a, b)| a.mul_add(b, acc))
|
.fold(f32x4::splat(0.), |acc, (a, b)| a.mul_add(b, acc))
|
||||||
.reduce_sum()
|
.reduce_sum()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
//! 4x4 matrix inverse
|
//! 4x4 matrix inverse
|
||||||
// Code ported from the `packed_simd` crate
|
// Code ported from the `packed_simd` crate
|
||||||
// Run this code with `cargo test --example matrix_inversion`
|
// Run this code with `cargo test --example matrix_inversion`
|
||||||
#![feature(array_chunks, portable_simd)]
|
#![feature(portable_simd)]
|
||||||
use core_simd::simd::prelude::*;
|
use core_simd::simd::prelude::*;
|
||||||
|
|
||||||
// Gotta define our own 4x4 matrix since Rust doesn't ship multidim arrays yet :^)
|
// Gotta define our own 4x4 matrix since Rust doesn't ship multidim arrays yet :^)
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
use crate::simd::{LaneCount, Simd, SimdElement, SupportedLaneCount};
|
use crate::simd::{Simd, SimdElement};
|
||||||
use core::fmt;
|
use core::fmt;
|
||||||
|
|
||||||
impl<T, const N: usize> fmt::Debug for Simd<T, N>
|
impl<T, const N: usize> fmt::Debug for Simd<T, N>
|
||||||
where
|
where
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
T: SimdElement + fmt::Debug,
|
T: SimdElement + fmt::Debug,
|
||||||
{
|
{
|
||||||
/// A `Simd<T, N>` has a debug format like the one for `[T]`:
|
/// A `Simd<T, N>` has a debug format like the one for `[T]`:
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::simd::{LaneCount, Simd, SupportedLaneCount};
|
use crate::simd::Simd;
|
||||||
use core::{
|
use core::{
|
||||||
iter::{Product, Sum},
|
iter::{Product, Sum},
|
||||||
ops::{Add, Mul},
|
ops::{Add, Mul},
|
||||||
|
|
@ -7,8 +7,6 @@ use core::{
|
||||||
macro_rules! impl_traits {
|
macro_rules! impl_traits {
|
||||||
{ $type:ty } => {
|
{ $type:ty } => {
|
||||||
impl<const N: usize> Sum<Self> for Simd<$type, N>
|
impl<const N: usize> Sum<Self> for Simd<$type, N>
|
||||||
where
|
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
|
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
|
||||||
|
|
@ -17,8 +15,6 @@ macro_rules! impl_traits {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<const N: usize> Product<Self> for Simd<$type, N>
|
impl<const N: usize> Product<Self> for Simd<$type, N>
|
||||||
where
|
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
fn product<I: Iterator<Item = Self>>(iter: I) -> Self {
|
fn product<I: Iterator<Item = Self>>(iter: I) -> Self {
|
||||||
|
|
@ -27,8 +23,6 @@ macro_rules! impl_traits {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, const N: usize> Sum<&'a Self> for Simd<$type, N>
|
impl<'a, const N: usize> Sum<&'a Self> for Simd<$type, N>
|
||||||
where
|
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
fn sum<I: Iterator<Item = &'a Self>>(iter: I) -> Self {
|
fn sum<I: Iterator<Item = &'a Self>>(iter: I) -> Self {
|
||||||
|
|
@ -37,8 +31,6 @@ macro_rules! impl_traits {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, const N: usize> Product<&'a Self> for Simd<$type, N>
|
impl<'a, const N: usize> Product<&'a Self> for Simd<$type, N>
|
||||||
where
|
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
fn product<I: Iterator<Item = &'a Self>>(iter: I) -> Self {
|
fn product<I: Iterator<Item = &'a Self>>(iter: I) -> Self {
|
||||||
|
|
|
||||||
|
|
@ -1,40 +0,0 @@
|
||||||
mod sealed {
|
|
||||||
pub trait Sealed {}
|
|
||||||
}
|
|
||||||
use sealed::Sealed;
|
|
||||||
|
|
||||||
/// Specifies the number of lanes in a SIMD vector as a type.
|
|
||||||
pub struct LaneCount<const N: usize>;
|
|
||||||
|
|
||||||
impl<const N: usize> LaneCount<N> {
|
|
||||||
/// The number of bytes in a bitmask with this many lanes.
|
|
||||||
pub const BITMASK_LEN: usize = N.div_ceil(8);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Statically guarantees that a lane count is marked as supported.
|
|
||||||
///
|
|
||||||
/// This trait is *sealed*: the list of implementors below is total.
|
|
||||||
/// Users do not have the ability to mark additional `LaneCount<N>` values as supported.
|
|
||||||
/// Only SIMD vectors with supported lane counts are constructable.
|
|
||||||
pub trait SupportedLaneCount: Sealed {
|
|
||||||
#[doc(hidden)]
|
|
||||||
type BitMask: Copy + Default + AsRef<[u8]> + AsMut<[u8]>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<const N: usize> Sealed for LaneCount<N> {}
|
|
||||||
|
|
||||||
macro_rules! supported_lane_count {
|
|
||||||
($($lanes:literal),+) => {
|
|
||||||
$(
|
|
||||||
impl SupportedLaneCount for LaneCount<$lanes> {
|
|
||||||
type BitMask = [u8; ($lanes + 7) / 8];
|
|
||||||
}
|
|
||||||
)+
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
supported_lane_count!(
|
|
||||||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26,
|
|
||||||
27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
|
|
||||||
51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64
|
|
||||||
);
|
|
||||||
|
|
@ -9,7 +9,8 @@
|
||||||
simd_ffi,
|
simd_ffi,
|
||||||
staged_api,
|
staged_api,
|
||||||
prelude_import,
|
prelude_import,
|
||||||
ptr_metadata
|
ptr_metadata,
|
||||||
|
rustc_attrs
|
||||||
)]
|
)]
|
||||||
#![cfg_attr(
|
#![cfg_attr(
|
||||||
all(
|
all(
|
||||||
|
|
@ -30,10 +31,6 @@
|
||||||
any(target_arch = "powerpc", target_arch = "powerpc64"),
|
any(target_arch = "powerpc", target_arch = "powerpc64"),
|
||||||
feature(stdarch_powerpc)
|
feature(stdarch_powerpc)
|
||||||
)]
|
)]
|
||||||
#![cfg_attr(
|
|
||||||
all(target_arch = "x86_64", target_feature = "avx512f"),
|
|
||||||
feature(stdarch_x86_avx512)
|
|
||||||
)]
|
|
||||||
#![warn(missing_docs, clippy::missing_inline_in_public_items)] // basically all items, really
|
#![warn(missing_docs, clippy::missing_inline_in_public_items)] // basically all items, really
|
||||||
#![deny(
|
#![deny(
|
||||||
unsafe_op_in_unsafe_fn,
|
unsafe_op_in_unsafe_fn,
|
||||||
|
|
@ -41,7 +38,7 @@
|
||||||
clippy::undocumented_unsafe_blocks
|
clippy::undocumented_unsafe_blocks
|
||||||
)]
|
)]
|
||||||
#![doc(test(attr(deny(warnings))))]
|
#![doc(test(attr(deny(warnings))))]
|
||||||
#![allow(internal_features)]
|
#![allow(internal_features, clippy::repr_packed_without_abi)]
|
||||||
#![unstable(feature = "portable_simd", issue = "86656")]
|
#![unstable(feature = "portable_simd", issue = "86656")]
|
||||||
//! Portable SIMD module.
|
//! Portable SIMD module.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,20 +2,33 @@
|
||||||
//! Types representing
|
//! Types representing
|
||||||
#![allow(non_camel_case_types)]
|
#![allow(non_camel_case_types)]
|
||||||
|
|
||||||
#[cfg_attr(
|
use crate::simd::{Select, Simd, SimdCast, SimdElement};
|
||||||
not(all(target_arch = "x86_64", target_feature = "avx512f")),
|
|
||||||
path = "masks/full_masks.rs"
|
|
||||||
)]
|
|
||||||
#[cfg_attr(
|
|
||||||
all(target_arch = "x86_64", target_feature = "avx512f"),
|
|
||||||
path = "masks/bitmask.rs"
|
|
||||||
)]
|
|
||||||
mod mask_impl;
|
|
||||||
|
|
||||||
use crate::simd::{LaneCount, Simd, SimdCast, SimdElement, SupportedLaneCount};
|
|
||||||
use core::cmp::Ordering;
|
use core::cmp::Ordering;
|
||||||
use core::{fmt, mem};
|
use core::{fmt, mem};
|
||||||
|
|
||||||
|
pub(crate) trait FixEndianness {
|
||||||
|
fn fix_endianness(self) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! impl_fix_endianness {
|
||||||
|
{ $($int:ty),* } => {
|
||||||
|
$(
|
||||||
|
impl FixEndianness for $int {
|
||||||
|
#[inline(always)]
|
||||||
|
fn fix_endianness(self) -> Self {
|
||||||
|
if cfg!(target_endian = "big") {
|
||||||
|
<$int>::reverse_bits(self)
|
||||||
|
} else {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_fix_endianness! { u8, u16, u32, u64 }
|
||||||
|
|
||||||
mod sealed {
|
mod sealed {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
|
@ -28,7 +41,6 @@ mod sealed {
|
||||||
pub trait Sealed {
|
pub trait Sealed {
|
||||||
fn valid<const N: usize>(values: Simd<Self, N>) -> bool
|
fn valid<const N: usize>(values: Simd<Self, N>) -> bool
|
||||||
where
|
where
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
Self: SimdElement;
|
Self: SimdElement;
|
||||||
|
|
||||||
fn eq(self, other: Self) -> bool;
|
fn eq(self, other: Self) -> bool;
|
||||||
|
|
@ -56,8 +68,6 @@ macro_rules! impl_element {
|
||||||
impl Sealed for $ty {
|
impl Sealed for $ty {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn valid<const N: usize>(value: Simd<Self, N>) -> bool
|
fn valid<const N: usize>(value: Simd<Self, N>) -> bool
|
||||||
where
|
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
{
|
||||||
// We can't use `Simd` directly, because `Simd`'s functions call this function and
|
// We can't use `Simd` directly, because `Simd`'s functions call this function and
|
||||||
// we will end up with an infinite loop.
|
// we will end up with an infinite loop.
|
||||||
|
|
@ -108,23 +118,19 @@ impl_element! { isize, usize }
|
||||||
/// The layout of this type is unspecified, and may change between platforms
|
/// The layout of this type is unspecified, and may change between platforms
|
||||||
/// and/or Rust versions, and code should not assume that it is equivalent to
|
/// and/or Rust versions, and code should not assume that it is equivalent to
|
||||||
/// `[T; N]`.
|
/// `[T; N]`.
|
||||||
|
///
|
||||||
|
/// `N` cannot be 0 and may be at most 64. This limit may be increased in
|
||||||
|
/// the future.
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct Mask<T, const N: usize>(mask_impl::Mask<T, N>)
|
pub struct Mask<T, const N: usize>(Simd<T, N>)
|
||||||
where
|
where
|
||||||
T: MaskElement,
|
T: MaskElement;
|
||||||
LaneCount<N>: SupportedLaneCount;
|
|
||||||
|
|
||||||
impl<T, const N: usize> Copy for Mask<T, N>
|
impl<T, const N: usize> Copy for Mask<T, N> where T: MaskElement {}
|
||||||
where
|
|
||||||
T: MaskElement,
|
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, const N: usize> Clone for Mask<T, N>
|
impl<T, const N: usize> Clone for Mask<T, N>
|
||||||
where
|
where
|
||||||
T: MaskElement,
|
T: MaskElement,
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
|
|
@ -135,12 +141,12 @@ where
|
||||||
impl<T, const N: usize> Mask<T, N>
|
impl<T, const N: usize> Mask<T, N>
|
||||||
where
|
where
|
||||||
T: MaskElement,
|
T: MaskElement,
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
{
|
||||||
/// Constructs a mask by setting all elements to the given value.
|
/// Constructs a mask by setting all elements to the given value.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn splat(value: bool) -> Self {
|
#[rustc_const_unstable(feature = "portable_simd", issue = "86656")]
|
||||||
Self(mask_impl::Mask::splat(value))
|
pub const fn splat(value: bool) -> Self {
|
||||||
|
Self(Simd::splat(if value { T::TRUE } else { T::FALSE }))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts an array of bools to a SIMD mask.
|
/// Converts an array of bools to a SIMD mask.
|
||||||
|
|
@ -156,7 +162,7 @@ where
|
||||||
let bytes: [u8; N] = mem::transmute_copy(&array);
|
let bytes: [u8; N] = mem::transmute_copy(&array);
|
||||||
let bools: Simd<i8, N> =
|
let bools: Simd<i8, N> =
|
||||||
core::intrinsics::simd::simd_ne(Simd::from_array(bytes), Simd::splat(0u8));
|
core::intrinsics::simd::simd_ne(Simd::from_array(bytes), Simd::splat(0u8));
|
||||||
Mask::from_int_unchecked(core::intrinsics::simd::simd_cast(bools))
|
Mask::from_simd_unchecked(core::intrinsics::simd::simd_cast(bools))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -174,7 +180,7 @@ where
|
||||||
// This would be hypothetically valid as an "in-place" transmute,
|
// This would be hypothetically valid as an "in-place" transmute,
|
||||||
// but these are "dependently-sized" types, so copy elision it is!
|
// but these are "dependently-sized" types, so copy elision it is!
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut bytes: Simd<i8, N> = core::intrinsics::simd::simd_cast(self.to_int());
|
let mut bytes: Simd<i8, N> = core::intrinsics::simd::simd_cast(self.to_simd());
|
||||||
bytes &= Simd::splat(1i8);
|
bytes &= Simd::splat(1i8);
|
||||||
mem::transmute_copy(&bytes)
|
mem::transmute_copy(&bytes)
|
||||||
}
|
}
|
||||||
|
|
@ -187,12 +193,12 @@ where
|
||||||
/// All elements must be either 0 or -1.
|
/// All elements must be either 0 or -1.
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use = "method returns a new mask and does not mutate the original value"]
|
#[must_use = "method returns a new mask and does not mutate the original value"]
|
||||||
pub unsafe fn from_int_unchecked(value: Simd<T, N>) -> Self {
|
pub unsafe fn from_simd_unchecked(value: Simd<T, N>) -> Self {
|
||||||
// Safety: the caller must confirm this invariant
|
// Safety: the caller must confirm this invariant
|
||||||
unsafe {
|
unsafe {
|
||||||
core::intrinsics::assume(<T as Sealed>::valid(value));
|
core::intrinsics::assume(<T as Sealed>::valid(value));
|
||||||
Self(mask_impl::Mask::from_int_unchecked(value))
|
|
||||||
}
|
}
|
||||||
|
Self(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts a vector of integers to a mask, where 0 represents `false` and -1
|
/// Converts a vector of integers to a mask, where 0 represents `false` and -1
|
||||||
|
|
@ -203,25 +209,26 @@ where
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use = "method returns a new mask and does not mutate the original value"]
|
#[must_use = "method returns a new mask and does not mutate the original value"]
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn from_int(value: Simd<T, N>) -> Self {
|
pub fn from_simd(value: Simd<T, N>) -> Self {
|
||||||
assert!(T::valid(value), "all values must be either 0 or -1",);
|
assert!(T::valid(value), "all values must be either 0 or -1",);
|
||||||
// Safety: the validity has been checked
|
// Safety: the validity has been checked
|
||||||
unsafe { Self::from_int_unchecked(value) }
|
unsafe { Self::from_simd_unchecked(value) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts the mask to a vector of integers, where 0 represents `false` and -1
|
/// Converts the mask to a vector of integers, where 0 represents `false` and -1
|
||||||
/// represents `true`.
|
/// represents `true`.
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use = "method returns a new vector and does not mutate the original value"]
|
#[must_use = "method returns a new vector and does not mutate the original value"]
|
||||||
pub fn to_int(self) -> Simd<T, N> {
|
pub fn to_simd(self) -> Simd<T, N> {
|
||||||
self.0.to_int()
|
self.0
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts the mask to a mask of any other element size.
|
/// Converts the mask to a mask of any other element size.
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use = "method returns a new mask and does not mutate the original value"]
|
#[must_use = "method returns a new mask and does not mutate the original value"]
|
||||||
pub fn cast<U: MaskElement>(self) -> Mask<U, N> {
|
pub fn cast<U: MaskElement>(self) -> Mask<U, N> {
|
||||||
Mask(self.0.convert())
|
// Safety: mask elements are integers
|
||||||
|
unsafe { Mask(core::intrinsics::simd::simd_as(self.0)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tests the value of the specified element.
|
/// Tests the value of the specified element.
|
||||||
|
|
@ -232,7 +239,7 @@ where
|
||||||
#[must_use = "method returns a new bool and does not mutate the original value"]
|
#[must_use = "method returns a new bool and does not mutate the original value"]
|
||||||
pub unsafe fn test_unchecked(&self, index: usize) -> bool {
|
pub unsafe fn test_unchecked(&self, index: usize) -> bool {
|
||||||
// Safety: the caller must confirm this invariant
|
// Safety: the caller must confirm this invariant
|
||||||
unsafe { self.0.test_unchecked(index) }
|
unsafe { T::eq(*self.0.as_array().get_unchecked(index), T::TRUE) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tests the value of the specified element.
|
/// Tests the value of the specified element.
|
||||||
|
|
@ -243,9 +250,7 @@ where
|
||||||
#[must_use = "method returns a new bool and does not mutate the original value"]
|
#[must_use = "method returns a new bool and does not mutate the original value"]
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn test(&self, index: usize) -> bool {
|
pub fn test(&self, index: usize) -> bool {
|
||||||
assert!(index < N, "element index out of range");
|
T::eq(self.0[index], T::TRUE)
|
||||||
// Safety: the element index has been checked
|
|
||||||
unsafe { self.test_unchecked(index) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the value of the specified element.
|
/// Sets the value of the specified element.
|
||||||
|
|
@ -256,7 +261,7 @@ where
|
||||||
pub unsafe fn set_unchecked(&mut self, index: usize, value: bool) {
|
pub unsafe fn set_unchecked(&mut self, index: usize, value: bool) {
|
||||||
// Safety: the caller must confirm this invariant
|
// Safety: the caller must confirm this invariant
|
||||||
unsafe {
|
unsafe {
|
||||||
self.0.set_unchecked(index, value);
|
*self.0.as_mut_array().get_unchecked_mut(index) = if value { T::TRUE } else { T::FALSE }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -267,35 +272,65 @@ where
|
||||||
#[inline]
|
#[inline]
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn set(&mut self, index: usize, value: bool) {
|
pub fn set(&mut self, index: usize, value: bool) {
|
||||||
assert!(index < N, "element index out of range");
|
self.0[index] = if value { T::TRUE } else { T::FALSE }
|
||||||
// Safety: the element index has been checked
|
|
||||||
unsafe {
|
|
||||||
self.set_unchecked(index, value);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if any element is set, or false otherwise.
|
/// Returns true if any element is set, or false otherwise.
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use = "method returns a new bool and does not mutate the original value"]
|
#[must_use = "method returns a new bool and does not mutate the original value"]
|
||||||
pub fn any(self) -> bool {
|
pub fn any(self) -> bool {
|
||||||
self.0.any()
|
// Safety: `self` is a mask vector
|
||||||
|
unsafe { core::intrinsics::simd::simd_reduce_any(self.0) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if all elements are set, or false otherwise.
|
/// Returns true if all elements are set, or false otherwise.
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use = "method returns a new bool and does not mutate the original value"]
|
#[must_use = "method returns a new bool and does not mutate the original value"]
|
||||||
pub fn all(self) -> bool {
|
pub fn all(self) -> bool {
|
||||||
self.0.all()
|
// Safety: `self` is a mask vector
|
||||||
|
unsafe { core::intrinsics::simd::simd_reduce_all(self.0) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a bitmask from a mask.
|
/// Creates a bitmask from a mask.
|
||||||
///
|
///
|
||||||
/// Each bit is set if the corresponding element in the mask is `true`.
|
/// Each bit is set if the corresponding element in the mask is `true`.
|
||||||
/// If the mask contains more than 64 elements, the bitmask is truncated to the first 64.
|
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use = "method returns a new integer and does not mutate the original value"]
|
#[must_use = "method returns a new integer and does not mutate the original value"]
|
||||||
pub fn to_bitmask(self) -> u64 {
|
pub fn to_bitmask(self) -> u64 {
|
||||||
self.0.to_bitmask_integer()
|
const {
|
||||||
|
assert!(N <= 64, "number of elements can't be greater than 64");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
unsafe fn to_bitmask_impl<T, U: FixEndianness, const M: usize, const N: usize>(
|
||||||
|
mask: Mask<T, N>,
|
||||||
|
) -> U
|
||||||
|
where
|
||||||
|
T: MaskElement,
|
||||||
|
{
|
||||||
|
let resized = mask.resize::<M>(false);
|
||||||
|
|
||||||
|
// Safety: `resized` is an integer vector with length M, which must match T
|
||||||
|
let bitmask: U = unsafe { core::intrinsics::simd::simd_bitmask(resized.0) };
|
||||||
|
|
||||||
|
// LLVM assumes bit order should match endianness
|
||||||
|
bitmask.fix_endianness()
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO modify simd_bitmask to zero-extend output, making this unnecessary
|
||||||
|
if N <= 8 {
|
||||||
|
// Safety: bitmask matches length
|
||||||
|
unsafe { to_bitmask_impl::<T, u8, 8, N>(self) as u64 }
|
||||||
|
} else if N <= 16 {
|
||||||
|
// Safety: bitmask matches length
|
||||||
|
unsafe { to_bitmask_impl::<T, u16, 16, N>(self) as u64 }
|
||||||
|
} else if N <= 32 {
|
||||||
|
// Safety: bitmask matches length
|
||||||
|
unsafe { to_bitmask_impl::<T, u32, 32, N>(self) as u64 }
|
||||||
|
} else {
|
||||||
|
// Safety: bitmask matches length
|
||||||
|
unsafe { to_bitmask_impl::<T, u64, 64, N>(self) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a mask from a bitmask.
|
/// Creates a mask from a bitmask.
|
||||||
|
|
@ -305,7 +340,7 @@ where
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use = "method returns a new mask and does not mutate the original value"]
|
#[must_use = "method returns a new mask and does not mutate the original value"]
|
||||||
pub fn from_bitmask(bitmask: u64) -> Self {
|
pub fn from_bitmask(bitmask: u64) -> Self {
|
||||||
Self(mask_impl::Mask::from_bitmask_integer(bitmask))
|
Self(bitmask.select(Simd::splat(T::TRUE), Simd::splat(T::FALSE)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Finds the index of the first set element.
|
/// Finds the index of the first set element.
|
||||||
|
|
@ -351,7 +386,7 @@ where
|
||||||
// Safety: the input and output are integer vectors
|
// Safety: the input and output are integer vectors
|
||||||
let index: Simd<T, N> = unsafe { core::intrinsics::simd::simd_cast(index) };
|
let index: Simd<T, N> = unsafe { core::intrinsics::simd::simd_cast(index) };
|
||||||
|
|
||||||
let masked_index = self.select(index, Self::splat(true).to_int());
|
let masked_index = self.select(index, Self::splat(true).to_simd());
|
||||||
|
|
||||||
// Safety: the input and output are integer vectors
|
// Safety: the input and output are integer vectors
|
||||||
let masked_index: Simd<T::Unsigned, N> =
|
let masked_index: Simd<T::Unsigned, N> =
|
||||||
|
|
@ -376,7 +411,6 @@ where
|
||||||
impl<T, const N: usize> From<[bool; N]> for Mask<T, N>
|
impl<T, const N: usize> From<[bool; N]> for Mask<T, N>
|
||||||
where
|
where
|
||||||
T: MaskElement,
|
T: MaskElement,
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(array: [bool; N]) -> Self {
|
fn from(array: [bool; N]) -> Self {
|
||||||
|
|
@ -387,7 +421,6 @@ where
|
||||||
impl<T, const N: usize> From<Mask<T, N>> for [bool; N]
|
impl<T, const N: usize> From<Mask<T, N>> for [bool; N]
|
||||||
where
|
where
|
||||||
T: MaskElement,
|
T: MaskElement,
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(vector: Mask<T, N>) -> Self {
|
fn from(vector: Mask<T, N>) -> Self {
|
||||||
|
|
@ -398,7 +431,6 @@ where
|
||||||
impl<T, const N: usize> Default for Mask<T, N>
|
impl<T, const N: usize> Default for Mask<T, N>
|
||||||
where
|
where
|
||||||
T: MaskElement,
|
T: MaskElement,
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
|
|
@ -409,7 +441,6 @@ where
|
||||||
impl<T, const N: usize> PartialEq for Mask<T, N>
|
impl<T, const N: usize> PartialEq for Mask<T, N>
|
||||||
where
|
where
|
||||||
T: MaskElement + PartialEq,
|
T: MaskElement + PartialEq,
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
|
@ -420,7 +451,6 @@ where
|
||||||
impl<T, const N: usize> PartialOrd for Mask<T, N>
|
impl<T, const N: usize> PartialOrd for Mask<T, N>
|
||||||
where
|
where
|
||||||
T: MaskElement + PartialOrd,
|
T: MaskElement + PartialOrd,
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||||
|
|
@ -431,7 +461,6 @@ where
|
||||||
impl<T, const N: usize> fmt::Debug for Mask<T, N>
|
impl<T, const N: usize> fmt::Debug for Mask<T, N>
|
||||||
where
|
where
|
||||||
T: MaskElement + fmt::Debug,
|
T: MaskElement + fmt::Debug,
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
|
@ -444,19 +473,18 @@ where
|
||||||
impl<T, const N: usize> core::ops::BitAnd for Mask<T, N>
|
impl<T, const N: usize> core::ops::BitAnd for Mask<T, N>
|
||||||
where
|
where
|
||||||
T: MaskElement,
|
T: MaskElement,
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
{
|
||||||
type Output = Self;
|
type Output = Self;
|
||||||
#[inline]
|
#[inline]
|
||||||
fn bitand(self, rhs: Self) -> Self {
|
fn bitand(self, rhs: Self) -> Self {
|
||||||
Self(self.0 & rhs.0)
|
// Safety: `self` is an integer vector
|
||||||
|
unsafe { Self(core::intrinsics::simd::simd_and(self.0, rhs.0)) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, const N: usize> core::ops::BitAnd<bool> for Mask<T, N>
|
impl<T, const N: usize> core::ops::BitAnd<bool> for Mask<T, N>
|
||||||
where
|
where
|
||||||
T: MaskElement,
|
T: MaskElement,
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
{
|
||||||
type Output = Self;
|
type Output = Self;
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
@ -468,7 +496,6 @@ where
|
||||||
impl<T, const N: usize> core::ops::BitAnd<Mask<T, N>> for bool
|
impl<T, const N: usize> core::ops::BitAnd<Mask<T, N>> for bool
|
||||||
where
|
where
|
||||||
T: MaskElement,
|
T: MaskElement,
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
{
|
||||||
type Output = Mask<T, N>;
|
type Output = Mask<T, N>;
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
@ -480,19 +507,18 @@ where
|
||||||
impl<T, const N: usize> core::ops::BitOr for Mask<T, N>
|
impl<T, const N: usize> core::ops::BitOr for Mask<T, N>
|
||||||
where
|
where
|
||||||
T: MaskElement,
|
T: MaskElement,
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
{
|
||||||
type Output = Self;
|
type Output = Self;
|
||||||
#[inline]
|
#[inline]
|
||||||
fn bitor(self, rhs: Self) -> Self {
|
fn bitor(self, rhs: Self) -> Self {
|
||||||
Self(self.0 | rhs.0)
|
// Safety: `self` is an integer vector
|
||||||
|
unsafe { Self(core::intrinsics::simd::simd_or(self.0, rhs.0)) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, const N: usize> core::ops::BitOr<bool> for Mask<T, N>
|
impl<T, const N: usize> core::ops::BitOr<bool> for Mask<T, N>
|
||||||
where
|
where
|
||||||
T: MaskElement,
|
T: MaskElement,
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
{
|
||||||
type Output = Self;
|
type Output = Self;
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
@ -504,7 +530,6 @@ where
|
||||||
impl<T, const N: usize> core::ops::BitOr<Mask<T, N>> for bool
|
impl<T, const N: usize> core::ops::BitOr<Mask<T, N>> for bool
|
||||||
where
|
where
|
||||||
T: MaskElement,
|
T: MaskElement,
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
{
|
||||||
type Output = Mask<T, N>;
|
type Output = Mask<T, N>;
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
@ -516,19 +541,18 @@ where
|
||||||
impl<T, const N: usize> core::ops::BitXor for Mask<T, N>
|
impl<T, const N: usize> core::ops::BitXor for Mask<T, N>
|
||||||
where
|
where
|
||||||
T: MaskElement,
|
T: MaskElement,
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
{
|
||||||
type Output = Self;
|
type Output = Self;
|
||||||
#[inline]
|
#[inline]
|
||||||
fn bitxor(self, rhs: Self) -> Self::Output {
|
fn bitxor(self, rhs: Self) -> Self::Output {
|
||||||
Self(self.0 ^ rhs.0)
|
// Safety: `self` is an integer vector
|
||||||
|
unsafe { Self(core::intrinsics::simd::simd_xor(self.0, rhs.0)) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, const N: usize> core::ops::BitXor<bool> for Mask<T, N>
|
impl<T, const N: usize> core::ops::BitXor<bool> for Mask<T, N>
|
||||||
where
|
where
|
||||||
T: MaskElement,
|
T: MaskElement,
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
{
|
||||||
type Output = Self;
|
type Output = Self;
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
@ -540,7 +564,6 @@ where
|
||||||
impl<T, const N: usize> core::ops::BitXor<Mask<T, N>> for bool
|
impl<T, const N: usize> core::ops::BitXor<Mask<T, N>> for bool
|
||||||
where
|
where
|
||||||
T: MaskElement,
|
T: MaskElement,
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
{
|
||||||
type Output = Mask<T, N>;
|
type Output = Mask<T, N>;
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
@ -552,30 +575,27 @@ where
|
||||||
impl<T, const N: usize> core::ops::Not for Mask<T, N>
|
impl<T, const N: usize> core::ops::Not for Mask<T, N>
|
||||||
where
|
where
|
||||||
T: MaskElement,
|
T: MaskElement,
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
{
|
||||||
type Output = Mask<T, N>;
|
type Output = Mask<T, N>;
|
||||||
#[inline]
|
#[inline]
|
||||||
fn not(self) -> Self::Output {
|
fn not(self) -> Self::Output {
|
||||||
Self(!self.0)
|
Self::splat(true) ^ self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, const N: usize> core::ops::BitAndAssign for Mask<T, N>
|
impl<T, const N: usize> core::ops::BitAndAssign for Mask<T, N>
|
||||||
where
|
where
|
||||||
T: MaskElement,
|
T: MaskElement,
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
fn bitand_assign(&mut self, rhs: Self) {
|
fn bitand_assign(&mut self, rhs: Self) {
|
||||||
self.0 = self.0 & rhs.0;
|
*self = *self & rhs;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, const N: usize> core::ops::BitAndAssign<bool> for Mask<T, N>
|
impl<T, const N: usize> core::ops::BitAndAssign<bool> for Mask<T, N>
|
||||||
where
|
where
|
||||||
T: MaskElement,
|
T: MaskElement,
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
fn bitand_assign(&mut self, rhs: bool) {
|
fn bitand_assign(&mut self, rhs: bool) {
|
||||||
|
|
@ -586,18 +606,16 @@ where
|
||||||
impl<T, const N: usize> core::ops::BitOrAssign for Mask<T, N>
|
impl<T, const N: usize> core::ops::BitOrAssign for Mask<T, N>
|
||||||
where
|
where
|
||||||
T: MaskElement,
|
T: MaskElement,
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
fn bitor_assign(&mut self, rhs: Self) {
|
fn bitor_assign(&mut self, rhs: Self) {
|
||||||
self.0 = self.0 | rhs.0;
|
*self = *self | rhs;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, const N: usize> core::ops::BitOrAssign<bool> for Mask<T, N>
|
impl<T, const N: usize> core::ops::BitOrAssign<bool> for Mask<T, N>
|
||||||
where
|
where
|
||||||
T: MaskElement,
|
T: MaskElement,
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
fn bitor_assign(&mut self, rhs: bool) {
|
fn bitor_assign(&mut self, rhs: bool) {
|
||||||
|
|
@ -608,18 +626,16 @@ where
|
||||||
impl<T, const N: usize> core::ops::BitXorAssign for Mask<T, N>
|
impl<T, const N: usize> core::ops::BitXorAssign for Mask<T, N>
|
||||||
where
|
where
|
||||||
T: MaskElement,
|
T: MaskElement,
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
fn bitxor_assign(&mut self, rhs: Self) {
|
fn bitxor_assign(&mut self, rhs: Self) {
|
||||||
self.0 = self.0 ^ rhs.0;
|
*self = *self ^ rhs;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, const N: usize> core::ops::BitXorAssign<bool> for Mask<T, N>
|
impl<T, const N: usize> core::ops::BitXorAssign<bool> for Mask<T, N>
|
||||||
where
|
where
|
||||||
T: MaskElement,
|
T: MaskElement,
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
fn bitxor_assign(&mut self, rhs: bool) {
|
fn bitxor_assign(&mut self, rhs: bool) {
|
||||||
|
|
@ -631,8 +647,6 @@ macro_rules! impl_from {
|
||||||
{ $from:ty => $($to:ty),* } => {
|
{ $from:ty => $($to:ty),* } => {
|
||||||
$(
|
$(
|
||||||
impl<const N: usize> From<Mask<$from, N>> for Mask<$to, N>
|
impl<const N: usize> From<Mask<$from, N>> for Mask<$to, N>
|
||||||
where
|
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(value: Mask<$from, N>) -> Self {
|
fn from(value: Mask<$from, N>) -> Self {
|
||||||
|
|
|
||||||
|
|
@ -1,228 +0,0 @@
|
||||||
#![allow(unused_imports)]
|
|
||||||
use super::MaskElement;
|
|
||||||
use crate::simd::{LaneCount, Simd, SupportedLaneCount};
|
|
||||||
use core::marker::PhantomData;
|
|
||||||
|
|
||||||
/// A mask where each lane is represented by a single bit.
|
|
||||||
#[repr(transparent)]
|
|
||||||
pub(crate) struct Mask<T, const N: usize>(
|
|
||||||
<LaneCount<N> as SupportedLaneCount>::BitMask,
|
|
||||||
PhantomData<T>,
|
|
||||||
)
|
|
||||||
where
|
|
||||||
T: MaskElement,
|
|
||||||
LaneCount<N>: SupportedLaneCount;
|
|
||||||
|
|
||||||
impl<T, const N: usize> Copy for Mask<T, N>
|
|
||||||
where
|
|
||||||
T: MaskElement,
|
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, const N: usize> Clone for Mask<T, N>
|
|
||||||
where
|
|
||||||
T: MaskElement,
|
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
|
||||||
#[inline]
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
*self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, const N: usize> PartialEq for Mask<T, N>
|
|
||||||
where
|
|
||||||
T: MaskElement,
|
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
|
||||||
#[inline]
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
self.0.as_ref() == other.0.as_ref()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, const N: usize> PartialOrd for Mask<T, N>
|
|
||||||
where
|
|
||||||
T: MaskElement,
|
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
|
||||||
#[inline]
|
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
|
|
||||||
self.0.as_ref().partial_cmp(other.0.as_ref())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, const N: usize> Eq for Mask<T, N>
|
|
||||||
where
|
|
||||||
T: MaskElement,
|
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, const N: usize> Ord for Mask<T, N>
|
|
||||||
where
|
|
||||||
T: MaskElement,
|
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
|
||||||
#[inline]
|
|
||||||
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
|
|
||||||
self.0.as_ref().cmp(other.0.as_ref())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, const N: usize> Mask<T, N>
|
|
||||||
where
|
|
||||||
T: MaskElement,
|
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
|
||||||
#[inline]
|
|
||||||
#[must_use = "method returns a new mask and does not mutate the original value"]
|
|
||||||
pub(crate) fn splat(value: bool) -> Self {
|
|
||||||
let mut mask = <LaneCount<N> as SupportedLaneCount>::BitMask::default();
|
|
||||||
if value {
|
|
||||||
mask.as_mut().fill(u8::MAX)
|
|
||||||
} else {
|
|
||||||
mask.as_mut().fill(u8::MIN)
|
|
||||||
}
|
|
||||||
if N % 8 > 0 {
|
|
||||||
*mask.as_mut().last_mut().unwrap() &= u8::MAX >> (8 - N % 8);
|
|
||||||
}
|
|
||||||
Self(mask, PhantomData)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
#[must_use = "method returns a new bool and does not mutate the original value"]
|
|
||||||
pub(crate) unsafe fn test_unchecked(&self, lane: usize) -> bool {
|
|
||||||
(self.0.as_ref()[lane / 8] >> (lane % 8)) & 0x1 > 0
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub(crate) unsafe fn set_unchecked(&mut self, lane: usize, value: bool) {
|
|
||||||
unsafe {
|
|
||||||
self.0.as_mut()[lane / 8] ^= ((value ^ self.test_unchecked(lane)) as u8) << (lane % 8)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
#[must_use = "method returns a new vector and does not mutate the original value"]
|
|
||||||
pub(crate) fn to_int(self) -> Simd<T, N> {
|
|
||||||
unsafe {
|
|
||||||
core::intrinsics::simd::simd_select_bitmask(
|
|
||||||
self.0,
|
|
||||||
Simd::splat(T::TRUE),
|
|
||||||
Simd::splat(T::FALSE),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
#[must_use = "method returns a new mask and does not mutate the original value"]
|
|
||||||
pub(crate) unsafe fn from_int_unchecked(value: Simd<T, N>) -> Self {
|
|
||||||
unsafe { Self(core::intrinsics::simd::simd_bitmask(value), PhantomData) }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub(crate) fn to_bitmask_integer(self) -> u64 {
|
|
||||||
let mut bitmask = [0u8; 8];
|
|
||||||
bitmask[..self.0.as_ref().len()].copy_from_slice(self.0.as_ref());
|
|
||||||
u64::from_ne_bytes(bitmask)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub(crate) fn from_bitmask_integer(bitmask: u64) -> Self {
|
|
||||||
let mut bytes = <LaneCount<N> as SupportedLaneCount>::BitMask::default();
|
|
||||||
let len = bytes.as_mut().len();
|
|
||||||
bytes
|
|
||||||
.as_mut()
|
|
||||||
.copy_from_slice(&bitmask.to_ne_bytes()[..len]);
|
|
||||||
Self(bytes, PhantomData)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
#[must_use = "method returns a new mask and does not mutate the original value"]
|
|
||||||
pub(crate) fn convert<U>(self) -> Mask<U, N>
|
|
||||||
where
|
|
||||||
U: MaskElement,
|
|
||||||
{
|
|
||||||
// Safety: bitmask layout does not depend on the element width
|
|
||||||
unsafe { core::mem::transmute_copy(&self) }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
#[must_use = "method returns a new bool and does not mutate the original value"]
|
|
||||||
pub(crate) fn any(self) -> bool {
|
|
||||||
self != Self::splat(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
#[must_use = "method returns a new bool and does not mutate the original value"]
|
|
||||||
pub(crate) fn all(self) -> bool {
|
|
||||||
self == Self::splat(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, const N: usize> core::ops::BitAnd for Mask<T, N>
|
|
||||||
where
|
|
||||||
T: MaskElement,
|
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
<LaneCount<N> as SupportedLaneCount>::BitMask: AsRef<[u8]> + AsMut<[u8]>,
|
|
||||||
{
|
|
||||||
type Output = Self;
|
|
||||||
#[inline]
|
|
||||||
fn bitand(mut self, rhs: Self) -> Self {
|
|
||||||
for (l, r) in self.0.as_mut().iter_mut().zip(rhs.0.as_ref().iter()) {
|
|
||||||
*l &= r;
|
|
||||||
}
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, const N: usize> core::ops::BitOr for Mask<T, N>
|
|
||||||
where
|
|
||||||
T: MaskElement,
|
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
<LaneCount<N> as SupportedLaneCount>::BitMask: AsRef<[u8]> + AsMut<[u8]>,
|
|
||||||
{
|
|
||||||
type Output = Self;
|
|
||||||
#[inline]
|
|
||||||
fn bitor(mut self, rhs: Self) -> Self {
|
|
||||||
for (l, r) in self.0.as_mut().iter_mut().zip(rhs.0.as_ref().iter()) {
|
|
||||||
*l |= r;
|
|
||||||
}
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, const N: usize> core::ops::BitXor for Mask<T, N>
|
|
||||||
where
|
|
||||||
T: MaskElement,
|
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
|
||||||
type Output = Self;
|
|
||||||
#[inline]
|
|
||||||
fn bitxor(mut self, rhs: Self) -> Self::Output {
|
|
||||||
for (l, r) in self.0.as_mut().iter_mut().zip(rhs.0.as_ref().iter()) {
|
|
||||||
*l ^= r;
|
|
||||||
}
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, const N: usize> core::ops::Not for Mask<T, N>
|
|
||||||
where
|
|
||||||
T: MaskElement,
|
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
|
||||||
type Output = Self;
|
|
||||||
#[inline]
|
|
||||||
fn not(mut self) -> Self::Output {
|
|
||||||
for x in self.0.as_mut() {
|
|
||||||
*x = !*x;
|
|
||||||
}
|
|
||||||
if N % 8 > 0 {
|
|
||||||
*self.0.as_mut().last_mut().unwrap() &= u8::MAX >> (8 - N % 8);
|
|
||||||
}
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,296 +0,0 @@
|
||||||
//! Masks that take up full SIMD vector registers.
|
|
||||||
|
|
||||||
use crate::simd::{LaneCount, MaskElement, Simd, SupportedLaneCount};
|
|
||||||
|
|
||||||
#[repr(transparent)]
|
|
||||||
pub(crate) struct Mask<T, const N: usize>(Simd<T, N>)
|
|
||||||
where
|
|
||||||
T: MaskElement,
|
|
||||||
LaneCount<N>: SupportedLaneCount;
|
|
||||||
|
|
||||||
impl<T, const N: usize> Copy for Mask<T, N>
|
|
||||||
where
|
|
||||||
T: MaskElement,
|
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, const N: usize> Clone for Mask<T, N>
|
|
||||||
where
|
|
||||||
T: MaskElement,
|
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
|
||||||
#[inline]
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
*self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, const N: usize> PartialEq for Mask<T, N>
|
|
||||||
where
|
|
||||||
T: MaskElement + PartialEq,
|
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
|
||||||
#[inline]
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
self.0.eq(&other.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, const N: usize> PartialOrd for Mask<T, N>
|
|
||||||
where
|
|
||||||
T: MaskElement + PartialOrd,
|
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
|
||||||
#[inline]
|
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
|
|
||||||
self.0.partial_cmp(&other.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, const N: usize> Eq for Mask<T, N>
|
|
||||||
where
|
|
||||||
T: MaskElement + Eq,
|
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, const N: usize> Ord for Mask<T, N>
|
|
||||||
where
|
|
||||||
T: MaskElement + Ord,
|
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
|
||||||
#[inline]
|
|
||||||
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
|
|
||||||
self.0.cmp(&other.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Used for bitmask bit order workaround
|
|
||||||
pub(crate) trait ReverseBits {
|
|
||||||
// Reverse the least significant `n` bits of `self`.
|
|
||||||
// (Remaining bits must be 0.)
|
|
||||||
fn reverse_bits(self, n: usize) -> Self;
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! impl_reverse_bits {
|
|
||||||
{ $($int:ty),* } => {
|
|
||||||
$(
|
|
||||||
impl ReverseBits for $int {
|
|
||||||
#[inline(always)]
|
|
||||||
fn reverse_bits(self, n: usize) -> Self {
|
|
||||||
let rev = <$int>::reverse_bits(self);
|
|
||||||
let bitsize = size_of::<$int>() * 8;
|
|
||||||
if n < bitsize {
|
|
||||||
// Shift things back to the right
|
|
||||||
rev >> (bitsize - n)
|
|
||||||
} else {
|
|
||||||
rev
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_reverse_bits! { u8, u16, u32, u64 }
|
|
||||||
|
|
||||||
impl<T, const N: usize> Mask<T, N>
|
|
||||||
where
|
|
||||||
T: MaskElement,
|
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
|
||||||
#[inline]
|
|
||||||
#[must_use = "method returns a new mask and does not mutate the original value"]
|
|
||||||
pub(crate) fn splat(value: bool) -> Self {
|
|
||||||
Self(Simd::splat(if value { T::TRUE } else { T::FALSE }))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
#[must_use = "method returns a new bool and does not mutate the original value"]
|
|
||||||
pub(crate) unsafe fn test_unchecked(&self, lane: usize) -> bool {
|
|
||||||
T::eq(self.0[lane], T::TRUE)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub(crate) unsafe fn set_unchecked(&mut self, lane: usize, value: bool) {
|
|
||||||
self.0[lane] = if value { T::TRUE } else { T::FALSE }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
#[must_use = "method returns a new vector and does not mutate the original value"]
|
|
||||||
pub(crate) fn to_int(self) -> Simd<T, N> {
|
|
||||||
self.0
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
#[must_use = "method returns a new mask and does not mutate the original value"]
|
|
||||||
pub(crate) unsafe fn from_int_unchecked(value: Simd<T, N>) -> Self {
|
|
||||||
Self(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
#[must_use = "method returns a new mask and does not mutate the original value"]
|
|
||||||
pub(crate) fn convert<U>(self) -> Mask<U, N>
|
|
||||||
where
|
|
||||||
U: MaskElement,
|
|
||||||
{
|
|
||||||
// Safety: masks are simply integer vectors of 0 and -1, and we can cast the element type.
|
|
||||||
unsafe { Mask(core::intrinsics::simd::simd_cast(self.0)) }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
unsafe fn to_bitmask_impl<U: ReverseBits, const M: usize>(self) -> U
|
|
||||||
where
|
|
||||||
LaneCount<M>: SupportedLaneCount,
|
|
||||||
{
|
|
||||||
let resized = self.to_int().resize::<M>(T::FALSE);
|
|
||||||
|
|
||||||
// Safety: `resized` is an integer vector with length M, which must match T
|
|
||||||
let bitmask: U = unsafe { core::intrinsics::simd::simd_bitmask(resized) };
|
|
||||||
|
|
||||||
// LLVM assumes bit order should match endianness
|
|
||||||
if cfg!(target_endian = "big") {
|
|
||||||
bitmask.reverse_bits(M)
|
|
||||||
} else {
|
|
||||||
bitmask
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
unsafe fn from_bitmask_impl<U: ReverseBits, const M: usize>(bitmask: U) -> Self
|
|
||||||
where
|
|
||||||
LaneCount<M>: SupportedLaneCount,
|
|
||||||
{
|
|
||||||
// LLVM assumes bit order should match endianness
|
|
||||||
let bitmask = if cfg!(target_endian = "big") {
|
|
||||||
bitmask.reverse_bits(M)
|
|
||||||
} else {
|
|
||||||
bitmask
|
|
||||||
};
|
|
||||||
|
|
||||||
// SAFETY: `mask` is the correct bitmask type for a u64 bitmask
|
|
||||||
let mask: Simd<T, M> = unsafe {
|
|
||||||
core::intrinsics::simd::simd_select_bitmask(
|
|
||||||
bitmask,
|
|
||||||
Simd::<T, M>::splat(T::TRUE),
|
|
||||||
Simd::<T, M>::splat(T::FALSE),
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
// SAFETY: `mask` only contains `T::TRUE` or `T::FALSE`
|
|
||||||
unsafe { Self::from_int_unchecked(mask.resize::<N>(T::FALSE)) }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub(crate) fn to_bitmask_integer(self) -> u64 {
|
|
||||||
// TODO modify simd_bitmask to zero-extend output, making this unnecessary
|
|
||||||
if N <= 8 {
|
|
||||||
// Safety: bitmask matches length
|
|
||||||
unsafe { self.to_bitmask_impl::<u8, 8>() as u64 }
|
|
||||||
} else if N <= 16 {
|
|
||||||
// Safety: bitmask matches length
|
|
||||||
unsafe { self.to_bitmask_impl::<u16, 16>() as u64 }
|
|
||||||
} else if N <= 32 {
|
|
||||||
// Safety: bitmask matches length
|
|
||||||
unsafe { self.to_bitmask_impl::<u32, 32>() as u64 }
|
|
||||||
} else {
|
|
||||||
// Safety: bitmask matches length
|
|
||||||
unsafe { self.to_bitmask_impl::<u64, 64>() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub(crate) fn from_bitmask_integer(bitmask: u64) -> Self {
|
|
||||||
// TODO modify simd_bitmask_select to truncate input, making this unnecessary
|
|
||||||
if N <= 8 {
|
|
||||||
// Safety: bitmask matches length
|
|
||||||
unsafe { Self::from_bitmask_impl::<u8, 8>(bitmask as u8) }
|
|
||||||
} else if N <= 16 {
|
|
||||||
// Safety: bitmask matches length
|
|
||||||
unsafe { Self::from_bitmask_impl::<u16, 16>(bitmask as u16) }
|
|
||||||
} else if N <= 32 {
|
|
||||||
// Safety: bitmask matches length
|
|
||||||
unsafe { Self::from_bitmask_impl::<u32, 32>(bitmask as u32) }
|
|
||||||
} else {
|
|
||||||
// Safety: bitmask matches length
|
|
||||||
unsafe { Self::from_bitmask_impl::<u64, 64>(bitmask) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
#[must_use = "method returns a new bool and does not mutate the original value"]
|
|
||||||
pub(crate) fn any(self) -> bool {
|
|
||||||
// Safety: use `self` as an integer vector
|
|
||||||
unsafe { core::intrinsics::simd::simd_reduce_any(self.to_int()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
#[must_use = "method returns a new bool and does not mutate the original value"]
|
|
||||||
pub(crate) fn all(self) -> bool {
|
|
||||||
// Safety: use `self` as an integer vector
|
|
||||||
unsafe { core::intrinsics::simd::simd_reduce_all(self.to_int()) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, const N: usize> From<Mask<T, N>> for Simd<T, N>
|
|
||||||
where
|
|
||||||
T: MaskElement,
|
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
|
||||||
#[inline]
|
|
||||||
fn from(value: Mask<T, N>) -> Self {
|
|
||||||
value.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, const N: usize> core::ops::BitAnd for Mask<T, N>
|
|
||||||
where
|
|
||||||
T: MaskElement,
|
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
|
||||||
type Output = Self;
|
|
||||||
#[inline]
|
|
||||||
fn bitand(self, rhs: Self) -> Self {
|
|
||||||
// Safety: `self` is an integer vector
|
|
||||||
unsafe { Self(core::intrinsics::simd::simd_and(self.0, rhs.0)) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, const N: usize> core::ops::BitOr for Mask<T, N>
|
|
||||||
where
|
|
||||||
T: MaskElement,
|
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
|
||||||
type Output = Self;
|
|
||||||
#[inline]
|
|
||||||
fn bitor(self, rhs: Self) -> Self {
|
|
||||||
// Safety: `self` is an integer vector
|
|
||||||
unsafe { Self(core::intrinsics::simd::simd_or(self.0, rhs.0)) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, const N: usize> core::ops::BitXor for Mask<T, N>
|
|
||||||
where
|
|
||||||
T: MaskElement,
|
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
|
||||||
type Output = Self;
|
|
||||||
#[inline]
|
|
||||||
fn bitxor(self, rhs: Self) -> Self {
|
|
||||||
// Safety: `self` is an integer vector
|
|
||||||
unsafe { Self(core::intrinsics::simd::simd_xor(self.0, rhs.0)) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, const N: usize> core::ops::Not for Mask<T, N>
|
|
||||||
where
|
|
||||||
T: MaskElement,
|
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
|
||||||
type Output = Self;
|
|
||||||
#[inline]
|
|
||||||
fn not(self) -> Self::Output {
|
|
||||||
Self::splat(true) ^ self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -5,7 +5,6 @@ mod alias;
|
||||||
mod cast;
|
mod cast;
|
||||||
mod fmt;
|
mod fmt;
|
||||||
mod iter;
|
mod iter;
|
||||||
mod lane_count;
|
|
||||||
mod masks;
|
mod masks;
|
||||||
mod ops;
|
mod ops;
|
||||||
mod select;
|
mod select;
|
||||||
|
|
@ -27,8 +26,8 @@ pub mod simd {
|
||||||
|
|
||||||
pub use crate::core_simd::alias::*;
|
pub use crate::core_simd::alias::*;
|
||||||
pub use crate::core_simd::cast::*;
|
pub use crate::core_simd::cast::*;
|
||||||
pub use crate::core_simd::lane_count::{LaneCount, SupportedLaneCount};
|
|
||||||
pub use crate::core_simd::masks::*;
|
pub use crate::core_simd::masks::*;
|
||||||
|
pub use crate::core_simd::select::*;
|
||||||
pub use crate::core_simd::swizzle::*;
|
pub use crate::core_simd::swizzle::*;
|
||||||
pub use crate::core_simd::to_bytes::ToBytes;
|
pub use crate::core_simd::to_bytes::ToBytes;
|
||||||
pub use crate::core_simd::vector::*;
|
pub use crate::core_simd::vector::*;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::simd::{LaneCount, Simd, SimdElement, SupportedLaneCount, cmp::SimdPartialEq};
|
use crate::simd::{Select, Simd, SimdElement, cmp::SimdPartialEq};
|
||||||
use core::ops::{Add, Mul};
|
use core::ops::{Add, Mul};
|
||||||
use core::ops::{BitAnd, BitOr, BitXor};
|
use core::ops::{BitAnd, BitOr, BitXor};
|
||||||
use core::ops::{Div, Rem, Sub};
|
use core::ops::{Div, Rem, Sub};
|
||||||
|
|
@ -12,7 +12,6 @@ mod unary;
|
||||||
impl<I, T, const N: usize> core::ops::Index<I> for Simd<T, N>
|
impl<I, T, const N: usize> core::ops::Index<I> for Simd<T, N>
|
||||||
where
|
where
|
||||||
T: SimdElement,
|
T: SimdElement,
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
I: core::slice::SliceIndex<[T]>,
|
I: core::slice::SliceIndex<[T]>,
|
||||||
{
|
{
|
||||||
type Output = I::Output;
|
type Output = I::Output;
|
||||||
|
|
@ -25,7 +24,6 @@ where
|
||||||
impl<I, T, const N: usize> core::ops::IndexMut<I> for Simd<T, N>
|
impl<I, T, const N: usize> core::ops::IndexMut<I> for Simd<T, N>
|
||||||
where
|
where
|
||||||
T: SimdElement,
|
T: SimdElement,
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
I: core::slice::SliceIndex<[T]>,
|
I: core::slice::SliceIndex<[T]>,
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
@ -130,7 +128,6 @@ macro_rules! for_base_types {
|
||||||
impl<const N: usize> $op<Self> for Simd<$scalar, N>
|
impl<const N: usize> $op<Self> for Simd<$scalar, N>
|
||||||
where
|
where
|
||||||
$scalar: SimdElement,
|
$scalar: SimdElement,
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
{
|
||||||
type Output = $out;
|
type Output = $out;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,6 @@ macro_rules! assign_ops {
|
||||||
where
|
where
|
||||||
Self: $trait<U, Output = Self>,
|
Self: $trait<U, Output = Self>,
|
||||||
T: SimdElement,
|
T: SimdElement,
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
fn $assign_call(&mut self, rhs: U) {
|
fn $assign_call(&mut self, rhs: U) {
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,6 @@ macro_rules! deref_lhs {
|
||||||
where
|
where
|
||||||
T: SimdElement,
|
T: SimdElement,
|
||||||
$simd: $trait<$simd, Output = $simd>,
|
$simd: $trait<$simd, Output = $simd>,
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
{
|
||||||
type Output = Simd<T, N>;
|
type Output = Simd<T, N>;
|
||||||
|
|
||||||
|
|
@ -33,7 +32,6 @@ macro_rules! deref_rhs {
|
||||||
where
|
where
|
||||||
T: SimdElement,
|
T: SimdElement,
|
||||||
$simd: $trait<$simd, Output = $simd>,
|
$simd: $trait<$simd, Output = $simd>,
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
{
|
||||||
type Output = Simd<T, N>;
|
type Output = Simd<T, N>;
|
||||||
|
|
||||||
|
|
@ -64,7 +62,6 @@ macro_rules! deref_ops {
|
||||||
where
|
where
|
||||||
T: SimdElement,
|
T: SimdElement,
|
||||||
$simd: $trait<$simd, Output = $simd>,
|
$simd: $trait<$simd, Output = $simd>,
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
{
|
||||||
type Output = $simd;
|
type Output = $simd;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,11 @@
|
||||||
// Shift operations uniquely typically only have a scalar on the right-hand side.
|
// Shift operations uniquely typically only have a scalar on the right-hand side.
|
||||||
// Here, we implement shifts for scalar RHS arguments.
|
// Here, we implement shifts for scalar RHS arguments.
|
||||||
|
|
||||||
use crate::simd::{LaneCount, Simd, SupportedLaneCount};
|
use crate::simd::Simd;
|
||||||
|
|
||||||
macro_rules! impl_splatted_shifts {
|
macro_rules! impl_splatted_shifts {
|
||||||
{ impl $trait:ident :: $trait_fn:ident for $ty:ty } => {
|
{ impl $trait:ident :: $trait_fn:ident for $ty:ty } => {
|
||||||
impl<const N: usize> core::ops::$trait<$ty> for Simd<$ty, N>
|
impl<const N: usize> core::ops::$trait<$ty> for Simd<$ty, N>
|
||||||
where
|
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
{
|
||||||
type Output = Self;
|
type Output = Self;
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
@ -17,8 +15,6 @@ macro_rules! impl_splatted_shifts {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<const N: usize> core::ops::$trait<&$ty> for Simd<$ty, N>
|
impl<const N: usize> core::ops::$trait<&$ty> for Simd<$ty, N>
|
||||||
where
|
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
{
|
||||||
type Output = Self;
|
type Output = Self;
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
@ -28,8 +24,6 @@ macro_rules! impl_splatted_shifts {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'lhs, const N: usize> core::ops::$trait<$ty> for &'lhs Simd<$ty, N>
|
impl<'lhs, const N: usize> core::ops::$trait<$ty> for &'lhs Simd<$ty, N>
|
||||||
where
|
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
{
|
||||||
type Output = Simd<$ty, N>;
|
type Output = Simd<$ty, N>;
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
@ -39,8 +33,6 @@ macro_rules! impl_splatted_shifts {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'lhs, const N: usize> core::ops::$trait<&$ty> for &'lhs Simd<$ty, N>
|
impl<'lhs, const N: usize> core::ops::$trait<&$ty> for &'lhs Simd<$ty, N>
|
||||||
where
|
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
{
|
||||||
type Output = Simd<$ty, N>;
|
type Output = Simd<$ty, N>;
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::simd::{LaneCount, Simd, SimdElement, SupportedLaneCount};
|
use crate::simd::{Simd, SimdElement};
|
||||||
use core::ops::{Neg, Not}; // unary ops
|
use core::ops::{Neg, Not}; // unary ops
|
||||||
|
|
||||||
macro_rules! neg {
|
macro_rules! neg {
|
||||||
|
|
@ -6,7 +6,6 @@ macro_rules! neg {
|
||||||
$(impl<const N: usize> Neg for Simd<$scalar, N>
|
$(impl<const N: usize> Neg for Simd<$scalar, N>
|
||||||
where
|
where
|
||||||
$scalar: SimdElement,
|
$scalar: SimdElement,
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
{
|
||||||
type Output = Self;
|
type Output = Self;
|
||||||
|
|
||||||
|
|
@ -40,7 +39,6 @@ macro_rules! not {
|
||||||
$(impl<const N: usize> Not for Simd<$scalar, N>
|
$(impl<const N: usize> Not for Simd<$scalar, N>
|
||||||
where
|
where
|
||||||
$scalar: SimdElement,
|
$scalar: SimdElement,
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
{
|
||||||
type Output = Self;
|
type Output = Self;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,54 +1,155 @@
|
||||||
use crate::simd::{LaneCount, Mask, MaskElement, Simd, SimdElement, SupportedLaneCount};
|
use crate::simd::{FixEndianness, Mask, MaskElement, Simd, SimdElement};
|
||||||
|
|
||||||
impl<T, const N: usize> Mask<T, N>
|
/// Choose elements from two vectors using a mask.
|
||||||
|
///
|
||||||
|
/// For each element in the mask, choose the corresponding element from `true_values` if
|
||||||
|
/// that element mask is true, and `false_values` if that element mask is false.
|
||||||
|
///
|
||||||
|
/// If the mask is `u64`, it's treated as a bitmask with the least significant bit
|
||||||
|
/// corresponding to the first element.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ## Selecting values from `Simd`
|
||||||
|
/// ```
|
||||||
|
/// # #![feature(portable_simd)]
|
||||||
|
/// # #[cfg(feature = "as_crate")] use core_simd::simd;
|
||||||
|
/// # #[cfg(not(feature = "as_crate"))] use core::simd;
|
||||||
|
/// # use simd::{Simd, Mask, Select};
|
||||||
|
/// let a = Simd::from_array([0, 1, 2, 3]);
|
||||||
|
/// let b = Simd::from_array([4, 5, 6, 7]);
|
||||||
|
/// let mask = Mask::<i32, 4>::from_array([true, false, false, true]);
|
||||||
|
/// let c = mask.select(a, b);
|
||||||
|
/// assert_eq!(c.to_array(), [0, 5, 6, 3]);
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// ## Selecting values from `Mask`
|
||||||
|
/// ```
|
||||||
|
/// # #![feature(portable_simd)]
|
||||||
|
/// # #[cfg(feature = "as_crate")] use core_simd::simd;
|
||||||
|
/// # #[cfg(not(feature = "as_crate"))] use core::simd;
|
||||||
|
/// # use simd::{Mask, Select};
|
||||||
|
/// let a = Mask::<i32, 4>::from_array([true, true, false, false]);
|
||||||
|
/// let b = Mask::<i32, 4>::from_array([false, false, true, true]);
|
||||||
|
/// let mask = Mask::<i32, 4>::from_array([true, false, false, true]);
|
||||||
|
/// let c = mask.select(a, b);
|
||||||
|
/// assert_eq!(c.to_array(), [true, false, true, false]);
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// ## Selecting with a bitmask
|
||||||
|
/// ```
|
||||||
|
/// # #![feature(portable_simd)]
|
||||||
|
/// # #[cfg(feature = "as_crate")] use core_simd::simd;
|
||||||
|
/// # #[cfg(not(feature = "as_crate"))] use core::simd;
|
||||||
|
/// # use simd::{Mask, Select};
|
||||||
|
/// let a = Mask::<i32, 4>::from_array([true, true, false, false]);
|
||||||
|
/// let b = Mask::<i32, 4>::from_array([false, false, true, true]);
|
||||||
|
/// let mask = 0b1001;
|
||||||
|
/// let c = mask.select(a, b);
|
||||||
|
/// assert_eq!(c.to_array(), [true, false, true, false]);
|
||||||
|
/// ```
|
||||||
|
pub trait Select<T> {
|
||||||
|
/// Choose elements
|
||||||
|
fn select(self, true_values: T, false_values: T) -> T;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, U, const N: usize> Select<Simd<T, N>> for Mask<U, N>
|
||||||
where
|
where
|
||||||
T: MaskElement,
|
T: SimdElement,
|
||||||
LaneCount<N>: SupportedLaneCount,
|
U: MaskElement,
|
||||||
{
|
{
|
||||||
/// Choose elements from two vectors.
|
|
||||||
///
|
|
||||||
/// For each element in the mask, choose the corresponding element from `true_values` if
|
|
||||||
/// that element mask is true, and `false_values` if that element mask is false.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
/// ```
|
|
||||||
/// # #![feature(portable_simd)]
|
|
||||||
/// # use core::simd::{Simd, Mask};
|
|
||||||
/// let a = Simd::from_array([0, 1, 2, 3]);
|
|
||||||
/// let b = Simd::from_array([4, 5, 6, 7]);
|
|
||||||
/// let mask = Mask::from_array([true, false, false, true]);
|
|
||||||
/// let c = mask.select(a, b);
|
|
||||||
/// assert_eq!(c.to_array(), [0, 5, 6, 3]);
|
|
||||||
/// ```
|
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use = "method returns a new vector and does not mutate the original inputs"]
|
fn select(self, true_values: Simd<T, N>, false_values: Simd<T, N>) -> Simd<T, N> {
|
||||||
pub fn select<U>(self, true_values: Simd<U, N>, false_values: Simd<U, N>) -> Simd<U, N>
|
// Safety:
|
||||||
where
|
// simd_as between masks is always safe (they're vectors of ints).
|
||||||
U: SimdElement<Mask = T>,
|
// simd_select uses a mask that matches the width and number of elements
|
||||||
{
|
unsafe {
|
||||||
// Safety: The mask has been cast to a vector of integers,
|
let mask: Simd<T::Mask, N> = core::intrinsics::simd::simd_as(self.to_simd());
|
||||||
// and the operands to select between are vectors of the same type and length.
|
core::intrinsics::simd::simd_select(mask, true_values, false_values)
|
||||||
unsafe { core::intrinsics::simd::simd_select(self.to_int(), true_values, false_values) }
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
/// Choose elements from two masks.
|
|
||||||
///
|
impl<T, const N: usize> Select<Simd<T, N>> for u64
|
||||||
/// For each element in the mask, choose the corresponding element from `true_values` if
|
where
|
||||||
/// that element mask is true, and `false_values` if that element mask is false.
|
T: SimdElement,
|
||||||
///
|
{
|
||||||
/// # Examples
|
#[inline]
|
||||||
/// ```
|
fn select(self, true_values: Simd<T, N>, false_values: Simd<T, N>) -> Simd<T, N> {
|
||||||
/// # #![feature(portable_simd)]
|
const {
|
||||||
/// # use core::simd::Mask;
|
assert!(N <= 64, "number of elements can't be greater than 64");
|
||||||
/// let a = Mask::<i32, 4>::from_array([true, true, false, false]);
|
}
|
||||||
/// let b = Mask::<i32, 4>::from_array([false, false, true, true]);
|
|
||||||
/// let mask = Mask::<i32, 4>::from_array([true, false, false, true]);
|
#[inline]
|
||||||
/// let c = mask.select_mask(a, b);
|
unsafe fn select_impl<T, U: FixEndianness, const M: usize, const N: usize>(
|
||||||
/// assert_eq!(c.to_array(), [true, false, true, false]);
|
bitmask: U,
|
||||||
/// ```
|
true_values: Simd<T, N>,
|
||||||
#[inline]
|
false_values: Simd<T, N>,
|
||||||
#[must_use = "method returns a new mask and does not mutate the original inputs"]
|
) -> Simd<T, N>
|
||||||
pub fn select_mask(self, true_values: Self, false_values: Self) -> Self {
|
where
|
||||||
self & true_values | !self & false_values
|
T: SimdElement,
|
||||||
|
{
|
||||||
|
let default = true_values[0];
|
||||||
|
let true_values = true_values.resize::<M>(default);
|
||||||
|
let false_values = false_values.resize::<M>(default);
|
||||||
|
|
||||||
|
// LLVM assumes bit order should match endianness
|
||||||
|
let bitmask = bitmask.fix_endianness();
|
||||||
|
|
||||||
|
// Safety: the caller guarantees that the size of U matches M
|
||||||
|
let selected = unsafe {
|
||||||
|
core::intrinsics::simd::simd_select_bitmask(bitmask, true_values, false_values)
|
||||||
|
};
|
||||||
|
|
||||||
|
selected.resize::<N>(default)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO modify simd_bitmask_select to truncate input, making this unnecessary
|
||||||
|
if N <= 8 {
|
||||||
|
let bitmask = self as u8;
|
||||||
|
// Safety: bitmask matches length
|
||||||
|
unsafe { select_impl::<T, u8, 8, N>(bitmask, true_values, false_values) }
|
||||||
|
} else if N <= 16 {
|
||||||
|
let bitmask = self as u16;
|
||||||
|
// Safety: bitmask matches length
|
||||||
|
unsafe { select_impl::<T, u16, 16, N>(bitmask, true_values, false_values) }
|
||||||
|
} else if N <= 32 {
|
||||||
|
let bitmask = self as u32;
|
||||||
|
// Safety: bitmask matches length
|
||||||
|
unsafe { select_impl::<T, u32, 32, N>(bitmask, true_values, false_values) }
|
||||||
|
} else {
|
||||||
|
let bitmask = self;
|
||||||
|
// Safety: bitmask matches length
|
||||||
|
unsafe { select_impl::<T, u64, 64, N>(bitmask, true_values, false_values) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, U, const N: usize> Select<Mask<T, N>> for Mask<U, N>
|
||||||
|
where
|
||||||
|
T: MaskElement,
|
||||||
|
U: MaskElement,
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn select(self, true_values: Mask<T, N>, false_values: Mask<T, N>) -> Mask<T, N> {
|
||||||
|
let selected: Simd<T, N> =
|
||||||
|
Select::select(self, true_values.to_simd(), false_values.to_simd());
|
||||||
|
|
||||||
|
// Safety: all values come from masks
|
||||||
|
unsafe { Mask::from_simd_unchecked(selected) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, const N: usize> Select<Mask<T, N>> for u64
|
||||||
|
where
|
||||||
|
T: MaskElement,
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn select(self, true_values: Mask<T, N>, false_values: Mask<T, N>) -> Mask<T, N> {
|
||||||
|
let selected: Simd<T, N> =
|
||||||
|
Select::select(self, true_values.to_simd(), false_values.to_simd());
|
||||||
|
|
||||||
|
// Safety: all values come from masks
|
||||||
|
unsafe { Mask::from_simd_unchecked(selected) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::simd::{
|
use crate::simd::{
|
||||||
LaneCount, Mask, Simd, SimdElement, SupportedLaneCount,
|
Mask, Simd, SimdElement,
|
||||||
ptr::{SimdConstPtr, SimdMutPtr},
|
ptr::{SimdConstPtr, SimdMutPtr},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -21,8 +21,6 @@ macro_rules! impl_number {
|
||||||
{ $($number:ty),* } => {
|
{ $($number:ty),* } => {
|
||||||
$(
|
$(
|
||||||
impl<const N: usize> SimdPartialEq for Simd<$number, N>
|
impl<const N: usize> SimdPartialEq for Simd<$number, N>
|
||||||
where
|
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
{
|
||||||
type Mask = Mask<<$number as SimdElement>::Mask, N>;
|
type Mask = Mask<<$number as SimdElement>::Mask, N>;
|
||||||
|
|
||||||
|
|
@ -30,14 +28,14 @@ macro_rules! impl_number {
|
||||||
fn simd_eq(self, other: Self) -> Self::Mask {
|
fn simd_eq(self, other: Self) -> Self::Mask {
|
||||||
// Safety: `self` is a vector, and the result of the comparison
|
// Safety: `self` is a vector, and the result of the comparison
|
||||||
// is always a valid mask.
|
// is always a valid mask.
|
||||||
unsafe { Mask::from_int_unchecked(core::intrinsics::simd::simd_eq(self, other)) }
|
unsafe { Mask::from_simd_unchecked(core::intrinsics::simd::simd_eq(self, other)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn simd_ne(self, other: Self) -> Self::Mask {
|
fn simd_ne(self, other: Self) -> Self::Mask {
|
||||||
// Safety: `self` is a vector, and the result of the comparison
|
// Safety: `self` is a vector, and the result of the comparison
|
||||||
// is always a valid mask.
|
// is always a valid mask.
|
||||||
unsafe { Mask::from_int_unchecked(core::intrinsics::simd::simd_ne(self, other)) }
|
unsafe { Mask::from_simd_unchecked(core::intrinsics::simd::simd_ne(self, other)) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)*
|
)*
|
||||||
|
|
@ -50,8 +48,6 @@ macro_rules! impl_mask {
|
||||||
{ $($integer:ty),* } => {
|
{ $($integer:ty),* } => {
|
||||||
$(
|
$(
|
||||||
impl<const N: usize> SimdPartialEq for Mask<$integer, N>
|
impl<const N: usize> SimdPartialEq for Mask<$integer, N>
|
||||||
where
|
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
{
|
||||||
type Mask = Self;
|
type Mask = Self;
|
||||||
|
|
||||||
|
|
@ -59,14 +55,14 @@ macro_rules! impl_mask {
|
||||||
fn simd_eq(self, other: Self) -> Self::Mask {
|
fn simd_eq(self, other: Self) -> Self::Mask {
|
||||||
// Safety: `self` is a vector, and the result of the comparison
|
// Safety: `self` is a vector, and the result of the comparison
|
||||||
// is always a valid mask.
|
// is always a valid mask.
|
||||||
unsafe { Self::from_int_unchecked(core::intrinsics::simd::simd_eq(self.to_int(), other.to_int())) }
|
unsafe { Self::from_simd_unchecked(core::intrinsics::simd::simd_eq(self.to_simd(), other.to_simd())) }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn simd_ne(self, other: Self) -> Self::Mask {
|
fn simd_ne(self, other: Self) -> Self::Mask {
|
||||||
// Safety: `self` is a vector, and the result of the comparison
|
// Safety: `self` is a vector, and the result of the comparison
|
||||||
// is always a valid mask.
|
// is always a valid mask.
|
||||||
unsafe { Self::from_int_unchecked(core::intrinsics::simd::simd_ne(self.to_int(), other.to_int())) }
|
unsafe { Self::from_simd_unchecked(core::intrinsics::simd::simd_ne(self.to_simd(), other.to_simd())) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)*
|
)*
|
||||||
|
|
@ -75,10 +71,7 @@ macro_rules! impl_mask {
|
||||||
|
|
||||||
impl_mask! { i8, i16, i32, i64, isize }
|
impl_mask! { i8, i16, i32, i64, isize }
|
||||||
|
|
||||||
impl<T, const N: usize> SimdPartialEq for Simd<*const T, N>
|
impl<T, const N: usize> SimdPartialEq for Simd<*const T, N> {
|
||||||
where
|
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
|
||||||
type Mask = Mask<isize, N>;
|
type Mask = Mask<isize, N>;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
@ -92,10 +85,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, const N: usize> SimdPartialEq for Simd<*mut T, N>
|
impl<T, const N: usize> SimdPartialEq for Simd<*mut T, N> {
|
||||||
where
|
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
|
||||||
type Mask = Mask<isize, N>;
|
type Mask = Mask<isize, N>;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::simd::{
|
use crate::simd::{
|
||||||
LaneCount, Mask, Simd, SupportedLaneCount,
|
Mask, Select, Simd,
|
||||||
cmp::SimdPartialEq,
|
cmp::SimdPartialEq,
|
||||||
ptr::{SimdConstPtr, SimdMutPtr},
|
ptr::{SimdConstPtr, SimdMutPtr},
|
||||||
};
|
};
|
||||||
|
|
@ -49,41 +49,37 @@ macro_rules! impl_integer {
|
||||||
{ $($integer:ty),* } => {
|
{ $($integer:ty),* } => {
|
||||||
$(
|
$(
|
||||||
impl<const N: usize> SimdPartialOrd for Simd<$integer, N>
|
impl<const N: usize> SimdPartialOrd for Simd<$integer, N>
|
||||||
where
|
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
fn simd_lt(self, other: Self) -> Self::Mask {
|
fn simd_lt(self, other: Self) -> Self::Mask {
|
||||||
// Safety: `self` is a vector, and the result of the comparison
|
// Safety: `self` is a vector, and the result of the comparison
|
||||||
// is always a valid mask.
|
// is always a valid mask.
|
||||||
unsafe { Mask::from_int_unchecked(core::intrinsics::simd::simd_lt(self, other)) }
|
unsafe { Mask::from_simd_unchecked(core::intrinsics::simd::simd_lt(self, other)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn simd_le(self, other: Self) -> Self::Mask {
|
fn simd_le(self, other: Self) -> Self::Mask {
|
||||||
// Safety: `self` is a vector, and the result of the comparison
|
// Safety: `self` is a vector, and the result of the comparison
|
||||||
// is always a valid mask.
|
// is always a valid mask.
|
||||||
unsafe { Mask::from_int_unchecked(core::intrinsics::simd::simd_le(self, other)) }
|
unsafe { Mask::from_simd_unchecked(core::intrinsics::simd::simd_le(self, other)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn simd_gt(self, other: Self) -> Self::Mask {
|
fn simd_gt(self, other: Self) -> Self::Mask {
|
||||||
// Safety: `self` is a vector, and the result of the comparison
|
// Safety: `self` is a vector, and the result of the comparison
|
||||||
// is always a valid mask.
|
// is always a valid mask.
|
||||||
unsafe { Mask::from_int_unchecked(core::intrinsics::simd::simd_gt(self, other)) }
|
unsafe { Mask::from_simd_unchecked(core::intrinsics::simd::simd_gt(self, other)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn simd_ge(self, other: Self) -> Self::Mask {
|
fn simd_ge(self, other: Self) -> Self::Mask {
|
||||||
// Safety: `self` is a vector, and the result of the comparison
|
// Safety: `self` is a vector, and the result of the comparison
|
||||||
// is always a valid mask.
|
// is always a valid mask.
|
||||||
unsafe { Mask::from_int_unchecked(core::intrinsics::simd::simd_ge(self, other)) }
|
unsafe { Mask::from_simd_unchecked(core::intrinsics::simd::simd_ge(self, other)) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<const N: usize> SimdOrd for Simd<$integer, N>
|
impl<const N: usize> SimdOrd for Simd<$integer, N>
|
||||||
where
|
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
fn simd_max(self, other: Self) -> Self {
|
fn simd_max(self, other: Self) -> Self {
|
||||||
|
|
@ -115,35 +111,33 @@ macro_rules! impl_float {
|
||||||
{ $($float:ty),* } => {
|
{ $($float:ty),* } => {
|
||||||
$(
|
$(
|
||||||
impl<const N: usize> SimdPartialOrd for Simd<$float, N>
|
impl<const N: usize> SimdPartialOrd for Simd<$float, N>
|
||||||
where
|
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
fn simd_lt(self, other: Self) -> Self::Mask {
|
fn simd_lt(self, other: Self) -> Self::Mask {
|
||||||
// Safety: `self` is a vector, and the result of the comparison
|
// Safety: `self` is a vector, and the result of the comparison
|
||||||
// is always a valid mask.
|
// is always a valid mask.
|
||||||
unsafe { Mask::from_int_unchecked(core::intrinsics::simd::simd_lt(self, other)) }
|
unsafe { Mask::from_simd_unchecked(core::intrinsics::simd::simd_lt(self, other)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn simd_le(self, other: Self) -> Self::Mask {
|
fn simd_le(self, other: Self) -> Self::Mask {
|
||||||
// Safety: `self` is a vector, and the result of the comparison
|
// Safety: `self` is a vector, and the result of the comparison
|
||||||
// is always a valid mask.
|
// is always a valid mask.
|
||||||
unsafe { Mask::from_int_unchecked(core::intrinsics::simd::simd_le(self, other)) }
|
unsafe { Mask::from_simd_unchecked(core::intrinsics::simd::simd_le(self, other)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn simd_gt(self, other: Self) -> Self::Mask {
|
fn simd_gt(self, other: Self) -> Self::Mask {
|
||||||
// Safety: `self` is a vector, and the result of the comparison
|
// Safety: `self` is a vector, and the result of the comparison
|
||||||
// is always a valid mask.
|
// is always a valid mask.
|
||||||
unsafe { Mask::from_int_unchecked(core::intrinsics::simd::simd_gt(self, other)) }
|
unsafe { Mask::from_simd_unchecked(core::intrinsics::simd::simd_gt(self, other)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn simd_ge(self, other: Self) -> Self::Mask {
|
fn simd_ge(self, other: Self) -> Self::Mask {
|
||||||
// Safety: `self` is a vector, and the result of the comparison
|
// Safety: `self` is a vector, and the result of the comparison
|
||||||
// is always a valid mask.
|
// is always a valid mask.
|
||||||
unsafe { Mask::from_int_unchecked(core::intrinsics::simd::simd_ge(self, other)) }
|
unsafe { Mask::from_simd_unchecked(core::intrinsics::simd::simd_ge(self, other)) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)*
|
)*
|
||||||
|
|
@ -156,50 +150,46 @@ macro_rules! impl_mask {
|
||||||
{ $($integer:ty),* } => {
|
{ $($integer:ty),* } => {
|
||||||
$(
|
$(
|
||||||
impl<const N: usize> SimdPartialOrd for Mask<$integer, N>
|
impl<const N: usize> SimdPartialOrd for Mask<$integer, N>
|
||||||
where
|
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
fn simd_lt(self, other: Self) -> Self::Mask {
|
fn simd_lt(self, other: Self) -> Self::Mask {
|
||||||
// Safety: `self` is a vector, and the result of the comparison
|
// Safety: `self` is a vector, and the result of the comparison
|
||||||
// is always a valid mask.
|
// is always a valid mask.
|
||||||
unsafe { Self::from_int_unchecked(core::intrinsics::simd::simd_lt(self.to_int(), other.to_int())) }
|
unsafe { Self::from_simd_unchecked(core::intrinsics::simd::simd_lt(self.to_simd(), other.to_simd())) }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn simd_le(self, other: Self) -> Self::Mask {
|
fn simd_le(self, other: Self) -> Self::Mask {
|
||||||
// Safety: `self` is a vector, and the result of the comparison
|
// Safety: `self` is a vector, and the result of the comparison
|
||||||
// is always a valid mask.
|
// is always a valid mask.
|
||||||
unsafe { Self::from_int_unchecked(core::intrinsics::simd::simd_le(self.to_int(), other.to_int())) }
|
unsafe { Self::from_simd_unchecked(core::intrinsics::simd::simd_le(self.to_simd(), other.to_simd())) }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn simd_gt(self, other: Self) -> Self::Mask {
|
fn simd_gt(self, other: Self) -> Self::Mask {
|
||||||
// Safety: `self` is a vector, and the result of the comparison
|
// Safety: `self` is a vector, and the result of the comparison
|
||||||
// is always a valid mask.
|
// is always a valid mask.
|
||||||
unsafe { Self::from_int_unchecked(core::intrinsics::simd::simd_gt(self.to_int(), other.to_int())) }
|
unsafe { Self::from_simd_unchecked(core::intrinsics::simd::simd_gt(self.to_simd(), other.to_simd())) }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn simd_ge(self, other: Self) -> Self::Mask {
|
fn simd_ge(self, other: Self) -> Self::Mask {
|
||||||
// Safety: `self` is a vector, and the result of the comparison
|
// Safety: `self` is a vector, and the result of the comparison
|
||||||
// is always a valid mask.
|
// is always a valid mask.
|
||||||
unsafe { Self::from_int_unchecked(core::intrinsics::simd::simd_ge(self.to_int(), other.to_int())) }
|
unsafe { Self::from_simd_unchecked(core::intrinsics::simd::simd_ge(self.to_simd(), other.to_simd())) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<const N: usize> SimdOrd for Mask<$integer, N>
|
impl<const N: usize> SimdOrd for Mask<$integer, N>
|
||||||
where
|
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
fn simd_max(self, other: Self) -> Self {
|
fn simd_max(self, other: Self) -> Self {
|
||||||
self.simd_gt(other).select_mask(other, self)
|
self.simd_gt(other).select(other, self)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn simd_min(self, other: Self) -> Self {
|
fn simd_min(self, other: Self) -> Self {
|
||||||
self.simd_lt(other).select_mask(other, self)
|
self.simd_lt(other).select(other, self)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
@ -218,10 +208,7 @@ macro_rules! impl_mask {
|
||||||
|
|
||||||
impl_mask! { i8, i16, i32, i64, isize }
|
impl_mask! { i8, i16, i32, i64, isize }
|
||||||
|
|
||||||
impl<T, const N: usize> SimdPartialOrd for Simd<*const T, N>
|
impl<T, const N: usize> SimdPartialOrd for Simd<*const T, N> {
|
||||||
where
|
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn simd_lt(self, other: Self) -> Self::Mask {
|
fn simd_lt(self, other: Self) -> Self::Mask {
|
||||||
self.addr().simd_lt(other.addr())
|
self.addr().simd_lt(other.addr())
|
||||||
|
|
@ -243,10 +230,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, const N: usize> SimdOrd for Simd<*const T, N>
|
impl<T, const N: usize> SimdOrd for Simd<*const T, N> {
|
||||||
where
|
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn simd_max(self, other: Self) -> Self {
|
fn simd_max(self, other: Self) -> Self {
|
||||||
self.simd_lt(other).select(other, self)
|
self.simd_lt(other).select(other, self)
|
||||||
|
|
@ -268,10 +252,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, const N: usize> SimdPartialOrd for Simd<*mut T, N>
|
impl<T, const N: usize> SimdPartialOrd for Simd<*mut T, N> {
|
||||||
where
|
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn simd_lt(self, other: Self) -> Self::Mask {
|
fn simd_lt(self, other: Self) -> Self::Mask {
|
||||||
self.addr().simd_lt(other.addr())
|
self.addr().simd_lt(other.addr())
|
||||||
|
|
@ -293,10 +274,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, const N: usize> SimdOrd for Simd<*mut T, N>
|
impl<T, const N: usize> SimdOrd for Simd<*mut T, N> {
|
||||||
where
|
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn simd_max(self, other: Self) -> Self {
|
fn simd_max(self, other: Self) -> Self {
|
||||||
self.simd_lt(other).select(other, self)
|
self.simd_lt(other).select(other, self)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use super::sealed::Sealed;
|
use super::sealed::Sealed;
|
||||||
use crate::simd::{
|
use crate::simd::{
|
||||||
LaneCount, Mask, Simd, SimdCast, SimdElement, SupportedLaneCount,
|
Mask, Select, Simd, SimdCast, SimdElement,
|
||||||
cmp::{SimdPartialEq, SimdPartialOrd},
|
cmp::{SimdPartialEq, SimdPartialOrd},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -240,15 +240,9 @@ pub trait SimdFloat: Copy + Sealed {
|
||||||
macro_rules! impl_trait {
|
macro_rules! impl_trait {
|
||||||
{ $($ty:ty { bits: $bits_ty:ty, mask: $mask_ty:ty }),* } => {
|
{ $($ty:ty { bits: $bits_ty:ty, mask: $mask_ty:ty }),* } => {
|
||||||
$(
|
$(
|
||||||
impl<const N: usize> Sealed for Simd<$ty, N>
|
impl<const N: usize> Sealed for Simd<$ty, N> {}
|
||||||
where
|
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<const N: usize> SimdFloat for Simd<$ty, N>
|
impl<const N: usize> SimdFloat for Simd<$ty, N>
|
||||||
where
|
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
{
|
||||||
type Mask = Mask<<$mask_ty as SimdElement>::Mask, N>;
|
type Mask = Mask<<$mask_ty as SimdElement>::Mask, N>;
|
||||||
type Scalar = $ty;
|
type Scalar = $ty;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
use super::sealed::Sealed;
|
use super::sealed::Sealed;
|
||||||
use crate::simd::{
|
use crate::simd::{
|
||||||
LaneCount, Mask, Simd, SimdCast, SimdElement, SupportedLaneCount, cmp::SimdOrd,
|
Mask, Select, Simd, SimdCast, SimdElement, cmp::SimdOrd, cmp::SimdPartialOrd, num::SimdUint,
|
||||||
cmp::SimdPartialOrd, num::SimdUint,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Operations on SIMD vectors of signed integers.
|
/// Operations on SIMD vectors of signed integers.
|
||||||
|
|
@ -242,16 +241,9 @@ pub trait SimdInt: Copy + Sealed {
|
||||||
macro_rules! impl_trait {
|
macro_rules! impl_trait {
|
||||||
{ $($ty:ident ($unsigned:ident)),* } => {
|
{ $($ty:ident ($unsigned:ident)),* } => {
|
||||||
$(
|
$(
|
||||||
impl<const N: usize> Sealed for Simd<$ty, N>
|
impl<const N: usize> Sealed for Simd<$ty, N> {}
|
||||||
where
|
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<const N: usize> SimdInt for Simd<$ty, N>
|
impl<const N: usize> SimdInt for Simd<$ty, N> {
|
||||||
where
|
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
|
||||||
type Mask = Mask<<$ty as SimdElement>::Mask, N>;
|
type Mask = Mask<<$ty as SimdElement>::Mask, N>;
|
||||||
type Scalar = $ty;
|
type Scalar = $ty;
|
||||||
type Unsigned = Simd<$unsigned, N>;
|
type Unsigned = Simd<$unsigned, N>;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use super::sealed::Sealed;
|
use super::sealed::Sealed;
|
||||||
use crate::simd::{LaneCount, Simd, SimdCast, SimdElement, SupportedLaneCount, cmp::SimdOrd};
|
use crate::simd::{Simd, SimdCast, SimdElement, cmp::SimdOrd};
|
||||||
|
|
||||||
/// Operations on SIMD vectors of unsigned integers.
|
/// Operations on SIMD vectors of unsigned integers.
|
||||||
pub trait SimdUint: Copy + Sealed {
|
pub trait SimdUint: Copy + Sealed {
|
||||||
|
|
@ -124,15 +124,9 @@ pub trait SimdUint: Copy + Sealed {
|
||||||
macro_rules! impl_trait {
|
macro_rules! impl_trait {
|
||||||
{ $($ty:ident ($signed:ident)),* } => {
|
{ $($ty:ident ($signed:ident)),* } => {
|
||||||
$(
|
$(
|
||||||
impl<const N: usize> Sealed for Simd<$ty, N>
|
impl<const N: usize> Sealed for Simd<$ty, N> {}
|
||||||
where
|
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<const N: usize> SimdUint for Simd<$ty, N>
|
impl<const N: usize> SimdUint for Simd<$ty, N>
|
||||||
where
|
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
{
|
||||||
type Scalar = $ty;
|
type Scalar = $ty;
|
||||||
type Cast<T: SimdElement> = Simd<T, N>;
|
type Cast<T: SimdElement> = Simd<T, N>;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use super::sealed::Sealed;
|
use super::sealed::Sealed;
|
||||||
use crate::simd::{LaneCount, Mask, Simd, SupportedLaneCount, cmp::SimdPartialEq, num::SimdUint};
|
use crate::simd::{Mask, Simd, cmp::SimdPartialEq, num::SimdUint};
|
||||||
|
|
||||||
/// Operations on SIMD vectors of constant pointers.
|
/// Operations on SIMD vectors of constant pointers.
|
||||||
pub trait SimdConstPtr: Copy + Sealed {
|
pub trait SimdConstPtr: Copy + Sealed {
|
||||||
|
|
@ -88,12 +88,9 @@ pub trait SimdConstPtr: Copy + Sealed {
|
||||||
fn wrapping_sub(self, count: Self::Usize) -> Self;
|
fn wrapping_sub(self, count: Self::Usize) -> Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, const N: usize> Sealed for Simd<*const T, N> where LaneCount<N>: SupportedLaneCount {}
|
impl<T, const N: usize> Sealed for Simd<*const T, N> {}
|
||||||
|
|
||||||
impl<T, const N: usize> SimdConstPtr for Simd<*const T, N>
|
impl<T, const N: usize> SimdConstPtr for Simd<*const T, N> {
|
||||||
where
|
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
|
||||||
type Usize = Simd<usize, N>;
|
type Usize = Simd<usize, N>;
|
||||||
type Isize = Simd<isize, N>;
|
type Isize = Simd<isize, N>;
|
||||||
type CastPtr<U> = Simd<*const U, N>;
|
type CastPtr<U> = Simd<*const U, N>;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use super::sealed::Sealed;
|
use super::sealed::Sealed;
|
||||||
use crate::simd::{LaneCount, Mask, Simd, SupportedLaneCount, cmp::SimdPartialEq, num::SimdUint};
|
use crate::simd::{Mask, Simd, cmp::SimdPartialEq, num::SimdUint};
|
||||||
|
|
||||||
/// Operations on SIMD vectors of mutable pointers.
|
/// Operations on SIMD vectors of mutable pointers.
|
||||||
pub trait SimdMutPtr: Copy + Sealed {
|
pub trait SimdMutPtr: Copy + Sealed {
|
||||||
|
|
@ -85,12 +85,9 @@ pub trait SimdMutPtr: Copy + Sealed {
|
||||||
fn wrapping_sub(self, count: Self::Usize) -> Self;
|
fn wrapping_sub(self, count: Self::Usize) -> Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, const N: usize> Sealed for Simd<*mut T, N> where LaneCount<N>: SupportedLaneCount {}
|
impl<T, const N: usize> Sealed for Simd<*mut T, N> {}
|
||||||
|
|
||||||
impl<T, const N: usize> SimdMutPtr for Simd<*mut T, N>
|
impl<T, const N: usize> SimdMutPtr for Simd<*mut T, N> {
|
||||||
where
|
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
|
||||||
type Usize = Simd<usize, N>;
|
type Usize = Simd<usize, N>;
|
||||||
type Isize = Simd<isize, N>;
|
type Isize = Simd<isize, N>;
|
||||||
type CastPtr<U> = Simd<*mut U, N>;
|
type CastPtr<U> = Simd<*mut U, N>;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::simd::{LaneCount, Mask, MaskElement, Simd, SimdElement, SupportedLaneCount};
|
use crate::simd::{Mask, MaskElement, Simd, SimdElement};
|
||||||
|
|
||||||
/// Constructs a new SIMD vector by copying elements from selected elements in other vectors.
|
/// Constructs a new SIMD vector by copying elements from selected elements in other vectors.
|
||||||
///
|
///
|
||||||
|
|
@ -82,8 +82,6 @@ pub trait Swizzle<const N: usize> {
|
||||||
fn swizzle<T, const M: usize>(vector: Simd<T, M>) -> Simd<T, N>
|
fn swizzle<T, const M: usize>(vector: Simd<T, M>) -> Simd<T, N>
|
||||||
where
|
where
|
||||||
T: SimdElement,
|
T: SimdElement,
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
LaneCount<M>: SupportedLaneCount,
|
|
||||||
{
|
{
|
||||||
// Safety: `vector` is a vector, and the index is a const vector of u32.
|
// Safety: `vector` is a vector, and the index is a const vector of u32.
|
||||||
unsafe {
|
unsafe {
|
||||||
|
|
@ -122,8 +120,6 @@ pub trait Swizzle<const N: usize> {
|
||||||
fn concat_swizzle<T, const M: usize>(first: Simd<T, M>, second: Simd<T, M>) -> Simd<T, N>
|
fn concat_swizzle<T, const M: usize>(first: Simd<T, M>, second: Simd<T, M>) -> Simd<T, N>
|
||||||
where
|
where
|
||||||
T: SimdElement,
|
T: SimdElement,
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
LaneCount<M>: SupportedLaneCount,
|
|
||||||
{
|
{
|
||||||
// Safety: `first` and `second` are vectors, and the index is a const vector of u32.
|
// Safety: `first` and `second` are vectors, and the index is a const vector of u32.
|
||||||
unsafe {
|
unsafe {
|
||||||
|
|
@ -161,11 +157,9 @@ pub trait Swizzle<const N: usize> {
|
||||||
fn swizzle_mask<T, const M: usize>(mask: Mask<T, M>) -> Mask<T, N>
|
fn swizzle_mask<T, const M: usize>(mask: Mask<T, M>) -> Mask<T, N>
|
||||||
where
|
where
|
||||||
T: MaskElement,
|
T: MaskElement,
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
LaneCount<M>: SupportedLaneCount,
|
|
||||||
{
|
{
|
||||||
// SAFETY: all elements of this mask come from another mask
|
// SAFETY: all elements of this mask come from another mask
|
||||||
unsafe { Mask::from_int_unchecked(Self::swizzle(mask.to_int())) }
|
unsafe { Mask::from_simd_unchecked(Self::swizzle(mask.to_simd())) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new mask from the elements of `first` and `second`.
|
/// Creates a new mask from the elements of `first` and `second`.
|
||||||
|
|
@ -177,18 +171,17 @@ pub trait Swizzle<const N: usize> {
|
||||||
fn concat_swizzle_mask<T, const M: usize>(first: Mask<T, M>, second: Mask<T, M>) -> Mask<T, N>
|
fn concat_swizzle_mask<T, const M: usize>(first: Mask<T, M>, second: Mask<T, M>) -> Mask<T, N>
|
||||||
where
|
where
|
||||||
T: MaskElement,
|
T: MaskElement,
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
LaneCount<M>: SupportedLaneCount,
|
|
||||||
{
|
{
|
||||||
// SAFETY: all elements of this mask come from another mask
|
// SAFETY: all elements of this mask come from another mask
|
||||||
unsafe { Mask::from_int_unchecked(Self::concat_swizzle(first.to_int(), second.to_int())) }
|
unsafe {
|
||||||
|
Mask::from_simd_unchecked(Self::concat_swizzle(first.to_simd(), second.to_simd()))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, const N: usize> Simd<T, N>
|
impl<T, const N: usize> Simd<T, N>
|
||||||
where
|
where
|
||||||
T: SimdElement,
|
T: SimdElement,
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
{
|
||||||
/// Reverse the order of the elements in the vector.
|
/// Reverse the order of the elements in the vector.
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
@ -462,10 +455,7 @@ where
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use = "method returns a new vector and does not mutate the original inputs"]
|
#[must_use = "method returns a new vector and does not mutate the original inputs"]
|
||||||
pub fn resize<const M: usize>(self, value: T) -> Simd<T, M>
|
pub fn resize<const M: usize>(self, value: T) -> Simd<T, M> {
|
||||||
where
|
|
||||||
LaneCount<M>: SupportedLaneCount,
|
|
||||||
{
|
|
||||||
struct Resize<const N: usize>;
|
struct Resize<const N: usize>;
|
||||||
impl<const N: usize, const M: usize> Swizzle<M> for Resize<N> {
|
impl<const N: usize, const M: usize> Swizzle<M> for Resize<N> {
|
||||||
const INDEX: [usize; M] = const {
|
const INDEX: [usize; M] = const {
|
||||||
|
|
@ -493,10 +483,7 @@ where
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use = "method returns a new vector and does not mutate the original inputs"]
|
#[must_use = "method returns a new vector and does not mutate the original inputs"]
|
||||||
pub fn extract<const START: usize, const LEN: usize>(self) -> Simd<T, LEN>
|
pub fn extract<const START: usize, const LEN: usize>(self) -> Simd<T, LEN> {
|
||||||
where
|
|
||||||
LaneCount<LEN>: SupportedLaneCount,
|
|
||||||
{
|
|
||||||
struct Extract<const N: usize, const START: usize>;
|
struct Extract<const N: usize, const START: usize>;
|
||||||
impl<const N: usize, const START: usize, const LEN: usize> Swizzle<LEN> for Extract<N, START> {
|
impl<const N: usize, const START: usize, const LEN: usize> Swizzle<LEN> for Extract<N, START> {
|
||||||
const INDEX: [usize; LEN] = const {
|
const INDEX: [usize; LEN] = const {
|
||||||
|
|
@ -517,14 +504,13 @@ where
|
||||||
impl<T, const N: usize> Mask<T, N>
|
impl<T, const N: usize> Mask<T, N>
|
||||||
where
|
where
|
||||||
T: MaskElement,
|
T: MaskElement,
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
{
|
||||||
/// Reverse the order of the elements in the mask.
|
/// Reverse the order of the elements in the mask.
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use = "method returns a new vector and does not mutate the original inputs"]
|
#[must_use = "method returns a new vector and does not mutate the original inputs"]
|
||||||
pub fn reverse(self) -> Self {
|
pub fn reverse(self) -> Self {
|
||||||
// Safety: swizzles are safe for masks
|
// Safety: swizzles are safe for masks
|
||||||
unsafe { Self::from_int_unchecked(self.to_int().reverse()) }
|
unsafe { Self::from_simd_unchecked(self.to_simd().reverse()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Rotates the mask such that the first `OFFSET` elements of the slice move to the end
|
/// Rotates the mask such that the first `OFFSET` elements of the slice move to the end
|
||||||
|
|
@ -534,7 +520,7 @@ where
|
||||||
#[must_use = "method returns a new vector and does not mutate the original inputs"]
|
#[must_use = "method returns a new vector and does not mutate the original inputs"]
|
||||||
pub fn rotate_elements_left<const OFFSET: usize>(self) -> Self {
|
pub fn rotate_elements_left<const OFFSET: usize>(self) -> Self {
|
||||||
// Safety: swizzles are safe for masks
|
// Safety: swizzles are safe for masks
|
||||||
unsafe { Self::from_int_unchecked(self.to_int().rotate_elements_left::<OFFSET>()) }
|
unsafe { Self::from_simd_unchecked(self.to_simd().rotate_elements_left::<OFFSET>()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Rotates the mask such that the first `self.len() - OFFSET` elements of the mask move to
|
/// Rotates the mask such that the first `self.len() - OFFSET` elements of the mask move to
|
||||||
|
|
@ -544,7 +530,7 @@ where
|
||||||
#[must_use = "method returns a new vector and does not mutate the original inputs"]
|
#[must_use = "method returns a new vector and does not mutate the original inputs"]
|
||||||
pub fn rotate_elements_right<const OFFSET: usize>(self) -> Self {
|
pub fn rotate_elements_right<const OFFSET: usize>(self) -> Self {
|
||||||
// Safety: swizzles are safe for masks
|
// Safety: swizzles are safe for masks
|
||||||
unsafe { Self::from_int_unchecked(self.to_int().rotate_elements_right::<OFFSET>()) }
|
unsafe { Self::from_simd_unchecked(self.to_simd().rotate_elements_right::<OFFSET>()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Shifts the mask elements to the left by `OFFSET`, filling in with
|
/// Shifts the mask elements to the left by `OFFSET`, filling in with
|
||||||
|
|
@ -554,7 +540,7 @@ where
|
||||||
pub fn shift_elements_left<const OFFSET: usize>(self, padding: bool) -> Self {
|
pub fn shift_elements_left<const OFFSET: usize>(self, padding: bool) -> Self {
|
||||||
// Safety: swizzles are safe for masks
|
// Safety: swizzles are safe for masks
|
||||||
unsafe {
|
unsafe {
|
||||||
Self::from_int_unchecked(self.to_int().shift_elements_left::<OFFSET>(if padding {
|
Self::from_simd_unchecked(self.to_simd().shift_elements_left::<OFFSET>(if padding {
|
||||||
T::TRUE
|
T::TRUE
|
||||||
} else {
|
} else {
|
||||||
T::FALSE
|
T::FALSE
|
||||||
|
|
@ -569,7 +555,7 @@ where
|
||||||
pub fn shift_elements_right<const OFFSET: usize>(self, padding: bool) -> Self {
|
pub fn shift_elements_right<const OFFSET: usize>(self, padding: bool) -> Self {
|
||||||
// Safety: swizzles are safe for masks
|
// Safety: swizzles are safe for masks
|
||||||
unsafe {
|
unsafe {
|
||||||
Self::from_int_unchecked(self.to_int().shift_elements_right::<OFFSET>(if padding {
|
Self::from_simd_unchecked(self.to_simd().shift_elements_right::<OFFSET>(if padding {
|
||||||
T::TRUE
|
T::TRUE
|
||||||
} else {
|
} else {
|
||||||
T::FALSE
|
T::FALSE
|
||||||
|
|
@ -598,9 +584,9 @@ where
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use = "method returns a new vector and does not mutate the original inputs"]
|
#[must_use = "method returns a new vector and does not mutate the original inputs"]
|
||||||
pub fn interleave(self, other: Self) -> (Self, Self) {
|
pub fn interleave(self, other: Self) -> (Self, Self) {
|
||||||
let (lo, hi) = self.to_int().interleave(other.to_int());
|
let (lo, hi) = self.to_simd().interleave(other.to_simd());
|
||||||
// Safety: swizzles are safe for masks
|
// Safety: swizzles are safe for masks
|
||||||
unsafe { (Self::from_int_unchecked(lo), Self::from_int_unchecked(hi)) }
|
unsafe { (Self::from_simd_unchecked(lo), Self::from_simd_unchecked(hi)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Deinterleave two masks.
|
/// Deinterleave two masks.
|
||||||
|
|
@ -627,12 +613,12 @@ where
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use = "method returns a new vector and does not mutate the original inputs"]
|
#[must_use = "method returns a new vector and does not mutate the original inputs"]
|
||||||
pub fn deinterleave(self, other: Self) -> (Self, Self) {
|
pub fn deinterleave(self, other: Self) -> (Self, Self) {
|
||||||
let (even, odd) = self.to_int().deinterleave(other.to_int());
|
let (even, odd) = self.to_simd().deinterleave(other.to_simd());
|
||||||
// Safety: swizzles are safe for masks
|
// Safety: swizzles are safe for masks
|
||||||
unsafe {
|
unsafe {
|
||||||
(
|
(
|
||||||
Self::from_int_unchecked(even),
|
Self::from_simd_unchecked(even),
|
||||||
Self::from_int_unchecked(odd),
|
Self::from_simd_unchecked(odd),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -653,13 +639,10 @@ where
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use = "method returns a new vector and does not mutate the original inputs"]
|
#[must_use = "method returns a new vector and does not mutate the original inputs"]
|
||||||
pub fn resize<const M: usize>(self, value: bool) -> Mask<T, M>
|
pub fn resize<const M: usize>(self, value: bool) -> Mask<T, M> {
|
||||||
where
|
|
||||||
LaneCount<M>: SupportedLaneCount,
|
|
||||||
{
|
|
||||||
// Safety: swizzles are safe for masks
|
// Safety: swizzles are safe for masks
|
||||||
unsafe {
|
unsafe {
|
||||||
Mask::<T, M>::from_int_unchecked(self.to_int().resize::<M>(if value {
|
Mask::<T, M>::from_simd_unchecked(self.to_simd().resize::<M>(if value {
|
||||||
T::TRUE
|
T::TRUE
|
||||||
} else {
|
} else {
|
||||||
T::FALSE
|
T::FALSE
|
||||||
|
|
@ -679,11 +662,8 @@ where
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use = "method returns a new vector and does not mutate the original inputs"]
|
#[must_use = "method returns a new vector and does not mutate the original inputs"]
|
||||||
pub fn extract<const START: usize, const LEN: usize>(self) -> Mask<T, LEN>
|
pub fn extract<const START: usize, const LEN: usize>(self) -> Mask<T, LEN> {
|
||||||
where
|
|
||||||
LaneCount<LEN>: SupportedLaneCount,
|
|
||||||
{
|
|
||||||
// Safety: swizzles are safe for masks
|
// Safety: swizzles are safe for masks
|
||||||
unsafe { Mask::<T, LEN>::from_int_unchecked(self.to_int().extract::<START, LEN>()) }
|
unsafe { Mask::<T, LEN>::from_simd_unchecked(self.to_simd().extract::<START, LEN>()) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,7 @@
|
||||||
use crate::simd::{LaneCount, Simd, SupportedLaneCount};
|
use crate::simd::Simd;
|
||||||
use core::mem;
|
use core::mem;
|
||||||
|
|
||||||
impl<const N: usize> Simd<u8, N>
|
impl<const N: usize> Simd<u8, N> {
|
||||||
where
|
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
|
||||||
/// Swizzle a vector of bytes according to the index vector.
|
/// Swizzle a vector of bytes according to the index vector.
|
||||||
/// Indices within range select the appropriate byte.
|
/// Indices within range select the appropriate byte.
|
||||||
/// Indices "out of bounds" instead select 0.
|
/// Indices "out of bounds" instead select 0.
|
||||||
|
|
@ -139,7 +136,7 @@ unsafe fn armv7_neon_swizzle_u8x16(bytes: Simd<u8, 16>, idxs: Simd<u8, 16>) -> S
|
||||||
#[inline]
|
#[inline]
|
||||||
#[allow(clippy::let_and_return)]
|
#[allow(clippy::let_and_return)]
|
||||||
unsafe fn avx2_pshufb(bytes: Simd<u8, 32>, idxs: Simd<u8, 32>) -> Simd<u8, 32> {
|
unsafe fn avx2_pshufb(bytes: Simd<u8, 32>, idxs: Simd<u8, 32>) -> Simd<u8, 32> {
|
||||||
use crate::simd::cmp::SimdPartialOrd;
|
use crate::simd::{Select, cmp::SimdPartialOrd};
|
||||||
#[cfg(target_arch = "x86")]
|
#[cfg(target_arch = "x86")]
|
||||||
use core::arch::x86;
|
use core::arch::x86;
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
|
@ -184,10 +181,7 @@ unsafe fn transize<T, const N: usize>(
|
||||||
f: unsafe fn(T, T) -> T,
|
f: unsafe fn(T, T) -> T,
|
||||||
a: Simd<u8, N>,
|
a: Simd<u8, N>,
|
||||||
b: Simd<u8, N>,
|
b: Simd<u8, N>,
|
||||||
) -> Simd<u8, N>
|
) -> Simd<u8, N> {
|
||||||
where
|
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
|
||||||
// SAFETY: Same obligation to use this function as to use mem::transmute_copy.
|
// SAFETY: Same obligation to use this function as to use mem::transmute_copy.
|
||||||
unsafe { mem::transmute_copy(&f(mem::transmute_copy(&a), mem::transmute_copy(&b))) }
|
unsafe { mem::transmute_copy(&f(mem::transmute_copy(&a), mem::transmute_copy(&b))) }
|
||||||
}
|
}
|
||||||
|
|
@ -196,11 +190,8 @@ where
|
||||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn zeroing_idxs<const N: usize>(idxs: Simd<u8, N>) -> Simd<u8, N>
|
fn zeroing_idxs<const N: usize>(idxs: Simd<u8, N>) -> Simd<u8, N> {
|
||||||
where
|
use crate::simd::{Select, cmp::SimdPartialOrd};
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
|
||||||
use crate::simd::cmp::SimdPartialOrd;
|
|
||||||
idxs.simd_lt(Simd::splat(N as u8))
|
idxs.simd_lt(Simd::splat(N as u8))
|
||||||
.select(idxs, Simd::splat(u8::MAX))
|
.select(idxs, Simd::splat(u8::MAX))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
use crate::simd::{
|
use crate::simd::{
|
||||||
LaneCount, Simd, SimdElement, SupportedLaneCount,
|
Simd, SimdElement,
|
||||||
num::{SimdFloat, SimdInt, SimdUint},
|
num::{SimdFloat, SimdInt, SimdUint},
|
||||||
};
|
};
|
||||||
|
|
||||||
mod sealed {
|
mod sealed {
|
||||||
use super::*;
|
use super::*;
|
||||||
pub trait Sealed {}
|
pub trait Sealed {}
|
||||||
impl<T: SimdElement, const N: usize> Sealed for Simd<T, N> where LaneCount<N>: SupportedLaneCount {}
|
impl<T: SimdElement, const N: usize> Sealed for Simd<T, N> {}
|
||||||
}
|
}
|
||||||
use sealed::Sealed;
|
use sealed::Sealed;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
|
use core::intrinsics::simd::SimdAlign;
|
||||||
|
|
||||||
use crate::simd::{
|
use crate::simd::{
|
||||||
LaneCount, Mask, MaskElement, SupportedLaneCount, Swizzle,
|
Mask, MaskElement,
|
||||||
cmp::SimdPartialOrd,
|
cmp::SimdPartialOrd,
|
||||||
num::SimdUint,
|
num::SimdUint,
|
||||||
ptr::{SimdConstPtr, SimdMutPtr},
|
ptr::{SimdConstPtr, SimdMutPtr},
|
||||||
|
|
@ -51,6 +53,8 @@ use crate::simd::{
|
||||||
/// Thus it is sound to [`transmute`] `Simd<T, N>` to `[T; N]` and should optimize to "zero cost",
|
/// Thus it is sound to [`transmute`] `Simd<T, N>` to `[T; N]` and should optimize to "zero cost",
|
||||||
/// but the reverse transmutation may require a copy the compiler cannot simply elide.
|
/// but the reverse transmutation may require a copy the compiler cannot simply elide.
|
||||||
///
|
///
|
||||||
|
/// `N` cannot be 0 and may be at most 64. This limit may be increased in the future.
|
||||||
|
///
|
||||||
/// # ABI "Features"
|
/// # ABI "Features"
|
||||||
/// Due to Rust's safety guarantees, `Simd<T, N>` is currently passed and returned via memory,
|
/// Due to Rust's safety guarantees, `Simd<T, N>` is currently passed and returned via memory,
|
||||||
/// not SIMD registers, except as an optimization. Using `#[inline]` on functions that accept
|
/// not SIMD registers, except as an optimization. Using `#[inline]` on functions that accept
|
||||||
|
|
@ -100,14 +104,13 @@ use crate::simd::{
|
||||||
// avoided, as it will likely become illegal on `#[repr(simd)]` structs in the future. It also
|
// avoided, as it will likely become illegal on `#[repr(simd)]` structs in the future. It also
|
||||||
// causes rustc to emit illegal LLVM IR in some cases.
|
// causes rustc to emit illegal LLVM IR in some cases.
|
||||||
#[repr(simd, packed)]
|
#[repr(simd, packed)]
|
||||||
|
#[rustc_simd_monomorphize_lane_limit = "64"]
|
||||||
pub struct Simd<T, const N: usize>([T; N])
|
pub struct Simd<T, const N: usize>([T; N])
|
||||||
where
|
where
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
T: SimdElement;
|
T: SimdElement;
|
||||||
|
|
||||||
impl<T, const N: usize> Simd<T, N>
|
impl<T, const N: usize> Simd<T, N>
|
||||||
where
|
where
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
T: SimdElement,
|
T: SimdElement,
|
||||||
{
|
{
|
||||||
/// Number of elements in this vector.
|
/// Number of elements in this vector.
|
||||||
|
|
@ -146,30 +149,8 @@ where
|
||||||
#[inline]
|
#[inline]
|
||||||
#[rustc_const_unstable(feature = "portable_simd", issue = "86656")]
|
#[rustc_const_unstable(feature = "portable_simd", issue = "86656")]
|
||||||
pub const fn splat(value: T) -> Self {
|
pub const fn splat(value: T) -> Self {
|
||||||
const fn splat_const<T, const N: usize>(value: T) -> Simd<T, N>
|
// SAFETY: T is a SimdElement, and the item type of Self.
|
||||||
where
|
unsafe { core::intrinsics::simd::simd_splat(value) }
|
||||||
T: SimdElement,
|
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
|
||||||
Simd::from_array([value; N])
|
|
||||||
}
|
|
||||||
|
|
||||||
fn splat_rt<T, const N: usize>(value: T) -> Simd<T, N>
|
|
||||||
where
|
|
||||||
T: SimdElement,
|
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
|
||||||
// This is preferred over `[value; N]`, since it's explicitly a splat:
|
|
||||||
// https://github.com/rust-lang/rust/issues/97804
|
|
||||||
struct Splat;
|
|
||||||
impl<const N: usize> Swizzle<N> for Splat {
|
|
||||||
const INDEX: [usize; N] = [0; N];
|
|
||||||
}
|
|
||||||
|
|
||||||
Splat::swizzle::<T, 1>(Simd::<T, 1>::from([value]))
|
|
||||||
}
|
|
||||||
|
|
||||||
core::intrinsics::const_eval_select((value,), splat_const, splat_rt)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an array reference containing the entire SIMD vector.
|
/// Returns an array reference containing the entire SIMD vector.
|
||||||
|
|
@ -195,7 +176,7 @@ where
|
||||||
|
|
||||||
/// Returns a mutable array reference containing the entire SIMD vector.
|
/// Returns a mutable array reference containing the entire SIMD vector.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn as_mut_array(&mut self) -> &mut [T; N] {
|
pub const fn as_mut_array(&mut self) -> &mut [T; N] {
|
||||||
// SAFETY: `Simd<T, N>` is just an overaligned `[T; N]` with
|
// SAFETY: `Simd<T, N>` is just an overaligned `[T; N]` with
|
||||||
// potential padding at the end, so pointer casting to a
|
// potential padding at the end, so pointer casting to a
|
||||||
// `&mut [T; N]` is safe.
|
// `&mut [T; N]` is safe.
|
||||||
|
|
@ -324,7 +305,7 @@ where
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn copy_to_slice(self, slice: &mut [T]) {
|
pub const fn copy_to_slice(self, slice: &mut [T]) {
|
||||||
assert!(
|
assert!(
|
||||||
slice.len() >= Self::LEN,
|
slice.len() >= Self::LEN,
|
||||||
"slice length must be at least the number of elements"
|
"slice length must be at least the number of elements"
|
||||||
|
|
@ -465,7 +446,7 @@ where
|
||||||
/// value from `or` is passed through.
|
/// value from `or` is passed through.
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
/// Enabled `ptr` elements must be safe to read as if by `std::ptr::read`.
|
/// Enabled `ptr` elements must be safe to read as if by `core::ptr::read`.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub unsafe fn load_select_ptr(
|
pub unsafe fn load_select_ptr(
|
||||||
|
|
@ -475,12 +456,11 @@ where
|
||||||
) -> Self {
|
) -> Self {
|
||||||
// SAFETY: The safety of reading elements through `ptr` is ensured by the caller.
|
// SAFETY: The safety of reading elements through `ptr` is ensured by the caller.
|
||||||
unsafe {
|
unsafe {
|
||||||
core::intrinsics::simd::simd_masked_load::<
|
core::intrinsics::simd::simd_masked_load::<_, _, _, { SimdAlign::Element }>(
|
||||||
_,
|
enable.to_simd(),
|
||||||
_,
|
ptr,
|
||||||
_,
|
or,
|
||||||
{ core::intrinsics::simd::SimdAlign::Element },
|
)
|
||||||
>(enable.to_int(), ptr, or)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -659,7 +639,7 @@ where
|
||||||
or: Self,
|
or: Self,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
// Safety: The caller is responsible for upholding all invariants
|
// Safety: The caller is responsible for upholding all invariants
|
||||||
unsafe { core::intrinsics::simd::simd_gather(or, source, enable.to_int()) }
|
unsafe { core::intrinsics::simd::simd_gather(or, source, enable.to_simd()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Conditionally write contiguous elements to `slice`. The `enable` mask controls
|
/// Conditionally write contiguous elements to `slice`. The `enable` mask controls
|
||||||
|
|
@ -731,12 +711,11 @@ where
|
||||||
pub unsafe fn store_select_ptr(self, ptr: *mut T, enable: Mask<<T as SimdElement>::Mask, N>) {
|
pub unsafe fn store_select_ptr(self, ptr: *mut T, enable: Mask<<T as SimdElement>::Mask, N>) {
|
||||||
// SAFETY: The safety of writing elements through `ptr` is ensured by the caller.
|
// SAFETY: The safety of writing elements through `ptr` is ensured by the caller.
|
||||||
unsafe {
|
unsafe {
|
||||||
core::intrinsics::simd::simd_masked_store::<
|
core::intrinsics::simd::simd_masked_store::<_, _, _, { SimdAlign::Element }>(
|
||||||
_,
|
enable.to_simd(),
|
||||||
_,
|
ptr,
|
||||||
_,
|
self,
|
||||||
{ core::intrinsics::simd::SimdAlign::Element },
|
)
|
||||||
>(enable.to_int(), ptr, self)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -896,20 +875,14 @@ where
|
||||||
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
|
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
|
||||||
pub unsafe fn scatter_select_ptr(self, dest: Simd<*mut T, N>, enable: Mask<isize, N>) {
|
pub unsafe fn scatter_select_ptr(self, dest: Simd<*mut T, N>, enable: Mask<isize, N>) {
|
||||||
// Safety: The caller is responsible for upholding all invariants
|
// Safety: The caller is responsible for upholding all invariants
|
||||||
unsafe { core::intrinsics::simd::simd_scatter(self, dest, enable.to_int()) }
|
unsafe { core::intrinsics::simd::simd_scatter(self, dest, enable.to_simd()) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, const N: usize> Copy for Simd<T, N>
|
impl<T, const N: usize> Copy for Simd<T, N> where T: SimdElement {}
|
||||||
where
|
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
T: SimdElement,
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, const N: usize> Clone for Simd<T, N>
|
impl<T, const N: usize> Clone for Simd<T, N>
|
||||||
where
|
where
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
T: SimdElement,
|
T: SimdElement,
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
@ -920,7 +893,6 @@ where
|
||||||
|
|
||||||
impl<T, const N: usize> Default for Simd<T, N>
|
impl<T, const N: usize> Default for Simd<T, N>
|
||||||
where
|
where
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
T: SimdElement + Default,
|
T: SimdElement + Default,
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
@ -931,7 +903,6 @@ where
|
||||||
|
|
||||||
impl<T, const N: usize> PartialEq for Simd<T, N>
|
impl<T, const N: usize> PartialEq for Simd<T, N>
|
||||||
where
|
where
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
T: SimdElement + PartialEq,
|
T: SimdElement + PartialEq,
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
@ -940,7 +911,7 @@ where
|
||||||
let mask = unsafe {
|
let mask = unsafe {
|
||||||
let tfvec: Simd<<T as SimdElement>::Mask, N> =
|
let tfvec: Simd<<T as SimdElement>::Mask, N> =
|
||||||
core::intrinsics::simd::simd_eq(*self, *other);
|
core::intrinsics::simd::simd_eq(*self, *other);
|
||||||
Mask::from_int_unchecked(tfvec)
|
Mask::from_simd_unchecked(tfvec)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Two vectors are equal if all elements are equal when compared elementwise
|
// Two vectors are equal if all elements are equal when compared elementwise
|
||||||
|
|
@ -954,7 +925,7 @@ where
|
||||||
let mask = unsafe {
|
let mask = unsafe {
|
||||||
let tfvec: Simd<<T as SimdElement>::Mask, N> =
|
let tfvec: Simd<<T as SimdElement>::Mask, N> =
|
||||||
core::intrinsics::simd::simd_ne(*self, *other);
|
core::intrinsics::simd::simd_ne(*self, *other);
|
||||||
Mask::from_int_unchecked(tfvec)
|
Mask::from_simd_unchecked(tfvec)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Two vectors are non-equal if any elements are non-equal when compared elementwise
|
// Two vectors are non-equal if any elements are non-equal when compared elementwise
|
||||||
|
|
@ -965,7 +936,6 @@ where
|
||||||
/// Lexicographic order. For the SIMD elementwise minimum and maximum, use simd_min and simd_max instead.
|
/// Lexicographic order. For the SIMD elementwise minimum and maximum, use simd_min and simd_max instead.
|
||||||
impl<T, const N: usize> PartialOrd for Simd<T, N>
|
impl<T, const N: usize> PartialOrd for Simd<T, N>
|
||||||
where
|
where
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
T: SimdElement + PartialOrd,
|
T: SimdElement + PartialOrd,
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
@ -975,17 +945,11 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, const N: usize> Eq for Simd<T, N>
|
impl<T, const N: usize> Eq for Simd<T, N> where T: SimdElement + Eq {}
|
||||||
where
|
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
T: SimdElement + Eq,
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Lexicographic order. For the SIMD elementwise minimum and maximum, use simd_min and simd_max instead.
|
/// Lexicographic order. For the SIMD elementwise minimum and maximum, use simd_min and simd_max instead.
|
||||||
impl<T, const N: usize> Ord for Simd<T, N>
|
impl<T, const N: usize> Ord for Simd<T, N>
|
||||||
where
|
where
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
T: SimdElement + Ord,
|
T: SimdElement + Ord,
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
@ -997,7 +961,6 @@ where
|
||||||
|
|
||||||
impl<T, const N: usize> core::hash::Hash for Simd<T, N>
|
impl<T, const N: usize> core::hash::Hash for Simd<T, N>
|
||||||
where
|
where
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
T: SimdElement + core::hash::Hash,
|
T: SimdElement + core::hash::Hash,
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
@ -1012,7 +975,6 @@ where
|
||||||
// array references
|
// array references
|
||||||
impl<T, const N: usize> AsRef<[T; N]> for Simd<T, N>
|
impl<T, const N: usize> AsRef<[T; N]> for Simd<T, N>
|
||||||
where
|
where
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
T: SimdElement,
|
T: SimdElement,
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
@ -1023,7 +985,6 @@ where
|
||||||
|
|
||||||
impl<T, const N: usize> AsMut<[T; N]> for Simd<T, N>
|
impl<T, const N: usize> AsMut<[T; N]> for Simd<T, N>
|
||||||
where
|
where
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
T: SimdElement,
|
T: SimdElement,
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
@ -1035,7 +996,6 @@ where
|
||||||
// slice references
|
// slice references
|
||||||
impl<T, const N: usize> AsRef<[T]> for Simd<T, N>
|
impl<T, const N: usize> AsRef<[T]> for Simd<T, N>
|
||||||
where
|
where
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
T: SimdElement,
|
T: SimdElement,
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
@ -1046,7 +1006,6 @@ where
|
||||||
|
|
||||||
impl<T, const N: usize> AsMut<[T]> for Simd<T, N>
|
impl<T, const N: usize> AsMut<[T]> for Simd<T, N>
|
||||||
where
|
where
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
T: SimdElement,
|
T: SimdElement,
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
@ -1058,7 +1017,6 @@ where
|
||||||
// vector/array conversion
|
// vector/array conversion
|
||||||
impl<T, const N: usize> From<[T; N]> for Simd<T, N>
|
impl<T, const N: usize> From<[T; N]> for Simd<T, N>
|
||||||
where
|
where
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
T: SimdElement,
|
T: SimdElement,
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
@ -1069,7 +1027,6 @@ where
|
||||||
|
|
||||||
impl<T, const N: usize> From<Simd<T, N>> for [T; N]
|
impl<T, const N: usize> From<Simd<T, N>> for [T; N]
|
||||||
where
|
where
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
T: SimdElement,
|
T: SimdElement,
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
@ -1080,7 +1037,6 @@ where
|
||||||
|
|
||||||
impl<T, const N: usize> TryFrom<&[T]> for Simd<T, N>
|
impl<T, const N: usize> TryFrom<&[T]> for Simd<T, N>
|
||||||
where
|
where
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
T: SimdElement,
|
T: SimdElement,
|
||||||
{
|
{
|
||||||
type Error = core::array::TryFromSliceError;
|
type Error = core::array::TryFromSliceError;
|
||||||
|
|
@ -1093,7 +1049,6 @@ where
|
||||||
|
|
||||||
impl<T, const N: usize> TryFrom<&mut [T]> for Simd<T, N>
|
impl<T, const N: usize> TryFrom<&mut [T]> for Simd<T, N>
|
||||||
where
|
where
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
T: SimdElement,
|
T: SimdElement,
|
||||||
{
|
{
|
||||||
type Error = core::array::TryFromSliceError;
|
type Error = core::array::TryFromSliceError;
|
||||||
|
|
@ -1231,10 +1186,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn lane_indices<const N: usize>() -> Simd<usize, N>
|
fn lane_indices<const N: usize>() -> Simd<usize, N> {
|
||||||
where
|
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
|
||||||
#![allow(clippy::needless_range_loop)]
|
#![allow(clippy::needless_range_loop)]
|
||||||
let mut index = [0; N];
|
let mut index = [0; N];
|
||||||
for i in 0..N {
|
for i in 0..N {
|
||||||
|
|
@ -1246,7 +1198,6 @@ where
|
||||||
#[inline]
|
#[inline]
|
||||||
fn mask_up_to<M, const N: usize>(len: usize) -> Mask<M, N>
|
fn mask_up_to<M, const N: usize>(len: usize) -> Mask<M, N>
|
||||||
where
|
where
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
M: MaskElement,
|
M: MaskElement,
|
||||||
{
|
{
|
||||||
let index = lane_indices::<N>();
|
let index = lane_indices::<N>();
|
||||||
|
|
|
||||||
|
|
@ -1,31 +1,26 @@
|
||||||
use crate::simd::*;
|
use crate::simd::*;
|
||||||
use core::arch::loongarch64::*;
|
use core::arch::loongarch64::*;
|
||||||
|
|
||||||
from_transmute! { unsafe u8x16 => v16u8 }
|
from_transmute! { unsafe u8x16 => m128i }
|
||||||
from_transmute! { unsafe u8x32 => v32u8 }
|
from_transmute! { unsafe u8x32 => m256i }
|
||||||
from_transmute! { unsafe i8x16 => v16i8 }
|
from_transmute! { unsafe i8x16 => m128i }
|
||||||
from_transmute! { unsafe i8x32 => v32i8 }
|
from_transmute! { unsafe i8x32 => m256i }
|
||||||
|
|
||||||
from_transmute! { unsafe u16x8 => v8u16 }
|
from_transmute! { unsafe u16x8 => m128i }
|
||||||
from_transmute! { unsafe u16x16 => v16u16 }
|
from_transmute! { unsafe u16x16 => m256i }
|
||||||
from_transmute! { unsafe i16x8 => v8i16 }
|
from_transmute! { unsafe i16x8 => m128i }
|
||||||
from_transmute! { unsafe i16x16 => v16i16 }
|
from_transmute! { unsafe i16x16 => m256i }
|
||||||
|
|
||||||
from_transmute! { unsafe u32x4 => v4u32 }
|
from_transmute! { unsafe u32x4 => m128i }
|
||||||
from_transmute! { unsafe u32x8 => v8u32 }
|
from_transmute! { unsafe u32x8 => m256i }
|
||||||
from_transmute! { unsafe i32x4 => v4i32 }
|
from_transmute! { unsafe i32x4 => m128i }
|
||||||
from_transmute! { unsafe i32x8 => v8i32 }
|
from_transmute! { unsafe i32x8 => m256i }
|
||||||
from_transmute! { unsafe f32x4 => v4f32 }
|
from_transmute! { unsafe f32x4 => m128 }
|
||||||
from_transmute! { unsafe f32x8 => v8f32 }
|
from_transmute! { unsafe f32x8 => m256 }
|
||||||
|
|
||||||
from_transmute! { unsafe u64x2 => v2u64 }
|
from_transmute! { unsafe u64x2 => m128i }
|
||||||
from_transmute! { unsafe u64x4 => v4u64 }
|
from_transmute! { unsafe u64x4 => m256i }
|
||||||
from_transmute! { unsafe i64x2 => v2i64 }
|
from_transmute! { unsafe i64x2 => m128i }
|
||||||
from_transmute! { unsafe i64x4 => v4i64 }
|
from_transmute! { unsafe i64x4 => m256i }
|
||||||
from_transmute! { unsafe f64x2 => v2f64 }
|
from_transmute! { unsafe f64x2 => m128d }
|
||||||
from_transmute! { unsafe f64x4 => v4f64 }
|
from_transmute! { unsafe f64x4 => m256d }
|
||||||
|
|
||||||
from_transmute! { unsafe usizex2 => v2u64 }
|
|
||||||
from_transmute! { unsafe usizex4 => v4u64 }
|
|
||||||
from_transmute! { unsafe isizex2 => v2i64 }
|
|
||||||
from_transmute! { unsafe isizex4 => v4i64 }
|
|
||||||
|
|
|
||||||
|
|
@ -14,17 +14,3 @@ from_transmute! { unsafe f32x4 => v128 }
|
||||||
from_transmute! { unsafe u64x2 => v128 }
|
from_transmute! { unsafe u64x2 => v128 }
|
||||||
from_transmute! { unsafe i64x2 => v128 }
|
from_transmute! { unsafe i64x2 => v128 }
|
||||||
from_transmute! { unsafe f64x2 => v128 }
|
from_transmute! { unsafe f64x2 => v128 }
|
||||||
|
|
||||||
#[cfg(target_pointer_width = "32")]
|
|
||||||
mod p32 {
|
|
||||||
use super::*;
|
|
||||||
from_transmute! { unsafe usizex4 => v128 }
|
|
||||||
from_transmute! { unsafe isizex4 => v128 }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_pointer_width = "64")]
|
|
||||||
mod p64 {
|
|
||||||
use super::*;
|
|
||||||
from_transmute! { unsafe usizex2 => v128 }
|
|
||||||
from_transmute! { unsafe isizex2 => v128 }
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -39,25 +39,3 @@ from_transmute! { unsafe i64x8 => __m512i }
|
||||||
from_transmute! { unsafe f64x2 => __m128d }
|
from_transmute! { unsafe f64x2 => __m128d }
|
||||||
from_transmute! { unsafe f64x4 => __m256d }
|
from_transmute! { unsafe f64x4 => __m256d }
|
||||||
from_transmute! { unsafe f64x8 => __m512d }
|
from_transmute! { unsafe f64x8 => __m512d }
|
||||||
|
|
||||||
#[cfg(target_pointer_width = "32")]
|
|
||||||
mod p32 {
|
|
||||||
use super::*;
|
|
||||||
from_transmute! { unsafe usizex4 => __m128i }
|
|
||||||
from_transmute! { unsafe usizex8 => __m256i }
|
|
||||||
from_transmute! { unsafe Simd<usize, 16> => __m512i }
|
|
||||||
from_transmute! { unsafe isizex4 => __m128i }
|
|
||||||
from_transmute! { unsafe isizex8 => __m256i }
|
|
||||||
from_transmute! { unsafe Simd<isize, 16> => __m512i }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_pointer_width = "64")]
|
|
||||||
mod p64 {
|
|
||||||
use super::*;
|
|
||||||
from_transmute! { unsafe usizex2 => __m128i }
|
|
||||||
from_transmute! { unsafe usizex4 => __m256i }
|
|
||||||
from_transmute! { unsafe usizex8 => __m512i }
|
|
||||||
from_transmute! { unsafe isizex2 => __m128i }
|
|
||||||
from_transmute! { unsafe isizex4 => __m256i }
|
|
||||||
from_transmute! { unsafe isizex8 => __m512i }
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -65,9 +65,9 @@ macro_rules! test_mask_api {
|
||||||
fn roundtrip_int_conversion() {
|
fn roundtrip_int_conversion() {
|
||||||
let values = [true, false, false, true, false, false, true, false];
|
let values = [true, false, false, true, false, false, true, false];
|
||||||
let mask = Mask::<$type, 8>::from_array(values);
|
let mask = Mask::<$type, 8>::from_array(values);
|
||||||
let int = mask.to_int();
|
let int = mask.to_simd();
|
||||||
assert_eq!(int.to_array(), [-1, 0, 0, -1, 0, 0, -1, 0]);
|
assert_eq!(int.to_array(), [-1, 0, 0, -1, 0, 0, -1, 0]);
|
||||||
assert_eq!(Mask::<$type, 8>::from_int(int), mask);
|
assert_eq!(Mask::<$type, 8>::from_simd(int), mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ use core_simd::simd;
|
||||||
|
|
||||||
use core::intrinsics::simd as intrinsics;
|
use core::intrinsics::simd as intrinsics;
|
||||||
|
|
||||||
use simd::{LaneCount, Simd, SupportedLaneCount};
|
use simd::Simd;
|
||||||
|
|
||||||
#[cfg(feature = "as_crate")]
|
#[cfg(feature = "as_crate")]
|
||||||
mod experimental {
|
mod experimental {
|
||||||
|
|
@ -66,28 +66,43 @@ pub trait StdFloat: Sealed + Sized {
|
||||||
|
|
||||||
/// Produces a vector where every element has the sine of the value
|
/// Produces a vector where every element has the sine of the value
|
||||||
/// in the equivalently-indexed element in `self`.
|
/// in the equivalently-indexed element in `self`.
|
||||||
|
#[inline]
|
||||||
#[must_use = "method returns a new vector and does not mutate the original value"]
|
#[must_use = "method returns a new vector and does not mutate the original value"]
|
||||||
fn sin(self) -> Self;
|
fn sin(self) -> Self {
|
||||||
|
unsafe { intrinsics::simd_fsin(self) }
|
||||||
|
}
|
||||||
|
|
||||||
/// Produces a vector where every element has the cosine of the value
|
/// Produces a vector where every element has the cosine of the value
|
||||||
/// in the equivalently-indexed element in `self`.
|
/// in the equivalently-indexed element in `self`.
|
||||||
|
#[inline]
|
||||||
#[must_use = "method returns a new vector and does not mutate the original value"]
|
#[must_use = "method returns a new vector and does not mutate the original value"]
|
||||||
fn cos(self) -> Self;
|
fn cos(self) -> Self {
|
||||||
|
unsafe { intrinsics::simd_fcos(self) }
|
||||||
|
}
|
||||||
|
|
||||||
/// Produces a vector where every element has the exponential (base e) of the value
|
/// Produces a vector where every element has the exponential (base e) of the value
|
||||||
/// in the equivalently-indexed element in `self`.
|
/// in the equivalently-indexed element in `self`.
|
||||||
|
#[inline]
|
||||||
#[must_use = "method returns a new vector and does not mutate the original value"]
|
#[must_use = "method returns a new vector and does not mutate the original value"]
|
||||||
fn exp(self) -> Self;
|
fn exp(self) -> Self {
|
||||||
|
unsafe { intrinsics::simd_fexp(self) }
|
||||||
|
}
|
||||||
|
|
||||||
/// Produces a vector where every element has the exponential (base 2) of the value
|
/// Produces a vector where every element has the exponential (base 2) of the value
|
||||||
/// in the equivalently-indexed element in `self`.
|
/// in the equivalently-indexed element in `self`.
|
||||||
|
#[inline]
|
||||||
#[must_use = "method returns a new vector and does not mutate the original value"]
|
#[must_use = "method returns a new vector and does not mutate the original value"]
|
||||||
fn exp2(self) -> Self;
|
fn exp2(self) -> Self {
|
||||||
|
unsafe { intrinsics::simd_fexp2(self) }
|
||||||
|
}
|
||||||
|
|
||||||
/// Produces a vector where every element has the natural logarithm of the value
|
/// Produces a vector where every element has the natural logarithm of the value
|
||||||
/// in the equivalently-indexed element in `self`.
|
/// in the equivalently-indexed element in `self`.
|
||||||
|
#[inline]
|
||||||
#[must_use = "method returns a new vector and does not mutate the original value"]
|
#[must_use = "method returns a new vector and does not mutate the original value"]
|
||||||
fn ln(self) -> Self;
|
fn ln(self) -> Self {
|
||||||
|
unsafe { intrinsics::simd_flog(self) }
|
||||||
|
}
|
||||||
|
|
||||||
/// Produces a vector where every element has the logarithm with respect to an arbitrary
|
/// Produces a vector where every element has the logarithm with respect to an arbitrary
|
||||||
/// in the equivalently-indexed elements in `self` and `base`.
|
/// in the equivalently-indexed elements in `self` and `base`.
|
||||||
|
|
@ -99,13 +114,19 @@ pub trait StdFloat: Sealed + Sized {
|
||||||
|
|
||||||
/// Produces a vector where every element has the base-2 logarithm of the value
|
/// Produces a vector where every element has the base-2 logarithm of the value
|
||||||
/// in the equivalently-indexed element in `self`.
|
/// in the equivalently-indexed element in `self`.
|
||||||
|
#[inline]
|
||||||
#[must_use = "method returns a new vector and does not mutate the original value"]
|
#[must_use = "method returns a new vector and does not mutate the original value"]
|
||||||
fn log2(self) -> Self;
|
fn log2(self) -> Self {
|
||||||
|
unsafe { intrinsics::simd_flog2(self) }
|
||||||
|
}
|
||||||
|
|
||||||
/// Produces a vector where every element has the base-10 logarithm of the value
|
/// Produces a vector where every element has the base-10 logarithm of the value
|
||||||
/// in the equivalently-indexed element in `self`.
|
/// in the equivalently-indexed element in `self`.
|
||||||
|
#[inline]
|
||||||
#[must_use = "method returns a new vector and does not mutate the original value"]
|
#[must_use = "method returns a new vector and does not mutate the original value"]
|
||||||
fn log10(self) -> Self;
|
fn log10(self) -> Self {
|
||||||
|
unsafe { intrinsics::simd_flog10(self) }
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the smallest integer greater than or equal to each element.
|
/// Returns the smallest integer greater than or equal to each element.
|
||||||
#[must_use = "method returns a new vector and does not mutate the original value"]
|
#[must_use = "method returns a new vector and does not mutate the original value"]
|
||||||
|
|
@ -140,68 +161,19 @@ pub trait StdFloat: Sealed + Sized {
|
||||||
fn fract(self) -> Self;
|
fn fract(self) -> Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<const N: usize> Sealed for Simd<f32, N> where LaneCount<N>: SupportedLaneCount {}
|
impl<const N: usize> Sealed for Simd<f32, N> {}
|
||||||
impl<const N: usize> Sealed for Simd<f64, N> where LaneCount<N>: SupportedLaneCount {}
|
impl<const N: usize> Sealed for Simd<f64, N> {}
|
||||||
|
|
||||||
macro_rules! impl_float {
|
impl<const N: usize> StdFloat for Simd<f32, N> {
|
||||||
{
|
#[inline]
|
||||||
$($fn:ident: $intrinsic:ident,)*
|
fn fract(self) -> Self {
|
||||||
} => {
|
self - self.trunc()
|
||||||
impl<const N: usize> StdFloat for Simd<f32, N>
|
|
||||||
where
|
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
|
||||||
#[inline]
|
|
||||||
fn fract(self) -> Self {
|
|
||||||
self - self.trunc()
|
|
||||||
}
|
|
||||||
|
|
||||||
$(
|
|
||||||
#[inline]
|
|
||||||
fn $fn(self) -> Self {
|
|
||||||
unsafe { intrinsics::$intrinsic(self) }
|
|
||||||
}
|
|
||||||
)*
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<const N: usize> StdFloat for Simd<f64, N>
|
|
||||||
where
|
|
||||||
LaneCount<N>: SupportedLaneCount,
|
|
||||||
{
|
|
||||||
#[inline]
|
|
||||||
fn fract(self) -> Self {
|
|
||||||
self - self.trunc()
|
|
||||||
}
|
|
||||||
|
|
||||||
$(
|
|
||||||
#[inline]
|
|
||||||
fn $fn(self) -> Self {
|
|
||||||
// https://github.com/llvm/llvm-project/issues/83729
|
|
||||||
#[cfg(target_arch = "aarch64")]
|
|
||||||
{
|
|
||||||
let mut ln = Self::splat(0f64);
|
|
||||||
for i in 0..N {
|
|
||||||
ln[i] = self[i].$fn()
|
|
||||||
}
|
|
||||||
ln
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(target_arch = "aarch64"))]
|
|
||||||
{
|
|
||||||
unsafe { intrinsics::$intrinsic(self) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)*
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_float! {
|
impl<const N: usize> StdFloat for Simd<f64, N> {
|
||||||
sin: simd_fsin,
|
#[inline]
|
||||||
cos: simd_fcos,
|
fn fract(self) -> Self {
|
||||||
exp: simd_fexp,
|
self - self.trunc()
|
||||||
exp2: simd_fexp2,
|
}
|
||||||
ln: simd_flog,
|
|
||||||
log2: simd_flog2,
|
|
||||||
log10: simd_flog10,
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,15 +16,33 @@ macro_rules! unary_test {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! binary_test {
|
macro_rules! unary_approx_test {
|
||||||
{ $scalar:tt, $($func:tt),+ } => {
|
{ $scalar:tt, $($func:tt),+ } => {
|
||||||
test_helpers::test_lanes! {
|
test_helpers::test_lanes! {
|
||||||
$(
|
$(
|
||||||
fn $func<const LANES: usize>() {
|
fn $func<const LANES: usize>() {
|
||||||
test_helpers::test_binary_elementwise(
|
test_helpers::test_unary_elementwise_approx(
|
||||||
|
&core_simd::simd::Simd::<$scalar, LANES>::$func,
|
||||||
|
&$scalar::$func,
|
||||||
|
&|_| true,
|
||||||
|
8,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! binary_approx_test {
|
||||||
|
{ $scalar:tt, $($func:tt),+ } => {
|
||||||
|
test_helpers::test_lanes! {
|
||||||
|
$(
|
||||||
|
fn $func<const LANES: usize>() {
|
||||||
|
test_helpers::test_binary_elementwise_approx(
|
||||||
&core_simd::simd::Simd::<$scalar, LANES>::$func,
|
&core_simd::simd::Simd::<$scalar, LANES>::$func,
|
||||||
&$scalar::$func,
|
&$scalar::$func,
|
||||||
&|_, _| true,
|
&|_, _| true,
|
||||||
|
16,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)*
|
)*
|
||||||
|
|
@ -53,10 +71,13 @@ macro_rules! impl_tests {
|
||||||
mod $scalar {
|
mod $scalar {
|
||||||
use std_float::StdFloat;
|
use std_float::StdFloat;
|
||||||
|
|
||||||
unary_test! { $scalar, sqrt, sin, cos, exp, exp2, ln, log2, log10, ceil, floor, round, trunc }
|
unary_test! { $scalar, sqrt, ceil, floor, round, trunc }
|
||||||
binary_test! { $scalar, log }
|
|
||||||
ternary_test! { $scalar, mul_add }
|
ternary_test! { $scalar, mul_add }
|
||||||
|
|
||||||
|
// https://github.com/rust-lang/miri/issues/3555
|
||||||
|
unary_approx_test! { $scalar, sin, cos, exp, exp2, ln, log2, log10 }
|
||||||
|
binary_approx_test! { $scalar, log }
|
||||||
|
|
||||||
test_helpers::test_lanes! {
|
test_helpers::test_lanes! {
|
||||||
fn fract<const LANES: usize>() {
|
fn fract<const LANES: usize>() {
|
||||||
test_helpers::test_unary_elementwise_flush_subnormals(
|
test_helpers::test_unary_elementwise_flush_subnormals(
|
||||||
|
|
|
||||||
|
|
@ -6,3 +6,4 @@ publish = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
proptest = { version = "0.10", default-features = false, features = ["alloc"] }
|
proptest = { version = "0.10", default-features = false, features = ["alloc"] }
|
||||||
|
float-cmp = "0.10"
|
||||||
|
|
|
||||||
110
library/portable-simd/crates/test_helpers/src/approxeq.rs
Normal file
110
library/portable-simd/crates/test_helpers/src/approxeq.rs
Normal file
|
|
@ -0,0 +1,110 @@
|
||||||
|
//! Compare numeric types approximately.
|
||||||
|
|
||||||
|
use float_cmp::Ulps;
|
||||||
|
|
||||||
|
pub trait ApproxEq {
|
||||||
|
fn approxeq(&self, other: &Self, _ulps: i64) -> bool;
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ApproxEq for bool {
|
||||||
|
fn approxeq(&self, other: &Self, _ulps: i64) -> bool {
|
||||||
|
self == other
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||||
|
write!(f, "{:?}", self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! impl_integer_approxeq {
|
||||||
|
{ $($type:ty),* } => {
|
||||||
|
$(
|
||||||
|
impl ApproxEq for $type {
|
||||||
|
fn approxeq(&self, other: &Self, _ulps: i64) -> bool {
|
||||||
|
self == other
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||||
|
write!(f, "{:?} ({:x})", self, self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)*
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_integer_approxeq! { u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize }
|
||||||
|
|
||||||
|
macro_rules! impl_float_approxeq {
|
||||||
|
{ $($type:ty),* } => {
|
||||||
|
$(
|
||||||
|
impl ApproxEq for $type {
|
||||||
|
fn approxeq(&self, other: &Self, ulps: i64) -> bool {
|
||||||
|
if self.is_nan() && other.is_nan() {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
(self.ulps(other) as i64).abs() <= ulps
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||||
|
write!(f, "{:?} ({:x})", self, self.to_bits())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)*
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_float_approxeq! { f32, f64 }
|
||||||
|
|
||||||
|
impl<T: ApproxEq, const N: usize> ApproxEq for [T; N] {
|
||||||
|
fn approxeq(&self, other: &Self, ulps: i64) -> bool {
|
||||||
|
self.iter()
|
||||||
|
.zip(other.iter())
|
||||||
|
.fold(true, |value, (left, right)| {
|
||||||
|
value && left.approxeq(right, ulps)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||||
|
#[repr(transparent)]
|
||||||
|
struct Wrapper<'a, T: ApproxEq>(&'a T);
|
||||||
|
|
||||||
|
impl<T: ApproxEq> core::fmt::Debug for Wrapper<'_, T> {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||||
|
self.0.fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
f.debug_list()
|
||||||
|
.entries(self.iter().map(|x| Wrapper(x)))
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub struct ApproxEqWrapper<'a, T>(pub &'a T, pub i64);
|
||||||
|
|
||||||
|
impl<T: ApproxEq> PartialEq<T> for ApproxEqWrapper<'_, T> {
|
||||||
|
fn eq(&self, other: &T) -> bool {
|
||||||
|
self.0.approxeq(other, self.1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ApproxEq> core::fmt::Debug for ApproxEqWrapper<'_, T> {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||||
|
self.0.fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! prop_assert_approxeq {
|
||||||
|
{ $a:expr, $b:expr, $ulps:expr $(,)? } => {
|
||||||
|
{
|
||||||
|
use $crate::approxeq::ApproxEqWrapper;
|
||||||
|
let a = $a;
|
||||||
|
let b = $b;
|
||||||
|
proptest::prop_assert_eq!(ApproxEqWrapper(&a, $ulps), b);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -12,6 +12,9 @@ pub mod wasm;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
pub mod biteq;
|
pub mod biteq;
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
pub mod approxeq;
|
||||||
|
|
||||||
pub mod subnormals;
|
pub mod subnormals;
|
||||||
use subnormals::FlushSubnormals;
|
use subnormals::FlushSubnormals;
|
||||||
|
|
||||||
|
|
@ -185,6 +188,41 @@ pub fn test_unary_elementwise<Scalar, ScalarResult, Vector, VectorResult, const
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Test a unary vector function against a unary scalar function, applied elementwise.
|
||||||
|
///
|
||||||
|
/// Floats are checked approximately.
|
||||||
|
pub fn test_unary_elementwise_approx<
|
||||||
|
Scalar,
|
||||||
|
ScalarResult,
|
||||||
|
Vector,
|
||||||
|
VectorResult,
|
||||||
|
const LANES: usize,
|
||||||
|
>(
|
||||||
|
fv: &dyn Fn(Vector) -> VectorResult,
|
||||||
|
fs: &dyn Fn(Scalar) -> ScalarResult,
|
||||||
|
check: &dyn Fn([Scalar; LANES]) -> bool,
|
||||||
|
ulps: i64,
|
||||||
|
) where
|
||||||
|
Scalar: Copy + core::fmt::Debug + DefaultStrategy,
|
||||||
|
ScalarResult: Copy + approxeq::ApproxEq + core::fmt::Debug + DefaultStrategy,
|
||||||
|
Vector: Into<[Scalar; LANES]> + From<[Scalar; LANES]> + Copy,
|
||||||
|
VectorResult: Into<[ScalarResult; LANES]> + From<[ScalarResult; LANES]> + Copy,
|
||||||
|
{
|
||||||
|
test_1(&|x: [Scalar; LANES]| {
|
||||||
|
proptest::prop_assume!(check(x));
|
||||||
|
let result_1: [ScalarResult; LANES] = fv(x.into()).into();
|
||||||
|
let result_2: [ScalarResult; LANES] = x
|
||||||
|
.iter()
|
||||||
|
.copied()
|
||||||
|
.map(fs)
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.try_into()
|
||||||
|
.unwrap();
|
||||||
|
crate::prop_assert_approxeq!(result_1, result_2, ulps);
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/// Test a unary vector function against a unary scalar function, applied elementwise.
|
/// Test a unary vector function against a unary scalar function, applied elementwise.
|
||||||
///
|
///
|
||||||
/// Where subnormals are flushed, use approximate equality.
|
/// Where subnormals are flushed, use approximate equality.
|
||||||
|
|
@ -290,6 +328,44 @@ pub fn test_binary_elementwise<
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Test a binary vector function against a binary scalar function, applied elementwise.
|
||||||
|
pub fn test_binary_elementwise_approx<
|
||||||
|
Scalar1,
|
||||||
|
Scalar2,
|
||||||
|
ScalarResult,
|
||||||
|
Vector1,
|
||||||
|
Vector2,
|
||||||
|
VectorResult,
|
||||||
|
const LANES: usize,
|
||||||
|
>(
|
||||||
|
fv: &dyn Fn(Vector1, Vector2) -> VectorResult,
|
||||||
|
fs: &dyn Fn(Scalar1, Scalar2) -> ScalarResult,
|
||||||
|
check: &dyn Fn([Scalar1; LANES], [Scalar2; LANES]) -> bool,
|
||||||
|
ulps: i64,
|
||||||
|
) where
|
||||||
|
Scalar1: Copy + core::fmt::Debug + DefaultStrategy,
|
||||||
|
Scalar2: Copy + core::fmt::Debug + DefaultStrategy,
|
||||||
|
ScalarResult: Copy + approxeq::ApproxEq + core::fmt::Debug + DefaultStrategy,
|
||||||
|
Vector1: Into<[Scalar1; LANES]> + From<[Scalar1; LANES]> + Copy,
|
||||||
|
Vector2: Into<[Scalar2; LANES]> + From<[Scalar2; LANES]> + Copy,
|
||||||
|
VectorResult: Into<[ScalarResult; LANES]> + From<[ScalarResult; LANES]> + Copy,
|
||||||
|
{
|
||||||
|
test_2(&|x: [Scalar1; LANES], y: [Scalar2; LANES]| {
|
||||||
|
proptest::prop_assume!(check(x, y));
|
||||||
|
let result_1: [ScalarResult; LANES] = fv(x.into(), y.into()).into();
|
||||||
|
let result_2: [ScalarResult; LANES] = x
|
||||||
|
.iter()
|
||||||
|
.copied()
|
||||||
|
.zip(y.iter().copied())
|
||||||
|
.map(|(x, y)| fs(x, y))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.try_into()
|
||||||
|
.unwrap();
|
||||||
|
crate::prop_assert_approxeq!(result_1, result_2, ulps);
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/// Test a binary vector function against a binary scalar function, applied elementwise.
|
/// Test a binary vector function against a binary scalar function, applied elementwise.
|
||||||
///
|
///
|
||||||
/// Where subnormals are flushed, use approximate equality.
|
/// Where subnormals are flushed, use approximate equality.
|
||||||
|
|
@ -528,8 +604,6 @@ macro_rules! test_lanes {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
fn implementation<const $lanes: usize>()
|
fn implementation<const $lanes: usize>()
|
||||||
where
|
|
||||||
core_simd::simd::LaneCount<$lanes>: core_simd::simd::SupportedLaneCount,
|
|
||||||
$body
|
$body
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
|
@ -628,8 +702,6 @@ macro_rules! test_lanes_panic {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
fn implementation<const $lanes: usize>()
|
fn implementation<const $lanes: usize>()
|
||||||
where
|
|
||||||
core_simd::simd::LaneCount<$lanes>: core_simd::simd::SupportedLaneCount,
|
|
||||||
$body
|
$body
|
||||||
|
|
||||||
// test some odd and even non-power-of-2 lengths on miri
|
// test some odd and even non-power-of-2 lengths on miri
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
[toolchain]
|
[toolchain]
|
||||||
channel = "nightly-2025-01-16"
|
channel = "nightly-2026-01-26"
|
||||||
components = ["rustfmt", "clippy", "miri", "rust-src"]
|
components = ["rustfmt", "clippy", "miri", "rust-src"]
|
||||||
|
|
|
||||||
|
|
@ -471,7 +471,7 @@ fn simd_ops_i32() {
|
||||||
fn simd_mask() {
|
fn simd_mask() {
|
||||||
use std::intrinsics::simd::*;
|
use std::intrinsics::simd::*;
|
||||||
|
|
||||||
let intmask = Mask::from_int(i32x4::from_array([0, -1, 0, 0]));
|
let intmask = Mask::from_simd(i32x4::from_array([0, -1, 0, 0]));
|
||||||
assert_eq!(intmask, Mask::from_array([false, true, false, false]));
|
assert_eq!(intmask, Mask::from_array([false, true, false, false]));
|
||||||
assert_eq!(intmask.to_array(), [false, true, false, false]);
|
assert_eq!(intmask.to_array(), [false, true, false, false]);
|
||||||
|
|
||||||
|
|
@ -486,8 +486,8 @@ fn simd_mask() {
|
||||||
|
|
||||||
// Also directly call intrinsic, to test both kinds of return types.
|
// Also directly call intrinsic, to test both kinds of return types.
|
||||||
unsafe {
|
unsafe {
|
||||||
let bitmask1: u16 = simd_bitmask(mask.to_int());
|
let bitmask1: u16 = simd_bitmask(mask.to_simd());
|
||||||
let bitmask2: [u8; 2] = simd_bitmask(mask.to_int());
|
let bitmask2: [u8; 2] = simd_bitmask(mask.to_simd());
|
||||||
if cfg!(target_endian = "little") {
|
if cfg!(target_endian = "little") {
|
||||||
assert_eq!(bitmask1, 0b1010001101001001);
|
assert_eq!(bitmask1, 0b1010001101001001);
|
||||||
assert_eq!(bitmask2, [0b01001001, 0b10100011]);
|
assert_eq!(bitmask2, [0b01001001, 0b10100011]);
|
||||||
|
|
@ -506,8 +506,8 @@ fn simd_mask() {
|
||||||
assert_eq!(bitmask, 0b1000);
|
assert_eq!(bitmask, 0b1000);
|
||||||
assert_eq!(Mask::<i64, 4>::from_bitmask(bitmask), mask);
|
assert_eq!(Mask::<i64, 4>::from_bitmask(bitmask), mask);
|
||||||
unsafe {
|
unsafe {
|
||||||
let bitmask1: u8 = simd_bitmask(mask.to_int());
|
let bitmask1: u8 = simd_bitmask(mask.to_simd());
|
||||||
let bitmask2: [u8; 1] = simd_bitmask(mask.to_int());
|
let bitmask2: [u8; 1] = simd_bitmask(mask.to_simd());
|
||||||
if cfg!(target_endian = "little") {
|
if cfg!(target_endian = "little") {
|
||||||
assert_eq!(bitmask1, 0b1000);
|
assert_eq!(bitmask1, 0b1000);
|
||||||
assert_eq!(bitmask2, [0b1000]);
|
assert_eq!(bitmask2, [0b1000]);
|
||||||
|
|
|
||||||
|
|
@ -80,20 +80,17 @@ struct InvalidNestedStructAttr {}
|
||||||
|
|
||||||
#[derive(Diagnostic)]
|
#[derive(Diagnostic)]
|
||||||
#[diag(nonsense("foo"), code = E0123, slug = "foo")]
|
#[diag(nonsense("foo"), code = E0123, slug = "foo")]
|
||||||
//~^ ERROR diagnostic slug must be the first argument
|
//~^ ERROR derive(Diagnostic): diagnostic slug not specified
|
||||||
//~| ERROR diagnostic slug not specified
|
|
||||||
struct InvalidNestedStructAttr1 {}
|
struct InvalidNestedStructAttr1 {}
|
||||||
|
|
||||||
#[derive(Diagnostic)]
|
#[derive(Diagnostic)]
|
||||||
#[diag(nonsense = "...", code = E0123, slug = "foo")]
|
#[diag(nonsense = "...", code = E0123, slug = "foo")]
|
||||||
//~^ ERROR unknown argument
|
//~^ ERROR diagnostic slug not specified
|
||||||
//~| ERROR diagnostic slug not specified
|
|
||||||
struct InvalidNestedStructAttr2 {}
|
struct InvalidNestedStructAttr2 {}
|
||||||
|
|
||||||
#[derive(Diagnostic)]
|
#[derive(Diagnostic)]
|
||||||
#[diag(nonsense = 4, code = E0123, slug = "foo")]
|
#[diag(nonsense = 4, code = E0123, slug = "foo")]
|
||||||
//~^ ERROR unknown argument
|
//~^ ERROR diagnostic slug not specified
|
||||||
//~| ERROR diagnostic slug not specified
|
|
||||||
struct InvalidNestedStructAttr3 {}
|
struct InvalidNestedStructAttr3 {}
|
||||||
|
|
||||||
#[derive(Diagnostic)]
|
#[derive(Diagnostic)]
|
||||||
|
|
@ -113,7 +110,6 @@ struct WrongPlaceField {
|
||||||
#[diag(no_crate_example, code = E0123)]
|
#[diag(no_crate_example, code = E0123)]
|
||||||
#[diag(no_crate_example, code = E0456)]
|
#[diag(no_crate_example, code = E0456)]
|
||||||
//~^ ERROR specified multiple times
|
//~^ ERROR specified multiple times
|
||||||
//~^^ ERROR specified multiple times
|
|
||||||
struct DiagSpecifiedTwice {}
|
struct DiagSpecifiedTwice {}
|
||||||
|
|
||||||
#[derive(Diagnostic)]
|
#[derive(Diagnostic)]
|
||||||
|
|
@ -543,7 +539,7 @@ struct LabelWithTrailingPath {
|
||||||
#[diag(no_crate_example, code = E0123)]
|
#[diag(no_crate_example, code = E0123)]
|
||||||
struct LabelWithTrailingNameValue {
|
struct LabelWithTrailingNameValue {
|
||||||
#[label(no_crate_label, foo = "...")]
|
#[label(no_crate_label, foo = "...")]
|
||||||
//~^ ERROR only `no_span` is a valid nested attribute
|
//~^ ERROR no nested attribute expected here
|
||||||
span: Span,
|
span: Span,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -551,7 +547,7 @@ struct LabelWithTrailingNameValue {
|
||||||
#[diag(no_crate_example, code = E0123)]
|
#[diag(no_crate_example, code = E0123)]
|
||||||
struct LabelWithTrailingList {
|
struct LabelWithTrailingList {
|
||||||
#[label(no_crate_label, foo("..."))]
|
#[label(no_crate_label, foo("..."))]
|
||||||
//~^ ERROR only `no_span` is a valid nested attribute
|
//~^ ERROR no nested attribute expected here
|
||||||
span: Span,
|
span: Span,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -807,7 +803,7 @@ struct SuggestionsInvalidItem {
|
||||||
sub: Span,
|
sub: Span,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Diagnostic)] //~ ERROR cannot find value `__code_34` in this scope
|
#[derive(Diagnostic)]
|
||||||
#[diag(no_crate_example)]
|
#[diag(no_crate_example)]
|
||||||
struct SuggestionsInvalidLiteral {
|
struct SuggestionsInvalidLiteral {
|
||||||
#[suggestion(code = 3)]
|
#[suggestion(code = 3)]
|
||||||
|
|
|
||||||
|
|
@ -48,12 +48,6 @@ LL | #[diag(code = E0123)]
|
||||||
|
|
|
|
||||||
= help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]`
|
= help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]`
|
||||||
|
|
||||||
error: derive(Diagnostic): diagnostic slug must be the first argument
|
|
||||||
--> $DIR/diagnostic-derive.rs:82:16
|
|
||||||
|
|
|
||||||
LL | #[diag(nonsense("foo"), code = E0123, slug = "foo")]
|
|
||||||
| ^
|
|
||||||
|
|
||||||
error: derive(Diagnostic): diagnostic slug not specified
|
error: derive(Diagnostic): diagnostic slug not specified
|
||||||
--> $DIR/diagnostic-derive.rs:82:1
|
--> $DIR/diagnostic-derive.rs:82:1
|
||||||
|
|
|
|
||||||
|
|
@ -62,32 +56,16 @@ LL | #[diag(nonsense("foo"), code = E0123, slug = "foo")]
|
||||||
|
|
|
|
||||||
= help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]`
|
= help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]`
|
||||||
|
|
||||||
error: derive(Diagnostic): unknown argument
|
|
||||||
--> $DIR/diagnostic-derive.rs:88:8
|
|
||||||
|
|
|
||||||
LL | #[diag(nonsense = "...", code = E0123, slug = "foo")]
|
|
||||||
| ^^^^^^^^
|
|
||||||
|
|
|
||||||
= note: only the `code` parameter is valid after the slug
|
|
||||||
|
|
||||||
error: derive(Diagnostic): diagnostic slug not specified
|
error: derive(Diagnostic): diagnostic slug not specified
|
||||||
--> $DIR/diagnostic-derive.rs:88:1
|
--> $DIR/diagnostic-derive.rs:87:1
|
||||||
|
|
|
|
||||||
LL | #[diag(nonsense = "...", code = E0123, slug = "foo")]
|
LL | #[diag(nonsense = "...", code = E0123, slug = "foo")]
|
||||||
| ^
|
| ^
|
||||||
|
|
|
|
||||||
= help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]`
|
= help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]`
|
||||||
|
|
||||||
error: derive(Diagnostic): unknown argument
|
|
||||||
--> $DIR/diagnostic-derive.rs:94:8
|
|
||||||
|
|
|
||||||
LL | #[diag(nonsense = 4, code = E0123, slug = "foo")]
|
|
||||||
| ^^^^^^^^
|
|
||||||
|
|
|
||||||
= note: only the `code` parameter is valid after the slug
|
|
||||||
|
|
||||||
error: derive(Diagnostic): diagnostic slug not specified
|
error: derive(Diagnostic): diagnostic slug not specified
|
||||||
--> $DIR/diagnostic-derive.rs:94:1
|
--> $DIR/diagnostic-derive.rs:92:1
|
||||||
|
|
|
|
||||||
LL | #[diag(nonsense = 4, code = E0123, slug = "foo")]
|
LL | #[diag(nonsense = 4, code = E0123, slug = "foo")]
|
||||||
| ^
|
| ^
|
||||||
|
|
@ -95,7 +73,7 @@ LL | #[diag(nonsense = 4, code = E0123, slug = "foo")]
|
||||||
= help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]`
|
= help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]`
|
||||||
|
|
||||||
error: derive(Diagnostic): unknown argument
|
error: derive(Diagnostic): unknown argument
|
||||||
--> $DIR/diagnostic-derive.rs:100:40
|
--> $DIR/diagnostic-derive.rs:97:40
|
||||||
|
|
|
|
||||||
LL | #[diag(no_crate_example, code = E0123, slug = "foo")]
|
LL | #[diag(no_crate_example, code = E0123, slug = "foo")]
|
||||||
| ^^^^
|
| ^^^^
|
||||||
|
|
@ -103,55 +81,43 @@ LL | #[diag(no_crate_example, code = E0123, slug = "foo")]
|
||||||
= note: only the `code` parameter is valid after the slug
|
= note: only the `code` parameter is valid after the slug
|
||||||
|
|
||||||
error: derive(Diagnostic): `#[suggestion = ...]` is not a valid attribute
|
error: derive(Diagnostic): `#[suggestion = ...]` is not a valid attribute
|
||||||
--> $DIR/diagnostic-derive.rs:107:5
|
--> $DIR/diagnostic-derive.rs:104:5
|
||||||
|
|
|
|
||||||
LL | #[suggestion = "bar"]
|
LL | #[suggestion = "bar"]
|
||||||
| ^
|
| ^
|
||||||
|
|
||||||
error: derive(Diagnostic): attribute specified multiple times
|
error: derive(Diagnostic): attribute specified multiple times
|
||||||
--> $DIR/diagnostic-derive.rs:114:8
|
--> $DIR/diagnostic-derive.rs:111:26
|
||||||
|
|
|
||||||
LL | #[diag(no_crate_example, code = E0456)]
|
|
||||||
| ^^^^^^^^^^^^^^^^
|
|
||||||
|
|
|
||||||
note: previously specified here
|
|
||||||
--> $DIR/diagnostic-derive.rs:113:8
|
|
||||||
|
|
|
||||||
LL | #[diag(no_crate_example, code = E0123)]
|
|
||||||
| ^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
error: derive(Diagnostic): attribute specified multiple times
|
|
||||||
--> $DIR/diagnostic-derive.rs:114:26
|
|
||||||
|
|
|
|
||||||
LL | #[diag(no_crate_example, code = E0456)]
|
LL | #[diag(no_crate_example, code = E0456)]
|
||||||
| ^^^^
|
| ^^^^
|
||||||
|
|
|
|
||||||
note: previously specified here
|
note: previously specified here
|
||||||
--> $DIR/diagnostic-derive.rs:113:26
|
--> $DIR/diagnostic-derive.rs:110:26
|
||||||
|
|
|
|
||||||
LL | #[diag(no_crate_example, code = E0123)]
|
LL | #[diag(no_crate_example, code = E0123)]
|
||||||
| ^^^^
|
| ^^^^
|
||||||
|
|
||||||
error: derive(Diagnostic): attribute specified multiple times
|
error: derive(Diagnostic): attribute specified multiple times
|
||||||
--> $DIR/diagnostic-derive.rs:120:40
|
--> $DIR/diagnostic-derive.rs:116:40
|
||||||
|
|
|
|
||||||
LL | #[diag(no_crate_example, code = E0123, code = E0456)]
|
LL | #[diag(no_crate_example, code = E0123, code = E0456)]
|
||||||
| ^^^^
|
| ^^^^
|
||||||
|
|
|
|
||||||
note: previously specified here
|
note: previously specified here
|
||||||
--> $DIR/diagnostic-derive.rs:120:26
|
--> $DIR/diagnostic-derive.rs:116:26
|
||||||
|
|
|
|
||||||
LL | #[diag(no_crate_example, code = E0123, code = E0456)]
|
LL | #[diag(no_crate_example, code = E0123, code = E0456)]
|
||||||
| ^^^^
|
| ^^^^
|
||||||
|
|
||||||
error: derive(Diagnostic): diagnostic slug must be the first argument
|
error: derive(Diagnostic): diagnostic slug must be the first argument
|
||||||
--> $DIR/diagnostic-derive.rs:125:43
|
--> $DIR/diagnostic-derive.rs:121:26
|
||||||
|
|
|
|
||||||
LL | #[diag(no_crate_example, no_crate::example, code = E0123)]
|
LL | #[diag(no_crate_example, no_crate::example, code = E0123)]
|
||||||
| ^
|
| ^^^^^^^^
|
||||||
|
|
||||||
error: derive(Diagnostic): diagnostic slug not specified
|
error: derive(Diagnostic): diagnostic slug not specified
|
||||||
--> $DIR/diagnostic-derive.rs:130:1
|
--> $DIR/diagnostic-derive.rs:126:1
|
||||||
|
|
|
|
||||||
LL | struct KindNotProvided {}
|
LL | struct KindNotProvided {}
|
||||||
| ^^^^^^
|
| ^^^^^^
|
||||||
|
|
@ -159,7 +125,7 @@ LL | struct KindNotProvided {}
|
||||||
= help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]`
|
= help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]`
|
||||||
|
|
||||||
error: derive(Diagnostic): diagnostic slug not specified
|
error: derive(Diagnostic): diagnostic slug not specified
|
||||||
--> $DIR/diagnostic-derive.rs:133:1
|
--> $DIR/diagnostic-derive.rs:129:1
|
||||||
|
|
|
|
||||||
LL | #[diag(code = E0123)]
|
LL | #[diag(code = E0123)]
|
||||||
| ^
|
| ^
|
||||||
|
|
@ -167,31 +133,31 @@ LL | #[diag(code = E0123)]
|
||||||
= help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]`
|
= help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]`
|
||||||
|
|
||||||
error: derive(Diagnostic): the `#[primary_span]` attribute can only be applied to fields of type `Span` or `MultiSpan`
|
error: derive(Diagnostic): the `#[primary_span]` attribute can only be applied to fields of type `Span` or `MultiSpan`
|
||||||
--> $DIR/diagnostic-derive.rs:144:5
|
--> $DIR/diagnostic-derive.rs:140:5
|
||||||
|
|
|
|
||||||
LL | #[primary_span]
|
LL | #[primary_span]
|
||||||
| ^
|
| ^
|
||||||
|
|
||||||
error: derive(Diagnostic): `#[nonsense]` is not a valid attribute
|
error: derive(Diagnostic): `#[nonsense]` is not a valid attribute
|
||||||
--> $DIR/diagnostic-derive.rs:152:5
|
--> $DIR/diagnostic-derive.rs:148:5
|
||||||
|
|
|
|
||||||
LL | #[nonsense]
|
LL | #[nonsense]
|
||||||
| ^
|
| ^
|
||||||
|
|
||||||
error: derive(Diagnostic): the `#[label(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan`
|
error: derive(Diagnostic): the `#[label(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan`
|
||||||
--> $DIR/diagnostic-derive.rs:169:5
|
--> $DIR/diagnostic-derive.rs:165:5
|
||||||
|
|
|
|
||||||
LL | #[label(no_crate_label)]
|
LL | #[label(no_crate_label)]
|
||||||
| ^
|
| ^
|
||||||
|
|
||||||
error: derive(Diagnostic): `name` doesn't refer to a field on this type
|
error: derive(Diagnostic): `name` doesn't refer to a field on this type
|
||||||
--> $DIR/diagnostic-derive.rs:177:46
|
--> $DIR/diagnostic-derive.rs:173:46
|
||||||
|
|
|
|
||||||
LL | #[suggestion(no_crate_suggestion, code = "{name}")]
|
LL | #[suggestion(no_crate_suggestion, code = "{name}")]
|
||||||
| ^^^^^^^^
|
| ^^^^^^^^
|
||||||
|
|
||||||
error: invalid format string: expected `}` but string was terminated
|
error: invalid format string: expected `}` but string was terminated
|
||||||
--> $DIR/diagnostic-derive.rs:182:10
|
--> $DIR/diagnostic-derive.rs:178:10
|
||||||
|
|
|
|
||||||
LL | #[derive(Diagnostic)]
|
LL | #[derive(Diagnostic)]
|
||||||
| ^^^^^^^^^^ expected `}` in format string
|
| ^^^^^^^^^^ expected `}` in format string
|
||||||
|
|
@ -200,7 +166,7 @@ LL | #[derive(Diagnostic)]
|
||||||
= note: this error originates in the derive macro `Diagnostic` (in Nightly builds, run with -Z macro-backtrace for more info)
|
= note: this error originates in the derive macro `Diagnostic` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
error: invalid format string: unmatched `}` found
|
error: invalid format string: unmatched `}` found
|
||||||
--> $DIR/diagnostic-derive.rs:192:10
|
--> $DIR/diagnostic-derive.rs:188:10
|
||||||
|
|
|
|
||||||
LL | #[derive(Diagnostic)]
|
LL | #[derive(Diagnostic)]
|
||||||
| ^^^^^^^^^^ unmatched `}` in format string
|
| ^^^^^^^^^^ unmatched `}` in format string
|
||||||
|
|
@ -209,47 +175,47 @@ LL | #[derive(Diagnostic)]
|
||||||
= note: this error originates in the derive macro `Diagnostic` (in Nightly builds, run with -Z macro-backtrace for more info)
|
= note: this error originates in the derive macro `Diagnostic` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
error: derive(Diagnostic): the `#[label(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan`
|
error: derive(Diagnostic): the `#[label(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan`
|
||||||
--> $DIR/diagnostic-derive.rs:212:5
|
--> $DIR/diagnostic-derive.rs:208:5
|
||||||
|
|
|
|
||||||
LL | #[label(no_crate_label)]
|
LL | #[label(no_crate_label)]
|
||||||
| ^
|
| ^
|
||||||
|
|
||||||
error: derive(Diagnostic): suggestion without `code = "..."`
|
error: derive(Diagnostic): suggestion without `code = "..."`
|
||||||
--> $DIR/diagnostic-derive.rs:231:5
|
--> $DIR/diagnostic-derive.rs:227:5
|
||||||
|
|
|
|
||||||
LL | #[suggestion(no_crate_suggestion)]
|
LL | #[suggestion(no_crate_suggestion)]
|
||||||
| ^
|
| ^
|
||||||
|
|
||||||
error: derive(Diagnostic): invalid nested attribute
|
error: derive(Diagnostic): invalid nested attribute
|
||||||
--> $DIR/diagnostic-derive.rs:239:18
|
--> $DIR/diagnostic-derive.rs:235:18
|
||||||
|
|
|
|
||||||
LL | #[suggestion(nonsense = "bar")]
|
LL | #[suggestion(nonsense = "bar")]
|
||||||
| ^^^^^^^^
|
| ^^^^^^^^
|
||||||
|
|
|
|
||||||
= help: only `no_span`, `style`, `code` and `applicability` are valid nested attributes
|
= help: only `style`, `code` and `applicability` are valid nested attributes
|
||||||
|
|
||||||
error: derive(Diagnostic): suggestion without `code = "..."`
|
error: derive(Diagnostic): suggestion without `code = "..."`
|
||||||
--> $DIR/diagnostic-derive.rs:239:5
|
--> $DIR/diagnostic-derive.rs:235:5
|
||||||
|
|
|
|
||||||
LL | #[suggestion(nonsense = "bar")]
|
LL | #[suggestion(nonsense = "bar")]
|
||||||
| ^
|
| ^
|
||||||
|
|
||||||
error: derive(Diagnostic): invalid nested attribute
|
error: derive(Diagnostic): invalid nested attribute
|
||||||
--> $DIR/diagnostic-derive.rs:248:18
|
--> $DIR/diagnostic-derive.rs:244:18
|
||||||
|
|
|
|
||||||
LL | #[suggestion(msg = "bar")]
|
LL | #[suggestion(msg = "bar")]
|
||||||
| ^^^
|
| ^^^
|
||||||
|
|
|
|
||||||
= help: only `no_span`, `style`, `code` and `applicability` are valid nested attributes
|
= help: only `style`, `code` and `applicability` are valid nested attributes
|
||||||
|
|
||||||
error: derive(Diagnostic): suggestion without `code = "..."`
|
error: derive(Diagnostic): suggestion without `code = "..."`
|
||||||
--> $DIR/diagnostic-derive.rs:248:5
|
--> $DIR/diagnostic-derive.rs:244:5
|
||||||
|
|
|
|
||||||
LL | #[suggestion(msg = "bar")]
|
LL | #[suggestion(msg = "bar")]
|
||||||
| ^
|
| ^
|
||||||
|
|
||||||
error: derive(Diagnostic): wrong field type for suggestion
|
error: derive(Diagnostic): wrong field type for suggestion
|
||||||
--> $DIR/diagnostic-derive.rs:271:5
|
--> $DIR/diagnostic-derive.rs:267:5
|
||||||
|
|
|
|
||||||
LL | #[suggestion(no_crate_suggestion, code = "This is suggested code")]
|
LL | #[suggestion(no_crate_suggestion, code = "This is suggested code")]
|
||||||
| ^
|
| ^
|
||||||
|
|
@ -257,79 +223,79 @@ LL | #[suggestion(no_crate_suggestion, code = "This is suggested code")]
|
||||||
= help: `#[suggestion(...)]` should be applied to fields of type `Span` or `(Span, Applicability)`
|
= help: `#[suggestion(...)]` should be applied to fields of type `Span` or `(Span, Applicability)`
|
||||||
|
|
||||||
error: derive(Diagnostic): attribute specified multiple times
|
error: derive(Diagnostic): attribute specified multiple times
|
||||||
--> $DIR/diagnostic-derive.rs:287:24
|
--> $DIR/diagnostic-derive.rs:283:24
|
||||||
|
|
|
|
||||||
LL | suggestion: (Span, Span, Applicability),
|
LL | suggestion: (Span, Span, Applicability),
|
||||||
| ^^^^
|
| ^^^^
|
||||||
|
|
|
|
||||||
note: previously specified here
|
note: previously specified here
|
||||||
--> $DIR/diagnostic-derive.rs:287:18
|
--> $DIR/diagnostic-derive.rs:283:18
|
||||||
|
|
|
|
||||||
LL | suggestion: (Span, Span, Applicability),
|
LL | suggestion: (Span, Span, Applicability),
|
||||||
| ^^^^
|
| ^^^^
|
||||||
|
|
||||||
error: derive(Diagnostic): attribute specified multiple times
|
error: derive(Diagnostic): attribute specified multiple times
|
||||||
--> $DIR/diagnostic-derive.rs:295:33
|
--> $DIR/diagnostic-derive.rs:291:33
|
||||||
|
|
|
|
||||||
LL | suggestion: (Applicability, Applicability, Span),
|
LL | suggestion: (Applicability, Applicability, Span),
|
||||||
| ^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^
|
||||||
|
|
|
|
||||||
note: previously specified here
|
note: previously specified here
|
||||||
--> $DIR/diagnostic-derive.rs:295:18
|
--> $DIR/diagnostic-derive.rs:291:18
|
||||||
|
|
|
|
||||||
LL | suggestion: (Applicability, Applicability, Span),
|
LL | suggestion: (Applicability, Applicability, Span),
|
||||||
| ^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^
|
||||||
|
|
||||||
error: derive(Diagnostic): `#[label = ...]` is not a valid attribute
|
error: derive(Diagnostic): `#[label = ...]` is not a valid attribute
|
||||||
--> $DIR/diagnostic-derive.rs:302:5
|
--> $DIR/diagnostic-derive.rs:298:5
|
||||||
|
|
|
|
||||||
LL | #[label = "bar"]
|
LL | #[label = "bar"]
|
||||||
| ^
|
| ^
|
||||||
|
|
||||||
error: derive(Diagnostic): attribute specified multiple times
|
error: derive(Diagnostic): attribute specified multiple times
|
||||||
--> $DIR/diagnostic-derive.rs:453:5
|
--> $DIR/diagnostic-derive.rs:449:5
|
||||||
|
|
|
|
||||||
LL | #[suggestion(no_crate_suggestion, code = "...", applicability = "maybe-incorrect")]
|
LL | #[suggestion(no_crate_suggestion, code = "...", applicability = "maybe-incorrect")]
|
||||||
| ^
|
| ^
|
||||||
|
|
|
|
||||||
note: previously specified here
|
note: previously specified here
|
||||||
--> $DIR/diagnostic-derive.rs:455:24
|
--> $DIR/diagnostic-derive.rs:451:24
|
||||||
|
|
|
|
||||||
LL | suggestion: (Span, Applicability),
|
LL | suggestion: (Span, Applicability),
|
||||||
| ^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^
|
||||||
|
|
||||||
error: derive(Diagnostic): invalid applicability
|
error: derive(Diagnostic): invalid applicability
|
||||||
--> $DIR/diagnostic-derive.rs:461:69
|
--> $DIR/diagnostic-derive.rs:457:69
|
||||||
|
|
|
|
||||||
LL | #[suggestion(no_crate_suggestion, code = "...", applicability = "batman")]
|
LL | #[suggestion(no_crate_suggestion, code = "...", applicability = "batman")]
|
||||||
| ^^^^^^^^
|
| ^^^^^^^^
|
||||||
|
|
||||||
error: derive(Diagnostic): the `#[help(...)]` attribute can only be applied to fields of type `Span`, `MultiSpan`, `bool` or `()`
|
error: derive(Diagnostic): the `#[help(...)]` attribute can only be applied to fields of type `Span`, `MultiSpan`, `bool` or `()`
|
||||||
--> $DIR/diagnostic-derive.rs:528:5
|
--> $DIR/diagnostic-derive.rs:524:5
|
||||||
|
|
|
|
||||||
LL | #[help(no_crate_help)]
|
LL | #[help(no_crate_help)]
|
||||||
| ^
|
| ^
|
||||||
|
|
||||||
error: derive(Diagnostic): a diagnostic slug must be the first argument to the attribute
|
error: derive(Diagnostic): a diagnostic slug must be the first argument to the attribute
|
||||||
--> $DIR/diagnostic-derive.rs:537:32
|
--> $DIR/diagnostic-derive.rs:533:29
|
||||||
|
|
|
|
||||||
LL | #[label(no_crate_label, foo)]
|
LL | #[label(no_crate_label, foo)]
|
||||||
| ^
|
| ^^^
|
||||||
|
|
||||||
error: derive(Diagnostic): only `no_span` is a valid nested attribute
|
error: derive(Diagnostic): no nested attribute expected here
|
||||||
--> $DIR/diagnostic-derive.rs:545:29
|
--> $DIR/diagnostic-derive.rs:541:29
|
||||||
|
|
|
|
||||||
LL | #[label(no_crate_label, foo = "...")]
|
LL | #[label(no_crate_label, foo = "...")]
|
||||||
| ^^^
|
| ^^^
|
||||||
|
|
||||||
error: derive(Diagnostic): only `no_span` is a valid nested attribute
|
error: derive(Diagnostic): no nested attribute expected here
|
||||||
--> $DIR/diagnostic-derive.rs:553:29
|
--> $DIR/diagnostic-derive.rs:549:29
|
||||||
|
|
|
|
||||||
LL | #[label(no_crate_label, foo("..."))]
|
LL | #[label(no_crate_label, foo("..."))]
|
||||||
| ^^^
|
| ^^^
|
||||||
|
|
||||||
error: derive(Diagnostic): `#[primary_span]` is not a valid attribute
|
error: derive(Diagnostic): `#[primary_span]` is not a valid attribute
|
||||||
--> $DIR/diagnostic-derive.rs:565:5
|
--> $DIR/diagnostic-derive.rs:561:5
|
||||||
|
|
|
|
||||||
LL | #[primary_span]
|
LL | #[primary_span]
|
||||||
| ^
|
| ^
|
||||||
|
|
@ -337,13 +303,13 @@ LL | #[primary_span]
|
||||||
= help: the `primary_span` field attribute is not valid for lint diagnostics
|
= help: the `primary_span` field attribute is not valid for lint diagnostics
|
||||||
|
|
||||||
error: derive(Diagnostic): `#[error(...)]` is not a valid attribute
|
error: derive(Diagnostic): `#[error(...)]` is not a valid attribute
|
||||||
--> $DIR/diagnostic-derive.rs:585:1
|
--> $DIR/diagnostic-derive.rs:581:1
|
||||||
|
|
|
|
||||||
LL | #[error(no_crate_example, code = E0123)]
|
LL | #[error(no_crate_example, code = E0123)]
|
||||||
| ^
|
| ^
|
||||||
|
|
||||||
error: derive(Diagnostic): diagnostic slug not specified
|
error: derive(Diagnostic): diagnostic slug not specified
|
||||||
--> $DIR/diagnostic-derive.rs:585:1
|
--> $DIR/diagnostic-derive.rs:581:1
|
||||||
|
|
|
|
||||||
LL | #[error(no_crate_example, code = E0123)]
|
LL | #[error(no_crate_example, code = E0123)]
|
||||||
| ^
|
| ^
|
||||||
|
|
@ -351,13 +317,13 @@ LL | #[error(no_crate_example, code = E0123)]
|
||||||
= help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]`
|
= help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]`
|
||||||
|
|
||||||
error: derive(Diagnostic): `#[warn_(...)]` is not a valid attribute
|
error: derive(Diagnostic): `#[warn_(...)]` is not a valid attribute
|
||||||
--> $DIR/diagnostic-derive.rs:592:1
|
--> $DIR/diagnostic-derive.rs:588:1
|
||||||
|
|
|
|
||||||
LL | #[warn_(no_crate_example, code = E0123)]
|
LL | #[warn_(no_crate_example, code = E0123)]
|
||||||
| ^
|
| ^
|
||||||
|
|
||||||
error: derive(Diagnostic): diagnostic slug not specified
|
error: derive(Diagnostic): diagnostic slug not specified
|
||||||
--> $DIR/diagnostic-derive.rs:592:1
|
--> $DIR/diagnostic-derive.rs:588:1
|
||||||
|
|
|
|
||||||
LL | #[warn_(no_crate_example, code = E0123)]
|
LL | #[warn_(no_crate_example, code = E0123)]
|
||||||
| ^
|
| ^
|
||||||
|
|
@ -365,13 +331,13 @@ LL | #[warn_(no_crate_example, code = E0123)]
|
||||||
= help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]`
|
= help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]`
|
||||||
|
|
||||||
error: derive(Diagnostic): `#[lint(...)]` is not a valid attribute
|
error: derive(Diagnostic): `#[lint(...)]` is not a valid attribute
|
||||||
--> $DIR/diagnostic-derive.rs:599:1
|
--> $DIR/diagnostic-derive.rs:595:1
|
||||||
|
|
|
|
||||||
LL | #[lint(no_crate_example, code = E0123)]
|
LL | #[lint(no_crate_example, code = E0123)]
|
||||||
| ^
|
| ^
|
||||||
|
|
||||||
error: derive(Diagnostic): diagnostic slug not specified
|
error: derive(Diagnostic): diagnostic slug not specified
|
||||||
--> $DIR/diagnostic-derive.rs:599:1
|
--> $DIR/diagnostic-derive.rs:595:1
|
||||||
|
|
|
|
||||||
LL | #[lint(no_crate_example, code = E0123)]
|
LL | #[lint(no_crate_example, code = E0123)]
|
||||||
| ^
|
| ^
|
||||||
|
|
@ -379,13 +345,13 @@ LL | #[lint(no_crate_example, code = E0123)]
|
||||||
= help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]`
|
= help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]`
|
||||||
|
|
||||||
error: derive(Diagnostic): `#[lint(...)]` is not a valid attribute
|
error: derive(Diagnostic): `#[lint(...)]` is not a valid attribute
|
||||||
--> $DIR/diagnostic-derive.rs:606:1
|
--> $DIR/diagnostic-derive.rs:602:1
|
||||||
|
|
|
|
||||||
LL | #[lint(no_crate_example, code = E0123)]
|
LL | #[lint(no_crate_example, code = E0123)]
|
||||||
| ^
|
| ^
|
||||||
|
|
||||||
error: derive(Diagnostic): diagnostic slug not specified
|
error: derive(Diagnostic): diagnostic slug not specified
|
||||||
--> $DIR/diagnostic-derive.rs:606:1
|
--> $DIR/diagnostic-derive.rs:602:1
|
||||||
|
|
|
|
||||||
LL | #[lint(no_crate_example, code = E0123)]
|
LL | #[lint(no_crate_example, code = E0123)]
|
||||||
| ^
|
| ^
|
||||||
|
|
@ -393,19 +359,19 @@ LL | #[lint(no_crate_example, code = E0123)]
|
||||||
= help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]`
|
= help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]`
|
||||||
|
|
||||||
error: derive(Diagnostic): attribute specified multiple times
|
error: derive(Diagnostic): attribute specified multiple times
|
||||||
--> $DIR/diagnostic-derive.rs:615:53
|
--> $DIR/diagnostic-derive.rs:611:53
|
||||||
|
|
|
|
||||||
LL | #[suggestion(no_crate_suggestion, code = "...", code = ",,,")]
|
LL | #[suggestion(no_crate_suggestion, code = "...", code = ",,,")]
|
||||||
| ^^^^
|
| ^^^^
|
||||||
|
|
|
|
||||||
note: previously specified here
|
note: previously specified here
|
||||||
--> $DIR/diagnostic-derive.rs:615:39
|
--> $DIR/diagnostic-derive.rs:611:39
|
||||||
|
|
|
|
||||||
LL | #[suggestion(no_crate_suggestion, code = "...", code = ",,,")]
|
LL | #[suggestion(no_crate_suggestion, code = "...", code = ",,,")]
|
||||||
| ^^^^
|
| ^^^^
|
||||||
|
|
||||||
error: derive(Diagnostic): wrong types for suggestion
|
error: derive(Diagnostic): wrong types for suggestion
|
||||||
--> $DIR/diagnostic-derive.rs:624:24
|
--> $DIR/diagnostic-derive.rs:620:24
|
||||||
|
|
|
|
||||||
LL | suggestion: (Span, usize),
|
LL | suggestion: (Span, usize),
|
||||||
| ^^^^^
|
| ^^^^^
|
||||||
|
|
@ -413,7 +379,7 @@ LL | suggestion: (Span, usize),
|
||||||
= help: `#[suggestion(...)]` on a tuple field must be applied to fields of type `(Span, Applicability)`
|
= help: `#[suggestion(...)]` on a tuple field must be applied to fields of type `(Span, Applicability)`
|
||||||
|
|
||||||
error: derive(Diagnostic): wrong types for suggestion
|
error: derive(Diagnostic): wrong types for suggestion
|
||||||
--> $DIR/diagnostic-derive.rs:632:17
|
--> $DIR/diagnostic-derive.rs:628:17
|
||||||
|
|
|
|
||||||
LL | suggestion: (Span,),
|
LL | suggestion: (Span,),
|
||||||
| ^^^^^^^
|
| ^^^^^^^
|
||||||
|
|
@ -421,13 +387,13 @@ LL | suggestion: (Span,),
|
||||||
= help: `#[suggestion(...)]` on a tuple field must be applied to fields of type `(Span, Applicability)`
|
= help: `#[suggestion(...)]` on a tuple field must be applied to fields of type `(Span, Applicability)`
|
||||||
|
|
||||||
error: derive(Diagnostic): suggestion without `code = "..."`
|
error: derive(Diagnostic): suggestion without `code = "..."`
|
||||||
--> $DIR/diagnostic-derive.rs:639:5
|
--> $DIR/diagnostic-derive.rs:635:5
|
||||||
|
|
|
|
||||||
LL | #[suggestion(no_crate_suggestion)]
|
LL | #[suggestion(no_crate_suggestion)]
|
||||||
| ^
|
| ^
|
||||||
|
|
||||||
error: derive(Diagnostic): `#[multipart_suggestion(...)]` is not a valid attribute
|
error: derive(Diagnostic): `#[multipart_suggestion(...)]` is not a valid attribute
|
||||||
--> $DIR/diagnostic-derive.rs:646:1
|
--> $DIR/diagnostic-derive.rs:642:1
|
||||||
|
|
|
|
||||||
LL | #[multipart_suggestion(no_crate_suggestion)]
|
LL | #[multipart_suggestion(no_crate_suggestion)]
|
||||||
| ^
|
| ^
|
||||||
|
|
@ -435,7 +401,7 @@ LL | #[multipart_suggestion(no_crate_suggestion)]
|
||||||
= help: consider creating a `Subdiagnostic` instead
|
= help: consider creating a `Subdiagnostic` instead
|
||||||
|
|
||||||
error: derive(Diagnostic): `#[multipart_suggestion(...)]` is not a valid attribute
|
error: derive(Diagnostic): `#[multipart_suggestion(...)]` is not a valid attribute
|
||||||
--> $DIR/diagnostic-derive.rs:649:1
|
--> $DIR/diagnostic-derive.rs:645:1
|
||||||
|
|
|
|
||||||
LL | #[multipart_suggestion()]
|
LL | #[multipart_suggestion()]
|
||||||
| ^
|
| ^
|
||||||
|
|
@ -443,7 +409,7 @@ LL | #[multipart_suggestion()]
|
||||||
= help: consider creating a `Subdiagnostic` instead
|
= help: consider creating a `Subdiagnostic` instead
|
||||||
|
|
||||||
error: derive(Diagnostic): `#[multipart_suggestion(...)]` is not a valid attribute
|
error: derive(Diagnostic): `#[multipart_suggestion(...)]` is not a valid attribute
|
||||||
--> $DIR/diagnostic-derive.rs:653:5
|
--> $DIR/diagnostic-derive.rs:649:5
|
||||||
|
|
|
|
||||||
LL | #[multipart_suggestion(no_crate_suggestion)]
|
LL | #[multipart_suggestion(no_crate_suggestion)]
|
||||||
| ^
|
| ^
|
||||||
|
|
@ -451,7 +417,7 @@ LL | #[multipart_suggestion(no_crate_suggestion)]
|
||||||
= help: consider creating a `Subdiagnostic` instead
|
= help: consider creating a `Subdiagnostic` instead
|
||||||
|
|
||||||
error: derive(Diagnostic): `#[suggestion(...)]` is not a valid attribute
|
error: derive(Diagnostic): `#[suggestion(...)]` is not a valid attribute
|
||||||
--> $DIR/diagnostic-derive.rs:661:1
|
--> $DIR/diagnostic-derive.rs:657:1
|
||||||
|
|
|
|
||||||
LL | #[suggestion(no_crate_suggestion, code = "...")]
|
LL | #[suggestion(no_crate_suggestion, code = "...")]
|
||||||
| ^
|
| ^
|
||||||
|
|
@ -459,7 +425,7 @@ LL | #[suggestion(no_crate_suggestion, code = "...")]
|
||||||
= help: `#[label]` and `#[suggestion]` can only be applied to fields
|
= help: `#[label]` and `#[suggestion]` can only be applied to fields
|
||||||
|
|
||||||
error: derive(Diagnostic): `#[label]` is not a valid attribute
|
error: derive(Diagnostic): `#[label]` is not a valid attribute
|
||||||
--> $DIR/diagnostic-derive.rs:670:1
|
--> $DIR/diagnostic-derive.rs:666:1
|
||||||
|
|
|
|
||||||
LL | #[label]
|
LL | #[label]
|
||||||
| ^
|
| ^
|
||||||
|
|
@ -467,73 +433,73 @@ LL | #[label]
|
||||||
= help: `#[label]` and `#[suggestion]` can only be applied to fields
|
= help: `#[label]` and `#[suggestion]` can only be applied to fields
|
||||||
|
|
||||||
error: derive(Diagnostic): `#[subdiagnostic(...)]` is not a valid attribute
|
error: derive(Diagnostic): `#[subdiagnostic(...)]` is not a valid attribute
|
||||||
--> $DIR/diagnostic-derive.rs:704:5
|
--> $DIR/diagnostic-derive.rs:700:5
|
||||||
|
|
|
|
||||||
LL | #[subdiagnostic(bad)]
|
LL | #[subdiagnostic(bad)]
|
||||||
| ^
|
| ^
|
||||||
|
|
||||||
error: derive(Diagnostic): `#[subdiagnostic = ...]` is not a valid attribute
|
error: derive(Diagnostic): `#[subdiagnostic = ...]` is not a valid attribute
|
||||||
--> $DIR/diagnostic-derive.rs:712:5
|
--> $DIR/diagnostic-derive.rs:708:5
|
||||||
|
|
|
|
||||||
LL | #[subdiagnostic = "bad"]
|
LL | #[subdiagnostic = "bad"]
|
||||||
| ^
|
| ^
|
||||||
|
|
||||||
error: derive(Diagnostic): `#[subdiagnostic(...)]` is not a valid attribute
|
error: derive(Diagnostic): `#[subdiagnostic(...)]` is not a valid attribute
|
||||||
--> $DIR/diagnostic-derive.rs:720:5
|
--> $DIR/diagnostic-derive.rs:716:5
|
||||||
|
|
|
|
||||||
LL | #[subdiagnostic(bad, bad)]
|
LL | #[subdiagnostic(bad, bad)]
|
||||||
| ^
|
| ^
|
||||||
|
|
||||||
error: derive(Diagnostic): `#[subdiagnostic(...)]` is not a valid attribute
|
error: derive(Diagnostic): `#[subdiagnostic(...)]` is not a valid attribute
|
||||||
--> $DIR/diagnostic-derive.rs:728:5
|
--> $DIR/diagnostic-derive.rs:724:5
|
||||||
|
|
|
|
||||||
LL | #[subdiagnostic("bad")]
|
LL | #[subdiagnostic("bad")]
|
||||||
| ^
|
| ^
|
||||||
|
|
||||||
error: derive(Diagnostic): `#[subdiagnostic(...)]` is not a valid attribute
|
error: derive(Diagnostic): `#[subdiagnostic(...)]` is not a valid attribute
|
||||||
--> $DIR/diagnostic-derive.rs:736:5
|
--> $DIR/diagnostic-derive.rs:732:5
|
||||||
|
|
|
|
||||||
LL | #[subdiagnostic(eager)]
|
LL | #[subdiagnostic(eager)]
|
||||||
| ^
|
| ^
|
||||||
|
|
||||||
error: derive(Diagnostic): `#[subdiagnostic(...)]` is not a valid attribute
|
error: derive(Diagnostic): `#[subdiagnostic(...)]` is not a valid attribute
|
||||||
--> $DIR/diagnostic-derive.rs:744:5
|
--> $DIR/diagnostic-derive.rs:740:5
|
||||||
|
|
|
|
||||||
LL | #[subdiagnostic(eager)]
|
LL | #[subdiagnostic(eager)]
|
||||||
| ^
|
| ^
|
||||||
|
|
||||||
error: derive(Diagnostic): `#[subdiagnostic(...)]` is not a valid attribute
|
error: derive(Diagnostic): `#[subdiagnostic(...)]` is not a valid attribute
|
||||||
--> $DIR/diagnostic-derive.rs:765:5
|
--> $DIR/diagnostic-derive.rs:761:5
|
||||||
|
|
|
|
||||||
LL | #[subdiagnostic(eager)]
|
LL | #[subdiagnostic(eager)]
|
||||||
| ^
|
| ^
|
||||||
|
|
||||||
error: derive(Diagnostic): expected at least one string literal for `code(...)`
|
error: derive(Diagnostic): expected at least one string literal for `code(...)`
|
||||||
--> $DIR/diagnostic-derive.rs:796:23
|
--> $DIR/diagnostic-derive.rs:792:23
|
||||||
|
|
|
|
||||||
LL | #[suggestion(code())]
|
LL | #[suggestion(code())]
|
||||||
| ^
|
| ^
|
||||||
|
|
||||||
error: derive(Diagnostic): `code(...)` must contain only string literals
|
error: derive(Diagnostic): `code(...)` must contain only string literals
|
||||||
--> $DIR/diagnostic-derive.rs:804:23
|
--> $DIR/diagnostic-derive.rs:800:23
|
||||||
|
|
|
|
||||||
LL | #[suggestion(code(foo))]
|
LL | #[suggestion(code(foo))]
|
||||||
| ^^^
|
| ^^^
|
||||||
|
|
||||||
error: unexpected token, expected `)`
|
error: unexpected token, expected `)`
|
||||||
--> $DIR/diagnostic-derive.rs:804:23
|
--> $DIR/diagnostic-derive.rs:800:23
|
||||||
|
|
|
|
||||||
LL | #[suggestion(code(foo))]
|
LL | #[suggestion(code(foo))]
|
||||||
| ^^^
|
| ^^^
|
||||||
|
|
||||||
error: expected string literal
|
error: expected string literal
|
||||||
--> $DIR/diagnostic-derive.rs:813:25
|
--> $DIR/diagnostic-derive.rs:809:25
|
||||||
|
|
|
|
||||||
LL | #[suggestion(code = 3)]
|
LL | #[suggestion(code = 3)]
|
||||||
| ^
|
| ^
|
||||||
|
|
||||||
error: derive(Diagnostic): `#[suggestion(...)]` is not a valid attribute
|
error: derive(Diagnostic): `#[suggestion(...)]` is not a valid attribute
|
||||||
--> $DIR/diagnostic-derive.rs:828:5
|
--> $DIR/diagnostic-derive.rs:824:5
|
||||||
|
|
|
|
||||||
LL | #[suggestion(no_crate_suggestion, code = "")]
|
LL | #[suggestion(no_crate_suggestion, code = "")]
|
||||||
| ^
|
| ^
|
||||||
|
|
@ -549,13 +515,13 @@ LL | #[nonsense(no_crate_example, code = E0123)]
|
||||||
| ^^^^^^^^
|
| ^^^^^^^^
|
||||||
|
|
||||||
error: cannot find attribute `nonsense` in this scope
|
error: cannot find attribute `nonsense` in this scope
|
||||||
--> $DIR/diagnostic-derive.rs:152:7
|
--> $DIR/diagnostic-derive.rs:148:7
|
||||||
|
|
|
|
||||||
LL | #[nonsense]
|
LL | #[nonsense]
|
||||||
| ^^^^^^^^
|
| ^^^^^^^^
|
||||||
|
|
||||||
error: cannot find attribute `error` in this scope
|
error: cannot find attribute `error` in this scope
|
||||||
--> $DIR/diagnostic-derive.rs:585:3
|
--> $DIR/diagnostic-derive.rs:581:3
|
||||||
|
|
|
|
||||||
LL | #[error(no_crate_example, code = E0123)]
|
LL | #[error(no_crate_example, code = E0123)]
|
||||||
| ^^^^^
|
| ^^^^^
|
||||||
|
|
@ -567,7 +533,7 @@ LL | struct ErrorAttribute {}
|
||||||
|
|
|
|
||||||
|
|
||||||
error: cannot find attribute `warn_` in this scope
|
error: cannot find attribute `warn_` in this scope
|
||||||
--> $DIR/diagnostic-derive.rs:592:3
|
--> $DIR/diagnostic-derive.rs:588:3
|
||||||
|
|
|
|
||||||
LL | #[warn_(no_crate_example, code = E0123)]
|
LL | #[warn_(no_crate_example, code = E0123)]
|
||||||
| ^^^^^
|
| ^^^^^
|
||||||
|
|
@ -579,7 +545,7 @@ LL + #[warn(no_crate_example, code = E0123)]
|
||||||
|
|
|
|
||||||
|
|
||||||
error: cannot find attribute `lint` in this scope
|
error: cannot find attribute `lint` in this scope
|
||||||
--> $DIR/diagnostic-derive.rs:599:3
|
--> $DIR/diagnostic-derive.rs:595:3
|
||||||
|
|
|
|
||||||
LL | #[lint(no_crate_example, code = E0123)]
|
LL | #[lint(no_crate_example, code = E0123)]
|
||||||
| ^^^^
|
| ^^^^
|
||||||
|
|
@ -591,7 +557,7 @@ LL + #[link(no_crate_example, code = E0123)]
|
||||||
|
|
|
|
||||||
|
|
||||||
error: cannot find attribute `lint` in this scope
|
error: cannot find attribute `lint` in this scope
|
||||||
--> $DIR/diagnostic-derive.rs:606:3
|
--> $DIR/diagnostic-derive.rs:602:3
|
||||||
|
|
|
|
||||||
LL | #[lint(no_crate_example, code = E0123)]
|
LL | #[lint(no_crate_example, code = E0123)]
|
||||||
| ^^^^
|
| ^^^^
|
||||||
|
|
@ -603,7 +569,7 @@ LL + #[link(no_crate_example, code = E0123)]
|
||||||
|
|
|
|
||||||
|
|
||||||
error: cannot find attribute `multipart_suggestion` in this scope
|
error: cannot find attribute `multipart_suggestion` in this scope
|
||||||
--> $DIR/diagnostic-derive.rs:646:3
|
--> $DIR/diagnostic-derive.rs:642:3
|
||||||
|
|
|
|
||||||
LL | #[multipart_suggestion(no_crate_suggestion)]
|
LL | #[multipart_suggestion(no_crate_suggestion)]
|
||||||
| ^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
@ -615,7 +581,7 @@ LL | struct MultipartSuggestion {
|
||||||
|
|
|
|
||||||
|
|
||||||
error: cannot find attribute `multipart_suggestion` in this scope
|
error: cannot find attribute `multipart_suggestion` in this scope
|
||||||
--> $DIR/diagnostic-derive.rs:649:3
|
--> $DIR/diagnostic-derive.rs:645:3
|
||||||
|
|
|
|
||||||
LL | #[multipart_suggestion()]
|
LL | #[multipart_suggestion()]
|
||||||
| ^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
@ -627,7 +593,7 @@ LL | struct MultipartSuggestion {
|
||||||
|
|
|
|
||||||
|
|
||||||
error: cannot find attribute `multipart_suggestion` in this scope
|
error: cannot find attribute `multipart_suggestion` in this scope
|
||||||
--> $DIR/diagnostic-derive.rs:653:7
|
--> $DIR/diagnostic-derive.rs:649:7
|
||||||
|
|
|
|
||||||
LL | #[multipart_suggestion(no_crate_suggestion)]
|
LL | #[multipart_suggestion(no_crate_suggestion)]
|
||||||
| ^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
@ -640,16 +606,8 @@ error[E0425]: cannot find value `nonsense` in module `crate::fluent_generated`
|
||||||
LL | #[diag(nonsense, code = E0123)]
|
LL | #[diag(nonsense, code = E0123)]
|
||||||
| ^^^^^^^^ not found in `crate::fluent_generated`
|
| ^^^^^^^^ not found in `crate::fluent_generated`
|
||||||
|
|
||||||
error[E0425]: cannot find value `__code_34` in this scope
|
|
||||||
--> $DIR/diagnostic-derive.rs:810:10
|
|
||||||
|
|
|
||||||
LL | #[derive(Diagnostic)]
|
|
||||||
| ^^^^^^^^^^ not found in this scope
|
|
||||||
|
|
|
||||||
= note: this error originates in the derive macro `Diagnostic` (in Nightly builds, run with -Z macro-backtrace for more info)
|
|
||||||
|
|
||||||
error[E0277]: the trait bound `Hello: IntoDiagArg` is not satisfied
|
error[E0277]: the trait bound `Hello: IntoDiagArg` is not satisfied
|
||||||
--> $DIR/diagnostic-derive.rs:351:12
|
--> $DIR/diagnostic-derive.rs:347:12
|
||||||
|
|
|
|
||||||
LL | #[derive(Diagnostic)]
|
LL | #[derive(Diagnostic)]
|
||||||
| ---------- required by a bound introduced by this call
|
| ---------- required by a bound introduced by this call
|
||||||
|
|
@ -670,7 +628,7 @@ note: required by a bound in `Diag::<'a, G>::arg`
|
||||||
= note: in this macro invocation
|
= note: in this macro invocation
|
||||||
= note: this error originates in the macro `with_fn` (in Nightly builds, run with -Z macro-backtrace for more info)
|
= note: this error originates in the macro `with_fn` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
error: aborting due to 85 previous errors
|
error: aborting due to 80 previous errors
|
||||||
|
|
||||||
Some errors have detailed explanations: E0277, E0425.
|
Some errors have detailed explanations: E0277, E0425.
|
||||||
For more information about an error, try `rustc --explain E0277`.
|
For more information about an error, try `rustc --explain E0277`.
|
||||||
|
|
|
||||||
|
|
@ -85,7 +85,7 @@ struct F {
|
||||||
|
|
||||||
#[derive(Subdiagnostic)]
|
#[derive(Subdiagnostic)]
|
||||||
#[label(bug = "...")]
|
#[label(bug = "...")]
|
||||||
//~^ ERROR only `no_span` is a valid nested attribute
|
//~^ ERROR no nested attribute expected here
|
||||||
//~| ERROR diagnostic slug must be first argument
|
//~| ERROR diagnostic slug must be first argument
|
||||||
struct G {
|
struct G {
|
||||||
#[primary_span]
|
#[primary_span]
|
||||||
|
|
@ -95,7 +95,7 @@ struct G {
|
||||||
|
|
||||||
#[derive(Subdiagnostic)]
|
#[derive(Subdiagnostic)]
|
||||||
#[label("...")]
|
#[label("...")]
|
||||||
//~^ ERROR unexpected literal in nested attribute, expected ident
|
//~^ ERROR expected identifier
|
||||||
struct H {
|
struct H {
|
||||||
#[primary_span]
|
#[primary_span]
|
||||||
span: Span,
|
span: Span,
|
||||||
|
|
@ -104,7 +104,7 @@ struct H {
|
||||||
|
|
||||||
#[derive(Subdiagnostic)]
|
#[derive(Subdiagnostic)]
|
||||||
#[label(slug = 4)]
|
#[label(slug = 4)]
|
||||||
//~^ ERROR only `no_span` is a valid nested attribute
|
//~^ ERROR no nested attribute expected here
|
||||||
//~| ERROR diagnostic slug must be first argument
|
//~| ERROR diagnostic slug must be first argument
|
||||||
struct J {
|
struct J {
|
||||||
#[primary_span]
|
#[primary_span]
|
||||||
|
|
@ -114,7 +114,7 @@ struct J {
|
||||||
|
|
||||||
#[derive(Subdiagnostic)]
|
#[derive(Subdiagnostic)]
|
||||||
#[label(slug("..."))]
|
#[label(slug("..."))]
|
||||||
//~^ ERROR only `no_span` is a valid nested attribute
|
//~^ ERROR no nested attribute expected here
|
||||||
//~| ERROR diagnostic slug must be first argument
|
//~| ERROR diagnostic slug must be first argument
|
||||||
struct K {
|
struct K {
|
||||||
#[primary_span]
|
#[primary_span]
|
||||||
|
|
@ -133,7 +133,7 @@ struct M {
|
||||||
|
|
||||||
#[derive(Subdiagnostic)]
|
#[derive(Subdiagnostic)]
|
||||||
#[label(no_crate_example, code = "...")]
|
#[label(no_crate_example, code = "...")]
|
||||||
//~^ ERROR only `no_span` is a valid nested attribute
|
//~^ ERROR no nested attribute expected here
|
||||||
struct N {
|
struct N {
|
||||||
#[primary_span]
|
#[primary_span]
|
||||||
span: Span,
|
span: Span,
|
||||||
|
|
@ -142,7 +142,7 @@ struct N {
|
||||||
|
|
||||||
#[derive(Subdiagnostic)]
|
#[derive(Subdiagnostic)]
|
||||||
#[label(no_crate_example, applicability = "machine-applicable")]
|
#[label(no_crate_example, applicability = "machine-applicable")]
|
||||||
//~^ ERROR only `no_span` is a valid nested attribute
|
//~^ ERROR no nested attribute expected here
|
||||||
struct O {
|
struct O {
|
||||||
#[primary_span]
|
#[primary_span]
|
||||||
span: Span,
|
span: Span,
|
||||||
|
|
@ -214,7 +214,7 @@ enum T {
|
||||||
enum U {
|
enum U {
|
||||||
#[label(code = "...")]
|
#[label(code = "...")]
|
||||||
//~^ ERROR diagnostic slug must be first argument of a `#[label(...)]` attribute
|
//~^ ERROR diagnostic slug must be first argument of a `#[label(...)]` attribute
|
||||||
//~| ERROR only `no_span` is a valid nested attribute
|
//~| ERROR no nested attribute expected here
|
||||||
A {
|
A {
|
||||||
#[primary_span]
|
#[primary_span]
|
||||||
span: Span,
|
span: Span,
|
||||||
|
|
@ -775,7 +775,7 @@ struct SuggestionStyleInvalid1 {
|
||||||
|
|
||||||
#[derive(Subdiagnostic)]
|
#[derive(Subdiagnostic)]
|
||||||
#[suggestion(no_crate_example, code = "", style = 42)]
|
#[suggestion(no_crate_example, code = "", style = 42)]
|
||||||
//~^ ERROR expected `= "xxx"`
|
//~^ ERROR expected string literal
|
||||||
struct SuggestionStyleInvalid2 {
|
struct SuggestionStyleInvalid2 {
|
||||||
#[primary_span]
|
#[primary_span]
|
||||||
sub: Span,
|
sub: Span,
|
||||||
|
|
@ -791,8 +791,7 @@ struct SuggestionStyleInvalid3 {
|
||||||
|
|
||||||
#[derive(Subdiagnostic)]
|
#[derive(Subdiagnostic)]
|
||||||
#[suggestion(no_crate_example, code = "", style("foo"))]
|
#[suggestion(no_crate_example, code = "", style("foo"))]
|
||||||
//~^ ERROR expected `= "xxx"`
|
//~^ ERROR expected `=`
|
||||||
//~| ERROR expected `,`
|
|
||||||
struct SuggestionStyleInvalid4 {
|
struct SuggestionStyleInvalid4 {
|
||||||
#[primary_span]
|
#[primary_span]
|
||||||
sub: Span,
|
sub: Span,
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ error: derive(Diagnostic): `#[label = ...]` is not a valid attribute
|
||||||
LL | #[label = "..."]
|
LL | #[label = "..."]
|
||||||
| ^
|
| ^
|
||||||
|
|
||||||
error: derive(Diagnostic): only `no_span` is a valid nested attribute
|
error: derive(Diagnostic): no nested attribute expected here
|
||||||
--> $DIR/subdiagnostic-derive.rs:87:9
|
--> $DIR/subdiagnostic-derive.rs:87:9
|
||||||
|
|
|
|
||||||
LL | #[label(bug = "...")]
|
LL | #[label(bug = "...")]
|
||||||
|
|
@ -34,13 +34,13 @@ error: derive(Diagnostic): diagnostic slug must be first argument of a `#[label(
|
||||||
LL | #[label(bug = "...")]
|
LL | #[label(bug = "...")]
|
||||||
| ^
|
| ^
|
||||||
|
|
||||||
error: unexpected literal in nested attribute, expected ident
|
error: expected identifier
|
||||||
--> $DIR/subdiagnostic-derive.rs:97:9
|
--> $DIR/subdiagnostic-derive.rs:97:9
|
||||||
|
|
|
|
||||||
LL | #[label("...")]
|
LL | #[label("...")]
|
||||||
| ^^^^^
|
| ^^^^^
|
||||||
|
|
||||||
error: derive(Diagnostic): only `no_span` is a valid nested attribute
|
error: derive(Diagnostic): no nested attribute expected here
|
||||||
--> $DIR/subdiagnostic-derive.rs:106:9
|
--> $DIR/subdiagnostic-derive.rs:106:9
|
||||||
|
|
|
|
||||||
LL | #[label(slug = 4)]
|
LL | #[label(slug = 4)]
|
||||||
|
|
@ -52,7 +52,7 @@ error: derive(Diagnostic): diagnostic slug must be first argument of a `#[label(
|
||||||
LL | #[label(slug = 4)]
|
LL | #[label(slug = 4)]
|
||||||
| ^
|
| ^
|
||||||
|
|
||||||
error: derive(Diagnostic): only `no_span` is a valid nested attribute
|
error: derive(Diagnostic): no nested attribute expected here
|
||||||
--> $DIR/subdiagnostic-derive.rs:116:9
|
--> $DIR/subdiagnostic-derive.rs:116:9
|
||||||
|
|
|
|
||||||
LL | #[label(slug("..."))]
|
LL | #[label(slug("..."))]
|
||||||
|
|
@ -70,13 +70,13 @@ error: derive(Diagnostic): diagnostic slug must be first argument of a `#[label(
|
||||||
LL | #[label()]
|
LL | #[label()]
|
||||||
| ^
|
| ^
|
||||||
|
|
||||||
error: derive(Diagnostic): only `no_span` is a valid nested attribute
|
error: derive(Diagnostic): no nested attribute expected here
|
||||||
--> $DIR/subdiagnostic-derive.rs:135:27
|
--> $DIR/subdiagnostic-derive.rs:135:27
|
||||||
|
|
|
|
||||||
LL | #[label(no_crate_example, code = "...")]
|
LL | #[label(no_crate_example, code = "...")]
|
||||||
| ^^^^
|
| ^^^^
|
||||||
|
|
||||||
error: derive(Diagnostic): only `no_span` is a valid nested attribute
|
error: derive(Diagnostic): no nested attribute expected here
|
||||||
--> $DIR/subdiagnostic-derive.rs:144:27
|
--> $DIR/subdiagnostic-derive.rs:144:27
|
||||||
|
|
|
|
||||||
LL | #[label(no_crate_example, applicability = "machine-applicable")]
|
LL | #[label(no_crate_example, applicability = "machine-applicable")]
|
||||||
|
|
@ -112,7 +112,7 @@ error: derive(Diagnostic): `#[bar(...)]` is not a valid attribute
|
||||||
LL | #[bar("...")]
|
LL | #[bar("...")]
|
||||||
| ^
|
| ^
|
||||||
|
|
||||||
error: derive(Diagnostic): only `no_span` is a valid nested attribute
|
error: derive(Diagnostic): no nested attribute expected here
|
||||||
--> $DIR/subdiagnostic-derive.rs:215:13
|
--> $DIR/subdiagnostic-derive.rs:215:13
|
||||||
|
|
|
|
||||||
LL | #[label(code = "...")]
|
LL | #[label(code = "...")]
|
||||||
|
|
@ -175,10 +175,10 @@ LL | | }
|
||||||
| |_^
|
| |_^
|
||||||
|
|
||||||
error: derive(Diagnostic): a diagnostic slug must be the first argument to the attribute
|
error: derive(Diagnostic): a diagnostic slug must be the first argument to the attribute
|
||||||
--> $DIR/subdiagnostic-derive.rs:317:44
|
--> $DIR/subdiagnostic-derive.rs:317:27
|
||||||
|
|
|
|
||||||
LL | #[label(no_crate_example, no_crate::example)]
|
LL | #[label(no_crate_example, no_crate::example)]
|
||||||
| ^
|
| ^^^^^^^^
|
||||||
|
|
||||||
error: derive(Diagnostic): attribute specified multiple times
|
error: derive(Diagnostic): attribute specified multiple times
|
||||||
--> $DIR/subdiagnostic-derive.rs:330:5
|
--> $DIR/subdiagnostic-derive.rs:330:5
|
||||||
|
|
@ -292,7 +292,7 @@ error: derive(Diagnostic): invalid nested attribute
|
||||||
LL | #[multipart_suggestion(no_crate_example, code = "...", applicability = "machine-applicable")]
|
LL | #[multipart_suggestion(no_crate_example, code = "...", applicability = "machine-applicable")]
|
||||||
| ^^^^
|
| ^^^^
|
||||||
|
|
|
|
||||||
= help: only `no_span`, `style` and `applicability` are valid nested attributes
|
= help: only `style` and `applicability` are valid nested attributes
|
||||||
|
|
||||||
error: derive(Diagnostic): multipart suggestion without any `#[suggestion_part(...)]` fields
|
error: derive(Diagnostic): multipart suggestion without any `#[suggestion_part(...)]` fields
|
||||||
--> $DIR/subdiagnostic-derive.rs:530:1
|
--> $DIR/subdiagnostic-derive.rs:530:1
|
||||||
|
|
@ -381,10 +381,10 @@ LL | #[applicability]
|
||||||
| ^
|
| ^
|
||||||
|
|
||||||
error: derive(Diagnostic): expected exactly one string literal for `code = ...`
|
error: derive(Diagnostic): expected exactly one string literal for `code = ...`
|
||||||
--> $DIR/subdiagnostic-derive.rs:663:34
|
--> $DIR/subdiagnostic-derive.rs:663:28
|
||||||
|
|
|
|
||||||
LL | #[suggestion_part(code("foo"))]
|
LL | #[suggestion_part(code("foo"))]
|
||||||
| ^
|
| ^^^^^
|
||||||
|
|
||||||
error: unexpected token, expected `)`
|
error: unexpected token, expected `)`
|
||||||
--> $DIR/subdiagnostic-derive.rs:663:28
|
--> $DIR/subdiagnostic-derive.rs:663:28
|
||||||
|
|
@ -393,10 +393,10 @@ LL | #[suggestion_part(code("foo"))]
|
||||||
| ^^^^^
|
| ^^^^^
|
||||||
|
|
||||||
error: derive(Diagnostic): expected exactly one string literal for `code = ...`
|
error: derive(Diagnostic): expected exactly one string literal for `code = ...`
|
||||||
--> $DIR/subdiagnostic-derive.rs:673:41
|
--> $DIR/subdiagnostic-derive.rs:673:28
|
||||||
|
|
|
|
||||||
LL | #[suggestion_part(code("foo", "bar"))]
|
LL | #[suggestion_part(code("foo", "bar"))]
|
||||||
| ^
|
| ^^^^^
|
||||||
|
|
||||||
error: unexpected token, expected `)`
|
error: unexpected token, expected `)`
|
||||||
--> $DIR/subdiagnostic-derive.rs:673:28
|
--> $DIR/subdiagnostic-derive.rs:673:28
|
||||||
|
|
@ -405,10 +405,10 @@ LL | #[suggestion_part(code("foo", "bar"))]
|
||||||
| ^^^^^
|
| ^^^^^
|
||||||
|
|
||||||
error: derive(Diagnostic): expected exactly one string literal for `code = ...`
|
error: derive(Diagnostic): expected exactly one string literal for `code = ...`
|
||||||
--> $DIR/subdiagnostic-derive.rs:683:30
|
--> $DIR/subdiagnostic-derive.rs:683:28
|
||||||
|
|
|
|
||||||
LL | #[suggestion_part(code(3))]
|
LL | #[suggestion_part(code(3))]
|
||||||
| ^
|
| ^
|
||||||
|
|
||||||
error: unexpected token, expected `)`
|
error: unexpected token, expected `)`
|
||||||
--> $DIR/subdiagnostic-derive.rs:683:28
|
--> $DIR/subdiagnostic-derive.rs:683:28
|
||||||
|
|
@ -417,10 +417,10 @@ LL | #[suggestion_part(code(3))]
|
||||||
| ^
|
| ^
|
||||||
|
|
||||||
error: derive(Diagnostic): expected exactly one string literal for `code = ...`
|
error: derive(Diagnostic): expected exactly one string literal for `code = ...`
|
||||||
--> $DIR/subdiagnostic-derive.rs:693:29
|
--> $DIR/subdiagnostic-derive.rs:693:28
|
||||||
|
|
|
|
||||||
LL | #[suggestion_part(code())]
|
LL | #[suggestion_part(code())]
|
||||||
| ^
|
| ^
|
||||||
|
|
||||||
error: expected string literal
|
error: expected string literal
|
||||||
--> $DIR/subdiagnostic-derive.rs:702:30
|
--> $DIR/subdiagnostic-derive.rs:702:30
|
||||||
|
|
@ -464,32 +464,26 @@ LL | #[suggestion(no_crate_example, code = "", style = "foo")]
|
||||||
|
|
|
|
||||||
= help: valid styles are `normal`, `short`, `hidden`, `verbose` and `tool-only`
|
= help: valid styles are `normal`, `short`, `hidden`, `verbose` and `tool-only`
|
||||||
|
|
||||||
error: derive(Diagnostic): expected `= "xxx"`
|
error: expected string literal
|
||||||
--> $DIR/subdiagnostic-derive.rs:777:49
|
--> $DIR/subdiagnostic-derive.rs:777:51
|
||||||
|
|
|
|
||||||
LL | #[suggestion(no_crate_example, code = "", style = 42)]
|
LL | #[suggestion(no_crate_example, code = "", style = 42)]
|
||||||
| ^
|
| ^^
|
||||||
|
|
||||||
error: derive(Diagnostic): a diagnostic slug must be the first argument to the attribute
|
error: derive(Diagnostic): a diagnostic slug must be the first argument to the attribute
|
||||||
--> $DIR/subdiagnostic-derive.rs:785:48
|
--> $DIR/subdiagnostic-derive.rs:785:43
|
||||||
|
|
|
|
||||||
LL | #[suggestion(no_crate_example, code = "", style)]
|
LL | #[suggestion(no_crate_example, code = "", style)]
|
||||||
| ^
|
| ^^^^^
|
||||||
|
|
||||||
error: derive(Diagnostic): expected `= "xxx"`
|
error: expected `=`
|
||||||
--> $DIR/subdiagnostic-derive.rs:793:48
|
|
||||||
|
|
|
||||||
LL | #[suggestion(no_crate_example, code = "", style("foo"))]
|
|
||||||
| ^
|
|
||||||
|
|
||||||
error: expected `,`
|
|
||||||
--> $DIR/subdiagnostic-derive.rs:793:48
|
--> $DIR/subdiagnostic-derive.rs:793:48
|
||||||
|
|
|
|
||||||
LL | #[suggestion(no_crate_example, code = "", style("foo"))]
|
LL | #[suggestion(no_crate_example, code = "", style("foo"))]
|
||||||
| ^
|
| ^
|
||||||
|
|
||||||
error: derive(Diagnostic): `#[primary_span]` is not a valid attribute
|
error: derive(Diagnostic): `#[primary_span]` is not a valid attribute
|
||||||
--> $DIR/subdiagnostic-derive.rs:805:5
|
--> $DIR/subdiagnostic-derive.rs:804:5
|
||||||
|
|
|
|
||||||
LL | #[primary_span]
|
LL | #[primary_span]
|
||||||
| ^
|
| ^
|
||||||
|
|
@ -498,7 +492,7 @@ LL | #[primary_span]
|
||||||
= help: to create a suggestion with multiple spans, use `#[multipart_suggestion]` instead
|
= help: to create a suggestion with multiple spans, use `#[multipart_suggestion]` instead
|
||||||
|
|
||||||
error: derive(Diagnostic): suggestion without `#[primary_span]` field
|
error: derive(Diagnostic): suggestion without `#[primary_span]` field
|
||||||
--> $DIR/subdiagnostic-derive.rs:802:1
|
--> $DIR/subdiagnostic-derive.rs:801:1
|
||||||
|
|
|
|
||||||
LL | #[suggestion(no_crate_example, code = "")]
|
LL | #[suggestion(no_crate_example, code = "")]
|
||||||
| ^
|
| ^
|
||||||
|
|
@ -557,5 +551,5 @@ error: cannot find attribute `bar` in this scope
|
||||||
LL | #[bar("...")]
|
LL | #[bar("...")]
|
||||||
| ^^^
|
| ^^^
|
||||||
|
|
||||||
error: aborting due to 84 previous errors
|
error: aborting due to 83 previous errors
|
||||||
|
|
||||||
|
|
|
||||||
10
tests/ui/frontmatter/content-cr.rs
Normal file
10
tests/ui/frontmatter/content-cr.rs
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
---
|
||||||
|
package.name = "
" # //~ ERROR bare CR not allowed in frontmatter
|
||||||
|
package.description = "é"
|
||||||
|
---
|
||||||
|
|
||||||
|
// ignore-tidy-cr
|
||||||
|
|
||||||
|
#![feature(frontmatter)]
|
||||||
|
|
||||||
|
pub fn main() {}
|
||||||
8
tests/ui/frontmatter/content-cr.stderr
Normal file
8
tests/ui/frontmatter/content-cr.stderr
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
error: bare CR not allowed in frontmatter
|
||||||
|
--> $DIR/content-cr.rs:2:17
|
||||||
|
|
|
||||||
|
LL | package.name = "␍" #
|
||||||
|
| ^
|
||||||
|
|
||||||
|
error: aborting due to 1 previous error
|
||||||
|
|
||||||
|
|
@ -2,7 +2,7 @@ error[E0599]: the method `quote_into_iter` exists for struct `Ipv4Addr`, but its
|
||||||
--> $DIR/not-repeatable.rs:11:13
|
--> $DIR/not-repeatable.rs:11:13
|
||||||
|
|
|
|
||||||
LL | struct Ipv4Addr;
|
LL | struct Ipv4Addr;
|
||||||
| --------------- method `quote_into_iter` not found for this struct because it doesn't satisfy `Ipv4Addr: Iterator`, `Ipv4Addr: ToTokens`, `Ipv4Addr: proc_macro::ext::RepIteratorExt` or `Ipv4Addr: proc_macro::ext::RepToTokensExt`
|
| --------------- method `quote_into_iter` not found for this struct because `Ipv4Addr` doesn't implement `Iterator` or `ToTokens`
|
||||||
...
|
...
|
||||||
LL | let _ = quote! { $($ip)* };
|
LL | let _ = quote! { $($ip)* };
|
||||||
| ^^^^^^^^^^^^^^^^^^ method cannot be called on `Ipv4Addr` due to unsatisfied trait bounds
|
| ^^^^^^^^^^^^^^^^^^ method cannot be called on `Ipv4Addr` due to unsatisfied trait bounds
|
||||||
|
|
|
||||||
35
tests/ui/rfcs/rfc-1861-extern-types/comparison.rs
Normal file
35
tests/ui/rfcs/rfc-1861-extern-types/comparison.rs
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
// Foreign type tests not covering all operations
|
||||||
|
//@ only-nightly
|
||||||
|
//@ build-pass
|
||||||
|
|
||||||
|
#![feature(extern_types)]
|
||||||
|
|
||||||
|
#![allow(ambiguous_wide_pointer_comparisons)]
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
type ForeignType;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
struct Example {
|
||||||
|
field: ForeignType,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// pointer comparison
|
||||||
|
let a = std::ptr::null::<ForeignType>();
|
||||||
|
let b = std::ptr::null::<ForeignType>();
|
||||||
|
|
||||||
|
assert!(a == b);
|
||||||
|
|
||||||
|
// field address computation
|
||||||
|
let p = std::ptr::null::<Example>();
|
||||||
|
unsafe {
|
||||||
|
let _ = &(*p).field;
|
||||||
|
}
|
||||||
|
|
||||||
|
// pointer casts involving extern types
|
||||||
|
let raw = std::ptr::null::<()>();
|
||||||
|
let ext = raw as *const ForeignType;
|
||||||
|
let _ = ext as *const ();
|
||||||
|
}
|
||||||
|
|
@ -12,6 +12,6 @@ use std::simd::num::*;
|
||||||
pub unsafe fn mask_to_array(mask: u8) -> [i32; 8] {
|
pub unsafe fn mask_to_array(mask: u8) -> [i32; 8] {
|
||||||
let mut output = [0; 8];
|
let mut output = [0; 8];
|
||||||
let m = masksizex8::from_bitmask(mask as _);
|
let m = masksizex8::from_bitmask(mask as _);
|
||||||
output.copy_from_slice(&m.to_int().cast::<i32>().to_array());
|
output.copy_from_slice(&m.to_simd().cast::<i32>().to_array());
|
||||||
output
|
output
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
struct Foo;
|
||||||
|
trait Bar {}
|
||||||
|
trait Baz {}
|
||||||
|
trait Bat { fn bat(&self); }
|
||||||
|
impl<T> Bat for T where T: 'static + Bar + Baz { fn bat(&self) { println!("generic bat"); } }
|
||||||
|
|
||||||
|
pub fn main() {
|
||||||
|
Foo::bat(()); //~ ERROR E0599
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
error[E0599]: the function or associated item `bat` exists for struct `Foo`, but its trait bounds were not satisfied
|
||||||
|
--> $DIR/associated-item-unsatisfied-trait-bounds.rs:8:10
|
||||||
|
|
|
||||||
|
LL | struct Foo;
|
||||||
|
| ---------- function or associated item `bat` not found for this struct because `Foo` doesn't implement `Bar` or `Baz`
|
||||||
|
...
|
||||||
|
LL | Foo::bat(());
|
||||||
|
| ^^^ function or associated item cannot be called on `Foo` due to unsatisfied trait bounds
|
||||||
|
|
|
||||||
|
note: for `bat` to be available, `Foo` must implement `Bar` and `Baz`
|
||||||
|
--> $DIR/associated-item-unsatisfied-trait-bounds.rs:5:38
|
||||||
|
|
|
||||||
|
LL | impl<T> Bat for T where T: 'static + Bar + Baz { fn bat(&self) { println!("generic bat"); } }
|
||||||
|
| --- - ^^^ ^^^ unsatisfied trait bound introduced here
|
||||||
|
| |
|
||||||
|
| unsatisfied trait bound introduced here
|
||||||
|
note: the traits `Bar` and `Baz` must be implemented
|
||||||
|
--> $DIR/associated-item-unsatisfied-trait-bounds.rs:2:1
|
||||||
|
|
|
||||||
|
LL | trait Bar {}
|
||||||
|
| ^^^^^^^^^
|
||||||
|
LL | trait Baz {}
|
||||||
|
| ^^^^^^^^^
|
||||||
|
= help: items from traits can only be used if the trait is implemented and in scope
|
||||||
|
note: `Bat` defines an item `bat`, perhaps you need to implement it
|
||||||
|
--> $DIR/associated-item-unsatisfied-trait-bounds.rs:4:1
|
||||||
|
|
|
||||||
|
LL | trait Bat { fn bat(&self); }
|
||||||
|
| ^^^^^^^^^
|
||||||
|
|
||||||
|
error: aborting due to 1 previous error
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0599`.
|
||||||
Loading…
Add table
Add a link
Reference in a new issue