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:
bors 2026-01-29 11:49:06 +00:00
commit 370143facf
60 changed files with 1562 additions and 1718 deletions

View file

@ -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"),)

View file

@ -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);
} }

View file

@ -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); }

View file

@ -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 }))
} }
} }

View file

@ -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 `;`

View file

@ -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 {

View file

@ -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);

View file

@ -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

View file

@ -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

View file

@ -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",
]

View file

@ -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.

View file

@ -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()
} }

View file

@ -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 :^)

View file

@ -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]`:

View file

@ -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 {

View file

@ -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
);

View file

@ -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.

View file

@ -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 {

View file

@ -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
}
}

View file

@ -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
}
}

View file

@ -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::*;

View file

@ -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;

View file

@ -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) {

View file

@ -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;

View file

@ -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]

View file

@ -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;

View file

@ -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) }
} }
} }

View file

@ -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]

View file

@ -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)

View file

@ -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;

View file

@ -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>;

View file

@ -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>;

View file

@ -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>;

View file

@ -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>;

View file

@ -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>()) }
} }
} }

View file

@ -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))
} }

View file

@ -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;

View file

@ -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>();

View file

@ -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 }

View file

@ -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 }
}

View file

@ -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 }
}

View file

@ -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]

View file

@ -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,
} }

View file

@ -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(

View file

@ -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"

View 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);
}
};
}

View file

@ -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

View file

@ -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"]

View file

@ -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]);

View file

@ -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)]

View file

@ -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`.

View file

@ -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,

View file

@ -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

View file

@ -0,0 +1,10 @@
---
package.name = " " # //~ ERROR bare CR not allowed in frontmatter
package.description = "é"
---
// ignore-tidy-cr
#![feature(frontmatter)]
pub fn main() {}

View 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

View file

@ -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

View 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 ();
}

View file

@ -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
} }

View file

@ -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
}

View file

@ -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`.