os: ubuntu-latest-xl
- name: dist-x86_64-apple
env:
- SCRIPT: "./x.py dist --exclude src/doc --exclude extended && ./x.py dist --target=x86_64-apple-darwin src/doc && ./x.py dist extended"
+ SCRIPT: "./x.py dist --exclude rust-docs --exclude extended && ./x.py dist --target=x86_64-apple-darwin rust-docs && ./x.py dist extended"
RUST_CONFIGURE_ARGS: "--host=x86_64-apple-darwin --target=x86_64-apple-darwin,aarch64-apple-ios,x86_64-apple-ios,aarch64-apple-ios-sim --enable-full-tools --enable-sanitizers --enable-profiler --set rust.jemalloc --set llvm.ninja=false"
RUSTC_RETRY_LINKER_ON_SEGFAULT: 1
MACOSX_DEPLOYMENT_TARGET: 10.7
pub template: Vec<InlineAsmTemplatePiece>,
pub template_strs: Box<[(Symbol, Option<Symbol>, Span)]>,
pub operands: Vec<(InlineAsmOperand, Span)>,
- pub clobber_abi: Option<(Symbol, Span)>,
+ pub clobber_abis: Vec<(Symbol, Span)>,
pub options: InlineAsmOptions,
pub line_spans: Vec<Span>,
}
/// E.g., `extern {}` or `extern "C" {}`.
ForeignMod(ForeignMod),
/// Module-level inline assembly (from `global_asm!()`).
- GlobalAsm(InlineAsm),
+ GlobalAsm(Box<InlineAsm>),
/// A type alias (`type`).
///
/// E.g., `type Foo = Bar<u8>;`.
if let Some(mut idx) = token_text.find('\n') {
code_to_the_left = false;
while let Some(next_newline) = &token_text[idx + 1..].find('\n') {
- idx = idx + 1 + next_newline;
+ idx += 1 + next_newline;
comments.push(Comment {
style: CommentStyle::BlankLine,
lines: vec![],
use rustc_ast::*;
use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::stable_set::FxHashSet;
use rustc_errors::struct_span_err;
use rustc_hir as hir;
use rustc_session::parse::feature_err;
.emit();
}
- let mut clobber_abi = None;
+ let mut clobber_abis = FxHashMap::default();
if let Some(asm_arch) = asm_arch {
- if let Some((abi_name, abi_span)) = asm.clobber_abi {
- match asm::InlineAsmClobberAbi::parse(asm_arch, &self.sess.target, abi_name) {
- Ok(abi) => clobber_abi = Some((abi, abi_span)),
+ for (abi_name, abi_span) in &asm.clobber_abis {
+ match asm::InlineAsmClobberAbi::parse(asm_arch, &self.sess.target, *abi_name) {
+ Ok(abi) => {
+ // If the abi was already in the list, emit an error
+ match clobber_abis.get(&abi) {
+ Some((prev_name, prev_sp)) => {
+ let mut err = self.sess.struct_span_err(
+ *abi_span,
+ &format!("`{}` ABI specified multiple times", prev_name),
+ );
+ err.span_label(*prev_sp, "previously specified here");
+
+ // Multiple different abi names may actually be the same ABI
+ // If the specified ABIs are not the same name, alert the user that they resolve to the same ABI
+ let source_map = self.sess.source_map();
+ if source_map.span_to_snippet(*prev_sp)
+ != source_map.span_to_snippet(*abi_span)
+ {
+ err.note("these ABIs are equivalent on the current target");
+ }
+
+ err.emit();
+ }
+ None => {
+ clobber_abis.insert(abi, (abi_name, *abi_span));
+ }
+ }
+ }
Err(&[]) => {
self.sess
.struct_span_err(
- abi_span,
+ *abi_span,
"`clobber_abi` is not supported on this target",
)
.emit();
}
Err(supported_abis) => {
let mut err =
- self.sess.struct_span_err(abi_span, "invalid ABI for `clobber_abi`");
+ self.sess.struct_span_err(*abi_span, "invalid ABI for `clobber_abi`");
let mut abis = format!("`{}`", supported_abis[0]);
for m in &supported_abis[1..] {
let _ = write!(abis, ", `{}`", m);
// If a clobber_abi is specified, add the necessary clobbers to the
// operands list.
- if let Some((abi, abi_span)) = clobber_abi {
+ let mut clobbered = FxHashSet::default();
+ for (abi, (_, abi_span)) in clobber_abis {
for &clobber in abi.clobbered_regs() {
+ // Don't emit a clobber for a register already clobbered
+ if clobbered.contains(&clobber) {
+ continue;
+ }
+
let mut output_used = false;
clobber.overlapping_regs(|reg| {
if used_output_regs.contains_key(®) {
},
self.lower_span(abi_span),
));
+ clobbered.insert(clobber);
}
}
}
use rustc_ast::{AssocTyConstraint, AssocTyConstraintKind, NodeId};
use rustc_ast::{PatKind, RangeEnd, VariantData};
use rustc_errors::struct_span_err;
-use rustc_feature::{AttributeGate, BUILTIN_ATTRIBUTE_MAP};
+use rustc_feature::{AttributeGate, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP};
use rustc_feature::{Features, GateIssue};
use rustc_session::parse::{feature_err, feature_err_issue};
use rustc_session::Session;
impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
fn visit_attribute(&mut self, attr: &ast::Attribute) {
- let attr_info =
- attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name)).map(|a| **a);
+ let attr_info = attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name));
// Check feature gates for built-in attributes.
- if let Some((.., AttributeGate::Gated(_, name, descr, has_feature))) = attr_info {
- gate_feature_fn!(self, has_feature, attr.span, name, descr);
+ if let Some(BuiltinAttribute {
+ gate: AttributeGate::Gated(_, name, descr, has_feature),
+ ..
+ }) = attr_info
+ {
+ gate_feature_fn!(self, has_feature, attr.span, *name, descr);
}
// Check unstable flavors of the `#[doc]` attribute.
if attr.has_name(sym::doc) {
let mut args = vec![AsmArg::Template(InlineAsmTemplatePiece::to_string(&asm.template))];
args.extend(asm.operands.iter().map(|(o, _)| AsmArg::Operand(o)));
- if let Some((abi, _)) = asm.clobber_abi {
- args.push(AsmArg::ClobberAbi(abi));
+ for (abi, _) in &asm.clobber_abis {
+ args.push(AsmArg::ClobberAbi(*abi));
}
if !asm.options.is_empty() {
args.push(AsmArg::Options(asm.options));
let param = generics.type_param(¶m_ty, tcx);
if let Some(generics) = tcx
.hir()
- .get_generics(tcx.closure_base_def_id(self.mir_def_id().to_def_id()))
+ .get_generics(tcx.typeck_root_def_id(self.mir_def_id().to_def_id()))
{
suggest_constraining_type_param(
tcx,
#![feature(bool_to_option)]
#![feature(box_patterns)]
#![feature(crate_visibility_modifier)]
-#![feature(format_args_capture)]
+#![cfg_attr(bootstrap, feature(format_args_capture))]
#![feature(in_band_lifetimes)]
#![feature(iter_zip)]
#![feature(let_else)]
errors_buffer: &mut Vec<Diagnostic>,
) {
let tcx = infcx.tcx;
- let base_def_id = tcx.closure_base_def_id(body.source.def_id());
+ let base_def_id = tcx.typeck_root_def_id(body.source.def_id());
if !tcx.has_attr(base_def_id, sym::rustc_regions) {
return;
}
// to store those. Otherwise, we'll pass in `None` to the
// functions below, which will trigger them to report errors
// eagerly.
- let mut outlives_requirements = infcx.tcx.is_closure(mir_def_id).then(Vec::new);
+ let mut outlives_requirements = infcx.tcx.is_typeck_child(mir_def_id).then(Vec::new);
self.check_type_tests(infcx, body, outlives_requirements.as_mut(), &mut errors_buffer);
tcx,
closure_substs,
self.num_external_vids,
- tcx.closure_base_def_id(closure_def_id),
+ tcx.typeck_root_def_id(closure_def_id),
);
debug!("apply_requirements: closure_mapping={:?}", closure_mapping);
use rustc_data_structures::vec_map::VecMap;
use rustc_errors::struct_span_err;
use rustc_hir as hir;
+use rustc_hir::def::DefKind;
use rustc_hir::def_id::LocalDefId;
use rustc_hir::lang_items::LangItem;
use rustc_index::vec::{Idx, IndexVec};
// though.
let category = match place.as_local() {
Some(RETURN_PLACE) => {
- if let BorrowCheckContext {
- universal_regions:
- UniversalRegions { defining_ty: DefiningTy::Const(def_id, _), .. },
- ..
- } = self.borrowck_context
- {
- if tcx.is_static(*def_id) {
+ let defining_ty = &self.borrowck_context.universal_regions.defining_ty;
+ if defining_ty.is_const() {
+ if tcx.is_static(defining_ty.def_id()) {
ConstraintCategory::UseAsStatic
} else {
ConstraintCategory::UseAsConst
}
}
TerminatorKind::SwitchInt { ref discr, switch_ty, .. } => {
+ self.check_operand(discr, term_location);
+
let discr_ty = discr.ty(body, tcx);
if let Err(terr) = self.sub_types(
discr_ty,
// FIXME: check the values
}
TerminatorKind::Call { ref func, ref args, ref destination, from_hir_call, .. } => {
+ self.check_operand(func, term_location);
+ for arg in args {
+ self.check_operand(arg, term_location);
+ }
+
let func_ty = func.ty(body, tcx);
debug!("check_terminator: call, func_ty={:?}", func_ty);
let sig = match func_ty.kind() {
self.check_call_inputs(body, term, &sig, args, term_location, from_hir_call);
}
TerminatorKind::Assert { ref cond, ref msg, .. } => {
+ self.check_operand(cond, term_location);
+
let cond_ty = cond.ty(body, tcx);
if cond_ty != tcx.types.bool {
span_mirbug!(self, term, "bad Assert ({:?}, not bool", cond_ty);
}
}
TerminatorKind::Yield { ref value, .. } => {
+ self.check_operand(value, term_location);
+
let value_ty = value.ty(body, tcx);
match body.yield_ty() {
None => span_mirbug!(self, term, "yield in non-generator"),
Some(RETURN_PLACE) => {
if let BorrowCheckContext {
universal_regions:
- UniversalRegions { defining_ty: DefiningTy::Const(def_id, _), .. },
+ UniversalRegions {
+ defining_ty:
+ DefiningTy::Const(def_id, _)
+ | DefiningTy::InlineConst(def_id, _),
+ ..
+ },
..
} = self.borrowck_context
{
}
}
+ fn check_operand(&mut self, op: &Operand<'tcx>, location: Location) {
+ if let Operand::Constant(constant) = op {
+ let maybe_uneval = match constant.literal {
+ ConstantKind::Ty(ct) => match ct.val {
+ ty::ConstKind::Unevaluated(uv) => Some(uv),
+ _ => None,
+ },
+ _ => None,
+ };
+ if let Some(uv) = maybe_uneval {
+ if uv.promoted.is_none() {
+ let tcx = self.tcx();
+ let def_id = uv.def.def_id_for_type_of();
+ if tcx.def_kind(def_id) == DefKind::InlineConst {
+ let predicates = self.prove_closure_bounds(
+ tcx,
+ def_id.expect_local(),
+ uv.substs(tcx),
+ location,
+ );
+ self.normalize_and_prove_instantiated_predicates(
+ def_id,
+ predicates,
+ location.to_locations(),
+ );
+ }
+ }
+ }
+ }
+ }
+
fn check_rvalue(&mut self, body: &Body<'tcx>, rvalue: &Rvalue<'tcx>, location: Location) {
let tcx = self.tcx();
match rvalue {
Rvalue::Aggregate(ak, ops) => {
+ for op in ops {
+ self.check_operand(op, location);
+ }
self.check_aggregate_rvalue(&body, rvalue, ak, ops, location)
}
Rvalue::Repeat(operand, len) => {
+ self.check_operand(operand, location);
+
// If the length cannot be evaluated we must assume that the length can be larger
// than 1.
// If the length is larger than 1, the repeat expression will need to copy the
}
}
- Rvalue::NullaryOp(_, ty) | Rvalue::ShallowInitBox(_, ty) => {
+ Rvalue::NullaryOp(_, ty) => {
+ let trait_ref = ty::TraitRef {
+ def_id: tcx.require_lang_item(LangItem::Sized, Some(self.last_span)),
+ substs: tcx.mk_substs_trait(ty, &[]),
+ };
+
+ self.prove_trait_ref(
+ trait_ref,
+ location.to_locations(),
+ ConstraintCategory::SizedBound,
+ );
+ }
+
+ Rvalue::ShallowInitBox(operand, ty) => {
+ self.check_operand(operand, location);
+
let trait_ref = ty::TraitRef {
def_id: tcx.require_lang_item(LangItem::Sized, Some(self.last_span)),
substs: tcx.mk_substs_trait(ty, &[]),
}
Rvalue::Cast(cast_kind, op, ty) => {
+ self.check_operand(op, location);
+
match cast_kind {
CastKind::Pointer(PointerCast::ReifyFnPointer) => {
let fn_sig = op.ty(body, tcx).fn_sig(tcx);
BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge,
box (left, right),
) => {
+ self.check_operand(left, location);
+ self.check_operand(right, location);
+
let ty_left = left.ty(body, tcx);
match ty_left.kind() {
// Types with regions are comparable if they have a common super-type.
}
}
+ Rvalue::Use(operand) | Rvalue::UnaryOp(_, operand) => {
+ self.check_operand(operand, location);
+ }
+
+ Rvalue::BinaryOp(_, box (left, right))
+ | Rvalue::CheckedBinaryOp(_, box (left, right)) => {
+ self.check_operand(left, location);
+ self.check_operand(right, location);
+ }
+
Rvalue::AddressOf(..)
| Rvalue::ThreadLocalRef(..)
- | Rvalue::Use(..)
| Rvalue::Len(..)
- | Rvalue::BinaryOp(..)
- | Rvalue::CheckedBinaryOp(..)
- | Rvalue::UnaryOp(..)
| Rvalue::Discriminant(..) => {}
}
}
use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin};
use rustc_middle::ty::fold::TypeFoldable;
use rustc_middle::ty::subst::{InternalSubsts, Subst, SubstsRef};
-use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt};
+use rustc_middle::ty::{self, InlineConstSubsts, InlineConstSubstsParts, RegionVid, Ty, TyCtxt};
use std::iter;
use crate::nll::ToRegionVid;
/// is that it has no inputs and a single return value, which is
/// the value of the constant.
Const(DefId, SubstsRef<'tcx>),
+
+ /// The MIR represents an inline const. The signature has no inputs and a
+ /// single return value found via `InlineConstSubsts::ty`.
+ InlineConst(DefId, SubstsRef<'tcx>),
}
impl<'tcx> DefiningTy<'tcx> {
DefiningTy::Generator(_, substs, _) => {
Either::Right(Either::Left(substs.as_generator().upvar_tys()))
}
- DefiningTy::FnDef(..) | DefiningTy::Const(..) => {
+ DefiningTy::FnDef(..) | DefiningTy::Const(..) | DefiningTy::InlineConst(..) => {
Either::Right(Either::Right(iter::empty()))
}
}
pub fn implicit_inputs(self) -> usize {
match self {
DefiningTy::Closure(..) | DefiningTy::Generator(..) => 1,
- DefiningTy::FnDef(..) | DefiningTy::Const(..) => 0,
+ DefiningTy::FnDef(..) | DefiningTy::Const(..) | DefiningTy::InlineConst(..) => 0,
}
}
}
pub fn is_const(&self) -> bool {
- matches!(*self, DefiningTy::Const(..))
+ matches!(*self, DefiningTy::Const(..) | DefiningTy::InlineConst(..))
}
pub fn def_id(&self) -> DefId {
DefiningTy::Closure(def_id, ..)
| DefiningTy::Generator(def_id, ..)
| DefiningTy::FnDef(def_id, ..)
- | DefiningTy::Const(def_id, ..) => def_id,
+ | DefiningTy::Const(def_id, ..)
+ | DefiningTy::InlineConst(def_id, ..) => def_id,
}
}
}
tcx: TyCtxt<'tcx>,
closure_substs: SubstsRef<'tcx>,
expected_num_vars: usize,
- closure_base_def_id: DefId,
+ typeck_root_def_id: DefId,
) -> IndexVec<RegionVid, ty::Region<'tcx>> {
let mut region_mapping = IndexVec::with_capacity(expected_num_vars);
region_mapping.push(tcx.lifetimes.re_static);
region_mapping.push(fr);
});
- for_each_late_bound_region_defined_on(tcx, closure_base_def_id, |r| {
+ for_each_late_bound_region_defined_on(tcx, typeck_root_def_id, |r| {
region_mapping.push(r);
});
// tests, and the resulting print-outs include def-ids
// and other things that are not stable across tests!
// So we just include the region-vid. Annoying.
- let closure_base_def_id = tcx.closure_base_def_id(def_id);
- for_each_late_bound_region_defined_on(tcx, closure_base_def_id, |r| {
+ let typeck_root_def_id = tcx.typeck_root_def_id(def_id);
+ for_each_late_bound_region_defined_on(tcx, typeck_root_def_id, |r| {
err.note(&format!("late-bound region is {:?}", self.to_region_vid(r),));
});
}
// FIXME: As above, we'd like to print out the region
// `r` but doing so is not stable across architectures
// and so forth.
- let closure_base_def_id = tcx.closure_base_def_id(def_id);
- for_each_late_bound_region_defined_on(tcx, closure_base_def_id, |r| {
+ let typeck_root_def_id = tcx.typeck_root_def_id(def_id);
+ for_each_late_bound_region_defined_on(tcx, typeck_root_def_id, |r| {
err.note(&format!("late-bound region is {:?}", self.to_region_vid(r),));
});
}
tcx.def_path_str_with_substs(def_id, substs),
));
}
+ DefiningTy::InlineConst(def_id, substs) => {
+ err.note(&format!(
+ "defining inline constant type: {}",
+ tcx.def_path_str_with_substs(def_id, substs),
+ ));
+ }
}
}
}
let mut indices = self.compute_indices(fr_static, defining_ty);
debug!("build: indices={:?}", indices);
- let closure_base_def_id = self.infcx.tcx.closure_base_def_id(self.mir_def.did.to_def_id());
+ let typeck_root_def_id = self.infcx.tcx.typeck_root_def_id(self.mir_def.did.to_def_id());
// If this is a closure or generator, then the late-bound regions from the enclosing
// function are actually external regions to us. For example, here, 'a is not local
// fn foo<'a>() {
// let c = || { let x: &'a u32 = ...; }
// }
- if self.mir_def.did.to_def_id() != closure_base_def_id {
+ if self.mir_def.did.to_def_id() != typeck_root_def_id {
self.infcx
.replace_late_bound_regions_with_nll_infer_vars(self.mir_def.did, &mut indices)
}
);
// Converse of above, if this is a function then the late-bound regions declared on its
// signature are local to the fn.
- if self.mir_def.did.to_def_id() == closure_base_def_id {
+ if self.mir_def.did.to_def_id() == typeck_root_def_id {
self.infcx
.replace_late_bound_regions_with_nll_infer_vars(self.mir_def.did, &mut indices);
}
/// see `DefiningTy` for details.
fn defining_ty(&self) -> DefiningTy<'tcx> {
let tcx = self.infcx.tcx;
- let closure_base_def_id = tcx.closure_base_def_id(self.mir_def.did.to_def_id());
+ let typeck_root_def_id = tcx.typeck_root_def_id(self.mir_def.did.to_def_id());
match tcx.hir().body_owner_kind(self.mir_hir_id) {
BodyOwnerKind::Closure | BodyOwnerKind::Fn => {
- let defining_ty = if self.mir_def.did.to_def_id() == closure_base_def_id {
- tcx.type_of(closure_base_def_id)
+ let defining_ty = if self.mir_def.did.to_def_id() == typeck_root_def_id {
+ tcx.type_of(typeck_root_def_id)
} else {
let tables = tcx.typeck(self.mir_def.did);
tables.node_type(self.mir_hir_id)
}
BodyOwnerKind::Const | BodyOwnerKind::Static(..) => {
- assert_eq!(self.mir_def.did.to_def_id(), closure_base_def_id);
- let identity_substs = InternalSubsts::identity_for_item(tcx, closure_base_def_id);
- let substs =
- self.infcx.replace_free_regions_with_nll_infer_vars(FR, identity_substs);
- DefiningTy::Const(self.mir_def.did.to_def_id(), substs)
+ let identity_substs = InternalSubsts::identity_for_item(tcx, typeck_root_def_id);
+ if self.mir_def.did.to_def_id() == typeck_root_def_id {
+ let substs =
+ self.infcx.replace_free_regions_with_nll_infer_vars(FR, identity_substs);
+ DefiningTy::Const(self.mir_def.did.to_def_id(), substs)
+ } else {
+ let ty = tcx.typeck(self.mir_def.did).node_type(self.mir_hir_id);
+ let substs = InlineConstSubsts::new(
+ tcx,
+ InlineConstSubstsParts { parent_substs: identity_substs, ty },
+ )
+ .substs;
+ let substs = self.infcx.replace_free_regions_with_nll_infer_vars(FR, substs);
+ DefiningTy::InlineConst(self.mir_def.did.to_def_id(), substs)
+ }
}
}
}
defining_ty: DefiningTy<'tcx>,
) -> UniversalRegionIndices<'tcx> {
let tcx = self.infcx.tcx;
- let closure_base_def_id = tcx.closure_base_def_id(self.mir_def.did.to_def_id());
- let identity_substs = InternalSubsts::identity_for_item(tcx, closure_base_def_id);
+ let typeck_root_def_id = tcx.typeck_root_def_id(self.mir_def.did.to_def_id());
+ let identity_substs = InternalSubsts::identity_for_item(tcx, typeck_root_def_id);
let fr_substs = match defining_ty {
- DefiningTy::Closure(_, ref substs) | DefiningTy::Generator(_, ref substs, _) => {
+ DefiningTy::Closure(_, ref substs)
+ | DefiningTy::Generator(_, ref substs, _)
+ | DefiningTy::InlineConst(_, ref substs) => {
// In the case of closures, we rely on the fact that
// the first N elements in the ClosureSubsts are
- // inherited from the `closure_base_def_id`.
+ // inherited from the `typeck_root_def_id`.
// Therefore, when we zip together (below) with
// `identity_substs`, we will get only those regions
// that correspond to early-bound regions declared on
- // the `closure_base_def_id`.
+ // the `typeck_root_def_id`.
assert!(substs.len() >= identity_substs.len());
assert_eq!(substs.regions().count(), identity_substs.regions().count());
substs
let ty = indices.fold_to_region_vids(tcx, ty);
ty::Binder::dummy(tcx.intern_type_list(&[ty]))
}
+
+ DefiningTy::InlineConst(def_id, substs) => {
+ assert_eq!(self.mir_def.did.to_def_id(), def_id);
+ let ty = substs.as_inline_const().ty();
+ ty::Binder::dummy(tcx.intern_type_list(&[ty]))
+ }
}
}
}
indices: &mut UniversalRegionIndices<'tcx>,
) {
debug!("replace_late_bound_regions_with_nll_infer_vars(mir_def_id={:?})", mir_def_id);
- let closure_base_def_id = self.tcx.closure_base_def_id(mir_def_id.to_def_id());
- for_each_late_bound_region_defined_on(self.tcx, closure_base_def_id, |r| {
+ let typeck_root_def_id = self.tcx.typeck_root_def_id(mir_def_id.to_def_id());
+ for_each_late_bound_region_defined_on(self.tcx, typeck_root_def_id, |r| {
debug!("replace_late_bound_regions_with_nll_infer_vars: r={:?}", r);
if !indices.indices.contains_key(&r) {
let region_vid = self.next_nll_region_var(FR);
operands: Vec<(ast::InlineAsmOperand, Span)>,
named_args: FxHashMap<Symbol, usize>,
reg_args: FxHashSet<usize>,
- clobber_abi: Option<(Symbol, Span)>,
+ clobber_abis: Vec<(Symbol, Span)>,
options: ast::InlineAsmOptions,
options_spans: Vec<Span>,
}
operands: vec![],
named_args: FxHashMap::default(),
reg_args: FxHashSet::default(),
- clobber_abi: None,
+ clobber_abis: Vec::new(),
options: ast::InlineAsmOptions::empty(),
options_spans: vec![],
};
.span_labels(args.options_spans.clone(), "previous options")
.span_label(span, "argument")
.emit();
- } else if let Some((_, abi_span)) = args.clobber_abi {
+ } else if let Some((_, abi_span)) = args.clobber_abis.last() {
ecx.struct_span_err(span, "arguments are not allowed after clobber_abi")
- .span_label(abi_span, "clobber_abi")
+ .span_label(*abi_span, "clobber_abi")
.span_label(span, "argument")
.emit();
}
// Bail out now since this is likely to confuse MIR
return Err(err);
}
- if let Some((_, abi_span)) = args.clobber_abi {
+
+ if args.clobber_abis.len() > 0 {
if is_global_asm {
- let err =
- ecx.struct_span_err(abi_span, "`clobber_abi` cannot be used with `global_asm!`");
+ let err = ecx.struct_span_err(
+ args.clobber_abis.iter().map(|(_, span)| *span).collect::<Vec<Span>>(),
+ "`clobber_abi` cannot be used with `global_asm!`",
+ );
// Bail out now since this is likely to confuse later stages
return Err(err);
regclass_outputs.clone(),
"asm with `clobber_abi` must specify explicit registers for outputs",
)
- .span_label(abi_span, "clobber_abi")
+ .span_labels(
+ args.clobber_abis.iter().map(|(_, span)| *span).collect::<Vec<Span>>(),
+ "clobber_abi",
+ )
.span_labels(regclass_outputs, "generic outputs")
.emit();
}
p.expect(&token::OpenDelim(token::DelimToken::Paren))?;
- let clobber_abi = match p.parse_str_lit() {
- Ok(str_lit) => str_lit.symbol_unescaped,
- Err(opt_lit) => {
- let span = opt_lit.map_or(p.token.span, |lit| lit.span);
- let mut err = p.sess.span_diagnostic.struct_span_err(span, "expected string literal");
- err.span_label(span, "not a string literal");
- return Err(err);
- }
- };
+ if p.eat(&token::CloseDelim(token::DelimToken::Paren)) {
+ let err = p.sess.span_diagnostic.struct_span_err(
+ p.token.span,
+ "at least one abi must be provided as an argument to `clobber_abi`",
+ );
+ return Err(err);
+ }
- p.expect(&token::CloseDelim(token::DelimToken::Paren))?;
+ let mut new_abis = Vec::new();
+ loop {
+ match p.parse_str_lit() {
+ Ok(str_lit) => {
+ new_abis.push((str_lit.symbol_unescaped, str_lit.span));
+ }
+ Err(opt_lit) => {
+ // If the non-string literal is a closing paren then it's the end of the list and is fine
+ if p.eat(&token::CloseDelim(token::DelimToken::Paren)) {
+ break;
+ }
+ let span = opt_lit.map_or(p.token.span, |lit| lit.span);
+ let mut err =
+ p.sess.span_diagnostic.struct_span_err(span, "expected string literal");
+ err.span_label(span, "not a string literal");
+ return Err(err);
+ }
+ };
- let new_span = span_start.to(p.prev_token.span);
+ // Allow trailing commas
+ if p.eat(&token::CloseDelim(token::DelimToken::Paren)) {
+ break;
+ }
+ p.expect(&token::Comma)?;
+ }
- if let Some((_, prev_span)) = args.clobber_abi {
- let mut err = p
- .sess
- .span_diagnostic
- .struct_span_err(new_span, "clobber_abi specified multiple times");
- err.span_label(prev_span, "clobber_abi previously specified here");
- return Err(err);
- } else if !args.options_spans.is_empty() {
+ let full_span = span_start.to(p.prev_token.span);
+
+ if !args.options_spans.is_empty() {
let mut err = p
.sess
.span_diagnostic
- .struct_span_err(new_span, "clobber_abi is not allowed after options");
+ .struct_span_err(full_span, "clobber_abi is not allowed after options");
err.span_labels(args.options_spans.clone(), "options");
return Err(err);
}
- args.clobber_abi = Some((clobber_abi, new_span));
+ match &new_abis[..] {
+ // should have errored above during parsing
+ [] => unreachable!(),
+ [(abi, _span)] => args.clobber_abis.push((*abi, full_span)),
+ [abis @ ..] => {
+ for (abi, span) in abis {
+ args.clobber_abis.push((*abi, *span));
+ }
+ }
+ }
Ok(())
}
template,
template_strs: template_strs.into_boxed_slice(),
operands: args.operands,
- clobber_abi: args.clobber_abi,
+ clobber_abis: args.clobber_abis,
options: args.options,
line_spans,
})
ident: Ident::empty(),
attrs: Vec::new(),
id: ast::DUMMY_NODE_ID,
- kind: ast::ItemKind::GlobalAsm(inline_asm),
+ kind: ast::ItemKind::GlobalAsm(Box::new(inline_asm)),
vis: ast::Visibility {
span: sp.shrink_to_lo(),
kind: ast::VisibilityKind::Inherited,
self.verify_arg_type(Exact(idx), ty)
}
None => {
- let capture_feature_enabled = self
- .ecx
- .ecfg
- .features
- .map_or(false, |features| features.format_args_capture);
-
// For the moment capturing variables from format strings expanded from macros is
// disabled (see RFC #2795)
- let can_capture = capture_feature_enabled && self.is_literal;
-
- if can_capture {
+ if self.is_literal {
// Treat this name as a variable to capture from the surrounding scope
let idx = self.args.len();
self.arg_types.push(Vec::new());
};
let mut err = self.ecx.struct_span_err(sp, &msg[..]);
- if capture_feature_enabled && !self.is_literal {
- err.note(&format!(
- "did you intend to capture a variable `{}` from \
- the surrounding scope?",
- name
- ));
- err.note(
- "to avoid ambiguity, `format_args!` cannot capture variables \
- when the format string is expanded from a macro",
- );
- } else if self.ecx.parse_sess().unstable_features.is_nightly_build() {
- err.help(&format!(
- "if you intended to capture `{}` from the surrounding scope, add \
- `#![feature(format_args_capture)]` to the crate attributes",
- name
- ));
- }
+ err.note(&format!(
+ "did you intend to capture a variable `{}` from \
+ the surrounding scope?",
+ name
+ ));
+ err.note(
+ "to avoid ambiguity, `format_args!` cannot capture variables \
+ when the format string is expanded from a macro",
+ );
err.emit();
}
};
use rustc_codegen_ssa::traits::*;
use rustc_codegen_ssa::{CompiledModule, ModuleCodegen};
+use rustc_data_structures::profiling::SelfProfilerRef;
use rustc_data_structures::small_c_str::SmallCStr;
use rustc_errors::{FatalError, Handler, Level};
use rustc_fs_util::{link_or_copy, path_to_c_string};
output: &Path,
dwo_output: Option<&Path>,
file_type: llvm::FileType,
+ self_profiler_ref: &SelfProfilerRef,
) -> Result<(), FatalError> {
unsafe {
let output_c = path_to_c_string(output);
file_type,
)
};
+
+ // Record artifact sizes for self-profiling
+ if result == llvm::LLVMRustResult::Success {
+ let artifact_kind = match file_type {
+ llvm::FileType::ObjectFile => "object_file",
+ llvm::FileType::AssemblyFile => "assembly_file",
+ };
+ record_artifact_size(self_profiler_ref, artifact_kind, output);
+ if let Some(dwo_file) = dwo_output {
+ record_artifact_size(self_profiler_ref, "dwo_file", dwo_file);
+ }
+ }
+
result.into_result().map_err(|()| {
let msg = format!("could not write output to {}", output.display());
llvm_err(handler, &msg)
let thin = ThinBuffer::new(llmod);
let data = thin.data();
+ if let Some(bitcode_filename) = bc_out.file_name() {
+ cgcx.prof.artifact_size(
+ "llvm_bitcode",
+ bitcode_filename.to_string_lossy(),
+ data.len() as u64,
+ );
+ }
+
if config.emit_bc || config.emit_obj == EmitObj::Bitcode {
let _timer = cgcx.prof.generic_activity_with_arg(
"LLVM_module_codegen_emit_bitcode",
}
let result = llvm::LLVMRustPrintModule(llmod, out_c.as_ptr(), demangle_callback);
+
+ if result == llvm::LLVMRustResult::Success {
+ record_artifact_size(&cgcx.prof, "llvm_ir", &out);
+ }
+
result.into_result().map_err(|()| {
let msg = format!("failed to write LLVM IR to {}", out.display());
llvm_err(diag_handler, &msg)
&path,
None,
llvm::FileType::AssemblyFile,
+ &cgcx.prof,
)
})?;
}
&obj_out,
dwo_out,
llvm::FileType::ObjectFile,
+ &cgcx.prof,
)
})?;
}
symbol_name.starts_with(b"__llvm_profile_")
}
}
+
+fn record_artifact_size(
+ self_profiler_ref: &SelfProfilerRef,
+ artifact_kind: &'static str,
+ path: &Path,
+) {
+ // Don't stat the file if we are not going to record its size.
+ if !self_profiler_ref.enabled() {
+ return;
+ }
+
+ if let Some(artifact_name) = path.file_name() {
+ let file_size = std::fs::metadata(path).map(|m| m.len()).unwrap_or(0);
+ self_profiler_ref.artifact_size(artifact_kind, artifact_name.to_string_lossy(), file_size);
+ }
+}
type_names::push_item_name(self.tcx(), def_id, false, &mut name);
// Find the enclosing function, in case this is a closure.
- let enclosing_fn_def_id = self.tcx().closure_base_def_id(def_id);
+ let enclosing_fn_def_id = self.tcx().typeck_root_def_id(def_id);
// Get_template_parameters() will append a `<...>` clause to the function
// name if necessary.
use rustc_middle::ty::{self, Ty};
use rustc_middle::{bug, span_bug};
use rustc_span::{sym, symbol::kw, Span, Symbol};
-use rustc_target::abi::{self, HasDataLayout, Primitive};
+use rustc_target::abi::{self, Align, HasDataLayout, Primitive};
use rustc_target::spec::{HasTargetSpec, PanicStrategy};
use std::cmp::Ordering;
let arg_tys = sig.inputs();
if name == sym::simd_select_bitmask {
- let in_ty = arg_tys[0];
- let m_len = match in_ty.kind() {
- // Note that this `.unwrap()` crashes for isize/usize, that's sort
- // of intentional as there's not currently a use case for that.
- ty::Int(i) => i.bit_width().unwrap(),
- ty::Uint(i) => i.bit_width().unwrap(),
- _ => return_error!("`{}` is not an integral type", in_ty),
- };
require_simd!(arg_tys[1], "argument");
- let (v_len, _) = arg_tys[1].simd_size_and_type(bx.tcx());
- require!(
- // Allow masks for vectors with fewer than 8 elements to be
- // represented with a u8 or i8.
- m_len == v_len || (m_len == 8 && v_len < 8),
- "mismatched lengths: mask length `{}` != other vector length `{}`",
- m_len,
- v_len
- );
+ let (len, _) = arg_tys[1].simd_size_and_type(bx.tcx());
+
+ let expected_int_bits = (len.max(8) - 1).next_power_of_two();
+ let expected_bytes = len / 8 + ((len % 8 > 0) as u64);
+
+ let mask_ty = arg_tys[0];
+ let mask = match mask_ty.kind() {
+ ty::Int(i) if i.bit_width() == Some(expected_int_bits) => args[0].immediate(),
+ ty::Uint(i) if i.bit_width() == Some(expected_int_bits) => args[0].immediate(),
+ ty::Array(elem, len)
+ if matches!(elem.kind(), ty::Uint(ty::UintTy::U8))
+ && len.try_eval_usize(bx.tcx, ty::ParamEnv::reveal_all())
+ == Some(expected_bytes) =>
+ {
+ let place = PlaceRef::alloca(bx, args[0].layout);
+ args[0].val.store(bx, place);
+ let int_ty = bx.type_ix(expected_bytes * 8);
+ let ptr = bx.pointercast(place.llval, bx.cx.type_ptr_to(int_ty));
+ bx.load(int_ty, ptr, Align::ONE)
+ }
+ _ => return_error!(
+ "invalid bitmask `{}`, expected `u{}` or `[u8; {}]`",
+ mask_ty,
+ expected_int_bits,
+ expected_bytes
+ ),
+ };
+
let i1 = bx.type_i1();
- let im = bx.type_ix(v_len);
- let i1xn = bx.type_vector(i1, v_len);
- let m_im = bx.trunc(args[0].immediate(), im);
+ let im = bx.type_ix(len);
+ let i1xn = bx.type_vector(i1, len);
+ let m_im = bx.trunc(mask, im);
let m_i1s = bx.bitcast(m_im, i1xn);
return Ok(bx.select(m_i1s, args[1].immediate(), args[2].immediate()));
}
if name == sym::simd_bitmask {
// The `fn simd_bitmask(vector) -> unsigned integer` intrinsic takes a
- // vector mask and returns an unsigned integer containing the most
- // significant bit (MSB) of each lane.
-
- // If the vector has less than 8 lanes, a u8 is returned with zeroed
- // trailing bits.
+ // vector mask and returns the most significant bit (MSB) of each lane in the form
+ // of either:
+ // * an unsigned integer
+ // * an array of `u8`
+ // If the vector has less than 8 lanes, a u8 is returned with zeroed trailing bits.
+ //
+ // The bit order of the result depends on the byte endianness, LSB-first for little
+ // endian and MSB-first for big endian.
let expected_int_bits = in_len.max(8);
- match ret_ty.kind() {
- ty::Uint(i) if i.bit_width() == Some(expected_int_bits) => (),
- _ => return_error!("bitmask `{}`, expected `u{}`", ret_ty, expected_int_bits),
- }
+ let expected_bytes = expected_int_bits / 8 + ((expected_int_bits % 8 > 0) as u64);
// Integer vector <i{in_bitwidth} x in_len>:
let (i_xn, in_elem_bitwidth) = match in_elem.kind() {
let i1xn = bx.trunc(i_xn_msb, bx.type_vector(bx.type_i1(), in_len));
// Bitcast <i1 x N> to iN:
let i_ = bx.bitcast(i1xn, bx.type_ix(in_len));
- // Zero-extend iN to the bitmask type:
- return Ok(bx.zext(i_, bx.type_ix(expected_int_bits)));
+
+ match ret_ty.kind() {
+ ty::Uint(i) if i.bit_width() == Some(expected_int_bits) => {
+ // Zero-extend iN to the bitmask type:
+ return Ok(bx.zext(i_, bx.type_ix(expected_int_bits)));
+ }
+ ty::Array(elem, len)
+ if matches!(elem.kind(), ty::Uint(ty::UintTy::U8))
+ && len.try_eval_usize(bx.tcx, ty::ParamEnv::reveal_all())
+ == Some(expected_bytes) =>
+ {
+ // Zero-extend iN to the array lengh:
+ let ze = bx.zext(i_, bx.type_ix(expected_bytes * 8));
+
+ // Convert the integer to a byte array
+ let ptr = bx.alloca(bx.type_ix(expected_bytes * 8), Align::ONE);
+ bx.store(ze, ptr, Align::ONE);
+ let array_ty = bx.type_array(bx.type_i8(), expected_bytes);
+ let ptr = bx.pointercast(ptr, bx.cx.type_ptr_to(array_ty));
+ return Ok(bx.load(array_ty, ptr, Align::ONE));
+ }
+ _ => return_error!(
+ "cannot return `{}`, expected `u{}` or `[u8; {}]`",
+ ret_ty,
+ expected_int_bits,
+ expected_bytes
+ ),
+ }
}
fn simd_simple_float_intrinsic(
if sess.opts.json_artifact_notifications {
sess.parse_sess.span_diagnostic.emit_artifact_notification(&out_filename, "link");
}
+
+ if sess.prof.enabled() {
+ if let Some(artifact_name) = out_filename.file_name() {
+ // Record size for self-profiling
+ let file_size = std::fs::metadata(&out_filename).map(|m| m.len()).unwrap_or(0);
+
+ sess.prof.artifact_size(
+ "linked_artifact",
+ artifact_name.to_string_lossy(),
+ file_size,
+ );
+ }
+ }
}
}
}
impl<'a> GccLinker<'a> {
- /// Argument that must be passed *directly* to the linker
+ /// Passes an argument directly to the linker.
///
- /// These arguments need to be prepended with `-Wl`, when a GCC-style linker is used.
- fn linker_arg<S>(&mut self, arg: S) -> &mut Self
- where
- S: AsRef<OsStr>,
- {
- if !self.is_ld {
- let mut os = OsString::from("-Wl,");
- os.push(arg.as_ref());
- self.cmd.arg(os);
+ /// When the linker is not ld-like such as when using a compiler as a linker, the argument is
+ /// prepended by `-Wl,`.
+ fn linker_arg(&mut self, arg: impl AsRef<OsStr>) -> &mut Self {
+ self.linker_args(&[arg]);
+ self
+ }
+
+ /// Passes a series of arguments directly to the linker.
+ ///
+ /// When the linker is ld-like, the arguments are simply appended to the command. When the
+ /// linker is not ld-like such as when using a compiler as a linker, the arguments are joined by
+ /// commas to form an argument that is then prepended with `-Wl`. In this situation, only a
+ /// single argument is appended to the command to ensure that the order of the arguments is
+ /// preserved by the compiler.
+ fn linker_args(&mut self, args: &[impl AsRef<OsStr>]) -> &mut Self {
+ if self.is_ld {
+ args.into_iter().for_each(|a| {
+ self.cmd.arg(a);
+ });
} else {
- self.cmd.arg(arg);
+ if !args.is_empty() {
+ let mut s = OsString::from("-Wl");
+ for a in args {
+ s.push(",");
+ s.push(a);
+ }
+ self.cmd.arg(s);
+ }
}
self
}
if let Some(path) = &self.sess.opts.debugging_opts.profile_sample_use {
self.linker_arg(&format!("-plugin-opt=sample-profile={}", path.display()));
};
- self.linker_arg(&format!("-plugin-opt={}", opt_level));
- self.linker_arg(&format!("-plugin-opt=mcpu={}", self.target_cpu));
+ self.linker_args(&[
+ &format!("-plugin-opt={}", opt_level),
+ &format!("-plugin-opt=mcpu={}", self.target_cpu),
+ ]);
}
fn build_dylib(&mut self, out_filename: &Path) {
// On mac we need to tell the linker to let this library be rpathed
if self.sess.target.is_like_osx {
- self.cmd.arg("-dynamiclib");
+ if !self.is_ld {
+ self.cmd.arg("-dynamiclib");
+ }
+
self.linker_arg("-dylib");
// Note that the `osx_rpath_install_name` option here is a hack
// principled solution at some point to force the compiler to pass
// the right `-Wl,-install_name` with an `@rpath` in it.
if self.sess.opts.cg.rpath || self.sess.opts.debugging_opts.osx_rpath_install_name {
- self.linker_arg("-install_name");
- let mut v = OsString::from("@rpath/");
- v.push(out_filename.file_name().unwrap());
- self.linker_arg(&v);
+ let mut rpath = OsString::from("@rpath/");
+ rpath.push(out_filename.file_name().unwrap());
+ self.linker_args(&[OsString::from("-install_name"), rpath]);
}
} else {
self.cmd.arg("-shared");
self.build_dylib(out_filename);
}
LinkOutputKind::WasiReactorExe => {
- self.linker_arg("--entry");
- self.linker_arg("_initialize");
+ self.linker_args(&["--entry", "_initialize"]);
}
}
// VxWorks compiler driver introduced `--static-crt` flag specifically for rustc,
self.cmd.arg(path);
}
fn full_relro(&mut self) {
- self.linker_arg("-zrelro");
- self.linker_arg("-znow");
+ self.linker_args(&["-zrelro", "-znow"]);
}
fn partial_relro(&mut self) {
self.linker_arg("-zrelro");
}
let is_windows = self.sess.target.is_like_windows;
- let mut arg = OsString::new();
let path = tmpdir.join(if is_windows { "list.def" } else { "list" });
debug!("EXPORTED SYMBOLS:");
}
if self.sess.target.is_like_osx {
- if !self.is_ld {
- arg.push("-Wl,")
- }
- arg.push("-exported_symbols_list,");
+ self.linker_args(&[OsString::from("-exported_symbols_list"), path.into()]);
} else if self.sess.target.is_like_solaris {
- if !self.is_ld {
- arg.push("-Wl,")
- }
- arg.push("-M,");
+ self.linker_args(&[OsString::from("-M"), path.into()]);
} else {
- if !self.is_ld {
- arg.push("-Wl,")
- }
- // Both LD and LLD accept export list in *.def file form, there are no flags required
- if !is_windows {
- arg.push("--version-script=")
+ if is_windows {
+ self.linker_arg(path);
+ } else {
+ let mut arg = OsString::from("--version-script=");
+ arg.push(path);
+ self.linker_arg(arg);
}
}
-
- arg.push(&path);
- self.cmd.arg(arg);
}
fn subsystem(&mut self, subsystem: &str) {
self.linker_arg("--as-needed");
} else if self.sess.target.is_like_solaris {
// -z ignore is the Solaris equivalent to the GNU ld --as-needed option
- self.linker_arg("-z");
- self.linker_arg("ignore");
+ self.linker_args(&["-z", "ignore"]);
}
}
}
| DefKind::Static
| DefKind::ConstParam
| DefKind::AnonConst
+ | DefKind::InlineConst
| DefKind::AssocConst
),
"Unexpected DefKind: {:?}",
/// Whether to enforce the validity invariant
fn enforce_validity(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool;
+ /// Whether to enforce validity (e.g., initialization and not having ptr provenance)
+ /// of integers and floats.
+ fn enforce_number_validity(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool;
+
/// Whether function calls should be [ABI](Abi)-checked.
fn enforce_abi(_ecx: &InterpCx<'mir, 'tcx, Self>) -> bool {
true
false // for now, we don't enforce validity
}
+ #[inline(always)]
+ fn enforce_number_validity(_ecx: &InterpCx<$mir, $tcx, Self>) -> bool {
+ true
+ }
+
#[inline(always)]
fn call_extra_fn(
_ecx: &mut InterpCx<$mir, $tcx, Self>,
let value = self.read_scalar(value)?;
// NOTE: Keep this in sync with the array optimization for int/float
// types below!
- if self.ctfe_mode.is_some() {
+ if M::enforce_number_validity(self.ecx) {
// Integers/floats in CTFE: Must be scalar bits, pointers are dangerous
let is_bits = value.check_init().map_or(false, |v| v.try_to_int().is_ok());
if !is_bits {
{ "{}", value } expected { "initialized plain (non-pointer) bytes" }
)
}
- } else {
- // At run-time, for now, we accept *anything* for these types, including
- // uninit. We should fix that, but let's start low.
}
Ok(true)
}
}
};
+ let allow_uninit_and_ptr = !M::enforce_number_validity(self.ecx);
match alloc.check_bytes(
alloc_range(Size::ZERO, size),
- /*allow_uninit_and_ptr*/ self.ctfe_mode.is_none(),
+ allow_uninit_and_ptr,
) {
// In the happy case, we needn't check anything else.
Ok(()) => {}
match elem {
ProjectionElem::Deref => {
let base_ty = Place::ty_from(place_local, proj_base, self.body, self.tcx).ty;
- if let ty::RawPtr(_) = base_ty.kind() {
+ if base_ty.is_unsafe_ptr() {
if proj_base.is_empty() {
let decl = &self.body.local_decls[place_local];
if let Some(box LocalInfo::StaticRef { def_id, .. }) = decl.local_info {
return;
}
}
- self.check_op(ops::RawPtrDeref);
+
+ // `*const T` is stable, `*mut T` is not
+ if !base_ty.is_mutable_ptr() {
+ return;
+ }
+
+ self.check_op(ops::RawMutPtrDeref);
}
if context.is_mutating_use() {
}
#[derive(Debug)]
-pub struct RawPtrDeref;
-impl NonConstOp for RawPtrDeref {
+pub struct RawMutPtrDeref;
+impl NonConstOp for RawMutPtrDeref {
fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status {
- Status::Unstable(sym::const_raw_ptr_deref)
+ Status::Unstable(sym::const_mut_refs)
}
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
feature_err(
&ccx.tcx.sess.parse_sess,
- sym::const_raw_ptr_deref,
+ sym::const_mut_refs,
span,
- &format!("dereferencing raw pointers in {}s is unstable", ccx.const_kind(),),
+ &format!("dereferencing raw mutable pointers in {}s is unstable", ccx.const_kind(),),
)
}
}
None,
compiler.output_dir(),
compiler.output_file(),
+ compiler.temps_dir(),
);
if should_stop == Compilation::Stop {
Some(compiler.input()),
compiler.output_dir(),
compiler.output_file(),
+ compiler.temps_dir(),
)
.and_then(|| {
RustcDefaultCalls::list_metadata(
input: Option<&Input>,
odir: &Option<PathBuf>,
ofile: &Option<PathBuf>,
+ temps_dir: &Option<PathBuf>,
) -> Compilation {
use rustc_session::config::PrintRequest::*;
// PrintRequest::NativeStaticLibs is special - printed during linking
});
let attrs = attrs.as_ref().unwrap();
let t_outputs = rustc_interface::util::build_output_filenames(
- input, odir, ofile, attrs, sess,
+ input, odir, ofile, temps_dir, attrs, sess,
);
let id = rustc_session::output::find_crate_name(sess, attrs, input);
if *req == PrintRequest::CrateName {
Erroneous code example:
```compile_fail,E0206
-type Foo = [u8; 256];
-impl Copy for Foo { } // error!
-
#[derive(Copy, Clone)]
struct Bar;
impl Copy for &'static mut Bar { } // error!
```
-You can only implement `Copy` for a struct or an enum. Both of the previous
-examples will fail, because neither `[u8; 256]` nor `&'static mut Bar`
-(mutable reference to `Bar`) is a struct or enum.
+You can only implement `Copy` for a struct or an enum.
+The previous example will fail because `&'static mut Bar`
+is not a struct or enum.
#![feature(crate_visibility_modifier)]
#![feature(backtrace)]
#![feature(if_let_guard)]
-#![feature(format_args_capture)]
+#![cfg_attr(bootstrap, feature(format_args_capture))]
#![feature(iter_zip)]
#![feature(let_else)]
#![feature(nll)]
#![feature(crate_visibility_modifier)]
#![feature(decl_macro)]
#![feature(destructuring_assignment)]
-#![feature(format_args_capture)]
+#![cfg_attr(bootstrap, feature(format_args_capture))]
#![feature(if_let_guard)]
#![feature(iter_zip)]
#![feature(let_else)]
span: Span,
input: TokenStream,
) -> Result<TokenStream, ErrorReported> {
+ let proc_macro_backtrace = ecx.ecfg.proc_macro_backtrace;
let server = proc_macro_server::Rustc::new(ecx);
- self.client.run(&EXEC_STRATEGY, server, input, ecx.ecfg.proc_macro_backtrace).map_err(|e| {
+ self.client.run(&EXEC_STRATEGY, server, input, proc_macro_backtrace).map_err(|e| {
let mut err = ecx.struct_span_err(span, "proc macro panicked");
if let Some(s) = e.as_str() {
err.help(&format!("message: {}", s));
annotation: TokenStream,
annotated: TokenStream,
) -> Result<TokenStream, ErrorReported> {
+ let proc_macro_backtrace = ecx.ecfg.proc_macro_backtrace;
let server = proc_macro_server::Rustc::new(ecx);
self.client
- .run(&EXEC_STRATEGY, server, annotation, annotated, ecx.ecfg.proc_macro_backtrace)
+ .run(&EXEC_STRATEGY, server, annotation, annotated, proc_macro_backtrace)
.map_err(|e| {
let mut err = ecx.struct_span_err(span, "custom attribute panicked");
if let Some(s) = e.as_str() {
nt_to_tokenstream(&item, &ecx.sess.parse_sess, CanSynthesizeMissingTokens::No)
};
+ let proc_macro_backtrace = ecx.ecfg.proc_macro_backtrace;
let server = proc_macro_server::Rustc::new(ecx);
- let stream =
- match self.client.run(&EXEC_STRATEGY, server, input, ecx.ecfg.proc_macro_backtrace) {
- Ok(stream) => stream,
- Err(e) => {
- let mut err = ecx.struct_span_err(span, "proc-macro derive panicked");
- if let Some(s) = e.as_str() {
- err.help(&format!("message: {}", s));
- }
- err.emit();
- return ExpandResult::Ready(vec![]);
+ let stream = match self.client.run(&EXEC_STRATEGY, server, input, proc_macro_backtrace) {
+ Ok(stream) => stream,
+ Err(e) => {
+ let mut err = ecx.struct_span_err(span, "proc-macro derive panicked");
+ if let Some(s) = e.as_str() {
+ err.help(&format!("message: {}", s));
}
- };
+ err.emit();
+ return ExpandResult::Ready(vec![]);
+ }
+ };
let error_count_before = ecx.sess.parse_sess.span_diagnostic.err_count();
let mut parser =
-use crate::base::{ExtCtxt, ResolverExpand};
+use crate::base::ExtCtxt;
use rustc_ast as ast;
use rustc_ast::token::{self, Nonterminal, NtIdent};
use rustc_ast_pretty::pprust;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::sync::Lrc;
-use rustc_errors::Diagnostic;
+use rustc_errors::{Diagnostic, PResult};
use rustc_lint_defs::builtin::PROC_MACRO_BACK_COMPAT;
use rustc_lint_defs::BuiltinLintDiagnostics;
use rustc_parse::lexer::nfc_normalize;
}
}
-impl FromInternal<(TreeAndSpacing, &'_ mut Vec<Self>, &mut Rustc<'_>)>
+impl FromInternal<(TreeAndSpacing, &'_ mut Vec<Self>, &mut Rustc<'_, '_>)>
for TokenTree<Group, Punct, Ident, Literal>
{
fn from_internal(
- ((tree, spacing), stack, rustc): (TreeAndSpacing, &mut Vec<Self>, &mut Rustc<'_>),
+ ((tree, spacing), stack, rustc): (TreeAndSpacing, &mut Vec<Self>, &mut Rustc<'_, '_>),
) -> Self {
use rustc_ast::token::*;
SingleQuote => op!('\''),
Ident(name, false) if name == kw::DollarCrate => tt!(Ident::dollar_crate()),
- Ident(name, is_raw) => tt!(Ident::new(rustc.sess, name, is_raw)),
+ Ident(name, is_raw) => tt!(Ident::new(rustc.sess(), name, is_raw)),
Lifetime(name) => {
let ident = symbol::Ident::new(name, span).without_first_quote();
- stack.push(tt!(Ident::new(rustc.sess, ident.name, false)));
+ stack.push(tt!(Ident::new(rustc.sess(), ident.name, false)));
tt!(Punct::new('\'', true))
}
Literal(lit) => tt!(Literal { lit }),
Interpolated(nt)
if let Some((name, is_raw)) = ident_name_compatibility_hack(&nt, span, rustc) =>
{
- TokenTree::Ident(Ident::new(rustc.sess, name.name, is_raw, name.span))
+ TokenTree::Ident(Ident::new(rustc.sess(), name.name, is_raw, name.span))
}
Interpolated(nt) => {
- let stream = nt_to_tokenstream(&nt, rustc.sess, CanSynthesizeMissingTokens::No);
+ let stream = nt_to_tokenstream(&nt, rustc.sess(), CanSynthesizeMissingTokens::No);
TokenTree::Group(Group {
delimiter: Delimiter::None,
stream,
span: DelimSpan::from_single(span),
- flatten: crate::base::pretty_printing_compatibility_hack(&nt, rustc.sess),
+ flatten: crate::base::pretty_printing_compatibility_hack(&nt, rustc.sess()),
})
}
span: Span,
}
-pub(crate) struct Rustc<'a> {
- resolver: &'a dyn ResolverExpand,
- sess: &'a ParseSess,
+pub(crate) struct Rustc<'a, 'b> {
+ ecx: &'a mut ExtCtxt<'b>,
def_site: Span,
call_site: Span,
mixed_site: Span,
- span_debug: bool,
krate: CrateNum,
rebased_spans: FxHashMap<usize, Span>,
}
-impl<'a> Rustc<'a> {
- pub fn new(cx: &'a ExtCtxt<'_>) -> Self {
- let expn_data = cx.current_expansion.id.expn_data();
+impl<'a, 'b> Rustc<'a, 'b> {
+ pub fn new(ecx: &'a mut ExtCtxt<'b>) -> Self {
+ let expn_data = ecx.current_expansion.id.expn_data();
Rustc {
- resolver: cx.resolver,
- sess: cx.parse_sess(),
- def_site: cx.with_def_site_ctxt(expn_data.def_site),
- call_site: cx.with_call_site_ctxt(expn_data.call_site),
- mixed_site: cx.with_mixed_site_ctxt(expn_data.call_site),
- span_debug: cx.ecfg.span_debug,
+ def_site: ecx.with_def_site_ctxt(expn_data.def_site),
+ call_site: ecx.with_call_site_ctxt(expn_data.call_site),
+ mixed_site: ecx.with_mixed_site_ctxt(expn_data.call_site),
krate: expn_data.macro_def_id.unwrap().krate,
rebased_spans: FxHashMap::default(),
+ ecx,
}
}
+ fn sess(&self) -> &ParseSess {
+ self.ecx.parse_sess()
+ }
+
fn lit(&mut self, kind: token::LitKind, symbol: Symbol, suffix: Option<Symbol>) -> Literal {
Literal { lit: token::Lit::new(kind, symbol, suffix), span: server::Span::call_site(self) }
}
}
-impl server::Types for Rustc<'_> {
+impl server::Types for Rustc<'_, '_> {
type FreeFunctions = FreeFunctions;
type TokenStream = TokenStream;
type TokenStreamBuilder = tokenstream::TokenStreamBuilder;
type Span = Span;
}
-impl server::FreeFunctions for Rustc<'_> {
+impl server::FreeFunctions for Rustc<'_, '_> {
fn track_env_var(&mut self, var: &str, value: Option<&str>) {
- self.sess.env_depinfo.borrow_mut().insert((Symbol::intern(var), value.map(Symbol::intern)));
+ self.sess()
+ .env_depinfo
+ .borrow_mut()
+ .insert((Symbol::intern(var), value.map(Symbol::intern)));
}
fn track_path(&mut self, path: &str) {
- self.sess.file_depinfo.borrow_mut().insert(Symbol::intern(path));
+ self.sess().file_depinfo.borrow_mut().insert(Symbol::intern(path));
}
}
-impl server::TokenStream for Rustc<'_> {
+impl server::TokenStream for Rustc<'_, '_> {
fn new(&mut self) -> Self::TokenStream {
TokenStream::default()
}
parse_stream_from_source_str(
FileName::proc_macro_source_code(src),
src.to_string(),
- self.sess,
+ self.sess(),
Some(self.call_site),
)
}
fn to_string(&mut self, stream: &Self::TokenStream) -> String {
pprust::tts_to_string(stream)
}
+ fn expand_expr(&mut self, stream: &Self::TokenStream) -> Result<Self::TokenStream, ()> {
+ // Parse the expression from our tokenstream.
+ let expr: PResult<'_, _> = try {
+ let mut p = rustc_parse::stream_to_parser(
+ self.sess(),
+ stream.clone(),
+ Some("proc_macro expand expr"),
+ );
+ let expr = p.parse_expr()?;
+ if p.token != token::Eof {
+ p.unexpected()?;
+ }
+ expr
+ };
+ let expr = expr.map_err(|mut err| err.emit())?;
+
+ // Perform eager expansion on the expression.
+ let expr = self
+ .ecx
+ .expander()
+ .fully_expand_fragment(crate::expand::AstFragment::Expr(expr))
+ .make_expr();
+
+ // NOTE: For now, limit `expand_expr` to exclusively expand to literals.
+ // This may be relaxed in the future.
+ // We don't use `nt_to_tokenstream` as the tokenstream currently cannot
+ // be recovered in the general case.
+ match &expr.kind {
+ ast::ExprKind::Lit(l) => {
+ Ok(tokenstream::TokenTree::token(token::Literal(l.token), l.span).into())
+ }
+ ast::ExprKind::Unary(ast::UnOp::Neg, e) => match &e.kind {
+ ast::ExprKind::Lit(l) => match l.token {
+ token::Lit { kind: token::Integer | token::Float, .. } => {
+ Ok(std::array::IntoIter::new([
+ // FIXME: The span of the `-` token is lost when
+ // parsing, so we cannot faithfully recover it here.
+ tokenstream::TokenTree::token(token::BinOp(token::Minus), e.span),
+ tokenstream::TokenTree::token(token::Literal(l.token), l.span),
+ ])
+ .collect())
+ }
+ _ => Err(()),
+ },
+ _ => Err(()),
+ },
+ _ => Err(()),
+ }
+ }
fn from_token_tree(
&mut self,
tree: TokenTree<Self::Group, Self::Punct, Self::Ident, Self::Literal>,
}
}
-impl server::TokenStreamBuilder for Rustc<'_> {
+impl server::TokenStreamBuilder for Rustc<'_, '_> {
fn new(&mut self) -> Self::TokenStreamBuilder {
tokenstream::TokenStreamBuilder::new()
}
}
}
-impl server::TokenStreamIter for Rustc<'_> {
+impl server::TokenStreamIter for Rustc<'_, '_> {
fn next(
&mut self,
iter: &mut Self::TokenStreamIter,
}
}
-impl server::Group for Rustc<'_> {
+impl server::Group for Rustc<'_, '_> {
fn new(&mut self, delimiter: Delimiter, stream: Self::TokenStream) -> Self::Group {
Group {
delimiter,
}
}
-impl server::Punct for Rustc<'_> {
+impl server::Punct for Rustc<'_, '_> {
fn new(&mut self, ch: char, spacing: Spacing) -> Self::Punct {
Punct::new(ch, spacing == Spacing::Joint, server::Span::call_site(self))
}
}
}
-impl server::Ident for Rustc<'_> {
+impl server::Ident for Rustc<'_, '_> {
fn new(&mut self, string: &str, span: Self::Span, is_raw: bool) -> Self::Ident {
- Ident::new(self.sess, Symbol::intern(string), is_raw, span)
+ Ident::new(self.sess(), Symbol::intern(string), is_raw, span)
}
fn span(&mut self, ident: Self::Ident) -> Self::Span {
ident.span
}
}
-impl server::Literal for Rustc<'_> {
+impl server::Literal for Rustc<'_, '_> {
fn from_str(&mut self, s: &str) -> Result<Self::Literal, ()> {
let name = FileName::proc_macro_source_code(s);
- let mut parser = rustc_parse::new_parser_from_source_str(self.sess, name, s.to_owned());
+ let mut parser = rustc_parse::new_parser_from_source_str(self.sess(), name, s.to_owned());
let first_span = parser.token.span.data();
let minus_present = parser.eat(&token::BinOp(token::Minus));
}
}
-impl server::SourceFile for Rustc<'_> {
+impl server::SourceFile for Rustc<'_, '_> {
fn eq(&mut self, file1: &Self::SourceFile, file2: &Self::SourceFile) -> bool {
Lrc::ptr_eq(file1, file2)
}
}
}
-impl server::MultiSpan for Rustc<'_> {
+impl server::MultiSpan for Rustc<'_, '_> {
fn new(&mut self) -> Self::MultiSpan {
vec![]
}
}
}
-impl server::Diagnostic for Rustc<'_> {
+impl server::Diagnostic for Rustc<'_, '_> {
fn new(&mut self, level: Level, msg: &str, spans: Self::MultiSpan) -> Self::Diagnostic {
let mut diag = Diagnostic::new(level.to_internal(), msg);
diag.set_span(MultiSpan::from_spans(spans));
diag.sub(level.to_internal(), msg, MultiSpan::from_spans(spans), None);
}
fn emit(&mut self, diag: Self::Diagnostic) {
- self.sess.span_diagnostic.emit_diagnostic(&diag);
+ self.sess().span_diagnostic.emit_diagnostic(&diag);
}
}
-impl server::Span for Rustc<'_> {
+impl server::Span for Rustc<'_, '_> {
fn debug(&mut self, span: Self::Span) -> String {
- if self.span_debug {
+ if self.ecx.ecfg.span_debug {
format!("{:?}", span)
} else {
format!("{:?} bytes({}..{})", span.ctxt(), span.lo().0, span.hi().0)
self.mixed_site
}
fn source_file(&mut self, span: Self::Span) -> Self::SourceFile {
- self.sess.source_map().lookup_char_pos(span.lo()).file
+ self.sess().source_map().lookup_char_pos(span.lo()).file
}
fn parent(&mut self, span: Self::Span) -> Option<Self::Span> {
span.parent_callsite()
span.source_callsite()
}
fn start(&mut self, span: Self::Span) -> LineColumn {
- let loc = self.sess.source_map().lookup_char_pos(span.lo());
+ let loc = self.sess().source_map().lookup_char_pos(span.lo());
LineColumn { line: loc.line, column: loc.col.to_usize() }
}
fn end(&mut self, span: Self::Span) -> LineColumn {
- let loc = self.sess.source_map().lookup_char_pos(span.hi());
+ let loc = self.sess().source_map().lookup_char_pos(span.hi());
LineColumn { line: loc.line, column: loc.col.to_usize() }
}
fn before(&mut self, span: Self::Span) -> Self::Span {
span.shrink_to_hi()
}
fn join(&mut self, first: Self::Span, second: Self::Span) -> Option<Self::Span> {
- let self_loc = self.sess.source_map().lookup_char_pos(first.lo());
- let other_loc = self.sess.source_map().lookup_char_pos(second.lo());
+ let self_loc = self.sess().source_map().lookup_char_pos(first.lo());
+ let other_loc = self.sess().source_map().lookup_char_pos(second.lo());
if self_loc.file.name != other_loc.file.name {
return None;
span.with_ctxt(at.ctxt())
}
fn source_text(&mut self, span: Self::Span) -> Option<String> {
- self.sess.source_map().span_to_snippet(span).ok()
+ self.sess().source_map().span_to_snippet(span).ok()
}
/// Saves the provided span into the metadata of
/// *the crate we are currently compiling*, which must
/// since we've loaded `my_proc_macro` from disk in order to execute it).
/// In this way, we have obtained a span pointing into `my_proc_macro`
fn save_span(&mut self, span: Self::Span) -> usize {
- self.sess.save_proc_macro_span(span)
+ self.sess().save_proc_macro_span(span)
}
fn recover_proc_macro_span(&mut self, id: usize) -> Self::Span {
- let (resolver, krate, def_site) = (self.resolver, self.krate, self.def_site);
+ let (resolver, krate, def_site) = (&*self.ecx.resolver, self.krate, self.def_site);
*self.rebased_spans.entry(id).or_insert_with(|| {
// FIXME: `SyntaxContext` for spans from proc macro crates is lost during encoding,
// replace it with a def-site context until we are encoding it properly.
fn ident_name_compatibility_hack(
nt: &Nonterminal,
orig_span: Span,
- rustc: &mut Rustc<'_>,
+ rustc: &mut Rustc<'_, '_>,
) -> Option<(rustc_span::symbol::Ident, bool)> {
if let NtIdent(ident, is_raw) = nt {
if let ExpnKind::Macro(_, macro_name) = orig_span.ctxt().outer_expn_data().kind {
- let source_map = rustc.sess.source_map();
+ let source_map = rustc.sess().source_map();
let filename = source_map.span_to_filename(orig_span);
if let FileName::Real(RealFileName::LocalPath(path)) = filename {
let matches_prefix = |prefix, filename| {
let snippet = source_map.span_to_snippet(orig_span);
if snippet.as_deref() == Ok("$name") {
if time_macros_impl {
- rustc.sess.buffer_lint_with_diagnostic(
+ rustc.sess().buffer_lint_with_diagnostic(
&PROC_MACRO_BACK_COMPAT,
orig_span,
ast::CRATE_NODE_ID,
.and_then(|c| c.parse::<u32>().ok())
.map_or(false, |v| v < 40)
{
- rustc.sess.buffer_lint_with_diagnostic(
+ rustc.sess().buffer_lint_with_diagnostic(
&PROC_MACRO_BACK_COMPAT,
orig_span,
ast::CRATE_NODE_ID,
source_map.span_to_filename(rustc.def_site)
{
if macro_path.to_string_lossy().contains("pin-project-internal-0.") {
- rustc.sess.buffer_lint_with_diagnostic(
+ rustc.sess().buffer_lint_with_diagnostic(
&PROC_MACRO_BACK_COMPAT,
orig_span,
ast::CRATE_NODE_ID,
(accepted, const_panic, "1.57.0", Some(51999), None),
/// Lessens the requirements for structs to implement `Unsize`.
(accepted, relaxed_struct_unsize, "1.58.0", Some(81793), None),
+ /// Allows dereferencing raw pointers during const eval.
+ (accepted, const_raw_ptr_deref, "1.58.0", Some(51911), None),
+ /// Allows capturing variables in scope using format_args!
+ (accepted, format_args_capture, "1.58.0", Some(67984), None),
// -------------------------------------------------------------------------
// feature-group-end: accepted features
/// Allows inferring `'static` outlives requirements (RFC 2093).
(active, infer_static_outlives_requirements, "1.26.0", Some(54185), None),
- /// Allows dereferencing raw pointers during const eval.
- (active, const_raw_ptr_deref, "1.27.0", Some(51911), None),
-
/// Allows inconsistent bounds in where clauses.
(active, trivial_bounds, "1.28.0", Some(48214), None),
/// Be more precise when looking for live drops in a const context.
(active, const_precise_live_drops, "1.46.0", Some(73255), None),
- /// Allows capturing variables in scope using format_args!
- (active, format_args_capture, "1.46.0", Some(67984), None),
-
/// Allows `if let` guard in match arms.
(active, if_let_guard, "1.47.0", Some(51114), None),
macro_rules! ungated {
($attr:ident, $typ:expr, $tpl:expr $(,)?) => {
- (sym::$attr, $typ, $tpl, Ungated)
+ BuiltinAttribute { name: sym::$attr, type_: $typ, template: $tpl, gate: Ungated }
};
}
macro_rules! gated {
($attr:ident, $typ:expr, $tpl:expr, $gate:ident, $msg:expr $(,)?) => {
- (sym::$attr, $typ, $tpl, Gated(Stability::Unstable, sym::$gate, $msg, cfg_fn!($gate)))
+ BuiltinAttribute {
+ name: sym::$attr,
+ type_: $typ,
+ template: $tpl,
+ gate: Gated(Stability::Unstable, sym::$gate, $msg, cfg_fn!($gate)),
+ }
};
($attr:ident, $typ:expr, $tpl:expr, $msg:expr $(,)?) => {
- (sym::$attr, $typ, $tpl, Gated(Stability::Unstable, sym::$attr, $msg, cfg_fn!($attr)))
+ BuiltinAttribute {
+ name: sym::$attr,
+ type_: $typ,
+ template: $tpl,
+ gate: Gated(Stability::Unstable, sym::$attr, $msg, cfg_fn!($attr)),
+ }
};
}
)
};
($attr:ident, $typ:expr, $tpl:expr, $msg:expr $(,)?) => {
- (
- sym::$attr,
- $typ,
- $tpl,
- Gated(Stability::Unstable, sym::rustc_attrs, $msg, cfg_fn!(rustc_attrs)),
- )
+ BuiltinAttribute {
+ name: sym::$attr,
+ type_: $typ,
+ template: $tpl,
+ gate: Gated(Stability::Unstable, sym::rustc_attrs, $msg, cfg_fn!(rustc_attrs)),
+ }
};
}
const IMPL_DETAIL: &str = "internal implementation detail";
const INTERNAL_UNSTABLE: &str = "this is an internal attribute that will never be stable";
-pub type BuiltinAttribute = (Symbol, AttributeType, AttributeTemplate, AttributeGate);
+pub struct BuiltinAttribute {
+ pub name: Symbol,
+ pub type_: AttributeType,
+ pub template: AttributeTemplate,
+ pub gate: AttributeGate,
+}
/// Attributes that have a special meaning to rustc or rustdoc.
#[rustfmt::skip]
),
// Plugins:
- (
- sym::plugin, CrateLevel, template!(List: "name"),
- Gated(
+ BuiltinAttribute {
+ name: sym::plugin,
+ type_: CrateLevel,
+ template: template!(List: "name"),
+ gate: Gated(
Stability::Deprecated(
"https://github.com/rust-lang/rust/pull/64675",
Some("may be removed in a future compiler version"),
sym::plugin,
"compiler plugins are deprecated",
cfg_fn!(plugin)
- )
- ),
+ ),
+ },
// Testing:
gated!(allow_fail, Normal, template!(Word), experimental!(allow_fail)),
lang, Normal, template!(NameValueStr: "name"), lang_items,
"language items are subject to change",
),
- (
- sym::rustc_diagnostic_item,
- Normal,
- template!(NameValueStr: "name"),
- Gated(
+ BuiltinAttribute {
+ name: sym::rustc_diagnostic_item,
+ type_: Normal,
+ template: template!(NameValueStr: "name"),
+ gate: Gated(
Stability::Unstable,
sym::rustc_attrs,
"diagnostic items compiler internal support for linting",
cfg_fn!(rustc_attrs),
),
- ),
+ },
gated!(
// Used in resolve:
prelude_import, Normal, template!(Word),
];
pub fn deprecated_attributes() -> Vec<&'static BuiltinAttribute> {
- BUILTIN_ATTRIBUTES.iter().filter(|(.., gate)| gate.is_deprecated()).collect()
+ BUILTIN_ATTRIBUTES.iter().filter(|attr| attr.gate.is_deprecated()).collect()
}
pub fn is_builtin_attr_name(name: Symbol) -> bool {
SyncLazy::new(|| {
let mut map = FxHashMap::default();
for attr in BUILTIN_ATTRIBUTES.iter() {
- if map.insert(attr.0, attr).is_some() {
- panic!("duplicate builtin attribute `{}`", attr.0);
+ if map.insert(attr.name, attr).is_some() {
+ panic!("duplicate builtin attribute `{}`", attr.name);
}
}
map
Use,
/// An `extern` block.
ForeignMod,
- /// Anonymous constant, e.g. the `1 + 2` in `[u8; 1 + 2]`, or `const { 1 + 2}`
+ /// Anonymous constant, e.g. the `1 + 2` in `[u8; 1 + 2]`
AnonConst,
+ /// An inline constant, e.g. `const { 1 + 2 }`
+ InlineConst,
/// Opaque type, aka `impl Trait`.
OpaqueTy,
Field,
DefKind::Use => "import",
DefKind::ForeignMod => "foreign module",
DefKind::AnonConst => "constant expression",
+ DefKind::InlineConst => "inline constant",
DefKind::Field => "field",
DefKind::Impl => "implementation",
DefKind::Closure => "closure",
| DefKind::OpaqueTy
| DefKind::Impl
| DefKind::Use
+ | DefKind::InlineConst
| DefKind::ExternCrate => "an",
DefKind::Macro(macro_kind) => macro_kind.article(),
_ => "a",
// Not namespaced.
DefKind::AnonConst
+ | DefKind::InlineConst
| DefKind::Field
| DefKind::LifetimeParam
| DefKind::ExternCrate
pub fn staging_dep_graph_path(sess: &Session) -> PathBuf {
in_incr_comp_dir_sess(sess, STAGING_DEP_GRAPH_FILENAME)
}
-pub fn dep_graph_path_from(incr_comp_session_dir: &Path) -> PathBuf {
- in_incr_comp_dir(incr_comp_session_dir, DEP_GRAPH_FILENAME)
-}
pub fn work_products_path(sess: &Session) -> PathBuf {
in_incr_comp_dir_sess(sess, WORK_PRODUCTS_FILENAME)
// Calling `sess.incr_comp_session_dir()` will panic if `sess.opts.incremental.is_none()`.
// Fortunately, we just checked that this isn't the case.
- let path = dep_graph_path_from(&sess.incr_comp_session_dir());
+ let path = dep_graph_path(&sess);
let report_incremental_info = sess.opts.debugging_opts.incremental_info;
let expected_hash = sess.opts.dep_tracking_hash(false);
}
impl $type {
+ /// Maximum value the index can take, as a `u32`.
$v const MAX_AS_U32: u32 = $max;
+ /// Maximum value the index can take.
$v const MAX: Self = Self::from_u32($max);
+ /// Creates a new index from a given `usize`.
+ ///
+ /// # Panics
+ ///
+ /// Will panic if `value` exceeds `MAX`.
#[inline]
$v const fn from_usize(value: usize) -> Self {
assert!(value <= ($max as usize));
+ // SAFETY: We just checked that `value <= max`.
unsafe {
Self::from_u32_unchecked(value as u32)
}
}
+ /// Creates a new index from a given `u32`.
+ ///
+ /// # Panics
+ ///
+ /// Will panic if `value` exceeds `MAX`.
#[inline]
$v const fn from_u32(value: u32) -> Self {
assert!(value <= $max);
+ // SAFETY: We just checked that `value <= max`.
unsafe {
Self::from_u32_unchecked(value)
}
}
+ /// Creates a new index from a given `u32`.
+ ///
+ /// # Safety
+ ///
+ /// The provided value must be less than or equal to the maximum value for the newtype.
+ /// Providing a value outside this range is undefined due to layout restrictions.
+ ///
+ /// Prefer using `from_u32`.
#[inline]
$v const unsafe fn from_u32_unchecked(value: u32) -> Self {
Self { private: value }
}
- /// Extracts the value of this index as an integer.
+ /// Extracts the value of this index as a `usize`.
#[inline]
$v const fn index(self) -> usize {
self.as_usize()
if let Some(SubregionOrigin::CompareImplMethodObligation {
span,
- item_name,
impl_item_def_id,
trait_item_def_id,
}) = origin
{
return self.report_extra_impl_obligation(
span,
- item_name,
impl_item_def_id,
trait_item_def_id,
&format!("`{}: {}`", bound_kind, sub),
{
if let SubregionOrigin::CompareImplTypeObligation {
span,
- item_name,
impl_item_def_id,
trait_item_def_id,
} = origin
{
- self.emit_associated_type_err(span, item_name, impl_item_def_id, trait_item_def_id);
+ self.emit_associated_type_err(
+ span,
+ self.infcx.tcx.item_name(impl_item_def_id),
+ impl_item_def_id,
+ trait_item_def_id,
+ );
return Some(ErrorReported);
}
}
);
err
}
- infer::CompareImplMethodObligation {
- span,
- item_name,
- impl_item_def_id,
- trait_item_def_id,
- } => self.report_extra_impl_obligation(
- span,
- item_name,
- impl_item_def_id,
- trait_item_def_id,
- &format!("`{}: {}`", sup, sub),
- ),
- infer::CompareImplTypeObligation {
- span,
- item_name,
- impl_item_def_id,
- trait_item_def_id,
- } => self.report_extra_impl_obligation(
- span,
- item_name,
- impl_item_def_id,
- trait_item_def_id,
- &format!("`{}: {}`", sup, sub),
- ),
+ infer::CompareImplMethodObligation { span, impl_item_def_id, trait_item_def_id } => {
+ self.report_extra_impl_obligation(
+ span,
+ impl_item_def_id,
+ trait_item_def_id,
+ &format!("`{}: {}`", sup, sub),
+ )
+ }
+ infer::CompareImplTypeObligation { span, impl_item_def_id, trait_item_def_id } => self
+ .report_extra_impl_obligation(
+ span,
+ impl_item_def_id,
+ trait_item_def_id,
+ &format!("`{}: {}`", sup, sub),
+ ),
}
}
/// Comparing the signature and requirements of an impl method against
/// the containing trait.
- CompareImplMethodObligation {
- span: Span,
- item_name: Symbol,
- impl_item_def_id: DefId,
- trait_item_def_id: DefId,
- },
+ CompareImplMethodObligation { span: Span, impl_item_def_id: DefId, trait_item_def_id: DefId },
/// Comparing the signature and requirements of an impl associated type
/// against the containing trait
- CompareImplTypeObligation {
- span: Span,
- item_name: Symbol,
- impl_item_def_id: DefId,
- trait_item_def_id: DefId,
- },
+ CompareImplTypeObligation { span: Span, impl_item_def_id: DefId, trait_item_def_id: DefId },
}
// `SubregionOrigin` is used a lot. Make sure it doesn't unintentionally get bigger.
}
traits::ObligationCauseCode::CompareImplMethodObligation {
- item_name,
impl_item_def_id,
trait_item_def_id,
} => SubregionOrigin::CompareImplMethodObligation {
span: cause.span,
- item_name,
impl_item_def_id,
trait_item_def_id,
},
traits::ObligationCauseCode::CompareImplTypeObligation {
- item_name,
impl_item_def_id,
trait_item_def_id,
} => SubregionOrigin::CompareImplTypeObligation {
span: cause.span,
- item_name,
impl_item_def_id,
trait_item_def_id,
},
/// function. We can then add implied bounds and the like from the
/// closure arguments into the environment -- these should only
/// apply in the closure body, so once we exit, we invoke
- /// `pop_snapshot_post_closure` to remove them.
+ /// `pop_snapshot_post_typeck_child` to remove them.
///
/// Example:
///
/// seems like it'd be readily fixed if we wanted. There are
/// similar leaks around givens that seem equally suspicious, to
/// be honest. --nmatsakis
- pub fn push_snapshot_pre_closure(&self) -> usize {
+ pub fn push_snapshot_pre_typeck_child(&self) -> usize {
self.region_bound_pairs_accum.len()
}
- /// See `push_snapshot_pre_closure`.
- pub fn pop_snapshot_post_closure(&mut self, len: usize) {
+ /// See `push_snapshot_pre_typeck_child`.
+ pub fn pop_snapshot_post_typeck_child(&mut self, len: usize) {
self.region_bound_pairs_accum.truncate(len);
}
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_middle::ty::TyCtxt;
-use rustc_span::symbol::Symbol;
use rustc_span::{MultiSpan, Span};
use std::fmt;
use std::iter;
pub fn report_extra_impl_obligation(
&self,
error_span: Span,
- item_name: Symbol,
- _impl_item_def_id: DefId,
+ impl_item_def_id: DefId,
trait_item_def_id: DefId,
requirement: &dyn fmt::Display,
) -> DiagnosticBuilder<'tcx> {
if let Some(trait_item_span) = self.tcx.hir().span_if_local(trait_item_def_id) {
let span = self.tcx.sess.source_map().guess_head_span(trait_item_span);
+ let item_name = self.tcx.item_name(impl_item_def_id);
err.span_label(span, format!("definition of `{}` from trait", item_name));
}
pub(crate) input_path: Option<PathBuf>,
pub(crate) output_dir: Option<PathBuf>,
pub(crate) output_file: Option<PathBuf>,
+ pub(crate) temps_dir: Option<PathBuf>,
pub(crate) register_lints: Option<Box<dyn Fn(&Session, &mut LintStore) + Send + Sync>>,
pub(crate) override_queries:
Option<fn(&Session, &mut ty::query::Providers, &mut ty::query::ExternProviders)>,
pub fn output_file(&self) -> &Option<PathBuf> {
&self.output_file
}
+ pub fn temps_dir(&self) -> &Option<PathBuf> {
+ &self.temps_dir
+ }
pub fn register_lints(&self) -> &Option<Box<dyn Fn(&Session, &mut LintStore) + Send + Sync>> {
&self.register_lints
}
sess: &Session,
attrs: &[ast::Attribute],
) -> OutputFilenames {
- util::build_output_filenames(&self.input, &self.output_dir, &self.output_file, attrs, sess)
+ util::build_output_filenames(
+ &self.input,
+ &self.output_dir,
+ &self.output_file,
+ &self.temps_dir,
+ attrs,
+ sess,
+ )
}
}
);
}
+ let temps_dir = sess.opts.debugging_opts.temps_dir.as_ref().map(|o| PathBuf::from(&o));
+
let compiler = Compiler {
sess,
codegen_backend,
input_path: config.input_path,
output_dir: config.output_dir,
output_file: config.output_file,
+ temps_dir,
register_lints: config.register_lints,
override_queries: config.override_queries,
};
&compiler.input,
&compiler.output_dir,
&compiler.output_file,
+ &compiler.temps_dir,
&krate.attrs,
sess,
);
}
}
+ if let Some(ref dir) = compiler.temps_dir {
+ if fs::create_dir_all(dir).is_err() {
+ sess.err("failed to find or create the directory specified by `--temps-dir`");
+ return Err(ErrorReported);
+ }
+ }
+
write_out_deps(sess, boxed_resolver, &outputs, &output_paths);
let only_dep_info = sess.opts.output_types.contains_key(&OutputType::DepInfo)
untracked!(span_debug, true);
untracked!(span_free_formats, true);
untracked!(strip, Strip::Debuginfo);
+ untracked!(temps_dir, Some(String::from("abc")));
untracked!(terminal_width, Some(80));
untracked!(threads, 99);
untracked!(time, true);
input: &Input,
odir: &Option<PathBuf>,
ofile: &Option<PathBuf>,
+ temps_dir: &Option<PathBuf>,
attrs: &[ast::Attribute],
sess: &Session,
) -> OutputFilenames {
dirpath,
stem,
None,
+ temps_dir.clone(),
sess.opts.cg.extra_filename.clone(),
sess.opts.output_types.clone(),
)
out_file.parent().unwrap_or_else(|| Path::new("")).to_path_buf(),
out_file.file_stem().unwrap_or_default().to_str().unwrap().to_string(),
ofile,
+ temps_dir.clone(),
sess.opts.cg.extra_filename.clone(),
sess.opts.output_types.clone(),
)
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticStyledString};
-use rustc_feature::{deprecated_attributes, AttributeGate, AttributeTemplate, AttributeType};
-use rustc_feature::{GateIssue, Stability};
+use rustc_feature::{deprecated_attributes, AttributeGate, BuiltinAttribute, GateIssue, Stability};
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{DefId, LocalDefId, LocalDefIdSet, CRATE_DEF_ID};
pub struct DeprecatedAttr {
// This is not free to compute, so we want to keep it around, rather than
// compute it for every attribute.
- depr_attrs: Vec<&'static (Symbol, AttributeType, AttributeTemplate, AttributeGate)>,
+ depr_attrs: Vec<&'static BuiltinAttribute>,
}
impl_lint_pass!(DeprecatedAttr => []);
impl EarlyLintPass for DeprecatedAttr {
fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &ast::Attribute) {
- for &&(n, _, _, ref g) in &self.depr_attrs {
- if attr.ident().map(|ident| ident.name) == Some(n) {
+ for BuiltinAttribute { name, gate, .. } in &self.depr_attrs {
+ if attr.ident().map(|ident| ident.name) == Some(*name) {
if let &AttributeGate::Gated(
Stability::Deprecated(link, suggestion),
name,
reason,
_,
- ) = g
+ ) = gate
{
let msg =
format!("use of deprecated attribute `{}`: {}. See {}", name, reason, link);
#![feature(bool_to_option)]
#![feature(box_patterns)]
#![feature(crate_visibility_modifier)]
-#![feature(format_args_capture)]
+#![cfg_attr(bootstrap, feature(format_args_capture))]
#![feature(iter_order_by)]
#![feature(iter_zip)]
#![feature(never_type)]
OptimizerLastEPCallbacks.push_back(
[SanitizerOptions](ModulePassManager &MPM, OptimizationLevel Level) {
MPM.addPass(RequireAnalysisPass<ASanGlobalsMetadataAnalysis, Module>());
- MPM.addPass(ModuleAddressSanitizerPass(
- /*CompileKernel=*/false, SanitizerOptions->SanitizeAddressRecover));
#if LLVM_VERSION_GE(14, 0)
- AddressSanitizerOptions opts(/*CompileKernel=*/false,
- SanitizerOptions->SanitizeAddressRecover,
- /*UseAfterScope=*/true,
- AsanDetectStackUseAfterReturnMode::Runtime);
- MPM.addPass(createModuleToFunctionPassAdaptor(AddressSanitizerPass(opts)));
+ AddressSanitizerOptions opts = AddressSanitizerOptions{
+ /*CompileKernel=*/false,
+ SanitizerOptions->SanitizeAddressRecover,
+ /*UseAfterScope=*/true,
+ AsanDetectStackUseAfterReturnMode::Runtime,
+ };
+ MPM.addPass(ModuleAddressSanitizerPass(opts));
#else
+ MPM.addPass(ModuleAddressSanitizerPass(
+ /*CompileKernel=*/false, SanitizerOptions->SanitizeAddressRecover));
MPM.addPass(createModuleToFunctionPassAdaptor(AddressSanitizerPass(
/*CompileKernel=*/false, SanitizerOptions->SanitizeAddressRecover,
/*UseAfterScope=*/true)));
let ctor_res =
Res::Def(DefKind::Ctor(CtorOf::Variant, ctor_kind), ctor_def_id);
let mut vis = self.get_visibility(ctor_def_id.index);
- if ctor_def_id == def_id && vis == ty::Visibility::Public {
+ if ctor_def_id == def_id && vis.is_public() {
// For non-exhaustive variants lower the constructor visibility to
// within the crate. We only need this for fictive constructors,
// for other constructors correct visibilities
}
let mut add_child = |bfs_queue: &mut VecDeque<_>, child: &Export, parent: DefId| {
- if child.vis != ty::Visibility::Public {
+ if !child.vis.is_public() {
return;
}
| DefKind::ConstParam
| DefKind::LifetimeParam
| DefKind::AnonConst
+ | DefKind::InlineConst
| DefKind::GlobalAsm
| DefKind::Closure
| DefKind::Generator
DefKind::Use
| DefKind::LifetimeParam
| DefKind::AnonConst
+ | DefKind::InlineConst
| DefKind::GlobalAsm
| DefKind::Closure
| DefKind::Generator
(true, mir_opt_base)
}
// Constants
- DefKind::AnonConst | DefKind::AssocConst | DefKind::Static | DefKind::Const => {
- (true, false)
- }
+ DefKind::AnonConst
+ | DefKind::InlineConst
+ | DefKind::AssocConst
+ | DefKind::Static
+ | DefKind::Const => (true, false),
// Full-fledged functions
DefKind::AssocFn | DefKind::Fn => {
let generics = tcx.generics_of(def_id);
| DefKind::Use
| DefKind::LifetimeParam
| DefKind::AnonConst
+ | DefKind::InlineConst
| DefKind::GlobalAsm
| DefKind::Closure
| DefKind::Generator
| DefKind::AssocFn
| DefKind::AssocConst
| DefKind::AnonConst
+ | DefKind::InlineConst
| DefKind::OpaqueTy
| DefKind::Impl
| DefKind::Field
result[header + 2] = (pos >> 8) as u8;
result[header + 3] = (pos >> 0) as u8;
+ // Record metadata size for self-profiling
+ tcx.prof.artifact_size("crate_metadata", "crate_metadata", result.len() as u64);
+
EncodedMetadata { raw_data: result }
}
};
DefKind::Ctor(ctor_of, def::CtorKind::from_hir(variant_data))
}
- Node::AnonConst(_) => DefKind::AnonConst,
+ Node::AnonConst(_) => {
+ let inline = match self.find(self.get_parent_node(hir_id)) {
+ Some(Node::Expr(&Expr {
+ kind: ExprKind::ConstBlock(ref anon_const), ..
+ })) if anon_const.hir_id == hir_id => true,
+ _ => false,
+ };
+ if inline { DefKind::InlineConst } else { DefKind::AnonConst }
+ }
Node::Field(_) => DefKind::Field,
Node::Expr(expr) => match expr.kind {
ExprKind::Closure(.., None) => DefKind::Closure,
write!(w, "static {}", if tcx.is_mutable_static(def_id) { "mut " } else { "" })?
}
(_, _) if is_function => write!(w, "fn ")?,
- (DefKind::AnonConst, _) => {} // things like anon const, not an item
+ (DefKind::AnonConst | DefKind::InlineConst, _) => {} // things like anon const, not an item
_ => bug!("Unexpected def kind {:?}", kind),
}
/// additional requirements that the closure's creator must verify.
query mir_borrowck(key: LocalDefId) -> &'tcx mir::BorrowCheckResult<'tcx> {
desc { |tcx| "borrow-checking `{}`", tcx.def_path_str(key.to_def_id()) }
- cache_on_disk_if(tcx) { tcx.is_closure(key.to_def_id()) }
+ cache_on_disk_if(tcx) { tcx.is_typeck_child(key.to_def_id()) }
}
query mir_borrowck_const_arg(key: (LocalDefId, DefId)) -> &'tcx mir::BorrowCheckResult<'tcx> {
desc {
desc { "check for overlap between inherent impls defined in this crate" }
}
+ /// Checks whether all impls in the crate pass the overlap check, returning
+ /// which impls fail it. If all impls are correct, the returned slice is empty.
+ query orphan_check_crate(_: ()) -> &'tcx [LocalDefId] {
+ desc {
+ "checking whether the immpl in the this crate follow the orphan rules",
+ }
+ }
+
/// Check whether the function has any recursion that could cause the inliner to trigger
/// a cycle. Returns the call stack causing the cycle. The call stack does not contain the
/// current function, just all intermediate functions.
}
/// Return all `impl` blocks in the current crate.
- ///
- /// To allow caching this between crates, you must pass in [`LOCAL_CRATE`] as the crate number.
- /// Passing in any other crate will cause an ICE.
- ///
- /// [`LOCAL_CRATE`]: rustc_hir::def_id::LOCAL_CRATE
query all_local_trait_impls(_: ()) -> &'tcx BTreeMap<DefId, Vec<LocalDefId>> {
desc { "local trait impls" }
}
/// Error derived when matching traits/impls; see ObligationCause for more details
CompareImplMethodObligation {
- item_name: Symbol,
impl_item_def_id: DefId,
trait_item_def_id: DefId,
},
/// Error derived when matching traits/impls; see ObligationCause for more details
CompareImplTypeObligation {
- item_name: Symbol,
impl_item_def_id: DefId,
trait_item_def_id: DefId,
},
use crate::mir::interpret::ConstValue;
use crate::mir::interpret::{LitToConstInput, Scalar};
-use crate::ty::{self, Ty, TyCtxt};
-use crate::ty::{ParamEnv, ParamEnvAnd};
+use crate::ty::{
+ self, InlineConstSubsts, InlineConstSubstsParts, InternalSubsts, ParamEnv, ParamEnvAnd, Ty,
+ TyCtxt, TypeFoldable,
+};
use rustc_errors::ErrorReported;
use rustc_hir as hir;
use rustc_hir::def_id::{DefId, LocalDefId};
let ty = tcx.type_of(def.def_id_for_type_of());
+ match Self::try_eval_lit_or_param(tcx, ty, expr) {
+ Some(v) => v,
+ None => tcx.mk_const(ty::Const {
+ val: ty::ConstKind::Unevaluated(ty::Unevaluated {
+ def: def.to_global(),
+ substs_: None,
+ promoted: None,
+ }),
+ ty,
+ }),
+ }
+ }
+
+ fn try_eval_lit_or_param(
+ tcx: TyCtxt<'tcx>,
+ ty: Ty<'tcx>,
+ expr: &'tcx hir::Expr<'tcx>,
+ ) -> Option<&'tcx Self> {
let lit_input = match expr.kind {
hir::ExprKind::Lit(ref lit) => Some(LitToConstInput { lit: &lit.node, ty, neg: false }),
hir::ExprKind::Unary(hir::UnOp::Neg, ref expr) => match expr.kind {
// If an error occurred, ignore that it's a literal and leave reporting the error up to
// mir.
if let Ok(c) = tcx.at(expr.span).lit_to_const(lit_input) {
- return c;
+ return Some(c);
} else {
tcx.sess.delay_span_bug(expr.span, "Const::from_anon_const: couldn't lit_to_const");
}
};
use hir::{def::DefKind::ConstParam, def::Res, ExprKind, Path, QPath};
- let val = match expr.kind {
+ match expr.kind {
ExprKind::Path(QPath::Resolved(_, &Path { res: Res::Def(ConstParam, def_id), .. })) => {
// Find the name and index of the const parameter by indexing the generics of
// the parent item and construct a `ParamConst`.
let generics = tcx.generics_of(item_def_id.to_def_id());
let index = generics.param_def_id_to_index[&def_id];
let name = tcx.hir().name(hir_id);
- ty::ConstKind::Param(ty::ParamConst::new(index, name))
+ Some(tcx.mk_const(ty::Const {
+ val: ty::ConstKind::Param(ty::ParamConst::new(index, name)),
+ ty,
+ }))
}
- _ => ty::ConstKind::Unevaluated(ty::Unevaluated {
- def: def.to_global(),
- substs_: None,
- promoted: None,
- }),
+ _ => None,
+ }
+ }
+
+ pub fn from_inline_const(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &'tcx Self {
+ debug!("Const::from_inline_const(def_id={:?})", def_id);
+
+ let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
+
+ let body_id = match tcx.hir().get(hir_id) {
+ hir::Node::AnonConst(ac) => ac.body,
+ _ => span_bug!(
+ tcx.def_span(def_id.to_def_id()),
+ "from_inline_const can only process anonymous constants"
+ ),
};
- tcx.mk_const(ty::Const { val, ty })
+ let expr = &tcx.hir().body(body_id).value;
+
+ let ty = tcx.typeck(def_id).node_type(hir_id);
+
+ let ret = match Self::try_eval_lit_or_param(tcx, ty, expr) {
+ Some(v) => v,
+ None => {
+ let typeck_root_def_id = tcx.typeck_root_def_id(def_id.to_def_id());
+ let parent_substs =
+ tcx.erase_regions(InternalSubsts::identity_for_item(tcx, typeck_root_def_id));
+ let substs =
+ InlineConstSubsts::new(tcx, InlineConstSubstsParts { parent_substs, ty })
+ .substs;
+ tcx.mk_const(ty::Const {
+ val: ty::ConstKind::Unevaluated(ty::Unevaluated {
+ def: ty::WithOptConstParam::unknown(def_id).to_global(),
+ substs_: Some(substs),
+ promoted: None,
+ }),
+ ty,
+ })
+ }
+ };
+ debug_assert!(!ret.has_free_regions(tcx));
+ ret
}
/// Interns the given value as a constant.
TupleSize(ExpectedFound<usize>),
FixedArraySize(ExpectedFound<u64>),
ArgCount,
+ FieldMisMatch(Symbol, Symbol),
RegionsDoesNotOutlive(Region<'tcx>, Region<'tcx>),
RegionsInsufficientlyPolymorphic(BoundRegionKind, Region<'tcx>),
pluralize!(values.found)
),
ArgCount => write!(f, "incorrect number of function parameters"),
+ FieldMisMatch(adt, field) => write!(f, "field type mismatch: {}.{}", adt, field),
RegionsDoesNotOutlive(..) => write!(f, "lifetime mismatch"),
RegionsInsufficientlyPolymorphic(br, _) => write!(
f,
| ArgumentMutability(_)
| TupleSize(_)
| ArgCount
+ | FieldMisMatch(..)
| RegionsDoesNotOutlive(..)
| RegionsInsufficientlyPolymorphic(..)
| RegionsOverlyPolymorphic(..)
Binder, BoundRegion, BoundRegionKind, BoundTy, BoundTyKind, BoundVar, BoundVariableKind,
CanonicalPolyFnSig, ClosureSubsts, ClosureSubstsParts, ConstVid, EarlyBoundRegion,
ExistentialPredicate, ExistentialProjection, ExistentialTraitRef, FnSig, FreeRegion, GenSig,
- GeneratorSubsts, GeneratorSubstsParts, ParamConst, ParamTy, PolyExistentialProjection,
- PolyExistentialTraitRef, PolyFnSig, PolyGenSig, PolyTraitRef, ProjectionTy, Region, RegionKind,
- RegionVid, TraitRef, TyKind, TypeAndMut, UpvarSubsts, VarianceDiagInfo, VarianceDiagMutKind,
+ GeneratorSubsts, GeneratorSubstsParts, InlineConstSubsts, InlineConstSubstsParts, ParamConst,
+ ParamTy, PolyExistentialProjection, PolyExistentialTraitRef, PolyFnSig, PolyGenSig,
+ PolyTraitRef, ProjectionTy, Region, RegionKind, RegionVid, TraitRef, TyKind, TypeAndMut,
+ UpvarSubsts, VarianceDiagInfo, VarianceDiagMutKind,
};
pub use self::trait_def::TraitDef;
Visibility::Invisible => false,
}
}
+
+ pub fn is_public(self) -> bool {
+ matches!(self, Visibility::Public)
+ }
}
/// The crate variances map is computed during typeck and contains the
| DefKind::Static
| DefKind::AssocConst
| DefKind::Ctor(..)
- | DefKind::AnonConst => self.mir_for_ctfe_opt_const_arg(def),
+ | DefKind::AnonConst
+ | DefKind::InlineConst => self.mir_for_ctfe_opt_const_arg(def),
// If the caller wants `mir_for_ctfe` of a function they should not be using
// `instance_mir`, so we'll assume const fn also wants the optimized version.
_ => {
// Iterate external crate defs but be mindful about visibility
while let Some(def) = queue.pop() {
for child in tcx.item_children(def).iter() {
- if child.vis != ty::Visibility::Public {
+ if !child.vis.is_public() {
continue;
}
TupleSize(x) => TupleSize(x),
FixedArraySize(x) => FixedArraySize(x),
ArgCount => ArgCount,
+ FieldMisMatch(x, y) => FieldMisMatch(x, y),
RegionsDoesNotOutlive(a, b) => {
return tcx.lift((a, b)).map(|(a, b)| RegionsDoesNotOutlive(a, b));
}
}
}
+/// An inline const is modeled like
+///
+/// const InlineConst<'l0...'li, T0...Tj, R>: R;
+///
+/// where:
+///
+/// - 'l0...'li and T0...Tj are the generic parameters
+/// inherited from the item that defined the inline const,
+/// - R represents the type of the constant.
+///
+/// When the inline const is instantiated, `R` is substituted as the actual inferred
+/// type of the constant. The reason that `R` is represented as an extra type parameter
+/// is the same reason that [`ClosureSubsts`] have `CS` and `U` as type parameters:
+/// inline const can reference lifetimes that are internal to the creating function.
+#[derive(Copy, Clone, Debug, TypeFoldable)]
+pub struct InlineConstSubsts<'tcx> {
+ /// Generic parameters from the enclosing item,
+ /// concatenated with the inferred type of the constant.
+ pub substs: SubstsRef<'tcx>,
+}
+
+/// Struct returned by `split()`.
+pub struct InlineConstSubstsParts<'tcx, T> {
+ pub parent_substs: &'tcx [GenericArg<'tcx>],
+ pub ty: T,
+}
+
+impl<'tcx> InlineConstSubsts<'tcx> {
+ /// Construct `InlineConstSubsts` from `InlineConstSubstsParts`.
+ pub fn new(
+ tcx: TyCtxt<'tcx>,
+ parts: InlineConstSubstsParts<'tcx, Ty<'tcx>>,
+ ) -> InlineConstSubsts<'tcx> {
+ InlineConstSubsts {
+ substs: tcx.mk_substs(
+ parts.parent_substs.iter().copied().chain(std::iter::once(parts.ty.into())),
+ ),
+ }
+ }
+
+ /// Divides the inline const substs into their respective components.
+ /// The ordering assumed here must match that used by `InlineConstSubsts::new` above.
+ fn split(self) -> InlineConstSubstsParts<'tcx, GenericArg<'tcx>> {
+ match self.substs[..] {
+ [ref parent_substs @ .., ty] => InlineConstSubstsParts { parent_substs, ty },
+ _ => bug!("inline const substs missing synthetics"),
+ }
+ }
+
+ /// Returns the substitutions of the inline const's parent.
+ pub fn parent_substs(self) -> &'tcx [GenericArg<'tcx>] {
+ self.split().parent_substs
+ }
+
+ /// Returns the type of this inline const.
+ pub fn ty(self) -> Ty<'tcx> {
+ self.split().ty.expect_ty()
+ }
+}
+
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Ord, Eq, Hash, TyEncodable, TyDecodable)]
#[derive(HashStable, TypeFoldable)]
pub enum ExistentialPredicate<'tcx> {
use crate::mir;
use crate::ty::codec::{TyDecoder, TyEncoder};
use crate::ty::fold::{TypeFoldable, TypeFolder, TypeVisitor};
-use crate::ty::sty::{ClosureSubsts, GeneratorSubsts};
+use crate::ty::sty::{ClosureSubsts, GeneratorSubsts, InlineConstSubsts};
use crate::ty::{self, Lift, List, ParamConst, Ty, TyCtxt};
use rustc_hir::def_id::DefId;
GeneratorSubsts { substs: self }
}
+ /// Interpret these substitutions as the substitutions of an inline const.
+ /// Inline const substitutions have a particular structure controlled by the
+ /// compiler that encodes information like the inferred type;
+ /// see `ty::InlineConstSubsts` struct for more comments.
+ pub fn as_inline_const(&'tcx self) -> InlineConstSubsts<'tcx> {
+ InlineConstSubsts { substs: self }
+ }
+
/// Creates an `InternalSubsts` that maps each generic parameter to itself.
pub fn identity_for_item(tcx: TyCtxt<'tcx>, def_id: DefId) -> SubstsRef<'tcx> {
Self::for_item(tcx, def_id, |param, _| tcx.mk_param_from_def(param))
matches!(self.def_kind(def_id), DefKind::Closure | DefKind::Generator)
}
+ /// Returns `true` if `def_id` refers to a definition that does not have its own
+ /// type-checking context, i.e. closure, generator or inline const.
+ pub fn is_typeck_child(self, def_id: DefId) -> bool {
+ matches!(
+ self.def_kind(def_id),
+ DefKind::Closure | DefKind::Generator | DefKind::InlineConst
+ )
+ }
+
/// Returns `true` if `def_id` refers to a trait (i.e., `trait Foo { ... }`).
pub fn is_trait(self, def_id: DefId) -> bool {
self.def_kind(def_id) == DefKind::Trait
matches!(self.def_kind(def_id), DefKind::Ctor(..))
}
- /// Given the def-ID of a fn or closure, returns the def-ID of
- /// the innermost fn item that the closure is contained within.
- /// This is a significant `DefId` because, when we do
- /// type-checking, we type-check this fn item and all of its
- /// (transitive) closures together. Therefore, when we fetch the
+ /// Given the `DefId`, returns the `DefId` of the innermost item that
+ /// has its own type-checking context or "inference enviornment".
+ ///
+ /// For example, a closure has its own `DefId`, but it is type-checked
+ /// with the containing item. Similarly, an inline const block has its
+ /// own `DefId` but it is type-checked together with the containing item.
+ ///
+ /// Therefore, when we fetch the
/// `typeck` the closure, for example, we really wind up
/// fetching the `typeck` the enclosing fn item.
- pub fn closure_base_def_id(self, def_id: DefId) -> DefId {
+ pub fn typeck_root_def_id(self, def_id: DefId) -> DefId {
let mut def_id = def_id;
- while self.is_closure(def_id) {
+ while self.is_typeck_child(def_id) {
def_id = self.parent(def_id).unwrap_or_else(|| {
bug!("closure {:?} has no parent", def_id);
});
let rv = f();
let duration = start.elapsed();
let mut accu = accu.lock();
- *accu = *accu + duration;
+ *accu += duration;
rv
}
hir::ExprKind::ConstBlock(ref anon_const) => {
let anon_const_def_id = self.tcx.hir().local_def_id(anon_const.hir_id);
- let value = ty::Const::from_anon_const(self.tcx, anon_const_def_id);
+ let value = ty::Const::from_inline_const(self.tcx, anon_const_def_id);
ExprKind::ConstBlock { value }
}
let (lit, neg) = match expr.kind {
hir::ExprKind::ConstBlock(ref anon_const) => {
let anon_const_def_id = self.tcx.hir().local_def_id(anon_const.hir_id);
- let value = ty::Const::from_anon_const(self.tcx, anon_const_def_id);
+ let value = ty::Const::from_inline_const(self.tcx, anon_const_def_id);
if matches!(value.val, ConstKind::Param(_)) {
let span = self.tcx.hir().span(anon_const.hir_id);
self.errors.push(PatternError::ConstParamInPattern(span));
/// expands it.
fn push(&mut self, row: PatStack<'p, 'tcx>) {
if !row.is_empty() && row.head().is_or_pat() {
- for row in row.expand_or_pat() {
- self.patterns.push(row);
- }
+ self.patterns.extend(row.expand_or_pat());
} else {
self.patterns.push(row);
}
return ret;
}
- assert!(rows.iter().all(|r| r.len() == v.len()));
+ debug_assert!(rows.iter().all(|r| r.len() == v.len()));
let ty = v.head().ty();
let is_non_exhaustive = cx.is_foreign_non_exhaustive_enum(ty);
match self_ty.kind() {
_ if is_copy => builder.copy_shim(),
- ty::Array(ty, len) => builder.array_shim(dest, src, ty, len),
ty::Closure(_, substs) => {
builder.tuple_like_shim(dest, src, substs.as_closure().upvar_tys())
}
);
}
- fn loop_header(
- &mut self,
- beg: Place<'tcx>,
- end: Place<'tcx>,
- loop_body: BasicBlock,
- loop_end: BasicBlock,
- is_cleanup: bool,
- ) {
- let tcx = self.tcx;
-
- let cond = self.make_place(Mutability::Mut, tcx.types.bool);
- let compute_cond = self.make_statement(StatementKind::Assign(Box::new((
- cond,
- Rvalue::BinaryOp(BinOp::Ne, Box::new((Operand::Copy(end), Operand::Copy(beg)))),
- ))));
-
- // `if end != beg { goto loop_body; } else { goto loop_end; }`
- self.block(
- vec![compute_cond],
- TerminatorKind::if_(tcx, Operand::Move(cond), loop_body, loop_end),
- is_cleanup,
- );
- }
-
- fn make_usize(&self, value: u64) -> Box<Constant<'tcx>> {
- Box::new(Constant {
- span: self.span,
- user_ty: None,
- literal: ty::Const::from_usize(self.tcx, value).into(),
- })
- }
-
- fn array_shim(
- &mut self,
- dest: Place<'tcx>,
- src: Place<'tcx>,
- ty: Ty<'tcx>,
- len: &'tcx ty::Const<'tcx>,
- ) {
- let tcx = self.tcx;
- let span = self.span;
-
- let beg = self.local_decls.push(LocalDecl::new(tcx.types.usize, span));
- let end = self.make_place(Mutability::Not, tcx.types.usize);
-
- // BB #0
- // `let mut beg = 0;`
- // `let end = len;`
- // `goto #1;`
- let inits = vec![
- self.make_statement(StatementKind::Assign(Box::new((
- Place::from(beg),
- Rvalue::Use(Operand::Constant(self.make_usize(0))),
- )))),
- self.make_statement(StatementKind::Assign(Box::new((
- end,
- Rvalue::Use(Operand::Constant(Box::new(Constant {
- span: self.span,
- user_ty: None,
- literal: len.into(),
- }))),
- )))),
- ];
- self.block(inits, TerminatorKind::Goto { target: BasicBlock::new(1) }, false);
-
- // BB #1: loop {
- // BB #2;
- // BB #3;
- // }
- // BB #4;
- self.loop_header(Place::from(beg), end, BasicBlock::new(2), BasicBlock::new(4), false);
-
- // BB #2
- // `dest[i] = Clone::clone(src[beg])`;
- // Goto #3 if ok, #5 if unwinding happens.
- let dest_field = self.tcx.mk_place_index(dest, beg);
- let src_field = self.tcx.mk_place_index(src, beg);
- self.make_clone_call(dest_field, src_field, ty, BasicBlock::new(3), BasicBlock::new(5));
-
- // BB #3
- // `beg = beg + 1;`
- // `goto #1`;
- let statements = vec![self.make_statement(StatementKind::Assign(Box::new((
- Place::from(beg),
- Rvalue::BinaryOp(
- BinOp::Add,
- Box::new((Operand::Copy(Place::from(beg)), Operand::Constant(self.make_usize(1)))),
- ),
- ))))];
- self.block(statements, TerminatorKind::Goto { target: BasicBlock::new(1) }, false);
-
- // BB #4
- // `return dest;`
- self.block(vec![], TerminatorKind::Return, false);
-
- // BB #5 (cleanup)
- // `let end = beg;`
- // `let mut beg = 0;`
- // goto #6;
- let end = beg;
- let beg = self.local_decls.push(LocalDecl::new(tcx.types.usize, span));
- let init = self.make_statement(StatementKind::Assign(Box::new((
- Place::from(beg),
- Rvalue::Use(Operand::Constant(self.make_usize(0))),
- ))));
- self.block(vec![init], TerminatorKind::Goto { target: BasicBlock::new(6) }, true);
-
- // BB #6 (cleanup): loop {
- // BB #7;
- // BB #8;
- // }
- // BB #9;
- self.loop_header(
- Place::from(beg),
- Place::from(end),
- BasicBlock::new(7),
- BasicBlock::new(9),
- true,
- );
-
- // BB #7 (cleanup)
- // `drop(dest[beg])`;
- self.block(
- vec![],
- TerminatorKind::Drop {
- place: self.tcx.mk_place_index(dest, beg),
- target: BasicBlock::new(8),
- unwind: None,
- },
- true,
- );
-
- // BB #8 (cleanup)
- // `beg = beg + 1;`
- // `goto #6;`
- let statement = self.make_statement(StatementKind::Assign(Box::new((
- Place::from(beg),
- Rvalue::BinaryOp(
- BinOp::Add,
- Box::new((Operand::Copy(Place::from(beg)), Operand::Constant(self.make_usize(1)))),
- ),
- ))));
- self.block(vec![statement], TerminatorKind::Goto { target: BasicBlock::new(6) }, true);
-
- // BB #9 (resume)
- self.block(vec![], TerminatorKind::Resume, true);
- }
-
fn tuple_like_shim<I>(&mut self, dest: Place<'tcx>, src: Place<'tcx>, tys: I)
where
I: Iterator<Item = Ty<'tcx>>,
)
});
+ if tcx.prof.enabled() {
+ // Record CGU size estimates for self-profiling.
+ for cgu in codegen_units {
+ tcx.prof.artifact_size(
+ "codegen_unit_size_estimate",
+ &cgu.name().as_str()[..],
+ cgu.size_estimate() as u64,
+ );
+ }
+ }
+
let mono_items: DefIdSet = items
.iter()
.filter_map(|mono_item| match *mono_item {
| DefKind::Use
| DefKind::ForeignMod
| DefKind::AnonConst
+ | DefKind::InlineConst
| DefKind::OpaqueTy
| DefKind::Field
| DefKind::LifetimeParam
generics: &'tcx ty::Generics,
unused_parameters: &FiniteBitSet<u32>,
) {
- let base_def_id = tcx.closure_base_def_id(def_id);
+ let base_def_id = tcx.typeck_root_def_id(def_id);
if !tcx.get_attrs(base_def_id).iter().any(|a| a.has_name(sym::rustc_polymorphize_error)) {
return;
}
ControlFlow::CONTINUE
}
ty::ConstKind::Unevaluated(uv)
- if self.tcx.def_kind(uv.def.did) == DefKind::AnonConst =>
+ if matches!(self.tcx.def_kind(uv.def.did), DefKind::AnonConst | DefKind::InlineConst) =>
{
self.visit_child_body(uv.def.did, uv.substs(self.tcx));
ControlFlow::CONTINUE
use rustc_ast::tokenstream::{DelimSpan, TokenTree};
use rustc_ast::{self as ast, Attribute, MacArgs, MacDelimiter, MetaItem, MetaItemKind};
use rustc_errors::{Applicability, FatalError, PResult};
-use rustc_feature::{AttributeTemplate, BUILTIN_ATTRIBUTE_MAP};
+use rustc_feature::{AttributeTemplate, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP};
use rustc_session::lint::builtin::ILL_FORMED_ATTRIBUTE_INPUT;
use rustc_session::parse::ParseSess;
use rustc_span::{sym, Symbol};
return;
}
- let attr_info =
- attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name)).map(|a| **a);
+ let attr_info = attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name));
// Check input tokens for built-in and key-value attributes.
match attr_info {
// `rustc_dummy` doesn't have any restrictions specific to built-in attributes.
- Some((name, _, template, _)) if name != sym::rustc_dummy => {
- check_builtin_attribute(sess, attr, name, template)
+ Some(BuiltinAttribute { name, template, .. }) if *name != sym::rustc_dummy => {
+ check_builtin_attribute(sess, attr, *name, *template)
}
_ if let MacArgs::Eq(..) = attr.get_normal_item().args => {
// All key-value attributes are restricted to meta-item syntax.
attr: &Attribute,
name: Symbol,
) -> ! {
- let template = BUILTIN_ATTRIBUTE_MAP.get(&name).expect("builtin attr defined").2;
+ let template = BUILTIN_ATTRIBUTE_MAP.get(&name).expect("builtin attr defined").template;
emit_malformed_attribute(sess, attr, name, template);
// This is fatal, otherwise it will likely cause a cascade of other errors
// (and an error here is expected to be very rare).
use rustc_ast::{ast, AttrStyle, Attribute, Lit, LitKind, NestedMetaItem};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_errors::{pluralize, struct_span_err, Applicability};
-use rustc_feature::{AttributeType, BUILTIN_ATTRIBUTE_MAP};
+use rustc_feature::{AttributeType, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP};
use rustc_hir as hir;
use rustc_hir::def_id::LocalDefId;
use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
}
if hir_id != CRATE_HIR_ID {
- if let Some((_, AttributeType::CrateLevel, ..)) =
+ if let Some(BuiltinAttribute { type_: AttributeType::CrateLevel, .. }) =
attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name))
{
self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![feature(crate_visibility_modifier)]
#![feature(in_band_lifetimes)]
-#![feature(format_args_capture)]
+#![cfg_attr(bootstrap, feature(format_args_capture))]
#![feature(iter_zip)]
#![feature(map_try_insert)]
#![feature(min_specialization)]
// properly, we can't miss any types.
match expr.kind {
- // Manually recurse over closures, because they are the only
+ // Manually recurse over closures and inline consts, because they are the only
// case of nested bodies that share the parent environment.
- hir::ExprKind::Closure(.., body, _, _) => {
+ hir::ExprKind::Closure(.., body, _, _)
+ | hir::ExprKind::ConstBlock(hir::AnonConst { body, .. }) => {
let body = visitor.tcx.hir().body(body);
visitor.visit_body(body);
}
}
fn region_scope_tree(tcx: TyCtxt<'_>, def_id: DefId) -> &ScopeTree {
- let closure_base_def_id = tcx.closure_base_def_id(def_id);
- if closure_base_def_id != def_id {
- return tcx.region_scope_tree(closure_base_def_id);
+ let typeck_root_def_id = tcx.typeck_root_def_id(def_id);
+ if typeck_root_def_id != def_id {
+ return tcx.region_scope_tree(typeck_root_def_id);
}
let id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local());
module: LocalDefId,
) {
let level = Some(AccessLevel::Reachable);
- if let ty::Visibility::Public = vis {
+ if vis.is_public() {
self.update(def_id, level);
}
match def_kind {
DefKind::Struct | DefKind::Union => {
// While structs and unions have type privacy, their fields do not.
- if let ty::Visibility::Public = vis {
+ if vis.is_public() {
let item =
self.tcx.hir().expect_item(self.tcx.hir().local_def_id_to_hir_id(def_id));
if let hir::ItemKind::Struct(ref struct_def, _)
| DefKind::Use
| DefKind::ForeignMod
| DefKind::AnonConst
+ | DefKind::InlineConst
| DefKind::Field
| DefKind::GlobalAsm
| DefKind::Impl
let def_id = self.tcx.hir().local_def_id(id);
if let Some(exports) = self.tcx.module_exports(def_id) {
for export in exports.iter() {
- if export.vis == ty::Visibility::Public {
+ if export.vis.is_public() {
if let Some(def_id) = export.res.opt_def_id() {
if let Some(def_id) = def_id.as_local() {
self.update(def_id, Some(AccessLevel::Exported));
/// 1. It's contained within a public type
/// 2. It comes from a private crate
fn leaks_private_dep(&self, item_id: DefId) -> bool {
- let ret = self.required_visibility == ty::Visibility::Public
- && self.tcx.is_private_dep(item_id.krate);
+ let ret = self.required_visibility.is_public() && self.tcx.is_private_dep(item_id.krate);
tracing::debug!("leaks_private_dep(item_id={:?})={}", item_id, ret);
ret
| DefKind::Use
| DefKind::ForeignMod
| DefKind::AnonConst
+ | DefKind::InlineConst
| DefKind::Field
| DefKind::LifetimeParam
| DefKind::GlobalAsm
// Mark the given macro as unused unless its name starts with `_`.
// Macro uses will remove items from this set, and the remaining
// items will be reported as `unused_macros`.
- fn insert_unused_macro(
- &mut self,
- ident: Ident,
- def_id: LocalDefId,
- node_id: NodeId,
- span: Span,
- ) {
+ fn insert_unused_macro(&mut self, ident: Ident, def_id: LocalDefId, node_id: NodeId) {
if !ident.as_str().starts_with('_') {
- self.r.unused_macros.insert(def_id, (node_id, span));
+ self.r.unused_macros.insert(def_id, (node_id, ident));
}
}
self.r.define(module, ident, MacroNS, (res, vis, span, expansion, IsMacroExport));
} else {
self.r.check_reserved_macro_name(ident, res);
- self.insert_unused_macro(ident, def_id, item.id, span);
+ self.insert_unused_macro(ident, def_id, item.id);
}
self.r.visibilities.insert(def_id, vis);
self.r.arenas.alloc_macro_rules_scope(MacroRulesScope::Binding(
_ => self.resolve_visibility(&item.vis),
};
if vis != ty::Visibility::Public {
- self.insert_unused_macro(ident, def_id, item.id, span);
+ self.insert_unused_macro(ident, def_id, item.id);
}
self.r.define(module, ident, MacroNS, (res, vis, span, expansion));
self.r.visibilities.insert(def_id, vis);
use rustc_ast_lowering::ResolverAstLowering;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::pluralize;
-use rustc_middle::ty;
use rustc_session::lint::builtin::{MACRO_USE_EXTERN_CRATE, UNUSED_IMPORTS};
use rustc_session::lint::BuiltinLintDiagnostics;
use rustc_span::{MultiSpan, Span, DUMMY_SP};
for import in self.potentially_unused_imports.iter() {
match import.kind {
_ if import.used.get()
- || import.vis.get() == ty::Visibility::Public
+ || import.vis.get().is_public()
|| import.span.is_dummy() =>
{
if let ImportKind::MacroUse = import.kind {
use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX, LOCAL_CRATE};
use rustc_hir::PrimTy;
use rustc_middle::bug;
-use rustc_middle::ty::{self, DefIdTree};
+use rustc_middle::ty::DefIdTree;
use rustc_session::Session;
use rustc_span::hygiene::MacroKind;
use rustc_span::lev_distance::find_best_match_for_name;
suggestions.extend(
BUILTIN_ATTRIBUTES
.iter()
- .map(|(name, ..)| TypoSuggestion::typo_from_res(*name, res)),
+ .map(|attr| TypoSuggestion::typo_from_res(attr.name, res)),
);
}
}
);
let def_span = self.session.source_map().guess_head_span(binding.span);
let mut note_span = MultiSpan::from_span(def_span);
- if !first && binding.vis == ty::Visibility::Public {
+ if !first && binding.vis.is_public() {
note_span.push_span_label(def_span, "consider importing it directly".into());
}
err.span_note(note_span, &msg);
import: Import { kind: ImportKind::ExternCrate { .. }, .. },
..
},
- ) => import.vis.get() == ty::Visibility::Public,
+ ) => import.vis.get().is_public(),
_ => false,
}
}
def_id: LocalDefId,
) -> Option<(LocalDefId, &'tcx FxHashSet<ItemLocalId>)> {
match tcx.def_kind(def_id) {
- DefKind::AnonConst => {
+ DefKind::AnonConst | DefKind::InlineConst => {
let mut def_id = tcx
.parent(def_id.to_def_id())
.unwrap_or_else(|| bug!("anon const or closure without a parent"));
#![feature(drain_filter)]
#![feature(bool_to_option)]
#![feature(crate_visibility_modifier)]
-#![feature(format_args_capture)]
+#![cfg_attr(bootstrap, feature(format_args_capture))]
#![feature(iter_zip)]
#![feature(let_else)]
#![feature(never_type)]
non_macro_attr: Lrc<SyntaxExtension>,
local_macro_def_scopes: FxHashMap<LocalDefId, Module<'a>>,
ast_transform_scopes: FxHashMap<LocalExpnId, Module<'a>>,
- unused_macros: FxHashMap<LocalDefId, (NodeId, Span)>,
+ unused_macros: FxHashMap<LocalDefId, (NodeId, Ident)>,
proc_macro_stubs: FxHashSet<LocalDefId>,
/// Traces collected during macro resolution and validated when it's complete.
single_segment_macro_resolutions:
}
fn check_unused_macros(&mut self) {
- for (_, &(node_id, span)) in self.unused_macros.iter() {
- self.lint_buffer.buffer_lint(UNUSED_MACROS, node_id, span, "unused macro definition");
+ for (_, &(node_id, ident)) in self.unused_macros.iter() {
+ self.lint_buffer.buffer_lint(
+ UNUSED_MACROS,
+ node_id,
+ ident.span,
+ &format!("unused macro definition: `{}`", ident.as_str()),
+ );
}
}
| HirDefKind::ForeignMod
| HirDefKind::LifetimeParam
| HirDefKind::AnonConst
+ | HirDefKind::InlineConst
| HirDefKind::Use
| HirDefKind::Field
| HirDefKind::GlobalAsm
pub out_directory: PathBuf,
filestem: String,
pub single_output_file: Option<PathBuf>,
+ pub temps_directory: Option<PathBuf>,
pub outputs: OutputTypes,
}
out_directory: PathBuf,
out_filestem: String,
single_output_file: Option<PathBuf>,
+ temps_directory: Option<PathBuf>,
extra: String,
outputs: OutputTypes,
) -> Self {
OutputFilenames {
out_directory,
single_output_file,
+ temps_directory,
outputs,
filestem: format!("{}{}", out_filestem, extra),
}
.get(&flavor)
.and_then(|p| p.to_owned())
.or_else(|| self.single_output_file.clone())
- .unwrap_or_else(|| self.temp_path(flavor, None))
+ .unwrap_or_else(|| self.output_path(flavor))
+ }
+
+ /// Gets the output path where a compilation artifact of the given type
+ /// should be placed on disk.
+ pub fn output_path(&self, flavor: OutputType) -> PathBuf {
+ let extension = flavor.extension();
+ self.with_directory_and_extension(&self.out_directory, &extension)
}
/// Gets the path where a compilation artifact of the given type for the
extension.push_str(ext);
}
- self.with_extension(&extension)
+ let temps_directory = self.temps_directory.as_ref().unwrap_or(&self.out_directory);
+
+ self.with_directory_and_extension(&temps_directory, &extension)
}
pub fn with_extension(&self, extension: &str) -> PathBuf {
- let mut path = self.out_directory.join(&self.filestem);
+ self.with_directory_and_extension(&self.out_directory, extension)
+ }
+
+ fn with_directory_and_extension(&self, directory: &PathBuf, extension: &str) -> PathBuf {
+ let mut path = directory.join(&self.filestem);
path.set_extension(extension);
path
}
"which mangling version to use for symbol names ('legacy' (default) or 'v0')"),
teach: bool = (false, parse_bool, [TRACKED],
"show extended diagnostic help (default: no)"),
+ temps_dir: Option<String> = (None, parse_opt_string, [UNTRACKED],
+ "the directory the intermediate files are written to"),
terminal_width: Option<usize> = (None, parse_opt_number, [UNTRACKED],
"set the current terminal width"),
tune_cpu: Option<String> = (None, parse_opt_string, [TRACKED],
if concrete.is_ok() && uv.substs(infcx.tcx).definitely_has_param_types_or_consts(infcx.tcx) {
match infcx.tcx.def_kind(uv.def.did) {
- DefKind::AnonConst => {
+ DefKind::AnonConst | DefKind::InlineConst => {
let mir_body = infcx.tcx.mir_for_ctfe_opt_const_arg(uv.def);
if mir_body.is_polymorphic {
// we want to look into them or treat them as opaque projections.
//
// Right now we do neither of that and simply always fail to unify them.
- DefKind::AnonConst => (),
+ DefKind::AnonConst | DefKind::InlineConst => (),
_ => return Ok(None),
}
}
}
if let ObligationCauseCode::CompareImplMethodObligation {
- item_name,
impl_item_def_id,
trait_item_def_id,
}
| ObligationCauseCode::CompareImplTypeObligation {
- item_name,
impl_item_def_id,
trait_item_def_id,
} = obligation.cause.code
{
self.report_extra_impl_obligation(
span,
- item_name,
impl_item_def_id,
trait_item_def_id,
&format!("`{}`", obligation.predicate),
let span = self.tcx.def_span(generator_did);
let in_progress_typeck_results = self.in_progress_typeck_results.map(|t| t.borrow());
- let generator_did_root = self.tcx.closure_base_def_id(generator_did);
+ let generator_did_root = self.tcx.typeck_root_def_id(generator_did);
debug!(
"maybe_note_obligation_cause_for_async_await: generator_did={:?} \
generator_did_root={:?} in_progress_typeck_results.hir_owner={:?} span={:?}",
)
});
}
- ObligationCauseCode::CompareImplMethodObligation {
- item_name,
- trait_item_def_id,
- ..
- } => {
+ ObligationCauseCode::CompareImplMethodObligation { trait_item_def_id, .. } => {
+ let item_name = self.tcx.item_name(trait_item_def_id);
let msg = format!(
"the requirement `{}` appears on the impl method `{}` but not on the \
corresponding trait method",
}
err.span_note(assoc_span, &msg);
}
- ObligationCauseCode::CompareImplTypeObligation {
- item_name, trait_item_def_id, ..
- } => {
+ ObligationCauseCode::CompareImplTypeObligation { trait_item_def_id, .. } => {
+ let item_name = self.tcx.item_name(trait_item_def_id);
let msg = format!(
"the requirement `{}` appears on the associated impl type `{}` but not on the \
corresponding associated trait type",
| ty::Char
| ty::RawPtr(..)
| ty::Never
- | ty::Ref(_, _, hir::Mutability::Not) => return Ok(()),
+ | ty::Ref(_, _, hir::Mutability::Not)
+ | ty::Array(..) => return Ok(()),
ty::Adt(adt, substs) => (adt, substs),
| ty::Char
| ty::RawPtr(..)
| ty::Never
- | ty::Ref(_, _, hir::Mutability::Not) => {
+ | ty::Ref(_, _, hir::Mutability::Not)
+ | ty::Array(..) => {
// Implementations provided in libcore
None
}
| ty::Foreign(..)
| ty::Ref(_, _, hir::Mutability::Mut) => None,
- ty::Array(element_ty, _) => {
- // (*) binder moved here
- Where(obligation.predicate.rebind(vec![element_ty]))
- }
-
ty::Tuple(tys) => {
// (*) binder moved here
Where(obligation.predicate.rebind(tys.iter().map(|k| k.expect_ty()).collect()))
sg
}
+// This function is only used when
+// encountering errors and inlining
+// it negatively impacts perf.
+#[cold]
+#[inline(never)]
fn report_overlap_conflict(
tcx: TyCtxt<'_>,
overlap: OverlapError,
match used_to_be_allowed {
None => {
sg.has_errored = true;
- let err = struct_span_err!(tcx.sess, impl_span, E0119, "");
- decorate(LintDiagnosticBuilder::new(err));
+ if overlap.with_impl.is_local() || !tcx.orphan_check_crate(()).contains(&impl_def_id) {
+ let err = struct_span_err!(tcx.sess, impl_span, E0119, "");
+ decorate(LintDiagnosticBuilder::new(err));
+ } else {
+ tcx.sess.delay_span_bug(impl_span, "impl should have failed the orphan check");
+ }
}
Some(kind) => {
let lint = match kind {
let is_copy = self_ty.is_copy_modulo_regions(tcx.at(DUMMY_SP), param_env);
match self_ty.kind() {
_ if is_copy => (),
- ty::Array(..) | ty::Closure(..) | ty::Tuple(..) => {}
+ ty::Closure(..) | ty::Tuple(..) => {}
_ => return Ok(None),
};
let parent_substs = InternalSubsts::identity_for_item(
self.tcx,
- self.tcx.closure_base_def_id(expr_def_id.to_def_id()),
+ self.tcx.typeck_root_def_id(expr_def_id.to_def_id()),
);
let tupled_upvars_ty = self.infcx.next_ty_var(TypeVariableOrigin {
impl_m_span,
impl_m_hir_id,
ObligationCauseCode::CompareImplMethodObligation {
- item_name: impl_m.ident.name,
impl_item_def_id: impl_m.def_id,
trait_item_def_id: trait_m.def_id,
},
impl_ty_span,
impl_ty_hir_id,
ObligationCauseCode::CompareImplTypeObligation {
- item_name: impl_ty.ident.name,
impl_item_def_id: impl_ty.def_id,
trait_item_def_id: trait_ty.def_id,
},
ty::PredicateKind::ConstEvaluatable(a),
ty::PredicateKind::ConstEvaluatable(b),
) => tcx.try_unify_abstract_consts((a, b)),
- (ty::PredicateKind::TypeOutlives(a), ty::PredicateKind::TypeOutlives(b)) => {
- relator.relate(predicate.rebind(a.0), p.rebind(b.0)).is_ok()
+ (
+ ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(ty_a, lt_a)),
+ ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(ty_b, lt_b)),
+ ) => {
+ relator.relate(predicate.rebind(ty_a), p.rebind(ty_b)).is_ok()
+ && relator.relate(predicate.rebind(lt_a), p.rebind(lt_b)).is_ok()
}
_ => predicate == p,
}
use rustc_hir as hir;
use rustc_hir::def::{CtorKind, DefKind, Res};
use rustc_hir::def_id::DefId;
+use rustc_hir::intravisit::Visitor;
use rustc_hir::{ExprKind, QPath};
use rustc_infer::infer;
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
-use rustc_middle::ty;
+use rustc_infer::infer::InferOk;
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase};
+use rustc_middle::ty::error::TypeError::{FieldMisMatch, Sorts};
+use rustc_middle::ty::relate::expected_found_bool;
use rustc_middle::ty::subst::SubstsRef;
-use rustc_middle::ty::Ty;
-use rustc_middle::ty::TypeFoldable;
-use rustc_middle::ty::{AdtKind, Visibility};
+use rustc_middle::ty::{self, AdtKind, Ty, TypeFoldable};
+use rustc_session::parse::feature_err;
use rustc_span::edition::LATEST_STABLE_EDITION;
use rustc_span::hygiene::DesugaringKind;
use rustc_span::lev_distance::find_best_match_for_name;
}
ExprKind::DropTemps(e) => self.check_expr_with_expectation(e, expected),
ExprKind::Array(args) => self.check_expr_array(args, expected, expr),
- ExprKind::ConstBlock(ref anon_const) => self.to_const(anon_const).ty,
+ ExprKind::ConstBlock(ref anon_const) => {
+ self.check_expr_const_block(anon_const, expected, expr)
+ }
ExprKind::Repeat(element, ref count) => {
self.check_expr_repeat(element, count, expected, expr)
}
self.tcx.mk_array(element_ty, args.len() as u64)
}
+ fn check_expr_const_block(
+ &self,
+ anon_const: &'tcx hir::AnonConst,
+ expected: Expectation<'tcx>,
+ _expr: &'tcx hir::Expr<'tcx>,
+ ) -> Ty<'tcx> {
+ let body = self.tcx.hir().body(anon_const.body);
+
+ // Create a new function context.
+ let fcx = FnCtxt::new(self, self.param_env, body.value.hir_id);
+ crate::check::GatherLocalsVisitor::new(&fcx).visit_body(body);
+
+ let ty = fcx.check_expr_with_expectation(&body.value, expected);
+ fcx.require_type_is_sized(ty, body.value.span, traits::ConstSized);
+ fcx.write_ty(anon_const.hir_id, ty);
+ ty
+ }
+
fn check_expr_repeat(
&self,
element: &'tcx hir::Expr<'tcx>,
.emit_err(StructExprNonExhaustive { span: expr.span, what: adt.variant_descr() });
}
- let error_happened = self.check_expr_struct_fields(
+ self.check_expr_struct_fields(
adt_ty,
expected,
expr.hir_id,
qpath.span(),
variant,
fields,
- base_expr.is_none(),
+ base_expr,
expr.span,
);
- if let Some(base_expr) = base_expr {
- // If check_expr_struct_fields hit an error, do not attempt to populate
- // the fields with the base_expr. This could cause us to hit errors later
- // when certain fields are assumed to exist that in fact do not.
- if !error_happened {
- self.check_expr_has_type_or_error(base_expr, adt_ty, |_| {});
- match adt_ty.kind() {
- ty::Adt(adt, substs) if adt.is_struct() => {
- let fru_field_types = adt
- .non_enum_variant()
- .fields
- .iter()
- .map(|f| {
- self.normalize_associated_types_in(
- expr.span,
- f.ty(self.tcx, substs),
- )
- })
- .collect();
-
- self.typeck_results
- .borrow_mut()
- .fru_field_types_mut()
- .insert(expr.hir_id, fru_field_types);
- }
- _ => {
- self.tcx
- .sess
- .emit_err(FunctionalRecordUpdateOnNonStruct { span: base_expr.span });
- }
- }
- }
- }
+
self.require_type_is_sized(adt_ty, expr.span, traits::StructInitializerSized);
adt_ty
}
span: Span,
variant: &'tcx ty::VariantDef,
ast_fields: &'tcx [hir::ExprField<'tcx>],
- check_completeness: bool,
+ base_expr: &'tcx Option<&'tcx hir::Expr<'tcx>>,
expr_span: Span,
- ) -> bool {
+ ) {
let tcx = self.tcx;
let adt_ty_hint = self
)
.emit();
}
- } else if check_completeness && !error_happened && !remaining_fields.is_empty() {
+ }
+
+ // If check_expr_struct_fields hit an error, do not attempt to populate
+ // the fields with the base_expr. This could cause us to hit errors later
+ // when certain fields are assumed to exist that in fact do not.
+ if error_happened {
+ return;
+ }
+
+ if let Some(base_expr) = base_expr {
+ // FIXME: We are currently creating two branches here in order to maintain
+ // consistency. But they should be merged as much as possible.
+ let fru_tys = if self.tcx.features().type_changing_struct_update {
+ let base_ty = self.check_expr(base_expr);
+ match adt_ty.kind() {
+ ty::Adt(adt, substs) if adt.is_struct() => {
+ match base_ty.kind() {
+ ty::Adt(base_adt, base_subs) if adt == base_adt => {
+ variant
+ .fields
+ .iter()
+ .map(|f| {
+ let fru_ty = self.normalize_associated_types_in(
+ expr_span,
+ self.field_ty(base_expr.span, f, base_subs),
+ );
+ let ident = self.tcx.adjust_ident(f.ident, variant.def_id);
+ if let Some(_) = remaining_fields.remove(&ident) {
+ let target_ty =
+ self.field_ty(base_expr.span, f, substs);
+ let cause = self.misc(base_expr.span);
+ match self
+ .at(&cause, self.param_env)
+ .sup(target_ty, fru_ty)
+ {
+ Ok(InferOk { obligations, value: () }) => {
+ self.register_predicates(obligations)
+ }
+ // FIXME: Need better diagnostics for `FieldMisMatch` error
+ Err(_) => self
+ .report_mismatched_types(
+ &cause,
+ target_ty,
+ fru_ty,
+ FieldMisMatch(
+ variant.ident.name,
+ ident.name,
+ ),
+ )
+ .emit(),
+ }
+ }
+ fru_ty
+ })
+ .collect()
+ }
+ _ => {
+ return self
+ .report_mismatched_types(
+ &self.misc(base_expr.span),
+ adt_ty,
+ base_ty,
+ Sorts(expected_found_bool(true, adt_ty, base_ty)),
+ )
+ .emit();
+ }
+ }
+ }
+ _ => {
+ return self
+ .tcx
+ .sess
+ .emit_err(FunctionalRecordUpdateOnNonStruct { span: base_expr.span });
+ }
+ }
+ } else {
+ self.check_expr_has_type_or_error(base_expr, adt_ty, |_| {
+ let base_ty = self.check_expr(base_expr);
+ let same_adt = match (adt_ty.kind(), base_ty.kind()) {
+ (ty::Adt(adt, _), ty::Adt(base_adt, _)) if adt == base_adt => true,
+ _ => false,
+ };
+ if self.tcx.sess.is_nightly_build() && same_adt {
+ feature_err(
+ &self.tcx.sess.parse_sess,
+ sym::type_changing_struct_update,
+ base_expr.span,
+ "type changing struct updating is experimental",
+ )
+ .emit();
+ }
+ });
+ match adt_ty.kind() {
+ ty::Adt(adt, substs) if adt.is_struct() => variant
+ .fields
+ .iter()
+ .map(|f| {
+ self.normalize_associated_types_in(expr_span, f.ty(self.tcx, substs))
+ })
+ .collect(),
+ _ => {
+ return self
+ .tcx
+ .sess
+ .emit_err(FunctionalRecordUpdateOnNonStruct { span: base_expr.span });
+ }
+ }
+ };
+ self.typeck_results.borrow_mut().fru_field_types_mut().insert(expr_id, fru_tys);
+ } else if kind_name != "union" && !remaining_fields.is_empty() {
let inaccessible_remaining_fields = remaining_fields.iter().any(|(_, (_, field))| {
!field.vis.is_accessible_from(tcx.parent_module(expr_id).to_def_id(), tcx)
});
self.report_missing_fields(adt_ty, span, remaining_fields);
}
}
-
- error_happened
}
fn check_struct_fields_on_error(
.filter_map(|field| {
// ignore already set fields and private fields from non-local crates
if skip.iter().any(|&x| x == field.ident.name)
- || (!variant.def_id.is_local() && field.vis != Visibility::Public)
+ || (!variant.def_id.is_local() && !field.vis.is_public())
{
None
} else {
let mut err = rustc_errors::struct_span_err!(
self.sess(),
self_ty.span,
- E0783,
+ E0782,
"{}",
msg,
);
use rustc_middle::ty::{self, Binder, Ty};
use rustc_span::symbol::{kw, sym};
+use rustc_middle::ty::subst::GenericArgKind;
use std::iter;
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let is_struct_pat_shorthand_field =
self.is_hir_id_from_struct_pattern_shorthand_field(expr.hir_id, expr.span);
let methods = self.get_conversion_methods(expr.span, expected, found, expr.hir_id);
- if let Ok(expr_text) = self.sess().source_map().span_to_snippet(expr.span) {
- let mut suggestions = iter::zip(iter::repeat(&expr_text), &methods)
- .filter_map(|(receiver, method)| {
- let method_call = format!(".{}()", method.ident);
- if receiver.ends_with(&method_call) {
- None // do not suggest code that is already there (#53348)
- } else {
- let method_call_list = [".to_vec()", ".to_string()"];
- let mut sugg = if receiver.ends_with(".clone()")
- && method_call_list.contains(&method_call.as_str())
- {
- let max_len = receiver.rfind('.').unwrap();
- vec![(
- expr.span,
- format!("{}{}", &receiver[..max_len], method_call),
- )]
+ if !methods.is_empty() {
+ if let Ok(expr_text) = self.sess().source_map().span_to_snippet(expr.span) {
+ let mut suggestions = iter::zip(iter::repeat(&expr_text), &methods)
+ .filter_map(|(receiver, method)| {
+ let method_call = format!(".{}()", method.ident);
+ if receiver.ends_with(&method_call) {
+ None // do not suggest code that is already there (#53348)
} else {
- if expr.precedence().order() < ExprPrecedence::MethodCall.order() {
- vec![
- (expr.span.shrink_to_lo(), "(".to_string()),
- (expr.span.shrink_to_hi(), format!("){}", method_call)),
- ]
+ let method_call_list = [".to_vec()", ".to_string()"];
+ let mut sugg = if receiver.ends_with(".clone()")
+ && method_call_list.contains(&method_call.as_str())
+ {
+ let max_len = receiver.rfind('.').unwrap();
+ vec![(
+ expr.span,
+ format!("{}{}", &receiver[..max_len], method_call),
+ )]
} else {
- vec![(expr.span.shrink_to_hi(), method_call)]
+ if expr.precedence().order()
+ < ExprPrecedence::MethodCall.order()
+ {
+ vec![
+ (expr.span.shrink_to_lo(), "(".to_string()),
+ (expr.span.shrink_to_hi(), format!("){}", method_call)),
+ ]
+ } else {
+ vec![(expr.span.shrink_to_hi(), method_call)]
+ }
+ };
+ if is_struct_pat_shorthand_field {
+ sugg.insert(
+ 0,
+ (expr.span.shrink_to_lo(), format!("{}: ", receiver)),
+ );
}
- };
- if is_struct_pat_shorthand_field {
- sugg.insert(
- 0,
- (expr.span.shrink_to_lo(), format!("{}: ", receiver)),
+ Some(sugg)
+ }
+ })
+ .peekable();
+ if suggestions.peek().is_some() {
+ err.multipart_suggestions(
+ "try using a conversion method",
+ suggestions,
+ Applicability::MaybeIncorrect,
+ );
+ }
+ }
+ } else if found.to_string().starts_with("Option<")
+ && expected.to_string() == "Option<&str>"
+ {
+ if let ty::Adt(_def, subst) = found.kind() {
+ if subst.len() != 0 {
+ if let GenericArgKind::Type(ty) = subst[0].unpack() {
+ let peeled = ty.peel_refs().to_string();
+ if peeled == "String" {
+ let ref_cnt = ty.to_string().len() - peeled.len();
+ let result = format!(".map(|x| &*{}x)", "*".repeat(ref_cnt));
+ err.span_suggestion_verbose(
+ expr.span.shrink_to_hi(),
+ "try converting the passed type into a `&str`",
+ result,
+ Applicability::MaybeIncorrect,
);
}
- Some(sugg)
}
- })
- .peekable();
- if suggestions.peek().is_some() {
- err.multipart_suggestions(
- "try using a conversion method",
- suggestions,
- Applicability::MaybeIncorrect,
- );
+ }
}
}
}
}
}
// We only want to suggest public or local traits (#45781).
- item.vis == ty::Visibility::Public || info.def_id.is_local()
+ item.vis.is_public() || info.def_id.is_local()
})
.is_some()
})
fn has_typeck_results(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
// Closures' typeck results come from their outermost function,
// as they are part of the same "inference environment".
- let outer_def_id = tcx.closure_base_def_id(def_id);
- if outer_def_id != def_id {
- return tcx.has_typeck_results(outer_def_id);
+ let typeck_root_def_id = tcx.typeck_root_def_id(def_id);
+ if typeck_root_def_id != def_id {
+ return tcx.has_typeck_results(typeck_root_def_id);
}
if let Some(def_id) = def_id.as_local() {
) -> &'tcx ty::TypeckResults<'tcx> {
// Closures' typeck results come from their outermost function,
// as they are part of the same "inference environment".
- let outer_def_id = tcx.closure_base_def_id(def_id.to_def_id()).expect_local();
- if outer_def_id != def_id {
- return tcx.typeck(outer_def_id);
+ let typeck_root_def_id = tcx.typeck_root_def_id(def_id.to_def_id()).expect_local();
+ if typeck_root_def_id != def_id {
+ return tcx.typeck(typeck_root_def_id);
}
let id = tcx.hir().local_def_id_to_hir_id(def_id);
// String and byte-string literals result in types `&str` and `&[u8]` respectively.
// All other literals result in non-reference types.
// As a result, we allow `if let 0 = &&0 {}` but not `if let "foo" = &&"foo {}`.
- PatKind::Lit(lt) => match self.check_expr(lt).kind() {
+ //
+ // Call `resolve_vars_if_possible` here for inline const blocks.
+ PatKind::Lit(lt) => match self.resolve_vars_if_possible(self.check_expr(lt)).kind() {
ty::Ref(..) => AdjustMode::Pass,
_ => AdjustMode::Peel,
},
self.visit_region_obligations(body_id.hir_id);
}
+ fn visit_inline_const(&mut self, id: hir::HirId, body: &'tcx hir::Body<'tcx>) {
+ debug!("visit_inline_const(id={:?})", id);
+
+ // Save state of current function. We will restore afterwards.
+ let old_body_id = self.body_id;
+ let old_body_owner = self.body_owner;
+ let env_snapshot = self.outlives_environment.push_snapshot_pre_typeck_child();
+
+ let body_id = body.id();
+ self.body_id = body_id.hir_id;
+ self.body_owner = self.tcx.hir().body_owner_def_id(body_id);
+
+ self.outlives_environment.save_implied_bounds(body_id.hir_id);
+
+ self.visit_body(body);
+ self.visit_region_obligations(body_id.hir_id);
+
+ // Restore state from previous function.
+ self.outlives_environment.pop_snapshot_post_typeck_child(env_snapshot);
+ self.body_id = old_body_id;
+ self.body_owner = old_body_owner;
+ }
+
fn visit_region_obligations(&mut self, hir_id: hir::HirId) {
debug!("visit_region_obligations: hir_id={:?}", hir_id);
// `visit_fn_body`. We will restore afterwards.
let old_body_id = self.body_id;
let old_body_owner = self.body_owner;
- let env_snapshot = self.outlives_environment.push_snapshot_pre_closure();
+ let env_snapshot = self.outlives_environment.push_snapshot_pre_typeck_child();
let body = self.tcx.hir().body(body_id);
self.visit_fn_body(hir_id, body, span);
// Restore state from previous function.
- self.outlives_environment.pop_snapshot_post_closure(env_snapshot);
+ self.outlives_environment.pop_snapshot_post_typeck_child(env_snapshot);
self.body_id = old_body_id;
self.body_owner = old_body_owner;
}
intravisit::walk_expr(self, expr);
}
+ hir::ExprKind::ConstBlock(anon_const) => {
+ let body = self.tcx.hir().body(anon_const.body);
+ self.visit_inline_const(anon_const.hir_id, body);
+ }
+
_ => intravisit::walk_expr(self, expr),
}
}
}
fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
- if let hir::ExprKind::Closure(cc, _, body_id, _, _) = expr.kind {
- let body = self.fcx.tcx.hir().body(body_id);
- self.visit_body(body);
- self.fcx.analyze_closure(expr.hir_id, expr.span, body_id, body, cc);
+ match expr.kind {
+ hir::ExprKind::Closure(cc, _, body_id, _, _) => {
+ let body = self.fcx.tcx.hir().body(body_id);
+ self.visit_body(body);
+ self.fcx.analyze_closure(expr.hir_id, expr.span, body_id, body, cc);
+ }
+ hir::ExprKind::ConstBlock(anon_const) => {
+ let body = self.fcx.tcx.hir().body(anon_const.body);
+ self.visit_body(body);
+ }
+ _ => {}
}
intravisit::walk_expr(self, expr);
hir::ExprKind::Field(..) => {
self.visit_field_id(e.hir_id);
}
+ hir::ExprKind::ConstBlock(anon_const) => {
+ self.visit_node_id(e.span, anon_const.hir_id);
+
+ let body = self.tcx().hir().body(anon_const.body);
+ self.visit_body(body);
+ }
_ => {}
}
use self::builtin::coerce_unsized_info;
use self::inherent_impls::{crate_inherent_impls, inherent_impls};
use self::inherent_impls_overlap::crate_inherent_impls_overlap_check;
+ use self::orphan::orphan_check_crate;
*providers = Providers {
coherent_trait,
inherent_impls,
crate_inherent_impls_overlap_check,
coerce_unsized_info,
+ orphan_check_crate,
..*providers
};
}
}
pub fn check_coherence(tcx: TyCtxt<'_>) {
+ tcx.sess.time("unsafety_checking", || unsafety::check(tcx));
+ tcx.ensure().orphan_check_crate(());
+
for &trait_def_id in tcx.all_local_trait_impls(()).keys() {
tcx.ensure().coherent_trait(trait_def_id);
}
- tcx.sess.time("unsafety_checking", || unsafety::check(tcx));
- tcx.sess.time("orphan_checking", || orphan::check(tcx));
-
// these queries are executed for side-effects (error reporting):
tcx.ensure().crate_inherent_impls(());
tcx.ensure().crate_inherent_impls_overlap_check(());
//! crate or pertains to a type defined in this crate.
use rustc_errors::struct_span_err;
+use rustc_errors::ErrorReported;
use rustc_hir as hir;
-use rustc_hir::itemlikevisit::ItemLikeVisitor;
use rustc_infer::infer::TyCtxtInferExt;
use rustc_middle::ty::{self, TyCtxt};
+use rustc_span::def_id::LocalDefId;
+use rustc_span::Span;
use rustc_trait_selection::traits;
-pub fn check(tcx: TyCtxt<'_>) {
- let mut orphan = OrphanChecker { tcx };
- tcx.hir().visit_all_item_likes(&mut orphan);
+pub(super) fn orphan_check_crate(tcx: TyCtxt<'_>, (): ()) -> &[LocalDefId] {
+ let mut errors = Vec::new();
+ for (_trait, impls_of_trait) in tcx.all_local_trait_impls(()) {
+ for &impl_of_trait in impls_of_trait {
+ match orphan_check_impl(tcx, impl_of_trait) {
+ Ok(()) => {}
+ Err(ErrorReported) => errors.push(impl_of_trait),
+ }
+ }
+ }
+ tcx.arena.alloc_slice(&errors)
}
-struct OrphanChecker<'tcx> {
- tcx: TyCtxt<'tcx>,
-}
+#[instrument(skip(tcx), level = "debug")]
+fn orphan_check_impl(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), ErrorReported> {
+ let trait_ref = tcx.impl_trait_ref(def_id).unwrap();
+ let trait_def_id = trait_ref.def_id;
-impl ItemLikeVisitor<'v> for OrphanChecker<'tcx> {
- /// Checks exactly one impl for orphan rules and other such
- /// restrictions. In this fn, it can happen that multiple errors
- /// apply to a specific impl, so just return after reporting one
- /// to prevent inundating the user with a bunch of similar error
- /// reports.
- fn visit_item(&mut self, item: &hir::Item<'_>) {
- // "Trait" impl
- if let hir::ItemKind::Impl(hir::Impl {
- generics, of_trait: Some(ref tr), self_ty, ..
- }) = &item.kind
- {
- debug!(
- "coherence2::orphan check: trait impl {}",
- self.tcx.hir().node_to_string(item.hir_id())
- );
- let trait_ref = self.tcx.impl_trait_ref(item.def_id).unwrap();
- let trait_def_id = trait_ref.def_id;
- let sm = self.tcx.sess.source_map();
- let sp = sm.guess_head_span(item.span);
- match traits::orphan_check(self.tcx, item.def_id.to_def_id()) {
- Ok(()) => {}
- Err(traits::OrphanCheckErr::NonLocalInputType(tys)) => {
- let mut err = struct_span_err!(
- self.tcx.sess,
- sp,
- E0117,
- "only traits defined in the current crate can be implemented for \
- arbitrary types"
- );
- err.span_label(sp, "impl doesn't use only types from inside the current crate");
- for (ty, is_target_ty) in &tys {
- let mut ty = *ty;
- self.tcx.infer_ctxt().enter(|infcx| {
- // Remove the lifetimes unnecessary for this error.
- ty = infcx.freshen(ty);
- });
- ty = match ty.kind() {
- // Remove the type arguments from the output, as they are not relevant.
- // You can think of this as the reverse of `resolve_vars_if_possible`.
- // That way if we had `Vec<MyType>`, we will properly attribute the
- // problem to `Vec<T>` and avoid confusing the user if they were to see
- // `MyType` in the error.
- ty::Adt(def, _) => self.tcx.mk_adt(def, ty::List::empty()),
- _ => ty,
- };
- let this = "this".to_string();
- let (ty, postfix) = match &ty.kind() {
- ty::Slice(_) => (this, " because slices are always foreign"),
- ty::Array(..) => (this, " because arrays are always foreign"),
- ty::Tuple(..) => (this, " because tuples are always foreign"),
- _ => (format!("`{}`", ty), ""),
- };
- let msg = format!("{} is not defined in the current crate{}", ty, postfix);
- if *is_target_ty {
- // Point at `D<A>` in `impl<A, B> for C<B> in D<A>`
- err.span_label(self_ty.span, &msg);
- } else {
- // Point at `C<B>` in `impl<A, B> for C<B> in D<A>`
- err.span_label(tr.path.span, &msg);
- }
- }
- err.note("define and implement a trait or new type instead");
- err.emit();
- return;
- }
- Err(traits::OrphanCheckErr::UncoveredTy(param_ty, local_type)) => {
- let mut sp = sp;
- for param in generics.params {
- if param.name.ident().to_string() == param_ty.to_string() {
- sp = param.span;
- }
- }
+ let item = tcx.hir().item(hir::ItemId { def_id });
+ let impl_ = match item.kind {
+ hir::ItemKind::Impl(ref impl_) => impl_,
+ _ => bug!("{:?} is not an impl: {:?}", def_id, item),
+ };
+ let sp = tcx.sess.source_map().guess_head_span(item.span);
+ let tr = impl_.of_trait.as_ref().unwrap();
+ match traits::orphan_check(tcx, item.def_id.to_def_id()) {
+ Ok(()) => {}
+ Err(err) => emit_orphan_check_error(
+ tcx,
+ sp,
+ tr.path.span,
+ impl_.self_ty.span,
+ &impl_.generics,
+ err,
+ )?,
+ }
+
+ // In addition to the above rules, we restrict impls of auto traits
+ // so that they can only be implemented on nominal types, such as structs,
+ // enums or foreign types. To see why this restriction exists, consider the
+ // following example (#22978). Imagine that crate A defines an auto trait
+ // `Foo` and a fn that operates on pairs of types:
+ //
+ // ```
+ // // Crate A
+ // auto trait Foo { }
+ // fn two_foos<A:Foo,B:Foo>(..) {
+ // one_foo::<(A,B)>(..)
+ // }
+ // fn one_foo<T:Foo>(..) { .. }
+ // ```
+ //
+ // This type-checks fine; in particular the fn
+ // `two_foos` is able to conclude that `(A,B):Foo`
+ // because `A:Foo` and `B:Foo`.
+ //
+ // Now imagine that crate B comes along and does the following:
+ //
+ // ```
+ // struct A { }
+ // struct B { }
+ // impl Foo for A { }
+ // impl Foo for B { }
+ // impl !Send for (A, B) { }
+ // ```
+ //
+ // This final impl is legal according to the orphan
+ // rules, but it invalidates the reasoning from
+ // `two_foos` above.
+ debug!(
+ "trait_ref={:?} trait_def_id={:?} trait_is_auto={}",
+ trait_ref,
+ trait_def_id,
+ tcx.trait_is_auto(trait_def_id)
+ );
+
+ if tcx.trait_is_auto(trait_def_id) && !trait_def_id.is_local() {
+ let self_ty = trait_ref.self_ty();
+ let opt_self_def_id = match *self_ty.kind() {
+ ty::Adt(self_def, _) => Some(self_def.did),
+ ty::Foreign(did) => Some(did),
+ _ => None,
+ };
- match local_type {
- Some(local_type) => {
- struct_span_err!(
- self.tcx.sess,
- sp,
- E0210,
- "type parameter `{}` must be covered by another type \
- when it appears before the first local type (`{}`)",
- param_ty,
- local_type
- )
- .span_label(
- sp,
- format!(
- "type parameter `{}` must be covered by another type \
- when it appears before the first local type (`{}`)",
- param_ty, local_type
- ),
- )
- .note(
- "implementing a foreign trait is only possible if at \
- least one of the types for which it is implemented is local, \
- and no uncovered type parameters appear before that first \
- local type",
- )
- .note(
- "in this case, 'before' refers to the following order: \
- `impl<..> ForeignTrait<T1, ..., Tn> for T0`, \
- where `T0` is the first and `Tn` is the last",
- )
- .emit();
- }
- None => {
- struct_span_err!(
- self.tcx.sess,
- sp,
- E0210,
- "type parameter `{}` must be used as the type parameter for some \
- local type (e.g., `MyStruct<{}>`)",
- param_ty,
- param_ty
- ).span_label(sp, format!(
- "type parameter `{}` must be used as the type parameter for some \
- local type",
- param_ty,
- )).note("implementing a foreign trait is only possible if at \
- least one of the types for which it is implemented is local"
- ).note("only traits defined in the current crate can be \
- implemented for a type parameter"
- ).emit();
- }
- };
- return;
+ let msg = match opt_self_def_id {
+ // We only want to permit nominal types, but not *all* nominal types.
+ // They must be local to the current crate, so that people
+ // can't do `unsafe impl Send for Rc<SomethingLocal>` or
+ // `impl !Send for Box<SomethingLocalAndSend>`.
+ Some(self_def_id) => {
+ if self_def_id.is_local() {
+ None
+ } else {
+ Some((
+ format!(
+ "cross-crate traits with a default impl, like `{}`, \
+ can only be implemented for a struct/enum type \
+ defined in the current crate",
+ tcx.def_path_str(trait_def_id)
+ ),
+ "can't implement cross-crate trait for type in another crate",
+ ))
}
}
+ _ => Some((
+ format!(
+ "cross-crate traits with a default impl, like `{}`, can \
+ only be implemented for a struct/enum type, not `{}`",
+ tcx.def_path_str(trait_def_id),
+ self_ty
+ ),
+ "can't implement cross-crate trait with a default impl for \
+ non-struct/enum type",
+ )),
+ };
- // In addition to the above rules, we restrict impls of auto traits
- // so that they can only be implemented on nominal types, such as structs,
- // enums or foreign types. To see why this restriction exists, consider the
- // following example (#22978). Imagine that crate A defines an auto trait
- // `Foo` and a fn that operates on pairs of types:
- //
- // ```
- // // Crate A
- // auto trait Foo { }
- // fn two_foos<A:Foo,B:Foo>(..) {
- // one_foo::<(A,B)>(..)
- // }
- // fn one_foo<T:Foo>(..) { .. }
- // ```
- //
- // This type-checks fine; in particular the fn
- // `two_foos` is able to conclude that `(A,B):Foo`
- // because `A:Foo` and `B:Foo`.
- //
- // Now imagine that crate B comes along and does the following:
- //
- // ```
- // struct A { }
- // struct B { }
- // impl Foo for A { }
- // impl Foo for B { }
- // impl !Send for (A, B) { }
- // ```
- //
- // This final impl is legal according to the orphan
- // rules, but it invalidates the reasoning from
- // `two_foos` above.
- debug!(
- "trait_ref={:?} trait_def_id={:?} trait_is_auto={}",
- trait_ref,
- trait_def_id,
- self.tcx.trait_is_auto(trait_def_id)
+ if let Some((msg, label)) = msg {
+ struct_span_err!(tcx.sess, sp, E0321, "{}", msg).span_label(sp, label).emit();
+ return Err(ErrorReported);
+ }
+ }
+
+ if let ty::Opaque(def_id, _) = *trait_ref.self_ty().kind() {
+ tcx.sess
+ .struct_span_err(sp, "cannot implement trait on type alias impl trait")
+ .span_note(tcx.def_span(def_id), "type alias impl trait defined here")
+ .emit();
+ return Err(ErrorReported);
+ }
+
+ Ok(())
+}
+
+fn emit_orphan_check_error(
+ tcx: TyCtxt<'tcx>,
+ sp: Span,
+ trait_span: Span,
+ self_ty_span: Span,
+ generics: &hir::Generics<'tcx>,
+ err: traits::OrphanCheckErr<'tcx>,
+) -> Result<!, ErrorReported> {
+ match err {
+ traits::OrphanCheckErr::NonLocalInputType(tys) => {
+ let mut err = struct_span_err!(
+ tcx.sess,
+ sp,
+ E0117,
+ "only traits defined in the current crate can be implemented for \
+ arbitrary types"
);
- if self.tcx.trait_is_auto(trait_def_id) && !trait_def_id.is_local() {
- let self_ty = trait_ref.self_ty();
- let opt_self_def_id = match *self_ty.kind() {
- ty::Adt(self_def, _) => Some(self_def.did),
- ty::Foreign(did) => Some(did),
- _ => None,
+ err.span_label(sp, "impl doesn't use only types from inside the current crate");
+ for (ty, is_target_ty) in &tys {
+ let mut ty = *ty;
+ tcx.infer_ctxt().enter(|infcx| {
+ // Remove the lifetimes unnecessary for this error.
+ ty = infcx.freshen(ty);
+ });
+ ty = match ty.kind() {
+ // Remove the type arguments from the output, as they are not relevant.
+ // You can think of this as the reverse of `resolve_vars_if_possible`.
+ // That way if we had `Vec<MyType>`, we will properly attribute the
+ // problem to `Vec<T>` and avoid confusing the user if they were to see
+ // `MyType` in the error.
+ ty::Adt(def, _) => tcx.mk_adt(def, ty::List::empty()),
+ _ => ty,
};
-
- let msg = match opt_self_def_id {
- // We only want to permit nominal types, but not *all* nominal types.
- // They must be local to the current crate, so that people
- // can't do `unsafe impl Send for Rc<SomethingLocal>` or
- // `impl !Send for Box<SomethingLocalAndSend>`.
- Some(self_def_id) => {
- if self_def_id.is_local() {
- None
- } else {
- Some((
- format!(
- "cross-crate traits with a default impl, like `{}`, \
- can only be implemented for a struct/enum type \
- defined in the current crate",
- self.tcx.def_path_str(trait_def_id)
- ),
- "can't implement cross-crate trait for type in another crate",
- ))
- }
- }
- _ => Some((
- format!(
- "cross-crate traits with a default impl, like `{}`, can \
- only be implemented for a struct/enum type, not `{}`",
- self.tcx.def_path_str(trait_def_id),
- self_ty
- ),
- "can't implement cross-crate trait with a default impl for \
- non-struct/enum type",
- )),
+ let this = "this".to_string();
+ let (ty, postfix) = match &ty.kind() {
+ ty::Slice(_) => (this, " because slices are always foreign"),
+ ty::Array(..) => (this, " because arrays are always foreign"),
+ ty::Tuple(..) => (this, " because tuples are always foreign"),
+ _ => (format!("`{}`", ty), ""),
};
-
- if let Some((msg, label)) = msg {
- struct_span_err!(self.tcx.sess, sp, E0321, "{}", msg)
- .span_label(sp, label)
- .emit();
- return;
+ let msg = format!("{} is not defined in the current crate{}", ty, postfix);
+ if *is_target_ty {
+ // Point at `D<A>` in `impl<A, B> for C<B> in D<A>`
+ err.span_label(self_ty_span, &msg);
+ } else {
+ // Point at `C<B>` in `impl<A, B> for C<B> in D<A>`
+ err.span_label(trait_span, &msg);
+ }
+ }
+ err.note("define and implement a trait or new type instead");
+ err.emit()
+ }
+ traits::OrphanCheckErr::UncoveredTy(param_ty, local_type) => {
+ let mut sp = sp;
+ for param in generics.params {
+ if param.name.ident().to_string() == param_ty.to_string() {
+ sp = param.span;
}
}
- if let ty::Opaque(def_id, _) = *trait_ref.self_ty().kind() {
- self.tcx
- .sess
- .struct_span_err(sp, "cannot implement trait on type alias impl trait")
- .span_note(self.tcx.def_span(def_id), "type alias impl trait defined here")
- .emit();
+ match local_type {
+ Some(local_type) => struct_span_err!(
+ tcx.sess,
+ sp,
+ E0210,
+ "type parameter `{}` must be covered by another type \
+ when it appears before the first local type (`{}`)",
+ param_ty,
+ local_type
+ )
+ .span_label(
+ sp,
+ format!(
+ "type parameter `{}` must be covered by another type \
+ when it appears before the first local type (`{}`)",
+ param_ty, local_type
+ ),
+ )
+ .note(
+ "implementing a foreign trait is only possible if at \
+ least one of the types for which it is implemented is local, \
+ and no uncovered type parameters appear before that first \
+ local type",
+ )
+ .note(
+ "in this case, 'before' refers to the following order: \
+ `impl<..> ForeignTrait<T1, ..., Tn> for T0`, \
+ where `T0` is the first and `Tn` is the last",
+ )
+ .emit(),
+ None => struct_span_err!(
+ tcx.sess,
+ sp,
+ E0210,
+ "type parameter `{}` must be used as the type parameter for some \
+ local type (e.g., `MyStruct<{}>`)",
+ param_ty,
+ param_ty
+ )
+ .span_label(
+ sp,
+ format!(
+ "type parameter `{}` must be used as the type parameter for some \
+ local type",
+ param_ty,
+ ),
+ )
+ .note(
+ "implementing a foreign trait is only possible if at \
+ least one of the types for which it is implemented is local",
+ )
+ .note(
+ "only traits defined in the current crate can be \
+ implemented for a type parameter",
+ )
+ .emit(),
}
}
}
- fn visit_trait_item(&mut self, _trait_item: &hir::TraitItem<'_>) {}
-
- fn visit_impl_item(&mut self, _impl_item: &hir::ImplItem<'_>) {}
-
- fn visit_foreign_item(&mut self, _foreign_item: &hir::ForeignItem<'_>) {}
+ Err(ErrorReported)
}
{
Some(parent_def_id.to_def_id())
}
-
+ Node::Expr(&Expr { kind: ExprKind::ConstBlock(_), .. }) => {
+ Some(tcx.typeck_root_def_id(def_id))
+ }
_ => None,
}
}
}
Node::Expr(&hir::Expr { kind: hir::ExprKind::Closure(..), .. }) => {
- Some(tcx.closure_base_def_id(def_id))
+ Some(tcx.typeck_root_def_id(def_id))
}
Node::Item(item) => match item.kind {
ItemKind::OpaqueTy(hir::OpaqueTy { impl_trait_fn, .. }) => {
}));
}
+ // provide junk type parameter defs for const blocks.
+ if let Node::AnonConst(_) = node {
+ let parent_node = tcx.hir().get(tcx.hir().get_parent_node(hir_id));
+ if let Node::Expr(&Expr { kind: ExprKind::ConstBlock(_), .. }) = parent_node {
+ params.push(ty::GenericParamDef {
+ index: type_start,
+ name: Symbol::intern("<const_ty>"),
+ def_id,
+ pure_wrt_drop: false,
+ kind: ty::GenericParamDefKind::Type {
+ has_default: false,
+ object_lifetime_default: rl::Set1::Empty,
+ synthetic: None,
+ },
+ });
+ }
+ }
+
let param_def_id_to_index = params.iter().map(|param| (param.def_id, param.index)).collect();
ty::Generics {
Node::Expr(&Expr { kind: ExprKind::ConstBlock(ref anon_const), .. })
if anon_const.hir_id == hir_id =>
{
- tcx.typeck(def_id).node_type(anon_const.hir_id)
+ let substs = InternalSubsts::identity_for_item(tcx, def_id.to_def_id());
+ substs.as_inline_const().ty()
}
Node::Expr(&Expr { kind: ExprKind::InlineAsm(asm), .. })
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![feature(bool_to_option)]
#![feature(crate_visibility_modifier)]
-#![feature(format_args_capture)]
+#![cfg_attr(bootstrap, feature(format_args_capture))]
#![feature(if_let_guard)]
#![feature(in_band_lifetimes)]
#![feature(is_sorted)]
#[lang = "owned_box"]
#[fundamental]
#[stable(feature = "rust1", since = "1.0.0")]
+// The declaration of the `Box` struct must be kept in sync with the
+// `alloc::alloc::box_free` function or ICEs will happen. See the comment
+// on `box_free` for more details.
pub struct Box<
T: ?Sized,
#[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global,
}
}
+impl<T: Ord> SpecExtend<Vec<T>> for BinaryHeap<T> {
+ fn spec_extend(&mut self, ref mut other: Vec<T>) {
+ let start = self.data.len();
+ self.data.append(other);
+ self.rebuild_tail(start);
+ }
+}
+
impl<T: Ord> SpecExtend<BinaryHeap<T>> for BinaryHeap<T> {
fn spec_extend(&mut self, ref mut other: BinaryHeap<T>) {
self.append(other);
//! format!("The number is {}", 1); // => "The number is 1"
//! format!("{:?}", (3, 4)); // => "(3, 4)"
//! format!("{value}", value=4); // => "4"
+//! let people = "Rustaceans";
+//! format!("Hello {people}!"); // => "Hello Rustaceans!"
//! format!("{} {}", 1, 2); // => "1 2"
//! format!("{:04}", 42); // => "0042" with leading zeros
//! format!("{:#?}", (100, 200)); // => "(
//! format!("{a} {c} {b}", a="a", b='b', c=3); // => "a 3 b"
//! ```
//!
+//! If a named parameter does not appear in the argument list, `format!` will
+//! reference a variable with that name in the current scope.
+//!
+//! ```
+//! let argument = 2 + 2;
+//! format!("{argument}"); // => "4"
+//!
+//! fn make_string(a: u32, b: &str) -> String {
+//! format!("{b} {a}")
+//! }
+//! make_string(927, "label"); // => "label 927"
+//! ```
+//!
//! It is not valid to put positional parameters (those without names) after
//! arguments that have names. Like with positional parameters, it is not
//! valid to provide named parameters that are unused by the format string.
//! println!("Hello {:1$}!", "x", 5);
//! println!("Hello {1:0$}!", 5, "x");
//! println!("Hello {:width$}!", "x", width = 5);
+//! let width = 5;
+//! println!("Hello {:width$}!", "x");
//! ```
//!
//! This is a parameter for the "minimum width" that the format should take up.
#![feature(fmt_internals)]
#![feature(fn_traits)]
#![feature(inherent_ascii_escape)]
+#![cfg_attr(bootstrap, feature(format_args_capture))]
#![feature(inplace_iteration)]
#![feature(iter_advance_by)]
#![feature(iter_zip)]
#[macro_use]
mod macros;
+mod raw_vec;
+
// Heaps provided for low-level allocation strategies
pub mod alloc;
pub mod borrow;
pub mod collections;
pub mod fmt;
-pub mod raw_vec;
pub mod rc;
pub mod slice;
pub mod str;
-#![unstable(feature = "raw_vec_internals", reason = "implementation detail", issue = "none")]
-#![doc(hidden)]
+#![unstable(feature = "raw_vec_internals", reason = "unstable const warnings", issue = "none")]
use core::alloc::LayoutError;
use core::cmp;
/// `usize::MAX`. This means that you need to be careful when round-tripping this type with a
/// `Box<[T]>`, since `capacity()` won't yield the length.
#[allow(missing_debug_implementations)]
-pub struct RawVec<T, A: Allocator = Global> {
+pub(crate) struct RawVec<T, A: Allocator = Global> {
ptr: Unique<T>,
cap: usize,
alloc: A,
/// # Aborts
///
/// Aborts on OOM.
- #[cfg(not(no_global_oom_handling))]
+ #[cfg(not(any(no_global_oom_handling, test)))]
#[must_use]
#[inline]
pub fn with_capacity(capacity: usize) -> Self {
}
/// Like `with_capacity`, but guarantees the buffer is zeroed.
- #[cfg(not(no_global_oom_handling))]
+ #[cfg(not(any(no_global_oom_handling, test)))]
#[must_use]
#[inline]
pub fn with_capacity_zeroed(capacity: usize) -> Self {
Self::with_capacity_zeroed_in(capacity, Global)
}
-
- /// Reconstitutes a `RawVec` from a pointer and capacity.
- ///
- /// # Safety
- ///
- /// The `ptr` must be allocated (on the system heap), and with the given `capacity`.
- /// The `capacity` cannot exceed `isize::MAX` for sized types. (only a concern on 32-bit
- /// systems). ZST vectors may have a capacity up to `usize::MAX`.
- /// If the `ptr` and `capacity` come from a `RawVec`, then this is guaranteed.
- #[inline]
- pub unsafe fn from_raw_parts(ptr: *mut T, capacity: usize) -> Self {
- unsafe { Self::from_raw_parts_in(ptr, capacity, Global) }
- }
}
impl<T, A: Allocator> RawVec<T, A> {
Self::allocate_in(capacity, AllocInit::Zeroed, alloc)
}
- /// Converts a `Box<[T]>` into a `RawVec<T>`.
- pub fn from_box(slice: Box<[T], A>) -> Self {
- unsafe {
- let (slice, alloc) = Box::into_raw_with_allocator(slice);
- RawVec::from_raw_parts_in(slice.as_mut_ptr(), slice.len(), alloc)
- }
- }
-
/// Converts the entire buffer into `Box<[MaybeUninit<T>]>` with the specified `len`.
///
/// Note that this will correctly reconstitute any `cap` changes
/// # Aborts
///
/// Aborts on OOM.
- ///
- /// # Examples
- ///
- /// ```
- /// # #![feature(raw_vec_internals)]
- /// # extern crate alloc;
- /// # use std::ptr;
- /// # use alloc::raw_vec::RawVec;
- /// struct MyVec<T> {
- /// buf: RawVec<T>,
- /// len: usize,
- /// }
- ///
- /// impl<T: Clone> MyVec<T> {
- /// pub fn push_all(&mut self, elems: &[T]) {
- /// self.buf.reserve(self.len, elems.len());
- /// // reserve would have aborted or panicked if the len exceeded
- /// // `isize::MAX` so this is safe to do unchecked now.
- /// for x in elems {
- /// unsafe {
- /// ptr::write(self.buf.ptr().add(self.len), x.clone());
- /// }
- /// self.len += 1;
- /// }
- /// }
- /// }
- /// # fn main() {
- /// # let mut vector = MyVec { buf: RawVec::new(), len: 0 };
- /// # vector.push_all(&[1, 3, 5, 7, 9]);
- /// # }
- /// ```
#[cfg(not(no_global_oom_handling))]
#[inline]
pub fn reserve(&mut self, len: usize, additional: usize) {
}
}
+#[cfg(not(bootstrap))]
+#[stable(feature = "copy_clone_array_lib", since = "1.58.0")]
+impl<T: Copy, const N: usize> Copy for [T; N] {}
+
+#[cfg(not(bootstrap))]
+#[stable(feature = "copy_clone_array_lib", since = "1.58.0")]
+impl<T: Clone, const N: usize> Clone for [T; N] {
+ #[inline]
+ fn clone(&self) -> Self {
+ SpecArrayClone::clone(self)
+ }
+
+ #[inline]
+ fn clone_from(&mut self, other: &Self) {
+ self.clone_from_slice(other);
+ }
+}
+
+#[cfg(not(bootstrap))]
+trait SpecArrayClone: Clone {
+ fn clone<const N: usize>(array: &[Self; N]) -> [Self; N];
+}
+
+#[cfg(not(bootstrap))]
+impl<T: Clone> SpecArrayClone for T {
+ #[inline]
+ default fn clone<const N: usize>(array: &[T; N]) -> [T; N] {
+ // SAFETY: we know for certain that this iterator will yield exactly `N`
+ // items.
+ unsafe { collect_into_array_unchecked(&mut array.iter().cloned()) }
+ }
+}
+
+#[cfg(not(bootstrap))]
+impl<T: Copy> SpecArrayClone for T {
+ #[inline]
+ fn clone<const N: usize>(array: &[T; N]) -> [T; N] {
+ *array
+ }
+}
+
// The Default impls cannot be done with const generics because `[T; 0]` doesn't
// require Default to be implemented, and having different impl blocks for
// different numbers isn't supported yet.
///
/// * Function item types (i.e., the distinct types defined for each function)
/// * Function pointer types (e.g., `fn() -> i32`)
-/// * Array types, for all sizes, if the item type also implements `Clone` (e.g., `[i32; 123456]`)
/// * Tuple types, if each component also implements `Clone` (e.g., `()`, `(i32, bool)`)
/// * Closure types, if they capture no value from the environment
/// or if all such captured values implement `Clone` themselves.
!ptr.is_null() && ptr as usize % mem::align_of::<T>() == 0
}
+/// Checks whether the regions of memory starting at `src` and `dst` of size
+/// `count * size_of::<T>()` do *not* overlap.
+#[cfg(debug_assertions)]
+pub(crate) fn is_nonoverlapping<T>(src: *const T, dst: *const T, count: usize) -> bool {
+ let src_usize = src as usize;
+ let dst_usize = dst as usize;
+ let size = mem::size_of::<T>().checked_mul(count).unwrap();
+ let diff = if src_usize > dst_usize { src_usize - dst_usize } else { dst_usize - src_usize };
+ // If the absolute distance between the ptrs is at least as big as the size of the buffer,
+ // they do not overlap.
+ diff >= size
+}
+
/// Copies `count * size_of::<T>()` bytes from `src` to `dst`. The source
/// and destination must *not* overlap.
///
pub fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: usize);
}
- // FIXME: Perform these checks only at run time
- /*if cfg!(debug_assertions)
- && !(is_aligned_and_not_null(src)
- && is_aligned_and_not_null(dst)
- && is_nonoverlapping(src, dst, count))
- {
- // Not panicking to keep codegen impact smaller.
- abort();
- }*/
+ #[cfg(debug_assertions)]
+ fn runtime_check<T>(src: *const T, dst: *mut T, count: usize) {
+ if !is_aligned_and_not_null(src)
+ || !is_aligned_and_not_null(dst)
+ || !is_nonoverlapping(src, dst, count)
+ {
+ // Not panicking to keep codegen impact smaller.
+ abort();
+ }
+ }
+ #[cfg(debug_assertions)]
+ const fn compiletime_check<T>(_src: *const T, _dst: *mut T, _count: usize) {}
+ #[cfg(debug_assertions)]
+ // SAFETY: runtime debug-assertions are a best-effort basis; it's fine to
+ // not do them during compile time
+ unsafe {
+ const_eval_select((src, dst, count), compiletime_check, runtime_check);
+ }
// SAFETY: the safety contract for `copy_nonoverlapping` must be
// upheld by the caller.
fn copy<T>(src: *const T, dst: *mut T, count: usize);
}
- // FIXME: Perform these checks only at run time
- /*if cfg!(debug_assertions) && !(is_aligned_and_not_null(src) && is_aligned_and_not_null(dst)) {
- // Not panicking to keep codegen impact smaller.
- abort();
- }*/
+ #[cfg(debug_assertions)]
+ fn runtime_check<T>(src: *const T, dst: *mut T) {
+ if !is_aligned_and_not_null(src) || !is_aligned_and_not_null(dst) {
+ // Not panicking to keep codegen impact smaller.
+ abort();
+ }
+ }
+ #[cfg(debug_assertions)]
+ const fn compiletime_check<T>(_src: *const T, _dst: *mut T) {}
+ #[cfg(debug_assertions)]
+ // SAFETY: runtime debug-assertions are a best-effort basis; it's fine to
+ // not do them during compile time
+ unsafe {
+ const_eval_select((src, dst), compiletime_check, runtime_check);
+ }
// SAFETY: the safety contract for `copy` must be upheld by the caller.
unsafe { copy(src, dst, count) }
#![feature(const_caller_location)]
#![feature(const_cell_into_inner)]
#![feature(const_discriminant)]
-#![cfg_attr(not(bootstrap), feature(const_eval_select))]
+#![feature(const_eval_select)]
#![feature(const_float_bits_conv)]
#![feature(const_float_classify)]
#![feature(const_fmt_arguments_new)]
#![feature(const_impl_trait)]
#![feature(const_mut_refs)]
#![feature(const_precise_live_drops)]
-#![feature(const_raw_ptr_deref)]
+#![cfg_attr(bootstrap, feature(const_raw_ptr_deref))]
#![feature(const_refs_to_cell)]
#![feature(decl_macro)]
#![feature(doc_cfg)]
}
}
+// Pull in the `core_simd` crate directly into libcore. The contents of
+// `core_simd` are in a different repository: rust-lang/portable-simd.
+//
+// `core_simd` depends on libcore, but the contents of this module are
+// set up in such a way that directly pulling it here works such that the
+// crate uses this crate as its libcore.
+#[path = "../../portable-simd/crates/core_simd/src/mod.rs"]
+#[allow(missing_debug_implementations, dead_code, unsafe_op_in_unsafe_fn, unused_unsafe)]
+#[allow(rustdoc::bare_urls)]
+#[unstable(feature = "portable_simd", issue = "86656")]
+#[cfg(not(bootstrap))]
+mod core_simd;
+
+#[doc = include_str!("../../portable-simd/crates/core_simd/src/core_simd_docs.md")]
+#[unstable(feature = "portable_simd", issue = "86656")]
+#[cfg(not(bootstrap))]
+pub mod simd {
+ #[unstable(feature = "portable_simd", issue = "86656")]
+ pub use crate::core_simd::simd::*;
+}
+
include!("primitive_docs.rs");
///
/// # Panics
///
-/// This will always [`panic!`].
+/// This will always [`panic!`] because `unreachable!` is just a shorthand for `panic!` with a
+/// fixed, specific message.
+///
+/// Like `panic!`, this macro has a second form for displaying custom values.
///
/// # Examples
///
/// if 3*i < i { panic!("u32 overflow"); }
/// if x < 3*i { return i-1; }
/// }
-/// unreachable!();
+/// unreachable!("The loop should always return");
/// }
/// ```
#[macro_export]
///
/// * Function item types (i.e., the distinct types defined for each function)
/// * Function pointer types (e.g., `fn() -> i32`)
-/// * Array types, for all sizes, if the item type also implements `Copy` (e.g., `[i32; 123456]`)
/// * Tuple types, if each component also implements `Copy` (e.g., `()`, `(i32, bool)`)
/// * Closure types, if they capture no value from the environment
/// or if all such captured values implement `Copy` themselves.
///
/// [`ptr::swap`]: crate::ptr::swap()
#[stable(feature = "pointer_methods", since = "1.26.0")]
+ #[rustc_const_unstable(feature = "const_swap", issue = "83163")]
#[inline(always)]
- pub unsafe fn swap(self, with: *mut T)
+ pub const unsafe fn swap(self, with: *mut T)
where
T: Sized,
{
/// assert!(v == ["a", "b", "e", "d", "c"]);
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
+ #[rustc_const_unstable(feature = "const_swap", issue = "83163")]
#[inline]
- pub fn swap(&mut self, a: usize, b: usize) {
+ pub const fn swap(&mut self, a: usize, b: usize) {
let _ = &self[a];
let _ = &self[b];
/// [`swap`]: slice::swap
/// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html
#[unstable(feature = "slice_swap_unchecked", issue = "88539")]
- pub unsafe fn swap_unchecked(&mut self, a: usize, b: usize) {
+ #[rustc_const_unstable(feature = "const_swap", issue = "83163")]
+ pub const unsafe fn swap_unchecked(&mut self, a: usize, b: usize) {
#[cfg(debug_assertions)]
{
let _ = &self[a];
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
pub fn reverse(&mut self) {
- let mut i: usize = 0;
- let ln = self.len();
-
- // For very small types, all the individual reads in the normal
- // path perform poorly. We can do better, given efficient unaligned
- // load/store, by loading a larger chunk and reversing a register.
-
- // Ideally LLVM would do this for us, as it knows better than we do
- // whether unaligned reads are efficient (since that changes between
- // different ARM versions, for example) and what the best chunk size
- // would be. Unfortunately, as of LLVM 4.0 (2017-05) it only unrolls
- // the loop, so we need to do this ourselves. (Hypothesis: reverse
- // is troublesome because the sides can be aligned differently --
- // will be, when the length is odd -- so there's no way of emitting
- // pre- and postludes to use fully-aligned SIMD in the middle.)
-
- let fast_unaligned = cfg!(any(target_arch = "x86", target_arch = "x86_64"));
-
- if fast_unaligned && mem::size_of::<T>() == 1 {
- // Use the llvm.bswap intrinsic to reverse u8s in a usize
- let chunk = mem::size_of::<usize>();
- while i + chunk - 1 < ln / 2 {
- // SAFETY: There are several things to check here:
- //
- // - Note that `chunk` is either 4 or 8 due to the cfg check
- // above. So `chunk - 1` is positive.
- // - Indexing with index `i` is fine as the loop check guarantees
- // `i + chunk - 1 < ln / 2`
- // <=> `i < ln / 2 - (chunk - 1) < ln / 2 < ln`.
- // - Indexing with index `ln - i - chunk = ln - (i + chunk)` is fine:
- // - `i + chunk > 0` is trivially true.
- // - The loop check guarantees:
- // `i + chunk - 1 < ln / 2`
- // <=> `i + chunk ≤ ln / 2 ≤ ln`, thus subtraction does not underflow.
- // - The `read_unaligned` and `write_unaligned` calls are fine:
- // - `pa` points to index `i` where `i < ln / 2 - (chunk - 1)`
- // (see above) and `pb` points to index `ln - i - chunk`, so
- // both are at least `chunk`
- // many bytes away from the end of `self`.
- // - Any initialized memory is valid `usize`.
- unsafe {
- let ptr = self.as_mut_ptr();
- let pa = ptr.add(i);
- let pb = ptr.add(ln - i - chunk);
- let va = ptr::read_unaligned(pa as *mut usize);
- let vb = ptr::read_unaligned(pb as *mut usize);
- ptr::write_unaligned(pa as *mut usize, vb.swap_bytes());
- ptr::write_unaligned(pb as *mut usize, va.swap_bytes());
- }
- i += chunk;
- }
- }
+ let half_len = self.len() / 2;
+ let Range { start, end } = self.as_mut_ptr_range();
+
+ // These slices will skip the middle item for an odd length,
+ // since that one doesn't need to move.
+ let (front_half, back_half) =
+ // SAFETY: Both are subparts of the original slice, so the memory
+ // range is valid, and they don't overlap because they're each only
+ // half (or less) of the original slice.
+ unsafe {
+ (
+ slice::from_raw_parts_mut(start, half_len),
+ slice::from_raw_parts_mut(end.sub(half_len), half_len),
+ )
+ };
- if fast_unaligned && mem::size_of::<T>() == 2 {
- // Use rotate-by-16 to reverse u16s in a u32
- let chunk = mem::size_of::<u32>() / 2;
- while i + chunk - 1 < ln / 2 {
- // SAFETY: An unaligned u32 can be read from `i` if `i + 1 < ln`
- // (and obviously `i < ln`), because each element is 2 bytes and
- // we're reading 4.
- //
- // `i + chunk - 1 < ln / 2` # while condition
- // `i + 2 - 1 < ln / 2`
- // `i + 1 < ln / 2`
- //
- // Since it's less than the length divided by 2, then it must be
- // in bounds.
- //
- // This also means that the condition `0 < i + chunk <= ln` is
- // always respected, ensuring the `pb` pointer can be used
- // safely.
- unsafe {
- let ptr = self.as_mut_ptr();
- let pa = ptr.add(i);
- let pb = ptr.add(ln - i - chunk);
- let va = ptr::read_unaligned(pa as *mut u32);
- let vb = ptr::read_unaligned(pb as *mut u32);
- ptr::write_unaligned(pa as *mut u32, vb.rotate_left(16));
- ptr::write_unaligned(pb as *mut u32, va.rotate_left(16));
- }
- i += chunk;
- }
- }
+ // Introducing a function boundary here means that the two halves
+ // get `noalias` markers, allowing better optimization as LLVM
+ // knows that they're disjoint, unlike in the original slice.
+ revswap(front_half, back_half, half_len);
- while i < ln / 2 {
- // SAFETY: `i` is inferior to half the length of the slice so
- // accessing `i` and `ln - i - 1` is safe (`i` starts at 0 and
- // will not go further than `ln / 2 - 1`).
- // The resulting pointers `pa` and `pb` are therefore valid and
- // aligned, and can be read from and written to.
- unsafe {
- self.swap_unchecked(i, ln - i - 1);
+ #[inline]
+ fn revswap<T>(a: &mut [T], b: &mut [T], n: usize) {
+ debug_assert_eq!(a.len(), n);
+ debug_assert_eq!(b.len(), n);
+
+ // Because this function is first compiled in isolation,
+ // this check tells LLVM that the indexing below is
+ // in-bounds. Then after inlining -- once the actual
+ // lengths of the slices are known -- it's removed.
+ let (a, b) = (&mut a[..n], &mut b[..n]);
+
+ for i in 0..n {
+ mem::swap(&mut a[i], &mut b[n - 1 - i]);
}
- i += 1;
}
}
#![feature(const_mut_refs)]
#![feature(const_pin)]
#![feature(const_slice_from_raw_parts)]
-#![feature(const_raw_ptr_deref)]
+#![cfg_attr(bootstrap, feature(const_raw_ptr_deref))]
#![feature(never_type)]
#![feature(unwrap_infallible)]
#![feature(result_into_ok_or_err)]
+#![cfg_attr(not(bootstrap), feature(portable_simd))]
#![feature(ptr_metadata)]
#![feature(once_cell)]
#![feature(unsized_tuple_coercion)]
mod pin;
mod ptr;
mod result;
+#[cfg(not(bootstrap))]
+mod simd;
mod slice;
mod str;
mod str_lossy;
--- /dev/null
+use core::simd::f32x4;
+
+#[test]
+fn testing() {
+ let x = f32x4::from_array([1.0, 1.0, 1.0, 1.0]);
+ let y = -x;
+
+ let h = x * 0.5;
+
+ let r = y.abs();
+ assert_eq!(x, r);
+ assert_eq!(h, f32x4::splat(0.5));
+}
--- /dev/null
+---
+name: Blank Issue
+about: Create a blank issue.
+---
--- /dev/null
+---
+name: Bug Report
+about: Create a bug report for Rust.
+labels: C-bug
+---
+<!--
+Thank you for filing a bug report! 🐛 Please provide a short summary of the bug,
+along with any information you feel relevant to replicating the bug.
+-->
+
+I tried this code:
+
+```rust
+<code>
+```
+
+I expected to see this happen: *explanation*
+
+Instead, this happened: *explanation*
+
+### Meta
+
+`rustc --version --verbose`:
+```
+<version>
+```
+
+
+`crate version in Cargo.toml`:
+```toml
+[dependencies]
+stdsimd =
+```
+<!-- If this specifies the repo at HEAD, please include the latest commit. -->
+
+
+<!--
+If a backtrace is available, please include a backtrace in the code block by
+setting `RUST_BACKTRACE=1` in your environment. e.g.
+`RUST_BACKTRACE=1 cargo build`.
+-->
+<details><summary>Backtrace</summary>
+<p>
+
+```
+<backtrace>
+```
+
+</p>
+</details>
--- /dev/null
+# This only controls whether a tiny, hard-to-find "open a blank issue" link appears at the end of
+# the template list.
+blank_issues_enabled: true
+contact_links:
+ - name: Intrinsic Support
+ url: https://github.com/rust-lang/stdarch/issues
+ about: Please direct issues about Rust's support for vendor intrinsics to core::arch
+ - name: Internal Compiler Error
+ url: https://github.com/rust-lang/rust/issues
+ about: Please report ICEs to the rustc repository
--- /dev/null
+---
+name: Feature Request
+about: Request an addition to the core::simd API
+labels: C-feature-request
+---
+<!--
+ Hello!
+
+ We are very interested in any feature requests you may have.
+
+ However, please be aware that core::simd exists to address concerns with creating a portable SIMD API for Rust.
+ Requests for extensions to compiler features, such as `target_feature`, binary versioning for SIMD APIs, or
+ improving specific compilation issues in general should be discussed at https://internals.rust-lang.org/
+-->
--- /dev/null
+Hello, welcome to `std::simd`!
+
+It seems this pull request template checklist was created while a lot of vector math ops were being implemented, and only really applies to ops. Feel free to delete everything here if it's not applicable, or ask for help if you're not sure what it means!
+
+For a given vector math operation on TxN, please add tests for interactions with:
+ - [ ] `T::MAX`
+ - [ ] `T::MIN`
+ - [ ] -1
+ - [ ] 1
+ - [ ] 0
+
+
+For a given vector math operation on TxN where T is a float, please add tests for test interactions with:
+ - [ ] a really large number, larger than the mantissa
+ - [ ] a really small "subnormal" number
+ - [ ] NaN
+ - [ ] Infinity
+ - [ ] Negative Infinity
--- /dev/null
+name: CI
+
+on:
+ pull_request:
+ push:
+ branches:
+ - master
+
+env:
+ CARGO_NET_RETRY: 10
+ RUSTUP_MAX_RETRIES: 10
+
+jobs:
+ rustfmt:
+ name: "rustfmt"
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v2
+ - name: Setup Rust
+ run: |
+ rustup update nightly --no-self-update
+ rustup default nightly
+ rustup component add rustfmt
+ - name: Run rustfmt
+ run: cargo fmt --all -- --check
+
+ clippy:
+ name: "clippy on ${{ matrix.target }}"
+ runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ target:
+ # We shouldn't really have any OS-specific code, so think of this as a list of architectures
+ - x86_64-unknown-linux-gnu
+ - i686-unknown-linux-gnu
+ - i586-unknown-linux-gnu
+ - aarch64-unknown-linux-gnu
+ - armv7-unknown-linux-gnueabihf
+ - mips-unknown-linux-gnu
+ - mips64-unknown-linux-gnuabi64
+ - powerpc-unknown-linux-gnu
+ - powerpc64-unknown-linux-gnu
+ - riscv64gc-unknown-linux-gnu
+ - s390x-unknown-linux-gnu
+ - sparc64-unknown-linux-gnu
+ - wasm32-unknown-unknown
+
+ steps:
+ - uses: actions/checkout@v2
+ - name: Setup Rust
+ run: |
+ rustup update nightly --no-self-update
+ rustup default nightly
+ rustup target add ${{ matrix.target }}
+ rustup component add clippy
+ - name: Run Clippy
+ run: cargo clippy --all-targets --target ${{ matrix.target }}
+
+ x86-tests:
+ name: "${{ matrix.target_feature }} on ${{ matrix.target }}"
+ runs-on: ${{ matrix.os }}
+ strategy:
+ fail-fast: false
+ matrix:
+ target: [x86_64-pc-windows-msvc, i686-pc-windows-msvc, i586-pc-windows-msvc, x86_64-unknown-linux-gnu, x86_64-apple-darwin]
+ # `default` means we use the default target config for the target,
+ # `native` means we run with `-Ctarget-cpu=native`, and anything else is
+ # an arg to `-Ctarget-feature`
+ target_feature: [default, native, +sse3, +ssse3, +sse4.1, +sse4.2, +avx, +avx2]
+
+ exclude:
+ # The macos runners seem to only reliably support up to `avx`.
+ - { target: x86_64-apple-darwin, target_feature: +avx2 }
+ # These features are statically known to be present for all 64 bit
+ # macs, and thus are covered by the `default` test
+ - { target: x86_64-apple-darwin, target_feature: +sse3 }
+ - { target: x86_64-apple-darwin, target_feature: +ssse3 }
+ # -Ctarget-cpu=native sounds like bad-news if target != host
+ - { target: i686-pc-windows-msvc, target_feature: native }
+ - { target: i586-pc-windows-msvc, target_feature: native }
+
+ include:
+ # Populate the `matrix.os` field
+ - { target: x86_64-apple-darwin, os: macos-latest }
+ - { target: x86_64-unknown-linux-gnu, os: ubuntu-latest }
+ - { target: x86_64-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
+ # avx512vl, but occasionally doesn't. Maybe one day we can enable it.
+
+ steps:
+ - uses: actions/checkout@v2
+ - name: Setup Rust
+ run: |
+ rustup update nightly --no-self-update
+ rustup default nightly
+ rustup target add ${{ matrix.target }}
+
+ - name: Configure RUSTFLAGS
+ shell: bash
+ run: |
+ case "${{ matrix.target_feature }}" in
+ default)
+ echo "RUSTFLAGS=-Dwarnings" >> $GITHUB_ENV;;
+ native)
+ echo "RUSTFLAGS=-Dwarnings -Ctarget-cpu=native" >> $GITHUB_ENV
+ ;;
+ *)
+ echo "RUSTFLAGS=-Dwarnings -Ctarget-feature=${{ matrix.target_feature }}" >> $GITHUB_ENV
+ ;;
+ esac
+
+ # Super useful for debugging why a SIGILL occurred.
+ - name: Dump target configuration and support
+ run: |
+ rustc -Vv
+
+ echo "Caveat: not all target features are expected to be logged"
+
+ echo "## Requested target configuration (RUSTFLAGS=$RUSTFLAGS)"
+ rustc --print=cfg --target=${{ matrix.target }} $RUSTFLAGS
+
+ echo "## Supported target configuration for --target=${{ matrix.target }}"
+ rustc --print=cfg --target=${{ matrix.target }} -Ctarget-cpu=native
+
+ echo "## Natively supported target configuration"
+ rustc --print=cfg -Ctarget-cpu=native
+
+ - name: Test (debug)
+ run: cargo test --verbose --target=${{ matrix.target }}
+
+ - name: Test (release)
+ run: cargo test --verbose --target=${{ matrix.target }} --release
+
+ wasm-tests:
+ name: "wasm (firefox, ${{ matrix.name }})"
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ include:
+ - { name: default, RUSTFLAGS: "" }
+ - { name: simd128, RUSTFLAGS: "-C target-feature=+simd128" }
+ steps:
+ - uses: actions/checkout@v2
+ - name: Setup Rust
+ run: |
+ rustup update nightly --no-self-update
+ rustup default nightly
+ - name: Install wasm-pack
+ run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
+ - name: Test (debug)
+ run: wasm-pack test --firefox --headless crates/core_simd
+ env:
+ RUSTFLAGS: ${{ matrix.rustflags }}
+ - name: Test (release)
+ run: wasm-pack test --firefox --headless crates/core_simd --release
+ env:
+ RUSTFLAGS: ${{ matrix.rustflags }}
+
+ cross-tests:
+ name: "${{ matrix.target }} (via cross)"
+ runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ # TODO: Sadly, we cant configure target-feature in a meaningful way
+ # because `cross` doesn't tell qemu to enable any non-default cpu
+ # features, nor does it give us a way to do so.
+ #
+ # Ultimately, we'd like to do something like [rust-lang/stdarch][stdarch].
+ # This is a lot more complex... but in practice it's likely that we can just
+ # snarf the docker config from around [here][1000-dockerfiles].
+ #
+ # [stdarch]: https://github.com/rust-lang/stdarch/blob/a5db4eaf/.github/workflows/main.yml#L67
+ # [1000-dockerfiles]: https://github.com/rust-lang/stdarch/tree/a5db4eaf/ci/docker
+
+ matrix:
+ target:
+ - i586-unknown-linux-gnu
+ # 32-bit arm has a few idiosyncracies like having subnormal flushing
+ # to zero on by default. Ideally we'd set
+ - armv7-unknown-linux-gnueabihf
+ - aarch64-unknown-linux-gnu
+ # Note: The issue above means neither of these mips targets will use
+ # MSA (mips simd) but MIPS uses a nonstandard binary representation
+ # for NaNs which makes it worth testing on despite that.
+ - mips-unknown-linux-gnu
+ - mips64-unknown-linux-gnuabi64
+ - riscv64gc-unknown-linux-gnu
+ # TODO this test works, but it appears to time out
+ # - powerpc-unknown-linux-gnu
+ # TODO this test is broken, but it appears to be a problem with QEMU, not us.
+ # - powerpc64le-unknown-linux-gnu
+ # TODO enable this once a new version of cross is released
+ # - powerpc64-unknown-linux-gnu
+
+ steps:
+ - uses: actions/checkout@v2
+ - name: Setup Rust
+ run: |
+ rustup update nightly --no-self-update
+ rustup default nightly
+ rustup target add ${{ matrix.target }}
+ rustup component add rust-src
+
+ - name: Install Cross
+ # Equivalent to `cargo install cross`, but downloading a prebuilt
+ # binary. Ideally we wouldn't hardcode a version, but the version number
+ # being part of the tarball means we can't just use the download/latest
+ # URL :(
+ run: |
+ CROSS_URL=https://github.com/rust-embedded/cross/releases/download/v0.2.1/cross-v0.2.1-x86_64-unknown-linux-gnu.tar.gz
+ mkdir -p "$HOME/.bin"
+ curl -sfSL --retry-delay 10 --retry 5 "${CROSS_URL}" | tar zxf - -C "$HOME/.bin"
+ echo "$HOME/.bin" >> $GITHUB_PATH
+
+ - name: Test (debug)
+ run: cross test --verbose --target=${{ matrix.target }}
+
+ - name: Test (release)
+ run: cross test --verbose --target=${{ matrix.target }} --release
+
+ features:
+ name: "Check cargo features (${{ matrix.simd }} × ${{ matrix.features }})"
+ runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ simd:
+ - ""
+ - "avx512"
+ features:
+ - ""
+ - "--features std"
+ - "--features generic_const_exprs"
+ - "--features std --features generic_const_exprs"
+
+ steps:
+ - uses: actions/checkout@v2
+ - name: Setup Rust
+ run: |
+ rustup update nightly --no-self-update
+ rustup default nightly
+ - name: Detect AVX512
+ run: echo "CPU_FEATURE=$(lscpu | grep -o avx512[a-z]* | sed s/avx/+avx/ | tr '\n' ',' )" >> $GITHUB_ENV
+ - name: Check build
+ if: ${{ matrix.simd == '' }}
+ run: RUSTFLAGS="-Dwarnings" cargo check --all-targets --no-default-features ${{ matrix.features }}
+ - name: Check AVX
+ if: ${{ matrix.simd == 'avx512' && contains(env.CPU_FEATURE, 'avx512') }}
+ run: |
+ echo "Found AVX features: $CPU_FEATURE"
+ RUSTFLAGS="-Dwarnings -Ctarget-feature=$CPU_FEATURE" cargo check --all-targets --no-default-features ${{ matrix.features }}
--- /dev/null
+name: Documentation
+
+on:
+ push:
+ branches:
+ - master
+
+jobs:
+ release:
+ name: Deploy Documentation
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout Repository
+ uses: actions/checkout@v1
+
+ - name: Setup Rust
+ run: |
+ rustup update nightly --no-self-update
+ rustup default nightly
+
+ - name: Build Documentation
+ run: cargo doc --no-deps
+
+ - name: Deploy Documentation
+ uses: peaceiris/actions-gh-pages@v3
+ with:
+ github_token: ${{ secrets.GITHUB_TOKEN }}
+ publish_branch: gh-pages
+ publish_dir: ./target/doc
--- /dev/null
+/target
+Cargo.lock
--- /dev/null
+# Contributing to `std::simd`
+
+Simple version:
+1. Fork it and `git clone` it
+2. Create your feature branch: `git checkout -b my-branch`
+3. Write your changes.
+4. Test it: `cargo test`. Remember to enable whatever SIMD features you intend to test by setting `RUSTFLAGS`.
+5. Commit your changes: `git commit add ./path/to/changes && git commit -m 'Fix some bug'`
+6. Push the branch: `git push --set-upstream origin my-branch`
+7. Submit a pull request!
+
+## Taking on an Issue
+
+SIMD can be quite complex, and even a "simple" issue can be huge. If an issue is organized like a tracking issue, with an itemized list of items that don't necessarily have to be done in a specific order, please take the issue one item at a time. This will help by letting work proceed apace on the rest of the issue. If it's a (relatively) small issue, feel free to announce your intention to solve it on the issue tracker and take it in one go!
+
+## CI
+
+We currently have 2 CI matrices through Travis CI and GitHub Actions that will automatically build and test your change in order to verify that `std::simd`'s portable API is, in fact, portable. If your change builds locally, but does not build on either, this is likely due to a platform-specific concern that your code has not addressed. Please consult the build logs and address the error, or ask for help if you need it.
+
+## Beyond stdsimd
+
+A large amount of the core SIMD implementation is found in the rustc_codegen_* crates in the [main rustc repo](https://github.com/rust-lang/rust). In addition, actual platform-specific functions are implemented in [stdarch]. Not all changes to `std::simd` require interacting with either of these, but if you're wondering where something is and it doesn't seem to be in this repository, those might be where to start looking.
+
+## Questions? Concerns? Need Help?
+
+Please feel free to ask in the [#project-portable-simd][zulip-portable-simd] stream on the [rust-lang Zulip][zulip] for help with making changes to `std::simd`!
+If your changes include directly modifying the compiler, it might also be useful to ask in [#t-compiler/help][zulip-compiler-help].
+
+[zulip-portable-simd]: https://rust-lang.zulipchat.com/#narrow/stream/257879-project-portable-simd
+[zulip-compiler-help]: https://rust-lang.zulipchat.com/#narrow/stream/182449-t-compiler.2Fhelp
+[zulip]: https://rust-lang.zulipchat.com
+[stdarch]: https://github.com/rust-lang/stdarch
--- /dev/null
+[workspace]
+
+members = [
+ "crates/core_simd",
+ "crates/test_helpers",
+]
--- /dev/null
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
--- /dev/null
+Copyright (c) 2020 The Rust Project Developers
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
--- /dev/null
+# The Rust standard library's portable SIMD API
+[![Build Status](https://travis-ci.com/rust-lang/portable-simd.svg?branch=master)](https://travis-ci.com/rust-lang/portable-simd)
+
+Code repository for the [Portable SIMD Project Group](https://github.com/rust-lang/project-portable-simd).
+Please refer to [CONTRIBUTING.md](./CONTRIBUTING.md) for our contributing guidelines.
+
+The docs for this crate are published from the main branch.
+You can [read them here][docs].
+
+If you have questions about SIMD, we have begun writing a [guide][simd-guide].
+We can also be found on [Zulip][zulip-project-portable-simd].
+
+If you are interested in support for a specific architecture, you may want [stdarch] instead.
+
+## Hello World
+
+Now we're gonna dip our toes into this world with a small SIMD "Hello, World!" example. Make sure your compiler is up to date and using `nightly`. We can do that by running
+
+```bash
+rustup update -- nightly
+```
+
+or by setting up `rustup default nightly` or else with `cargo +nightly {build,test,run}`. After updating, run
+```bash
+cargo new hellosimd
+```
+to create a new crate. Edit `hellosimd/Cargo.toml` to be
+```toml
+[package]
+name = "hellosimd"
+version = "0.1.0"
+edition = "2018"
+[dependencies]
+core_simd = { git = "https://github.com/rust-lang/portable-simd" }
+```
+
+and finally write this in `src/main.rs`:
+```rust
+use core_simd::*;
+fn main() {
+ let a = f32x4::splat(10.0);
+ let b = f32x4::from_array([1.0, 2.0, 3.0, 4.0]);
+ println!("{:?}", a + b);
+}
+```
+
+Explanation: We import all the bindings from the crate with the first line. Then, we construct our SIMD vectors with methods like `splat` or `from_array`. Finally, we can use operators on them like `+` and the appropriate SIMD instructions will be carried out. When we run `cargo run` you should get `[11.0, 12.0, 13.0, 14.0]`.
+
+## Code Organization
+
+Currently the crate is organized so that each element type is a file, and then the 64-bit, 128-bit, 256-bit, and 512-bit vectors using those types are contained in said file.
+
+All types are then exported as a single, flat module.
+
+Depending on the size of the primitive type, the number of lanes the vector will have varies. For example, 128-bit vectors have four `f32` lanes and two `f64` lanes.
+
+The supported element types are as follows:
+* **Floating Point:** `f32`, `f64`
+* **Signed Integers:** `i8`, `i16`, `i32`, `i64`, `i128`, `isize`
+* **Unsigned Integers:** `u8`, `u16`, `u32`, `u64`, `u128`, `usize`
+* **Masks:** `mask8`, `mask16`, `mask32`, `mask64`, `mask128`, `masksize`
+
+Floating point, signed integers, and unsigned integers are the [primitive types](https://doc.rust-lang.org/core/primitive/index.html) you're already used to.
+The `mask` types are "truthy" values, but they use the number of bits in their name instead of just 1 bit like a normal `bool` uses.
+
+[simd-guide]: ./beginners-guide.md
+[zulip-project-portable-simd]: https://rust-lang.zulipchat.com/#narrow/stream/257879-project-portable-simd
+[stdarch]: https://github.com/rust-lang/stdarch
+[docs]: https://rust-lang.github.io/portable-simd/core_simd
--- /dev/null
+
+# Beginner's Guide To SIMD
+
+Hello and welcome to our SIMD basics guide!
+
+Because SIMD is a subject that many programmers haven't worked with before, we thought that it's best to outline some terms and other basics for you to get started with.
+
+## Quick Background
+
+**SIMD** stands for *Single Instruction, Multiple Data*. In other words, SIMD is when the CPU performs a single action on more than one logical piece of data at the same time. Instead of adding two registers that each contain one `f32` value and getting an `f32` as the result, you might add two registers that each contain `f32x4` (128 bits of data) and then you get an `f32x4` as the output.
+
+This might seem a tiny bit weird at first, but there's a good reason for it. Back in the day, as CPUs got faster and faster, eventually they got so fast that the CPU would just melt itself. The heat management (heat sinks, fans, etc) simply couldn't keep up with how much electricity was going through the metal. Two main strategies were developed to help get around the limits of physics.
+* One of them you're probably familiar with: Multi-core processors. By giving a processor more than one core, each core can do its own work, and because they're physically distant (at least on the CPU's scale) the heat can still be managed. Unfortunately, not all tasks can just be split up across cores in an efficient way.
+* The second strategy is SIMD. If you can't make the register go any faster, you can still make the register *wider*. This lets you process more data at a time, which is *almost* as good as just having a faster CPU. As with multi-core programming, SIMD doesn't fit every kind of task, so you have to know when it will improve your program.
+
+## Terms
+
+SIMD has a few special vocabulary terms you should know:
+
+* **Vector:** A SIMD value is called a vector. This shouldn't be confused with the `Vec<T>` type. A SIMD vector has a fixed size, known at compile time. All of the elements within the vector are of the same type. This makes vectors *similar to* arrays. One difference is that a vector is generally aligned to its *entire* size (eg: 16 bytes, 32 bytes, etc), not just the size of an individual element. Sometimes vector data is called "packed" data.
+
+* **Vectorize**: An operation that uses SIMD instructions to operate over a vector is often referred to as "vectorized".
+
+* **Autovectorization**: Also known as _implicit vectorization_. This is when a compiler can automatically recognize a situation where scalar instructions may be replaced with SIMD instructions, and use those instead.
+
+* **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.
+
+* **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.
+
+* **Vector Register:** The extra-wide registers that are used for SIMD operations are commonly called vector registers, though you may also see "SIMD registers", vendor names for specific features, or even "floating-point register" as it is common for the same registers to be used with both scalar and vectorized floating-point operations.
+
+* **Vertical:** When an operation is "vertical", each lane processes individually without regard to the other lanes in the same vector. For example, a "vertical add" between two vectors would add lane 0 in `a` with lane 0 in `b`, with the total in lane 0 of `out`, and then the same thing for lanes 1, 2, etc. Most SIMD operations are vertical operations, so if your problem is a vertical problem then you can probably solve it with SIMD.
+
+* **Horizontal:** When an operation is "horizontal", the lanes within a single vector interact in some way. A "horizontal add" might add up lane 0 of `a` with lane 1 of `a`, with the total in lane 0 of `out`.
+
+* **Target Feature:** Rust calls a CPU architecture extension a `target_feature`. Proper SIMD requires various CPU extensions to be enabled (details below). Don't confuse this with `feature`, which is a Cargo crate concept.
+
+## Target Features
+
+When using SIMD, you should be familiar with the CPU feature set that you're targeting.
+
+On `arm` and `aarch64` it's fairly simple. There's just one CPU feature that controls if SIMD is available: `neon` (or "NEON", all caps, as the ARM docs often put it). Neon registers can be used as 64-bit or 128-bit. When doing 128-bit operations it just uses two 64-bit registers as a single 128-bit register.
+
+> By default, the `aarch64`, `arm`, and `thumb` Rust targets generally do not enable `neon` unless it's in the target string.
+
+On `x86` and `x86_64` it's slightly more complicated. The SIMD support is split into many levels:
+* 128-bit: `sse`, `sse2`, `sse3`, `ssse3` (not a typo!), `sse4.1`, `sse4.2`, `sse4a` (AMD only)
+* 256-bit (mostly): `avx`, `avx2`, `fma`
+* 512-bit (mostly): a *wide* range of `avx512` variations
+
+The list notes the bit widths available at each feature level, though the operations of the more advanced features can generally be used with the smaller register sizes as well. For example, new operations introduced in `avx` generally have a 128-bit form as well as a 256-bit form. This means that even if you only do 128-bit work you can still benefit from the later feature levels.
+
+> By default, the `i686` and `x86_64` Rust targets enable `sse` and `sse2`.
+
+### Selecting Additional Target Features
+
+If you want to enable support for a target feature within your build, generally you should use a [target-feature](https://rust-lang.github.io/packed_simd/perf-guide/target-feature/rustflags.html#target-feature) setting within you `RUSTFLAGS` setting.
+
+If you know that you're targeting a specific CPU you can instead use the [target-cpu](https://rust-lang.github.io/packed_simd/perf-guide/target-feature/rustflags.html#target-cpu) flag and the compiler will enable the correct set of features for that CPU.
+
+The [Steam Hardware Survey](https://store.steampowered.com/hwsurvey/Steam-Hardware-Software-Survey-Welcome-to-Steam) is one of the few places with data on how common various CPU features are. The dataset is limited to "the kinds of computers owned by people who play computer games", so the info only covers `x86`/`x86_64`, and it also probably skews to slightly higher quality computers than average. Still, we can see that the `sse` levels have very high support, `avx` and `avx2` are quite common as well, and the `avx-512` family is still so early in adoption you can barely find it in consumer grade stuff.
+
+## Running a program compiled for a CPU feature level that the CPU doesn't support is automatic undefined behavior.
+
+This means that if you build your program with `avx` support enabled and run it on a CPU without `avx` support, it's **instantly** undefined behavior.
+
+Even without an `unsafe` block in sight.
+
+This is no bug in Rust, or soundness hole in the type system. You just plain can't make a CPU do what it doesn't know how to do.
+
+This is why the various Rust targets *don't* enable many CPU feature flags by default: requiring a more advanced CPU makes the final binary *less* portable.
+
+So please select an appropriate CPU feature level when building your programs.
+
+## Size, Alignment, and Unsafe Code
+
+Most of the portable SIMD API is designed to allow the user to gloss over the details of different architectures and avoid using unsafe code. However, there are plenty of reasons to want to use unsafe code with these SIMD types, such as using an intrinsic function from `core::arch` to further accelerate particularly specialized SIMD operations on a given platform, while still using the portable API elsewhere. For these cases, there are some rules to keep in mind.
+
+Fortunately, most SIMD types have a fairly predictable size. `i32x4` is bit-equivalent to `[i32; 4]` and so can be bitcast to it, e.g. using [`mem::transmute`], though the API usually offers a safe cast you can use instead.
+
+However, this is not the same as alignment. Computer architectures generally prefer aligned accesses, especially when moving data between memory and vector registers, and while some support specialized operations that can bend the rules to help with this, unaligned access is still typically slow, or even undefined behavior. In addition, different architectures can require different alignments when interacting with their native SIMD types. For this reason, any `#[repr(simd)]` type has a non-portable alignment. If it is necessary to directly interact with the alignment of these types, it should be via [`mem::align_of`].
+
+[`mem::transmute`]: https://doc.rust-lang.org/core/mem/fn.transmute.html
+[`mem::align_of`]: https://doc.rust-lang.org/core/mem/fn.align_of.html
\ No newline at end of file
--- /dev/null
+[package]
+name = "core_simd"
+version = "0.1.0"
+edition = "2021"
+homepage = "https://github.com/rust-lang/portable-simd"
+repository = "https://github.com/rust-lang/portable-simd"
+keywords = ["core", "simd", "intrinsics"]
+categories = ["hardware-support", "no-std"]
+license = "MIT OR Apache-2.0"
+
+[features]
+default = ["std", "generic_const_exprs"]
+std = []
+generic_const_exprs = []
+
+[target.'cfg(target_arch = "wasm32")'.dev-dependencies.wasm-bindgen]
+version = "0.2"
+
+[dev-dependencies.wasm-bindgen-test]
+version = "0.3"
+
+[dev-dependencies.proptest]
+version = "0.10"
+default-features = false
+features = ["alloc"]
+
+[dev-dependencies.test_helpers]
+path = "../test_helpers"
--- /dev/null
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
--- /dev/null
+Copyright (c) 2020 The Rust Project Developers
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
--- /dev/null
+//! 4x4 matrix inverse
+// Code ported from the `packed_simd` crate
+// Run this code with `cargo test --example matrix_inversion`
+#![feature(array_chunks, portable_simd)]
+use core_simd::simd::*;
+use Which::*;
+
+// Gotta define our own 4x4 matrix since Rust doesn't ship multidim arrays yet :^)
+#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
+pub struct Matrix4x4([[f32; 4]; 4]);
+
+#[allow(clippy::too_many_lines)]
+pub fn scalar_inv4x4(m: Matrix4x4) -> Option<Matrix4x4> {
+ let m = m.0;
+
+ #[rustfmt::skip]
+ let mut inv = [
+ // row 0:
+ [
+ // 0,0:
+ m[1][1] * m[2][2] * m[3][3] -
+ m[1][1] * m[2][3] * m[3][2] -
+ m[2][1] * m[1][2] * m[3][3] +
+ m[2][1] * m[1][3] * m[3][2] +
+ m[3][1] * m[1][2] * m[2][3] -
+ m[3][1] * m[1][3] * m[2][2],
+ // 0,1:
+ -m[0][1] * m[2][2] * m[3][3] +
+ m[0][1] * m[2][3] * m[3][2] +
+ m[2][1] * m[0][2] * m[3][3] -
+ m[2][1] * m[0][3] * m[3][2] -
+ m[3][1] * m[0][2] * m[2][3] +
+ m[3][1] * m[0][3] * m[2][2],
+ // 0,2:
+ m[0][1] * m[1][2] * m[3][3] -
+ m[0][1] * m[1][3] * m[3][2] -
+ m[1][1] * m[0][2] * m[3][3] +
+ m[1][1] * m[0][3] * m[3][2] +
+ m[3][1] * m[0][2] * m[1][3] -
+ m[3][1] * m[0][3] * m[1][2],
+ // 0,3:
+ -m[0][1] * m[1][2] * m[2][3] +
+ m[0][1] * m[1][3] * m[2][2] +
+ m[1][1] * m[0][2] * m[2][3] -
+ m[1][1] * m[0][3] * m[2][2] -
+ m[2][1] * m[0][2] * m[1][3] +
+ m[2][1] * m[0][3] * m[1][2],
+ ],
+ // row 1
+ [
+ // 1,0:
+ -m[1][0] * m[2][2] * m[3][3] +
+ m[1][0] * m[2][3] * m[3][2] +
+ m[2][0] * m[1][2] * m[3][3] -
+ m[2][0] * m[1][3] * m[3][2] -
+ m[3][0] * m[1][2] * m[2][3] +
+ m[3][0] * m[1][3] * m[2][2],
+ // 1,1:
+ m[0][0] * m[2][2] * m[3][3] -
+ m[0][0] * m[2][3] * m[3][2] -
+ m[2][0] * m[0][2] * m[3][3] +
+ m[2][0] * m[0][3] * m[3][2] +
+ m[3][0] * m[0][2] * m[2][3] -
+ m[3][0] * m[0][3] * m[2][2],
+ // 1,2:
+ -m[0][0] * m[1][2] * m[3][3] +
+ m[0][0] * m[1][3] * m[3][2] +
+ m[1][0] * m[0][2] * m[3][3] -
+ m[1][0] * m[0][3] * m[3][2] -
+ m[3][0] * m[0][2] * m[1][3] +
+ m[3][0] * m[0][3] * m[1][2],
+ // 1,3:
+ m[0][0] * m[1][2] * m[2][3] -
+ m[0][0] * m[1][3] * m[2][2] -
+ m[1][0] * m[0][2] * m[2][3] +
+ m[1][0] * m[0][3] * m[2][2] +
+ m[2][0] * m[0][2] * m[1][3] -
+ m[2][0] * m[0][3] * m[1][2],
+ ],
+ // row 2
+ [
+ // 2,0:
+ m[1][0] * m[2][1] * m[3][3] -
+ m[1][0] * m[2][3] * m[3][1] -
+ m[2][0] * m[1][1] * m[3][3] +
+ m[2][0] * m[1][3] * m[3][1] +
+ m[3][0] * m[1][1] * m[2][3] -
+ m[3][0] * m[1][3] * m[2][1],
+ // 2,1:
+ -m[0][0] * m[2][1] * m[3][3] +
+ m[0][0] * m[2][3] * m[3][1] +
+ m[2][0] * m[0][1] * m[3][3] -
+ m[2][0] * m[0][3] * m[3][1] -
+ m[3][0] * m[0][1] * m[2][3] +
+ m[3][0] * m[0][3] * m[2][1],
+ // 2,2:
+ m[0][0] * m[1][1] * m[3][3] -
+ m[0][0] * m[1][3] * m[3][1] -
+ m[1][0] * m[0][1] * m[3][3] +
+ m[1][0] * m[0][3] * m[3][1] +
+ m[3][0] * m[0][1] * m[1][3] -
+ m[3][0] * m[0][3] * m[1][1],
+ // 2,3:
+ -m[0][0] * m[1][1] * m[2][3] +
+ m[0][0] * m[1][3] * m[2][1] +
+ m[1][0] * m[0][1] * m[2][3] -
+ m[1][0] * m[0][3] * m[2][1] -
+ m[2][0] * m[0][1] * m[1][3] +
+ m[2][0] * m[0][3] * m[1][1],
+ ],
+ // row 3
+ [
+ // 3,0:
+ -m[1][0] * m[2][1] * m[3][2] +
+ m[1][0] * m[2][2] * m[3][1] +
+ m[2][0] * m[1][1] * m[3][2] -
+ m[2][0] * m[1][2] * m[3][1] -
+ m[3][0] * m[1][1] * m[2][2] +
+ m[3][0] * m[1][2] * m[2][1],
+ // 3,1:
+ m[0][0] * m[2][1] * m[3][2] -
+ m[0][0] * m[2][2] * m[3][1] -
+ m[2][0] * m[0][1] * m[3][2] +
+ m[2][0] * m[0][2] * m[3][1] +
+ m[3][0] * m[0][1] * m[2][2] -
+ m[3][0] * m[0][2] * m[2][1],
+ // 3,2:
+ -m[0][0] * m[1][1] * m[3][2] +
+ m[0][0] * m[1][2] * m[3][1] +
+ m[1][0] * m[0][1] * m[3][2] -
+ m[1][0] * m[0][2] * m[3][1] -
+ m[3][0] * m[0][1] * m[1][2] +
+ m[3][0] * m[0][2] * m[1][1],
+ // 3,3:
+ m[0][0] * m[1][1] * m[2][2] -
+ m[0][0] * m[1][2] * m[2][1] -
+ m[1][0] * m[0][1] * m[2][2] +
+ m[1][0] * m[0][2] * m[2][1] +
+ m[2][0] * m[0][1] * m[1][2] -
+ m[2][0] * m[0][2] * m[1][1],
+ ],
+ ];
+
+ let det = m[0][0] * inv[0][0] + m[0][1] * inv[1][0] + m[0][2] * inv[2][0] + m[0][3] * inv[3][0];
+ if det == 0. {
+ return None;
+ }
+
+ let det_inv = 1. / det;
+
+ for row in &mut inv {
+ for elem in row.iter_mut() {
+ *elem *= det_inv;
+ }
+ }
+
+ Some(Matrix4x4(inv))
+}
+
+pub fn simd_inv4x4(m: Matrix4x4) -> Option<Matrix4x4> {
+ let m = m.0;
+ let m_0 = f32x4::from_array(m[0]);
+ let m_1 = f32x4::from_array(m[1]);
+ let m_2 = f32x4::from_array(m[2]);
+ let m_3 = f32x4::from_array(m[3]);
+
+ const SHUFFLE01: [Which; 4] = [First(0), First(1), Second(0), Second(1)];
+ const SHUFFLE02: [Which; 4] = [First(0), First(2), Second(0), Second(2)];
+ const SHUFFLE13: [Which; 4] = [First(1), First(3), Second(1), Second(3)];
+ const SHUFFLE23: [Which; 4] = [First(2), First(3), Second(2), Second(3)];
+
+ let tmp = simd_swizzle!(m_0, m_1, SHUFFLE01);
+ let row1 = simd_swizzle!(m_2, m_3, SHUFFLE01);
+
+ let row0 = simd_swizzle!(tmp, row1, SHUFFLE02);
+ let row1 = simd_swizzle!(row1, tmp, SHUFFLE13);
+
+ let tmp = simd_swizzle!(m_0, m_1, SHUFFLE23);
+ let row3 = simd_swizzle!(m_2, m_3, SHUFFLE23);
+ let row2 = simd_swizzle!(tmp, row3, SHUFFLE02);
+ let row3 = simd_swizzle!(row3, tmp, SHUFFLE13);
+
+ let tmp = (row2 * row3).reverse().rotate_lanes_right::<2>();
+ let minor0 = row1 * tmp;
+ let minor1 = row0 * tmp;
+ let tmp = tmp.rotate_lanes_right::<2>();
+ let minor0 = (row1 * tmp) - minor0;
+ let minor1 = (row0 * tmp) - minor1;
+ let minor1 = minor1.rotate_lanes_right::<2>();
+
+ let tmp = (row1 * row2).reverse().rotate_lanes_right::<2>();
+ let minor0 = (row3 * tmp) + minor0;
+ let minor3 = row0 * tmp;
+ let tmp = tmp.rotate_lanes_right::<2>();
+
+ let minor0 = minor0 - row3 * tmp;
+ let minor3 = row0 * tmp - minor3;
+ let minor3 = minor3.rotate_lanes_right::<2>();
+
+ let tmp = (row3 * row1.rotate_lanes_right::<2>())
+ .reverse()
+ .rotate_lanes_right::<2>();
+ let row2 = row2.rotate_lanes_right::<2>();
+ let minor0 = row2 * tmp + minor0;
+ let minor2 = row0 * tmp;
+ let tmp = tmp.rotate_lanes_right::<2>();
+ let minor0 = minor0 - row2 * tmp;
+ let minor2 = row0 * tmp - minor2;
+ let minor2 = minor2.rotate_lanes_right::<2>();
+
+ let tmp = (row0 * row1).reverse().rotate_lanes_right::<2>();
+ let minor2 = minor2 + row3 * tmp;
+ let minor3 = row2 * tmp - minor3;
+ let tmp = tmp.rotate_lanes_right::<2>();
+ let minor2 = row3 * tmp - minor2;
+ let minor3 = minor3 - row2 * tmp;
+
+ let tmp = (row0 * row3).reverse().rotate_lanes_right::<2>();
+ let minor1 = minor1 - row2 * tmp;
+ let minor2 = row1 * tmp + minor2;
+ let tmp = tmp.rotate_lanes_right::<2>();
+ let minor1 = row2 * tmp + minor1;
+ let minor2 = minor2 - row1 * tmp;
+
+ let tmp = (row0 * row2).reverse().rotate_lanes_right::<2>();
+ let minor1 = row3 * tmp + minor1;
+ let minor3 = minor3 - row1 * tmp;
+ let tmp = tmp.rotate_lanes_right::<2>();
+ let minor1 = minor1 - row3 * tmp;
+ let minor3 = row1 * tmp + minor3;
+
+ let det = row0 * minor0;
+ let det = det.rotate_lanes_right::<2>() + det;
+ let det = det.reverse().rotate_lanes_right::<2>() + det;
+
+ if det.horizontal_sum() == 0. {
+ return None;
+ }
+ // calculate the reciprocal
+ let tmp = f32x4::splat(1.0) / det;
+ let det = tmp + tmp - det * tmp * tmp;
+
+ let res0 = minor0 * det;
+ let res1 = minor1 * det;
+ let res2 = minor2 * det;
+ let res3 = minor3 * det;
+
+ let mut m = m;
+
+ m[0] = res0.to_array();
+ m[1] = res1.to_array();
+ m[2] = res2.to_array();
+ m[3] = res3.to_array();
+
+ Some(Matrix4x4(m))
+}
+
+#[cfg(test)]
+#[rustfmt::skip]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test() {
+ let tests: &[(Matrix4x4, Option<Matrix4x4>)] = &[
+ // Identity:
+ (Matrix4x4([
+ [1., 0., 0., 0.],
+ [0., 1., 0., 0.],
+ [0., 0., 1., 0.],
+ [0., 0., 0., 1.],
+ ]),
+ Some(Matrix4x4([
+ [1., 0., 0., 0.],
+ [0., 1., 0., 0.],
+ [0., 0., 1., 0.],
+ [0., 0., 0., 1.],
+ ]))
+ ),
+ // None:
+ (Matrix4x4([
+ [1., 2., 3., 4.],
+ [12., 11., 10., 9.],
+ [5., 6., 7., 8.],
+ [16., 15., 14., 13.],
+ ]),
+ None
+ ),
+ // Other:
+ (Matrix4x4([
+ [1., 1., 1., 0.],
+ [0., 3., 1., 2.],
+ [2., 3., 1., 0.],
+ [1., 0., 2., 1.],
+ ]),
+ Some(Matrix4x4([
+ [-3., -0.5, 1.5, 1.0],
+ [ 1., 0.25, -0.25, -0.5],
+ [ 3., 0.25, -1.25, -0.5],
+ [-3., 0.0, 1.0, 1.0],
+ ]))
+ ),
+
+
+ ];
+
+ for &(input, output) in tests {
+ assert_eq!(scalar_inv4x4(input), output);
+ assert_eq!(simd_inv4x4(input), output);
+ }
+ }
+}
+
+fn main() {
+ // Empty main to make cargo happy
+}
--- /dev/null
+#![cfg_attr(feature = "std", feature(portable_simd))]
+
+/// Benchmarks game nbody code
+/// Taken from the `packed_simd` crate
+/// Run this benchmark with `cargo test --example nbody`
+#[cfg(feature = "std")]
+mod nbody {
+ use core_simd::*;
+
+ use std::f64::consts::PI;
+ const SOLAR_MASS: f64 = 4.0 * PI * PI;
+ const DAYS_PER_YEAR: f64 = 365.24;
+
+ #[derive(Debug, Clone, Copy)]
+ struct Body {
+ pub x: f64x4,
+ pub v: f64x4,
+ pub mass: f64,
+ }
+
+ const N_BODIES: usize = 5;
+ const BODIES: [Body; N_BODIES] = [
+ // sun:
+ Body {
+ x: f64x4::from_array([0., 0., 0., 0.]),
+ v: f64x4::from_array([0., 0., 0., 0.]),
+ mass: SOLAR_MASS,
+ },
+ // jupiter:
+ Body {
+ x: f64x4::from_array([
+ 4.84143144246472090e+00,
+ -1.16032004402742839e+00,
+ -1.03622044471123109e-01,
+ 0.,
+ ]),
+ v: f64x4::from_array([
+ 1.66007664274403694e-03 * DAYS_PER_YEAR,
+ 7.69901118419740425e-03 * DAYS_PER_YEAR,
+ -6.90460016972063023e-05 * DAYS_PER_YEAR,
+ 0.,
+ ]),
+ mass: 9.54791938424326609e-04 * SOLAR_MASS,
+ },
+ // saturn:
+ Body {
+ x: f64x4::from_array([
+ 8.34336671824457987e+00,
+ 4.12479856412430479e+00,
+ -4.03523417114321381e-01,
+ 0.,
+ ]),
+ v: f64x4::from_array([
+ -2.76742510726862411e-03 * DAYS_PER_YEAR,
+ 4.99852801234917238e-03 * DAYS_PER_YEAR,
+ 2.30417297573763929e-05 * DAYS_PER_YEAR,
+ 0.,
+ ]),
+ mass: 2.85885980666130812e-04 * SOLAR_MASS,
+ },
+ // uranus:
+ Body {
+ x: f64x4::from_array([
+ 1.28943695621391310e+01,
+ -1.51111514016986312e+01,
+ -2.23307578892655734e-01,
+ 0.,
+ ]),
+ v: f64x4::from_array([
+ 2.96460137564761618e-03 * DAYS_PER_YEAR,
+ 2.37847173959480950e-03 * DAYS_PER_YEAR,
+ -2.96589568540237556e-05 * DAYS_PER_YEAR,
+ 0.,
+ ]),
+ mass: 4.36624404335156298e-05 * SOLAR_MASS,
+ },
+ // neptune:
+ Body {
+ x: f64x4::from_array([
+ 1.53796971148509165e+01,
+ -2.59193146099879641e+01,
+ 1.79258772950371181e-01,
+ 0.,
+ ]),
+ v: f64x4::from_array([
+ 2.68067772490389322e-03 * DAYS_PER_YEAR,
+ 1.62824170038242295e-03 * DAYS_PER_YEAR,
+ -9.51592254519715870e-05 * DAYS_PER_YEAR,
+ 0.,
+ ]),
+ mass: 5.15138902046611451e-05 * SOLAR_MASS,
+ },
+ ];
+
+ fn offset_momentum(bodies: &mut [Body; N_BODIES]) {
+ let (sun, rest) = bodies.split_at_mut(1);
+ let sun = &mut sun[0];
+ for body in rest {
+ let m_ratio = body.mass / SOLAR_MASS;
+ sun.v -= body.v * m_ratio;
+ }
+ }
+
+ fn energy(bodies: &[Body; N_BODIES]) -> f64 {
+ let mut e = 0.;
+ for i in 0..N_BODIES {
+ let bi = &bodies[i];
+ e += bi.mass * (bi.v * bi.v).horizontal_sum() * 0.5;
+ for bj in bodies.iter().take(N_BODIES).skip(i + 1) {
+ let dx = bi.x - bj.x;
+ e -= bi.mass * bj.mass / (dx * dx).horizontal_sum().sqrt()
+ }
+ }
+ e
+ }
+
+ fn advance(bodies: &mut [Body; N_BODIES], dt: f64) {
+ const N: usize = N_BODIES * (N_BODIES - 1) / 2;
+
+ // compute distance between bodies:
+ let mut r = [f64x4::splat(0.); N];
+ {
+ let mut i = 0;
+ for j in 0..N_BODIES {
+ for k in j + 1..N_BODIES {
+ r[i] = bodies[j].x - bodies[k].x;
+ i += 1;
+ }
+ }
+ }
+
+ let mut mag = [0.0; N];
+ for i in (0..N).step_by(2) {
+ let d2s = f64x2::from_array([
+ (r[i] * r[i]).horizontal_sum(),
+ (r[i + 1] * r[i + 1]).horizontal_sum(),
+ ]);
+ let dmags = f64x2::splat(dt) / (d2s * d2s.sqrt());
+ mag[i] = dmags[0];
+ mag[i + 1] = dmags[1];
+ }
+
+ let mut i = 0;
+ for j in 0..N_BODIES {
+ for k in j + 1..N_BODIES {
+ let f = r[i] * mag[i];
+ bodies[j].v -= f * bodies[k].mass;
+ bodies[k].v += f * bodies[j].mass;
+ i += 1
+ }
+ }
+ for body in bodies {
+ body.x += dt * body.v
+ }
+ }
+
+ pub fn run(n: usize) -> (f64, f64) {
+ let mut bodies = BODIES;
+ offset_momentum(&mut bodies);
+ let energy_before = energy(&bodies);
+ for _ in 0..n {
+ advance(&mut bodies, 0.01);
+ }
+ let energy_after = energy(&bodies);
+
+ (energy_before, energy_after)
+ }
+}
+
+#[cfg(feature = "std")]
+#[cfg(test)]
+mod tests {
+ // Good enough for demonstration purposes, not going for strictness here.
+ fn approx_eq_f64(a: f64, b: f64) -> bool {
+ (a - b).abs() < 0.00001
+ }
+ #[test]
+ fn test() {
+ const OUTPUT: [f64; 2] = [-0.169075164, -0.169087605];
+ let (energy_before, energy_after) = super::nbody::run(1000);
+ assert!(approx_eq_f64(energy_before, OUTPUT[0]));
+ assert!(approx_eq_f64(energy_after, OUTPUT[1]));
+ }
+}
+
+fn main() {
+ #[cfg(feature = "std")]
+ {
+ let (energy_before, energy_after) = nbody::run(1000);
+ println!("Energy before: {}", energy_before);
+ println!("Energy after: {}", energy_after);
+ }
+}
--- /dev/null
+use crate::simd::intrinsics;
+use crate::simd::{LaneCount, Mask, Simd, SimdElement, SupportedLaneCount};
+
+impl<T, const LANES: usize> Simd<T, LANES>
+where
+ T: SimdElement + PartialEq,
+ LaneCount<LANES>: SupportedLaneCount,
+{
+ /// Test if each lane is equal to the corresponding lane in `other`.
+ #[inline]
+ pub fn lanes_eq(self, other: Self) -> Mask<T::Mask, LANES> {
+ unsafe { Mask::from_int_unchecked(intrinsics::simd_eq(self, other)) }
+ }
+
+ /// Test if each lane is not equal to the corresponding lane in `other`.
+ #[inline]
+ pub fn lanes_ne(self, other: Self) -> Mask<T::Mask, LANES> {
+ unsafe { Mask::from_int_unchecked(intrinsics::simd_ne(self, other)) }
+ }
+}
+
+impl<T, const LANES: usize> Simd<T, LANES>
+where
+ T: SimdElement + PartialOrd,
+ LaneCount<LANES>: SupportedLaneCount,
+{
+ /// Test if each lane is less than the corresponding lane in `other`.
+ #[inline]
+ pub fn lanes_lt(self, other: Self) -> Mask<T::Mask, LANES> {
+ unsafe { Mask::from_int_unchecked(intrinsics::simd_lt(self, other)) }
+ }
+
+ /// Test if each lane is greater than the corresponding lane in `other`.
+ #[inline]
+ pub fn lanes_gt(self, other: Self) -> Mask<T::Mask, LANES> {
+ unsafe { Mask::from_int_unchecked(intrinsics::simd_gt(self, other)) }
+ }
+
+ /// Test if each lane is less than or equal to the corresponding lane in `other`.
+ #[inline]
+ pub fn lanes_le(self, other: Self) -> Mask<T::Mask, LANES> {
+ unsafe { Mask::from_int_unchecked(intrinsics::simd_le(self, other)) }
+ }
+
+ /// Test if each lane is greater than or equal to the corresponding lane in `other`.
+ #[inline]
+ pub fn lanes_ge(self, other: Self) -> Mask<T::Mask, LANES> {
+ unsafe { Mask::from_int_unchecked(intrinsics::simd_ge(self, other)) }
+ }
+}
--- /dev/null
+Portable SIMD module.
+
+This module offers a portable abstraction for SIMD operations
+that is not bound to any particular hardware architecture.
--- /dev/null
+use crate::simd::{LaneCount, Simd, SimdElement, SupportedLaneCount};
+use core::fmt;
+
+macro_rules! impl_fmt_trait {
+ { $($trait:ident,)* } => {
+ $(
+ impl<T, const LANES: usize> fmt::$trait for Simd<T, LANES>
+ where
+ LaneCount<LANES>: SupportedLaneCount,
+ T: SimdElement + fmt::$trait,
+ {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ #[repr(transparent)]
+ struct Wrapper<'a, T: fmt::$trait>(&'a T);
+
+ impl<T: fmt::$trait> fmt::Debug for Wrapper<'_, T> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ self.0.fmt(f)
+ }
+ }
+
+ f.debug_list()
+ .entries(self.as_array().iter().map(|x| Wrapper(x)))
+ .finish()
+ }
+ }
+ )*
+ }
+}
+
+impl_fmt_trait! {
+ Debug,
+ Binary,
+ LowerExp,
+ UpperExp,
+ Octal,
+ LowerHex,
+ UpperHex,
+}
--- /dev/null
+//! This module contains the LLVM intrinsics bindings that provide the functionality for this
+//! crate.
+//!
+//! The LLVM assembly language is documented here: <https://llvm.org/docs/LangRef.html>
+
+/// These intrinsics aren't linked directly from LLVM and are mostly undocumented, however they are
+/// simply lowered to the matching LLVM instructions by the compiler. The associated instruction
+/// is documented alongside each intrinsic.
+extern "platform-intrinsic" {
+ /// add/fadd
+ pub(crate) fn simd_add<T>(x: T, y: T) -> T;
+
+ /// sub/fsub
+ pub(crate) fn simd_sub<T>(x: T, y: T) -> T;
+
+ /// mul/fmul
+ pub(crate) fn simd_mul<T>(x: T, y: T) -> T;
+
+ /// udiv/sdiv/fdiv
+ pub(crate) fn simd_div<T>(x: T, y: T) -> T;
+
+ /// urem/srem/frem
+ pub(crate) fn simd_rem<T>(x: T, y: T) -> T;
+
+ /// shl
+ pub(crate) fn simd_shl<T>(x: T, y: T) -> T;
+
+ /// lshr/ashr
+ pub(crate) fn simd_shr<T>(x: T, y: T) -> T;
+
+ /// and
+ pub(crate) fn simd_and<T>(x: T, y: T) -> T;
+
+ /// or
+ pub(crate) fn simd_or<T>(x: T, y: T) -> T;
+
+ /// xor
+ pub(crate) fn simd_xor<T>(x: T, y: T) -> T;
+
+ /// fptoui/fptosi/uitofp/sitofp
+ pub(crate) fn simd_cast<T, U>(x: T) -> U;
+
+ /// neg/fneg
+ pub(crate) fn simd_neg<T>(x: T) -> T;
+
+ /// fabs
+ pub(crate) fn simd_fabs<T>(x: T) -> T;
+
+ pub(crate) fn simd_eq<T, U>(x: T, y: T) -> U;
+ pub(crate) fn simd_ne<T, U>(x: T, y: T) -> U;
+ pub(crate) fn simd_lt<T, U>(x: T, y: T) -> U;
+ pub(crate) fn simd_le<T, U>(x: T, y: T) -> U;
+ pub(crate) fn simd_gt<T, U>(x: T, y: T) -> U;
+ pub(crate) fn simd_ge<T, U>(x: T, y: T) -> U;
+
+ // shufflevector
+ pub(crate) fn simd_shuffle<T, U, V>(x: T, y: T, idx: U) -> V;
+
+ pub(crate) fn simd_gather<T, U, V>(val: T, ptr: U, mask: V) -> T;
+ pub(crate) fn simd_scatter<T, U, V>(val: T, ptr: U, mask: V);
+
+ // {s,u}add.sat
+ pub(crate) fn simd_saturating_add<T>(x: T, y: T) -> T;
+
+ // {s,u}sub.sat
+ pub(crate) fn simd_saturating_sub<T>(x: T, y: T) -> T;
+
+ // reductions
+ pub(crate) fn simd_reduce_add_ordered<T, U>(x: T, y: U) -> U;
+ pub(crate) fn simd_reduce_mul_ordered<T, U>(x: T, y: U) -> U;
+ #[allow(unused)]
+ pub(crate) fn simd_reduce_all<T>(x: T) -> bool;
+ #[allow(unused)]
+ pub(crate) fn simd_reduce_any<T>(x: T) -> bool;
+ pub(crate) fn simd_reduce_max<T, U>(x: T) -> U;
+ pub(crate) fn simd_reduce_min<T, U>(x: T) -> U;
+ pub(crate) fn simd_reduce_and<T, U>(x: T) -> U;
+ pub(crate) fn simd_reduce_or<T, U>(x: T) -> U;
+ pub(crate) fn simd_reduce_xor<T, U>(x: T) -> U;
+
+ // truncate integer vector to bitmask
+ #[allow(unused)]
+ pub(crate) fn simd_bitmask<T, U>(x: T) -> U;
+
+ // select
+ pub(crate) fn simd_select<M, T>(m: M, a: T, b: T) -> T;
+ #[allow(unused)]
+ pub(crate) fn simd_select_bitmask<M, T>(m: M, a: T, b: T) -> T;
+}
+
+#[cfg(feature = "std")]
+mod std {
+ extern "platform-intrinsic" {
+ // ceil
+ pub(crate) fn simd_ceil<T>(x: T) -> T;
+
+ // floor
+ pub(crate) fn simd_floor<T>(x: T) -> T;
+
+ // round
+ pub(crate) fn simd_round<T>(x: T) -> T;
+
+ // trunc
+ pub(crate) fn simd_trunc<T>(x: T) -> T;
+
+ // fsqrt
+ pub(crate) fn simd_fsqrt<T>(x: T) -> T;
+
+ // fma
+ pub(crate) fn simd_fma<T>(x: T, y: T, z: T) -> T;
+ }
+}
+
+#[cfg(feature = "std")]
+pub(crate) use crate::simd::intrinsics::std::*;
--- /dev/null
+use crate::simd::{LaneCount, Simd, SupportedLaneCount};
+use core::{
+ iter::{Product, Sum},
+ ops::{Add, Mul},
+};
+
+macro_rules! impl_traits {
+ { $type:ty } => {
+ impl<const LANES: usize> Sum<Self> for Simd<$type, LANES>
+ where
+ LaneCount<LANES>: SupportedLaneCount,
+ {
+ fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
+ iter.fold(Simd::splat(0 as $type), Add::add)
+ }
+ }
+
+ impl<const LANES: usize> Product<Self> for Simd<$type, LANES>
+ where
+ LaneCount<LANES>: SupportedLaneCount,
+ {
+ fn product<I: Iterator<Item = Self>>(iter: I) -> Self {
+ iter.fold(Simd::splat(1 as $type), Mul::mul)
+ }
+ }
+
+ impl<'a, const LANES: usize> Sum<&'a Self> for Simd<$type, LANES>
+ where
+ LaneCount<LANES>: SupportedLaneCount,
+ {
+ fn sum<I: Iterator<Item = &'a Self>>(iter: I) -> Self {
+ iter.fold(Simd::splat(0 as $type), Add::add)
+ }
+ }
+
+ impl<'a, const LANES: usize> Product<&'a Self> for Simd<$type, LANES>
+ where
+ LaneCount<LANES>: SupportedLaneCount,
+ {
+ fn product<I: Iterator<Item = &'a Self>>(iter: I) -> Self {
+ iter.fold(Simd::splat(1 as $type), Mul::mul)
+ }
+ }
+ }
+}
+
+impl_traits! { f32 }
+impl_traits! { f64 }
+impl_traits! { u8 }
+impl_traits! { u16 }
+impl_traits! { u32 }
+impl_traits! { u64 }
+impl_traits! { usize }
+impl_traits! { i8 }
+impl_traits! { i16 }
+impl_traits! { i32 }
+impl_traits! { i64 }
+impl_traits! { isize }
--- /dev/null
+mod sealed {
+ pub trait Sealed {}
+}
+use sealed::Sealed;
+
+/// A type representing a vector lane count.
+pub struct LaneCount<const LANES: usize>;
+
+impl<const LANES: usize> LaneCount<LANES> {
+ /// The number of bytes in a bitmask with this many lanes.
+ pub const BITMASK_LEN: usize = (LANES + 7) / 8;
+}
+
+/// Helper trait for vector lane counts.
+pub trait SupportedLaneCount: Sealed {
+ #[doc(hidden)]
+ type BitMask: Copy + Default + AsRef<[u8]> + AsMut<[u8]>;
+
+ #[doc(hidden)]
+ type IntBitMask;
+}
+
+impl<const LANES: usize> Sealed for LaneCount<LANES> {}
+
+impl SupportedLaneCount for LaneCount<1> {
+ type BitMask = [u8; 1];
+ type IntBitMask = u8;
+}
+impl SupportedLaneCount for LaneCount<2> {
+ type BitMask = [u8; 1];
+ type IntBitMask = u8;
+}
+impl SupportedLaneCount for LaneCount<4> {
+ type BitMask = [u8; 1];
+ type IntBitMask = u8;
+}
+impl SupportedLaneCount for LaneCount<8> {
+ type BitMask = [u8; 1];
+ type IntBitMask = u8;
+}
+impl SupportedLaneCount for LaneCount<16> {
+ type BitMask = [u8; 2];
+ type IntBitMask = u16;
+}
+impl SupportedLaneCount for LaneCount<32> {
+ type BitMask = [u8; 4];
+ type IntBitMask = u32;
+}
--- /dev/null
+#![cfg_attr(not(feature = "std"), no_std)]
+#![feature(
+ const_fn_trait_bound,
+ decl_macro,
+ platform_intrinsics,
+ repr_simd,
+ simd_ffi,
+ staged_api,
+ stdsimd
+)]
+#![cfg_attr(feature = "generic_const_exprs", feature(generic_const_exprs))]
+#![cfg_attr(feature = "generic_const_exprs", allow(incomplete_features))]
+#![warn(missing_docs)]
+#![deny(unsafe_op_in_unsafe_fn)]
+#![unstable(feature = "portable_simd", issue = "86656")]
+//! Portable SIMD module.
+
+#[path = "mod.rs"]
+mod core_simd;
+pub use self::core_simd::simd;
+pub use simd::*;
--- /dev/null
+//! Types and traits associated with masking lanes of vectors.
+//! Types representing
+#![allow(non_camel_case_types)]
+
+#[cfg_attr(
+ 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, SimdElement, SupportedLaneCount};
+use core::cmp::Ordering;
+use core::fmt;
+
+mod sealed {
+ use super::*;
+
+ /// Not only does this seal the `MaskElement` trait, but these functions prevent other traits
+ /// from bleeding into the parent bounds.
+ ///
+ /// For example, `eq` could be provided by requiring `MaskElement: PartialEq`, but that would
+ /// prevent us from ever removing that bound, or from implementing `MaskElement` on
+ /// non-`PartialEq` types in the future.
+ pub trait Sealed {
+ fn valid<const LANES: usize>(values: Simd<Self, LANES>) -> bool
+ where
+ LaneCount<LANES>: SupportedLaneCount,
+ Self: SimdElement;
+
+ fn eq(self, other: Self) -> bool;
+
+ const TRUE: Self;
+
+ const FALSE: Self;
+ }
+}
+use sealed::Sealed;
+
+/// Marker trait for types that may be used as SIMD mask elements.
+pub unsafe trait MaskElement: SimdElement + Sealed {}
+
+macro_rules! impl_element {
+ { $ty:ty } => {
+ impl Sealed for $ty {
+ fn valid<const LANES: usize>(value: Simd<Self, LANES>) -> bool
+ where
+ LaneCount<LANES>: SupportedLaneCount,
+ {
+ (value.lanes_eq(Simd::splat(0)) | value.lanes_eq(Simd::splat(-1))).all()
+ }
+
+ fn eq(self, other: Self) -> bool { self == other }
+
+ const TRUE: Self = -1;
+ const FALSE: Self = 0;
+ }
+
+ unsafe impl MaskElement for $ty {}
+ }
+}
+
+impl_element! { i8 }
+impl_element! { i16 }
+impl_element! { i32 }
+impl_element! { i64 }
+impl_element! { isize }
+
+/// A SIMD vector mask for `LANES` elements of width specified by `Element`.
+///
+/// The layout of this type is unspecified.
+#[repr(transparent)]
+pub struct Mask<T, const LANES: usize>(mask_impl::Mask<T, LANES>)
+where
+ T: MaskElement,
+ LaneCount<LANES>: SupportedLaneCount;
+
+impl<T, const LANES: usize> Copy for Mask<T, LANES>
+where
+ T: MaskElement,
+ LaneCount<LANES>: SupportedLaneCount,
+{
+}
+
+impl<T, const LANES: usize> Clone for Mask<T, LANES>
+where
+ T: MaskElement,
+ LaneCount<LANES>: SupportedLaneCount,
+{
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+
+impl<T, const LANES: usize> Mask<T, LANES>
+where
+ T: MaskElement,
+ LaneCount<LANES>: SupportedLaneCount,
+{
+ /// Construct a mask by setting all lanes to the given value.
+ pub fn splat(value: bool) -> Self {
+ Self(mask_impl::Mask::splat(value))
+ }
+
+ /// Converts an array to a SIMD vector.
+ pub fn from_array(array: [bool; LANES]) -> Self {
+ let mut vector = Self::splat(false);
+ for (i, v) in array.iter().enumerate() {
+ vector.set(i, *v);
+ }
+ vector
+ }
+
+ /// Converts a SIMD vector to an array.
+ pub fn to_array(self) -> [bool; LANES] {
+ let mut array = [false; LANES];
+ for (i, v) in array.iter_mut().enumerate() {
+ *v = self.test(i);
+ }
+ array
+ }
+
+ /// Converts a vector of integers to a mask, where 0 represents `false` and -1
+ /// represents `true`.
+ ///
+ /// # Safety
+ /// All lanes must be either 0 or -1.
+ #[inline]
+ pub unsafe fn from_int_unchecked(value: Simd<T, LANES>) -> Self {
+ unsafe { Self(mask_impl::Mask::from_int_unchecked(value)) }
+ }
+
+ /// Converts a vector of integers to a mask, where 0 represents `false` and -1
+ /// represents `true`.
+ ///
+ /// # Panics
+ /// Panics if any lane is not 0 or -1.
+ #[inline]
+ pub fn from_int(value: Simd<T, LANES>) -> Self {
+ assert!(T::valid(value), "all values must be either 0 or -1",);
+ unsafe { Self::from_int_unchecked(value) }
+ }
+
+ /// Converts the mask to a vector of integers, where 0 represents `false` and -1
+ /// represents `true`.
+ #[inline]
+ pub fn to_int(self) -> Simd<T, LANES> {
+ self.0.to_int()
+ }
+
+ /// Tests the value of the specified lane.
+ ///
+ /// # Safety
+ /// `lane` must be less than `LANES`.
+ #[inline]
+ pub unsafe fn test_unchecked(&self, lane: usize) -> bool {
+ unsafe { self.0.test_unchecked(lane) }
+ }
+
+ /// Tests the value of the specified lane.
+ ///
+ /// # Panics
+ /// Panics if `lane` is greater than or equal to the number of lanes in the vector.
+ #[inline]
+ pub fn test(&self, lane: usize) -> bool {
+ assert!(lane < LANES, "lane index out of range");
+ unsafe { self.test_unchecked(lane) }
+ }
+
+ /// Sets the value of the specified lane.
+ ///
+ /// # Safety
+ /// `lane` must be less than `LANES`.
+ #[inline]
+ pub unsafe fn set_unchecked(&mut self, lane: usize, value: bool) {
+ unsafe {
+ self.0.set_unchecked(lane, value);
+ }
+ }
+
+ /// Sets the value of the specified lane.
+ ///
+ /// # Panics
+ /// Panics if `lane` is greater than or equal to the number of lanes in the vector.
+ #[inline]
+ pub fn set(&mut self, lane: usize, value: bool) {
+ assert!(lane < LANES, "lane index out of range");
+ unsafe {
+ self.set_unchecked(lane, value);
+ }
+ }
+
+ /// Convert this mask to a bitmask, with one bit set per lane.
+ #[cfg(feature = "generic_const_exprs")]
+ pub fn to_bitmask(self) -> [u8; LaneCount::<LANES>::BITMASK_LEN] {
+ self.0.to_bitmask()
+ }
+
+ /// Convert a bitmask to a mask.
+ #[cfg(feature = "generic_const_exprs")]
+ pub fn from_bitmask(bitmask: [u8; LaneCount::<LANES>::BITMASK_LEN]) -> Self {
+ Self(mask_impl::Mask::from_bitmask(bitmask))
+ }
+
+ /// Returns true if any lane is set, or false otherwise.
+ #[inline]
+ pub fn any(self) -> bool {
+ self.0.any()
+ }
+
+ /// Returns true if all lanes are set, or false otherwise.
+ #[inline]
+ pub fn all(self) -> bool {
+ self.0.all()
+ }
+}
+
+// vector/array conversion
+impl<T, const LANES: usize> From<[bool; LANES]> for Mask<T, LANES>
+where
+ T: MaskElement,
+ LaneCount<LANES>: SupportedLaneCount,
+{
+ fn from(array: [bool; LANES]) -> Self {
+ Self::from_array(array)
+ }
+}
+
+impl<T, const LANES: usize> From<Mask<T, LANES>> for [bool; LANES]
+where
+ T: MaskElement,
+ LaneCount<LANES>: SupportedLaneCount,
+{
+ fn from(vector: Mask<T, LANES>) -> Self {
+ vector.to_array()
+ }
+}
+
+impl<T, const LANES: usize> Default for Mask<T, LANES>
+where
+ T: MaskElement,
+ LaneCount<LANES>: SupportedLaneCount,
+{
+ #[inline]
+ fn default() -> Self {
+ Self::splat(false)
+ }
+}
+
+impl<T, const LANES: usize> PartialEq for Mask<T, LANES>
+where
+ T: MaskElement + PartialEq,
+ LaneCount<LANES>: SupportedLaneCount,
+{
+ #[inline]
+ fn eq(&self, other: &Self) -> bool {
+ self.0 == other.0
+ }
+}
+
+impl<T, const LANES: usize> PartialOrd for Mask<T, LANES>
+where
+ T: MaskElement + PartialOrd,
+ LaneCount<LANES>: SupportedLaneCount,
+{
+ #[inline]
+ fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+ self.0.partial_cmp(&other.0)
+ }
+}
+
+impl<T, const LANES: usize> fmt::Debug for Mask<T, LANES>
+where
+ T: MaskElement + fmt::Debug,
+ LaneCount<LANES>: SupportedLaneCount,
+{
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_list()
+ .entries((0..LANES).map(|lane| self.test(lane)))
+ .finish()
+ }
+}
+
+impl<T, const LANES: usize> core::ops::BitAnd for Mask<T, LANES>
+where
+ T: MaskElement,
+ LaneCount<LANES>: SupportedLaneCount,
+{
+ type Output = Self;
+ #[inline]
+ fn bitand(self, rhs: Self) -> Self {
+ Self(self.0 & rhs.0)
+ }
+}
+
+impl<T, const LANES: usize> core::ops::BitAnd<bool> for Mask<T, LANES>
+where
+ T: MaskElement,
+ LaneCount<LANES>: SupportedLaneCount,
+{
+ type Output = Self;
+ #[inline]
+ fn bitand(self, rhs: bool) -> Self {
+ self & Self::splat(rhs)
+ }
+}
+
+impl<T, const LANES: usize> core::ops::BitAnd<Mask<T, LANES>> for bool
+where
+ T: MaskElement,
+ LaneCount<LANES>: SupportedLaneCount,
+{
+ type Output = Mask<T, LANES>;
+ #[inline]
+ fn bitand(self, rhs: Mask<T, LANES>) -> Mask<T, LANES> {
+ Mask::splat(self) & rhs
+ }
+}
+
+impl<T, const LANES: usize> core::ops::BitOr for Mask<T, LANES>
+where
+ T: MaskElement,
+ LaneCount<LANES>: SupportedLaneCount,
+{
+ type Output = Self;
+ #[inline]
+ fn bitor(self, rhs: Self) -> Self {
+ Self(self.0 | rhs.0)
+ }
+}
+
+impl<T, const LANES: usize> core::ops::BitOr<bool> for Mask<T, LANES>
+where
+ T: MaskElement,
+ LaneCount<LANES>: SupportedLaneCount,
+{
+ type Output = Self;
+ #[inline]
+ fn bitor(self, rhs: bool) -> Self {
+ self | Self::splat(rhs)
+ }
+}
+
+impl<T, const LANES: usize> core::ops::BitOr<Mask<T, LANES>> for bool
+where
+ T: MaskElement,
+ LaneCount<LANES>: SupportedLaneCount,
+{
+ type Output = Mask<T, LANES>;
+ #[inline]
+ fn bitor(self, rhs: Mask<T, LANES>) -> Mask<T, LANES> {
+ Mask::splat(self) | rhs
+ }
+}
+
+impl<T, const LANES: usize> core::ops::BitXor for Mask<T, LANES>
+where
+ T: MaskElement,
+ LaneCount<LANES>: SupportedLaneCount,
+{
+ type Output = Self;
+ #[inline]
+ fn bitxor(self, rhs: Self) -> Self::Output {
+ Self(self.0 ^ rhs.0)
+ }
+}
+
+impl<T, const LANES: usize> core::ops::BitXor<bool> for Mask<T, LANES>
+where
+ T: MaskElement,
+ LaneCount<LANES>: SupportedLaneCount,
+{
+ type Output = Self;
+ #[inline]
+ fn bitxor(self, rhs: bool) -> Self::Output {
+ self ^ Self::splat(rhs)
+ }
+}
+
+impl<T, const LANES: usize> core::ops::BitXor<Mask<T, LANES>> for bool
+where
+ T: MaskElement,
+ LaneCount<LANES>: SupportedLaneCount,
+{
+ type Output = Mask<T, LANES>;
+ #[inline]
+ fn bitxor(self, rhs: Mask<T, LANES>) -> Self::Output {
+ Mask::splat(self) ^ rhs
+ }
+}
+
+impl<T, const LANES: usize> core::ops::Not for Mask<T, LANES>
+where
+ T: MaskElement,
+ LaneCount<LANES>: SupportedLaneCount,
+{
+ type Output = Mask<T, LANES>;
+ #[inline]
+ fn not(self) -> Self::Output {
+ Self(!self.0)
+ }
+}
+
+impl<T, const LANES: usize> core::ops::BitAndAssign for Mask<T, LANES>
+where
+ T: MaskElement,
+ LaneCount<LANES>: SupportedLaneCount,
+{
+ #[inline]
+ fn bitand_assign(&mut self, rhs: Self) {
+ self.0 = self.0 & rhs.0;
+ }
+}
+
+impl<T, const LANES: usize> core::ops::BitAndAssign<bool> for Mask<T, LANES>
+where
+ T: MaskElement,
+ LaneCount<LANES>: SupportedLaneCount,
+{
+ #[inline]
+ fn bitand_assign(&mut self, rhs: bool) {
+ *self &= Self::splat(rhs);
+ }
+}
+
+impl<T, const LANES: usize> core::ops::BitOrAssign for Mask<T, LANES>
+where
+ T: MaskElement,
+ LaneCount<LANES>: SupportedLaneCount,
+{
+ #[inline]
+ fn bitor_assign(&mut self, rhs: Self) {
+ self.0 = self.0 | rhs.0;
+ }
+}
+
+impl<T, const LANES: usize> core::ops::BitOrAssign<bool> for Mask<T, LANES>
+where
+ T: MaskElement,
+ LaneCount<LANES>: SupportedLaneCount,
+{
+ #[inline]
+ fn bitor_assign(&mut self, rhs: bool) {
+ *self |= Self::splat(rhs);
+ }
+}
+
+impl<T, const LANES: usize> core::ops::BitXorAssign for Mask<T, LANES>
+where
+ T: MaskElement,
+ LaneCount<LANES>: SupportedLaneCount,
+{
+ #[inline]
+ fn bitxor_assign(&mut self, rhs: Self) {
+ self.0 = self.0 ^ rhs.0;
+ }
+}
+
+impl<T, const LANES: usize> core::ops::BitXorAssign<bool> for Mask<T, LANES>
+where
+ T: MaskElement,
+ LaneCount<LANES>: SupportedLaneCount,
+{
+ #[inline]
+ fn bitxor_assign(&mut self, rhs: bool) {
+ *self ^= Self::splat(rhs);
+ }
+}
+
+/// Vector of eight 8-bit masks
+pub type mask8x8 = Mask<i8, 8>;
+
+/// Vector of 16 8-bit masks
+pub type mask8x16 = Mask<i8, 16>;
+
+/// Vector of 32 8-bit masks
+pub type mask8x32 = Mask<i8, 32>;
+
+/// Vector of 16 8-bit masks
+pub type mask8x64 = Mask<i8, 64>;
+
+/// Vector of four 16-bit masks
+pub type mask16x4 = Mask<i16, 4>;
+
+/// Vector of eight 16-bit masks
+pub type mask16x8 = Mask<i16, 8>;
+
+/// Vector of 16 16-bit masks
+pub type mask16x16 = Mask<i16, 16>;
+
+/// Vector of 32 16-bit masks
+pub type mask16x32 = Mask<i32, 32>;
+
+/// Vector of two 32-bit masks
+pub type mask32x2 = Mask<i32, 2>;
+
+/// Vector of four 32-bit masks
+pub type mask32x4 = Mask<i32, 4>;
+
+/// Vector of eight 32-bit masks
+pub type mask32x8 = Mask<i32, 8>;
+
+/// Vector of 16 32-bit masks
+pub type mask32x16 = Mask<i32, 16>;
+
+/// Vector of two 64-bit masks
+pub type mask64x2 = Mask<i64, 2>;
+
+/// Vector of four 64-bit masks
+pub type mask64x4 = Mask<i64, 4>;
+
+/// Vector of eight 64-bit masks
+pub type mask64x8 = Mask<i64, 8>;
+
+/// Vector of two pointer-width masks
+pub type masksizex2 = Mask<isize, 2>;
+
+/// Vector of four pointer-width masks
+pub type masksizex4 = Mask<isize, 4>;
+
+/// Vector of eight pointer-width masks
+pub type masksizex8 = Mask<isize, 8>;
+
+macro_rules! impl_from {
+ { $from:ty => $($to:ty),* } => {
+ $(
+ impl<const LANES: usize> From<Mask<$from, LANES>> for Mask<$to, LANES>
+ where
+ LaneCount<LANES>: SupportedLaneCount,
+ {
+ fn from(value: Mask<$from, LANES>) -> Self {
+ Self(value.0.convert())
+ }
+ }
+ )*
+ }
+}
+impl_from! { i8 => i16, i32, i64, isize }
+impl_from! { i16 => i32, i64, isize, i8 }
+impl_from! { i32 => i64, isize, i8, i16 }
+impl_from! { i64 => isize, i8, i16, i32 }
+impl_from! { isize => i8, i16, i32, i64 }
--- /dev/null
+use super::MaskElement;
+use crate::simd::intrinsics;
+use crate::simd::{LaneCount, Simd, SupportedLaneCount};
+use core::marker::PhantomData;
+
+/// A mask where each lane is represented by a single bit.
+#[repr(transparent)]
+pub struct Mask<T, const LANES: usize>(
+ <LaneCount<LANES> as SupportedLaneCount>::BitMask,
+ PhantomData<T>,
+)
+where
+ T: MaskElement,
+ LaneCount<LANES>: SupportedLaneCount;
+
+impl<T, const LANES: usize> Copy for Mask<T, LANES>
+where
+ T: MaskElement,
+ LaneCount<LANES>: SupportedLaneCount,
+{
+}
+
+impl<T, const LANES: usize> Clone for Mask<T, LANES>
+where
+ T: MaskElement,
+ LaneCount<LANES>: SupportedLaneCount,
+{
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+
+impl<T, const LANES: usize> PartialEq for Mask<T, LANES>
+where
+ T: MaskElement,
+ LaneCount<LANES>: SupportedLaneCount,
+{
+ fn eq(&self, other: &Self) -> bool {
+ self.0.as_ref() == other.0.as_ref()
+ }
+}
+
+impl<T, const LANES: usize> PartialOrd for Mask<T, LANES>
+where
+ T: MaskElement,
+ LaneCount<LANES>: SupportedLaneCount,
+{
+ fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
+ self.0.as_ref().partial_cmp(other.0.as_ref())
+ }
+}
+
+impl<T, const LANES: usize> Eq for Mask<T, LANES>
+where
+ T: MaskElement,
+ LaneCount<LANES>: SupportedLaneCount,
+{
+}
+
+impl<T, const LANES: usize> Ord for Mask<T, LANES>
+where
+ T: MaskElement,
+ LaneCount<LANES>: SupportedLaneCount,
+{
+ fn cmp(&self, other: &Self) -> core::cmp::Ordering {
+ self.0.as_ref().cmp(other.0.as_ref())
+ }
+}
+
+impl<T, const LANES: usize> Mask<T, LANES>
+where
+ T: MaskElement,
+ LaneCount<LANES>: SupportedLaneCount,
+{
+ #[inline]
+ pub fn splat(value: bool) -> Self {
+ let mut mask = <LaneCount<LANES> as SupportedLaneCount>::BitMask::default();
+ if value {
+ mask.as_mut().fill(u8::MAX)
+ } else {
+ mask.as_mut().fill(u8::MIN)
+ }
+ if LANES % 8 > 0 {
+ *mask.as_mut().last_mut().unwrap() &= u8::MAX >> (8 - LANES % 8);
+ }
+ Self(mask, PhantomData)
+ }
+
+ #[inline]
+ pub unsafe fn test_unchecked(&self, lane: usize) -> bool {
+ (self.0.as_ref()[lane / 8] >> (lane % 8)) & 0x1 > 0
+ }
+
+ #[inline]
+ pub 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]
+ pub fn to_int(self) -> Simd<T, LANES> {
+ unsafe {
+ let mask: <LaneCount<LANES> as SupportedLaneCount>::IntBitMask =
+ core::mem::transmute_copy(&self);
+ intrinsics::simd_select_bitmask(mask, Simd::splat(T::TRUE), Simd::splat(T::FALSE))
+ }
+ }
+
+ #[inline]
+ pub unsafe fn from_int_unchecked(value: Simd<T, LANES>) -> Self {
+ // TODO remove the transmute when rustc is more flexible
+ assert_eq!(
+ core::mem::size_of::<<LaneCount::<LANES> as SupportedLaneCount>::BitMask>(),
+ core::mem::size_of::<<LaneCount::<LANES> as SupportedLaneCount>::IntBitMask>(),
+ );
+ unsafe {
+ let mask: <LaneCount<LANES> as SupportedLaneCount>::IntBitMask =
+ intrinsics::simd_bitmask(value);
+ Self(core::mem::transmute_copy(&mask), PhantomData)
+ }
+ }
+
+ #[cfg(feature = "generic_const_exprs")]
+ #[inline]
+ pub fn to_bitmask(self) -> [u8; LaneCount::<LANES>::BITMASK_LEN] {
+ // Safety: these are the same type and we are laundering the generic
+ unsafe { core::mem::transmute_copy(&self.0) }
+ }
+
+ #[cfg(feature = "generic_const_exprs")]
+ #[inline]
+ pub fn from_bitmask(bitmask: [u8; LaneCount::<LANES>::BITMASK_LEN]) -> Self {
+ // Safety: these are the same type and we are laundering the generic
+ Self(unsafe { core::mem::transmute_copy(&bitmask) }, PhantomData)
+ }
+
+ #[inline]
+ pub fn convert<U>(self) -> Mask<U, LANES>
+ where
+ U: MaskElement,
+ {
+ unsafe { core::mem::transmute_copy(&self) }
+ }
+
+ #[inline]
+ pub fn any(self) -> bool {
+ self != Self::splat(false)
+ }
+
+ #[inline]
+ pub fn all(self) -> bool {
+ self == Self::splat(true)
+ }
+}
+
+impl<T, const LANES: usize> core::ops::BitAnd for Mask<T, LANES>
+where
+ T: MaskElement,
+ LaneCount<LANES>: SupportedLaneCount,
+ <LaneCount<LANES> 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 LANES: usize> core::ops::BitOr for Mask<T, LANES>
+where
+ T: MaskElement,
+ LaneCount<LANES>: SupportedLaneCount,
+ <LaneCount<LANES> 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 LANES: usize> core::ops::BitXor for Mask<T, LANES>
+where
+ T: MaskElement,
+ LaneCount<LANES>: 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 LANES: usize> core::ops::Not for Mask<T, LANES>
+where
+ T: MaskElement,
+ LaneCount<LANES>: SupportedLaneCount,
+{
+ type Output = Self;
+ #[inline]
+ fn not(mut self) -> Self::Output {
+ for x in self.0.as_mut() {
+ *x = !*x;
+ }
+ if LANES % 8 > 0 {
+ *self.0.as_mut().last_mut().unwrap() &= u8::MAX >> (8 - LANES % 8);
+ }
+ self
+ }
+}
--- /dev/null
+//! Masks that take up full SIMD vector registers.
+
+use super::MaskElement;
+use crate::simd::intrinsics;
+use crate::simd::{LaneCount, Simd, SupportedLaneCount};
+
+#[repr(transparent)]
+pub struct Mask<T, const LANES: usize>(Simd<T, LANES>)
+where
+ T: MaskElement,
+ LaneCount<LANES>: SupportedLaneCount;
+
+impl<T, const LANES: usize> Copy for Mask<T, LANES>
+where
+ T: MaskElement,
+ LaneCount<LANES>: SupportedLaneCount,
+{
+}
+
+impl<T, const LANES: usize> Clone for Mask<T, LANES>
+where
+ T: MaskElement,
+ LaneCount<LANES>: SupportedLaneCount,
+{
+ #[inline]
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+
+impl<T, const LANES: usize> PartialEq for Mask<T, LANES>
+where
+ T: MaskElement + PartialEq,
+ LaneCount<LANES>: SupportedLaneCount,
+{
+ fn eq(&self, other: &Self) -> bool {
+ self.0.eq(&other.0)
+ }
+}
+
+impl<T, const LANES: usize> PartialOrd for Mask<T, LANES>
+where
+ T: MaskElement + PartialOrd,
+ LaneCount<LANES>: SupportedLaneCount,
+{
+ fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
+ self.0.partial_cmp(&other.0)
+ }
+}
+
+impl<T, const LANES: usize> Eq for Mask<T, LANES>
+where
+ T: MaskElement + Eq,
+ LaneCount<LANES>: SupportedLaneCount,
+{
+}
+
+impl<T, const LANES: usize> Ord for Mask<T, LANES>
+where
+ T: MaskElement + Ord,
+ LaneCount<LANES>: SupportedLaneCount,
+{
+ fn cmp(&self, other: &Self) -> core::cmp::Ordering {
+ self.0.cmp(&other.0)
+ }
+}
+
+impl<T, const LANES: usize> Mask<T, LANES>
+where
+ T: MaskElement,
+ LaneCount<LANES>: SupportedLaneCount,
+{
+ pub fn splat(value: bool) -> Self {
+ Self(Simd::splat(if value { T::TRUE } else { T::FALSE }))
+ }
+
+ #[inline]
+ pub unsafe fn test_unchecked(&self, lane: usize) -> bool {
+ T::eq(self.0[lane], T::TRUE)
+ }
+
+ #[inline]
+ pub unsafe fn set_unchecked(&mut self, lane: usize, value: bool) {
+ self.0[lane] = if value { T::TRUE } else { T::FALSE }
+ }
+
+ #[inline]
+ pub fn to_int(self) -> Simd<T, LANES> {
+ self.0
+ }
+
+ #[inline]
+ pub unsafe fn from_int_unchecked(value: Simd<T, LANES>) -> Self {
+ Self(value)
+ }
+
+ #[inline]
+ pub fn convert<U>(self) -> Mask<U, LANES>
+ where
+ U: MaskElement,
+ {
+ unsafe { Mask(intrinsics::simd_cast(self.0)) }
+ }
+
+ #[cfg(feature = "generic_const_exprs")]
+ #[inline]
+ pub fn to_bitmask(self) -> [u8; LaneCount::<LANES>::BITMASK_LEN] {
+ unsafe {
+ // TODO remove the transmute when rustc can use arrays of u8 as bitmasks
+ assert_eq!(
+ core::mem::size_of::<<LaneCount::<LANES> as SupportedLaneCount>::IntBitMask>(),
+ LaneCount::<LANES>::BITMASK_LEN,
+ );
+ let bitmask: <LaneCount<LANES> as SupportedLaneCount>::IntBitMask =
+ intrinsics::simd_bitmask(self.0);
+ let mut bitmask: [u8; LaneCount::<LANES>::BITMASK_LEN] =
+ core::mem::transmute_copy(&bitmask);
+
+ // There is a bug where LLVM appears to implement this operation with the wrong
+ // bit order.
+ // TODO fix this in a better way
+ if cfg!(target_endian = "big") {
+ for x in bitmask.as_mut() {
+ *x = x.reverse_bits();
+ }
+ }
+
+ bitmask
+ }
+ }
+
+ #[cfg(feature = "generic_const_exprs")]
+ #[inline]
+ pub fn from_bitmask(mut bitmask: [u8; LaneCount::<LANES>::BITMASK_LEN]) -> Self {
+ unsafe {
+ // There is a bug where LLVM appears to implement this operation with the wrong
+ // bit order.
+ // TODO fix this in a better way
+ if cfg!(target_endian = "big") {
+ for x in bitmask.as_mut() {
+ *x = x.reverse_bits();
+ }
+ }
+
+ // TODO remove the transmute when rustc can use arrays of u8 as bitmasks
+ assert_eq!(
+ core::mem::size_of::<<LaneCount::<LANES> as SupportedLaneCount>::IntBitMask>(),
+ LaneCount::<LANES>::BITMASK_LEN,
+ );
+ let bitmask: <LaneCount<LANES> as SupportedLaneCount>::IntBitMask =
+ core::mem::transmute_copy(&bitmask);
+
+ Self::from_int_unchecked(intrinsics::simd_select_bitmask(
+ bitmask,
+ Self::splat(true).to_int(),
+ Self::splat(false).to_int(),
+ ))
+ }
+ }
+
+ #[inline]
+ pub fn any(self) -> bool {
+ unsafe { intrinsics::simd_reduce_any(self.to_int()) }
+ }
+
+ #[inline]
+ pub fn all(self) -> bool {
+ unsafe { intrinsics::simd_reduce_all(self.to_int()) }
+ }
+}
+
+impl<T, const LANES: usize> core::convert::From<Mask<T, LANES>> for Simd<T, LANES>
+where
+ T: MaskElement,
+ LaneCount<LANES>: SupportedLaneCount,
+{
+ fn from(value: Mask<T, LANES>) -> Self {
+ value.0
+ }
+}
+
+impl<T, const LANES: usize> core::ops::BitAnd for Mask<T, LANES>
+where
+ T: MaskElement,
+ LaneCount<LANES>: SupportedLaneCount,
+{
+ type Output = Self;
+ #[inline]
+ fn bitand(self, rhs: Self) -> Self {
+ unsafe { Self(intrinsics::simd_and(self.0, rhs.0)) }
+ }
+}
+
+impl<T, const LANES: usize> core::ops::BitOr for Mask<T, LANES>
+where
+ T: MaskElement,
+ LaneCount<LANES>: SupportedLaneCount,
+{
+ type Output = Self;
+ #[inline]
+ fn bitor(self, rhs: Self) -> Self {
+ unsafe { Self(intrinsics::simd_or(self.0, rhs.0)) }
+ }
+}
+
+impl<T, const LANES: usize> core::ops::BitXor for Mask<T, LANES>
+where
+ T: MaskElement,
+ LaneCount<LANES>: SupportedLaneCount,
+{
+ type Output = Self;
+ #[inline]
+ fn bitxor(self, rhs: Self) -> Self {
+ unsafe { Self(intrinsics::simd_xor(self.0, rhs.0)) }
+ }
+}
+
+impl<T, const LANES: usize> core::ops::Not for Mask<T, LANES>
+where
+ T: MaskElement,
+ LaneCount<LANES>: SupportedLaneCount,
+{
+ type Output = Self;
+ #[inline]
+ fn not(self) -> Self::Output {
+ Self::splat(true) ^ self
+ }
+}
--- /dev/null
+use crate::simd::intrinsics::{simd_saturating_add, simd_saturating_sub};
+use crate::simd::{LaneCount, Simd, SupportedLaneCount};
+
+macro_rules! impl_uint_arith {
+ ($($ty:ty),+) => {
+ $( impl<const LANES: usize> Simd<$ty, LANES> where LaneCount<LANES>: SupportedLaneCount {
+
+ /// Lanewise saturating add.
+ ///
+ /// # Examples
+ /// ```
+ /// # #![feature(portable_simd)]
+ /// # #[cfg(feature = "std")] use core_simd::Simd;
+ /// # #[cfg(not(feature = "std"))] use core::simd::Simd;
+ #[doc = concat!("# use core::", stringify!($ty), "::MAX;")]
+ /// let x = Simd::from_array([2, 1, 0, MAX]);
+ /// let max = Simd::splat(MAX);
+ /// let unsat = x + max;
+ /// let sat = x.saturating_add(max);
+ /// assert_eq!(x - 1, unsat);
+ /// assert_eq!(sat, max);
+ /// ```
+ #[inline]
+ pub fn saturating_add(self, second: Self) -> Self {
+ unsafe { simd_saturating_add(self, second) }
+ }
+
+ /// Lanewise saturating subtract.
+ ///
+ /// # Examples
+ /// ```
+ /// # #![feature(portable_simd)]
+ /// # #[cfg(feature = "std")] use core_simd::Simd;
+ /// # #[cfg(not(feature = "std"))] use core::simd::Simd;
+ #[doc = concat!("# use core::", stringify!($ty), "::MAX;")]
+ /// let x = Simd::from_array([2, 1, 0, MAX]);
+ /// let max = Simd::splat(MAX);
+ /// let unsat = x - max;
+ /// let sat = x.saturating_sub(max);
+ /// assert_eq!(unsat, x + 1);
+ /// assert_eq!(sat, Simd::splat(0));
+ #[inline]
+ pub fn saturating_sub(self, second: Self) -> Self {
+ unsafe { simd_saturating_sub(self, second) }
+ }
+ })+
+ }
+}
+
+macro_rules! impl_int_arith {
+ ($($ty:ty),+) => {
+ $( impl<const LANES: usize> Simd<$ty, LANES> where LaneCount<LANES>: SupportedLaneCount {
+
+ /// Lanewise saturating add.
+ ///
+ /// # Examples
+ /// ```
+ /// # #![feature(portable_simd)]
+ /// # #[cfg(feature = "std")] use core_simd::Simd;
+ /// # #[cfg(not(feature = "std"))] use core::simd::Simd;
+ #[doc = concat!("# use core::", stringify!($ty), "::{MIN, MAX};")]
+ /// let x = Simd::from_array([MIN, 0, 1, MAX]);
+ /// let max = Simd::splat(MAX);
+ /// let unsat = x + max;
+ /// let sat = x.saturating_add(max);
+ /// assert_eq!(unsat, Simd::from_array([-1, MAX, MIN, -2]));
+ /// assert_eq!(sat, Simd::from_array([-1, MAX, MAX, MAX]));
+ /// ```
+ #[inline]
+ pub fn saturating_add(self, second: Self) -> Self {
+ unsafe { simd_saturating_add(self, second) }
+ }
+
+ /// Lanewise saturating subtract.
+ ///
+ /// # Examples
+ /// ```
+ /// # #![feature(portable_simd)]
+ /// # #[cfg(feature = "std")] use core_simd::Simd;
+ /// # #[cfg(not(feature = "std"))] use core::simd::Simd;
+ #[doc = concat!("# use core::", stringify!($ty), "::{MIN, MAX};")]
+ /// let x = Simd::from_array([MIN, -2, -1, MAX]);
+ /// let max = Simd::splat(MAX);
+ /// let unsat = x - max;
+ /// let sat = x.saturating_sub(max);
+ /// assert_eq!(unsat, Simd::from_array([1, MAX, MIN, 0]));
+ /// assert_eq!(sat, Simd::from_array([MIN, MIN, MIN, 0]));
+ #[inline]
+ pub fn saturating_sub(self, second: Self) -> Self {
+ unsafe { simd_saturating_sub(self, second) }
+ }
+
+ /// Lanewise absolute value, implemented in Rust.
+ /// Every lane becomes its absolute value.
+ ///
+ /// # Examples
+ /// ```
+ /// # #![feature(portable_simd)]
+ /// # #[cfg(feature = "std")] use core_simd::Simd;
+ /// # #[cfg(not(feature = "std"))] use core::simd::Simd;
+ #[doc = concat!("# use core::", stringify!($ty), "::{MIN, MAX};")]
+ /// let xs = Simd::from_array([MIN, MIN +1, -5, 0]);
+ /// assert_eq!(xs.abs(), Simd::from_array([MIN, MAX, 5, 0]));
+ /// ```
+ #[inline]
+ pub fn abs(self) -> Self {
+ const SHR: $ty = <$ty>::BITS as $ty - 1;
+ let m = self >> SHR;
+ (self^m) - m
+ }
+
+ /// Lanewise saturating absolute value, implemented in Rust.
+ /// As abs(), except the MIN value becomes MAX instead of itself.
+ ///
+ /// # Examples
+ /// ```
+ /// # #![feature(portable_simd)]
+ /// # #[cfg(feature = "std")] use core_simd::Simd;
+ /// # #[cfg(not(feature = "std"))] use core::simd::Simd;
+ #[doc = concat!("# use core::", stringify!($ty), "::{MIN, MAX};")]
+ /// let xs = Simd::from_array([MIN, -2, 0, 3]);
+ /// let unsat = xs.abs();
+ /// let sat = xs.saturating_abs();
+ /// assert_eq!(unsat, Simd::from_array([MIN, 2, 0, 3]));
+ /// assert_eq!(sat, Simd::from_array([MAX, 2, 0, 3]));
+ /// ```
+ #[inline]
+ pub fn saturating_abs(self) -> Self {
+ // arith shift for -1 or 0 mask based on sign bit, giving 2s complement
+ const SHR: $ty = <$ty>::BITS as $ty - 1;
+ let m = self >> SHR;
+ (self^m).saturating_sub(m)
+ }
+
+ /// Lanewise saturating negation, implemented in Rust.
+ /// As neg(), except the MIN value becomes MAX instead of itself.
+ ///
+ /// # Examples
+ /// ```
+ /// # #![feature(portable_simd)]
+ /// # #[cfg(feature = "std")] use core_simd::Simd;
+ /// # #[cfg(not(feature = "std"))] use core::simd::Simd;
+ #[doc = concat!("# use core::", stringify!($ty), "::{MIN, MAX};")]
+ /// let x = Simd::from_array([MIN, -2, 3, MAX]);
+ /// let unsat = -x;
+ /// let sat = x.saturating_neg();
+ /// assert_eq!(unsat, Simd::from_array([MIN, 2, -3, MIN + 1]));
+ /// assert_eq!(sat, Simd::from_array([MAX, 2, -3, MIN + 1]));
+ /// ```
+ #[inline]
+ pub fn saturating_neg(self) -> Self {
+ Self::splat(0).saturating_sub(self)
+ }
+ })+
+ }
+}
+
+impl_uint_arith! { u8, u16, u32, u64, usize }
+impl_int_arith! { i8, i16, i32, i64, isize }
--- /dev/null
+#[macro_use]
+mod reduction;
+
+#[macro_use]
+mod swizzle;
+
+pub(crate) mod intrinsics;
+
+#[cfg(feature = "generic_const_exprs")]
+mod to_bytes;
+
+mod comparisons;
+mod fmt;
+mod iter;
+mod lane_count;
+mod masks;
+mod math;
+mod ops;
+mod round;
+mod select;
+mod vector;
+mod vendor;
+
+#[doc = include_str!("core_simd_docs.md")]
+pub mod simd {
+ pub(crate) use crate::core_simd::intrinsics;
+
+ pub use crate::core_simd::lane_count::{LaneCount, SupportedLaneCount};
+ pub use crate::core_simd::masks::*;
+ pub use crate::core_simd::select::Select;
+ pub use crate::core_simd::swizzle::*;
+ pub use crate::core_simd::vector::*;
+}
--- /dev/null
+use crate::simd::intrinsics;
+use crate::simd::{LaneCount, Simd, SimdElement, SupportedLaneCount};
+
+impl<I, T, const LANES: usize> core::ops::Index<I> for Simd<T, LANES>
+where
+ T: SimdElement,
+ LaneCount<LANES>: SupportedLaneCount,
+ I: core::slice::SliceIndex<[T]>,
+{
+ type Output = I::Output;
+ fn index(&self, index: I) -> &Self::Output {
+ &self.as_array()[index]
+ }
+}
+
+impl<I, T, const LANES: usize> core::ops::IndexMut<I> for Simd<T, LANES>
+where
+ T: SimdElement,
+ LaneCount<LANES>: SupportedLaneCount,
+ I: core::slice::SliceIndex<[T]>,
+{
+ fn index_mut(&mut self, index: I) -> &mut Self::Output {
+ &mut self.as_mut_array()[index]
+ }
+}
+
+/// Checks if the right-hand side argument of a left- or right-shift would cause overflow.
+fn invalid_shift_rhs<T>(rhs: T) -> bool
+where
+ T: Default + PartialOrd + core::convert::TryFrom<usize>,
+ <T as core::convert::TryFrom<usize>>::Error: core::fmt::Debug,
+{
+ let bits_in_type = T::try_from(8 * core::mem::size_of::<T>()).unwrap();
+ rhs < T::default() || rhs >= bits_in_type
+}
+
+/// Automatically implements operators over references in addition to the provided operator.
+macro_rules! impl_ref_ops {
+ // binary op
+ {
+ impl<const $lanes:ident: usize> core::ops::$trait:ident<$rhs:ty> for $type:ty
+ where
+ LaneCount<$lanes2:ident>: SupportedLaneCount,
+ {
+ type Output = $output:ty;
+
+ $(#[$attrs:meta])*
+ fn $fn:ident($self_tok:ident, $rhs_arg:ident: $rhs_arg_ty:ty) -> Self::Output $body:tt
+ }
+ } => {
+ impl<const $lanes: usize> core::ops::$trait<$rhs> for $type
+ where
+ LaneCount<$lanes2>: SupportedLaneCount,
+ {
+ type Output = $output;
+
+ $(#[$attrs])*
+ fn $fn($self_tok, $rhs_arg: $rhs_arg_ty) -> Self::Output $body
+ }
+
+ impl<const $lanes: usize> core::ops::$trait<&'_ $rhs> for $type
+ where
+ LaneCount<$lanes2>: SupportedLaneCount,
+ {
+ type Output = <$type as core::ops::$trait<$rhs>>::Output;
+
+ $(#[$attrs])*
+ fn $fn($self_tok, $rhs_arg: &$rhs) -> Self::Output {
+ core::ops::$trait::$fn($self_tok, *$rhs_arg)
+ }
+ }
+
+ impl<const $lanes: usize> core::ops::$trait<$rhs> for &'_ $type
+ where
+ LaneCount<$lanes2>: SupportedLaneCount,
+ {
+ type Output = <$type as core::ops::$trait<$rhs>>::Output;
+
+ $(#[$attrs])*
+ fn $fn($self_tok, $rhs_arg: $rhs) -> Self::Output {
+ core::ops::$trait::$fn(*$self_tok, $rhs_arg)
+ }
+ }
+
+ impl<const $lanes: usize> core::ops::$trait<&'_ $rhs> for &'_ $type
+ where
+ LaneCount<$lanes2>: SupportedLaneCount,
+ {
+ type Output = <$type as core::ops::$trait<$rhs>>::Output;
+
+ $(#[$attrs])*
+ fn $fn($self_tok, $rhs_arg: &$rhs) -> Self::Output {
+ core::ops::$trait::$fn(*$self_tok, *$rhs_arg)
+ }
+ }
+ };
+
+ // binary assignment op
+ {
+ impl<const $lanes:ident: usize> core::ops::$trait:ident<$rhs:ty> for $type:ty
+ where
+ LaneCount<$lanes2:ident>: SupportedLaneCount,
+ {
+ $(#[$attrs:meta])*
+ fn $fn:ident(&mut $self_tok:ident, $rhs_arg:ident: $rhs_arg_ty:ty) $body:tt
+ }
+ } => {
+ impl<const $lanes: usize> core::ops::$trait<$rhs> for $type
+ where
+ LaneCount<$lanes2>: SupportedLaneCount,
+ {
+ $(#[$attrs])*
+ fn $fn(&mut $self_tok, $rhs_arg: $rhs_arg_ty) $body
+ }
+
+ impl<const $lanes: usize> core::ops::$trait<&'_ $rhs> for $type
+ where
+ LaneCount<$lanes2>: SupportedLaneCount,
+ {
+ $(#[$attrs])*
+ fn $fn(&mut $self_tok, $rhs_arg: &$rhs_arg_ty) {
+ core::ops::$trait::$fn($self_tok, *$rhs_arg)
+ }
+ }
+ };
+
+ // unary op
+ {
+ impl<const $lanes:ident: usize> core::ops::$trait:ident for $type:ty
+ where
+ LaneCount<$lanes2:ident>: SupportedLaneCount,
+ {
+ type Output = $output:ty;
+ fn $fn:ident($self_tok:ident) -> Self::Output $body:tt
+ }
+ } => {
+ impl<const $lanes: usize> core::ops::$trait for $type
+ where
+ LaneCount<$lanes2>: SupportedLaneCount,
+ {
+ type Output = $output;
+ fn $fn($self_tok) -> Self::Output $body
+ }
+
+ impl<const $lanes: usize> core::ops::$trait for &'_ $type
+ where
+ LaneCount<$lanes2>: SupportedLaneCount,
+ {
+ type Output = <$type as core::ops::$trait>::Output;
+ fn $fn($self_tok) -> Self::Output {
+ core::ops::$trait::$fn(*$self_tok)
+ }
+ }
+ }
+}
+
+/// Automatically implements operators over vectors and scalars for a particular vector.
+macro_rules! impl_op {
+ { impl Add for $scalar:ty } => {
+ impl_op! { @binary $scalar, Add::add, AddAssign::add_assign, simd_add }
+ };
+ { impl Sub for $scalar:ty } => {
+ impl_op! { @binary $scalar, Sub::sub, SubAssign::sub_assign, simd_sub }
+ };
+ { impl Mul for $scalar:ty } => {
+ impl_op! { @binary $scalar, Mul::mul, MulAssign::mul_assign, simd_mul }
+ };
+ { impl Div for $scalar:ty } => {
+ impl_op! { @binary $scalar, Div::div, DivAssign::div_assign, simd_div }
+ };
+ { impl Rem for $scalar:ty } => {
+ impl_op! { @binary $scalar, Rem::rem, RemAssign::rem_assign, simd_rem }
+ };
+ { impl Shl for $scalar:ty } => {
+ impl_op! { @binary $scalar, Shl::shl, ShlAssign::shl_assign, simd_shl }
+ };
+ { impl Shr for $scalar:ty } => {
+ impl_op! { @binary $scalar, Shr::shr, ShrAssign::shr_assign, simd_shr }
+ };
+ { impl BitAnd for $scalar:ty } => {
+ impl_op! { @binary $scalar, BitAnd::bitand, BitAndAssign::bitand_assign, simd_and }
+ };
+ { impl BitOr for $scalar:ty } => {
+ impl_op! { @binary $scalar, BitOr::bitor, BitOrAssign::bitor_assign, simd_or }
+ };
+ { impl BitXor for $scalar:ty } => {
+ impl_op! { @binary $scalar, BitXor::bitxor, BitXorAssign::bitxor_assign, simd_xor }
+ };
+
+ { impl Not for $scalar:ty } => {
+ impl_ref_ops! {
+ impl<const LANES: usize> core::ops::Not for Simd<$scalar, LANES>
+ where
+ LaneCount<LANES>: SupportedLaneCount,
+ {
+ type Output = Self;
+ fn not(self) -> Self::Output {
+ self ^ Self::splat(!<$scalar>::default())
+ }
+ }
+ }
+ };
+
+ { impl Neg for $scalar:ty } => {
+ impl_ref_ops! {
+ impl<const LANES: usize> core::ops::Neg for Simd<$scalar, LANES>
+ where
+ LaneCount<LANES>: SupportedLaneCount,
+ {
+ type Output = Self;
+ fn neg(self) -> Self::Output {
+ unsafe { intrinsics::simd_neg(self) }
+ }
+ }
+ }
+ };
+
+ // generic binary op with assignment when output is `Self`
+ { @binary $scalar:ty, $trait:ident :: $trait_fn:ident, $assign_trait:ident :: $assign_trait_fn:ident, $intrinsic:ident } => {
+ impl_ref_ops! {
+ impl<const LANES: usize> core::ops::$trait<Self> for Simd<$scalar, LANES>
+ where
+ LaneCount<LANES>: SupportedLaneCount,
+ {
+ type Output = Self;
+
+ #[inline]
+ fn $trait_fn(self, rhs: Self) -> Self::Output {
+ unsafe {
+ intrinsics::$intrinsic(self, rhs)
+ }
+ }
+ }
+ }
+
+ impl_ref_ops! {
+ impl<const LANES: usize> core::ops::$trait<$scalar> for Simd<$scalar, LANES>
+ where
+ LaneCount<LANES>: SupportedLaneCount,
+ {
+ type Output = Self;
+
+ #[inline]
+ fn $trait_fn(self, rhs: $scalar) -> Self::Output {
+ core::ops::$trait::$trait_fn(self, Self::splat(rhs))
+ }
+ }
+ }
+
+ impl_ref_ops! {
+ impl<const LANES: usize> core::ops::$trait<Simd<$scalar, LANES>> for $scalar
+ where
+ LaneCount<LANES>: SupportedLaneCount,
+ {
+ type Output = Simd<$scalar, LANES>;
+
+ #[inline]
+ fn $trait_fn(self, rhs: Simd<$scalar, LANES>) -> Self::Output {
+ core::ops::$trait::$trait_fn(Simd::splat(self), rhs)
+ }
+ }
+ }
+
+ impl_ref_ops! {
+ impl<const LANES: usize> core::ops::$assign_trait<Self> for Simd<$scalar, LANES>
+ where
+ LaneCount<LANES>: SupportedLaneCount,
+ {
+ #[inline]
+ fn $assign_trait_fn(&mut self, rhs: Self) {
+ unsafe {
+ *self = intrinsics::$intrinsic(*self, rhs);
+ }
+ }
+ }
+ }
+
+ impl_ref_ops! {
+ impl<const LANES: usize> core::ops::$assign_trait<$scalar> for Simd<$scalar, LANES>
+ where
+ LaneCount<LANES>: SupportedLaneCount,
+ {
+ #[inline]
+ fn $assign_trait_fn(&mut self, rhs: $scalar) {
+ core::ops::$assign_trait::$assign_trait_fn(self, Self::splat(rhs));
+ }
+ }
+ }
+ };
+}
+
+/// Implements floating-point operators for the provided types.
+macro_rules! impl_float_ops {
+ { $($scalar:ty),* } => {
+ $(
+ impl_op! { impl Add for $scalar }
+ impl_op! { impl Sub for $scalar }
+ impl_op! { impl Mul for $scalar }
+ impl_op! { impl Div for $scalar }
+ impl_op! { impl Rem for $scalar }
+ impl_op! { impl Neg for $scalar }
+ )*
+ };
+}
+
+/// Implements unsigned integer operators for the provided types.
+macro_rules! impl_unsigned_int_ops {
+ { $($scalar:ty),* } => {
+ $(
+ impl_op! { impl Add for $scalar }
+ impl_op! { impl Sub for $scalar }
+ impl_op! { impl Mul for $scalar }
+ impl_op! { impl BitAnd for $scalar }
+ impl_op! { impl BitOr for $scalar }
+ impl_op! { impl BitXor for $scalar }
+ impl_op! { impl Not for $scalar }
+
+ // Integers panic on divide by 0
+ impl_ref_ops! {
+ impl<const LANES: usize> core::ops::Div<Self> for Simd<$scalar, LANES>
+ where
+ LaneCount<LANES>: SupportedLaneCount,
+ {
+ type Output = Self;
+
+ #[inline]
+ fn div(self, rhs: Self) -> Self::Output {
+ if rhs.as_array()
+ .iter()
+ .any(|x| *x == 0)
+ {
+ panic!("attempt to divide by zero");
+ }
+
+ // Guards for div(MIN, -1),
+ // this check only applies to signed ints
+ if <$scalar>::MIN != 0 && self.as_array().iter()
+ .zip(rhs.as_array().iter())
+ .any(|(x,y)| *x == <$scalar>::MIN && *y == -1 as _) {
+ panic!("attempt to divide with overflow");
+ }
+ unsafe { intrinsics::simd_div(self, rhs) }
+ }
+ }
+ }
+
+ impl_ref_ops! {
+ impl<const LANES: usize> core::ops::Div<$scalar> for Simd<$scalar, LANES>
+ where
+ LaneCount<LANES>: SupportedLaneCount,
+ {
+ type Output = Self;
+
+ #[inline]
+ fn div(self, rhs: $scalar) -> Self::Output {
+ if rhs == 0 {
+ panic!("attempt to divide by zero");
+ }
+ if <$scalar>::MIN != 0 &&
+ self.as_array().iter().any(|x| *x == <$scalar>::MIN) &&
+ rhs == -1 as _ {
+ panic!("attempt to divide with overflow");
+ }
+ let rhs = Self::splat(rhs);
+ unsafe { intrinsics::simd_div(self, rhs) }
+ }
+ }
+ }
+
+ impl_ref_ops! {
+ impl<const LANES: usize> core::ops::Div<Simd<$scalar, LANES>> for $scalar
+ where
+ LaneCount<LANES>: SupportedLaneCount,
+ {
+ type Output = Simd<$scalar, LANES>;
+
+ #[inline]
+ fn div(self, rhs: Simd<$scalar, LANES>) -> Self::Output {
+ Simd::splat(self) / rhs
+ }
+ }
+ }
+
+ impl_ref_ops! {
+ impl<const LANES: usize> core::ops::DivAssign<Self> for Simd<$scalar, LANES>
+ where
+ LaneCount<LANES>: SupportedLaneCount,
+ {
+ #[inline]
+ fn div_assign(&mut self, rhs: Self) {
+ *self = *self / rhs;
+ }
+ }
+ }
+
+ impl_ref_ops! {
+ impl<const LANES: usize> core::ops::DivAssign<$scalar> for Simd<$scalar, LANES>
+ where
+ LaneCount<LANES>: SupportedLaneCount,
+ {
+ #[inline]
+ fn div_assign(&mut self, rhs: $scalar) {
+ *self = *self / rhs;
+ }
+ }
+ }
+
+ // remainder panics on zero divisor
+ impl_ref_ops! {
+ impl<const LANES: usize> core::ops::Rem<Self> for Simd<$scalar, LANES>
+ where
+ LaneCount<LANES>: SupportedLaneCount,
+ {
+ type Output = Self;
+
+ #[inline]
+ fn rem(self, rhs: Self) -> Self::Output {
+ if rhs.as_array()
+ .iter()
+ .any(|x| *x == 0)
+ {
+ panic!("attempt to calculate the remainder with a divisor of zero");
+ }
+
+ // Guards for rem(MIN, -1)
+ // this branch applies the check only to signed ints
+ if <$scalar>::MIN != 0 && self.as_array().iter()
+ .zip(rhs.as_array().iter())
+ .any(|(x,y)| *x == <$scalar>::MIN && *y == -1 as _) {
+ panic!("attempt to calculate the remainder with overflow");
+ }
+ unsafe { intrinsics::simd_rem(self, rhs) }
+ }
+ }
+ }
+
+ impl_ref_ops! {
+ impl<const LANES: usize> core::ops::Rem<$scalar> for Simd<$scalar, LANES>
+ where
+ LaneCount<LANES>: SupportedLaneCount,
+ {
+ type Output = Self;
+
+ #[inline]
+ fn rem(self, rhs: $scalar) -> Self::Output {
+ if rhs == 0 {
+ panic!("attempt to calculate the remainder with a divisor of zero");
+ }
+ if <$scalar>::MIN != 0 &&
+ self.as_array().iter().any(|x| *x == <$scalar>::MIN) &&
+ rhs == -1 as _ {
+ panic!("attempt to calculate the remainder with overflow");
+ }
+ let rhs = Self::splat(rhs);
+ unsafe { intrinsics::simd_rem(self, rhs) }
+ }
+ }
+ }
+
+ impl_ref_ops! {
+ impl<const LANES: usize> core::ops::Rem<Simd<$scalar, LANES>> for $scalar
+ where
+ LaneCount<LANES>: SupportedLaneCount,
+ {
+ type Output = Simd<$scalar, LANES>;
+
+ #[inline]
+ fn rem(self, rhs: Simd<$scalar, LANES>) -> Self::Output {
+ Simd::splat(self) % rhs
+ }
+ }
+ }
+
+ impl_ref_ops! {
+ impl<const LANES: usize> core::ops::RemAssign<Self> for Simd<$scalar, LANES>
+ where
+ LaneCount<LANES>: SupportedLaneCount,
+ {
+ #[inline]
+ fn rem_assign(&mut self, rhs: Self) {
+ *self = *self % rhs;
+ }
+ }
+ }
+
+ impl_ref_ops! {
+ impl<const LANES: usize> core::ops::RemAssign<$scalar> for Simd<$scalar, LANES>
+ where
+ LaneCount<LANES>: SupportedLaneCount,
+ {
+ #[inline]
+ fn rem_assign(&mut self, rhs: $scalar) {
+ *self = *self % rhs;
+ }
+ }
+ }
+
+ // shifts panic on overflow
+ impl_ref_ops! {
+ impl<const LANES: usize> core::ops::Shl<Self> for Simd<$scalar, LANES>
+ where
+ LaneCount<LANES>: SupportedLaneCount,
+ {
+ type Output = Self;
+
+ #[inline]
+ fn shl(self, rhs: Self) -> Self::Output {
+ // TODO there is probably a better way of doing this
+ if rhs.as_array()
+ .iter()
+ .copied()
+ .any(invalid_shift_rhs)
+ {
+ panic!("attempt to shift left with overflow");
+ }
+ unsafe { intrinsics::simd_shl(self, rhs) }
+ }
+ }
+ }
+
+ impl_ref_ops! {
+ impl<const LANES: usize> core::ops::Shl<$scalar> for Simd<$scalar, LANES>
+ where
+ LaneCount<LANES>: SupportedLaneCount,
+ {
+ type Output = Self;
+
+ #[inline]
+ fn shl(self, rhs: $scalar) -> Self::Output {
+ if invalid_shift_rhs(rhs) {
+ panic!("attempt to shift left with overflow");
+ }
+ let rhs = Self::splat(rhs);
+ unsafe { intrinsics::simd_shl(self, rhs) }
+ }
+ }
+ }
+
+
+ impl_ref_ops! {
+ impl<const LANES: usize> core::ops::ShlAssign<Self> for Simd<$scalar, LANES>
+ where
+ LaneCount<LANES>: SupportedLaneCount,
+ {
+ #[inline]
+ fn shl_assign(&mut self, rhs: Self) {
+ *self = *self << rhs;
+ }
+ }
+ }
+
+ impl_ref_ops! {
+ impl<const LANES: usize> core::ops::ShlAssign<$scalar> for Simd<$scalar, LANES>
+ where
+ LaneCount<LANES>: SupportedLaneCount,
+ {
+ #[inline]
+ fn shl_assign(&mut self, rhs: $scalar) {
+ *self = *self << rhs;
+ }
+ }
+ }
+
+ impl_ref_ops! {
+ impl<const LANES: usize> core::ops::Shr<Self> for Simd<$scalar, LANES>
+ where
+ LaneCount<LANES>: SupportedLaneCount,
+ {
+ type Output = Self;
+
+ #[inline]
+ fn shr(self, rhs: Self) -> Self::Output {
+ // TODO there is probably a better way of doing this
+ if rhs.as_array()
+ .iter()
+ .copied()
+ .any(invalid_shift_rhs)
+ {
+ panic!("attempt to shift with overflow");
+ }
+ unsafe { intrinsics::simd_shr(self, rhs) }
+ }
+ }
+ }
+
+ impl_ref_ops! {
+ impl<const LANES: usize> core::ops::Shr<$scalar> for Simd<$scalar, LANES>
+ where
+ LaneCount<LANES>: SupportedLaneCount,
+ {
+ type Output = Self;
+
+ #[inline]
+ fn shr(self, rhs: $scalar) -> Self::Output {
+ if invalid_shift_rhs(rhs) {
+ panic!("attempt to shift with overflow");
+ }
+ let rhs = Self::splat(rhs);
+ unsafe { intrinsics::simd_shr(self, rhs) }
+ }
+ }
+ }
+
+
+ impl_ref_ops! {
+ impl<const LANES: usize> core::ops::ShrAssign<Self> for Simd<$scalar, LANES>
+ where
+ LaneCount<LANES>: SupportedLaneCount,
+ {
+ #[inline]
+ fn shr_assign(&mut self, rhs: Self) {
+ *self = *self >> rhs;
+ }
+ }
+ }
+
+ impl_ref_ops! {
+ impl<const LANES: usize> core::ops::ShrAssign<$scalar> for Simd<$scalar, LANES>
+ where
+ LaneCount<LANES>: SupportedLaneCount,
+ {
+ #[inline]
+ fn shr_assign(&mut self, rhs: $scalar) {
+ *self = *self >> rhs;
+ }
+ }
+ }
+ )*
+ };
+}
+
+/// Implements unsigned integer operators for the provided types.
+macro_rules! impl_signed_int_ops {
+ { $($scalar:ty),* } => {
+ impl_unsigned_int_ops! { $($scalar),* }
+ $( // scalar
+ impl_op! { impl Neg for $scalar }
+ )*
+ };
+}
+
+impl_unsigned_int_ops! { u8, u16, u32, u64, usize }
+impl_signed_int_ops! { i8, i16, i32, i64, isize }
+impl_float_ops! { f32, f64 }
--- /dev/null
+use crate::simd::intrinsics::{
+ simd_reduce_add_ordered, simd_reduce_and, simd_reduce_max, simd_reduce_min,
+ simd_reduce_mul_ordered, simd_reduce_or, simd_reduce_xor,
+};
+use crate::simd::{LaneCount, Simd, SupportedLaneCount};
+
+macro_rules! impl_integer_reductions {
+ { $scalar:ty } => {
+ impl<const LANES: usize> Simd<$scalar, LANES>
+ where
+ LaneCount<LANES>: SupportedLaneCount,
+ {
+ /// Horizontal wrapping add. Returns the sum of the lanes of the vector, with wrapping addition.
+ #[inline]
+ pub fn horizontal_sum(self) -> $scalar {
+ unsafe { simd_reduce_add_ordered(self, 0) }
+ }
+
+ /// Horizontal wrapping multiply. Returns the product of the lanes of the vector, with wrapping multiplication.
+ #[inline]
+ pub fn horizontal_product(self) -> $scalar {
+ unsafe { simd_reduce_mul_ordered(self, 1) }
+ }
+
+ /// Horizontal bitwise "and". Returns the cumulative bitwise "and" across the lanes of
+ /// the vector.
+ #[inline]
+ pub fn horizontal_and(self) -> $scalar {
+ unsafe { simd_reduce_and(self) }
+ }
+
+ /// Horizontal bitwise "or". Returns the cumulative bitwise "or" across the lanes of
+ /// the vector.
+ #[inline]
+ pub fn horizontal_or(self) -> $scalar {
+ unsafe { simd_reduce_or(self) }
+ }
+
+ /// Horizontal bitwise "xor". Returns the cumulative bitwise "xor" across the lanes of
+ /// the vector.
+ #[inline]
+ pub fn horizontal_xor(self) -> $scalar {
+ unsafe { simd_reduce_xor(self) }
+ }
+
+ /// Horizontal maximum. Returns the maximum lane in the vector.
+ #[inline]
+ pub fn horizontal_max(self) -> $scalar {
+ unsafe { simd_reduce_max(self) }
+ }
+
+ /// Horizontal minimum. Returns the minimum lane in the vector.
+ #[inline]
+ pub fn horizontal_min(self) -> $scalar {
+ unsafe { simd_reduce_min(self) }
+ }
+ }
+ }
+}
+
+impl_integer_reductions! { i8 }
+impl_integer_reductions! { i16 }
+impl_integer_reductions! { i32 }
+impl_integer_reductions! { i64 }
+impl_integer_reductions! { isize }
+impl_integer_reductions! { u8 }
+impl_integer_reductions! { u16 }
+impl_integer_reductions! { u32 }
+impl_integer_reductions! { u64 }
+impl_integer_reductions! { usize }
+
+macro_rules! impl_float_reductions {
+ { $scalar:ty } => {
+ impl<const LANES: usize> Simd<$scalar, LANES>
+ where
+ LaneCount<LANES>: SupportedLaneCount,
+ {
+
+ /// Horizontal add. Returns the sum of the lanes of the vector.
+ #[inline]
+ pub fn horizontal_sum(self) -> $scalar {
+ // LLVM sum is inaccurate on i586
+ if cfg!(all(target_arch = "x86", not(target_feature = "sse2"))) {
+ self.as_array().iter().sum()
+ } else {
+ unsafe { simd_reduce_add_ordered(self, 0.) }
+ }
+ }
+
+ /// Horizontal multiply. Returns the product of the lanes of the vector.
+ #[inline]
+ pub fn horizontal_product(self) -> $scalar {
+ // LLVM product is inaccurate on i586
+ if cfg!(all(target_arch = "x86", not(target_feature = "sse2"))) {
+ self.as_array().iter().product()
+ } else {
+ unsafe { simd_reduce_mul_ordered(self, 1.) }
+ }
+ }
+
+ /// Horizontal maximum. Returns the maximum lane in the vector.
+ ///
+ /// Returns values based on equality, so a vector containing both `0.` and `-0.` may
+ /// return either. This function will not return `NaN` unless all lanes are `NaN`.
+ #[inline]
+ pub fn horizontal_max(self) -> $scalar {
+ unsafe { simd_reduce_max(self) }
+ }
+
+ /// Horizontal minimum. Returns the minimum lane in the vector.
+ ///
+ /// Returns values based on equality, so a vector containing both `0.` and `-0.` may
+ /// return either. This function will not return `NaN` unless all lanes are `NaN`.
+ #[inline]
+ pub fn horizontal_min(self) -> $scalar {
+ unsafe { simd_reduce_min(self) }
+ }
+ }
+ }
+}
+
+impl_float_reductions! { f32 }
+impl_float_reductions! { f64 }
--- /dev/null
+use crate::simd::intrinsics;
+use crate::simd::{LaneCount, Simd, SupportedLaneCount};
+
+macro_rules! implement {
+ {
+ $type:ty, $int_type:ty
+ } => {
+ #[cfg(feature = "std")]
+ impl<const LANES: usize> Simd<$type, LANES>
+ where
+ LaneCount<LANES>: SupportedLaneCount,
+ {
+ /// Returns the smallest integer greater than or equal to each lane.
+ #[must_use = "method returns a new vector and does not mutate the original value"]
+ #[inline]
+ pub fn ceil(self) -> Self {
+ unsafe { intrinsics::simd_ceil(self) }
+ }
+
+ /// Returns the largest integer value less than or equal to each lane.
+ #[must_use = "method returns a new vector and does not mutate the original value"]
+ #[inline]
+ pub fn floor(self) -> Self {
+ unsafe { intrinsics::simd_floor(self) }
+ }
+
+ /// Rounds to the nearest integer value. Ties round toward zero.
+ #[must_use = "method returns a new vector and does not mutate the original value"]
+ #[inline]
+ pub fn round(self) -> Self {
+ unsafe { intrinsics::simd_round(self) }
+ }
+
+ /// Returns the floating point's integer value, with its fractional part removed.
+ #[must_use = "method returns a new vector and does not mutate the original value"]
+ #[inline]
+ pub fn trunc(self) -> Self {
+ unsafe { intrinsics::simd_trunc(self) }
+ }
+
+ /// Returns the floating point's fractional value, with its integer part removed.
+ #[must_use = "method returns a new vector and does not mutate the original value"]
+ #[inline]
+ pub fn fract(self) -> Self {
+ self - self.trunc()
+ }
+ }
+
+ impl<const LANES: usize> Simd<$type, LANES>
+ where
+ LaneCount<LANES>: SupportedLaneCount,
+ {
+ /// Rounds toward zero and converts to the same-width integer type, assuming that
+ /// the value is finite and fits in that type.
+ ///
+ /// # Safety
+ /// The value must:
+ ///
+ /// * Not be NaN
+ /// * Not be infinite
+ /// * Be representable in the return type, after truncating off its fractional part
+ #[inline]
+ pub unsafe fn to_int_unchecked(self) -> Simd<$int_type, LANES> {
+ unsafe { intrinsics::simd_cast(self) }
+ }
+
+ /// Creates a floating-point vector from an integer vector. Rounds values that are
+ /// not exactly representable.
+ #[inline]
+ pub fn round_from_int(value: Simd<$int_type, LANES>) -> Self {
+ unsafe { intrinsics::simd_cast(value) }
+ }
+ }
+ }
+}
+
+implement! { f32, i32 }
+implement! { f64, i64 }
--- /dev/null
+use crate::simd::intrinsics;
+use crate::simd::{LaneCount, Mask, MaskElement, Simd, SimdElement, SupportedLaneCount};
+
+mod sealed {
+ pub trait Sealed<Mask> {
+ fn select(mask: Mask, true_values: Self, false_values: Self) -> Self;
+ }
+}
+use sealed::Sealed;
+
+/// Supporting trait for vector `select` function
+pub trait Select<Mask>: Sealed<Mask> {}
+
+impl<T, const LANES: usize> Sealed<Mask<T::Mask, LANES>> for Simd<T, LANES>
+where
+ T: SimdElement,
+ LaneCount<LANES>: SupportedLaneCount,
+{
+ #[inline]
+ fn select(mask: Mask<T::Mask, LANES>, true_values: Self, false_values: Self) -> Self {
+ unsafe { intrinsics::simd_select(mask.to_int(), true_values, false_values) }
+ }
+}
+
+impl<T, const LANES: usize> Select<Mask<T::Mask, LANES>> for Simd<T, LANES>
+where
+ T: SimdElement,
+ LaneCount<LANES>: SupportedLaneCount,
+{
+}
+
+impl<T, const LANES: usize> Sealed<Self> for Mask<T, LANES>
+where
+ T: MaskElement,
+ LaneCount<LANES>: SupportedLaneCount,
+{
+ #[inline]
+ fn select(mask: Self, true_values: Self, false_values: Self) -> Self {
+ mask & true_values | !mask & false_values
+ }
+}
+
+impl<T, const LANES: usize> Select<Self> for Mask<T, LANES>
+where
+ T: MaskElement,
+ LaneCount<LANES>: SupportedLaneCount,
+{
+}
+
+impl<T, const LANES: usize> Mask<T, LANES>
+where
+ T: MaskElement,
+ LaneCount<LANES>: SupportedLaneCount,
+{
+ /// Choose lanes from two vectors.
+ ///
+ /// For each lane in the mask, choose the corresponding lane from `true_values` if
+ /// that lane mask is true, and `false_values` if that lane mask is false.
+ ///
+ /// ```
+ /// # #![feature(portable_simd)]
+ /// # #[cfg(feature = "std")] use core_simd::{Simd, Mask};
+ /// # #[cfg(not(feature = "std"))] 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]);
+ /// ```
+ ///
+ /// `select` can also be used on masks:
+ /// ```
+ /// # #![feature(portable_simd)]
+ /// # #[cfg(feature = "std")] use core_simd::Mask;
+ /// # #[cfg(not(feature = "std"))] use core::simd::Mask;
+ /// 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]);
+ /// ```
+ #[inline]
+ pub fn select<S: Select<Self>>(self, true_values: S, false_values: S) -> S {
+ S::select(self, true_values, false_values)
+ }
+}
--- /dev/null
+use crate::simd::intrinsics;
+use crate::simd::{LaneCount, Simd, SimdElement, SupportedLaneCount};
+
+/// Constructs a new vector by selecting values from the lanes of the source vector or vectors to use.
+///
+/// When swizzling one vector, the indices of the result vector are indicated by a `const` array
+/// of `usize`, like [`Swizzle`].
+/// When swizzling two vectors, the indices are indicated by a `const` array of [`Which`], like
+/// [`Swizzle2`].
+///
+/// # Examples
+/// ## One source vector
+/// ```
+/// # #![feature(portable_simd)]
+/// # #[cfg(feature = "std")] use core_simd::{Simd, simd_swizzle};
+/// # #[cfg(not(feature = "std"))] use core::simd::{Simd, simd_swizzle};
+/// let v = Simd::<f32, 4>::from_array([0., 1., 2., 3.]);
+///
+/// // Keeping the same size
+/// let r = simd_swizzle!(v, [3, 0, 1, 2]);
+/// assert_eq!(r.to_array(), [3., 0., 1., 2.]);
+///
+/// // Changing the number of lanes
+/// let r = simd_swizzle!(v, [3, 1]);
+/// assert_eq!(r.to_array(), [3., 1.]);
+/// ```
+///
+/// ## Two source vectors
+/// ```
+/// # #![feature(portable_simd)]
+/// # #[cfg(feature = "std")] use core_simd::{Simd, simd_swizzle, Which};
+/// # #[cfg(not(feature = "std"))] use core::simd::{Simd, simd_swizzle, Which};
+/// use Which::*;
+/// let a = Simd::<f32, 4>::from_array([0., 1., 2., 3.]);
+/// let b = Simd::<f32, 4>::from_array([4., 5., 6., 7.]);
+///
+/// // Keeping the same size
+/// let r = simd_swizzle!(a, b, [First(0), First(1), Second(2), Second(3)]);
+/// assert_eq!(r.to_array(), [0., 1., 6., 7.]);
+///
+/// // Changing the number of lanes
+/// let r = simd_swizzle!(a, b, [First(0), Second(0)]);
+/// assert_eq!(r.to_array(), [0., 4.]);
+/// ```
+#[allow(unused_macros)]
+pub macro simd_swizzle {
+ (
+ $vector:expr, $index:expr $(,)?
+ ) => {
+ {
+ use $crate::simd::Swizzle;
+ struct Impl;
+ impl<const LANES: usize> Swizzle<LANES, {$index.len()}> for Impl {
+ const INDEX: [usize; {$index.len()}] = $index;
+ }
+ Impl::swizzle($vector)
+ }
+ },
+ (
+ $first:expr, $second:expr, $index:expr $(,)?
+ ) => {
+ {
+ use $crate::simd::{Which, Swizzle2};
+ struct Impl;
+ impl<const LANES: usize> Swizzle2<LANES, {$index.len()}> for Impl {
+ const INDEX: [Which; {$index.len()}] = $index;
+ }
+ Impl::swizzle2($first, $second)
+ }
+ }
+}
+
+/// An index into one of two vectors.
+#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub enum Which {
+ /// Indexes the first vector.
+ First(usize),
+ /// Indexes the second vector.
+ Second(usize),
+}
+
+/// Create a vector from the elements of another vector.
+pub trait Swizzle<const INPUT_LANES: usize, const OUTPUT_LANES: usize> {
+ /// Map from the lanes of the input vector to the output vector.
+ const INDEX: [usize; OUTPUT_LANES];
+
+ /// Create a new vector from the lanes of `vector`.
+ ///
+ /// Lane `i` of the output is `vector[Self::INDEX[i]]`.
+ fn swizzle<T>(vector: Simd<T, INPUT_LANES>) -> Simd<T, OUTPUT_LANES>
+ where
+ T: SimdElement,
+ LaneCount<INPUT_LANES>: SupportedLaneCount,
+ LaneCount<OUTPUT_LANES>: SupportedLaneCount,
+ {
+ unsafe { intrinsics::simd_shuffle(vector, vector, Self::INDEX_IMPL) }
+ }
+}
+
+/// Create a vector from the elements of two other vectors.
+pub trait Swizzle2<const INPUT_LANES: usize, const OUTPUT_LANES: usize> {
+ /// Map from the lanes of the input vectors to the output vector
+ const INDEX: [Which; OUTPUT_LANES];
+
+ /// Create a new vector from the lanes of `first` and `second`.
+ ///
+ /// Lane `i` is `first[j]` when `Self::INDEX[i]` is `First(j)`, or `second[j]` when it is
+ /// `Second(j)`.
+ fn swizzle2<T>(
+ first: Simd<T, INPUT_LANES>,
+ second: Simd<T, INPUT_LANES>,
+ ) -> Simd<T, OUTPUT_LANES>
+ where
+ T: SimdElement,
+ LaneCount<INPUT_LANES>: SupportedLaneCount,
+ LaneCount<OUTPUT_LANES>: SupportedLaneCount,
+ {
+ unsafe { intrinsics::simd_shuffle(first, second, Self::INDEX_IMPL) }
+ }
+}
+
+/// The `simd_shuffle` intrinsic expects `u32`, so do error checking and conversion here.
+/// This trait hides `INDEX_IMPL` from the public API.
+trait SwizzleImpl<const INPUT_LANES: usize, const OUTPUT_LANES: usize> {
+ const INDEX_IMPL: [u32; OUTPUT_LANES];
+}
+
+impl<T, const INPUT_LANES: usize, const OUTPUT_LANES: usize> SwizzleImpl<INPUT_LANES, OUTPUT_LANES>
+ for T
+where
+ T: Swizzle<INPUT_LANES, OUTPUT_LANES> + ?Sized,
+{
+ const INDEX_IMPL: [u32; OUTPUT_LANES] = {
+ let mut output = [0; OUTPUT_LANES];
+ let mut i = 0;
+ while i < OUTPUT_LANES {
+ let index = Self::INDEX[i];
+ assert!(index as u32 as usize == index);
+ assert!(index < INPUT_LANES, "source lane exceeds input lane count",);
+ output[i] = index as u32;
+ i += 1;
+ }
+ output
+ };
+}
+
+/// The `simd_shuffle` intrinsic expects `u32`, so do error checking and conversion here.
+/// This trait hides `INDEX_IMPL` from the public API.
+trait Swizzle2Impl<const INPUT_LANES: usize, const OUTPUT_LANES: usize> {
+ const INDEX_IMPL: [u32; OUTPUT_LANES];
+}
+
+impl<T, const INPUT_LANES: usize, const OUTPUT_LANES: usize> Swizzle2Impl<INPUT_LANES, OUTPUT_LANES>
+ for T
+where
+ T: Swizzle2<INPUT_LANES, OUTPUT_LANES> + ?Sized,
+{
+ const INDEX_IMPL: [u32; OUTPUT_LANES] = {
+ let mut output = [0; OUTPUT_LANES];
+ let mut i = 0;
+ while i < OUTPUT_LANES {
+ let (offset, index) = match Self::INDEX[i] {
+ Which::First(index) => (false, index),
+ Which::Second(index) => (true, index),
+ };
+ assert!(index < INPUT_LANES, "source lane exceeds input lane count",);
+
+ // lanes are indexed by the first vector, then second vector
+ let index = if offset { index + INPUT_LANES } else { index };
+ assert!(index as u32 as usize == index);
+ output[i] = index as u32;
+ i += 1;
+ }
+ output
+ };
+}
+
+impl<T, const LANES: usize> Simd<T, LANES>
+where
+ T: SimdElement,
+ LaneCount<LANES>: SupportedLaneCount,
+{
+ /// Reverse the order of the lanes in the vector.
+ #[inline]
+ pub fn reverse(self) -> Self {
+ const fn reverse_index<const LANES: usize>() -> [usize; LANES] {
+ let mut index = [0; LANES];
+ let mut i = 0;
+ while i < LANES {
+ index[i] = LANES - i - 1;
+ i += 1;
+ }
+ index
+ }
+
+ struct Reverse;
+
+ impl<const LANES: usize> Swizzle<LANES, LANES> for Reverse {
+ const INDEX: [usize; LANES] = reverse_index::<LANES>();
+ }
+
+ Reverse::swizzle(self)
+ }
+
+ /// Rotates the vector such that the first `OFFSET` elements of the slice move to the end
+ /// while the last `LANES - OFFSET` elements move to the front. After calling `rotate_lanes_left`,
+ /// the element previously in lane `OFFSET` will become the first element in the slice.
+ #[inline]
+ pub fn rotate_lanes_left<const OFFSET: usize>(self) -> Self {
+ const fn rotate_index<const OFFSET: usize, const LANES: usize>() -> [usize; LANES] {
+ let offset = OFFSET % LANES;
+ let mut index = [0; LANES];
+ let mut i = 0;
+ while i < LANES {
+ index[i] = (i + offset) % LANES;
+ i += 1;
+ }
+ index
+ }
+
+ struct Rotate<const OFFSET: usize>;
+
+ impl<const OFFSET: usize, const LANES: usize> Swizzle<LANES, LANES> for Rotate<OFFSET> {
+ const INDEX: [usize; LANES] = rotate_index::<OFFSET, LANES>();
+ }
+
+ Rotate::<OFFSET>::swizzle(self)
+ }
+
+ /// Rotates the vector such that the first `LANES - OFFSET` elements of the vector move to
+ /// the end while the last `OFFSET` elements move to the front. After calling `rotate_lanes_right`,
+ /// the element previously at index `LANES - OFFSET` will become the first element in the slice.
+ #[inline]
+ pub fn rotate_lanes_right<const OFFSET: usize>(self) -> Self {
+ const fn rotate_index<const OFFSET: usize, const LANES: usize>() -> [usize; LANES] {
+ let offset = LANES - OFFSET % LANES;
+ let mut index = [0; LANES];
+ let mut i = 0;
+ while i < LANES {
+ index[i] = (i + offset) % LANES;
+ i += 1;
+ }
+ index
+ }
+
+ struct Rotate<const OFFSET: usize>;
+
+ impl<const OFFSET: usize, const LANES: usize> Swizzle<LANES, LANES> for Rotate<OFFSET> {
+ const INDEX: [usize; LANES] = rotate_index::<OFFSET, LANES>();
+ }
+
+ Rotate::<OFFSET>::swizzle(self)
+ }
+
+ /// Interleave two vectors.
+ ///
+ /// Produces two vectors with lanes taken alternately from `self` and `other`.
+ ///
+ /// The first result contains the first `LANES / 2` lanes from `self` and `other`,
+ /// alternating, starting with the first lane of `self`.
+ ///
+ /// The second result contains the last `LANES / 2` lanes from `self` and `other`,
+ /// alternating, starting with the lane `LANES / 2` from the start of `self`.
+ ///
+ /// ```
+ /// #![feature(portable_simd)]
+ /// # #[cfg(feature = "std")] use core_simd::Simd;
+ /// # #[cfg(not(feature = "std"))] use core::simd::Simd;
+ /// let a = Simd::from_array([0, 1, 2, 3]);
+ /// let b = Simd::from_array([4, 5, 6, 7]);
+ /// let (x, y) = a.interleave(b);
+ /// assert_eq!(x.to_array(), [0, 4, 1, 5]);
+ /// assert_eq!(y.to_array(), [2, 6, 3, 7]);
+ /// ```
+ #[inline]
+ pub fn interleave(self, other: Self) -> (Self, Self) {
+ const fn lo<const LANES: usize>() -> [Which; LANES] {
+ let mut idx = [Which::First(0); LANES];
+ let mut i = 0;
+ while i < LANES {
+ let offset = i / 2;
+ idx[i] = if i % 2 == 0 {
+ Which::First(offset)
+ } else {
+ Which::Second(offset)
+ };
+ i += 1;
+ }
+ idx
+ }
+ const fn hi<const LANES: usize>() -> [Which; LANES] {
+ let mut idx = [Which::First(0); LANES];
+ let mut i = 0;
+ while i < LANES {
+ let offset = (LANES + i) / 2;
+ idx[i] = if i % 2 == 0 {
+ Which::First(offset)
+ } else {
+ Which::Second(offset)
+ };
+ i += 1;
+ }
+ idx
+ }
+
+ struct Lo;
+ struct Hi;
+
+ impl<const LANES: usize> Swizzle2<LANES, LANES> for Lo {
+ const INDEX: [Which; LANES] = lo::<LANES>();
+ }
+
+ impl<const LANES: usize> Swizzle2<LANES, LANES> for Hi {
+ const INDEX: [Which; LANES] = hi::<LANES>();
+ }
+
+ (Lo::swizzle2(self, other), Hi::swizzle2(self, other))
+ }
+
+ /// Deinterleave two vectors.
+ ///
+ /// The first result takes every other lane of `self` and then `other`, starting with
+ /// the first lane.
+ ///
+ /// The second result takes every other lane of `self` and then `other`, starting with
+ /// the second lane.
+ ///
+ /// ```
+ /// #![feature(portable_simd)]
+ /// # #[cfg(feature = "std")] use core_simd::Simd;
+ /// # #[cfg(not(feature = "std"))] use core::simd::Simd;
+ /// let a = Simd::from_array([0, 4, 1, 5]);
+ /// let b = Simd::from_array([2, 6, 3, 7]);
+ /// let (x, y) = a.deinterleave(b);
+ /// assert_eq!(x.to_array(), [0, 1, 2, 3]);
+ /// assert_eq!(y.to_array(), [4, 5, 6, 7]);
+ /// ```
+ #[inline]
+ pub fn deinterleave(self, other: Self) -> (Self, Self) {
+ const fn even<const LANES: usize>() -> [Which; LANES] {
+ let mut idx = [Which::First(0); LANES];
+ let mut i = 0;
+ while i < LANES / 2 {
+ idx[i] = Which::First(2 * i);
+ idx[i + LANES / 2] = Which::Second(2 * i);
+ i += 1;
+ }
+ idx
+ }
+ const fn odd<const LANES: usize>() -> [Which; LANES] {
+ let mut idx = [Which::First(0); LANES];
+ let mut i = 0;
+ while i < LANES / 2 {
+ idx[i] = Which::First(2 * i + 1);
+ idx[i + LANES / 2] = Which::Second(2 * i + 1);
+ i += 1;
+ }
+ idx
+ }
+
+ struct Even;
+ struct Odd;
+
+ impl<const LANES: usize> Swizzle2<LANES, LANES> for Even {
+ const INDEX: [Which; LANES] = even::<LANES>();
+ }
+
+ impl<const LANES: usize> Swizzle2<LANES, LANES> for Odd {
+ const INDEX: [Which; LANES] = odd::<LANES>();
+ }
+
+ (Even::swizzle2(self, other), Odd::swizzle2(self, other))
+ }
+}
--- /dev/null
+macro_rules! impl_to_bytes {
+ { $ty:ty, $size:literal } => {
+ impl<const LANES: usize> crate::simd::Simd<$ty, LANES>
+ where
+ crate::simd::LaneCount<LANES>: crate::simd::SupportedLaneCount,
+ crate::simd::LaneCount<{{ $size * LANES }}>: crate::simd::SupportedLaneCount,
+ {
+ /// Return the memory representation of this integer as a byte array in native byte
+ /// order.
+ pub fn to_ne_bytes(self) -> crate::simd::Simd<u8, {{ $size * LANES }}> {
+ unsafe { core::mem::transmute_copy(&self) }
+ }
+
+ /// Create a native endian integer value from its memory representation as a byte array
+ /// in native endianness.
+ pub fn from_ne_bytes(bytes: crate::simd::Simd<u8, {{ $size * LANES }}>) -> Self {
+ unsafe { core::mem::transmute_copy(&bytes) }
+ }
+ }
+ }
+}
+
+impl_to_bytes! { u8, 1 }
+impl_to_bytes! { u16, 2 }
+impl_to_bytes! { u32, 4 }
+impl_to_bytes! { u64, 8 }
+#[cfg(target_pointer_width = "32")]
+impl_to_bytes! { usize, 4 }
+#[cfg(target_pointer_width = "64")]
+impl_to_bytes! { usize, 8 }
+
+impl_to_bytes! { i8, 1 }
+impl_to_bytes! { i16, 2 }
+impl_to_bytes! { i32, 4 }
+impl_to_bytes! { i64, 8 }
+#[cfg(target_pointer_width = "32")]
+impl_to_bytes! { isize, 4 }
+#[cfg(target_pointer_width = "64")]
+impl_to_bytes! { isize, 8 }
--- /dev/null
+mod float;
+mod int;
+mod uint;
+
+pub use float::*;
+pub use int::*;
+pub use uint::*;
+
+// Vectors of pointers are not for public use at the current time.
+pub(crate) mod ptr;
+
+use crate::simd::intrinsics;
+use crate::simd::{LaneCount, Mask, MaskElement, SupportedLaneCount};
+
+/// A SIMD vector of `LANES` elements of type `T`.
+#[repr(simd)]
+pub struct Simd<T, const LANES: usize>([T; LANES])
+where
+ T: SimdElement,
+ LaneCount<LANES>: SupportedLaneCount;
+
+impl<T, const LANES: usize> Simd<T, LANES>
+where
+ LaneCount<LANES>: SupportedLaneCount,
+ T: SimdElement,
+{
+ /// Number of lanes in this vector.
+ pub const LANES: usize = LANES;
+
+ /// Get the number of lanes in this vector.
+ pub const fn lanes(&self) -> usize {
+ LANES
+ }
+
+ /// Construct a SIMD vector by setting all lanes to the given value.
+ pub const fn splat(value: T) -> Self {
+ Self([value; LANES])
+ }
+
+ /// Returns an array reference containing the entire SIMD vector.
+ pub const fn as_array(&self) -> &[T; LANES] {
+ &self.0
+ }
+
+ /// Returns a mutable array reference containing the entire SIMD vector.
+ pub fn as_mut_array(&mut self) -> &mut [T; LANES] {
+ &mut self.0
+ }
+
+ /// Converts an array to a SIMD vector.
+ pub const fn from_array(array: [T; LANES]) -> Self {
+ Self(array)
+ }
+
+ /// Converts a SIMD vector to an array.
+ pub const fn to_array(self) -> [T; LANES] {
+ self.0
+ }
+
+ /// Converts a slice to a SIMD vector containing `slice[..LANES]`
+ /// # Panics
+ /// `from_slice` will panic if the slice's `len` is less than the vector's `Simd::LANES`.
+ #[must_use]
+ pub const fn from_slice(slice: &[T]) -> Self {
+ assert!(
+ slice.len() >= LANES,
+ "slice length must be at least the number of lanes"
+ );
+ let mut array = [slice[0]; LANES];
+ let mut i = 0;
+ while i < LANES {
+ array[i] = slice[i];
+ i += 1;
+ }
+ Self(array)
+ }
+
+ /// Reads from potentially discontiguous indices in `slice` to construct a SIMD vector.
+ /// If an index is out-of-bounds, the lane is instead selected from the `or` vector.
+ ///
+ /// # Examples
+ /// ```
+ /// # #![feature(portable_simd)]
+ /// # #[cfg(feature = "std")] use core_simd::Simd;
+ /// # #[cfg(not(feature = "std"))] use core::simd::Simd;
+ /// let vec: Vec<i32> = vec![10, 11, 12, 13, 14, 15, 16, 17, 18];
+ /// let idxs = Simd::from_array([9, 3, 0, 5]);
+ /// let alt = Simd::from_array([-5, -4, -3, -2]);
+ ///
+ /// let result = Simd::gather_or(&vec, idxs, alt); // Note the lane that is out-of-bounds.
+ /// assert_eq!(result, Simd::from_array([-5, 13, 10, 15]));
+ /// ```
+ #[must_use]
+ #[inline]
+ pub fn gather_or(slice: &[T], idxs: Simd<usize, LANES>, or: Self) -> Self {
+ Self::gather_select(slice, Mask::splat(true), idxs, or)
+ }
+
+ /// Reads from potentially discontiguous indices in `slice` to construct a SIMD vector.
+ /// If an index is out-of-bounds, the lane is set to the default value for the type.
+ ///
+ /// # Examples
+ /// ```
+ /// # #![feature(portable_simd)]
+ /// # #[cfg(feature = "std")] use core_simd::Simd;
+ /// # #[cfg(not(feature = "std"))] use core::simd::Simd;
+ /// let vec: Vec<i32> = vec![10, 11, 12, 13, 14, 15, 16, 17, 18];
+ /// let idxs = Simd::from_array([9, 3, 0, 5]);
+ ///
+ /// let result = Simd::gather_or_default(&vec, idxs); // Note the lane that is out-of-bounds.
+ /// assert_eq!(result, Simd::from_array([0, 13, 10, 15]));
+ /// ```
+ #[must_use]
+ #[inline]
+ pub fn gather_or_default(slice: &[T], idxs: Simd<usize, LANES>) -> Self
+ where
+ T: Default,
+ {
+ Self::gather_or(slice, idxs, Self::splat(T::default()))
+ }
+
+ /// Reads from potentially discontiguous indices in `slice` to construct a SIMD vector.
+ /// The mask `enable`s all `true` lanes and disables all `false` lanes.
+ /// If an index is disabled or is out-of-bounds, the lane is selected from the `or` vector.
+ ///
+ /// # Examples
+ /// ```
+ /// # #![feature(portable_simd)]
+ /// # #[cfg(feature = "std")] use core_simd::{Simd, Mask};
+ /// # #[cfg(not(feature = "std"))] use core::simd::{Simd, Mask};
+ /// let vec: Vec<i32> = vec![10, 11, 12, 13, 14, 15, 16, 17, 18];
+ /// let idxs = Simd::from_array([9, 3, 0, 5]);
+ /// let alt = Simd::from_array([-5, -4, -3, -2]);
+ /// let enable = Mask::from_array([true, true, true, false]); // Note the mask of the last lane.
+ ///
+ /// let result = Simd::gather_select(&vec, enable, idxs, alt); // Note the lane that is out-of-bounds.
+ /// assert_eq!(result, Simd::from_array([-5, 13, 10, -2]));
+ /// ```
+ #[must_use]
+ #[inline]
+ pub fn gather_select(
+ slice: &[T],
+ enable: Mask<isize, LANES>,
+ idxs: Simd<usize, LANES>,
+ or: Self,
+ ) -> Self {
+ let enable: Mask<isize, LANES> = enable & idxs.lanes_lt(Simd::splat(slice.len()));
+ // SAFETY: We have masked-off out-of-bounds lanes.
+ unsafe { Self::gather_select_unchecked(slice, enable, idxs, or) }
+ }
+
+ /// Reads from potentially discontiguous indices in `slice` to construct a SIMD vector.
+ /// The mask `enable`s all `true` lanes and disables all `false` lanes.
+ /// If an index is disabled, the lane is selected from the `or` vector.
+ ///
+ /// # Safety
+ ///
+ /// Calling this function with an `enable`d out-of-bounds index is *[undefined behavior]*
+ /// even if the resulting value is not used.
+ ///
+ /// # Examples
+ /// ```
+ /// # #![feature(portable_simd)]
+ /// # #[cfg(feature = "std")] use core_simd::{Simd, Mask};
+ /// # #[cfg(not(feature = "std"))] use core::simd::{Simd, Mask};
+ /// let vec: Vec<i32> = vec![10, 11, 12, 13, 14, 15, 16, 17, 18];
+ /// let idxs = Simd::from_array([9, 3, 0, 5]);
+ /// let alt = Simd::from_array([-5, -4, -3, -2]);
+ /// let enable = Mask::from_array([true, true, true, false]); // Note the final mask lane.
+ /// // If this mask was used to gather, it would be unsound. Let's fix that.
+ /// let enable = enable & idxs.lanes_lt(Simd::splat(vec.len()));
+ ///
+ /// // We have masked the OOB lane, so it's safe to gather now.
+ /// let result = unsafe { Simd::gather_select_unchecked(&vec, enable, idxs, alt) };
+ /// assert_eq!(result, Simd::from_array([-5, 13, 10, -2]));
+ /// ```
+ /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html
+ #[must_use]
+ #[inline]
+ pub unsafe fn gather_select_unchecked(
+ slice: &[T],
+ enable: Mask<isize, LANES>,
+ idxs: Simd<usize, LANES>,
+ or: Self,
+ ) -> Self {
+ let base_ptr = crate::simd::ptr::SimdConstPtr::splat(slice.as_ptr());
+ // Ferris forgive me, I have done pointer arithmetic here.
+ let ptrs = base_ptr.wrapping_add(idxs);
+ // SAFETY: The ptrs have been bounds-masked to prevent memory-unsafe reads insha'allah
+ unsafe { intrinsics::simd_gather(or, ptrs, enable.to_int()) }
+ }
+
+ /// Writes the values in a SIMD vector to potentially discontiguous indices in `slice`.
+ /// If two lanes in the scattered vector would write to the same index
+ /// only the last lane is guaranteed to actually be written.
+ ///
+ /// # Examples
+ /// ```
+ /// # #![feature(portable_simd)]
+ /// # #[cfg(feature = "std")] use core_simd::Simd;
+ /// # #[cfg(not(feature = "std"))] use core::simd::Simd;
+ /// let mut vec: Vec<i32> = vec![10, 11, 12, 13, 14, 15, 16, 17, 18];
+ /// let idxs = Simd::from_array([9, 3, 0, 0]);
+ /// let vals = Simd::from_array([-27, 82, -41, 124]);
+ ///
+ /// vals.scatter(&mut vec, idxs); // index 0 receives two writes.
+ /// assert_eq!(vec, vec![124, 11, 12, 82, 14, 15, 16, 17, 18]);
+ /// ```
+ #[inline]
+ pub fn scatter(self, slice: &mut [T], idxs: Simd<usize, LANES>) {
+ self.scatter_select(slice, Mask::splat(true), idxs)
+ }
+
+ /// Writes the values in a SIMD vector to multiple potentially discontiguous indices in `slice`.
+ /// The mask `enable`s all `true` lanes and disables all `false` lanes.
+ /// If an enabled index is out-of-bounds, the lane is not written.
+ /// If two enabled lanes in the scattered vector would write to the same index,
+ /// only the last lane is guaranteed to actually be written.
+ ///
+ /// # Examples
+ /// ```
+ /// # #![feature(portable_simd)]
+ /// # #[cfg(feature = "std")] use core_simd::{Simd, Mask};
+ /// # #[cfg(not(feature = "std"))] use core::simd::{Simd, Mask};
+ /// let mut vec: Vec<i32> = vec![10, 11, 12, 13, 14, 15, 16, 17, 18];
+ /// let idxs = Simd::from_array([9, 3, 0, 0]);
+ /// let vals = Simd::from_array([-27, 82, -41, 124]);
+ /// let enable = Mask::from_array([true, true, true, false]); // Note the mask of the last lane.
+ ///
+ /// vals.scatter_select(&mut vec, enable, idxs); // index 0's second write is masked, thus omitted.
+ /// assert_eq!(vec, vec![-41, 11, 12, 82, 14, 15, 16, 17, 18]);
+ /// ```
+ #[inline]
+ pub fn scatter_select(
+ self,
+ slice: &mut [T],
+ enable: Mask<isize, LANES>,
+ idxs: Simd<usize, LANES>,
+ ) {
+ let enable: Mask<isize, LANES> = enable & idxs.lanes_lt(Simd::splat(slice.len()));
+ // SAFETY: We have masked-off out-of-bounds lanes.
+ unsafe { self.scatter_select_unchecked(slice, enable, idxs) }
+ }
+
+ /// Writes the values in a SIMD vector to multiple potentially discontiguous indices in `slice`.
+ /// The mask `enable`s all `true` lanes and disables all `false` lanes.
+ /// If two enabled lanes in the scattered vector would write to the same index,
+ /// only the last lane is guaranteed to actually be written.
+ ///
+ /// # Safety
+ ///
+ /// Calling this function with an enabled out-of-bounds index is *[undefined behavior]*,
+ /// and may lead to memory corruption.
+ ///
+ /// # Examples
+ /// ```
+ /// # #![feature(portable_simd)]
+ /// # #[cfg(feature = "std")] use core_simd::{Simd, Mask};
+ /// # #[cfg(not(feature = "std"))] use core::simd::{Simd, Mask};
+ /// let mut vec: Vec<i32> = vec![10, 11, 12, 13, 14, 15, 16, 17, 18];
+ /// let idxs = Simd::from_array([9, 3, 0, 0]);
+ /// let vals = Simd::from_array([-27, 82, -41, 124]);
+ /// let enable = Mask::from_array([true, true, true, false]); // Note the mask of the last lane.
+ /// // If this mask was used to scatter, it would be unsound. Let's fix that.
+ /// let enable = enable & idxs.lanes_lt(Simd::splat(vec.len()));
+ ///
+ /// // We have masked the OOB lane, so it's safe to scatter now.
+ /// unsafe { vals.scatter_select_unchecked(&mut vec, enable, idxs); }
+ /// // index 0's second write is masked, thus was omitted.
+ /// assert_eq!(vec, vec![-41, 11, 12, 82, 14, 15, 16, 17, 18]);
+ /// ```
+ /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html
+ #[inline]
+ pub unsafe fn scatter_select_unchecked(
+ self,
+ slice: &mut [T],
+ enable: Mask<isize, LANES>,
+ idxs: Simd<usize, LANES>,
+ ) {
+ // SAFETY: This block works with *mut T derived from &mut 'a [T],
+ // which means it is delicate in Rust's borrowing model, circa 2021:
+ // &mut 'a [T] asserts uniqueness, so deriving &'a [T] invalidates live *mut Ts!
+ // Even though this block is largely safe methods, it must be exactly this way
+ // to prevent invalidating the raw ptrs while they're live.
+ // Thus, entering this block requires all values to use being already ready:
+ // 0. idxs we want to write to, which are used to construct the mask.
+ // 1. enable, which depends on an initial &'a [T] and the idxs.
+ // 2. actual values to scatter (self).
+ // 3. &mut [T] which will become our base ptr.
+ unsafe {
+ // Now Entering ☢️ *mut T Zone
+ let base_ptr = crate::simd::ptr::SimdMutPtr::splat(slice.as_mut_ptr());
+ // Ferris forgive me, I have done pointer arithmetic here.
+ let ptrs = base_ptr.wrapping_add(idxs);
+ // The ptrs have been bounds-masked to prevent memory-unsafe writes insha'allah
+ intrinsics::simd_scatter(self, ptrs, enable.to_int())
+ // Cleared ☢️ *mut T Zone
+ }
+ }
+}
+
+impl<T, const LANES: usize> Copy for Simd<T, LANES>
+where
+ T: SimdElement,
+ LaneCount<LANES>: SupportedLaneCount,
+{
+}
+
+impl<T, const LANES: usize> Clone for Simd<T, LANES>
+where
+ T: SimdElement,
+ LaneCount<LANES>: SupportedLaneCount,
+{
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+
+impl<T, const LANES: usize> Default for Simd<T, LANES>
+where
+ LaneCount<LANES>: SupportedLaneCount,
+ T: SimdElement + Default,
+{
+ #[inline]
+ fn default() -> Self {
+ Self::splat(T::default())
+ }
+}
+
+impl<T, const LANES: usize> PartialEq for Simd<T, LANES>
+where
+ LaneCount<LANES>: SupportedLaneCount,
+ T: SimdElement + PartialEq,
+{
+ #[inline]
+ fn eq(&self, other: &Self) -> bool {
+ // TODO use SIMD equality
+ self.to_array() == other.to_array()
+ }
+}
+
+impl<T, const LANES: usize> PartialOrd for Simd<T, LANES>
+where
+ LaneCount<LANES>: SupportedLaneCount,
+ T: SimdElement + PartialOrd,
+{
+ #[inline]
+ fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
+ // TODO use SIMD equality
+ self.to_array().partial_cmp(other.as_ref())
+ }
+}
+
+impl<T, const LANES: usize> Eq for Simd<T, LANES>
+where
+ LaneCount<LANES>: SupportedLaneCount,
+ T: SimdElement + Eq,
+{
+}
+
+impl<T, const LANES: usize> Ord for Simd<T, LANES>
+where
+ LaneCount<LANES>: SupportedLaneCount,
+ T: SimdElement + Ord,
+{
+ #[inline]
+ fn cmp(&self, other: &Self) -> core::cmp::Ordering {
+ // TODO use SIMD equality
+ self.to_array().cmp(other.as_ref())
+ }
+}
+
+impl<T, const LANES: usize> core::hash::Hash for Simd<T, LANES>
+where
+ LaneCount<LANES>: SupportedLaneCount,
+ T: SimdElement + core::hash::Hash,
+{
+ #[inline]
+ fn hash<H>(&self, state: &mut H)
+ where
+ H: core::hash::Hasher,
+ {
+ self.as_array().hash(state)
+ }
+}
+
+// array references
+impl<T, const LANES: usize> AsRef<[T; LANES]> for Simd<T, LANES>
+where
+ LaneCount<LANES>: SupportedLaneCount,
+ T: SimdElement,
+{
+ #[inline]
+ fn as_ref(&self) -> &[T; LANES] {
+ &self.0
+ }
+}
+
+impl<T, const LANES: usize> AsMut<[T; LANES]> for Simd<T, LANES>
+where
+ LaneCount<LANES>: SupportedLaneCount,
+ T: SimdElement,
+{
+ #[inline]
+ fn as_mut(&mut self) -> &mut [T; LANES] {
+ &mut self.0
+ }
+}
+
+// slice references
+impl<T, const LANES: usize> AsRef<[T]> for Simd<T, LANES>
+where
+ LaneCount<LANES>: SupportedLaneCount,
+ T: SimdElement,
+{
+ #[inline]
+ fn as_ref(&self) -> &[T] {
+ &self.0
+ }
+}
+
+impl<T, const LANES: usize> AsMut<[T]> for Simd<T, LANES>
+where
+ LaneCount<LANES>: SupportedLaneCount,
+ T: SimdElement,
+{
+ #[inline]
+ fn as_mut(&mut self) -> &mut [T] {
+ &mut self.0
+ }
+}
+
+// vector/array conversion
+impl<T, const LANES: usize> From<[T; LANES]> for Simd<T, LANES>
+where
+ LaneCount<LANES>: SupportedLaneCount,
+ T: SimdElement,
+{
+ fn from(array: [T; LANES]) -> Self {
+ Self(array)
+ }
+}
+
+impl<T, const LANES: usize> From<Simd<T, LANES>> for [T; LANES]
+where
+ LaneCount<LANES>: SupportedLaneCount,
+ T: SimdElement,
+{
+ fn from(vector: Simd<T, LANES>) -> Self {
+ vector.to_array()
+ }
+}
+
+mod sealed {
+ pub trait Sealed {}
+}
+use sealed::Sealed;
+
+/// Marker trait for types that may be used as SIMD vector elements.
+/// SAFETY: This trait, when implemented, asserts the compiler can monomorphize
+/// `#[repr(simd)]` structs with the marked type as an element.
+/// Strictly, it is valid to impl if the vector will not be miscompiled.
+/// Practically, it is user-unfriendly to impl it if the vector won't compile,
+/// even when no soundness guarantees are broken by allowing the user to try.
+pub unsafe trait SimdElement: Sealed + Copy {
+ /// The mask element type corresponding to this element type.
+ type Mask: MaskElement;
+}
+
+impl Sealed for u8 {}
+unsafe impl SimdElement for u8 {
+ type Mask = i8;
+}
+
+impl Sealed for u16 {}
+unsafe impl SimdElement for u16 {
+ type Mask = i16;
+}
+
+impl Sealed for u32 {}
+unsafe impl SimdElement for u32 {
+ type Mask = i32;
+}
+
+impl Sealed for u64 {}
+unsafe impl SimdElement for u64 {
+ type Mask = i64;
+}
+
+impl Sealed for usize {}
+unsafe impl SimdElement for usize {
+ type Mask = isize;
+}
+
+impl Sealed for i8 {}
+unsafe impl SimdElement for i8 {
+ type Mask = i8;
+}
+
+impl Sealed for i16 {}
+unsafe impl SimdElement for i16 {
+ type Mask = i16;
+}
+
+impl Sealed for i32 {}
+unsafe impl SimdElement for i32 {
+ type Mask = i32;
+}
+
+impl Sealed for i64 {}
+unsafe impl SimdElement for i64 {
+ type Mask = i64;
+}
+
+impl Sealed for isize {}
+unsafe impl SimdElement for isize {
+ type Mask = isize;
+}
+
+impl Sealed for f32 {}
+unsafe impl SimdElement for f32 {
+ type Mask = i32;
+}
+
+impl Sealed for f64 {}
+unsafe impl SimdElement for f64 {
+ type Mask = i64;
+}
--- /dev/null
+#![allow(non_camel_case_types)]
+
+use crate::simd::intrinsics;
+use crate::simd::{LaneCount, Mask, Simd, SupportedLaneCount};
+
+/// Implements inherent methods for a float vector containing multiple
+/// `$lanes` of float `$type`, which uses `$bits_ty` as its binary
+/// representation.
+macro_rules! impl_float_vector {
+ { $type:ty, $bits_ty:ty, $mask_ty:ty } => {
+ impl<const LANES: usize> Simd<$type, LANES>
+ where
+ LaneCount<LANES>: SupportedLaneCount,
+ {
+ /// Raw transmutation to an unsigned integer vector type with the
+ /// same size and number of lanes.
+ #[inline]
+ pub fn to_bits(self) -> Simd<$bits_ty, LANES> {
+ assert_eq!(core::mem::size_of::<Self>(), core::mem::size_of::<Simd<$bits_ty, LANES>>());
+ unsafe { core::mem::transmute_copy(&self) }
+ }
+
+ /// Raw transmutation from an unsigned integer vector type with the
+ /// same size and number of lanes.
+ #[inline]
+ pub fn from_bits(bits: Simd<$bits_ty, LANES>) -> Self {
+ assert_eq!(core::mem::size_of::<Self>(), core::mem::size_of::<Simd<$bits_ty, LANES>>());
+ unsafe { core::mem::transmute_copy(&bits) }
+ }
+
+ /// Produces a vector where every lane has the absolute value of the
+ /// equivalently-indexed lane in `self`.
+ #[inline]
+ pub fn abs(self) -> Self {
+ unsafe { intrinsics::simd_fabs(self) }
+ }
+
+ /// Fused multiply-add. Computes `(self * a) + b` with only one rounding error,
+ /// yielding a more accurate result than an unfused multiply-add.
+ ///
+ /// Using `mul_add` *may* be more performant than an unfused multiply-add if the target
+ /// architecture has a dedicated `fma` CPU instruction. However, this is not always
+ /// true, and will be heavily dependent on designing algorithms with specific target
+ /// hardware in mind.
+ #[cfg(feature = "std")]
+ #[inline]
+ pub fn mul_add(self, a: Self, b: Self) -> Self {
+ unsafe { intrinsics::simd_fma(self, a, b) }
+ }
+
+ /// Produces a vector where every lane has the square root value
+ /// of the equivalently-indexed lane in `self`
+ #[inline]
+ #[cfg(feature = "std")]
+ pub fn sqrt(self) -> Self {
+ unsafe { intrinsics::simd_fsqrt(self) }
+ }
+
+ /// Takes the reciprocal (inverse) of each lane, `1/x`.
+ #[inline]
+ pub fn recip(self) -> Self {
+ Self::splat(1.0) / self
+ }
+
+ /// Converts each lane from radians to degrees.
+ #[inline]
+ pub fn to_degrees(self) -> Self {
+ // to_degrees uses a special constant for better precision, so extract that constant
+ self * Self::splat(<$type>::to_degrees(1.))
+ }
+
+ /// Converts each lane from degrees to radians.
+ #[inline]
+ pub fn to_radians(self) -> Self {
+ self * Self::splat(<$type>::to_radians(1.))
+ }
+
+ /// Returns true for each lane if it has a positive sign, including
+ /// `+0.0`, `NaN`s with positive sign bit and positive infinity.
+ #[inline]
+ pub fn is_sign_positive(self) -> Mask<$mask_ty, LANES> {
+ !self.is_sign_negative()
+ }
+
+ /// Returns true for each lane if it has a negative sign, including
+ /// `-0.0`, `NaN`s with negative sign bit and negative infinity.
+ #[inline]
+ pub fn is_sign_negative(self) -> Mask<$mask_ty, LANES> {
+ let sign_bits = self.to_bits() & Simd::splat((!0 >> 1) + 1);
+ sign_bits.lanes_gt(Simd::splat(0))
+ }
+
+ /// Returns true for each lane if its value is `NaN`.
+ #[inline]
+ pub fn is_nan(self) -> Mask<$mask_ty, LANES> {
+ self.lanes_ne(self)
+ }
+
+ /// Returns true for each lane if its value is positive infinity or negative infinity.
+ #[inline]
+ pub fn is_infinite(self) -> Mask<$mask_ty, LANES> {
+ self.abs().lanes_eq(Self::splat(<$type>::INFINITY))
+ }
+
+ /// Returns true for each lane if its value is neither infinite nor `NaN`.
+ #[inline]
+ pub fn is_finite(self) -> Mask<$mask_ty, LANES> {
+ self.abs().lanes_lt(Self::splat(<$type>::INFINITY))
+ }
+
+ /// Returns true for each lane if its value is subnormal.
+ #[inline]
+ pub fn is_subnormal(self) -> Mask<$mask_ty, LANES> {
+ self.abs().lanes_ne(Self::splat(0.0)) & (self.to_bits() & Self::splat(<$type>::INFINITY).to_bits()).lanes_eq(Simd::splat(0))
+ }
+
+ /// Returns true for each lane if its value is neither neither zero, infinite,
+ /// subnormal, or `NaN`.
+ #[inline]
+ pub fn is_normal(self) -> Mask<$mask_ty, LANES> {
+ !(self.abs().lanes_eq(Self::splat(0.0)) | self.is_nan() | self.is_subnormal() | self.is_infinite())
+ }
+
+ /// Replaces each lane with a number that represents its sign.
+ ///
+ /// * `1.0` if the number is positive, `+0.0`, or `INFINITY`
+ /// * `-1.0` if the number is negative, `-0.0`, or `NEG_INFINITY`
+ /// * `NAN` if the number is `NAN`
+ #[inline]
+ pub fn signum(self) -> Self {
+ self.is_nan().select(Self::splat(<$type>::NAN), Self::splat(1.0).copysign(self))
+ }
+
+ /// Returns each lane with the magnitude of `self` and the sign of `sign`.
+ ///
+ /// If any lane is a `NAN`, then a `NAN` with the sign of `sign` is returned.
+ #[inline]
+ pub fn copysign(self, sign: Self) -> Self {
+ let sign_bit = sign.to_bits() & Self::splat(-0.).to_bits();
+ let magnitude = self.to_bits() & !Self::splat(-0.).to_bits();
+ Self::from_bits(sign_bit | magnitude)
+ }
+
+ /// Returns the minimum of each lane.
+ ///
+ /// If one of the values is `NAN`, then the other value is returned.
+ #[inline]
+ pub fn min(self, other: Self) -> Self {
+ // TODO consider using an intrinsic
+ self.is_nan().select(
+ other,
+ self.lanes_ge(other).select(other, self)
+ )
+ }
+
+ /// Returns the maximum of each lane.
+ ///
+ /// If one of the values is `NAN`, then the other value is returned.
+ #[inline]
+ pub fn max(self, other: Self) -> Self {
+ // TODO consider using an intrinsic
+ self.is_nan().select(
+ other,
+ self.lanes_le(other).select(other, self)
+ )
+ }
+
+ /// Restrict each lane to a certain interval unless it is NaN.
+ ///
+ /// For each lane in `self`, returns the corresponding lane in `max` if the lane is
+ /// greater than `max`, and the corresponding lane in `min` if the lane is less
+ /// than `min`. Otherwise returns the lane in `self`.
+ #[inline]
+ pub fn clamp(self, min: Self, max: Self) -> Self {
+ assert!(
+ min.lanes_le(max).all(),
+ "each lane in `min` must be less than or equal to the corresponding lane in `max`",
+ );
+ let mut x = self;
+ x = x.lanes_lt(min).select(min, x);
+ x = x.lanes_gt(max).select(max, x);
+ x
+ }
+ }
+ };
+}
+
+impl_float_vector! { f32, u32, i32 }
+impl_float_vector! { f64, u64, i64 }
+
+/// Vector of two `f32` values
+pub type f32x2 = Simd<f32, 2>;
+
+/// Vector of four `f32` values
+pub type f32x4 = Simd<f32, 4>;
+
+/// Vector of eight `f32` values
+pub type f32x8 = Simd<f32, 8>;
+
+/// Vector of 16 `f32` values
+pub type f32x16 = Simd<f32, 16>;
+
+/// Vector of two `f64` values
+pub type f64x2 = Simd<f64, 2>;
+
+/// Vector of four `f64` values
+pub type f64x4 = Simd<f64, 4>;
+
+/// Vector of eight `f64` values
+pub type f64x8 = Simd<f64, 8>;
--- /dev/null
+#![allow(non_camel_case_types)]
+
+use crate::simd::{LaneCount, Mask, Simd, SupportedLaneCount};
+
+/// Implements additional integer traits (Eq, Ord, Hash) on the specified vector `$name`, holding multiple `$lanes` of `$type`.
+macro_rules! impl_integer_vector {
+ { $type:ty } => {
+ impl<const LANES: usize> Simd<$type, LANES>
+ where
+ LaneCount<LANES>: SupportedLaneCount,
+ {
+ /// Returns true for each positive lane and false if it is zero or negative.
+ #[inline]
+ pub fn is_positive(self) -> Mask<$type, LANES> {
+ self.lanes_gt(Self::splat(0))
+ }
+
+ /// Returns true for each negative lane and false if it is zero or positive.
+ #[inline]
+ pub fn is_negative(self) -> Mask<$type, LANES> {
+ self.lanes_lt(Self::splat(0))
+ }
+
+ /// Returns numbers representing the sign of each lane.
+ /// * `0` if the number is zero
+ /// * `1` if the number is positive
+ /// * `-1` if the number is negative
+ #[inline]
+ pub fn signum(self) -> Self {
+ self.is_positive().select(
+ Self::splat(1),
+ self.is_negative().select(Self::splat(-1), Self::splat(0))
+ )
+ }
+ }
+ }
+}
+
+impl_integer_vector! { isize }
+impl_integer_vector! { i16 }
+impl_integer_vector! { i32 }
+impl_integer_vector! { i64 }
+impl_integer_vector! { i8 }
+
+/// Vector of two `isize` values
+pub type isizex2 = Simd<isize, 2>;
+
+/// Vector of four `isize` values
+pub type isizex4 = Simd<isize, 4>;
+
+/// Vector of eight `isize` values
+pub type isizex8 = Simd<isize, 8>;
+
+/// Vector of two `i16` values
+pub type i16x2 = Simd<i16, 2>;
+
+/// Vector of four `i16` values
+pub type i16x4 = Simd<i16, 4>;
+
+/// Vector of eight `i16` values
+pub type i16x8 = Simd<i16, 8>;
+
+/// Vector of 16 `i16` values
+pub type i16x16 = Simd<i16, 16>;
+
+/// Vector of 32 `i16` values
+pub type i16x32 = Simd<i16, 32>;
+
+/// Vector of two `i32` values
+pub type i32x2 = Simd<i32, 2>;
+
+/// Vector of four `i32` values
+pub type i32x4 = Simd<i32, 4>;
+
+/// Vector of eight `i32` values
+pub type i32x8 = Simd<i32, 8>;
+
+/// Vector of 16 `i32` values
+pub type i32x16 = Simd<i32, 16>;
+
+/// Vector of two `i64` values
+pub type i64x2 = Simd<i64, 2>;
+
+/// Vector of four `i64` values
+pub type i64x4 = Simd<i64, 4>;
+
+/// Vector of eight `i64` values
+pub type i64x8 = Simd<i64, 8>;
+
+/// Vector of four `i8` values
+pub type i8x4 = Simd<i8, 4>;
+
+/// Vector of eight `i8` values
+pub type i8x8 = Simd<i8, 8>;
+
+/// Vector of 16 `i8` values
+pub type i8x16 = Simd<i8, 16>;
+
+/// Vector of 32 `i8` values
+pub type i8x32 = Simd<i8, 32>;
+
+/// Vector of 64 `i8` values
+pub type i8x64 = Simd<i8, 64>;
--- /dev/null
+//! Private implementation details of public gather/scatter APIs.
+use crate::simd::{LaneCount, Simd, SupportedLaneCount};
+use core::mem;
+
+/// A vector of *const T.
+#[derive(Debug, Copy, Clone)]
+#[repr(simd)]
+pub(crate) struct SimdConstPtr<T, const LANES: usize>([*const T; LANES]);
+
+impl<T, const LANES: usize> SimdConstPtr<T, LANES>
+where
+ LaneCount<LANES>: SupportedLaneCount,
+ T: Sized,
+{
+ #[inline]
+ #[must_use]
+ pub fn splat(ptr: *const T) -> Self {
+ Self([ptr; LANES])
+ }
+
+ #[inline]
+ #[must_use]
+ pub fn wrapping_add(self, addend: Simd<usize, LANES>) -> Self {
+ unsafe {
+ let x: Simd<usize, LANES> = mem::transmute_copy(&self);
+ mem::transmute_copy(&{ x + (addend * mem::size_of::<T>()) })
+ }
+ }
+}
+
+/// A vector of *mut T. Be very careful around potential aliasing.
+#[derive(Debug, Copy, Clone)]
+#[repr(simd)]
+pub(crate) struct SimdMutPtr<T, const LANES: usize>([*mut T; LANES]);
+
+impl<T, const LANES: usize> SimdMutPtr<T, LANES>
+where
+ LaneCount<LANES>: SupportedLaneCount,
+ T: Sized,
+{
+ #[inline]
+ #[must_use]
+ pub fn splat(ptr: *mut T) -> Self {
+ Self([ptr; LANES])
+ }
+
+ #[inline]
+ #[must_use]
+ pub fn wrapping_add(self, addend: Simd<usize, LANES>) -> Self {
+ unsafe {
+ let x: Simd<usize, LANES> = mem::transmute_copy(&self);
+ mem::transmute_copy(&{ x + (addend * mem::size_of::<T>()) })
+ }
+ }
+}
--- /dev/null
+#![allow(non_camel_case_types)]
+
+use crate::simd::Simd;
+
+/// Vector of two `usize` values
+pub type usizex2 = Simd<usize, 2>;
+
+/// Vector of four `usize` values
+pub type usizex4 = Simd<usize, 4>;
+
+/// Vector of eight `usize` values
+pub type usizex8 = Simd<usize, 8>;
+
+/// Vector of two `u16` values
+pub type u16x2 = Simd<u16, 2>;
+
+/// Vector of four `u16` values
+pub type u16x4 = Simd<u16, 4>;
+
+/// Vector of eight `u16` values
+pub type u16x8 = Simd<u16, 8>;
+
+/// Vector of 16 `u16` values
+pub type u16x16 = Simd<u16, 16>;
+
+/// Vector of 32 `u16` values
+pub type u16x32 = Simd<u16, 32>;
+
+/// Vector of two `u32` values
+pub type u32x2 = Simd<u32, 2>;
+
+/// Vector of four `u32` values
+pub type u32x4 = Simd<u32, 4>;
+
+/// Vector of eight `u32` values
+pub type u32x8 = Simd<u32, 8>;
+
+/// Vector of 16 `u32` values
+pub type u32x16 = Simd<u32, 16>;
+
+/// Vector of two `u64` values
+pub type u64x2 = Simd<u64, 2>;
+
+/// Vector of four `u64` values
+pub type u64x4 = Simd<u64, 4>;
+
+/// Vector of eight `u64` values
+pub type u64x8 = Simd<u64, 8>;
+
+/// Vector of four `u8` values
+pub type u8x4 = Simd<u8, 4>;
+
+/// Vector of eight `u8` values
+pub type u8x8 = Simd<u8, 8>;
+
+/// Vector of 16 `u8` values
+pub type u8x16 = Simd<u8, 16>;
+
+/// Vector of 32 `u8` values
+pub type u8x32 = Simd<u8, 32>;
+
+/// Vector of 64 `u8` values
+pub type u8x64 = Simd<u8, 64>;
--- /dev/null
+/// Provides implementations of `From<$a> for $b` and `From<$b> for $a` that transmutes the value.
+#[allow(unused)]
+macro_rules! from_transmute {
+ { unsafe $a:ty => $b:ty } => {
+ from_transmute!{ @impl $a => $b }
+ from_transmute!{ @impl $b => $a }
+ };
+ { @impl $from:ty => $to:ty } => {
+ impl core::convert::From<$from> for $to {
+ #[inline]
+ fn from(value: $from) -> $to {
+ unsafe { core::mem::transmute(value) }
+ }
+ }
+ };
+}
+
+/// Conversions to x86's SIMD types.
+#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+mod x86;
+
+#[cfg(any(target_arch = "wasm32"))]
+mod wasm32;
+
+#[cfg(any(target_arch = "aarch64", target_arch = "arm",))]
+mod arm;
+
+#[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))]
+mod powerpc;
--- /dev/null
+#![allow(unused)]
+use crate::simd::*;
+
+#[cfg(target_arch = "arm")]
+use core::arch::arm::*;
+
+#[cfg(target_arch = "aarch64")]
+use core::arch::aarch64::*;
+
+#[cfg(any(
+ target_arch = "aarch64",
+ all(target_arch = "arm", target_feature = "v7"),
+))]
+mod neon {
+ use super::*;
+
+ from_transmute! { unsafe f32x2 => float32x2_t }
+ from_transmute! { unsafe f32x4 => float32x4_t }
+
+ from_transmute! { unsafe u8x8 => uint8x8_t }
+ from_transmute! { unsafe u8x16 => uint8x16_t }
+ from_transmute! { unsafe i8x8 => int8x8_t }
+ from_transmute! { unsafe i8x16 => int8x16_t }
+ from_transmute! { unsafe u8x8 => poly8x8_t }
+ from_transmute! { unsafe u8x16 => poly8x16_t }
+
+ from_transmute! { unsafe u16x4 => uint16x4_t }
+ from_transmute! { unsafe u16x8 => uint16x8_t }
+ from_transmute! { unsafe i16x4 => int16x4_t }
+ from_transmute! { unsafe i16x8 => int16x8_t }
+ from_transmute! { unsafe u16x4 => poly16x4_t }
+ from_transmute! { unsafe u16x8 => poly16x8_t }
+
+ from_transmute! { unsafe u32x2 => uint32x2_t }
+ from_transmute! { unsafe u32x4 => uint32x4_t }
+ from_transmute! { unsafe i32x2 => int32x2_t }
+ from_transmute! { unsafe i32x4 => int32x4_t }
+
+ from_transmute! { unsafe Simd<u64, 1> => uint64x1_t }
+ from_transmute! { unsafe u64x2 => uint64x2_t }
+ from_transmute! { unsafe Simd<i64, 1> => int64x1_t }
+ from_transmute! { unsafe i64x2 => int64x2_t }
+ from_transmute! { unsafe Simd<u64, 1> => poly64x1_t }
+ from_transmute! { unsafe u64x2 => poly64x2_t }
+}
+
+#[cfg(any(
+ all(target_feature = "v5te", not(target_feature = "mclass")),
+ all(target_feature = "mclass", target_feature = "dsp"),
+))]
+mod dsp {
+ use super::*;
+
+ from_transmute! { unsafe Simd<u16, 2> => uint16x2_t }
+ from_transmute! { unsafe Simd<i16, 2> => int16x2_t }
+}
+
+#[cfg(any(
+ all(target_feature = "v6", not(target_feature = "mclass")),
+ all(target_feature = "mclass", target_feature = "dsp"),
+))]
+mod simd32 {
+ use super::*;
+
+ from_transmute! { unsafe Simd<u8, 4> => uint8x4_t }
+ from_transmute! { unsafe Simd<i8, 4> => int8x4_t }
+}
+
+#[cfg(target_arch = "aarch64")]
+mod aarch64 {
+ use super::neon::*;
+ use super::*;
+
+ from_transmute! { unsafe Simd<f64, 1> => float64x1_t }
+ from_transmute! { unsafe f64x2 => float64x2_t }
+}
--- /dev/null
+use crate::simd::*;
+
+#[cfg(target_arch = "powerpc")]
+use core::arch::powerpc::*;
+
+#[cfg(target_arch = "powerpc64")]
+use core::arch::powerpc64::*;
+
+from_transmute! { unsafe f64x2 => vector_double }
+from_transmute! { unsafe i64x2 => vector_signed_long }
+from_transmute! { unsafe u64x2 => vector_unsigned_long }
--- /dev/null
+use crate::simd::*;
+use core::arch::wasm32::v128;
+
+from_transmute! { unsafe u8x16 => v128 }
+from_transmute! { unsafe i8x16 => v128 }
+
+from_transmute! { unsafe u16x8 => v128 }
+from_transmute! { unsafe i16x8 => v128 }
+
+from_transmute! { unsafe u32x4 => v128 }
+from_transmute! { unsafe i32x4 => v128 }
+from_transmute! { unsafe f32x4 => v128 }
+
+from_transmute! { unsafe u64x2 => v128 }
+from_transmute! { unsafe i64x2 => 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 }
+}
--- /dev/null
+use crate::simd::*;
+
+#[cfg(any(target_arch = "x86"))]
+use core::arch::x86::*;
+
+#[cfg(target_arch = "x86_64")]
+use core::arch::x86_64::*;
+
+from_transmute! { unsafe u8x16 => __m128i }
+from_transmute! { unsafe u8x32 => __m256i }
+//from_transmute! { unsafe u8x64 => __m512i }
+from_transmute! { unsafe i8x16 => __m128i }
+from_transmute! { unsafe i8x32 => __m256i }
+//from_transmute! { unsafe i8x64 => __m512i }
+
+from_transmute! { unsafe u16x8 => __m128i }
+from_transmute! { unsafe u16x16 => __m256i }
+from_transmute! { unsafe u16x32 => __m512i }
+from_transmute! { unsafe i16x8 => __m128i }
+from_transmute! { unsafe i16x16 => __m256i }
+from_transmute! { unsafe i16x32 => __m512i }
+
+from_transmute! { unsafe u32x4 => __m128i }
+from_transmute! { unsafe u32x8 => __m256i }
+from_transmute! { unsafe u32x16 => __m512i }
+from_transmute! { unsafe i32x4 => __m128i }
+from_transmute! { unsafe i32x8 => __m256i }
+from_transmute! { unsafe i32x16 => __m512i }
+from_transmute! { unsafe f32x4 => __m128 }
+from_transmute! { unsafe f32x8 => __m256 }
+from_transmute! { unsafe f32x16 => __m512 }
+
+from_transmute! { unsafe u64x2 => __m128i }
+from_transmute! { unsafe u64x4 => __m256i }
+from_transmute! { unsafe u64x8 => __m512i }
+from_transmute! { unsafe i64x2 => __m128i }
+from_transmute! { unsafe i64x4 => __m256i }
+from_transmute! { unsafe i64x8 => __m512i }
+from_transmute! { unsafe f64x2 => __m128d }
+from_transmute! { unsafe f64x4 => __m256d }
+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 }
+}
--- /dev/null
+#![feature(portable_simd)]
+
+#[macro_use]
+mod ops_macros;
+impl_float_tests! { f32, i32 }
--- /dev/null
+#![feature(portable_simd)]
+
+#[macro_use]
+mod ops_macros;
+impl_float_tests! { f64, i64 }
--- /dev/null
+#![feature(portable_simd)]
+
+#[macro_use]
+mod ops_macros;
+impl_signed_tests! { i16 }
--- /dev/null
+#![feature(portable_simd)]
+
+#[macro_use]
+mod ops_macros;
+impl_signed_tests! { i32 }
--- /dev/null
+#![feature(portable_simd)]
+
+#[macro_use]
+mod ops_macros;
+impl_signed_tests! { i64 }
--- /dev/null
+#![feature(portable_simd)]
+
+#[macro_use]
+mod ops_macros;
+impl_signed_tests! { i8 }
--- /dev/null
+#![feature(portable_simd)]
+
+#[macro_use]
+mod ops_macros;
+impl_signed_tests! { isize }
--- /dev/null
+#![feature(portable_simd)]
+
+mod mask_ops_impl;
--- /dev/null
+mask_tests! { mask16x4, 4 }
+mask_tests! { mask16x8, 8 }
+mask_tests! { mask16x16, 16 }
+mask_tests! { mask16x32, 32 }
--- /dev/null
+mask_tests! { mask32x2, 2 }
+mask_tests! { mask32x4, 4 }
+mask_tests! { mask32x8, 8 }
+mask_tests! { mask32x16, 16 }
--- /dev/null
+mask_tests! { mask64x2, 2 }
+mask_tests! { mask64x4, 4 }
+mask_tests! { mask64x8, 8 }
--- /dev/null
+mask_tests! { mask8x8, 8 }
+mask_tests! { mask8x16, 16 }
+mask_tests! { mask8x32, 32 }
--- /dev/null
+macro_rules! mask_tests {
+ { $vector:ident, $lanes:literal } => {
+ #[cfg(test)]
+ mod $vector {
+ use core_simd::$vector as Vector;
+ const LANES: usize = $lanes;
+
+ #[cfg(target_arch = "wasm32")]
+ use wasm_bindgen_test::*;
+
+ #[cfg(target_arch = "wasm32")]
+ wasm_bindgen_test_configure!(run_in_browser);
+
+ fn from_slice(slice: &[bool]) -> Vector {
+ let mut value = Vector::default();
+ for (i, b) in slice.iter().take(LANES).enumerate() {
+ value.set(i, *b);
+ }
+ value
+ }
+
+ fn apply_unary_lanewise(x: Vector, f: impl Fn(bool) -> bool) -> Vector {
+ let mut value = Vector::default();
+ for i in 0..LANES {
+ value.set(i, f(x.test(i)));
+ }
+ value
+ }
+
+ fn apply_binary_lanewise(x: Vector, y: Vector, f: impl Fn(bool, bool) -> bool) -> Vector {
+ let mut value = Vector::default();
+ for i in 0..LANES {
+ value.set(i, f(x.test(i), y.test(i)));
+ }
+ value
+ }
+
+ fn apply_binary_scalar_lhs_lanewise(x: bool, mut y: Vector, f: impl Fn(bool, bool) -> bool) -> Vector {
+ for i in 0..LANES {
+ y.set(i, f(x, y.test(i)));
+ }
+ y
+ }
+
+ fn apply_binary_scalar_rhs_lanewise(mut x: Vector, y: bool, f: impl Fn(bool, bool) -> bool) -> Vector {
+ for i in 0..LANES {
+ x.set(i, f(x.test(i), y));
+ }
+ x
+ }
+
+ const A: [bool; 64] = [
+ false, true, false, true, false, false, true, true,
+ false, true, false, true, false, false, true, true,
+ false, true, false, true, false, false, true, true,
+ false, true, false, true, false, false, true, true,
+ false, true, false, true, false, false, true, true,
+ false, true, false, true, false, false, true, true,
+ false, true, false, true, false, false, true, true,
+ false, true, false, true, false, false, true, true,
+ ];
+ const B: [bool; 64] = [
+ false, false, true, true, false, true, false, true,
+ false, false, true, true, false, true, false, true,
+ false, false, true, true, false, true, false, true,
+ false, false, true, true, false, true, false, true,
+ false, false, true, true, false, true, false, true,
+ false, false, true, true, false, true, false, true,
+ false, false, true, true, false, true, false, true,
+ false, false, true, true, false, true, false, true,
+ ];
+
+ #[test]
+ #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
+ fn bitand() {
+ let a = from_slice(&A);
+ let b = from_slice(&B);
+ let expected = apply_binary_lanewise(a, b, core::ops::BitAnd::bitand);
+ assert_eq!(a & b, expected);
+ }
+
+ #[test]
+ #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
+ fn bitand_assign() {
+ let mut a = from_slice(&A);
+ let b = from_slice(&B);
+ let expected = apply_binary_lanewise(a, b, core::ops::BitAnd::bitand);
+ a &= b;
+ assert_eq!(a, expected);
+ }
+
+ #[test]
+ #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
+ fn bitand_scalar_rhs() {
+ let a = from_slice(&A);
+ let expected = a;
+ assert_eq!(a & true, expected);
+ assert_eq!(a & false, Vector::splat(false));
+ }
+
+ #[test]
+ #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
+ fn bitand_scalar_lhs() {
+ let a = from_slice(&A);
+ let expected = a;
+ assert_eq!(true & a, expected);
+ assert_eq!(false & a, Vector::splat(false));
+ }
+
+ #[test]
+ #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
+ fn bitand_assign_scalar() {
+ let mut a = from_slice(&A);
+ let expected = a;
+ a &= true;
+ assert_eq!(a, expected);
+ a &= false;
+ assert_eq!(a, Vector::splat(false));
+ }
+
+ #[test]
+ #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
+ fn bitor() {
+ let a = from_slice(&A);
+ let b = from_slice(&B);
+ let expected = apply_binary_lanewise(a, b, core::ops::BitOr::bitor);
+ assert_eq!(a | b, expected);
+ }
+
+ #[test]
+ #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
+ fn bitor_assign() {
+ let mut a = from_slice(&A);
+ let b = from_slice(&B);
+ let expected = apply_binary_lanewise(a, b, core::ops::BitOr::bitor);
+ a |= b;
+ assert_eq!(a, expected);
+ }
+
+ #[test]
+ #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
+ fn bitor_scalar_rhs() {
+ let a = from_slice(&A);
+ assert_eq!(a | false, a);
+ assert_eq!(a | true, Vector::splat(true));
+ }
+
+ #[test]
+ #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
+ fn bitor_scalar_lhs() {
+ let a = from_slice(&A);
+ assert_eq!(false | a, a);
+ assert_eq!(true | a, Vector::splat(true));
+ }
+
+ #[test]
+ #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
+ fn bitor_assign_scalar() {
+ let mut a = from_slice(&A);
+ let expected = a;
+ a |= false;
+ assert_eq!(a, expected);
+ a |= true;
+ assert_eq!(a, Vector::splat(true));
+ }
+
+ #[test]
+ #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
+ fn bitxor() {
+ let a = from_slice(&A);
+ let b = from_slice(&B);
+ let expected = apply_binary_lanewise(a, b, core::ops::BitXor::bitxor);
+ assert_eq!(a ^ b, expected);
+ }
+
+ #[test]
+ #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
+ fn bitxor_assign() {
+ let mut a = from_slice(&A);
+ let b = from_slice(&B);
+ let expected = apply_binary_lanewise(a, b, core::ops::BitXor::bitxor);
+ a ^= b;
+ assert_eq!(a, expected);
+ }
+
+ #[test]
+ #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
+ fn bitxor_scalar_rhs() {
+ let a = from_slice(&A);
+ let expected = apply_binary_scalar_rhs_lanewise(a, true, core::ops::BitXor::bitxor);
+ assert_eq!(a ^ false, a);
+ assert_eq!(a ^ true, expected);
+ }
+
+ #[test]
+ #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
+ fn bitxor_scalar_lhs() {
+ let a = from_slice(&A);
+ let expected = apply_binary_scalar_lhs_lanewise(true, a, core::ops::BitXor::bitxor);
+ assert_eq!(false ^ a, a);
+ assert_eq!(true ^ a, expected);
+ }
+
+ #[test]
+ #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
+ fn bitxor_assign_scalar() {
+ let mut a = from_slice(&A);
+ let expected_unset = a;
+ let expected_set = apply_binary_scalar_rhs_lanewise(a, true, core::ops::BitXor::bitxor);
+ a ^= false;
+ assert_eq!(a, expected_unset);
+ a ^= true;
+ assert_eq!(a, expected_set);
+ }
+
+ #[test]
+ #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
+ fn not() {
+ let v = from_slice(&A);
+ let expected = apply_unary_lanewise(v, core::ops::Not::not);
+ assert_eq!(!v, expected);
+ }
+ }
+ }
+}
--- /dev/null
+mask_tests! { masksizex2, 2 }
+mask_tests! { masksizex4, 4 }
+mask_tests! { masksizex8, 8 }
--- /dev/null
+#[macro_use]
+mod mask_macros;
+
+#[rustfmt::skip]
+mod mask8;
+mod mask16;
+mod mask32;
+mod mask64;
+mod masksize;
--- /dev/null
+#![feature(portable_simd)]
+
+#[cfg(target_arch = "wasm32")]
+use wasm_bindgen_test::*;
+
+#[cfg(target_arch = "wasm32")]
+wasm_bindgen_test_configure!(run_in_browser);
+
+macro_rules! test_mask_api {
+ { $type:ident } => {
+ #[allow(non_snake_case)]
+ mod $type {
+ #[cfg(target_arch = "wasm32")]
+ use wasm_bindgen_test::*;
+
+ #[test]
+ #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
+ fn set_and_test() {
+ let values = [true, false, false, true, false, false, true, false];
+ let mut mask = core_simd::Mask::<$type, 8>::splat(false);
+ for (lane, value) in values.iter().copied().enumerate() {
+ mask.set(lane, value);
+ }
+ for (lane, value) in values.iter().copied().enumerate() {
+ assert_eq!(mask.test(lane), value);
+ }
+ }
+
+ #[test]
+ #[should_panic]
+ fn set_invalid_lane() {
+ let mut mask = core_simd::Mask::<$type, 8>::splat(false);
+ mask.set(8, true);
+ let _ = mask;
+ }
+
+ #[test]
+ #[should_panic]
+ fn test_invalid_lane() {
+ let mask = core_simd::Mask::<$type, 8>::splat(false);
+ let _ = mask.test(8);
+ }
+
+ #[test]
+ fn any() {
+ assert!(!core_simd::Mask::<$type, 8>::splat(false).any());
+ assert!(core_simd::Mask::<$type, 8>::splat(true).any());
+ let mut v = core_simd::Mask::<$type, 8>::splat(false);
+ v.set(2, true);
+ assert!(v.any());
+ }
+
+ #[test]
+ fn all() {
+ assert!(!core_simd::Mask::<$type, 8>::splat(false).all());
+ assert!(core_simd::Mask::<$type, 8>::splat(true).all());
+ let mut v = core_simd::Mask::<$type, 8>::splat(false);
+ v.set(2, true);
+ assert!(!v.all());
+ }
+
+ #[test]
+ fn roundtrip_int_conversion() {
+ let values = [true, false, false, true, false, false, true, false];
+ let mask = core_simd::Mask::<$type, 8>::from_array(values);
+ let int = mask.to_int();
+ assert_eq!(int.to_array(), [-1, 0, 0, -1, 0, 0, -1, 0]);
+ assert_eq!(core_simd::Mask::<$type, 8>::from_int(int), mask);
+ }
+
+ #[cfg(feature = "generic_const_exprs")]
+ #[test]
+ fn roundtrip_bitmask_conversion() {
+ let values = [
+ true, false, false, true, false, false, true, false,
+ true, true, false, false, false, false, false, true,
+ ];
+ let mask = core_simd::Mask::<$type, 16>::from_array(values);
+ let bitmask = mask.to_bitmask();
+ assert_eq!(bitmask, [0b01001001, 0b10000011]);
+ assert_eq!(core_simd::Mask::<$type, 16>::from_bitmask(bitmask), mask);
+ }
+ }
+ }
+}
+
+mod mask_api {
+ test_mask_api! { i8 }
+ test_mask_api! { i16 }
+ test_mask_api! { i32 }
+ test_mask_api! { i64 }
+ test_mask_api! { isize }
+}
+
+#[test]
+fn convert() {
+ let values = [true, false, false, true, false, false, true, false];
+ assert_eq!(
+ core_simd::Mask::<i8, 8>::from_array(values),
+ core_simd::Mask::<i32, 8>::from_array(values).into()
+ );
+}
--- /dev/null
+/// Implements a test on a unary operation using proptest.
+///
+/// Compares the vector operation to the equivalent scalar operation.
+#[macro_export]
+macro_rules! impl_unary_op_test {
+ { $scalar:ty, $trait:ident :: $fn:ident, $scalar_fn:expr } => {
+ test_helpers::test_lanes! {
+ fn $fn<const LANES: usize>() {
+ test_helpers::test_unary_elementwise(
+ &<core_simd::Simd<$scalar, LANES> as core::ops::$trait>::$fn,
+ &$scalar_fn,
+ &|_| true,
+ );
+ }
+ }
+ };
+ { $scalar:ty, $trait:ident :: $fn:ident } => {
+ impl_unary_op_test! { $scalar, $trait::$fn, <$scalar as core::ops::$trait>::$fn }
+ };
+}
+
+/// Implements a test on a binary operation using proptest.
+///
+/// Compares the vector operation to the equivalent scalar operation.
+#[macro_export]
+macro_rules! impl_binary_op_test {
+ { $scalar:ty, $trait:ident :: $fn:ident, $trait_assign:ident :: $fn_assign:ident, $scalar_fn:expr } => {
+ mod $fn {
+ use super::*;
+ use core_simd::Simd;
+
+ test_helpers::test_lanes! {
+ fn normal<const LANES: usize>() {
+ test_helpers::test_binary_elementwise(
+ &<Simd<$scalar, LANES> as core::ops::$trait>::$fn,
+ &$scalar_fn,
+ &|_, _| true,
+ );
+ }
+
+ fn scalar_rhs<const LANES: usize>() {
+ test_helpers::test_binary_scalar_rhs_elementwise(
+ &<Simd<$scalar, LANES> as core::ops::$trait<$scalar>>::$fn,
+ &$scalar_fn,
+ &|_, _| true,
+ );
+ }
+
+ fn scalar_lhs<const LANES: usize>() {
+ test_helpers::test_binary_scalar_lhs_elementwise(
+ &<$scalar as core::ops::$trait<Simd<$scalar, LANES>>>::$fn,
+ &$scalar_fn,
+ &|_, _| true,
+ );
+ }
+
+ fn assign<const LANES: usize>() {
+ test_helpers::test_binary_elementwise(
+ &|mut a, b| { <Simd<$scalar, LANES> as core::ops::$trait_assign>::$fn_assign(&mut a, b); a },
+ &$scalar_fn,
+ &|_, _| true,
+ );
+ }
+
+ fn assign_scalar_rhs<const LANES: usize>() {
+ test_helpers::test_binary_scalar_rhs_elementwise(
+ &|mut a, b| { <Simd<$scalar, LANES> as core::ops::$trait_assign<$scalar>>::$fn_assign(&mut a, b); a },
+ &$scalar_fn,
+ &|_, _| true,
+ );
+ }
+ }
+ }
+ };
+ { $scalar:ty, $trait:ident :: $fn:ident, $trait_assign:ident :: $fn_assign:ident } => {
+ impl_binary_op_test! { $scalar, $trait::$fn, $trait_assign::$fn_assign, <$scalar as core::ops::$trait>::$fn }
+ };
+}
+
+/// Implements a test on a binary operation using proptest.
+///
+/// Like `impl_binary_op_test`, but allows providing a function for rejecting particular inputs
+/// (like the `proptest_assume` macro).
+///
+/// Compares the vector operation to the equivalent scalar operation.
+#[macro_export]
+macro_rules! impl_binary_checked_op_test {
+ { $scalar:ty, $trait:ident :: $fn:ident, $trait_assign:ident :: $fn_assign:ident, $scalar_fn:expr, $check_fn:expr } => {
+ mod $fn {
+ use super::*;
+ use core_simd::Simd;
+
+ test_helpers::test_lanes! {
+ fn normal<const LANES: usize>() {
+ test_helpers::test_binary_elementwise(
+ &<Simd<$scalar, LANES> as core::ops::$trait>::$fn,
+ &$scalar_fn,
+ &|x, y| x.iter().zip(y.iter()).all(|(x, y)| $check_fn(*x, *y)),
+ );
+ }
+
+ fn scalar_rhs<const LANES: usize>() {
+ test_helpers::test_binary_scalar_rhs_elementwise(
+ &<Simd<$scalar, LANES> as core::ops::$trait<$scalar>>::$fn,
+ &$scalar_fn,
+ &|x, y| x.iter().all(|x| $check_fn(*x, y)),
+ );
+ }
+
+ fn scalar_lhs<const LANES: usize>() {
+ test_helpers::test_binary_scalar_lhs_elementwise(
+ &<$scalar as core::ops::$trait<Simd<$scalar, LANES>>>::$fn,
+ &$scalar_fn,
+ &|x, y| y.iter().all(|y| $check_fn(x, *y)),
+ );
+ }
+
+ fn assign<const LANES: usize>() {
+ test_helpers::test_binary_elementwise(
+ &|mut a, b| { <Simd<$scalar, LANES> as core::ops::$trait_assign>::$fn_assign(&mut a, b); a },
+ &$scalar_fn,
+ &|x, y| x.iter().zip(y.iter()).all(|(x, y)| $check_fn(*x, *y)),
+ )
+ }
+
+ fn assign_scalar_rhs<const LANES: usize>() {
+ test_helpers::test_binary_scalar_rhs_elementwise(
+ &|mut a, b| { <Simd<$scalar, LANES> as core::ops::$trait_assign<$scalar>>::$fn_assign(&mut a, b); a },
+ &$scalar_fn,
+ &|x, y| x.iter().all(|x| $check_fn(*x, y)),
+ )
+ }
+ }
+ }
+ };
+ { $scalar:ty, $trait:ident :: $fn:ident, $trait_assign:ident :: $fn_assign:ident, $check_fn:expr } => {
+ impl_binary_checked_op_test! { $scalar, $trait::$fn, $trait_assign::$fn_assign, <$scalar as core::ops::$trait>::$fn, $check_fn }
+ };
+}
+
+#[macro_export]
+macro_rules! impl_common_integer_tests {
+ { $vector:ident, $scalar:ident } => {
+ test_helpers::test_lanes! {
+ fn horizontal_sum<const LANES: usize>() {
+ test_helpers::test_1(&|x| {
+ test_helpers::prop_assert_biteq! (
+ $vector::<LANES>::from_array(x).horizontal_sum(),
+ x.iter().copied().fold(0 as $scalar, $scalar::wrapping_add),
+ );
+ Ok(())
+ });
+ }
+
+ fn horizontal_product<const LANES: usize>() {
+ test_helpers::test_1(&|x| {
+ test_helpers::prop_assert_biteq! (
+ $vector::<LANES>::from_array(x).horizontal_product(),
+ x.iter().copied().fold(1 as $scalar, $scalar::wrapping_mul),
+ );
+ Ok(())
+ });
+ }
+
+ fn horizontal_and<const LANES: usize>() {
+ test_helpers::test_1(&|x| {
+ test_helpers::prop_assert_biteq! (
+ $vector::<LANES>::from_array(x).horizontal_and(),
+ x.iter().copied().fold(-1i8 as $scalar, <$scalar as core::ops::BitAnd>::bitand),
+ );
+ Ok(())
+ });
+ }
+
+ fn horizontal_or<const LANES: usize>() {
+ test_helpers::test_1(&|x| {
+ test_helpers::prop_assert_biteq! (
+ $vector::<LANES>::from_array(x).horizontal_or(),
+ x.iter().copied().fold(0 as $scalar, <$scalar as core::ops::BitOr>::bitor),
+ );
+ Ok(())
+ });
+ }
+
+ fn horizontal_xor<const LANES: usize>() {
+ test_helpers::test_1(&|x| {
+ test_helpers::prop_assert_biteq! (
+ $vector::<LANES>::from_array(x).horizontal_xor(),
+ x.iter().copied().fold(0 as $scalar, <$scalar as core::ops::BitXor>::bitxor),
+ );
+ Ok(())
+ });
+ }
+
+ fn horizontal_max<const LANES: usize>() {
+ test_helpers::test_1(&|x| {
+ test_helpers::prop_assert_biteq! (
+ $vector::<LANES>::from_array(x).horizontal_max(),
+ x.iter().copied().max().unwrap(),
+ );
+ Ok(())
+ });
+ }
+
+ fn horizontal_min<const LANES: usize>() {
+ test_helpers::test_1(&|x| {
+ test_helpers::prop_assert_biteq! (
+ $vector::<LANES>::from_array(x).horizontal_min(),
+ x.iter().copied().min().unwrap(),
+ );
+ Ok(())
+ });
+ }
+ }
+ }
+}
+
+/// Implement tests for signed integers.
+#[macro_export]
+macro_rules! impl_signed_tests {
+ { $scalar:tt } => {
+ mod $scalar {
+ type Vector<const LANES: usize> = core_simd::Simd<Scalar, LANES>;
+ type Scalar = $scalar;
+
+ impl_common_integer_tests! { Vector, Scalar }
+
+ test_helpers::test_lanes! {
+ fn neg<const LANES: usize>() {
+ test_helpers::test_unary_elementwise(
+ &<Vector::<LANES> as core::ops::Neg>::neg,
+ &<Scalar as core::ops::Neg>::neg,
+ &|x| !x.contains(&Scalar::MIN),
+ );
+ }
+
+ fn is_positive<const LANES: usize>() {
+ test_helpers::test_unary_mask_elementwise(
+ &Vector::<LANES>::is_positive,
+ &Scalar::is_positive,
+ &|_| true,
+ );
+ }
+
+ fn is_negative<const LANES: usize>() {
+ test_helpers::test_unary_mask_elementwise(
+ &Vector::<LANES>::is_negative,
+ &Scalar::is_negative,
+ &|_| true,
+ );
+ }
+
+ fn signum<const LANES: usize>() {
+ test_helpers::test_unary_elementwise(
+ &Vector::<LANES>::signum,
+ &Scalar::signum,
+ &|_| true,
+ )
+ }
+
+ }
+
+ test_helpers::test_lanes_panic! {
+ fn div_min_overflow_panics<const LANES: usize>() {
+ let a = Vector::<LANES>::splat(Scalar::MIN);
+ let b = Vector::<LANES>::splat(-1);
+ let _ = a / b;
+ }
+
+ fn div_by_all_zeros_panics<const LANES: usize>() {
+ let a = Vector::<LANES>::splat(42);
+ let b = Vector::<LANES>::splat(0);
+ let _ = a / b;
+ }
+
+ fn div_by_one_zero_panics<const LANES: usize>() {
+ let a = Vector::<LANES>::splat(42);
+ let mut b = Vector::<LANES>::splat(21);
+ b[0] = 0 as _;
+ let _ = a / b;
+ }
+
+ fn rem_min_overflow_panic<const LANES: usize>() {
+ let a = Vector::<LANES>::splat(Scalar::MIN);
+ let b = Vector::<LANES>::splat(-1);
+ let _ = a % b;
+ }
+
+ fn rem_zero_panic<const LANES: usize>() {
+ let a = Vector::<LANES>::splat(42);
+ let b = Vector::<LANES>::splat(0);
+ let _ = a % b;
+ }
+ }
+
+ test_helpers::test_lanes! {
+ fn div_neg_one_no_panic<const LANES: usize>() {
+ let a = Vector::<LANES>::splat(42);
+ let b = Vector::<LANES>::splat(-1);
+ let _ = a / b;
+ }
+
+ fn rem_neg_one_no_panic<const LANES: usize>() {
+ let a = Vector::<LANES>::splat(42);
+ let b = Vector::<LANES>::splat(-1);
+ let _ = a % b;
+ }
+ }
+
+ impl_binary_op_test!(Scalar, Add::add, AddAssign::add_assign, Scalar::wrapping_add);
+ impl_binary_op_test!(Scalar, Sub::sub, SubAssign::sub_assign, Scalar::wrapping_sub);
+ impl_binary_op_test!(Scalar, Mul::mul, MulAssign::mul_assign, Scalar::wrapping_mul);
+
+ // Exclude Div and Rem panicking cases
+ impl_binary_checked_op_test!(Scalar, Div::div, DivAssign::div_assign, Scalar::wrapping_div, |x, y| y != 0 && !(x == Scalar::MIN && y == -1));
+ impl_binary_checked_op_test!(Scalar, Rem::rem, RemAssign::rem_assign, Scalar::wrapping_rem, |x, y| y != 0 && !(x == Scalar::MIN && y == -1));
+
+ impl_unary_op_test!(Scalar, Not::not);
+ impl_binary_op_test!(Scalar, BitAnd::bitand, BitAndAssign::bitand_assign);
+ impl_binary_op_test!(Scalar, BitOr::bitor, BitOrAssign::bitor_assign);
+ impl_binary_op_test!(Scalar, BitXor::bitxor, BitXorAssign::bitxor_assign);
+ }
+ }
+}
+
+/// Implement tests for unsigned integers.
+#[macro_export]
+macro_rules! impl_unsigned_tests {
+ { $scalar:tt } => {
+ mod $scalar {
+ type Vector<const LANES: usize> = core_simd::Simd<Scalar, LANES>;
+ type Scalar = $scalar;
+
+ impl_common_integer_tests! { Vector, Scalar }
+
+ test_helpers::test_lanes_panic! {
+ fn rem_zero_panic<const LANES: usize>() {
+ let a = Vector::<LANES>::splat(42);
+ let b = Vector::<LANES>::splat(0);
+ let _ = a % b;
+ }
+ }
+
+ impl_binary_op_test!(Scalar, Add::add, AddAssign::add_assign, Scalar::wrapping_add);
+ impl_binary_op_test!(Scalar, Sub::sub, SubAssign::sub_assign, Scalar::wrapping_sub);
+ impl_binary_op_test!(Scalar, Mul::mul, MulAssign::mul_assign, Scalar::wrapping_mul);
+
+ // Exclude Div and Rem panicking cases
+ impl_binary_checked_op_test!(Scalar, Div::div, DivAssign::div_assign, Scalar::wrapping_div, |_, y| y != 0);
+ impl_binary_checked_op_test!(Scalar, Rem::rem, RemAssign::rem_assign, Scalar::wrapping_rem, |_, y| y != 0);
+
+ impl_unary_op_test!(Scalar, Not::not);
+ impl_binary_op_test!(Scalar, BitAnd::bitand, BitAndAssign::bitand_assign);
+ impl_binary_op_test!(Scalar, BitOr::bitor, BitOrAssign::bitor_assign);
+ impl_binary_op_test!(Scalar, BitXor::bitxor, BitXorAssign::bitxor_assign);
+ }
+ }
+}
+
+/// Implement tests for floating point numbers.
+#[macro_export]
+macro_rules! impl_float_tests {
+ { $scalar:tt, $int_scalar:tt } => {
+ mod $scalar {
+ type Vector<const LANES: usize> = core_simd::Simd<Scalar, LANES>;
+ type Scalar = $scalar;
+
+ impl_unary_op_test!(Scalar, Neg::neg);
+ impl_binary_op_test!(Scalar, Add::add, AddAssign::add_assign);
+ impl_binary_op_test!(Scalar, Sub::sub, SubAssign::sub_assign);
+ impl_binary_op_test!(Scalar, Mul::mul, MulAssign::mul_assign);
+ impl_binary_op_test!(Scalar, Div::div, DivAssign::div_assign);
+ impl_binary_op_test!(Scalar, Rem::rem, RemAssign::rem_assign);
+
+ test_helpers::test_lanes! {
+ fn is_sign_positive<const LANES: usize>() {
+ test_helpers::test_unary_mask_elementwise(
+ &Vector::<LANES>::is_sign_positive,
+ &Scalar::is_sign_positive,
+ &|_| true,
+ );
+ }
+
+ fn is_sign_negative<const LANES: usize>() {
+ test_helpers::test_unary_mask_elementwise(
+ &Vector::<LANES>::is_sign_negative,
+ &Scalar::is_sign_negative,
+ &|_| true,
+ );
+ }
+
+ fn is_finite<const LANES: usize>() {
+ test_helpers::test_unary_mask_elementwise(
+ &Vector::<LANES>::is_finite,
+ &Scalar::is_finite,
+ &|_| true,
+ );
+ }
+
+ fn is_infinite<const LANES: usize>() {
+ test_helpers::test_unary_mask_elementwise(
+ &Vector::<LANES>::is_infinite,
+ &Scalar::is_infinite,
+ &|_| true,
+ );
+ }
+
+ fn is_nan<const LANES: usize>() {
+ test_helpers::test_unary_mask_elementwise(
+ &Vector::<LANES>::is_nan,
+ &Scalar::is_nan,
+ &|_| true,
+ );
+ }
+
+ fn is_normal<const LANES: usize>() {
+ test_helpers::test_unary_mask_elementwise(
+ &Vector::<LANES>::is_normal,
+ &Scalar::is_normal,
+ &|_| true,
+ );
+ }
+
+ fn is_subnormal<const LANES: usize>() {
+ test_helpers::test_unary_mask_elementwise(
+ &Vector::<LANES>::is_subnormal,
+ &Scalar::is_subnormal,
+ &|_| true,
+ );
+ }
+
+ fn abs<const LANES: usize>() {
+ test_helpers::test_unary_elementwise(
+ &Vector::<LANES>::abs,
+ &Scalar::abs,
+ &|_| true,
+ )
+ }
+
+ fn recip<const LANES: usize>() {
+ test_helpers::test_unary_elementwise(
+ &Vector::<LANES>::recip,
+ &Scalar::recip,
+ &|_| true,
+ )
+ }
+
+ fn to_degrees<const LANES: usize>() {
+ test_helpers::test_unary_elementwise(
+ &Vector::<LANES>::to_degrees,
+ &Scalar::to_degrees,
+ &|_| true,
+ )
+ }
+
+ fn to_radians<const LANES: usize>() {
+ test_helpers::test_unary_elementwise(
+ &Vector::<LANES>::to_radians,
+ &Scalar::to_radians,
+ &|_| true,
+ )
+ }
+
+ fn signum<const LANES: usize>() {
+ test_helpers::test_unary_elementwise(
+ &Vector::<LANES>::signum,
+ &Scalar::signum,
+ &|_| true,
+ )
+ }
+
+ fn copysign<const LANES: usize>() {
+ test_helpers::test_binary_elementwise(
+ &Vector::<LANES>::copysign,
+ &Scalar::copysign,
+ &|_, _| true,
+ )
+ }
+
+ fn min<const LANES: usize>() {
+ // Regular conditions (both values aren't zero)
+ test_helpers::test_binary_elementwise(
+ &Vector::<LANES>::min,
+ &Scalar::min,
+ // Reject the case where both values are zero with different signs
+ &|a, b| {
+ for (a, b) in a.iter().zip(b.iter()) {
+ if *a == 0. && *b == 0. && a.signum() != b.signum() {
+ return false;
+ }
+ }
+ true
+ }
+ );
+
+ // Special case where both values are zero
+ let p_zero = Vector::<LANES>::splat(0.);
+ let n_zero = Vector::<LANES>::splat(-0.);
+ assert!(p_zero.min(n_zero).to_array().iter().all(|x| *x == 0.));
+ assert!(n_zero.min(p_zero).to_array().iter().all(|x| *x == 0.));
+ }
+
+ fn max<const LANES: usize>() {
+ // Regular conditions (both values aren't zero)
+ test_helpers::test_binary_elementwise(
+ &Vector::<LANES>::max,
+ &Scalar::max,
+ // Reject the case where both values are zero with different signs
+ &|a, b| {
+ for (a, b) in a.iter().zip(b.iter()) {
+ if *a == 0. && *b == 0. && a.signum() != b.signum() {
+ return false;
+ }
+ }
+ true
+ }
+ );
+
+ // Special case where both values are zero
+ let p_zero = Vector::<LANES>::splat(0.);
+ let n_zero = Vector::<LANES>::splat(-0.);
+ assert!(p_zero.max(n_zero).to_array().iter().all(|x| *x == 0.));
+ assert!(n_zero.max(p_zero).to_array().iter().all(|x| *x == 0.));
+ }
+
+ fn clamp<const LANES: usize>() {
+ test_helpers::test_3(&|value: [Scalar; LANES], mut min: [Scalar; LANES], mut max: [Scalar; LANES]| {
+ for (min, max) in min.iter_mut().zip(max.iter_mut()) {
+ if max < min {
+ core::mem::swap(min, max);
+ }
+ if min.is_nan() {
+ *min = Scalar::NEG_INFINITY;
+ }
+ if max.is_nan() {
+ *max = Scalar::INFINITY;
+ }
+ }
+
+ let mut result_scalar = [Scalar::default(); LANES];
+ for i in 0..LANES {
+ result_scalar[i] = value[i].clamp(min[i], max[i]);
+ }
+ let result_vector = Vector::from_array(value).clamp(min.into(), max.into()).to_array();
+ test_helpers::prop_assert_biteq!(result_scalar, result_vector);
+ Ok(())
+ })
+ }
+
+ fn horizontal_sum<const LANES: usize>() {
+ test_helpers::test_1(&|x| {
+ test_helpers::prop_assert_biteq! (
+ Vector::<LANES>::from_array(x).horizontal_sum(),
+ x.iter().sum(),
+ );
+ Ok(())
+ });
+ }
+
+ fn horizontal_product<const LANES: usize>() {
+ test_helpers::test_1(&|x| {
+ test_helpers::prop_assert_biteq! (
+ Vector::<LANES>::from_array(x).horizontal_product(),
+ x.iter().product(),
+ );
+ Ok(())
+ });
+ }
+
+ fn horizontal_max<const LANES: usize>() {
+ test_helpers::test_1(&|x| {
+ let vmax = Vector::<LANES>::from_array(x).horizontal_max();
+ let smax = x.iter().copied().fold(Scalar::NAN, Scalar::max);
+ // 0 and -0 are treated the same
+ if !(x.contains(&0.) && x.contains(&-0.) && vmax.abs() == 0. && smax.abs() == 0.) {
+ test_helpers::prop_assert_biteq!(vmax, smax);
+ }
+ Ok(())
+ });
+ }
+
+ fn horizontal_min<const LANES: usize>() {
+ test_helpers::test_1(&|x| {
+ let vmax = Vector::<LANES>::from_array(x).horizontal_min();
+ let smax = x.iter().copied().fold(Scalar::NAN, Scalar::min);
+ // 0 and -0 are treated the same
+ if !(x.contains(&0.) && x.contains(&-0.) && vmax.abs() == 0. && smax.abs() == 0.) {
+ test_helpers::prop_assert_biteq!(vmax, smax);
+ }
+ Ok(())
+ });
+ }
+ }
+
+ #[cfg(feature = "std")]
+ mod std {
+ use super::*;
+ test_helpers::test_lanes! {
+ fn sqrt<const LANES: usize>() {
+ test_helpers::test_unary_elementwise(
+ &Vector::<LANES>::sqrt,
+ &Scalar::sqrt,
+ &|_| true,
+ )
+ }
+
+ fn mul_add<const LANES: usize>() {
+ test_helpers::test_ternary_elementwise(
+ &Vector::<LANES>::mul_add,
+ &Scalar::mul_add,
+ &|_, _, _| true,
+ )
+ }
+ }
+ }
+ }
+ }
+}
--- /dev/null
+#![feature(portable_simd)]
+
+macro_rules! float_rounding_test {
+ { $scalar:tt, $int_scalar:tt } => {
+ mod $scalar {
+ type Vector<const LANES: usize> = core_simd::Simd<$scalar, LANES>;
+ type Scalar = $scalar;
+ type IntScalar = $int_scalar;
+
+ #[cfg(feature = "std")]
+ test_helpers::test_lanes! {
+ fn ceil<const LANES: usize>() {
+ test_helpers::test_unary_elementwise(
+ &Vector::<LANES>::ceil,
+ &Scalar::ceil,
+ &|_| true,
+ )
+ }
+
+ fn floor<const LANES: usize>() {
+ test_helpers::test_unary_elementwise(
+ &Vector::<LANES>::floor,
+ &Scalar::floor,
+ &|_| true,
+ )
+ }
+
+ fn round<const LANES: usize>() {
+ test_helpers::test_unary_elementwise(
+ &Vector::<LANES>::round,
+ &Scalar::round,
+ &|_| true,
+ )
+ }
+
+ fn trunc<const LANES: usize>() {
+ test_helpers::test_unary_elementwise(
+ &Vector::<LANES>::trunc,
+ &Scalar::trunc,
+ &|_| true,
+ )
+ }
+
+ fn fract<const LANES: usize>() {
+ test_helpers::test_unary_elementwise(
+ &Vector::<LANES>::fract,
+ &Scalar::fract,
+ &|_| true,
+ )
+ }
+ }
+
+ test_helpers::test_lanes! {
+ fn from_int<const LANES: usize>() {
+ test_helpers::test_unary_elementwise(
+ &Vector::<LANES>::round_from_int,
+ &|x| x as Scalar,
+ &|_| true,
+ )
+ }
+
+ fn to_int_unchecked<const LANES: usize>() {
+ // The maximum integer that can be represented by the equivalently sized float has
+ // all of the mantissa digits set to 1, pushed up to the MSB.
+ const ALL_MANTISSA_BITS: IntScalar = ((1 << <Scalar>::MANTISSA_DIGITS) - 1);
+ const MAX_REPRESENTABLE_VALUE: Scalar =
+ (ALL_MANTISSA_BITS << (core::mem::size_of::<Scalar>() * 8 - <Scalar>::MANTISSA_DIGITS as usize - 1)) as Scalar;
+
+ let mut runner = proptest::test_runner::TestRunner::default();
+ runner.run(
+ &test_helpers::array::UniformArrayStrategy::new(-MAX_REPRESENTABLE_VALUE..MAX_REPRESENTABLE_VALUE),
+ |x| {
+ let result_1 = unsafe { Vector::from_array(x).to_int_unchecked().to_array() };
+ let result_2 = {
+ let mut result = [0; LANES];
+ for (i, o) in x.iter().zip(result.iter_mut()) {
+ *o = unsafe { i.to_int_unchecked() };
+ }
+ result
+ };
+ test_helpers::prop_assert_biteq!(result_1, result_2);
+ Ok(())
+ },
+ ).unwrap();
+ }
+ }
+ }
+ }
+}
+
+float_rounding_test! { f32, i32 }
+float_rounding_test! { f64, i64 }
--- /dev/null
+#![feature(portable_simd)]
+use core_simd::{Simd, Swizzle};
+
+#[cfg(target_arch = "wasm32")]
+use wasm_bindgen_test::*;
+
+#[cfg(target_arch = "wasm32")]
+wasm_bindgen_test_configure!(run_in_browser);
+
+#[test]
+#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
+fn swizzle() {
+ struct Index;
+ impl Swizzle<4, 4> for Index {
+ const INDEX: [usize; 4] = [2, 1, 3, 0];
+ }
+ impl Swizzle<4, 2> for Index {
+ const INDEX: [usize; 2] = [1, 1];
+ }
+
+ let vector = Simd::from_array([2, 4, 1, 9]);
+ assert_eq!(Index::swizzle(vector).to_array(), [1, 4, 9, 2]);
+ assert_eq!(Index::swizzle(vector).to_array(), [4, 4]);
+}
+
+#[test]
+#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
+fn reverse() {
+ let a = Simd::from_array([1, 2, 3, 4]);
+ assert_eq!(a.reverse().to_array(), [4, 3, 2, 1]);
+}
+
+#[test]
+#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
+fn rotate() {
+ let a = Simd::from_array([1, 2, 3, 4]);
+ assert_eq!(a.rotate_lanes_left::<0>().to_array(), [1, 2, 3, 4]);
+ assert_eq!(a.rotate_lanes_left::<1>().to_array(), [2, 3, 4, 1]);
+ assert_eq!(a.rotate_lanes_left::<2>().to_array(), [3, 4, 1, 2]);
+ assert_eq!(a.rotate_lanes_left::<3>().to_array(), [4, 1, 2, 3]);
+ assert_eq!(a.rotate_lanes_left::<4>().to_array(), [1, 2, 3, 4]);
+ assert_eq!(a.rotate_lanes_left::<5>().to_array(), [2, 3, 4, 1]);
+ assert_eq!(a.rotate_lanes_right::<0>().to_array(), [1, 2, 3, 4]);
+ assert_eq!(a.rotate_lanes_right::<1>().to_array(), [4, 1, 2, 3]);
+ assert_eq!(a.rotate_lanes_right::<2>().to_array(), [3, 4, 1, 2]);
+ assert_eq!(a.rotate_lanes_right::<3>().to_array(), [2, 3, 4, 1]);
+ assert_eq!(a.rotate_lanes_right::<4>().to_array(), [1, 2, 3, 4]);
+ assert_eq!(a.rotate_lanes_right::<5>().to_array(), [4, 1, 2, 3]);
+}
+
+#[test]
+#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
+fn interleave() {
+ let a = Simd::from_array([0, 1, 2, 3, 4, 5, 6, 7]);
+ let b = Simd::from_array([8, 9, 10, 11, 12, 13, 14, 15]);
+ let (lo, hi) = a.interleave(b);
+ assert_eq!(lo.to_array(), [0, 8, 1, 9, 2, 10, 3, 11]);
+ assert_eq!(hi.to_array(), [4, 12, 5, 13, 6, 14, 7, 15]);
+ let (even, odd) = lo.deinterleave(hi);
+ assert_eq!(even, a);
+ assert_eq!(odd, b);
+}
--- /dev/null
+#![feature(portable_simd, generic_const_exprs, adt_const_params)]
+#![allow(incomplete_features)]
+#![cfg(feature = "generic_const_exprs")]
+
+use core_simd::Simd;
+
+#[test]
+fn byte_convert() {
+ let int = Simd::<u32, 2>::from_array([0xdeadbeef, 0x8badf00d]);
+ let bytes = int.to_ne_bytes();
+ assert_eq!(int[0].to_ne_bytes(), bytes[..4]);
+ assert_eq!(int[1].to_ne_bytes(), bytes[4..]);
+ assert_eq!(Simd::<u32, 2>::from_ne_bytes(bytes), int);
+}
--- /dev/null
+#![feature(portable_simd)]
+
+#[macro_use]
+mod ops_macros;
+impl_unsigned_tests! { u16 }
--- /dev/null
+#![feature(portable_simd)]
+
+#[macro_use]
+mod ops_macros;
+impl_unsigned_tests! { u32 }
--- /dev/null
+#![feature(portable_simd)]
+
+#[macro_use]
+mod ops_macros;
+impl_unsigned_tests! { u64 }
--- /dev/null
+#![feature(portable_simd)]
+
+#[macro_use]
+mod ops_macros;
+impl_unsigned_tests! { u8 }
--- /dev/null
+#![feature(portable_simd)]
+
+#[macro_use]
+mod ops_macros;
+impl_unsigned_tests! { usize }
--- /dev/null
+{
+ "goog:chromeOptions": {
+ "args": [
+ "--enable-features=WebAssemblySimd"
+ ]
+ }
+}
--- /dev/null
+[package]
+name = "test_helpers"
+version = "0.1.0"
+edition = "2021"
+publish = false
+
+[dependencies.proptest]
+version = "0.10"
+default-features = false
+features = ["alloc"]
--- /dev/null
+//! Generic-length array strategy.
+
+// Adapted from proptest's array code
+// Copyright 2017 Jason Lingle
+
+use core::{marker::PhantomData, mem::MaybeUninit};
+use proptest::{
+ strategy::{NewTree, Strategy, ValueTree},
+ test_runner::TestRunner,
+};
+
+#[must_use = "strategies do nothing unless used"]
+#[derive(Clone, Copy, Debug)]
+pub struct UniformArrayStrategy<S, T> {
+ strategy: S,
+ _marker: PhantomData<T>,
+}
+
+impl<S, T> UniformArrayStrategy<S, T> {
+ pub const fn new(strategy: S) -> Self {
+ Self {
+ strategy,
+ _marker: PhantomData,
+ }
+ }
+}
+
+pub struct ArrayValueTree<T> {
+ tree: T,
+ shrinker: usize,
+ last_shrinker: Option<usize>,
+}
+
+impl<T, S, const LANES: usize> Strategy for UniformArrayStrategy<S, [T; LANES]>
+where
+ T: core::fmt::Debug,
+ S: Strategy<Value = T>,
+{
+ type Tree = ArrayValueTree<[S::Tree; LANES]>;
+ type Value = [T; LANES];
+
+ fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
+ let tree: [S::Tree; LANES] = unsafe {
+ let mut tree: [MaybeUninit<S::Tree>; LANES] = MaybeUninit::uninit().assume_init();
+ for t in tree.iter_mut() {
+ *t = MaybeUninit::new(self.strategy.new_tree(runner)?)
+ }
+ core::mem::transmute_copy(&tree)
+ };
+ Ok(ArrayValueTree {
+ tree,
+ shrinker: 0,
+ last_shrinker: None,
+ })
+ }
+}
+
+impl<T: ValueTree, const LANES: usize> ValueTree for ArrayValueTree<[T; LANES]> {
+ type Value = [T::Value; LANES];
+
+ fn current(&self) -> Self::Value {
+ unsafe {
+ let mut value: [MaybeUninit<T::Value>; LANES] = MaybeUninit::uninit().assume_init();
+ for (tree_elem, value_elem) in self.tree.iter().zip(value.iter_mut()) {
+ *value_elem = MaybeUninit::new(tree_elem.current());
+ }
+ core::mem::transmute_copy(&value)
+ }
+ }
+
+ fn simplify(&mut self) -> bool {
+ while self.shrinker < LANES {
+ if self.tree[self.shrinker].simplify() {
+ self.last_shrinker = Some(self.shrinker);
+ return true;
+ } else {
+ self.shrinker += 1;
+ }
+ }
+
+ false
+ }
+
+ fn complicate(&mut self) -> bool {
+ if let Some(shrinker) = self.last_shrinker {
+ self.shrinker = shrinker;
+ if self.tree[shrinker].complicate() {
+ true
+ } else {
+ self.last_shrinker = None;
+ false
+ }
+ } else {
+ false
+ }
+ }
+}
--- /dev/null
+//! Compare numeric types by exact bit value.
+
+pub trait BitEq {
+ fn biteq(&self, other: &Self) -> bool;
+ fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result;
+}
+
+impl BitEq for bool {
+ fn biteq(&self, other: &Self) -> bool {
+ self == other
+ }
+
+ fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
+ write!(f, "{:?}", self)
+ }
+}
+
+macro_rules! impl_integer_biteq {
+ { $($type:ty),* } => {
+ $(
+ impl BitEq for $type {
+ fn biteq(&self, other: &Self) -> bool {
+ self == other
+ }
+
+ fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
+ write!(f, "{:?} ({:x})", self, self)
+ }
+ }
+ )*
+ };
+}
+
+impl_integer_biteq! { u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize }
+
+macro_rules! impl_float_biteq {
+ { $($type:ty),* } => {
+ $(
+ impl BitEq for $type {
+ fn biteq(&self, other: &Self) -> bool {
+ if self.is_nan() && other.is_nan() {
+ true // exact nan bits don't matter
+ } else {
+ self.to_bits() == other.to_bits()
+ }
+ }
+
+ fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
+ write!(f, "{:?} ({:x})", self, self.to_bits())
+ }
+ }
+ )*
+ };
+}
+
+impl_float_biteq! { f32, f64 }
+
+impl<T: BitEq, const N: usize> BitEq for [T; N] {
+ fn biteq(&self, other: &Self) -> bool {
+ self.iter()
+ .zip(other.iter())
+ .fold(true, |value, (left, right)| value && left.biteq(right))
+ }
+
+ fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
+ #[repr(transparent)]
+ struct Wrapper<'a, T: BitEq>(&'a T);
+
+ impl<T: BitEq> 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 BitEqWrapper<'a, T>(pub &'a T);
+
+impl<T: BitEq> PartialEq for BitEqWrapper<'_, T> {
+ fn eq(&self, other: &Self) -> bool {
+ self.0.biteq(other.0)
+ }
+}
+
+impl<T: BitEq> core::fmt::Debug for BitEqWrapper<'_, T> {
+ fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
+ self.0.fmt(f)
+ }
+}
+
+#[macro_export]
+macro_rules! prop_assert_biteq {
+ { $a:expr, $b:expr $(,)? } => {
+ {
+ use $crate::biteq::BitEqWrapper;
+ let a = $a;
+ let b = $b;
+ proptest::prop_assert_eq!(BitEqWrapper(&a), BitEqWrapper(&b));
+ }
+ }
+}
--- /dev/null
+pub mod array;
+
+#[cfg(target_arch = "wasm32")]
+pub mod wasm;
+
+#[macro_use]
+pub mod biteq;
+
+/// Specifies the default strategy for testing a type.
+///
+/// This strategy should be what "makes sense" to test.
+pub trait DefaultStrategy {
+ type Strategy: proptest::strategy::Strategy<Value = Self>;
+ fn default_strategy() -> Self::Strategy;
+}
+
+macro_rules! impl_num {
+ { $type:tt } => {
+ impl DefaultStrategy for $type {
+ type Strategy = proptest::num::$type::Any;
+ fn default_strategy() -> Self::Strategy {
+ proptest::num::$type::ANY
+ }
+ }
+ }
+}
+
+impl_num! { i8 }
+impl_num! { i16 }
+impl_num! { i32 }
+impl_num! { i64 }
+impl_num! { isize }
+impl_num! { u8 }
+impl_num! { u16 }
+impl_num! { u32 }
+impl_num! { u64 }
+impl_num! { usize }
+impl_num! { f32 }
+impl_num! { f64 }
+
+#[cfg(not(target_arch = "wasm32"))]
+impl DefaultStrategy for u128 {
+ type Strategy = proptest::num::u128::Any;
+ fn default_strategy() -> Self::Strategy {
+ proptest::num::u128::ANY
+ }
+}
+
+#[cfg(not(target_arch = "wasm32"))]
+impl DefaultStrategy for i128 {
+ type Strategy = proptest::num::i128::Any;
+ fn default_strategy() -> Self::Strategy {
+ proptest::num::i128::ANY
+ }
+}
+
+#[cfg(target_arch = "wasm32")]
+impl DefaultStrategy for u128 {
+ type Strategy = crate::wasm::u128::Any;
+ fn default_strategy() -> Self::Strategy {
+ crate::wasm::u128::ANY
+ }
+}
+
+#[cfg(target_arch = "wasm32")]
+impl DefaultStrategy for i128 {
+ type Strategy = crate::wasm::i128::Any;
+ fn default_strategy() -> Self::Strategy {
+ crate::wasm::i128::ANY
+ }
+}
+
+impl<T: core::fmt::Debug + DefaultStrategy, const LANES: usize> DefaultStrategy for [T; LANES] {
+ type Strategy = crate::array::UniformArrayStrategy<T::Strategy, Self>;
+ fn default_strategy() -> Self::Strategy {
+ Self::Strategy::new(T::default_strategy())
+ }
+}
+
+/// Test a function that takes a single value.
+pub fn test_1<A: core::fmt::Debug + DefaultStrategy>(
+ f: &dyn Fn(A) -> proptest::test_runner::TestCaseResult,
+) {
+ let mut runner = proptest::test_runner::TestRunner::default();
+ runner.run(&A::default_strategy(), f).unwrap();
+}
+
+/// Test a function that takes two values.
+pub fn test_2<A: core::fmt::Debug + DefaultStrategy, B: core::fmt::Debug + DefaultStrategy>(
+ f: &dyn Fn(A, B) -> proptest::test_runner::TestCaseResult,
+) {
+ let mut runner = proptest::test_runner::TestRunner::default();
+ runner
+ .run(&(A::default_strategy(), B::default_strategy()), |(a, b)| {
+ f(a, b)
+ })
+ .unwrap();
+}
+
+/// Test a function that takes two values.
+pub fn test_3<
+ A: core::fmt::Debug + DefaultStrategy,
+ B: core::fmt::Debug + DefaultStrategy,
+ C: core::fmt::Debug + DefaultStrategy,
+>(
+ f: &dyn Fn(A, B, C) -> proptest::test_runner::TestCaseResult,
+) {
+ let mut runner = proptest::test_runner::TestRunner::default();
+ runner
+ .run(
+ &(
+ A::default_strategy(),
+ B::default_strategy(),
+ C::default_strategy(),
+ ),
+ |(a, b, c)| f(a, b, c),
+ )
+ .unwrap();
+}
+
+/// Test a unary vector function against a unary scalar function, applied elementwise.
+#[inline(never)]
+pub fn test_unary_elementwise<Scalar, ScalarResult, Vector, VectorResult, const LANES: usize>(
+ fv: &dyn Fn(Vector) -> VectorResult,
+ fs: &dyn Fn(Scalar) -> ScalarResult,
+ check: &dyn Fn([Scalar; LANES]) -> bool,
+) where
+ Scalar: Copy + Default + core::fmt::Debug + DefaultStrategy,
+ ScalarResult: Copy + Default + biteq::BitEq + core::fmt::Debug + DefaultStrategy,
+ Vector: Into<[Scalar; LANES]> + From<[Scalar; LANES]> + Copy,
+ VectorResult: Into<[ScalarResult; LANES]> + From<[ScalarResult; LANES]> + Copy,
+{
+ test_1(&|x: [Scalar; LANES]| {
+ proptest::prop_assume!(check(x));
+ let result_1: [ScalarResult; LANES] = fv(x.into()).into();
+ let result_2: [ScalarResult; LANES] = {
+ let mut result = [ScalarResult::default(); LANES];
+ for (i, o) in x.iter().zip(result.iter_mut()) {
+ *o = fs(*i);
+ }
+ result
+ };
+ crate::prop_assert_biteq!(result_1, result_2);
+ Ok(())
+ });
+}
+
+/// Test a unary vector function against a unary scalar function, applied elementwise.
+#[inline(never)]
+pub fn test_unary_mask_elementwise<Scalar, Vector, Mask, const LANES: usize>(
+ fv: &dyn Fn(Vector) -> Mask,
+ fs: &dyn Fn(Scalar) -> bool,
+ check: &dyn Fn([Scalar; LANES]) -> bool,
+) where
+ Scalar: Copy + Default + core::fmt::Debug + DefaultStrategy,
+ Vector: Into<[Scalar; LANES]> + From<[Scalar; LANES]> + Copy,
+ Mask: Into<[bool; LANES]> + From<[bool; LANES]> + Copy,
+{
+ test_1(&|x: [Scalar; LANES]| {
+ proptest::prop_assume!(check(x));
+ let result_1: [bool; LANES] = fv(x.into()).into();
+ let result_2: [bool; LANES] = {
+ let mut result = [false; LANES];
+ for (i, o) in x.iter().zip(result.iter_mut()) {
+ *o = fs(*i);
+ }
+ result
+ };
+ crate::prop_assert_biteq!(result_1, result_2);
+ Ok(())
+ });
+}
+
+/// Test a binary vector function against a binary scalar function, applied elementwise.
+#[inline(never)]
+pub fn test_binary_elementwise<
+ 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,
+) where
+ Scalar1: Copy + Default + core::fmt::Debug + DefaultStrategy,
+ Scalar2: Copy + Default + core::fmt::Debug + DefaultStrategy,
+ ScalarResult: Copy + Default + biteq::BitEq + core::fmt::Debug + DefaultStrategy,
+ Vector1: Into<[Scalar1; LANES]> + From<[Scalar1; LANES]> + Copy,
+ Vector2: Into<[Scalar2; LANES]> + From<[Scalar2; LANES]> + Copy,
+ VectorResult: Into<[ScalarResult; LANES]> + From<[ScalarResult; LANES]> + Copy,
+{
+ test_2(&|x: [Scalar1; LANES], y: [Scalar2; LANES]| {
+ proptest::prop_assume!(check(x, y));
+ let result_1: [ScalarResult; LANES] = fv(x.into(), y.into()).into();
+ let result_2: [ScalarResult; LANES] = {
+ let mut result = [ScalarResult::default(); LANES];
+ for ((i1, i2), o) in x.iter().zip(y.iter()).zip(result.iter_mut()) {
+ *o = fs(*i1, *i2);
+ }
+ result
+ };
+ crate::prop_assert_biteq!(result_1, result_2);
+ Ok(())
+ });
+}
+
+/// Test a binary vector-scalar function against a binary scalar function, applied elementwise.
+#[inline(never)]
+pub fn test_binary_scalar_rhs_elementwise<
+ Scalar1,
+ Scalar2,
+ ScalarResult,
+ Vector,
+ VectorResult,
+ const LANES: usize,
+>(
+ fv: &dyn Fn(Vector, Scalar2) -> VectorResult,
+ fs: &dyn Fn(Scalar1, Scalar2) -> ScalarResult,
+ check: &dyn Fn([Scalar1; LANES], Scalar2) -> bool,
+) where
+ Scalar1: Copy + Default + core::fmt::Debug + DefaultStrategy,
+ Scalar2: Copy + Default + core::fmt::Debug + DefaultStrategy,
+ ScalarResult: Copy + Default + biteq::BitEq + core::fmt::Debug + DefaultStrategy,
+ Vector: Into<[Scalar1; LANES]> + From<[Scalar1; LANES]> + Copy,
+ VectorResult: Into<[ScalarResult; LANES]> + From<[ScalarResult; LANES]> + Copy,
+{
+ test_2(&|x: [Scalar1; LANES], y: Scalar2| {
+ proptest::prop_assume!(check(x, y));
+ let result_1: [ScalarResult; LANES] = fv(x.into(), y).into();
+ let result_2: [ScalarResult; LANES] = {
+ let mut result = [ScalarResult::default(); LANES];
+ for (i, o) in x.iter().zip(result.iter_mut()) {
+ *o = fs(*i, y);
+ }
+ result
+ };
+ crate::prop_assert_biteq!(result_1, result_2);
+ Ok(())
+ });
+}
+
+/// Test a binary vector-scalar function against a binary scalar function, applied elementwise.
+#[inline(never)]
+pub fn test_binary_scalar_lhs_elementwise<
+ Scalar1,
+ Scalar2,
+ ScalarResult,
+ Vector,
+ VectorResult,
+ const LANES: usize,
+>(
+ fv: &dyn Fn(Scalar1, Vector) -> VectorResult,
+ fs: &dyn Fn(Scalar1, Scalar2) -> ScalarResult,
+ check: &dyn Fn(Scalar1, [Scalar2; LANES]) -> bool,
+) where
+ Scalar1: Copy + Default + core::fmt::Debug + DefaultStrategy,
+ Scalar2: Copy + Default + core::fmt::Debug + DefaultStrategy,
+ ScalarResult: Copy + Default + biteq::BitEq + core::fmt::Debug + DefaultStrategy,
+ Vector: Into<[Scalar2; LANES]> + From<[Scalar2; LANES]> + Copy,
+ VectorResult: Into<[ScalarResult; LANES]> + From<[ScalarResult; LANES]> + Copy,
+{
+ test_2(&|x: Scalar1, y: [Scalar2; LANES]| {
+ proptest::prop_assume!(check(x, y));
+ let result_1: [ScalarResult; LANES] = fv(x, y.into()).into();
+ let result_2: [ScalarResult; LANES] = {
+ let mut result = [ScalarResult::default(); LANES];
+ for (i, o) in y.iter().zip(result.iter_mut()) {
+ *o = fs(x, *i);
+ }
+ result
+ };
+ crate::prop_assert_biteq!(result_1, result_2);
+ Ok(())
+ });
+}
+
+/// Test a ternary vector function against a ternary scalar function, applied elementwise.
+#[inline(never)]
+pub fn test_ternary_elementwise<
+ Scalar1,
+ Scalar2,
+ Scalar3,
+ ScalarResult,
+ Vector1,
+ Vector2,
+ Vector3,
+ VectorResult,
+ const LANES: usize,
+>(
+ fv: &dyn Fn(Vector1, Vector2, Vector3) -> VectorResult,
+ fs: &dyn Fn(Scalar1, Scalar2, Scalar3) -> ScalarResult,
+ check: &dyn Fn([Scalar1; LANES], [Scalar2; LANES], [Scalar3; LANES]) -> bool,
+) where
+ Scalar1: Copy + Default + core::fmt::Debug + DefaultStrategy,
+ Scalar2: Copy + Default + core::fmt::Debug + DefaultStrategy,
+ Scalar3: Copy + Default + core::fmt::Debug + DefaultStrategy,
+ ScalarResult: Copy + Default + biteq::BitEq + core::fmt::Debug + DefaultStrategy,
+ Vector1: Into<[Scalar1; LANES]> + From<[Scalar1; LANES]> + Copy,
+ Vector2: Into<[Scalar2; LANES]> + From<[Scalar2; LANES]> + Copy,
+ Vector3: Into<[Scalar3; LANES]> + From<[Scalar3; LANES]> + Copy,
+ VectorResult: Into<[ScalarResult; LANES]> + From<[ScalarResult; LANES]> + Copy,
+{
+ test_3(
+ &|x: [Scalar1; LANES], y: [Scalar2; LANES], z: [Scalar3; LANES]| {
+ proptest::prop_assume!(check(x, y, z));
+ let result_1: [ScalarResult; LANES] = fv(x.into(), y.into(), z.into()).into();
+ let result_2: [ScalarResult; LANES] = {
+ let mut result = [ScalarResult::default(); LANES];
+ for ((i1, (i2, i3)), o) in
+ x.iter().zip(y.iter().zip(z.iter())).zip(result.iter_mut())
+ {
+ *o = fs(*i1, *i2, *i3);
+ }
+ result
+ };
+ crate::prop_assert_biteq!(result_1, result_2);
+ Ok(())
+ },
+ );
+}
+
+/// Expand a const-generic test into separate tests for each possible lane count.
+#[macro_export]
+macro_rules! test_lanes {
+ {
+ $(fn $test:ident<const $lanes:ident: usize>() $body:tt)*
+ } => {
+ $(
+ mod $test {
+ use super::*;
+
+ fn implementation<const $lanes: usize>()
+ where
+ core_simd::LaneCount<$lanes>: core_simd::SupportedLaneCount,
+ $body
+
+ #[cfg(target_arch = "wasm32")]
+ wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
+
+ #[test]
+ #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
+ fn lanes_1() {
+ implementation::<1>();
+ }
+
+ #[test]
+ #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
+ fn lanes_2() {
+ implementation::<2>();
+ }
+
+ #[test]
+ #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
+ fn lanes_4() {
+ implementation::<4>();
+ }
+
+ #[test]
+ #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
+ fn lanes_8() {
+ implementation::<8>();
+ }
+
+ #[test]
+ #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
+ fn lanes_16() {
+ implementation::<16>();
+ }
+
+ #[test]
+ #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
+ fn lanes_32() {
+ implementation::<32>();
+ }
+ }
+ )*
+ }
+}
+
+/// Expand a const-generic `#[should_panic]` test into separate tests for each possible lane count.
+#[macro_export]
+macro_rules! test_lanes_panic {
+ {
+ $(fn $test:ident<const $lanes:ident: usize>() $body:tt)*
+ } => {
+ $(
+ mod $test {
+ use super::*;
+
+ fn implementation<const $lanes: usize>()
+ where
+ core_simd::LaneCount<$lanes>: core_simd::SupportedLaneCount,
+ $body
+
+ #[test]
+ #[should_panic]
+ fn lanes_1() {
+ implementation::<1>();
+ }
+
+ #[test]
+ #[should_panic]
+ fn lanes_2() {
+ implementation::<2>();
+ }
+
+ #[test]
+ #[should_panic]
+ fn lanes_4() {
+ implementation::<4>();
+ }
+
+ #[test]
+ #[should_panic]
+ fn lanes_8() {
+ implementation::<8>();
+ }
+
+ #[test]
+ #[should_panic]
+ fn lanes_16() {
+ implementation::<16>();
+ }
+
+ #[test]
+ #[should_panic]
+ fn lanes_32() {
+ implementation::<32>();
+ }
+ }
+ )*
+ }
+}
--- /dev/null
+//! Strategies for `u128` and `i128`, since proptest doesn't provide them for the wasm target.
+
+macro_rules! impl_num {
+ { $name:ident } => {
+ pub(crate) mod $name {
+ type InnerStrategy = crate::array::UniformArrayStrategy<proptest::num::u64::Any, [u64; 2]>;
+ use proptest::strategy::{Strategy, ValueTree, NewTree};
+
+
+ #[must_use = "strategies do nothing unless used"]
+ #[derive(Clone, Copy, Debug)]
+ pub struct Any {
+ strategy: InnerStrategy,
+ }
+
+ pub struct BinarySearch {
+ inner: <InnerStrategy as Strategy>::Tree,
+ }
+
+ impl ValueTree for BinarySearch {
+ type Value = $name;
+
+ fn current(&self) -> $name {
+ unsafe { core::mem::transmute(self.inner.current()) }
+ }
+
+ fn simplify(&mut self) -> bool {
+ self.inner.simplify()
+ }
+
+ fn complicate(&mut self) -> bool {
+ self.inner.complicate()
+ }
+ }
+
+ impl Strategy for Any {
+ type Tree = BinarySearch;
+ type Value = $name;
+
+ fn new_tree(&self, runner: &mut proptest::test_runner::TestRunner) -> NewTree<Self> {
+ Ok(BinarySearch { inner: self.strategy.new_tree(runner)? })
+ }
+ }
+
+ pub const ANY: Any = Any { strategy: InnerStrategy::new(proptest::num::u64::ANY) };
+ }
+ }
+}
+
+impl_num! { u128 }
+impl_num! { i128 }
fn clone($self: &$S::TokenStream) -> $S::TokenStream;
fn new() -> $S::TokenStream;
fn is_empty($self: &$S::TokenStream) -> bool;
+ fn expand_expr($self: &$S::TokenStream) -> Result<$S::TokenStream, ()>;
fn from_str(src: &str) -> $S::TokenStream;
fn to_string($self: &$S::TokenStream) -> String;
fn from_token_tree(
#[derive(Debug)]
pub struct LexError;
-impl LexError {
- fn new() -> Self {
- LexError
- }
-}
-
#[stable(feature = "proc_macro_lexerror_impls", since = "1.44.0")]
impl fmt::Display for LexError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
#[stable(feature = "proc_macro_lib", since = "1.15.0")]
impl !Sync for LexError {}
+/// Error returned from `TokenStream::expand_expr`.
+#[unstable(feature = "proc_macro_expand", issue = "90765")]
+#[non_exhaustive]
+#[derive(Debug)]
+pub struct ExpandError;
+
+#[unstable(feature = "proc_macro_expand", issue = "90765")]
+impl fmt::Display for ExpandError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.write_str("macro expansion failed")
+ }
+}
+
+#[unstable(feature = "proc_macro_expand", issue = "90765")]
+impl error::Error for ExpandError {}
+
+#[unstable(feature = "proc_macro_expand", issue = "90765")]
+impl !Send for ExpandError {}
+
+#[unstable(feature = "proc_macro_expand", issue = "90765")]
+impl !Sync for ExpandError {}
+
impl TokenStream {
/// Returns an empty `TokenStream` containing no token trees.
#[stable(feature = "proc_macro_lib2", since = "1.29.0")]
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
+
+ /// Parses this `TokenStream` as an expression and attempts to expand any
+ /// macros within it. Returns the expanded `TokenStream`.
+ ///
+ /// Currently only expressions expanding to literals will succeed, although
+ /// this may be relaxed in the future.
+ ///
+ /// NOTE: In error conditions, `expand_expr` may leave macros unexpanded,
+ /// report an error, failing compilation, and/or return an `Err(..)`. The
+ /// specific behavior for any error condition, and what conditions are
+ /// considered errors, is unspecified and may change in the future.
+ #[unstable(feature = "proc_macro_expand", issue = "90765")]
+ pub fn expand_expr(&self) -> Result<TokenStream, ExpandError> {
+ match bridge::client::TokenStream::expand_expr(&self.0) {
+ Ok(stream) => Ok(TokenStream(stream)),
+ Err(_) => Err(ExpandError),
+ }
+ }
}
/// Attempts to break the string into tokens and parse those tokens into a token stream.
fn from_str(src: &str) -> Result<Self, LexError> {
match bridge::client::Literal::from_str(src) {
Ok(literal) => Ok(Literal(literal)),
- Err(()) => Err(LexError::new()),
+ Err(()) => Err(LexError),
}
}
}
//! not. Normally, this would require a `find` followed by an `insert`,
//! effectively duplicating the search effort on each insertion.
//!
-//! When a user calls `map.entry(&key)`, the map will search for the key and
+//! When a user calls `map.entry(key)`, the map will search for the key and
//! then yield a variant of the `Entry` enum.
//!
//! If a `Vacant(entry)` is yielded, then the key *was not* found. In this case
/// T` and `*mut T`. More about `const` as used in raw pointers can be read at the Rust docs for the [pointer primitive].
///
/// [pointer primitive]: pointer
-/// [Rust Book]: ../book/ch03-01-variables-and-mutability.html#differences-between-variables-and-constants
+/// [Rust Book]: ../book/ch03-01-variables-and-mutability.html#constants
/// [Reference]: ../reference/items/constant-items.html
/// [const-eval]: ../reference/const_eval.html
mod const_keyword {}
#![feature(const_ipv4)]
#![feature(const_ipv6)]
#![feature(const_option)]
-#![feature(const_raw_ptr_deref)]
+#![cfg_attr(bootstrap, feature(const_raw_ptr_deref))]
+#![cfg_attr(not(bootstrap), feature(const_mut_refs))]
#![feature(const_socketaddr)]
#![feature(const_trait_impl)]
#![feature(container_error_extra)]
#![feature(panic_internals)]
#![feature(panic_unwind)]
#![feature(pin_static_ref)]
+#![cfg_attr(not(bootstrap), feature(portable_simd))]
#![feature(prelude_import)]
#![feature(ptr_internals)]
#![feature(rustc_attrs)]
pub use core::ptr;
#[stable(feature = "rust1", since = "1.0.0")]
pub use core::result;
+#[unstable(feature = "portable_simd", issue = "86656")]
+#[cfg(not(bootstrap))]
+pub use core::simd;
#[unstable(feature = "async_stream", issue = "79024")]
pub use core::stream;
#[stable(feature = "i128", since = "1.26.0")]
/// Sets the supplementary group IDs for the calling process. Translates to
/// a `setgroups` call in the child process.
- #[unstable(feature = "setgroups", issue = "38527", reason = "")]
+ #[unstable(feature = "setgroups", issue = "90747")]
fn groups(
&mut self,
#[cfg(not(target_os = "vxworks"))] groups: &[u32],
/// [`ExitStatusError`](process::ExitStatusError).
///
/// On Unix, `ExitStatus` **does not necessarily represent an exit status**, as
-/// passed to the `exit` system call or returned by
+/// passed to the `_exit` system call or returned by
/// [`ExitStatus::code()`](crate::process::ExitStatus::code). It represents **any wait status**
/// as returned by one of the `wait` family of system
/// calls.
use super::raw::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle};
use crate::convert::TryFrom;
-use crate::ffi::c_void;
use crate::fmt;
use crate::fs;
use crate::marker::PhantomData;
use crate::mem::forget;
-use crate::ptr::NonNull;
use crate::sys::c;
use crate::sys_common::{AsInner, FromInner, IntoInner};
///
/// This uses `repr(transparent)` and has the representation of a host handle,
/// so it can be used in FFI in places where a handle is passed as an argument,
-/// it is not captured or consumed, and it is never null.
+/// it is not captured or consumed.
///
/// Note that it *may* have the value `INVALID_HANDLE_VALUE` (-1), which is
/// sometimes a valid handle value. See [here] for the full story.
///
+/// And, it *may* have the value `NULL` (0), which can occur when consoles are
+/// detached from processes, or when `windows_subsystem` is used.
+///
/// [here]: https://devblogs.microsoft.com/oldnewthing/20040302-00/?p=40443
#[derive(Copy, Clone)]
#[repr(transparent)]
#[unstable(feature = "io_safety", issue = "87074")]
pub struct BorrowedHandle<'handle> {
- handle: NonNull<c_void>,
+ handle: RawHandle,
_phantom: PhantomData<&'handle OwnedHandle>,
}
///
/// This closes the handle on drop.
///
-/// This uses `repr(transparent)` and has the representation of a host handle,
-/// so it can be used in FFI in places where a handle is passed as a consumed
-/// argument or returned as an owned value, and is never null.
-///
/// Note that it *may* have the value `INVALID_HANDLE_VALUE` (-1), which is
-/// sometimes a valid handle value. See [here] for the full story. For APIs
-/// like `CreateFileW` which report errors with `INVALID_HANDLE_VALUE` instead
-/// of null, use [`HandleOrInvalid`] instead of `Option<OwnedHandle>`.
+/// sometimes a valid handle value. See [here] for the full story.
+///
+/// And, it *may* have the value `NULL` (0), which can occur when consoles are
+/// detached from processes, or when `windows_subsystem` is used.
///
/// `OwnedHandle` uses [`CloseHandle`] to close its handle on drop. As such,
/// it must not be used with handles to open registry keys which need to be
/// [`RegCloseKey`]: https://docs.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-regclosekey
///
/// [here]: https://devblogs.microsoft.com/oldnewthing/20040302-00/?p=40443
-#[repr(transparent)]
#[unstable(feature = "io_safety", issue = "87074")]
pub struct OwnedHandle {
- handle: NonNull<c_void>,
+ handle: RawHandle,
}
+/// FFI type for handles in return values or out parameters, where `NULL` is used
+/// as a sentry value to indicate errors, such as in the return value of `CreateThread`. This uses
+/// `repr(transparent)` and has the representation of a host handle, so that it can be used in such
+/// FFI declarations.
+///
+/// The only thing you can usefully do with a `HandleOrNull` is to convert it into an
+/// `OwnedHandle` using its [`TryFrom`] implementation; this conversion takes care of the check for
+/// `NULL`. This ensures that such FFI calls cannot start using the handle without
+/// checking for `NULL` first.
+///
+/// This type concerns any value other than `NULL` to be valid, including `INVALID_HANDLE_VALUE`.
+/// This is because APIs that use `NULL` as their sentry value don't treat `INVALID_HANDLE_VALUE`
+/// as special.
+///
+/// If this holds a valid handle, it will close the handle on drop.
+#[repr(transparent)]
+#[unstable(feature = "io_safety", issue = "87074")]
+#[derive(Debug)]
+pub struct HandleOrNull(OwnedHandle);
+
/// FFI type for handles in return values or out parameters, where `INVALID_HANDLE_VALUE` is used
/// as a sentry value to indicate errors, such as in the return value of `CreateFileW`. This uses
/// `repr(transparent)` and has the representation of a host handle, so that it can be used in such
/// `INVALID_HANDLE_VALUE`. This ensures that such FFI calls cannot start using the handle without
/// checking for `INVALID_HANDLE_VALUE` first.
///
+/// This type concerns any value other than `INVALID_HANDLE_VALUE` to be valid, including `NULL`.
+/// This is because APIs that use `INVALID_HANDLE_VALUE` as their sentry value may return `NULL`
+/// under `windows_subsystem = "windows"` or other situations where I/O devices are detached.
+///
/// If this holds a valid handle, it will close the handle on drop.
#[repr(transparent)]
#[unstable(feature = "io_safety", issue = "87074")]
#[derive(Debug)]
-pub struct HandleOrInvalid(Option<OwnedHandle>);
+pub struct HandleOrInvalid(OwnedHandle);
// The Windows [`HANDLE`] type may be transferred across and shared between
// thread boundaries (despite containing a `*mut void`, which in general isn't
//
// [`HANDLE`]: std::os::windows::raw::HANDLE
unsafe impl Send for OwnedHandle {}
+unsafe impl Send for HandleOrNull {}
unsafe impl Send for HandleOrInvalid {}
unsafe impl Send for BorrowedHandle<'_> {}
unsafe impl Sync for OwnedHandle {}
+unsafe impl Sync for HandleOrNull {}
unsafe impl Sync for HandleOrInvalid {}
unsafe impl Sync for BorrowedHandle<'_> {}
/// # Safety
///
/// The resource pointed to by `handle` must be a valid open handle, it
- /// must remain open for the duration of the returned `BorrowedHandle`, and
- /// it must not be null.
+ /// must remain open for the duration of the returned `BorrowedHandle`.
///
/// Note that it *may* have the value `INVALID_HANDLE_VALUE` (-1), which is
/// sometimes a valid handle value. See [here] for the full story.
///
+ /// And, it *may* have the value `NULL` (0), which can occur when consoles are
+ /// detached from processes, or when `windows_subsystem` is used.
+ ///
/// [here]: https://devblogs.microsoft.com/oldnewthing/20040302-00/?p=40443
#[inline]
#[unstable(feature = "io_safety", issue = "87074")]
pub unsafe fn borrow_raw_handle(handle: RawHandle) -> Self {
- assert!(!handle.is_null());
- Self { handle: NonNull::new_unchecked(handle), _phantom: PhantomData }
+ Self { handle, _phantom: PhantomData }
+ }
+}
+
+impl TryFrom<HandleOrNull> for OwnedHandle {
+ type Error = ();
+
+ #[inline]
+ fn try_from(handle_or_null: HandleOrNull) -> Result<Self, ()> {
+ let owned_handle = handle_or_null.0;
+ if owned_handle.handle.is_null() { Err(()) } else { Ok(owned_handle) }
}
}
#[inline]
fn try_from(handle_or_invalid: HandleOrInvalid) -> Result<Self, ()> {
- // In theory, we ought to be able to assume that the pointer here is
- // never null, use `OwnedHandle` rather than `Option<OwnedHandle>`, and
- // obviate the the panic path here. Unfortunately, Win32 documentation
- // doesn't explicitly guarantee this anywhere.
- //
- // APIs like [`CreateFileW`] itself have `HANDLE` arguments where a
- // null handle indicates an absent value, which wouldn't work if null
- // were a valid handle value, so it seems very unlikely that it could
- // ever return null. But who knows?
- //
- // [`CreateFileW`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew
- let owned_handle = handle_or_invalid.0.expect("A `HandleOrInvalid` was null!");
- if owned_handle.handle.as_ptr() == c::INVALID_HANDLE_VALUE {
- Err(())
- } else {
- Ok(owned_handle)
- }
+ let owned_handle = handle_or_invalid.0;
+ if owned_handle.handle == c::INVALID_HANDLE_VALUE { Err(()) } else { Ok(owned_handle) }
}
}
impl AsRawHandle for BorrowedHandle<'_> {
#[inline]
fn as_raw_handle(&self) -> RawHandle {
- self.handle.as_ptr()
+ self.handle
}
}
impl AsRawHandle for OwnedHandle {
#[inline]
fn as_raw_handle(&self) -> RawHandle {
- self.handle.as_ptr()
+ self.handle
}
}
impl IntoRawHandle for OwnedHandle {
#[inline]
fn into_raw_handle(self) -> RawHandle {
- let handle = self.handle.as_ptr();
+ let handle = self.handle;
forget(self);
handle
}
impl FromRawHandle for OwnedHandle {
/// Constructs a new instance of `Self` from the given raw handle.
///
- /// Use `HandleOrInvalid` instead of `Option<OwnedHandle>` for APIs that
- /// use `INVALID_HANDLE_VALUE` to indicate failure.
- ///
/// # Safety
///
/// The resource pointed to by `handle` must be open and suitable for
/// [here]: https://devblogs.microsoft.com/oldnewthing/20040302-00/?p=40443
#[inline]
unsafe fn from_raw_handle(handle: RawHandle) -> Self {
- assert!(!handle.is_null());
- Self { handle: NonNull::new_unchecked(handle) }
+ Self { handle }
+ }
+}
+
+impl FromRawHandle for HandleOrNull {
+ /// Constructs a new instance of `Self` from the given `RawHandle` returned
+ /// from a Windows API that uses null to indicate failure, such as
+ /// `CreateThread`.
+ ///
+ /// Use `HandleOrInvalid` instead of `HandleOrNull` for APIs that
+ /// use `INVALID_HANDLE_VALUE` to indicate failure.
+ ///
+ /// # Safety
+ ///
+ /// The resource pointed to by `handle` must be either open and otherwise
+ /// unowned, or null. Note that not all Windows APIs use null for errors;
+ /// see [here] for the full story.
+ ///
+ /// [here]: https://devblogs.microsoft.com/oldnewthing/20040302-00/?p=40443
+ #[inline]
+ unsafe fn from_raw_handle(handle: RawHandle) -> Self {
+ Self(OwnedHandle::from_raw_handle(handle))
}
}
/// from a Windows API that uses `INVALID_HANDLE_VALUE` to indicate
/// failure, such as `CreateFileW`.
///
- /// Use `Option<OwnedHandle>` instead of `HandleOrInvalid` for APIs that
+ /// Use `HandleOrNull` instead of `HandleOrInvalid` for APIs that
/// use null to indicate failure.
///
/// # Safety
///
/// The resource pointed to by `handle` must be either open and otherwise
- /// unowned, or equal to `INVALID_HANDLE_VALUE` (-1). It must not be null.
- /// Note that not all Windows APIs use `INVALID_HANDLE_VALUE` for errors;
- /// see [here] for the full story.
+ /// unowned, null, or equal to `INVALID_HANDLE_VALUE` (-1). Note that not
+ /// all Windows APIs use `INVALID_HANDLE_VALUE` for errors; see [here] for
+ /// the full story.
///
/// [here]: https://devblogs.microsoft.com/oldnewthing/20040302-00/?p=40443
#[inline]
unsafe fn from_raw_handle(handle: RawHandle) -> Self {
- // We require non-null here to catch errors earlier.
- Self(Some(OwnedHandle::from_raw_handle(handle)))
+ Self(OwnedHandle::from_raw_handle(handle))
}
}
#[inline]
fn drop(&mut self) {
unsafe {
- let _ = c::CloseHandle(self.handle.as_ptr());
+ let _ = c::CloseHandle(self.handle);
}
}
}
impl<'a> cmp::PartialEq for Components<'a> {
#[inline]
fn eq(&self, other: &Components<'a>) -> bool {
+ let Components { path: _, front: _, back: _, has_physical_root: _, prefix: _ } = self;
+
+ // Fast path for exact matches, e.g. for hashmap lookups.
+ // Don't explicitly compare the prefix or has_physical_root fields since they'll
+ // either be covered by the `path` buffer or are only relevant for `prefix_verbatim()`.
+ if self.path.len() == other.path.len()
+ && self.front == other.front
+ && self.back == State::Body
+ && other.back == State::Body
+ && self.prefix_verbatim() == other.prefix_verbatim()
+ {
+ // possible future improvement: this could bail out earlier if there were a
+ // reverse memcmp/bcmp comparing back to front
+ if self.path == other.path {
+ return true;
+ }
+ }
+
+ // compare back to front since absolute paths often share long prefixes
Iterator::eq(self.clone().rev(), other.clone().rev())
}
}
// The fast path isn't taken for paths with a PrefixComponent to avoid backtracking into
// the middle of one
if left.prefix.is_none() && right.prefix.is_none() && left.front == right.front {
- // this might benefit from a [u8]::first_mismatch simd implementation, if it existed
- let first_difference =
- match left.path.iter().zip(right.path.iter()).position(|(&a, &b)| a != b) {
- None if left.path.len() == right.path.len() => return cmp::Ordering::Equal,
- None => left.path.len().min(right.path.len()),
- Some(diff) => diff,
- };
+ // possible future improvement: a [u8]::first_mismatch simd implementation
+ let first_difference = match left.path.iter().zip(right.path).position(|(&a, &b)| a != b) {
+ None if left.path.len() == right.path.len() => return cmp::Ordering::Equal,
+ None => left.path.len().min(right.path.len()),
+ Some(diff) => diff,
+ };
if let Some(previous_sep) =
left.path[..first_difference].iter().rposition(|&b| left.is_sep_byte(b))
#[stable(feature = "rust1", since = "1.0.0")]
impl Hash for Path {
fn hash<H: Hasher>(&self, h: &mut H) {
- for component in self.components() {
- component.hash(h);
+ let bytes = self.as_u8_slice();
+ let prefix_len = match parse_prefix(&self.inner) {
+ Some(prefix) => {
+ prefix.hash(h);
+ prefix.len()
+ }
+ None => 0,
+ };
+ let bytes = &bytes[prefix_len..];
+
+ let mut component_start = 0;
+ let mut bytes_hashed = 0;
+
+ for i in 0..bytes.len() {
+ if is_sep_byte(bytes[i]) {
+ if i > component_start {
+ let to_hash = &bytes[component_start..i];
+ h.write(to_hash);
+ bytes_hashed += to_hash.len();
+ }
+
+ // skip over separator and optionally a following CurDir item
+ // since components() would normalize these away
+ component_start = i + match bytes[i..] {
+ [_, b'.', b'/', ..] | [_, b'.'] => 2,
+ _ => 1,
+ };
+ }
+ }
+
+ if component_start < bytes.len() {
+ let to_hash = &bytes[component_start..];
+ h.write(to_hash);
+ bytes_hashed += to_hash.len();
}
+
+ h.write_usize(bytes_hashed);
}
}
use super::*;
-use crate::collections::BTreeSet;
+use crate::collections::hash_map::DefaultHasher;
+use crate::collections::{BTreeSet, HashSet};
+use crate::hash::Hasher;
use crate::rc::Rc;
use crate::sync::Arc;
use core::hint::black_box;
fn test_ord() {
macro_rules! ord(
($ord:ident, $left:expr, $right:expr) => ( {
- assert_eq!(Path::new($left).cmp(&Path::new($right)), core::cmp::Ordering::$ord);
+ use core::cmp::Ordering;
+
+ let left = Path::new($left);
+ let right = Path::new($right);
+ assert_eq!(left.cmp(&right), Ordering::$ord);
+ if (core::cmp::Ordering::$ord == Ordering::Equal) {
+ assert_eq!(left, right);
+
+ let mut hasher = DefaultHasher::new();
+ left.hash(&mut hasher);
+ let left_hash = hasher.finish();
+ hasher = DefaultHasher::new();
+ right.hash(&mut hasher);
+ let right_hash = hasher.finish();
+
+ assert_eq!(left_hash, right_hash, "hashes for {:?} and {:?} must match", left, right);
+ } else {
+ assert_ne!(left, right);
+ }
});
);
set.insert(paths[500].as_path());
});
}
+
+#[bench]
+fn bench_path_hashset(b: &mut test::Bencher) {
+ let prefix = "/my/home/is/my/castle/and/my/castle/has/a/rusty/workbench/";
+ let paths: Vec<_> =
+ (0..1000).map(|num| PathBuf::from(prefix).join(format!("file {}.rs", num))).collect();
+
+ let mut set = HashSet::new();
+
+ paths.iter().for_each(|p| {
+ set.insert(p.as_path());
+ });
+
+ b.iter(|| {
+ set.remove(paths[500].as_path());
+ set.insert(black_box(paths[500].as_path()))
+ });
+}
+
+#[bench]
+fn bench_path_hashset_miss(b: &mut test::Bencher) {
+ let prefix = "/my/home/is/my/castle/and/my/castle/has/a/rusty/workbench/";
+ let paths: Vec<_> =
+ (0..1000).map(|num| PathBuf::from(prefix).join(format!("file {}.rs", num))).collect();
+
+ let mut set = HashSet::new();
+
+ paths.iter().for_each(|p| {
+ set.insert(p.as_path());
+ });
+
+ let probe = PathBuf::from(prefix).join("other");
+
+ b.iter(|| set.remove(black_box(probe.as_path())));
+}
+
+#[bench]
+fn bench_hash_path_short(b: &mut test::Bencher) {
+ let mut hasher = DefaultHasher::new();
+ let path = Path::new("explorer.exe");
+
+ b.iter(|| black_box(path).hash(&mut hasher));
+
+ black_box(hasher.finish());
+}
+
+#[bench]
+fn bench_hash_path_long(b: &mut test::Bencher) {
+ let mut hasher = DefaultHasher::new();
+ let path =
+ Path::new("/aaaaa/aaaaaa/./../aaaaaaaa/bbbbbbbbbbbbb/ccccccccccc/ddddddddd/eeeeeee.fff");
+
+ b.iter(|| black_box(path).hash(&mut hasher));
+
+ black_box(hasher.finish());
+}
///
/// [`status`]: Command::status
/// [`wait`]: Child::wait
+//
+// We speak slightly loosely (here and in various other places in the stdlib docs) about `exit`
+// vs `_exit`. Naming of Unix system calls is not standardised across Unices, so terminology is a
+// matter of convention and tradition. For clarity we usually speak of `exit`, even when we might
+// mean an underlying system call such as `_exit`.
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
#[stable(feature = "process", since = "1.0.0")]
pub struct ExitStatus(imp::ExitStatus);
pub mod sockets;
pub use self::fs::*;
-pub const SOLID_BP_PROGRAM_EXITED: usize = 15;
-pub const SOLID_BP_CSABORT: usize = 16;
-
#[inline(always)]
pub fn breakpoint_program_exited(tid: usize) {
unsafe {
match () {
+ // SOLID_BP_PROGRAM_EXITED = 15
#[cfg(target_arch = "arm")]
- () => asm!("bkpt #{}", const SOLID_BP_PROGRAM_EXITED, in("r0") tid),
+ () => asm!("bkpt #15", in("r0") tid),
#[cfg(target_arch = "aarch64")]
- () => asm!("hlt #{}", const SOLID_BP_PROGRAM_EXITED, in("x0") tid),
+ () => asm!("hlt #15", in("x0") tid),
}
}
}
pub fn breakpoint_abort() {
unsafe {
match () {
+ // SOLID_BP_CSABORT = 16
#[cfg(target_arch = "arm")]
- () => asm!("bkpt #{}", const SOLID_BP_CSABORT),
+ () => asm!("bkpt #16"),
#[cfg(target_arch = "aarch64")]
- () => asm!("hlt #{}", const SOLID_BP_CSABORT),
+ () => asm!("hlt #16"),
}
}
}
b == b'/'
}
+#[inline]
pub fn parse_prefix(_: &OsStr) -> Option<Prefix<'_>> {
None
}
fn clone3(cl_args: *mut clone_args, len: libc::size_t) -> libc::c_long
}
+ // Bypassing libc for `clone3` can make further libc calls unsafe,
+ // so we use it sparingly for now. See #89522 for details.
+ // Some tools (e.g. sandboxing tools) may also expect `fork`
+ // rather than `clone3`.
+ let want_clone3_pidfd = self.get_create_pidfd();
+
// If we fail to create a pidfd for any reason, this will
// stay as -1, which indicates an error.
let mut pidfd: pid_t = -1;
// Attempt to use the `clone3` syscall, which supports more arguments
// (in particular, the ability to create a pidfd). If this fails,
// we will fall through this block to a call to `fork()`
- if HAS_CLONE3.load(Ordering::Relaxed) {
- let mut flags = 0;
- if self.get_create_pidfd() {
- flags |= CLONE_PIDFD;
- }
-
+ if want_clone3_pidfd && HAS_CLONE3.load(Ordering::Relaxed) {
let mut args = clone_args {
- flags,
+ flags: CLONE_PIDFD,
pidfd: &mut pidfd as *mut pid_t as u64,
child_tid: 0,
parent_tid: 0,
}
}
- // If we get here, the 'clone3' syscall does not exist
- // or we do not have permission to call it
+ // Generally, we just call `fork`. If we get here after wanting `clone3`,
+ // then the syscall does not exist or we do not have permission to call it.
cvt(libc::fork()).map(|res| (res, pidfd))
}
}
/// Unix exit statuses
+//
+// This is not actually an "exit status" in Unix terminology. Rather, it is a "wait status".
+// See the discussion in comments and doc comments for `std::process::ExitStatus`.
#[derive(PartialEq, Eq, Clone, Copy)]
pub struct ExitStatus(c_int);
# do not format submodules
"library/backtrace",
+ "library/portable-simd",
"library/stdarch",
"compiler/rustc_codegen_cranelift",
"compiler/rustc_codegen_gcc",
- `llvm-libunwind` now accepts `in-tree` (formerly true), `system` or `no` (formerly false) [#77703](https://github.com/rust-lang/rust/pull/77703)
- The options `infodir`, `localstatedir`, and `gpg-password-file` are no longer allowed in config.toml. Previously, they were ignored without warning. Note that `infodir` and `localstatedir` are still accepted by `./configure`, with a warning. [#82451](https://github.com/rust-lang/rust/pull/82451)
- Add options for enabling overflow checks, one for std (`overflow-checks-std`) and one for everything else (`overflow-checks`). Both default to false.
+- Change the names for `dist` commmands to match the component they generate. [#90684](https://github.com/rust-lang/rust/pull/90684)
### Non-breaking changes
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
let default = run.builder.config.docs;
- run.path("src/doc").default_condition(default)
+ run.path("rust-docs").default_condition(default)
}
fn make_run(run: RunConfig<'_>) {
const DEFAULT: bool = true;
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
- run.path("src/librustc")
+ let builder = run.builder;
+ run.path("rustc-docs").default_condition(builder.config.compiler_docs)
}
fn make_run(run: RunConfig<'_>) {
/// Builds the `rustc-docs` installer component.
fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> {
let host = self.host;
- if !builder.config.compiler_docs {
- return None;
- }
builder.default_doc(&[]);
let mut tarball = Tarball::new(builder, "rustc-docs", &host.triple);
const DEFAULT: bool = true;
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
- run.never()
+ run.path("rust-mingw")
}
fn make_run(run: RunConfig<'_>) {
const ONLY_HOSTS: bool = true;
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
- run.path("src/librustc")
+ run.path("rustc")
}
fn make_run(run: RunConfig<'_>) {
const DEFAULT: bool = true;
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
- run.path("library/std")
+ run.path("rust-std")
}
fn make_run(run: RunConfig<'_>) {
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
let default = should_build_extended_tool(&run.builder, "analysis");
- run.path("analysis").default_condition(default)
+ run.path("rust-analysis").default_condition(default)
}
fn make_run(run: RunConfig<'_>) {
const ONLY_HOSTS: bool = true;
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
- run.path("src")
+ run.path("rust-src")
}
fn make_run(run: RunConfig<'_>) {
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
let builder = run.builder;
- run.path("src").default_condition(builder.config.rust_dist_src)
+ run.path("rustc-src").default_condition(builder.config.rust_dist_src)
}
fn make_run(run: RunConfig<'_>) {
const ONLY_HOSTS: bool = true;
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
- run.path("src/tools/build-manifest")
+ run.path("build-manifest")
}
fn make_run(run: RunConfig<'_>) {
const ONLY_HOSTS: bool = true;
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
- run.path("reproducible")
+ run.path("reproducible-artifacts")
}
fn make_run(run: RunConfig<'_>) {
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
let builder = run.builder;
- run.krate("rustc-main").path("compiler").default_condition(builder.config.docs)
+ run.krate("rustc-main").path("compiler").default_condition(builder.config.compiler_docs)
}
fn make_run(run: RunConfig<'_>) {
})
.collect::<Vec<_>>();
- if !builder.config.compiler_docs && !builder.was_invoked_explicitly::<Self>() {
- builder.info("\tskipping - compiler/librustdoc docs disabled");
- return;
- }
-
// This is the intended out directory for compiler documentation.
let out = builder.compiler_doc_out(target);
t!(fs::create_dir_all(&out));
const ONLY_HOSTS: bool = true;
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
- run.krate($should_run)
+ let builder = run.builder;
+ run.krate($should_run).default_condition(builder.config.compiler_docs)
}
fn make_run(run: RunConfig<'_>) {
let compiler = builder.compiler(stage, builder.config.build);
- if !builder.config.compiler_docs && !builder.was_invoked_explicitly::<Self>() {
- builder.info("\tskipping - compiler/tool docs disabled");
- return;
- }
-
// Build rustc docs so that we generate relative links.
builder.ensure(Rustc { stage, target });
const ONLY_HOSTS: bool = true;
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
- run.path("src/test/rustdoc-js-std")
+ run.suite_path("src/test/rustdoc-js-std")
}
fn make_run(run: RunConfig<'_>) {
.arg(builder.doc_out(self.target))
.arg("--test-folder")
.arg(builder.src.join("src/test/rustdoc-js-std"));
+ for path in &builder.paths {
+ if let Some(p) =
+ util::is_valid_test_suite_arg(path, "src/test/rustdoc-js-std", builder)
+ {
+ if !p.ends_with(".js") {
+ eprintln!("A non-js file was given: `{}`", path.display());
+ panic!("Cannot run rustdoc-js-std tests");
+ }
+ command.arg("--test-file").arg(path);
+ }
+ }
builder.ensure(crate::doc::Std { target: self.target, stage: builder.top_stage });
builder.run(&mut command);
} else {
const ONLY_HOSTS: bool = true;
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
- run.path("src/test/rustdoc-js")
+ run.suite_path("src/test/rustdoc-js")
}
fn make_run(run: RunConfig<'_>) {
.arg("--tests-folder")
.arg(builder.build.src.join("src/test/rustdoc-gui"));
for path in &builder.paths {
- if let Some(name) = path.file_name().and_then(|f| f.to_str()) {
- if name.ends_with(".goml") {
+ if let Some(p) = util::is_valid_test_suite_arg(path, "src/test/rustdoc-gui", builder) {
+ if !p.ends_with(".goml") {
+ eprintln!("A non-goml file was given: `{}`", path.display());
+ panic!("Cannot run rustdoc-gui tests");
+ }
+ if let Some(name) = path.file_name().and_then(|f| f.to_str()) {
command.arg("--file").arg(name);
}
}
// Get test-args by striping suite path
let mut test_args: Vec<&str> = paths
.iter()
- .map(|p| match p.strip_prefix(".") {
- Ok(path) => path,
- Err(_) => p,
- })
- .filter(|p| p.starts_with(suite_path))
- .filter(|p| {
- let exists = p.is_dir() || p.is_file();
- if !exists {
- if let Some(p) = p.to_str() {
- builder.info(&format!(
- "Warning: Skipping \"{}\": not a regular file or directory",
- p
- ));
- }
- }
- exists
- })
- .filter_map(|p| {
- // Since test suite paths are themselves directories, if we don't
- // specify a directory or file, we'll get an empty string here
- // (the result of the test suite directory without its suite prefix).
- // Therefore, we need to filter these out, as only the first --test-args
- // flag is respected, so providing an empty --test-args conflicts with
- // any following it.
- match p.strip_prefix(suite_path).ok().and_then(|p| p.to_str()) {
- Some(s) if !s.is_empty() => Some(s),
- _ => None,
- }
- })
+ .filter_map(|p| util::is_valid_test_suite_arg(p, suite_path, builder))
.collect();
test_args.append(&mut builder.config.cmd.test_args());
$name:ident, $path:expr, $tool_name:expr
$(,is_external_tool = $external:expr)*
$(,is_unstable_tool = $unstable:expr)*
- $(,features = $features:expr)*
;
)+) => {
#[derive(Copy, PartialEq, Eq, Clone)]
} else {
SourceType::InTree
},
- extra_features: {
- // FIXME(#60643): avoid this lint by using `_`
- let mut _tmp = Vec::new();
- $(_tmp.extend($features);)*
- _tmp
- },
+ extra_features: vec![],
}).expect("expected to build -- essential tool")
}
}
|| target.contains("fuchsia")
|| target.contains("bpf"))
}
+
+pub fn is_valid_test_suite_arg<'a, P: AsRef<Path>>(
+ path: &'a Path,
+ suite_path: P,
+ builder: &Builder<'_>,
+) -> Option<&'a str> {
+ let suite_path = suite_path.as_ref();
+ let path = match path.strip_prefix(".") {
+ Ok(p) => p,
+ Err(_) => path,
+ };
+ if !path.starts_with(suite_path) {
+ return None;
+ }
+ let exists = path.is_dir() || path.is_file();
+ if !exists {
+ if let Some(p) = path.to_str() {
+ builder.info(&format!("Warning: Skipping \"{}\": not a regular file or directory", p));
+ }
+ return None;
+ }
+ // Since test suite paths are themselves directories, if we don't
+ // specify a directory or file, we'll get an empty string here
+ // (the result of the test suite directory without its suite prefix).
+ // Therefore, we need to filter these out, as only the first --test-args
+ // flag is respected, so providing an empty --test-args conflicts with
+ // any following it.
+ match path.strip_prefix(suite_path).ok().and_then(|p| p.to_str()) {
+ Some(s) if !s.is_empty() => Some(s),
+ _ => None,
+ }
+}
ENV SCRIPT ../src/ci/pgo.sh python3 ../x.py dist \
--host $HOSTS --target $HOSTS \
--include-default-paths \
- src/tools/build-manifest
+ build-manifest
ENV CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_LINKER=clang
# This is the only builder which will create source tarballs
# tier 2 targets produced by this builder.
- name: dist-x86_64-apple
env:
- SCRIPT: ./x.py dist --exclude src/doc --exclude extended && ./x.py dist --target=x86_64-apple-darwin src/doc && ./x.py dist extended
+ SCRIPT: ./x.py dist --exclude rust-docs --exclude extended && ./x.py dist --target=x86_64-apple-darwin rust-docs && ./x.py dist extended
RUST_CONFIGURE_ARGS: --host=x86_64-apple-darwin --target=x86_64-apple-darwin,aarch64-apple-ios,x86_64-apple-ios,aarch64-apple-ios-sim --enable-full-tools --enable-sanitizers --enable-profiler --set rust.jemalloc --set llvm.ninja=false
RUSTC_RETRY_LINKER_ON_SEGFAULT: 1
MACOSX_DEPLOYMENT_TARGET: 10.7
-Subproject commit fd9299792852c9a368cb236748781852f75cdac6
+Subproject commit 5c5dbc5b196c9564422b3193264f3288d2a051ce
-Subproject commit 7c0088ca744d293a5f4b1e2ac378e7c23d30fe55
+Subproject commit 27f4a84d3852e9416cae5861254fa53a825c56bd
-Subproject commit 358e6a61d5f4f0496d0a81e70cdcd25d05307342
+Subproject commit c6b4bf831e9a40aec34f53067d20634839a6778b
-Subproject commit 27f1ff5e440ef78828b68ab882b98e1b10d9af32
+Subproject commit e9d45342d7a6c1def4731f1782d87ea317ba30c3
-Subproject commit b06008731af0f7d07cd0614e820c8276dfed1c18
+Subproject commit 196ef69aa68f2cef44f37566ee7db37daf00301b
--- /dev/null
+# `temps-dir`
+
+--------------------
+
+The `-Ztemps-dir` compiler flag specifies the directory to write the
+intermediate files in. If not set, the output directory is used. This option is
+useful if you are running more than one instance of `rustc` (e.g. with different
+`--crate-type` settings), and you need to make sure they are not overwriting
+each other's intermediate files. No files are kept unless `-C save-temps=yes` is
+also set.
--- /dev/null
+# `type_changing_struct_update`
+
+The tracking issue for this feature is: [#86555]
+
+[#86555]: https://github.com/rust-lang/rust/issues/86555
+
+------------------------
+
+This implements [RFC2528]. When turned on, you can create instances of the same struct
+that have different generic type or lifetime parameters.
+
+[RFC2528]: https://github.com/rust-lang/rfcs/blob/master/text/2528-type-changing-struct-update-syntax.md
+
+```rust
+#![allow(unused_variables, dead_code)]
+#![feature(type_changing_struct_update)]
+
+fn main () {
+ struct Foo<T, U> {
+ field1: T,
+ field2: U,
+ }
+
+ let base: Foo<String, i32> = Foo {
+ field1: String::from("hello"),
+ field2: 1234,
+ };
+ let updated: Foo<f64, i32> = Foo {
+ field1: 3.14,
+ ..base
+ };
+}
+```
Note that the `fn` or `static` item does not need to be public or `#[no_mangle]`: the compiler will automatically insert the appropriate mangled symbol name into the assembly code.
-By default, `asm!` assumes that any register not specified as an output will have its contents preserved by the assembly code. The [`clobber_abi`](#abi-clobbers) argument to `asm!` tells the compiler to automatically insert the necessary clobber operands according to the given calling convention ABI: any register which is not fully preserved in that ABI will be treated as clobbered.
+By default, `asm!` assumes that any register not specified as an output will have its contents preserved by the assembly code. The [`clobber_abi`](#abi-clobbers) argument to `asm!` tells the compiler to automatically insert the necessary clobber operands according to the given calling convention ABI: any register which is not fully preserved in that ABI will be treated as clobbered. Multiple `clobber_abi` arguments may be provided and all clobbers from all specified ABIs will be inserted.
## Register template modifiers
operand_expr := expr / "_" / expr "=>" expr / expr "=>" "_"
reg_operand := dir_spec "(" reg_spec ")" operand_expr
operand := reg_operand / "const" const_expr / "sym" path
-clobber_abi := "clobber_abi(" <abi> ")"
+clobber_abi := "clobber_abi(" <abi> *["," <abi>] [","] ")"
option := "pure" / "nomem" / "readonly" / "preserves_flags" / "noreturn" / "nostack" / "att_syntax" / "raw"
options := "options(" option *["," option] [","] ")"
-asm := "asm!(" format_string *("," format_string) *("," [ident "="] operand) ["," clobber_abi] *("," options) [","] ")"
+asm := "asm!(" format_string *("," format_string) *("," [ident "="] operand) *("," clobber_abi) *("," options) [","] ")"
```
Inline assembly is currently supported on the following architectures:
| AArch64 | `vreg` | `v[0-31]` | `w` |
| AArch64 | `vreg_low16` | `v[0-15]` | `x` |
| AArch64 | `preg` | `p[0-15]`, `ffr` | Only clobbers |
-| ARM | `reg` | `r[0-12]`, `r14` | `r` |
-| ARM (Thumb) | `reg_thumb` | `r[0-r7]` | `l` |
+| ARM (ARM) | `reg` | `r[0-12]`, `r14` | `r` |
+| ARM (Thumb2) | `reg` | `r[0-12]`, `r14` | `r` |
+| ARM (Thumb1) | `reg` | `r[0-7]` | `r` |
| ARM (ARM) | `reg_thumb` | `r[0-r12]`, `r14` | `l` |
+| ARM (Thumb2) | `reg_thumb` | `r[0-7]` | `l` |
+| ARM (Thumb1) | `reg_thumb` | `r[0-7]` | `l` |
| ARM | `sreg` | `s[0-31]` | `t` |
| ARM | `sreg_low16` | `s[0-15]` | `x` |
| ARM | `dreg` | `d[0-31]` | `w` |
The `clobber_abi` keyword can be used to apply a default set of clobbers to an `asm` block. This will automatically insert the necessary clobber constraints as needed for calling a function with a particular calling convention: if the calling convention does not fully preserve the value of a register across a call then a `lateout("reg") _` is implicitly added to the operands list.
+`clobber_abi` may be specified any number of times. It will insert a clobber for all unique registers in the union of all specified calling conventions.
+
Generic register class outputs are disallowed by the compiler when `clobber_abi` is used: all outputs must specify an explicit register. Explicit register outputs have precedence over the implicit clobbers inserted by `clobber_abi`: a clobber will only be inserted for a register if that register is not used as an output.
The following ABIs can be used with `clobber_abi`:
+++ /dev/null
-# `format_args_capture`
-
-The tracking issue for this feature is: [#67984]
-
-[#67984]: https://github.com/rust-lang/rust/issues/67984
-
-------------------------
-
-Enables `format_args!` (and macros which use `format_args!` in their implementation, such
-as `format!`, `print!` and `panic!`) to capture variables from the surrounding scope.
-This avoids the need to pass named parameters when the binding in question
-already exists in scope.
-
-```rust
-#![feature(format_args_capture)]
-
-let (person, species, name) = ("Charlie Brown", "dog", "Snoopy");
-
-// captures named argument `person`
-print!("Hello {person}");
-
-// captures named arguments `species` and `name`
-format!("The {species}'s name is {name}.");
-```
-
-This also works for formatting parameters such as width and precision:
-
-```rust
-#![feature(format_args_capture)]
-
-let precision = 2;
-let s = format!("{:.precision$}", 1.324223);
-
-assert_eq!(&s, "1.32");
-```
-
-A non-exhaustive list of macros which benefit from this functionality include:
-- `format!`
-- `print!` and `println!`
-- `eprint!` and `eprintln!`
-- `write!` and `writeln!`
-- `panic!`
-- `unreachable!`
-- `unimplemented!`
-- `todo!`
-- `assert!` and similar
-- macros in many thirdparty crates, such as `log`
tcx.associated_items(did)
.in_definition_order()
.filter_map(|item| {
- if associated_trait.is_some() || item.vis == ty::Visibility::Public {
+ if associated_trait.is_some() || item.vis.is_public() {
Some(item.clean(cx))
} else {
None
// two namespaces, so the target may be listed twice. Make sure we only
// visit each node at most once.
for &item in cx.tcx.item_children(did).iter() {
- if item.vis == ty::Visibility::Public {
+ if item.vis.is_public() {
let res = item.res.expect_non_local();
if let Some(def_id) = res.mod_def_id() {
if did == def_id || !visited.insert(def_id) {
use std::{mem, vec};
use crate::core::{self, DocContext, ImplTraitParam};
-use crate::doctree;
use crate::formats::item_type::ItemType;
+use crate::visit_ast::Module as DocModule;
use utils::*;
fn clean(&self, cx: &mut DocContext<'_>) -> T;
}
-impl Clean<Item> for doctree::Module<'_> {
+impl Clean<Item> for DocModule<'_> {
fn clean(&self, cx: &mut DocContext<'_>) -> Item {
let mut items: Vec<Item> = vec![];
items.extend(self.foreigns.iter().map(|x| x.clean(cx)));
// this is the ID of the crate itself
let crate_def_id = DefId { krate: cnum, index: CRATE_DEF_INDEX };
let attrs = cx.tcx.hir().attrs(krate.hir_id());
- let please_inline = krate.vis.node.is_pub()
+ let please_inline = cx.tcx.visibility(krate.def_id).is_public()
&& attrs.iter().any(|a| {
a.has_name(sym::doc)
&& match a.meta_item_list() {
return Vec::new();
}
+ let visibility = cx.tcx.visibility(import.def_id);
let attrs = cx.tcx.hir().attrs(import.hir_id());
let inline_attr = attrs.lists(sym::doc).get_word_attr(sym::inline);
- let pub_underscore = import.vis.node.is_pub() && name == kw::Underscore;
+ let pub_underscore = visibility.is_public() && name == kw::Underscore;
+ let current_mod = cx.tcx.parent_module_from_def_id(import.def_id);
+ let parent_mod = cx.tcx.parent_module_from_def_id(current_mod);
if pub_underscore {
if let Some(ref inline) = inline_attr {
// forcefully don't inline if this is not public or if the
// #[doc(no_inline)] attribute is present.
// Don't inline doc(hidden) imports so they can be stripped at a later stage.
- let mut denied = !(import.vis.node.is_pub()
- || (cx.render_options.document_private && import.vis.node.is_pub_restricted()))
+ let mut denied = !(visibility.is_public()
+ || (cx.render_options.document_private
+ && visibility.is_accessible_from(parent_mod.to_def_id(), cx.tcx)))
|| pub_underscore
|| attrs.iter().any(|a| {
a.has_name(sym::doc)
as_keyword(Res::Def(DefKind::Mod, id.def_id.to_def_id()))
}
hir::ItemKind::Use(path, hir::UseKind::Single)
- if item.vis.node.is_pub() =>
+ if tcx.visibility(id.def_id).is_public() =>
{
as_keyword(path.res.expect_non_local())
.map(|(_, prim)| (id.def_id.to_def_id(), prim))
as_primitive(Res::Def(DefKind::Mod, id.def_id.to_def_id()))
}
hir::ItemKind::Use(path, hir::UseKind::Single)
- if item.vis.node.is_pub() =>
+ if tcx.visibility(id.def_id).is_public() =>
{
as_primitive(path.res.expect_non_local()).map(|(_, prim)| {
// Pretend the primitive is local.
| Res::NonMacroAttr(_)
| Res::Err => return res.def_id(),
Res::Def(
- TyParam | ConstParam | Ctor(..) | ExternCrate | Use | ForeignMod | AnonConst | OpaqueTy
- | Field | LifetimeParam | GlobalAsm | Impl | Closure | Generator,
+ TyParam | ConstParam | Ctor(..) | ExternCrate | Use | ForeignMod | AnonConst
+ | InlineConst | OpaqueTy | Field | LifetimeParam | GlobalAsm | Impl | Closure
+ | Generator,
id,
) => return id,
};
use rustc_middle::middle::privacy::AccessLevels;
use rustc_middle::ty::{ParamEnv, Ty, TyCtxt};
use rustc_resolve as resolve;
+use rustc_resolve::Namespace::TypeNS;
use rustc_session::config::{self, CrateType, ErrorOutputType};
use rustc_session::lint;
use rustc_session::DiagnosticOutput;
use rustc_session::Session;
+use rustc_span::def_id::CRATE_DEF_INDEX;
use rustc_span::source_map;
use rustc_span::symbol::sym;
-use rustc_span::Span;
+use rustc_span::{Span, DUMMY_SP};
use std::cell::RefCell;
use std::lazy::SyncLazy;
// Closures' tables come from their outermost function,
// as they are part of the same "inference environment".
// This avoids emitting errors for the parent twice (see similar code in `typeck_with_fallback`)
- let outer_def_id = tcx.closure_base_def_id(def_id.to_def_id()).expect_local();
- if outer_def_id != def_id {
- return tcx.typeck(outer_def_id);
+ let typeck_root_def_id = tcx.typeck_root_def_id(def_id.to_def_id()).expect_local();
+ if typeck_root_def_id != def_id {
+ return tcx.typeck(typeck_root_def_id);
}
let hir = tcx.hir();
}
crate fn create_resolver<'a>(
+ externs: config::Externs,
queries: &Queries<'a>,
sess: &Session,
) -> Rc<RefCell<interface::BoxedResolver>> {
let (krate, resolver, _) = &*abort_on_err(queries.expansion(), sess).peek();
let resolver = resolver.clone();
- crate::passes::collect_intra_doc_links::load_intra_link_crates(resolver, krate)
+ let resolver = crate::passes::collect_intra_doc_links::load_intra_link_crates(resolver, krate);
+
+ // FIXME: somehow rustdoc is still missing crates even though we loaded all
+ // the known necessary crates. Load them all unconditionally until we find a way to fix this.
+ // DO NOT REMOVE THIS without first testing on the reproducer in
+ // https://github.com/jyn514/objr/commit/edcee7b8124abf0e4c63873e8422ff81beb11ebb
+ let extern_names: Vec<String> = externs
+ .iter()
+ .filter(|(_, entry)| entry.add_prelude)
+ .map(|(name, _)| name)
+ .cloned()
+ .collect();
+ resolver.borrow_mut().access(|resolver| {
+ sess.time("load_extern_crates", || {
+ for extern_name in &extern_names {
+ debug!("loading extern crate {}", extern_name);
+ if let Err(()) = resolver
+ .resolve_str_path_error(
+ DUMMY_SP,
+ extern_name,
+ TypeNS,
+ LocalDefId { local_def_index: CRATE_DEF_INDEX }.to_def_id(),
+ ) {
+ warn!("unable to resolve external crate {} (do you have an unused `--extern` crate?)", extern_name)
+ }
+ }
+ });
+ });
+
+ resolver
}
crate fn run_global_ctxt(
+++ /dev/null
-//! This module is used to store stuff from Rust's AST in a more convenient
-//! manner (and with prettier names) before cleaning.
-use rustc_middle::ty::TyCtxt;
-use rustc_span::{self, Span, Symbol};
-
-use rustc_hir as hir;
-
-#[derive(Debug)]
-crate struct Module<'hir> {
- crate name: Symbol,
- crate where_inner: Span,
- crate mods: Vec<Module<'hir>>,
- crate id: hir::HirId,
- // (item, renamed)
- crate items: Vec<(&'hir hir::Item<'hir>, Option<Symbol>)>,
- crate foreigns: Vec<(&'hir hir::ForeignItem<'hir>, Option<Symbol>)>,
-}
-
-impl Module<'hir> {
- crate fn new(name: Symbol, id: hir::HirId, where_inner: Span) -> Module<'hir> {
- Module { name, id, where_inner, mods: Vec::new(), items: Vec::new(), foreigns: Vec::new() }
- }
-
- crate fn where_outer(&self, tcx: TyCtxt<'_>) -> Span {
- tcx.hir().span(self.id)
- }
-}
desc,
parent,
parent_idx: None,
- search_type: get_index_search_type(&item, self.tcx),
+ search_type: get_index_search_type(&item, self.tcx, self.cache),
aliases: item.attrs.get_doc_aliases(),
});
}
| DefKind::Use
| DefKind::ForeignMod
| DefKind::AnonConst
+ | DefKind::InlineConst
| DefKind::OpaqueTy
| DefKind::Field
| DefKind::LifetimeParam
/// Builds the search index from the collected metadata
crate fn build_index<'tcx>(krate: &clean::Crate, cache: &mut Cache, tcx: TyCtxt<'tcx>) -> String {
let mut defid_to_pathid = FxHashMap::default();
- let mut crate_items = Vec::with_capacity(cache.search_index.len());
let mut crate_paths = vec![];
// Attach all orphan items to the type's definition if the type
desc,
parent: Some(did),
parent_idx: None,
- search_type: get_index_search_type(item, tcx),
+ search_type: get_index_search_type(item, tcx, cache),
aliases: item.attrs.get_doc_aliases(),
});
}
// Reduce `DefId` in paths into smaller sequential numbers,
// and prune the paths that do not appear in the index.
- let mut lastpath = String::new();
+ let mut lastpath = "";
let mut lastpathid = 0usize;
- for item in search_index {
- item.parent_idx = item.parent.and_then(|defid| match defid_to_pathid.entry(defid) {
- Entry::Occupied(entry) => Some(*entry.get()),
- Entry::Vacant(entry) => {
- let pathid = lastpathid;
- entry.insert(pathid);
- lastpathid += 1;
+ let crate_items: Vec<&IndexItem> = search_index
+ .iter_mut()
+ .map(|item| {
+ item.parent_idx = item.parent.and_then(|defid| match defid_to_pathid.entry(defid) {
+ Entry::Occupied(entry) => Some(*entry.get()),
+ Entry::Vacant(entry) => {
+ let pathid = lastpathid;
+ entry.insert(pathid);
+ lastpathid += 1;
- if let Some(&(ref fqp, short)) = paths.get(&defid) {
- crate_paths.push((short, fqp.last().unwrap().clone()));
- Some(pathid)
- } else {
- None
+ if let Some(&(ref fqp, short)) = paths.get(&defid) {
+ crate_paths.push((short, fqp.last().unwrap().clone()));
+ Some(pathid)
+ } else {
+ None
+ }
}
+ });
+
+ // Omit the parent path if it is same to that of the prior item.
+ if lastpath == &item.path {
+ item.path.clear();
+ } else {
+ lastpath = &item.path;
}
- });
- // Omit the parent path if it is same to that of the prior item.
- if lastpath == item.path {
- item.path.clear();
- } else {
- lastpath = item.path.clone();
- }
- crate_items.push(&*item);
- }
+ &*item
+ })
+ .collect();
struct CrateData<'a> {
doc: String,
crate fn get_index_search_type<'tcx>(
item: &clean::Item,
tcx: TyCtxt<'tcx>,
+ cache: &Cache,
) -> Option<IndexItemFunctionType> {
let (mut inputs, mut output) = match *item.kind {
- clean::FunctionItem(ref f) => get_all_types(&f.generics, &f.decl, tcx),
- clean::MethodItem(ref m, _) => get_all_types(&m.generics, &m.decl, tcx),
- clean::TyMethodItem(ref m) => get_all_types(&m.generics, &m.decl, tcx),
+ clean::FunctionItem(ref f) => get_all_types(&f.generics, &f.decl, tcx, cache),
+ clean::MethodItem(ref m, _) => get_all_types(&m.generics, &m.decl, tcx, cache),
+ clean::TyMethodItem(ref m) => get_all_types(&m.generics, &m.decl, tcx, cache),
_ => return None,
};
inputs.retain(|a| a.ty.name.is_some());
output.retain(|a| a.ty.name.is_some());
- let output = if output.is_empty() { None } else { Some(output) };
Some(IndexItemFunctionType { inputs, output })
}
/// The point of this function is to replace bounds with types.
///
/// i.e. `[T, U]` when you have the following bounds: `T: Display, U: Option<T>` will return
-/// `[Display, Option]` (we just returns the list of the types, we don't care about the
-/// wrapped types in here).
+/// `[Display, Option]`. If a type parameter has no trait bound, it is discarded.
+///
+/// Important note: It goes through generics recursively. So if you have
+/// `T: Option<Result<(), ()>>`, it'll go into `Option` and then into `Result`.
crate fn get_real_types<'tcx>(
generics: &Generics,
arg: &Type,
tcx: TyCtxt<'tcx>,
recurse: usize,
res: &mut Vec<TypeWithKind>,
+ cache: &Cache,
) {
fn insert_ty(
res: &mut Vec<TypeWithKind>,
tcx: TyCtxt<'_>,
ty: Type,
mut generics: Vec<TypeWithKind>,
+ _cache: &Cache,
) {
let is_full_generic = ty.is_full_generic();
- if is_full_generic && generics.len() == 1 {
- // In this case, no need to go through an intermediate state if the generics
- // contains only one element.
- //
- // For example:
- //
- // fn foo<T: Display>(r: Option<T>) {}
- //
- // In this case, it would contain:
- //
- // ```
- // [{
- // name: "option",
- // generics: [{
- // name: "",
- // generics: [
- // name: "Display",
- // generics: []
- // }]
- // }]
- // }]
- // ```
- //
- // After removing the intermediate (unnecessary) full generic, it'll become:
- //
- // ```
- // [{
- // name: "option",
- // generics: [{
- // name: "Display",
- // generics: []
- // }]
- // }]
- // ```
- //
- // To be noted that it can work if there is ONLY ONE generic, otherwise we still
- // need to keep it as is!
- res.push(generics.pop().unwrap());
- return;
+ if is_full_generic {
+ if generics.is_empty() {
+ // This is a type parameter with no trait bounds (for example: `T` in
+ // `fn f<T>(p: T)`, so not useful for the rustdoc search because we would end up
+ // with an empty type with an empty name. Let's just discard it.
+ return;
+ } else if generics.len() == 1 {
+ // In this case, no need to go through an intermediate state if the type parameter
+ // contains only one trait bound.
+ //
+ // For example:
+ //
+ // `fn foo<T: Display>(r: Option<T>) {}`
+ //
+ // In this case, it would contain:
+ //
+ // ```
+ // [{
+ // name: "option",
+ // generics: [{
+ // name: "",
+ // generics: [
+ // name: "Display",
+ // generics: []
+ // }]
+ // }]
+ // }]
+ // ```
+ //
+ // After removing the intermediate (unnecessary) type parameter, it'll become:
+ //
+ // ```
+ // [{
+ // name: "option",
+ // generics: [{
+ // name: "Display",
+ // generics: []
+ // }]
+ // }]
+ // ```
+ //
+ // To be noted that it can work if there is ONLY ONE trait bound, otherwise we still
+ // need to keep it as is!
+ res.push(generics.pop().unwrap());
+ return;
+ }
}
let mut index_ty = get_index_type(&ty, generics);
if index_ty.name.as_ref().map(|s| s.is_empty()).unwrap_or(true) {
return;
}
+ // If this argument is a type parameter and not a trait bound or a type, we need to look
+ // for its bounds.
if let Type::Generic(arg_s) = *arg {
+ // First we check if the bounds are in a `where` predicate...
if let Some(where_pred) = generics.where_predicates.iter().find(|g| match g {
WherePredicate::BoundPredicate { ty, .. } => {
ty.def_id_no_primitives() == arg.def_id_no_primitives()
continue;
}
if let Some(ty) = x.get_type() {
- get_real_types(generics, &ty, tcx, recurse + 1, &mut ty_generics);
+ get_real_types(
+ generics,
+ &ty,
+ tcx,
+ recurse + 1,
+ &mut ty_generics,
+ cache,
+ );
}
}
}
}
- insert_ty(res, tcx, arg.clone(), ty_generics);
+ insert_ty(res, tcx, arg.clone(), ty_generics, cache);
}
+ // Otherwise we check if the trait bounds are "inlined" like `T: Option<u32>`...
if let Some(bound) = generics.params.iter().find(|g| g.is_type() && g.name == arg_s) {
let mut ty_generics = Vec::new();
for bound in bound.get_bounds().unwrap_or(&[]) {
if let Some(path) = bound.get_trait_path() {
let ty = Type::ResolvedPath { did: path.def_id(), path };
- get_real_types(generics, &ty, tcx, recurse + 1, &mut ty_generics);
+ get_real_types(generics, &ty, tcx, recurse + 1, &mut ty_generics, cache);
}
}
- insert_ty(res, tcx, arg.clone(), ty_generics);
+ insert_ty(res, tcx, arg.clone(), ty_generics, cache);
}
} else {
+ // This is not a type parameter. So for example if we have `T, U: Option<T>`, and we're
+ // looking at `Option`, we enter this "else" condition, otherwise if it's `T`, we don't.
+ //
+ // So in here, we can add it directly and look for its own type parameters (so for `Option`,
+ // we will look for them but not for `T`).
let mut ty_generics = Vec::new();
if let Some(arg_generics) = arg.generics() {
for gen in arg_generics.iter() {
- get_real_types(generics, gen, tcx, recurse + 1, &mut ty_generics);
+ get_real_types(generics, gen, tcx, recurse + 1, &mut ty_generics, cache);
}
}
- insert_ty(res, tcx, arg.clone(), ty_generics);
+ insert_ty(res, tcx, arg.clone(), ty_generics, cache);
}
}
generics: &Generics,
decl: &FnDecl,
tcx: TyCtxt<'tcx>,
+ cache: &Cache,
) -> (Vec<TypeWithKind>, Vec<TypeWithKind>) {
let mut all_types = Vec::new();
for arg in decl.inputs.values.iter() {
if arg.type_.is_self_type() {
continue;
}
- // FIXME: performance wise, it'd be much better to move `args` declaration outside of the
- // loop and replace this line with `args.clear()`.
let mut args = Vec::new();
- get_real_types(generics, &arg.type_, tcx, 0, &mut args);
+ get_real_types(generics, &arg.type_, tcx, 0, &mut args, cache);
if !args.is_empty() {
- // FIXME: once back to performance improvements, replace this line with:
- // `all_types.extend(args.drain(..));`.
all_types.extend(args);
} else {
if let Some(kind) = arg.type_.def_id_no_primitives().map(|did| tcx.def_kind(did).into())
let mut ret_types = Vec::new();
match decl.output {
FnRetTy::Return(ref return_type) => {
- get_real_types(generics, return_type, tcx, 0, &mut ret_types);
+ get_real_types(generics, return_type, tcx, 0, &mut ret_types, cache);
if ret_types.is_empty() {
if let Some(kind) =
return_type.def_id_no_primitives().map(|did| tcx.def_kind(did).into())
#[derive(Debug)]
crate struct IndexItemFunctionType {
inputs: Vec<TypeWithKind>,
- output: Option<Vec<TypeWithKind>>,
+ output: Vec<TypeWithKind>,
}
impl Serialize for IndexItemFunctionType {
S: Serializer,
{
// If we couldn't figure out a type, just write `null`.
- let mut iter = self.inputs.iter();
- if match self.output {
- Some(ref output) => iter.chain(output.iter()).any(|i| i.ty.name.is_none()),
- None => iter.any(|i| i.ty.name.is_none()),
- } {
+ let has_missing = self.inputs.iter().chain(self.output.iter()).any(|i| i.ty.name.is_none());
+ if has_missing {
serializer.serialize_none()
} else {
let mut seq = serializer.serialize_seq(None)?;
seq.serialize_element(&self.inputs)?;
- if let Some(output) = &self.output {
- if output.len() > 1 {
- seq.serialize_element(&output)?;
- } else {
- seq.serialize_element(&output[0])?;
- }
+ match self.output.as_slice() {
+ [] => {}
+ [one] => seq.serialize_element(one)?,
+ all => seq.serialize_element(all)?,
}
seq.end()
}
}
// Recurse into any further impls that might exist for `target`
- if let Some(target_did) = target.def_id_no_primitives() {
+ if let Some(target_did) = target.def_id(c) {
if let Some(target_impls) = c.impls.get(&target_did) {
if let Some(target_deref_impl) = target_impls.iter().find(|i| {
i.inner_impl()
mod core;
mod docfs;
mod doctest;
-mod doctree;
mod error;
mod externalfiles;
mod fold;
let default_passes = options.default_passes;
let output_format = options.output_format;
// FIXME: fix this clone (especially render_options)
+ let externs = options.externs.clone();
let manual_passes = options.manual_passes.clone();
let render_options = options.render_options.clone();
let scrape_examples_options = options.scrape_examples_options.clone();
// We need to hold on to the complete resolver, so we cause everything to be
// cloned for the analysis passes to use. Suboptimal, but necessary in the
// current architecture.
- let resolver = core::create_resolver(queries, sess);
+ let resolver = core::create_resolver(externs, queries, sess);
if sess.diagnostic().has_errors_or_lint_errors() {
sess.fatal("Compilation failed, aborting rustdoc");
| Use
| LifetimeParam
| Ctor(_, _)
- | AnonConst => {
+ | AnonConst
+ | InlineConst => {
let note = assoc_item_not_allowed(res);
if let Some(span) = sp {
diag.span_label(span, ¬e);
+use ast::visit;
use rustc_ast as ast;
use rustc_hir::def::Namespace::TypeNS;
use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID};
let mut loader = IntraLinkCrateLoader { current_mod: CRATE_DEF_ID, resolver };
// `walk_crate` doesn't visit the crate itself for some reason.
loader.load_links_in_attrs(&krate.attrs, krate.span);
- ast::visit::walk_crate(&mut loader, krate);
+ visit::walk_crate(&mut loader, krate);
loader.resolver
}
}
}
-impl ast::visit::Visitor<'_> for IntraLinkCrateLoader {
+impl visit::Visitor<'_> for IntraLinkCrateLoader {
+ fn visit_foreign_item(&mut self, item: &ast::ForeignItem) {
+ self.load_links_in_attrs(&item.attrs, item.span);
+ visit::walk_foreign_item(self, item)
+ }
+
fn visit_item(&mut self, item: &ast::Item) {
use rustc_ast_lowering::ResolverAstLowering;
let old_mod = mem::replace(&mut self.current_mod, new_mod);
self.load_links_in_attrs(&item.attrs, item.span);
- ast::visit::walk_item(self, item);
+ visit::walk_item(self, item);
self.current_mod = old_mod;
} else {
self.load_links_in_attrs(&item.attrs, item.span);
- ast::visit::walk_item(self, item);
+ visit::walk_item(self, item);
}
}
+
+ // NOTE: if doc-comments are ever allowed on function parameters, this will have to implement `visit_param` too.
+
+ fn visit_assoc_item(&mut self, item: &ast::AssocItem, ctxt: visit::AssocCtxt) {
+ self.load_links_in_attrs(&item.attrs, item.span);
+ visit::walk_assoc_item(self, item, ctxt)
+ }
+
+ fn visit_field_def(&mut self, field: &ast::FieldDef) {
+ self.load_links_in_attrs(&field.attrs, field.span);
+ visit::walk_field_def(self, field)
+ }
+
+ fn visit_variant(&mut self, v: &ast::Variant) {
+ self.load_links_in_attrs(&v.attrs, v.span);
+ visit::walk_variant(self, v)
+ }
}
// Follow all `Deref` targets of included items and recursively add them as valid
fn add_deref_target(
+ cx: &DocContext<'_>,
map: &FxHashMap<DefId, &Type>,
cleaner: &mut BadImplStripper,
type_did: DefId,
debug!("add_deref_target: type {:?}, target {:?}", type_did, target);
if let Some(target_prim) = target.primitive_type() {
cleaner.prims.insert(target_prim);
- } else if let Some(target_did) = target.def_id_no_primitives() {
+ } else if let Some(target_did) = target.def_id(&cx.cache) {
// `impl Deref<Target = S> for S`
if target_did == type_did {
// Avoid infinite cycles
return;
}
cleaner.items.insert(target_did.into());
- add_deref_target(map, cleaner, target_did);
+ add_deref_target(cx, map, cleaner, target_did);
}
}
}
// `Deref` target type and the impl for type positions, this map of types is keyed by
// `DefId` and for convenience uses a special cleaner that accepts `DefId`s directly.
if cleaner.keep_impl_with_def_id(for_did.into()) {
- add_deref_target(&type_did_to_deref_target, &mut cleaner, for_did);
+ add_deref_target(cx, &type_did_to_deref_target, &mut cleaner, for_did);
}
}
}
use rustc_middle::middle::privacy::AccessLevel;
use rustc_middle::ty::TyCtxt;
use rustc_span::def_id::{CRATE_DEF_ID, LOCAL_CRATE};
-use rustc_span::source_map::Spanned;
use rustc_span::symbol::{kw, sym, Symbol};
+use rustc_span::Span;
use std::mem;
use crate::clean::{self, cfg::Cfg, AttributesExt, NestedAttributesExt};
use crate::core;
-use crate::doctree::*;
+
+/// This module is used to store stuff from Rust's AST in a more convenient
+/// manner (and with prettier names) before cleaning.
+#[derive(Debug)]
+crate struct Module<'hir> {
+ crate name: Symbol,
+ crate where_inner: Span,
+ crate mods: Vec<Module<'hir>>,
+ crate id: hir::HirId,
+ // (item, renamed)
+ crate items: Vec<(&'hir hir::Item<'hir>, Option<Symbol>)>,
+ crate foreigns: Vec<(&'hir hir::ForeignItem<'hir>, Option<Symbol>)>,
+}
+
+impl Module<'hir> {
+ crate fn new(name: Symbol, id: hir::HirId, where_inner: Span) -> Module<'hir> {
+ Module { name, id, where_inner, mods: Vec::new(), items: Vec::new(), foreigns: Vec::new() }
+ }
+
+ crate fn where_outer(&self, tcx: TyCtxt<'_>) -> Span {
+ tcx.hir().span(self.id)
+ }
+}
// FIXME: Should this be replaced with tcx.def_path_str?
fn def_id_to_path(tcx: TyCtxt<'_>, did: DefId) -> Vec<String> {
}
crate fn visit(mut self) -> Module<'tcx> {
- let span = self.cx.tcx.def_span(CRATE_DEF_ID);
let mut top_level_module = self.visit_mod_contents(
- &Spanned { span, node: hir::VisibilityKind::Public },
hir::CRATE_HIR_ID,
self.cx.tcx.hir().root_module(),
self.cx.tcx.crate_name(LOCAL_CRATE),
fn visit_mod_contents(
&mut self,
- vis: &hir::Visibility<'_>,
id: hir::HirId,
m: &'tcx hir::Mod<'tcx>,
name: Symbol,
) -> Module<'tcx> {
let mut om = Module::new(name, id, m.inner);
+ let def_id = self.cx.tcx.hir().local_def_id(id).to_def_id();
// Keep track of if there were any private modules in the path.
let orig_inside_public_path = self.inside_public_path;
- self.inside_public_path &= vis.node.is_pub();
+ self.inside_public_path &= self.cx.tcx.visibility(def_id).is_public();
for &i in m.item_ids {
let item = self.cx.tcx.hir().item(i);
self.visit_item(item, None, &mut om);
let name = renamed.unwrap_or(item.ident.name);
let def_id = item.def_id.to_def_id();
- let is_pub = item.vis.node.is_pub() || self.cx.tcx.has_attr(def_id, sym::macro_export);
+ let is_pub = self.cx.tcx.visibility(def_id).is_public();
if is_pub {
self.store_path(item.def_id.to_def_id());
}
}
hir::ItemKind::Mod(ref m) => {
- om.mods.push(self.visit_mod_contents(&item.vis, item.hir_id(), m, name));
+ om.mods.push(self.visit_mod_contents(item.hir_id(), m, name));
}
hir::ItemKind::Fn(..)
| hir::ItemKind::ExternCrate(..)
om: &mut Module<'tcx>,
) {
// If inlining we only want to include public functions.
- if !self.inlining || item.vis.node.is_pub() {
+ if !self.inlining || self.cx.tcx.visibility(item.def_id).is_public() {
om.foreigns.push((item, renamed));
}
}
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{CrateNum, DefId, CRATE_DEF_INDEX};
use rustc_middle::middle::privacy::{AccessLevel, AccessLevels};
-use rustc_middle::ty::{TyCtxt, Visibility};
+use rustc_middle::ty::TyCtxt;
use rustc_span::symbol::sym;
use crate::clean::{AttributesExt, NestedAttributesExt};
for item in self.tcx.item_children(def_id).iter() {
if let Some(def_id) = item.res.opt_def_id() {
if self.tcx.def_key(def_id).parent.map_or(false, |d| d == def_id.index)
- || item.vis == Visibility::Public
+ || item.vis.is_public()
{
self.visit_item(item.res);
}
fn visit_item(&mut self, res: Res<!>) {
let def_id = res.def_id();
let vis = self.tcx.visibility(def_id);
- let inherited_item_level = if vis == Visibility::Public { self.prev_level } else { None };
+ let inherited_item_level = if vis.is_public() { self.prev_level } else { None };
let item_level = self.update(def_id, inherited_item_level);
-Subproject commit a7348ae0df3c71581dbe3d355fc0fb6ce6332dd0
+Subproject commit e048e97f5280e8a232a43ae134d395aeab67c2e8
--- /dev/null
+// compile-flags: -O
+
+#![crate_type = "lib"]
+
+// CHECK-LABEL: @array_clone
+#[no_mangle]
+pub fn array_clone(a: &[u8; 2]) -> [u8; 2] {
+ // CHECK-NOT: getelementptr
+ // CHECK-NOT: load i8
+ // CHECK-NOT: zext
+ // CHECK-NOT: shl
+ // CHECK: load i16
+ // CHECK-NEXT: ret i16
+ a.clone()
+}
--- /dev/null
+// compile-flags: -O
+// only-x86_64
+// ignore-debug: the debug assertions in from_raw_parts get in the way
+
+#![crate_type = "lib"]
+
+// CHECK-LABEL: @slice_reverse_u8
+#[no_mangle]
+pub fn slice_reverse_u8(slice: &mut [u8]) {
+ // CHECK-NOT: panic_bounds_check
+ // CHECK-NOT: slice_end_index_len_fail
+ // CHECK: shufflevector <{{[0-9]+}} x i8>
+ // CHECK-NOT: panic_bounds_check
+ // CHECK-NOT: slice_end_index_len_fail
+ slice.reverse();
+}
+
+// CHECK-LABEL: @slice_reverse_i32
+#[no_mangle]
+pub fn slice_reverse_i32(slice: &mut [i32]) {
+ // CHECK-NOT: panic_bounds_check
+ // CHECK-NOT: slice_end_index_len_fail
+ // CHECK: shufflevector <{{[0-9]+}} x i32>
+ // CHECK-NOT: panic_bounds_check
+ // CHECK-NOT: slice_end_index_len_fail
+ slice.reverse();
+}
--- /dev/null
+-include ../../run-make-fulldeps/tools.mk
+
+# Regression test for issue #10971
+# Running two invocations in parallel would overwrite each other's temp files.
+
+all:
+ touch $(TMPDIR)/lib.rs
+
+ $(RUSTC) --crate-type=lib -Z temps-dir=$(TMPDIR)/temp1 $(TMPDIR)/lib.rs & \
+ $(RUSTC) --crate-type=staticlib -Z temps-dir=$(TMPDIR)/temp2 $(TMPDIR)/lib.rs
--- /dev/null
+// intentionally empty
--- /dev/null
+// intentionally empty
--- /dev/null
+// intentionally empty
--- /dev/null
+// intentionally empty
--- /dev/null
+// check-pass
+// aux-crate:dep1=dep1.rs
+// aux-crate:dep2=dep2.rs
+// aux-crate:dep3=dep3.rs
+// aux-crate:dep4=dep4.rs
+#![deny(rustdoc::broken_intra_doc_links)]
+
+pub trait Trait {
+ /// [dep1]
+ type Item;
+}
+
+pub struct S {
+ /// [dep2]
+ pub x: usize,
+}
+
+extern "C" {
+ /// [dep3]
+ pub fn printf();
+}
+
+pub enum E {
+ /// [dep4]
+ A
+}
asm!("", clobber_abi(foo));
//~^ ERROR expected string literal
asm!("", clobber_abi("C" foo));
- //~^ ERROR expected `)`, found `foo`
+ //~^ ERROR expected one of `)` or `,`, found `foo`
asm!("", clobber_abi("C", foo));
- //~^ ERROR expected `)`, found `,`
+ //~^ ERROR expected string literal
asm!("{}", clobber_abi("C"), const foo);
//~^ ERROR arguments are not allowed after clobber_abi
//~^^ ERROR attempt to use a non-constant value in a constant
//~^ ERROR clobber_abi is not allowed after options
asm!("{}", options(), clobber_abi("C"), const foo);
//~^ ERROR clobber_abi is not allowed after options
- asm!("", clobber_abi("C"), clobber_abi("C"));
- //~^ ERROR clobber_abi specified multiple times
asm!("{a}", a = const foo, a = const bar);
//~^ ERROR duplicate argument named `a`
//~^^ ERROR argument never used
global_asm!("", clobber_abi(FOO));
//~^ ERROR expected string literal
global_asm!("", clobber_abi("C" FOO));
-//~^ ERROR expected `)`, found `FOO`
+//~^ ERROR expected one of `)` or `,`, found `FOO`
global_asm!("", clobber_abi("C", FOO));
-//~^ ERROR expected `)`, found `,`
+//~^ ERROR expected string literal
global_asm!("{}", clobber_abi("C"), const FOO);
//~^ ERROR arguments are not allowed after clobber_abi
//~^^ ERROR `clobber_abi` cannot be used with `global_asm!`
//~^ ERROR clobber_abi is not allowed after options
global_asm!("{}", options(), clobber_abi("C"), const FOO);
//~^ ERROR clobber_abi is not allowed after options
-global_asm!("", clobber_abi("C"), clobber_abi("C"));
-//~^ ERROR clobber_abi specified multiple times
global_asm!("{a}", a = const FOO, a = const BAR);
//~^ ERROR duplicate argument named `a`
//~^^ ERROR argument never used
LL | asm!("", clobber_abi(foo));
| ^^^ not a string literal
-error: expected `)`, found `foo`
+error: expected one of `)` or `,`, found `foo`
--> $DIR/parse-error.rs:42:34
|
LL | asm!("", clobber_abi("C" foo));
- | ^^^ expected `)`
+ | ^^^ expected one of `)` or `,`
-error: expected `)`, found `,`
- --> $DIR/parse-error.rs:44:33
+error: expected string literal
+ --> $DIR/parse-error.rs:44:35
|
LL | asm!("", clobber_abi("C", foo));
- | ^ expected `)`
+ | ^^^ not a string literal
error: arguments are not allowed after clobber_abi
--> $DIR/parse-error.rs:46:38
| |
| options
-error: clobber_abi specified multiple times
- --> $DIR/parse-error.rs:53:36
- |
-LL | asm!("", clobber_abi("C"), clobber_abi("C"));
- | ---------------- ^^^^^^^^^^^^^^^^
- | |
- | clobber_abi previously specified here
-
error: duplicate argument named `a`
- --> $DIR/parse-error.rs:55:36
+ --> $DIR/parse-error.rs:53:36
|
LL | asm!("{a}", a = const foo, a = const bar);
| ------------- ^^^^^^^^^^^^^ duplicate argument
| previously here
error: argument never used
- --> $DIR/parse-error.rs:55:36
+ --> $DIR/parse-error.rs:53:36
|
LL | asm!("{a}", a = const foo, a = const bar);
| ^^^^^^^^^^^^^ argument never used
= help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {1} */"`
error: explicit register arguments cannot have names
- --> $DIR/parse-error.rs:60:18
+ --> $DIR/parse-error.rs:58:18
|
LL | asm!("", a = in("x0") foo);
| ^^^^^^^^^^^^^^^^
error: named arguments cannot follow explicit register arguments
- --> $DIR/parse-error.rs:62:35
+ --> $DIR/parse-error.rs:60:35
|
LL | asm!("{a}", in("x0") foo, a = const bar);
| ------------ ^^^^^^^^^^^^^ named argument
| explicit register argument
error: named arguments cannot follow explicit register arguments
- --> $DIR/parse-error.rs:65:35
+ --> $DIR/parse-error.rs:63:35
|
LL | asm!("{a}", in("x0") foo, a = const bar);
| ------------ ^^^^^^^^^^^^^ named argument
| explicit register argument
error: positional arguments cannot follow named arguments or explicit register arguments
- --> $DIR/parse-error.rs:68:35
+ --> $DIR/parse-error.rs:66:35
|
LL | asm!("{1}", in("x0") foo, const bar);
| ------------ ^^^^^^^^^ positional argument
| explicit register argument
error: expected one of `clobber_abi`, `const`, `in`, `inlateout`, `inout`, `lateout`, `options`, `out`, or `sym`, found `""`
- --> $DIR/parse-error.rs:71:29
+ --> $DIR/parse-error.rs:69:29
|
LL | asm!("", options(), "");
| ^^ expected one of 9 possible tokens
error: expected one of `clobber_abi`, `const`, `in`, `inlateout`, `inout`, `lateout`, `options`, `out`, or `sym`, found `"{}"`
- --> $DIR/parse-error.rs:73:33
+ --> $DIR/parse-error.rs:71:33
|
LL | asm!("{}", in(reg) foo, "{}", out(reg) foo);
| ^^^^ expected one of 9 possible tokens
error: asm template must be a string literal
- --> $DIR/parse-error.rs:75:14
+ --> $DIR/parse-error.rs:73:14
|
LL | asm!(format!("{{{}}}", 0), in(reg) foo);
| ^^^^^^^^^^^^^^^^^^^^
= note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info)
error: asm template must be a string literal
- --> $DIR/parse-error.rs:77:21
+ --> $DIR/parse-error.rs:75:21
|
LL | asm!("{1}", format!("{{{}}}", 0), in(reg) foo, out(reg) bar);
| ^^^^^^^^^^^^^^^^^^^^
= note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info)
error: _ cannot be used for input operands
- --> $DIR/parse-error.rs:79:28
+ --> $DIR/parse-error.rs:77:28
|
LL | asm!("{}", in(reg) _);
| ^
error: _ cannot be used for input operands
- --> $DIR/parse-error.rs:81:31
+ --> $DIR/parse-error.rs:79:31
|
LL | asm!("{}", inout(reg) _);
| ^
error: _ cannot be used for input operands
- --> $DIR/parse-error.rs:83:35
+ --> $DIR/parse-error.rs:81:35
|
LL | asm!("{}", inlateout(reg) _);
| ^
error: requires at least a template string argument
- --> $DIR/parse-error.rs:90:1
+ --> $DIR/parse-error.rs:88:1
|
LL | global_asm!();
| ^^^^^^^^^^^^^
error: asm template must be a string literal
- --> $DIR/parse-error.rs:92:13
+ --> $DIR/parse-error.rs:90:13
|
LL | global_asm!(FOO);
| ^^^
error: expected token: `,`
- --> $DIR/parse-error.rs:94:18
+ --> $DIR/parse-error.rs:92:18
|
LL | global_asm!("{}" FOO);
| ^^^ expected `,`
error: expected operand, options, or additional template string
- --> $DIR/parse-error.rs:96:19
+ --> $DIR/parse-error.rs:94:19
|
LL | global_asm!("{}", FOO);
| ^^^ expected operand, options, or additional template string
error: expected expression, found end of macro arguments
- --> $DIR/parse-error.rs:98:24
+ --> $DIR/parse-error.rs:96:24
|
LL | global_asm!("{}", const);
| ^ expected expression
error: expected one of `,`, `.`, `?`, or an operator, found `FOO`
- --> $DIR/parse-error.rs:100:30
+ --> $DIR/parse-error.rs:98:30
|
LL | global_asm!("{}", const(reg) FOO);
| ^^^ expected one of `,`, `.`, `?`, or an operator
error: expected one of `)`, `att_syntax`, or `raw`, found `FOO`
- --> $DIR/parse-error.rs:102:25
+ --> $DIR/parse-error.rs:100:25
|
LL | global_asm!("", options(FOO));
| ^^^ expected one of `)`, `att_syntax`, or `raw`
error: expected one of `)`, `att_syntax`, or `raw`, found `nomem`
- --> $DIR/parse-error.rs:104:25
+ --> $DIR/parse-error.rs:102:25
|
LL | global_asm!("", options(nomem FOO));
| ^^^^^ expected one of `)`, `att_syntax`, or `raw`
error: expected one of `)`, `att_syntax`, or `raw`, found `nomem`
- --> $DIR/parse-error.rs:106:25
+ --> $DIR/parse-error.rs:104:25
|
LL | global_asm!("", options(nomem, FOO));
| ^^^^^ expected one of `)`, `att_syntax`, or `raw`
error: arguments are not allowed after options
- --> $DIR/parse-error.rs:108:30
+ --> $DIR/parse-error.rs:106:30
|
LL | global_asm!("{}", options(), const FOO);
| --------- ^^^^^^^^^ argument
| previous options
error: expected string literal
- --> $DIR/parse-error.rs:110:29
+ --> $DIR/parse-error.rs:108:29
|
LL | global_asm!("", clobber_abi(FOO));
| ^^^ not a string literal
-error: expected `)`, found `FOO`
- --> $DIR/parse-error.rs:112:33
+error: expected one of `)` or `,`, found `FOO`
+ --> $DIR/parse-error.rs:110:33
|
LL | global_asm!("", clobber_abi("C" FOO));
- | ^^^ expected `)`
+ | ^^^ expected one of `)` or `,`
-error: expected `)`, found `,`
- --> $DIR/parse-error.rs:114:32
+error: expected string literal
+ --> $DIR/parse-error.rs:112:34
|
LL | global_asm!("", clobber_abi("C", FOO));
- | ^ expected `)`
+ | ^^^ not a string literal
error: arguments are not allowed after clobber_abi
- --> $DIR/parse-error.rs:116:37
+ --> $DIR/parse-error.rs:114:37
|
LL | global_asm!("{}", clobber_abi("C"), const FOO);
| ---------------- ^^^^^^^^^ argument
| clobber_abi
error: `clobber_abi` cannot be used with `global_asm!`
- --> $DIR/parse-error.rs:116:19
+ --> $DIR/parse-error.rs:114:19
|
LL | global_asm!("{}", clobber_abi("C"), const FOO);
| ^^^^^^^^^^^^^^^^
error: clobber_abi is not allowed after options
- --> $DIR/parse-error.rs:119:28
+ --> $DIR/parse-error.rs:117:28
|
LL | global_asm!("", options(), clobber_abi("C"));
| --------- ^^^^^^^^^^^^^^^^
| options
error: clobber_abi is not allowed after options
- --> $DIR/parse-error.rs:121:30
+ --> $DIR/parse-error.rs:119:30
|
LL | global_asm!("{}", options(), clobber_abi("C"), const FOO);
| --------- ^^^^^^^^^^^^^^^^
| |
| options
-error: clobber_abi specified multiple times
- --> $DIR/parse-error.rs:123:35
- |
-LL | global_asm!("", clobber_abi("C"), clobber_abi("C"));
- | ---------------- ^^^^^^^^^^^^^^^^
- | |
- | clobber_abi previously specified here
-
error: duplicate argument named `a`
- --> $DIR/parse-error.rs:125:35
+ --> $DIR/parse-error.rs:121:35
|
LL | global_asm!("{a}", a = const FOO, a = const BAR);
| ------------- ^^^^^^^^^^^^^ duplicate argument
| previously here
error: argument never used
- --> $DIR/parse-error.rs:125:35
+ --> $DIR/parse-error.rs:121:35
|
LL | global_asm!("{a}", a = const FOO, a = const BAR);
| ^^^^^^^^^^^^^ argument never used
= help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {1} */"`
error: expected one of `clobber_abi`, `const`, or `options`, found `""`
- --> $DIR/parse-error.rs:128:28
+ --> $DIR/parse-error.rs:124:28
|
LL | global_asm!("", options(), "");
| ^^ expected one of `clobber_abi`, `const`, or `options`
error: expected one of `clobber_abi`, `const`, or `options`, found `"{}"`
- --> $DIR/parse-error.rs:130:30
+ --> $DIR/parse-error.rs:126:30
|
LL | global_asm!("{}", const FOO, "{}", const FOO);
| ^^^^ expected one of `clobber_abi`, `const`, or `options`
error: asm template must be a string literal
- --> $DIR/parse-error.rs:132:13
+ --> $DIR/parse-error.rs:128:13
|
LL | global_asm!(format!("{{{}}}", 0), const FOO);
| ^^^^^^^^^^^^^^^^^^^^
= note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info)
error: asm template must be a string literal
- --> $DIR/parse-error.rs:134:20
+ --> $DIR/parse-error.rs:130:20
|
LL | global_asm!("{1}", format!("{{{}}}", 0), const FOO, const BAR);
| ^^^^^^^^^^^^^^^^^^^^
| ^^^ non-constant value
error[E0435]: attempt to use a non-constant value in a constant
- --> $DIR/parse-error.rs:55:31
+ --> $DIR/parse-error.rs:53:31
|
LL | let mut foo = 0;
| ---------- help: consider using `const` instead of `let`: `const foo`
| ^^^ non-constant value
error[E0435]: attempt to use a non-constant value in a constant
- --> $DIR/parse-error.rs:55:46
+ --> $DIR/parse-error.rs:53:46
|
LL | let mut bar = 0;
| ---------- help: consider using `const` instead of `let`: `const bar`
| ^^^ non-constant value
error[E0435]: attempt to use a non-constant value in a constant
- --> $DIR/parse-error.rs:62:45
+ --> $DIR/parse-error.rs:60:45
|
LL | let mut bar = 0;
| ---------- help: consider using `const` instead of `let`: `const bar`
| ^^^ non-constant value
error[E0435]: attempt to use a non-constant value in a constant
- --> $DIR/parse-error.rs:65:45
+ --> $DIR/parse-error.rs:63:45
|
LL | let mut bar = 0;
| ---------- help: consider using `const` instead of `let`: `const bar`
| ^^^ non-constant value
error[E0435]: attempt to use a non-constant value in a constant
- --> $DIR/parse-error.rs:68:41
+ --> $DIR/parse-error.rs:66:41
|
LL | let mut bar = 0;
| ---------- help: consider using `const` instead of `let`: `const bar`
LL | asm!("{1}", in("x0") foo, const bar);
| ^^^ non-constant value
-error: aborting due to 66 previous errors
+error: aborting due to 64 previous errors
For more information about this error, try `rustc --explain E0435`.
--- /dev/null
+// needs-asm-support
+// only-x86_64
+
+// checks various modes of failure for the `clobber_abi` argument (after parsing)
+
+#![feature(asm)]
+
+fn main() {
+ unsafe {
+ asm!("", clobber_abi("C"));
+ asm!("", clobber_abi("foo"));
+ //~^ ERROR invalid ABI for `clobber_abi`
+ asm!("", clobber_abi("C", "foo"));
+ //~^ ERROR invalid ABI for `clobber_abi`
+ asm!("", clobber_abi("C", "C"));
+ //~^ ERROR `C` ABI specified multiple times
+ asm!("", clobber_abi("win64", "sysv64"));
+ asm!("", clobber_abi("win64", "efiapi"));
+ //~^ ERROR `win64` ABI specified multiple times
+ asm!("", clobber_abi("C", "foo", "C"));
+ //~^ ERROR invalid ABI for `clobber_abi`
+ //~| ERROR `C` ABI specified multiple times
+ asm!("", clobber_abi("win64", "foo", "efiapi"));
+ //~^ ERROR invalid ABI for `clobber_abi`
+ //~| ERROR `win64` ABI specified multiple times
+ asm!("", clobber_abi("C"), clobber_abi("C"));
+ //~^ ERROR `C` ABI specified multiple times
+ asm!("", clobber_abi("win64"), clobber_abi("sysv64"));
+ asm!("", clobber_abi("win64"), clobber_abi("efiapi"));
+ //~^ ERROR `win64` ABI specified multiple times
+ }
+}
--- /dev/null
+error: invalid ABI for `clobber_abi`
+ --> $DIR/bad-clobber-abi.rs:11:18
+ |
+LL | asm!("", clobber_abi("foo"));
+ | ^^^^^^^^^^^^^^^^^^
+ |
+ = note: the following ABIs are supported on this target: `C`, `system`, `efiapi`, `win64`, `sysv64`
+
+error: invalid ABI for `clobber_abi`
+ --> $DIR/bad-clobber-abi.rs:13:35
+ |
+LL | asm!("", clobber_abi("C", "foo"));
+ | ^^^^^
+ |
+ = note: the following ABIs are supported on this target: `C`, `system`, `efiapi`, `win64`, `sysv64`
+
+error: `C` ABI specified multiple times
+ --> $DIR/bad-clobber-abi.rs:15:35
+ |
+LL | asm!("", clobber_abi("C", "C"));
+ | --- ^^^
+ | |
+ | previously specified here
+
+error: `win64` ABI specified multiple times
+ --> $DIR/bad-clobber-abi.rs:18:39
+ |
+LL | asm!("", clobber_abi("win64", "efiapi"));
+ | ------- ^^^^^^^^
+ | |
+ | previously specified here
+ |
+ = note: these ABIs are equivalent on the current target
+
+error: invalid ABI for `clobber_abi`
+ --> $DIR/bad-clobber-abi.rs:20:35
+ |
+LL | asm!("", clobber_abi("C", "foo", "C"));
+ | ^^^^^
+ |
+ = note: the following ABIs are supported on this target: `C`, `system`, `efiapi`, `win64`, `sysv64`
+
+error: `C` ABI specified multiple times
+ --> $DIR/bad-clobber-abi.rs:20:42
+ |
+LL | asm!("", clobber_abi("C", "foo", "C"));
+ | --- ^^^
+ | |
+ | previously specified here
+
+error: invalid ABI for `clobber_abi`
+ --> $DIR/bad-clobber-abi.rs:23:39
+ |
+LL | asm!("", clobber_abi("win64", "foo", "efiapi"));
+ | ^^^^^
+ |
+ = note: the following ABIs are supported on this target: `C`, `system`, `efiapi`, `win64`, `sysv64`
+
+error: `win64` ABI specified multiple times
+ --> $DIR/bad-clobber-abi.rs:23:46
+ |
+LL | asm!("", clobber_abi("win64", "foo", "efiapi"));
+ | ------- ^^^^^^^^
+ | |
+ | previously specified here
+ |
+ = note: these ABIs are equivalent on the current target
+
+error: `C` ABI specified multiple times
+ --> $DIR/bad-clobber-abi.rs:26:36
+ |
+LL | asm!("", clobber_abi("C"), clobber_abi("C"));
+ | ---------------- ^^^^^^^^^^^^^^^^
+ | |
+ | previously specified here
+
+error: `win64` ABI specified multiple times
+ --> $DIR/bad-clobber-abi.rs:29:40
+ |
+LL | asm!("", clobber_abi("win64"), clobber_abi("efiapi"));
+ | -------------------- ^^^^^^^^^^^^^^^^^^^^^
+ | |
+ | previously specified here
+ |
+ = note: these ABIs are equivalent on the current target
+
+error: aborting due to 10 previous errors
+
//~^ ERROR invalid ABI for `clobber_abi`
asm!("{}", out(reg) foo, clobber_abi("C"));
//~^ ERROR asm with `clobber_abi` must specify explicit registers for outputs
+ asm!("{}", out(reg) foo, clobber_abi("C"), clobber_abi("C"));
+ //~^ ERROR asm with `clobber_abi` must specify explicit registers for outputs
+ //~| ERROR `C` ABI specified multiple times
asm!("", out("eax") foo, clobber_abi("C"));
}
}
| |
| generic outputs
+error: asm with `clobber_abi` must specify explicit registers for outputs
+ --> $DIR/bad-options.rs:24:20
+ |
+LL | asm!("{}", out(reg) foo, clobber_abi("C"), clobber_abi("C"));
+ | ^^^^^^^^^^^^ ---------------- ---------------- clobber_abi
+ | | |
+ | | clobber_abi
+ | generic outputs
+
error: expected one of `)`, `att_syntax`, or `raw`, found `nomem`
- --> $DIR/bad-options.rs:28:25
+ --> $DIR/bad-options.rs:31:25
|
LL | global_asm!("", options(nomem));
| ^^^^^ expected one of `)`, `att_syntax`, or `raw`
error: expected one of `)`, `att_syntax`, or `raw`, found `readonly`
- --> $DIR/bad-options.rs:30:25
+ --> $DIR/bad-options.rs:33:25
|
LL | global_asm!("", options(readonly));
| ^^^^^^^^ expected one of `)`, `att_syntax`, or `raw`
error: expected one of `)`, `att_syntax`, or `raw`, found `noreturn`
- --> $DIR/bad-options.rs:32:25
+ --> $DIR/bad-options.rs:35:25
|
LL | global_asm!("", options(noreturn));
| ^^^^^^^^ expected one of `)`, `att_syntax`, or `raw`
error: expected one of `)`, `att_syntax`, or `raw`, found `pure`
- --> $DIR/bad-options.rs:34:25
+ --> $DIR/bad-options.rs:37:25
|
LL | global_asm!("", options(pure));
| ^^^^ expected one of `)`, `att_syntax`, or `raw`
error: expected one of `)`, `att_syntax`, or `raw`, found `nostack`
- --> $DIR/bad-options.rs:36:25
+ --> $DIR/bad-options.rs:39:25
|
LL | global_asm!("", options(nostack));
| ^^^^^^^ expected one of `)`, `att_syntax`, or `raw`
error: expected one of `)`, `att_syntax`, or `raw`, found `preserves_flags`
- --> $DIR/bad-options.rs:38:25
+ --> $DIR/bad-options.rs:41:25
|
LL | global_asm!("", options(preserves_flags));
| ^^^^^^^^^^^^^^^ expected one of `)`, `att_syntax`, or `raw`
|
= note: the following ABIs are supported on this target: `C`, `system`, `efiapi`, `win64`, `sysv64`
-error: aborting due to 13 previous errors
+error: `C` ABI specified multiple times
+ --> $DIR/bad-options.rs:24:52
+ |
+LL | asm!("{}", out(reg) foo, clobber_abi("C"), clobber_abi("C"));
+ | ---------------- ^^^^^^^^^^^^^^^^
+ | |
+ | previously specified here
+
+error: aborting due to 15 previous errors
--- /dev/null
+// run-pass
+// needs-asm-support
+// only-x86_64
+
+// Checks that multiple clobber_abi options can be used
+
+#![feature(asm, asm_sym)]
+
+extern "sysv64" fn foo(x: i32) -> i32 {
+ x + 16
+}
+
+extern "win64" fn bar(x: i32) -> i32 {
+ x / 2
+}
+
+fn main() {
+ let x = 8;
+ let y: i32;
+ // call `foo` with `x` as the input, and then `bar` with the output of `foo`
+ // and output that to `y`
+ unsafe {
+ asm!(
+ "call {}; mov rcx, rax; call {}",
+ sym foo,
+ sym bar,
+ in("rdi") x,
+ out("rax") y,
+ clobber_abi("sysv64", "win64"),
+ );
+ }
+ assert_eq!((x, y), (8, 12));
+}
asm!("{}", options(), const foo);
//~^ ERROR arguments are not allowed after options
//~^^ ERROR attempt to use a non-constant value in a constant
+ asm!("", clobber_abi());
+ //~^ ERROR at least one abi must be provided
asm!("", clobber_abi(foo));
//~^ ERROR expected string literal
asm!("", clobber_abi("C" foo));
- //~^ ERROR expected `)`, found `foo`
+ //~^ ERROR expected one of `)` or `,`, found `foo`
asm!("", clobber_abi("C", foo));
- //~^ ERROR expected `)`, found `,`
+ //~^ ERROR expected string literal
asm!("{}", clobber_abi("C"), const foo);
//~^ ERROR arguments are not allowed after clobber_abi
//~^^ ERROR attempt to use a non-constant value in a constant
//~^ ERROR clobber_abi is not allowed after options
asm!("{}", options(), clobber_abi("C"), const foo);
//~^ ERROR clobber_abi is not allowed after options
- asm!("", clobber_abi("C"), clobber_abi("C"));
- //~^ ERROR clobber_abi specified multiple times
asm!("{a}", a = const foo, a = const bar);
//~^ ERROR duplicate argument named `a`
//~^^ ERROR argument never used
global_asm!("", clobber_abi(FOO));
//~^ ERROR expected string literal
global_asm!("", clobber_abi("C" FOO));
-//~^ ERROR expected `)`, found `FOO`
+//~^ ERROR expected one of `)` or `,`, found `FOO`
global_asm!("", clobber_abi("C", FOO));
-//~^ ERROR expected `)`, found `,`
+//~^ ERROR expected string literal
global_asm!("{}", clobber_abi("C"), const FOO);
//~^ ERROR arguments are not allowed after clobber_abi
//~^^ ERROR `clobber_abi` cannot be used with `global_asm!`
global_asm!("{}", options(), clobber_abi("C"), const FOO);
//~^ ERROR clobber_abi is not allowed after options
global_asm!("", clobber_abi("C"), clobber_abi("C"));
-//~^ ERROR clobber_abi specified multiple times
+//~^ ERROR `clobber_abi` cannot be used with `global_asm!`
global_asm!("{a}", a = const FOO, a = const BAR);
//~^ ERROR duplicate argument named `a`
//~^^ ERROR argument never used
| |
| previous options
-error: expected string literal
+error: at least one abi must be provided as an argument to `clobber_abi`
--> $DIR/parse-error.rs:40:30
|
+LL | asm!("", clobber_abi());
+ | ^
+
+error: expected string literal
+ --> $DIR/parse-error.rs:42:30
+ |
LL | asm!("", clobber_abi(foo));
| ^^^ not a string literal
-error: expected `)`, found `foo`
- --> $DIR/parse-error.rs:42:34
+error: expected one of `)` or `,`, found `foo`
+ --> $DIR/parse-error.rs:44:34
|
LL | asm!("", clobber_abi("C" foo));
- | ^^^ expected `)`
+ | ^^^ expected one of `)` or `,`
-error: expected `)`, found `,`
- --> $DIR/parse-error.rs:44:33
+error: expected string literal
+ --> $DIR/parse-error.rs:46:35
|
LL | asm!("", clobber_abi("C", foo));
- | ^ expected `)`
+ | ^^^ not a string literal
error: arguments are not allowed after clobber_abi
- --> $DIR/parse-error.rs:46:38
+ --> $DIR/parse-error.rs:48:38
|
LL | asm!("{}", clobber_abi("C"), const foo);
| ---------------- ^^^^^^^^^ argument
| clobber_abi
error: clobber_abi is not allowed after options
- --> $DIR/parse-error.rs:49:29
+ --> $DIR/parse-error.rs:51:29
|
LL | asm!("", options(), clobber_abi("C"));
| --------- ^^^^^^^^^^^^^^^^
| options
error: clobber_abi is not allowed after options
- --> $DIR/parse-error.rs:51:31
+ --> $DIR/parse-error.rs:53:31
|
LL | asm!("{}", options(), clobber_abi("C"), const foo);
| --------- ^^^^^^^^^^^^^^^^
| |
| options
-error: clobber_abi specified multiple times
- --> $DIR/parse-error.rs:53:36
- |
-LL | asm!("", clobber_abi("C"), clobber_abi("C"));
- | ---------------- ^^^^^^^^^^^^^^^^
- | |
- | clobber_abi previously specified here
-
error: duplicate argument named `a`
--> $DIR/parse-error.rs:55:36
|
LL | global_asm!("", clobber_abi(FOO));
| ^^^ not a string literal
-error: expected `)`, found `FOO`
+error: expected one of `)` or `,`, found `FOO`
--> $DIR/parse-error.rs:112:33
|
LL | global_asm!("", clobber_abi("C" FOO));
- | ^^^ expected `)`
+ | ^^^ expected one of `)` or `,`
-error: expected `)`, found `,`
- --> $DIR/parse-error.rs:114:32
+error: expected string literal
+ --> $DIR/parse-error.rs:114:34
|
LL | global_asm!("", clobber_abi("C", FOO));
- | ^ expected `)`
+ | ^^^ not a string literal
error: arguments are not allowed after clobber_abi
--> $DIR/parse-error.rs:116:37
| |
| options
-error: clobber_abi specified multiple times
- --> $DIR/parse-error.rs:123:35
+error: `clobber_abi` cannot be used with `global_asm!`
+ --> $DIR/parse-error.rs:123:17
|
LL | global_asm!("", clobber_abi("C"), clobber_abi("C"));
- | ---------------- ^^^^^^^^^^^^^^^^
- | |
- | clobber_abi previously specified here
+ | ^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^
error: duplicate argument named `a`
--> $DIR/parse-error.rs:125:35
| ^^^ non-constant value
error[E0435]: attempt to use a non-constant value in a constant
- --> $DIR/parse-error.rs:46:44
+ --> $DIR/parse-error.rs:48:44
|
LL | let mut foo = 0;
| ---------- help: consider using `const` instead of `let`: `const foo`
].clone();
});
- assert!(result.is_err());
+ assert!(child.is_err());
assert_eq!(
1,
Rc::strong_count(&counter)
fn foo() { }
fn main() {
+ // FIXME: add closures when they're considered WF
test_copy_clone(foo);
let f: fn() = foo;
test_copy_clone(f);
- // FIXME: add closures when they're considered WF
- test_copy_clone([1; 56]);
+ // FIXME(#86252): reinstate array test after chalk upgrade
+ //test_copy_clone([1; 56]);
test_copy_clone((1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1));
test_copy_clone((1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, true, 'a', 1.1));
test_copy_clone(());
extern crate trait_impl_conflict;
use trait_impl_conflict::Foo;
-impl<A> Foo for A {
- //~^ ERROR E0119
- //~| ERROR E0210
+impl<A> Foo for A { //~ ERROR E0210
}
fn main() {
-error[E0119]: conflicting implementations of trait `trait_impl_conflict::Foo` for type `isize`
- --> $DIR/coherence-cross-crate-conflict.rs:9:1
- |
-LL | impl<A> Foo for A {
- | ^^^^^^^^^^^^^^^^^
- |
- = note: conflicting implementation in crate `trait_impl_conflict`:
- - impl Foo for isize;
-
error[E0210]: type parameter `A` must be used as the type parameter for some local type (e.g., `MyStruct<A>`)
--> $DIR/coherence-cross-crate-conflict.rs:9:6
|
= note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local
= note: only traits defined in the current crate can be implemented for a type parameter
-error: aborting due to 2 previous errors
+error: aborting due to previous error
-Some errors have detailed explanations: E0119, E0210.
-For more information about an error, try `rustc --explain E0119`.
+For more information about this error, try `rustc --explain E0210`.
-error[E0371]: the object type `(dyn Object + Marker2 + 'static)` automatically implements the trait `Marker1`
- --> $DIR/coherence-impl-trait-for-marker-trait-negative.rs:15:1
- |
-LL | impl !Marker1 for dyn Object + Marker2 { }
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `(dyn Object + Marker2 + 'static)` automatically implements trait `Marker1`
-
-error[E0371]: the object type `(dyn Object + Marker2 + 'static)` automatically implements the trait `Marker2`
- --> $DIR/coherence-impl-trait-for-marker-trait-negative.rs:17:1
- |
-LL | impl !Marker2 for dyn Object + Marker2 { }
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `(dyn Object + Marker2 + 'static)` automatically implements trait `Marker2`
-
error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
--> $DIR/coherence-impl-trait-for-marker-trait-negative.rs:23:1
|
LL | impl !Send for dyn Object + Marker2 {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't implement cross-crate trait with a default impl for non-struct/enum type
+error[E0371]: the object type `(dyn Object + Marker2 + 'static)` automatically implements the trait `Marker1`
+ --> $DIR/coherence-impl-trait-for-marker-trait-negative.rs:15:1
+ |
+LL | impl !Marker1 for dyn Object + Marker2 { }
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `(dyn Object + Marker2 + 'static)` automatically implements trait `Marker1`
+
+error[E0371]: the object type `(dyn Object + Marker2 + 'static)` automatically implements the trait `Marker2`
+ --> $DIR/coherence-impl-trait-for-marker-trait-negative.rs:17:1
+ |
+LL | impl !Marker2 for dyn Object + Marker2 { }
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `(dyn Object + Marker2 + 'static)` automatically implements trait `Marker2`
+
error: aborting due to 5 previous errors
Some errors have detailed explanations: E0117, E0321, E0371.
-error[E0371]: the object type `(dyn Object + Marker2 + 'static)` automatically implements the trait `Marker1`
- --> $DIR/coherence-impl-trait-for-marker-trait-positive.rs:15:1
- |
-LL | impl Marker1 for dyn Object + Marker2 { }
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `(dyn Object + Marker2 + 'static)` automatically implements trait `Marker1`
-
-error[E0371]: the object type `(dyn Object + Marker2 + 'static)` automatically implements the trait `Marker2`
- --> $DIR/coherence-impl-trait-for-marker-trait-positive.rs:17:1
- |
-LL | impl Marker2 for dyn Object + Marker2 { }
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `(dyn Object + Marker2 + 'static)` automatically implements trait `Marker2`
-
error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
--> $DIR/coherence-impl-trait-for-marker-trait-positive.rs:23:1
|
LL | unsafe impl Send for dyn Object + Marker2 {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't implement cross-crate trait with a default impl for non-struct/enum type
+error[E0371]: the object type `(dyn Object + Marker2 + 'static)` automatically implements the trait `Marker1`
+ --> $DIR/coherence-impl-trait-for-marker-trait-positive.rs:15:1
+ |
+LL | impl Marker1 for dyn Object + Marker2 { }
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `(dyn Object + Marker2 + 'static)` automatically implements trait `Marker1`
+
+error[E0371]: the object type `(dyn Object + Marker2 + 'static)` automatically implements the trait `Marker2`
+ --> $DIR/coherence-impl-trait-for-marker-trait-positive.rs:17:1
+ |
+LL | impl Marker2 for dyn Object + Marker2 { }
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `(dyn Object + Marker2 + 'static)` automatically implements trait `Marker2`
+
error: aborting due to 5 previous errors
Some errors have detailed explanations: E0117, E0321, E0371.
use std::marker::Copy;
impl Copy for i32 {}
-//~^ ERROR E0119
-//~| ERROR E0117
+//~^ ERROR E0117
enum TestE {
A
}
//~^ ERROR E0206
//~| ERROR E0117
impl Copy for &'static [NotSync] {}
-//~^ ERROR E0119
-//~| ERROR E0117
+//~^ ERROR E0117
fn main() {
}
-error[E0119]: conflicting implementations of trait `std::marker::Copy` for type `i32`
- --> $DIR/coherence-impls-copy.rs:5:1
- |
-LL | impl Copy for i32 {}
- | ^^^^^^^^^^^^^^^^^
- |
- = note: conflicting implementation in crate `core`:
- - impl Copy for i32;
-
-error[E0119]: conflicting implementations of trait `std::marker::Copy` for type `&NotSync`
- --> $DIR/coherence-impls-copy.rs:29:1
- |
-LL | impl Copy for &'static NotSync {}
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = note: conflicting implementation in crate `core`:
- - impl<T> Copy for &T
- where T: ?Sized;
-
-error[E0119]: conflicting implementations of trait `std::marker::Copy` for type `&[NotSync]`
- --> $DIR/coherence-impls-copy.rs:34:1
- |
-LL | impl Copy for &'static [NotSync] {}
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = note: conflicting implementation in crate `core`:
- - impl<T> Copy for &T
- where T: ?Sized;
-
-error[E0206]: the trait `Copy` may not be implemented for this type
- --> $DIR/coherence-impls-copy.rs:22:15
- |
-LL | impl Copy for &'static mut MyType {}
- | ^^^^^^^^^^^^^^^^^^^ type is not a structure or enumeration
-
-error[E0206]: the trait `Copy` may not be implemented for this type
- --> $DIR/coherence-impls-copy.rs:26:15
- |
-LL | impl Copy for (MyType, MyType) {}
- | ^^^^^^^^^^^^^^^^ type is not a structure or enumeration
-
-error[E0206]: the trait `Copy` may not be implemented for this type
- --> $DIR/coherence-impls-copy.rs:31:15
- |
-LL | impl Copy for [MyType] {}
- | ^^^^^^^^ type is not a structure or enumeration
-
error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
--> $DIR/coherence-impls-copy.rs:5:1
|
= note: define and implement a trait or new type instead
error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
- --> $DIR/coherence-impls-copy.rs:26:1
+ --> $DIR/coherence-impls-copy.rs:25:1
|
LL | impl Copy for (MyType, MyType) {}
| ^^^^^^^^^^^^^^----------------
= note: define and implement a trait or new type instead
error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
- --> $DIR/coherence-impls-copy.rs:31:1
+ --> $DIR/coherence-impls-copy.rs:30:1
|
LL | impl Copy for [MyType] {}
| ^^^^^^^^^^^^^^--------
= note: define and implement a trait or new type instead
error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
- --> $DIR/coherence-impls-copy.rs:34:1
+ --> $DIR/coherence-impls-copy.rs:33:1
|
LL | impl Copy for &'static [NotSync] {}
| ^^^^^^^^^^^^^^------------------
|
= note: define and implement a trait or new type instead
-error: aborting due to 10 previous errors
+error[E0119]: conflicting implementations of trait `std::marker::Copy` for type `&NotSync`
+ --> $DIR/coherence-impls-copy.rs:28:1
+ |
+LL | impl Copy for &'static NotSync {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: conflicting implementation in crate `core`:
+ - impl<T> Copy for &T
+ where T: ?Sized;
+
+error[E0206]: the trait `Copy` may not be implemented for this type
+ --> $DIR/coherence-impls-copy.rs:21:15
+ |
+LL | impl Copy for &'static mut MyType {}
+ | ^^^^^^^^^^^^^^^^^^^ type is not a structure or enumeration
+
+error[E0206]: the trait `Copy` may not be implemented for this type
+ --> $DIR/coherence-impls-copy.rs:25:15
+ |
+LL | impl Copy for (MyType, MyType) {}
+ | ^^^^^^^^^^^^^^^^ type is not a structure or enumeration
+
+error[E0206]: the trait `Copy` may not be implemented for this type
+ --> $DIR/coherence-impls-copy.rs:30:15
+ |
+LL | impl Copy for [MyType] {}
+ | ^^^^^^^^ type is not a structure or enumeration
+
+error: aborting due to 8 previous errors
Some errors have detailed explanations: E0117, E0119, E0206.
For more information about an error, try `rustc --explain E0117`.
//~^ ERROR E0117
unsafe impl Send for &'static [NotSync] {}
-//~^ ERROR conflicting implementations of trait
-//~| ERROR only traits defined in the current crate
+//~^ ERROR only traits defined in the current crate
fn main() {}
-error[E0119]: conflicting implementations of trait `std::marker::Send` for type `&[NotSync]`
- --> $DIR/coherence-impls-send.rs:25:1
- |
-LL | unsafe impl Send for &'static [NotSync] {}
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = note: conflicting implementation in crate `core`:
- - impl<T> Send for &T
- where T: Sync, T: ?Sized;
- = note: upstream crates may add a new impl of trait `std::marker::Sync` for type `[NotSync]` in future versions
-
error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
--> $DIR/coherence-impls-send.rs:16:1
|
|
= note: define and implement a trait or new type instead
-error: aborting due to 5 previous errors
+error: aborting due to 4 previous errors
-Some errors have detailed explanations: E0117, E0119, E0321.
+Some errors have detailed explanations: E0117, E0321.
For more information about an error, try `rustc --explain E0117`.
+error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
+ --> $DIR/coherence-impls-sized.rs:20:1
+ |
+LL | impl Sized for (MyType, MyType) {}
+ | ^^^^^^^^^^^^^^^----------------
+ | | |
+ | | this is not defined in the current crate because tuples are always foreign
+ | impl doesn't use only types from inside the current crate
+ |
+ = note: define and implement a trait or new type instead
+
+error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
+ --> $DIR/coherence-impls-sized.rs:27:1
+ |
+LL | impl Sized for [MyType] {}
+ | ^^^^^^^^^^^^^^^--------
+ | | |
+ | | this is not defined in the current crate because slices are always foreign
+ | impl doesn't use only types from inside the current crate
+ |
+ = note: define and implement a trait or new type instead
+
+error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
+ --> $DIR/coherence-impls-sized.rs:31:1
+ |
+LL | impl Sized for &'static [NotSync] {}
+ | ^^^^^^^^^^^^^^^------------------
+ | | |
+ | | this is not defined in the current crate because slices are always foreign
+ | impl doesn't use only types from inside the current crate
+ |
+ = note: define and implement a trait or new type instead
+
error[E0322]: explicit impls for the `Sized` trait are not permitted
--> $DIR/coherence-impls-sized.rs:14:1
|
LL | impl Sized for &'static [NotSync] {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl of 'Sized' not allowed
-error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
- --> $DIR/coherence-impls-sized.rs:20:1
- |
-LL | impl Sized for (MyType, MyType) {}
- | ^^^^^^^^^^^^^^^----------------
- | | |
- | | this is not defined in the current crate because tuples are always foreign
- | impl doesn't use only types from inside the current crate
- |
- = note: define and implement a trait or new type instead
-
-error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
- --> $DIR/coherence-impls-sized.rs:27:1
- |
-LL | impl Sized for [MyType] {}
- | ^^^^^^^^^^^^^^^--------
- | | |
- | | this is not defined in the current crate because slices are always foreign
- | impl doesn't use only types from inside the current crate
- |
- = note: define and implement a trait or new type instead
-
-error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
- --> $DIR/coherence-impls-sized.rs:31:1
- |
-LL | impl Sized for &'static [NotSync] {}
- | ^^^^^^^^^^^^^^^------------------
- | | |
- | | this is not defined in the current crate because slices are always foreign
- | impl doesn't use only types from inside the current crate
- |
- = note: define and implement a trait or new type instead
-
error: aborting due to 9 previous errors
Some errors have detailed explanations: E0117, E0322.
error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
- --> $DIR/coherence-orphan.rs:10:1
+ --> $DIR/coherence-orphan.rs:17:1
|
-LL | impl TheTrait<usize> for isize { }
- | ^^^^^---------------^^^^^-----
- | | | |
- | | | `isize` is not defined in the current crate
- | | `usize` is not defined in the current crate
+LL | impl !Send for Vec<isize> { }
+ | ^^^^^^^^^^^^^^^----------
+ | | |
+ | | `Vec` is not defined in the current crate
| impl doesn't use only types from inside the current crate
|
= note: define and implement a trait or new type instead
error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
- --> $DIR/coherence-orphan.rs:17:1
+ --> $DIR/coherence-orphan.rs:10:1
|
-LL | impl !Send for Vec<isize> { }
- | ^^^^^^^^^^^^^^^----------
- | | |
- | | `Vec` is not defined in the current crate
+LL | impl TheTrait<usize> for isize { }
+ | ^^^^^---------------^^^^^-----
+ | | | |
+ | | | `isize` is not defined in the current crate
+ | | `usize` is not defined in the current crate
| impl doesn't use only types from inside the current crate
|
= note: define and implement a trait or new type instead
// ignore-sgx no processes
#![feature(process_exec, rustc_private)]
+extern crate libc;
+
use std::env;
use std::io::Error;
use std::os::unix::process::CommandExt;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
-#[cfg(not(target_os = "linux"))]
-fn getpid() -> u32 {
- use std::process;
- process::id()
-}
-
-/// We need to directly use the getpid syscall instead of using `process::id()`
-/// because the libc wrapper might return incorrect values after a process was
-/// forked.
-#[cfg(target_os = "linux")]
-fn getpid() -> u32 {
- extern crate libc;
- unsafe {
- libc::syscall(libc::SYS_getpid) as _
- }
-}
-
fn main() {
if let Some(arg) = env::args().nth(1) {
match &arg[..] {
};
assert_eq!(output.raw_os_error(), Some(102));
- let pid = getpid();
+ let pid = unsafe { libc::getpid() };
+ assert!(pid >= 0);
let output = unsafe {
Command::new(&me)
.arg("empty")
.pre_exec(move || {
- let child = getpid();
+ let child = libc::getpid();
+ assert!(child >= 0);
assert!(pid != child);
Ok(())
})
-// Check that you can't dereference raw pointers in constants.
+// Check that you can't dereference invalid raw pointers in constants.
fn main() {
static C: u64 = unsafe {*(0xdeadbeef as *const u64)};
- //~^ ERROR dereferencing raw pointers in statics is unstable
+ //~^ ERROR could not evaluate static initializer
println!("{}", C);
}
-error[E0658]: dereferencing raw pointers in statics is unstable
+error[E0080]: could not evaluate static initializer
--> $DIR/const-deref-ptr.rs:4:29
|
LL | static C: u64 = unsafe {*(0xdeadbeef as *const u64)};
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = note: see issue #51911 <https://github.com/rust-lang/rust/issues/51911> for more information
- = help: add `#![feature(const_raw_ptr_deref)]` to the crate attributes to enable
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ 0xdeadbeef is not a valid pointer
error: aborting due to previous error
-For more information about this error, try `rustc --explain E0658`.
+For more information about this error, try `rustc --explain E0080`.
+++ /dev/null
-// New test for #53818: modifying static memory at compile-time is not allowed.
-// The test should never compile successfully
-
-#![feature(const_raw_ptr_deref, const_mut_refs)]
-
-use std::cell::UnsafeCell;
-
-struct Foo(UnsafeCell<u32>);
-
-unsafe impl Send for Foo {}
-unsafe impl Sync for Foo {}
-
-static FOO: Foo = Foo(UnsafeCell::new(42));
-
-static BAR: () = unsafe {
- *FOO.0.get() = 5; //~ ERROR
-};
-
-fn main() {}
+++ /dev/null
-error[E0080]: could not evaluate static initializer
- --> $DIR/assign-to-static-within-other-static-2.rs:16:5
- |
-LL | *FOO.0.get() = 5;
- | ^^^^^^^^^^^^^^^^ modifying a static's initial value from another static's initializer
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0080`.
// New test for #53818: modifying static memory at compile-time is not allowed.
// The test should never compile successfully
-#![feature(const_raw_ptr_deref)]
-
use std::cell::UnsafeCell;
static mut FOO: u32 = 42;
error[E0080]: could not evaluate static initializer
- --> $DIR/assign-to-static-within-other-static.rs:10:5
+ --> $DIR/assign-to-static-within-other-static.rs:8:5
|
LL | FOO = 5;
| ^^^^^^^ modifying a static's initial value from another static's initializer
-#![feature(const_raw_ptr_deref)]
-
fn main() {}
// fine
error[E0080]: evaluation of constant value failed
- --> $DIR/const_raw_ptr_ops2.rs:9:26
+ --> $DIR/const_raw_ptr_ops2.rs:7:26
|
LL | const Z2: i32 = unsafe { *(42 as *const i32) };
| ^^^^^^^^^^^^^^^^^^^ 0x2a is not a valid pointer
error[E0080]: evaluation of constant value failed
- --> $DIR/const_raw_ptr_ops2.rs:11:26
+ --> $DIR/const_raw_ptr_ops2.rs:9:26
|
LL | const Z3: i32 = unsafe { *(44 as *const i32) };
| ^^^^^^^^^^^^^^^^^^^ 0x2c is not a valid pointer
-#![feature(const_raw_ptr_deref)]
-
use std::mem;
// Make sure we error with the right kind of error on a too large slice.
error[E0080]: evaluation of constant value failed
- --> $DIR/dangling.rs:8:16
+ --> $DIR/dangling.rs:6:16
|
LL | let _val = &*slice;
| ^^^^^^^ invalid metadata in wide pointer: slice is bigger than largest supported object
#![feature(core_intrinsics)]
#![feature(const_heap)]
-#![feature(const_raw_ptr_deref)]
#![feature(const_mut_refs)]
use std::intrinsics;
error[E0080]: evaluation of constant value failed
- --> $DIR/alloc_intrinsic_errors.rs:10:17
+ --> $DIR/alloc_intrinsic_errors.rs:9:17
|
LL | const FOO: i32 = foo();
- | ----- inside `FOO` at $DIR/alloc_intrinsic_errors.rs:7:18
+ | ----- inside `FOO` at $DIR/alloc_intrinsic_errors.rs:6:18
...
LL | let _ = intrinsics::const_allocate(4, 3) as * mut i32;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
| align has to be a power of 2, `3` is not a power of 2
- | inside `foo` at $DIR/alloc_intrinsic_errors.rs:10:17
+ | inside `foo` at $DIR/alloc_intrinsic_errors.rs:9:17
error: aborting due to previous error
// run-pass
#![feature(core_intrinsics)]
#![feature(const_heap)]
-#![feature(const_raw_ptr_deref)]
#![feature(const_mut_refs)]
use std::intrinsics;
#![feature(core_intrinsics)]
#![feature(const_heap)]
-#![feature(const_raw_ptr_deref)]
#![feature(const_mut_refs)]
use std::intrinsics;
error: untyped pointers are not allowed in constant
- --> $DIR/alloc_intrinsic_nontransient_fail.rs:7:1
+ --> $DIR/alloc_intrinsic_nontransient_fail.rs:6:1
|
LL | const FOO: *const i32 = foo();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// run-pass
#![feature(core_intrinsics)]
#![feature(const_heap)]
-#![feature(const_raw_ptr_deref)]
#![feature(const_mut_refs)]
use std::intrinsics;
error[E0080]: it is undefined behavior to use this value
- --> $DIR/alloc_intrinsic_uninit.rs:9:1
+ --> $DIR/alloc_intrinsic_uninit.rs:8:1
|
LL | const BAR: &i32 = unsafe { &*(intrinsics::const_allocate(4, 4) as *mut i32) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed at .<deref>: encountered uninitialized bytes, but expected initialized plain (non-pointer) bytes
error[E0080]: it is undefined behavior to use this value
- --> $DIR/alloc_intrinsic_uninit.rs:9:1
+ --> $DIR/alloc_intrinsic_uninit.rs:8:1
|
LL | const BAR: &i32 = unsafe { &*(intrinsics::const_allocate(4, 4) as *mut i32) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed at .<deref>: encountered uninitialized bytes, but expected initialized plain (non-pointer) bytes
// compile-test
#![feature(core_intrinsics)]
#![feature(const_heap)]
-#![feature(const_raw_ptr_deref)]
#![feature(const_mut_refs)]
use std::intrinsics;
#![feature(core_intrinsics)]
#![feature(const_heap)]
-#![feature(const_raw_ptr_deref)]
#![feature(const_mut_refs)]
use std::intrinsics;
error: untyped pointers are not allowed in constant
- --> $DIR/alloc_intrinsic_untyped.rs:7:1
+ --> $DIR/alloc_intrinsic_untyped.rs:6:1
|
LL | const BAR: *mut i32 = unsafe { intrinsics::const_allocate(4, 4) as *mut i32};
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// New test for #53818: modifying static memory at compile-time is not allowed.
// The test should never compile successfully
-#![feature(const_raw_ptr_deref)]
+#![feature(const_mut_refs)]
use std::cell::UnsafeCell;
static BAR: () = unsafe {
*FOO.0.get() = 5;
- //~^ mutation through a reference
+ //~^ ERROR could not evaluate static initializer
};
fn main() {
-error[E0658]: mutation through a reference is not allowed in statics
+error[E0080]: could not evaluate static initializer
--> $DIR/mod-static-with-const-fn.rs:16:5
|
LL | *FOO.0.get() = 5;
- | ^^^^^^^^^^^^^^^^
- |
- = note: see issue #57349 <https://github.com/rust-lang/rust/issues/57349> for more information
- = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable
+ | ^^^^^^^^^^^^^^^^ modifying a static's initial value from another static's initializer
error: aborting due to previous error
-For more information about this error, try `rustc --explain E0658`.
+For more information about this error, try `rustc --explain E0080`.
// Test for the behavior described in <https://github.com/rust-lang/rust/issues/87184>.
-#![feature(const_mut_refs, const_raw_ptr_deref)]
+#![feature(const_mut_refs)]
const PARTIAL_OVERWRITE: () = {
let mut p = &42;
-#![feature(const_raw_ptr_deref)]
-
fn main() {
let x: &'static bool = &(42 as *const i32 == 43 as *const i32);
//~^ ERROR temporary value dropped while borrowed
error[E0716]: temporary value dropped while borrowed
- --> $DIR/promoted_raw_ptr_ops.rs:4:29
+ --> $DIR/promoted_raw_ptr_ops.rs:2:29
|
LL | let x: &'static bool = &(42 as *const i32 == 43 as *const i32);
| ------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use
| - temporary value is freed at the end of this statement
error[E0716]: temporary value dropped while borrowed
- --> $DIR/promoted_raw_ptr_ops.rs:6:30
+ --> $DIR/promoted_raw_ptr_ops.rs:4:30
|
LL | let y: &'static usize = &(&1 as *const i32 as usize + 1);
| -------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use
| - temporary value is freed at the end of this statement
error[E0716]: temporary value dropped while borrowed
- --> $DIR/promoted_raw_ptr_ops.rs:8:28
+ --> $DIR/promoted_raw_ptr_ops.rs:6:28
|
LL | let z: &'static i32 = &(unsafe { *(42 as *const i32) });
| ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use
| - temporary value is freed at the end of this statement
error[E0716]: temporary value dropped while borrowed
- --> $DIR/promoted_raw_ptr_ops.rs:10:29
+ --> $DIR/promoted_raw_ptr_ops.rs:8:29
|
LL | let a: &'static bool = &(main as fn() == main as fn());
| ------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use
#![feature(const_mut_refs)]
#![feature(raw_ref_op)]
-#![feature(const_raw_ptr_deref)]
const NULL: *mut i32 = std::ptr::null_mut();
const A: *const i32 = &4;
error[E0764]: mutable references are not allowed in the final value of constants
- --> $DIR/mut_ref_in_final.rs:11:21
+ --> $DIR/mut_ref_in_final.rs:10:21
|
LL | const B: *mut i32 = &mut 4;
| ^^^^^^
error[E0716]: temporary value dropped while borrowed
- --> $DIR/mut_ref_in_final.rs:17:40
+ --> $DIR/mut_ref_in_final.rs:16:40
|
LL | const B3: Option<&mut i32> = Some(&mut 42);
| ----------^^-
| using this value as a constant requires that borrow lasts for `'static`
error[E0716]: temporary value dropped while borrowed
- --> $DIR/mut_ref_in_final.rs:20:42
+ --> $DIR/mut_ref_in_final.rs:19:42
|
LL | const B4: Option<&mut i32> = helper(&mut 42);
| ------------^^-
| using this value as a constant requires that borrow lasts for `'static`
error[E0716]: temporary value dropped while borrowed
- --> $DIR/mut_ref_in_final.rs:35:65
+ --> $DIR/mut_ref_in_final.rs:34:65
|
LL | const FOO: NotAMutex<&mut i32> = NotAMutex(UnsafeCell::new(&mut 42));
| -------------------------------^^--
| using this value as a constant requires that borrow lasts for `'static`
error[E0716]: temporary value dropped while borrowed
- --> $DIR/mut_ref_in_final.rs:38:67
+ --> $DIR/mut_ref_in_final.rs:37:67
|
LL | static FOO2: NotAMutex<&mut i32> = NotAMutex(UnsafeCell::new(&mut 42));
| -------------------------------^^--
| using this value as a static requires that borrow lasts for `'static`
error[E0716]: temporary value dropped while borrowed
- --> $DIR/mut_ref_in_final.rs:41:71
+ --> $DIR/mut_ref_in_final.rs:40:71
|
LL | static mut FOO3: NotAMutex<&mut i32> = NotAMutex(UnsafeCell::new(&mut 42));
| -------------------------------^^--
#![feature(const_mut_refs)]
#![feature(raw_ref_op)]
-#![feature(const_raw_ptr_deref)]
// This file checks that our dynamic checks catch things that the static checks miss.
// We do not have static checks for these, because we do not look into function bodies.
error[E0080]: evaluation of constant value failed
- --> $DIR/mut_ref_in_final_dynamic_check.rs:14:10
+ --> $DIR/mut_ref_in_final_dynamic_check.rs:13:10
|
LL | Some(&mut *(42 as *mut i32))
| ^^^^^^^^^^^^^^^^^^^^^^
| |
| 0x2a is not a valid pointer
- | inside `helper` at $DIR/mut_ref_in_final_dynamic_check.rs:14:10
+ | inside `helper` at $DIR/mut_ref_in_final_dynamic_check.rs:13:10
...
LL | const A: Option<&mut i32> = helper();
- | -------- inside `A` at $DIR/mut_ref_in_final_dynamic_check.rs:19:29
+ | -------- inside `A` at $DIR/mut_ref_in_final_dynamic_check.rs:18:29
error: encountered dangling pointer in final constant
- --> $DIR/mut_ref_in_final_dynamic_check.rs:26:1
+ --> $DIR/mut_ref_in_final_dynamic_check.rs:25:1
|
LL | const B: Option<&mut i32> = helper2();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
const WRITE: () = unsafe {
*std::ptr::null_mut() = 0;
- //~^ ERROR dereferencing raw pointers in constants is unstable
- //~| HELP add `#![feature(const_raw_ptr_deref)]` to the crate attributes to enable
+ //~^ ERROR dereferencing raw mutable pointers in constants is unstable
+ //~| HELP add `#![feature(const_mut_refs)]` to the crate attributes to enable
};
fn main() {}
-error[E0658]: dereferencing raw pointers in constants is unstable
+error[E0658]: dereferencing raw mutable pointers in constants is unstable
--> $DIR/const-suggest-feature.rs:2:5
|
LL | *std::ptr::null_mut() = 0;
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
- = note: see issue #51911 <https://github.com/rust-lang/rust/issues/51911> for more information
- = help: add `#![feature(const_raw_ptr_deref)]` to the crate attributes to enable
+ = note: see issue #57349 <https://github.com/rust-lang/rust/issues/57349> for more information
+ = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable
error: aborting due to previous error
-// build-pass (FIXME(62277): could be check-pass?)
-
-#![feature(const_raw_ptr_deref)]
+// check-pass
const FOO: &str = unsafe { &*(1_usize as *const [u8; 0] as *const [u8] as *const str) };
// check-pass
-#![feature(const_raw_ptr_deref)]
use std::ptr;
const fn bad_const_fn_deref_raw(x: *mut usize) -> &'static usize { unsafe { &*x } }
-//~^ dereferencing raw pointers in constant functions
+//~^ dereferencing raw mutable pointers in constant functions
const unsafe fn bad_const_unsafe_deref_raw(x: *mut usize) -> usize { *x }
-//~^ dereferencing raw pointers in constant functions
+//~^ dereferencing raw mutable pointers in constant functions
const unsafe fn bad_const_unsafe_deref_raw_ref(x: *mut usize) -> &'static usize { &*x }
-//~^ dereferencing raw pointers in constant functions
+//~^ dereferencing raw mutable pointers in constant functions
fn main() {}
-error[E0658]: dereferencing raw pointers in constant functions is unstable
+error[E0658]: dereferencing raw mutable pointers in constant functions is unstable
--> $DIR/min_const_fn_unsafe_bad.rs:1:77
|
LL | const fn bad_const_fn_deref_raw(x: *mut usize) -> &'static usize { unsafe { &*x } }
| ^^^
|
- = note: see issue #51911 <https://github.com/rust-lang/rust/issues/51911> for more information
- = help: add `#![feature(const_raw_ptr_deref)]` to the crate attributes to enable
+ = note: see issue #57349 <https://github.com/rust-lang/rust/issues/57349> for more information
+ = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable
-error[E0658]: dereferencing raw pointers in constant functions is unstable
+error[E0658]: dereferencing raw mutable pointers in constant functions is unstable
--> $DIR/min_const_fn_unsafe_bad.rs:4:70
|
LL | const unsafe fn bad_const_unsafe_deref_raw(x: *mut usize) -> usize { *x }
| ^^
|
- = note: see issue #51911 <https://github.com/rust-lang/rust/issues/51911> for more information
- = help: add `#![feature(const_raw_ptr_deref)]` to the crate attributes to enable
+ = note: see issue #57349 <https://github.com/rust-lang/rust/issues/57349> for more information
+ = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable
-error[E0658]: dereferencing raw pointers in constant functions is unstable
+error[E0658]: dereferencing raw mutable pointers in constant functions is unstable
--> $DIR/min_const_fn_unsafe_bad.rs:7:83
|
LL | const unsafe fn bad_const_unsafe_deref_raw_ref(x: *mut usize) -> &'static usize { &*x }
| ^^^
|
- = note: see issue #51911 <https://github.com/rust-lang/rust/issues/51911> for more information
- = help: add `#![feature(const_raw_ptr_deref)]` to the crate attributes to enable
+ = note: see issue #57349 <https://github.com/rust-lang/rust/issues/57349> for more information
+ = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable
error: aborting due to 3 previous errors
|
LL | unsafe { *(&FOO as *const _ as *const usize) }
| ^^^
-help: skipping check for `const_raw_ptr_deref` feature
- --> $DIR/const_refers_to_static.rs:18:14
- |
-LL | unsafe { *(&FOO as *const _ as *const usize) }
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: skipping check that does not even have a feature gate
--> $DIR/const_refers_to_static.rs:22:32
|
|
LL | unsafe { &*(&FOO as *const _ as *const usize) }
| ^^^
-help: skipping check for `const_raw_ptr_deref` feature
- --> $DIR/const_refers_to_static2.rs:14:14
- |
-LL | unsafe { &*(&FOO as *const _ as *const usize) }
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: skipping check that does not even have a feature gate
--> $DIR/const_refers_to_static2.rs:21:6
|
|
LL | unsafe { &*(&FOO as *const _ as *const usize) }
| ^^^
-help: skipping check for `const_raw_ptr_deref` feature
- --> $DIR/const_refers_to_static2.rs:14:14
- |
-LL | unsafe { &*(&FOO as *const _ as *const usize) }
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: skipping check that does not even have a feature gate
--> $DIR/const_refers_to_static2.rs:21:6
|
// run-pass
-#![feature(const_raw_ptr_deref)]
#![feature(const_ptr_offset_from)]
struct Struct {
-#![feature(const_raw_ptr_deref)]
#![feature(const_ptr_offset_from)]
#![feature(core_intrinsics)]
error[E0080]: evaluation of constant value failed
- --> $DIR/offset_from_ub.rs:18:27
+ --> $DIR/offset_from_ub.rs:17:27
|
LL | let offset = unsafe { ptr_offset_from(field_ptr, base_ptr) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ptr_offset_from cannot compute offset of pointers into different allocations.
| 0x2a is not a valid pointer
| inside `ptr::const_ptr::<impl *const u8>::offset_from` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
|
- ::: $DIR/offset_from_ub.rs:24:14
+ ::: $DIR/offset_from_ub.rs:23:14
|
LL | unsafe { (42 as *const u8).offset_from(&5u8) as usize }
- | ----------------------------------- inside `NOT_PTR` at $DIR/offset_from_ub.rs:24:14
+ | ----------------------------------- inside `NOT_PTR` at $DIR/offset_from_ub.rs:23:14
error[E0080]: evaluation of constant value failed
- --> $DIR/offset_from_ub.rs:31:14
+ --> $DIR/offset_from_ub.rs:30:14
|
LL | unsafe { ptr_offset_from(field_ptr, base_ptr as *const u16) }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ exact_div: 1_isize cannot be divided by 2_isize without remainder
error[E0080]: evaluation of constant value failed
- --> $DIR/offset_from_ub.rs:37:14
+ --> $DIR/offset_from_ub.rs:36:14
|
LL | unsafe { ptr_offset_from(ptr, ptr) }
| ^^^^^^^^^^^^^^^^^^^^^^^^^ null pointer is not a valid pointer for this operation
error[E0080]: evaluation of constant value failed
- --> $DIR/offset_from_ub.rs:44:14
+ --> $DIR/offset_from_ub.rs:43:14
|
LL | unsafe { ptr_offset_from(ptr2, ptr1) }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ 0x10 is not a valid pointer
core_intrinsics,
const_raw_ptr_comparison,
const_ptr_offset,
- const_raw_ptr_deref
)]
const FOO: &usize = &42;
| pointer arithmetic failed: alloc3 has size $WORD, so pointer to $TWO_WORDS bytes starting at offset 0 is out-of-bounds
| inside `ptr::const_ptr::<impl *const usize>::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
|
- ::: $DIR/ptr_comparisons.rs:60:34
+ ::: $DIR/ptr_comparisons.rs:59:34
|
LL | const _: *const usize = unsafe { (FOO as *const usize).offset(2) };
- | ------------------------------- inside `_` at $DIR/ptr_comparisons.rs:60:34
+ | ------------------------------- inside `_` at $DIR/ptr_comparisons.rs:59:34
error[E0080]: evaluation of constant value failed
- --> $DIR/ptr_comparisons.rs:63:33
+ --> $DIR/ptr_comparisons.rs:62:33
|
LL | unsafe { std::ptr::addr_of!((*(FOO as *const usize as *const [u8; 1000]))[999]) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ dereferencing pointer failed: alloc3 has size $WORD, so pointer to 1000 bytes starting at offset 0 is out-of-bounds
error: any use of this value will cause an error
- --> $DIR/ptr_comparisons.rs:67:27
+ --> $DIR/ptr_comparisons.rs:66:27
|
LL | const _: usize = unsafe { std::mem::transmute::<*const usize, usize>(FOO) + 4 };
| --------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^---
= note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800>
error: any use of this value will cause an error
- --> $DIR/ptr_comparisons.rs:72:27
+ --> $DIR/ptr_comparisons.rs:71:27
|
LL | const _: usize = unsafe { *std::mem::transmute::<&&usize, &usize>(&FOO) + 4 };
| --------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^---
// stderr-per-bitwidth
-#![feature(const_raw_ptr_deref, never_type)]
+#![feature(never_type)]
const _: &[!; 1] = unsafe { &*(1_usize as *const [!; 1]) }; //~ ERROR undefined behavior
const _: &[!; 0] = unsafe { &*(1_usize as *const [!; 0]) }; // ok
+++ /dev/null
-error[E0658]: dereferencing raw pointers in constants is unstable
- --> $DIR/write_to_mut_ref_dest.rs:11:18
- |
-LL | unsafe { *b = 5; }
- | ^^^^^^
- |
- = note: see issue #51911 <https://github.com/rust-lang/rust/issues/51911> for more information
- = help: add `#![feature(const_raw_ptr_deref)]` to the crate attributes to enable
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0658`.
// revisions: stock mut_refs
+//[mut_refs] check-pass
#![cfg_attr(mut_refs, feature(const_mut_refs))]
let mut a = 42;
{
let b: *mut u32 = &mut a; //[stock]~ ERROR mutable references are not allowed in constants
- unsafe { *b = 5; } //~ ERROR dereferencing raw pointers in constants
+ unsafe { *b = 5; } //[stock]~ ERROR dereferencing raw mutable pointers in constants
}
&{a}
};
error[E0658]: mutable references are not allowed in constants
- --> $DIR/write_to_mut_ref_dest.rs:10:27
+ --> $DIR/write_to_mut_ref_dest.rs:11:27
|
LL | let b: *mut u32 = &mut a;
| ^^^^^^
= note: see issue #57349 <https://github.com/rust-lang/rust/issues/57349> for more information
= help: add `#![feature(const_mut_refs)]` to the crate attributes to enable
-error[E0658]: dereferencing raw pointers in constants is unstable
- --> $DIR/write_to_mut_ref_dest.rs:11:18
+error[E0658]: dereferencing raw mutable pointers in constants is unstable
+ --> $DIR/write_to_mut_ref_dest.rs:12:18
|
LL | unsafe { *b = 5; }
| ^^^^^^
|
- = note: see issue #51911 <https://github.com/rust-lang/rust/issues/51911> for more information
- = help: add `#![feature(const_raw_ptr_deref)]` to the crate attributes to enable
+ = note: see issue #57349 <https://github.com/rust-lang/rust/issues/57349> for more information
+ = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable
error: aborting due to 2 previous errors
LL | impl Drop for Nonexistent {
| ^^^^^^^^^^^ not found in this scope
-error[E0120]: the `Drop` trait may only be implemented for structs, enums, and unions
- --> $DIR/drop-on-non-struct.rs:1:19
- |
-LL | impl<'a> Drop for &'a mut isize {
- | ^^^^^^^^^^^^^ must be a struct, enum, or union
-
error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
--> $DIR/drop-on-non-struct.rs:1:1
|
|
= note: define and implement a trait or new type instead
+error[E0120]: the `Drop` trait may only be implemented for structs, enums, and unions
+ --> $DIR/drop-on-non-struct.rs:1:19
+ |
+LL | impl<'a> Drop for &'a mut isize {
+ | ^^^^^^^^^^^^^ must be a struct, enum, or union
+
error: aborting due to 3 previous errors
Some errors have detailed explanations: E0117, E0120, E0412.
--- /dev/null
+struct Wrapper<'a, T>(&'a T)
+where
+ T: 'a;
+
+impl<'a, T> Drop for Wrapper<'a, T>
+where
+ T: 'static,
+ //~^ error: `Drop` impl requires `T: 'static` but the struct it is implemented for does not
+{
+ fn drop(&mut self) {}
+}
+
+fn main() {}
--- /dev/null
+error[E0367]: `Drop` impl requires `T: 'static` but the struct it is implemented for does not
+ --> $DIR/relate_lt_in_type_outlives_bound.rs:7:8
+ |
+LL | T: 'static,
+ | ^^^^^^^
+ |
+note: the implementor must specify the same requirement
+ --> $DIR/relate_lt_in_type_outlives_bound.rs:1:1
+ |
+LL | / struct Wrapper<'a, T>(&'a T)
+LL | | where
+LL | | T: 'a;
+ | |__________^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0367`.
-error[E0783]: trait objects without an explicit `dyn` are deprecated
+error[E0782]: trait objects without an explicit `dyn` are deprecated
--> $DIR/dyn-trait-sugg-2021.rs:10:5
|
LL | Foo::hi(123);
error: aborting due to previous error
-For more information about this error, try `rustc --explain E0783`.
+For more information about this error, try `rustc --explain E0782`.
// run-pass
-#![feature(arbitrary_enum_discriminant, const_raw_ptr_deref, test)]
+#![feature(arbitrary_enum_discriminant, test)]
extern crate test;
-error[E0120]: the `Drop` trait may only be implemented for structs, enums, and unions
- --> $DIR/E0117.rs:1:15
- |
-LL | impl Drop for u32 {}
- | ^^^ must be a struct, enum, or union
-
error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
--> $DIR/E0117.rs:1:1
|
|
= note: define and implement a trait or new type instead
+error[E0120]: the `Drop` trait may only be implemented for structs, enums, and unions
+ --> $DIR/E0117.rs:1:15
+ |
+LL | impl Drop for u32 {}
+ | ^^^ must be a struct, enum, or union
+
error: aborting due to 2 previous errors
Some errors have detailed explanations: E0117, E0120.
-type Foo = [u8; 256];
-
-impl Copy for Foo { }
-//~^ ERROR the trait `Copy` may not be implemented for this type
-//~| ERROR only traits defined in the current crate can be implemented for arbitrary types
-
#[derive(Copy, Clone)]
struct Bar;
error[E0206]: the trait `Copy` may not be implemented for this type
- --> $DIR/E0206.rs:3:15
- |
-LL | impl Copy for Foo { }
- | ^^^ type is not a structure or enumeration
-
-error[E0206]: the trait `Copy` may not be implemented for this type
- --> $DIR/E0206.rs:10:15
+ --> $DIR/E0206.rs:4:15
|
LL | impl Copy for &'static mut Bar { }
| ^^^^^^^^^^^^^^^^ type is not a structure or enumeration
-error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
- --> $DIR/E0206.rs:3:1
- |
-LL | impl Copy for Foo { }
- | ^^^^^^^^^^^^^^---
- | | |
- | | this is not defined in the current crate because arrays are always foreign
- | impl doesn't use only types from inside the current crate
- |
- = note: define and implement a trait or new type instead
-
-error: aborting due to 3 previous errors
+error: aborting due to previous error
-Some errors have detailed explanations: E0117, E0206.
-For more information about an error, try `rustc --explain E0117`.
+For more information about this error, try `rustc --explain E0206`.
-#![feature(const_raw_ptr_deref)]
+#![feature(const_mut_refs)]
-const REG_ADDR: *const u8 = 0x5f3759df as *const u8;
+const REG_ADDR: *mut u8 = 0x5f3759df as *mut u8;
const VALUE: u8 = unsafe { *REG_ADDR };
//~^ ERROR evaluation of constant value failed
-// gate-test-const_raw_ptr_deref
-
-const REG_ADDR: *const u8 = 0x5f3759df as *const u8;
+const REG_ADDR: *mut u8 = 0x5f3759df as *mut u8;
const VALUE: u8 = unsafe { *REG_ADDR };
-//~^ ERROR dereferencing raw pointers in constants is unstable
+//~^ ERROR dereferencing raw mutable pointers in constants is unstable
const unsafe fn unreachable() -> ! {
use std::convert::Infallible;
- const INFALLIBLE: *const Infallible = [].as_ptr();
+ const INFALLIBLE: *mut Infallible = &[] as *const [Infallible] as *const _ as _;
match *INFALLIBLE {}
- //~^ ERROR dereferencing raw pointers in constant functions is unstable
+ //~^ ERROR dereferencing raw mutable pointers in constant functions is unstable
const BAD: () = unsafe { match *INFALLIBLE {} };
- //~^ ERROR dereferencing raw pointers in constants is unstable
+ //~^ ERROR dereferencing raw mutable pointers in constants is unstable
}
fn main() {
-error[E0658]: dereferencing raw pointers in constants is unstable
- --> $DIR/E0396.rs:5:28
+error[E0658]: dereferencing raw mutable pointers in constants is unstable
+ --> $DIR/E0396.rs:3:28
|
LL | const VALUE: u8 = unsafe { *REG_ADDR };
| ^^^^^^^^^
|
- = note: see issue #51911 <https://github.com/rust-lang/rust/issues/51911> for more information
- = help: add `#![feature(const_raw_ptr_deref)]` to the crate attributes to enable
+ = note: see issue #57349 <https://github.com/rust-lang/rust/issues/57349> for more information
+ = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable
-error[E0658]: dereferencing raw pointers in constant functions is unstable
- --> $DIR/E0396.rs:12:11
+error[E0658]: dereferencing raw mutable pointers in constant functions is unstable
+ --> $DIR/E0396.rs:10:11
|
LL | match *INFALLIBLE {}
| ^^^^^^^^^^^
|
- = note: see issue #51911 <https://github.com/rust-lang/rust/issues/51911> for more information
- = help: add `#![feature(const_raw_ptr_deref)]` to the crate attributes to enable
+ = note: see issue #57349 <https://github.com/rust-lang/rust/issues/57349> for more information
+ = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable
-error[E0658]: dereferencing raw pointers in constants is unstable
- --> $DIR/E0396.rs:15:36
+error[E0658]: dereferencing raw mutable pointers in constants is unstable
+ --> $DIR/E0396.rs:13:36
|
LL | const BAD: () = unsafe { match *INFALLIBLE {} };
| ^^^^^^^^^^^
|
- = note: see issue #51911 <https://github.com/rust-lang/rust/issues/51911> for more information
- = help: add `#![feature(const_raw_ptr_deref)]` to the crate attributes to enable
+ = note: see issue #57349 <https://github.com/rust-lang/rust/issues/57349> for more information
+ = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable
error: aborting due to 3 previous errors
struct Q;
impl<R> External for (Q, R) {} //~ ERROR only traits defined
-//~^ ERROR conflicting implementations of trait
fn main() {}
-error[E0119]: conflicting implementations of trait `complex_impl_support::External` for type `(Q, complex_impl_support::M<'_, '_, '_, std::boxed::Box<_>, _, _>)`
- --> $DIR/complex-impl.rs:9:1
- |
-LL | impl<R> External for (Q, R) {}
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = note: conflicting implementation in crate `complex_impl_support`:
- - impl<'a, 'b, 'c, T, U, V, W> External for (T, M<'a, 'b, 'c, Box<U>, V, W>)
- where <U as FnOnce<(T,)>>::Output == V, <V as Iterator>::Item == T, 'b: 'a, T: 'a, U: 'static, U: FnOnce<(T,)>, V: Iterator, V: Clone, W: Add, <W as Add>::Output: Copy;
-
error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
--> $DIR/complex-impl.rs:9:1
|
|
= note: define and implement a trait or new type instead
-error: aborting due to 2 previous errors
+error: aborting due to previous error
-Some errors have detailed explanations: E0117, E0119.
-For more information about an error, try `rustc --explain E0117`.
+For more information about this error, try `rustc --explain E0117`.
struct Foo;
impl<Foo> Deref for Foo { } //~ ERROR must be used
-//~^ ERROR conflicting implementations
fn main() {}
-error[E0119]: conflicting implementations of trait `std::ops::Deref` for type `&_`
- --> $DIR/issue-28981.rs:5:1
- |
-LL | impl<Foo> Deref for Foo { }
- | ^^^^^^^^^^^^^^^^^^^^^^^
- |
- = note: conflicting implementation in crate `core`:
- - impl<T> Deref for &T
- where T: ?Sized;
-
error[E0210]: type parameter `Foo` must be used as the type parameter for some local type (e.g., `MyStruct<Foo>`)
--> $DIR/issue-28981.rs:5:6
|
= note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local
= note: only traits defined in the current crate can be implemented for a type parameter
-error: aborting due to 2 previous errors
+error: aborting due to previous error
-Some errors have detailed explanations: E0119, E0210.
-For more information about an error, try `rustc --explain E0119`.
+For more information about this error, try `rustc --explain E0210`.
+++ /dev/null
-#[derive(Debug)]
-struct Machine<S> {
- state: S,
- common_field1: &'static str,
- common_field2: i32,
-}
-#[derive(Debug)]
-struct State1;
-#[derive(Debug, PartialEq)]
-struct State2;
-
-fn update_to_state2() {
- let m1: Machine<State1> = Machine {
- state: State1,
- common_field1: "hello",
- common_field2: 2,
- };
- let m2: Machine<State2> = Machine {
- state: State2,
- ..m1 //~ ERROR mismatched types
- };
- // FIXME: this should trigger feature gate
- assert_eq!(State2, m2.state);
-}
-
-fn main() {}
+++ /dev/null
-error[E0308]: mismatched types
- --> $DIR/feature-gate-type_changing_struct_update.rs:20:11
- |
-LL | ..m1
- | ^^ expected struct `State2`, found struct `State1`
- |
- = note: expected struct `Machine<State2>`
- found struct `Machine<State1>`
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0308`.
+++ /dev/null
-fn main() {
- format!("{foo}"); //~ ERROR: there is no argument named `foo`
-
- // panic! doesn't hit format_args! unless there are two or more arguments.
- panic!("{foo} {bar}", bar=1); //~ ERROR: there is no argument named `foo`
-}
+++ /dev/null
-error: there is no argument named `foo`
- --> $DIR/feature-gate-format-args-capture.rs:2:14
- |
-LL | format!("{foo}");
- | ^^^^^
- |
- = help: if you intended to capture `foo` from the surrounding scope, add `#![feature(format_args_capture)]` to the crate attributes
-
-error: there is no argument named `foo`
- --> $DIR/feature-gate-format-args-capture.rs:5:13
- |
-LL | panic!("{foo} {bar}", bar=1);
- | ^^^^^
- |
- = help: if you intended to capture `foo` from the surrounding scope, add `#![feature(format_args_capture)]` to the crate attributes
-
-error: aborting due to 2 previous errors
-
-#![feature(format_args_capture)]
-
fn main() {
format!(concat!("{foo}")); //~ ERROR: there is no argument named `foo`
format!(concat!("{ba", "r} {}"), 1); //~ ERROR: there is no argument named `bar`
error: there is no argument named `foo`
- --> $DIR/format-args-capture-macro-hygiene.rs:4:13
+ --> $DIR/format-args-capture-macro-hygiene.rs:2:13
|
LL | format!(concat!("{foo}"));
| ^^^^^^^^^^^^^^^^
= note: this error originates in the macro `concat` (in Nightly builds, run with -Z macro-backtrace for more info)
error: there is no argument named `bar`
- --> $DIR/format-args-capture-macro-hygiene.rs:5:13
+ --> $DIR/format-args-capture-macro-hygiene.rs:3:13
|
LL | format!(concat!("{ba", "r} {}"), 1);
| ^^^^^^^^^^^^^^^^^^^^^^^
-#![feature(format_args_capture)]
-
fn main() {
format!("{} {foo} {} {bar} {}", 1, 2, 3);
//~^ ERROR: cannot find value `foo` in this scope
error: named argument never used
- --> $DIR/format-args-capture-missing-variables.rs:10:51
+ --> $DIR/format-args-capture-missing-variables.rs:8:51
|
LL | format!("{valuea} {valueb}", valuea=5, valuec=7);
| ------------------- ^ named argument never used
| formatting specifier missing
error[E0425]: cannot find value `foo` in this scope
- --> $DIR/format-args-capture-missing-variables.rs:4:17
+ --> $DIR/format-args-capture-missing-variables.rs:2:17
|
LL | format!("{} {foo} {} {bar} {}", 1, 2, 3);
| ^^^^^ not found in this scope
error[E0425]: cannot find value `bar` in this scope
- --> $DIR/format-args-capture-missing-variables.rs:4:26
+ --> $DIR/format-args-capture-missing-variables.rs:2:26
|
LL | format!("{} {foo} {} {bar} {}", 1, 2, 3);
| ^^^^^ not found in this scope
error[E0425]: cannot find value `foo` in this scope
- --> $DIR/format-args-capture-missing-variables.rs:8:14
+ --> $DIR/format-args-capture-missing-variables.rs:6:14
|
LL | format!("{foo}");
| ^^^^^ not found in this scope
error[E0425]: cannot find value `valueb` in this scope
- --> $DIR/format-args-capture-missing-variables.rs:10:23
+ --> $DIR/format-args-capture-missing-variables.rs:8:23
|
LL | format!("{valuea} {valueb}", valuea=5, valuec=7);
| ^^^^^^^^ not found in this scope
error[E0425]: cannot find value `foo` in this scope
- --> $DIR/format-args-capture-missing-variables.rs:16:9
+ --> $DIR/format-args-capture-missing-variables.rs:14:9
|
LL | {foo}
| ^^^^^ not found in this scope
error[E0425]: cannot find value `foo` in this scope
- --> $DIR/format-args-capture-missing-variables.rs:21:13
+ --> $DIR/format-args-capture-missing-variables.rs:19:13
|
LL | panic!("{foo} {bar}", bar=1);
| ^^^^^ not found in this scope
// run-pass
-#![feature(format_args_capture)]
#![feature(cfg_panic)]
fn main() {
//~^ ERROR: invalid reference to positional arguments 3, 4 and 5 (there are 3 arguments)
format!("{} {foo} {} {bar} {}", 1, 2, 3);
- //~^ ERROR: there is no argument named `foo`
- //~^^ ERROR: there is no argument named `bar`
+ //~^ ERROR: cannot find value `foo` in this scope
+ //~^^ ERROR: cannot find value `bar` in this scope
- format!("{foo}"); //~ ERROR: no argument named `foo`
+ format!("{foo}"); //~ ERROR: cannot find value `foo` in this scope
format!("", 1, 2); //~ ERROR: multiple unused formatting arguments
format!("{}", 1, 2); //~ ERROR: argument never used
format!("{1}", 1, 2); //~ ERROR: argument never used
// bad named arguments, #35082
format!("{valuea} {valueb}", valuea=5, valuec=7);
- //~^ ERROR there is no argument named `valueb`
+ //~^ ERROR cannot find value `valueb` in this scope
//~^^ ERROR named argument never used
// bad syntax of the format string
{foo}
"##);
- //~^^^ ERROR: there is no argument named `foo`
+ //~^^^ ERROR: cannot find value `foo` in this scope
// bad syntax in format string with multiple newlines, #53836
format!("first number: {}
|
= note: positional arguments are zero-based
-error: there is no argument named `foo`
- --> $DIR/ifmt-bad-arg.rs:27:17
- |
-LL | format!("{} {foo} {} {bar} {}", 1, 2, 3);
- | ^^^^^
- |
- = help: if you intended to capture `foo` from the surrounding scope, add `#![feature(format_args_capture)]` to the crate attributes
-
-error: there is no argument named `bar`
- --> $DIR/ifmt-bad-arg.rs:27:26
- |
-LL | format!("{} {foo} {} {bar} {}", 1, 2, 3);
- | ^^^^^
- |
- = help: if you intended to capture `bar` from the surrounding scope, add `#![feature(format_args_capture)]` to the crate attributes
-
-error: there is no argument named `foo`
- --> $DIR/ifmt-bad-arg.rs:31:14
- |
-LL | format!("{foo}");
- | ^^^^^
- |
- = help: if you intended to capture `foo` from the surrounding scope, add `#![feature(format_args_capture)]` to the crate attributes
-
error: multiple unused formatting arguments
--> $DIR/ifmt-bad-arg.rs:32:17
|
| |
| named argument
-error: there is no argument named `valueb`
- --> $DIR/ifmt-bad-arg.rs:45:23
- |
-LL | format!("{valuea} {valueb}", valuea=5, valuec=7);
- | ^^^^^^^^
- |
- = help: if you intended to capture `valueb` from the surrounding scope, add `#![feature(format_args_capture)]` to the crate attributes
-
error: named argument never used
--> $DIR/ifmt-bad-arg.rs:45:51
|
|
= note: printf formatting not supported; see the documentation for `std::fmt`
-error: there is no argument named `foo`
- --> $DIR/ifmt-bad-arg.rs:60:9
- |
-LL | {foo}
- | ^^^^^
- |
- = help: if you intended to capture `foo` from the surrounding scope, add `#![feature(format_args_capture)]` to the crate attributes
-
error: invalid format string: expected `'}'`, found `'t'`
--> $DIR/ifmt-bad-arg.rs:75:1
|
= note: positional arguments are zero-based
= note: for information about formatting flags, visit https://doc.rust-lang.org/std/fmt/index.html
+error[E0425]: cannot find value `foo` in this scope
+ --> $DIR/ifmt-bad-arg.rs:27:17
+ |
+LL | format!("{} {foo} {} {bar} {}", 1, 2, 3);
+ | ^^^^^ not found in this scope
+
+error[E0425]: cannot find value `bar` in this scope
+ --> $DIR/ifmt-bad-arg.rs:27:26
+ |
+LL | format!("{} {foo} {} {bar} {}", 1, 2, 3);
+ | ^^^^^ not found in this scope
+
+error[E0425]: cannot find value `foo` in this scope
+ --> $DIR/ifmt-bad-arg.rs:31:14
+ |
+LL | format!("{foo}");
+ | ^^^^^ not found in this scope
+
+error[E0425]: cannot find value `valueb` in this scope
+ --> $DIR/ifmt-bad-arg.rs:45:23
+ |
+LL | format!("{valuea} {valueb}", valuea=5, valuec=7);
+ | ^^^^^^^^ not found in this scope
+
+error[E0425]: cannot find value `foo` in this scope
+ --> $DIR/ifmt-bad-arg.rs:60:9
+ |
+LL | {foo}
+ | ^^^^^ not found in this scope
+
error[E0308]: mismatched types
--> $DIR/ifmt-bad-arg.rs:78:32
|
error: aborting due to 36 previous errors
-For more information about this error, try `rustc --explain E0308`.
+Some errors have detailed explanations: E0308, E0425.
+For more information about an error, try `rustc --explain E0308`.
--- /dev/null
+// check-pass
+
+#![feature(inline_const)]
+#![allow(incomplete_features)]
+
+pub fn todo<T>() -> T {
+ const { todo!() }
+}
+
+fn main() {
+ let _: usize = const { 0 };
+}
--- /dev/null
+#![allow(incomplete_features)]
+#![feature(const_mut_refs)]
+#![feature(inline_const)]
+
+use std::marker::PhantomData;
+
+#[derive(PartialEq, Eq)]
+pub struct InvariantRef<'a, T: ?Sized>(&'a T, PhantomData<&'a mut &'a T>);
+
+impl<'a, T: ?Sized> InvariantRef<'a, T> {
+ pub const fn new(r: &'a T) -> Self {
+ InvariantRef(r, PhantomData)
+ }
+}
+
+impl<'a> InvariantRef<'a, ()> {
+ pub const NEW: Self = InvariantRef::new(&());
+}
+
+fn equate<T>(x: T, y: T){}
+
+fn foo<'a>() {
+ let y = ();
+ equate(InvariantRef::new(&y), const { InvariantRef::<'a>::NEW });
+ //~^ ERROR `y` does not live long enough [E0597]
+}
+
+fn main() {
+ foo();
+}
--- /dev/null
+error[E0597]: `y` does not live long enough
+ --> $DIR/const-expr-lifetime-err.rs:24:30
+ |
+LL | fn foo<'a>() {
+ | -- lifetime `'a` defined here
+LL | let y = ();
+LL | equate(InvariantRef::new(&y), const { InvariantRef::<'a>::NEW });
+ | ------------------^^-
+ | | |
+ | | borrowed value does not live long enough
+ | argument requires that `y` is borrowed for `'a`
+LL |
+LL | }
+ | - `y` dropped here while still borrowed
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0597`.
--- /dev/null
+// run-pass
+
+#![allow(incomplete_features)]
+#![feature(const_mut_refs)]
+#![feature(inline_const)]
+
+use std::marker::PhantomData;
+
+// rust-lang/rust#78174: ICE: "cannot convert ReErased to a region vid"
+fn issue_78174() {
+ let foo = const { "foo" };
+ assert_eq!(foo, "foo");
+}
+
+pub struct InvariantRef<'a, T: ?Sized>(&'a T, PhantomData<&'a mut &'a T>);
+
+impl<'a, T: ?Sized> InvariantRef<'a, T> {
+ pub const fn new(r: &'a T) -> Self {
+ InvariantRef(r, PhantomData)
+ }
+}
+
+fn get_invariant_ref<'a>() -> InvariantRef<'a, ()> {
+ const { InvariantRef::<'a, ()>::new(&()) }
+}
+
+fn get_invariant_ref2<'a>() -> InvariantRef<'a, ()> {
+ // Try some type inference
+ const { InvariantRef::new(&()) }
+}
+
+fn main() {
+ issue_78174();
+ get_invariant_ref();
+ get_invariant_ref2();
+}
--- /dev/null
+// check-pass
+
+#![feature(inline_const)]
+#![allow(incomplete_features)]
+
+fn main() {
+ match 1u64 {
+ 0 => (),
+ const { 0 + 1 } => (),
+ const { 2 - 1 } ..= const { u64::MAX } => (),
+ }
+}
--- /dev/null
+// ignore-test
+
+#![allow(incomplete_features)]
+#![feature(const_mut_refs)]
+#![feature(inline_const)]
+
+use std::marker::PhantomData;
+
+#[derive(PartialEq, Eq)]
+pub struct InvariantRef<'a, T: ?Sized>(&'a T, PhantomData<&'a mut &'a T>);
+
+impl<'a, T: ?Sized> InvariantRef<'a, T> {
+ pub const fn new(r: &'a T) -> Self {
+ InvariantRef(r, PhantomData)
+ }
+}
+
+impl<'a> InvariantRef<'a, ()> {
+ pub const NEW: Self = InvariantRef::new(&());
+}
+
+fn match_invariant_ref<'a>() {
+ let y = ();
+ match InvariantRef::new(&y) {
+ //~^ ERROR `y` does not live long enough [E0597]
+ // FIXME(nbdd0121): This should give the same error as `InvariantRef::<'a>::NEW` (without
+ // const block)
+ const { InvariantRef::<'a>::NEW } => (),
+ }
+}
+
+fn main() {
+ match_invariant_ref();
+}
--- /dev/null
+// run-pass
+
+#![allow(incomplete_features)]
+#![feature(const_mut_refs)]
+#![feature(inline_const)]
+
+use std::marker::PhantomData;
+
+// rust-lang/rust#78174: ICE: "cannot convert ReErased to a region vid"
+fn issue_78174() {
+ match "foo" {
+ const { concat!("fo", "o") } => (),
+ _ => unreachable!(),
+ }
+}
+
+#[derive(PartialEq, Eq)]
+pub struct InvariantRef<'a, T: ?Sized>(&'a T, PhantomData<&'a mut &'a T>);
+
+impl<'a, T: ?Sized> InvariantRef<'a, T> {
+ pub const fn new(r: &'a T) -> Self {
+ InvariantRef(r, PhantomData)
+ }
+}
+
+fn match_invariant_ref<'a>() {
+ match const { InvariantRef::<'a, _>::new(&()) } {
+ const { InvariantRef::<'a, ()>::new(&()) } => {
+ }
+ }
+}
+
+fn main() {
+ issue_78174();
+ match_invariant_ref();
+}
trait A {
}
-impl<T> Drop for T where T: A { //~ ERROR E0119
- //~^ ERROR E0120
- //~| ERROR E0210
+impl<T> Drop for T where T: A {
+ //~^ ERROR E0120
+ //~| ERROR E0210
fn drop(&mut self) {
}
}
-error[E0119]: conflicting implementations of trait `std::ops::Drop` for type `std::boxed::Box<_, _>`
- --> $DIR/issue-41974.rs:7:1
+error[E0210]: type parameter `T` must be used as the type parameter for some local type (e.g., `MyStruct<T>`)
+ --> $DIR/issue-41974.rs:7:6
|
LL | impl<T> Drop for T where T: A {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^ type parameter `T` must be used as the type parameter for some local type
|
- = note: conflicting implementation in crate `alloc`:
- - impl<T, A> Drop for Box<T, A>
- where A: Allocator, T: ?Sized;
- = note: downstream crates may implement trait `A` for type `std::boxed::Box<_, _>`
+ = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local
+ = note: only traits defined in the current crate can be implemented for a type parameter
error[E0120]: the `Drop` trait may only be implemented for structs, enums, and unions
--> $DIR/issue-41974.rs:7:18
LL | impl<T> Drop for T where T: A {
| ^ must be a struct, enum, or union
-error[E0210]: type parameter `T` must be used as the type parameter for some local type (e.g., `MyStruct<T>`)
- --> $DIR/issue-41974.rs:7:6
- |
-LL | impl<T> Drop for T where T: A {
- | ^ type parameter `T` must be used as the type parameter for some local type
- |
- = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local
- = note: only traits defined in the current crate can be implemented for a type parameter
-
-error: aborting due to 3 previous errors
+error: aborting due to 2 previous errors
-Some errors have detailed explanations: E0119, E0120, E0210.
-For more information about an error, try `rustc --explain E0119`.
+Some errors have detailed explanations: E0120, E0210.
+For more information about an error, try `rustc --explain E0120`.
-warning: unused macro definition
- --> $DIR/issue-70041.rs:4:1
+warning: unused macro definition: `regex`
+ --> $DIR/issue-70041.rs:4:14
|
-LL | / macro_rules! regex {
-LL | |
-LL | | () => {};
-LL | | }
- | |_^
+LL | macro_rules! regex {
+ | ^^^^^
|
= note: `#[warn(unused_macros)]` on by default
-error: unused macro definition
- --> $DIR/unused-macro-rules.rs:4:1
+error: unused macro definition: `unused`
+ --> $DIR/unused-macro-rules.rs:4:14
|
-LL | / macro_rules! unused {
-LL | | () => {};
-LL | | }
- | |_^
+LL | macro_rules! unused {
+ | ^^^^^^
|
note: the lint level is defined here
--> $DIR/unused-macro-rules.rs:1:9
LL | #![deny(unused_macros)]
| ^^^^^^^^^^^^^
-error: unused macro definition
- --> $DIR/unused-macro-rules.rs:11:9
+error: unused macro definition: `m`
+ --> $DIR/unused-macro-rules.rs:11:22
|
-LL | / macro_rules! m {
-LL | | () => {};
-LL | | }
- | |_________^
-...
-LL | create_macro!();
- | --------------- in this macro invocation
- |
- = note: this error originates in the macro `create_macro` (in Nightly builds, run with -Z macro-backtrace for more info)
+LL | macro_rules! m {
+ | ^
-error: unused macro definition
- --> $DIR/unused-macro-rules.rs:24:5
+error: unused macro definition: `unused`
+ --> $DIR/unused-macro-rules.rs:24:18
|
-LL | / macro_rules! unused {
-LL | | () => {};
-LL | | }
- | |_____^
+LL | macro_rules! unused {
+ | ^^^^^^
|
note: the lint level is defined here
--> $DIR/unused-macro-rules.rs:23:12
-error: unused macro definition
- --> $DIR/unused-macro.rs:5:1
+error: unused macro definition: `unused`
+ --> $DIR/unused-macro.rs:5:7
|
-LL | / macro unused {
-LL | | () => {}
-LL | | }
- | |_^
+LL | macro unused {
+ | ^^^^^^
|
note: the lint level is defined here
--> $DIR/unused-macro.rs:2:9
LL | #![deny(unused_macros)]
| ^^^^^^^^^^^^^
-error: unused macro definition
- --> $DIR/unused-macro.rs:15:5
+error: unused macro definition: `unused`
+ --> $DIR/unused-macro.rs:15:11
|
-LL | / macro unused {
-LL | | () => {}
-LL | | }
- | |_____^
+LL | macro unused {
+ | ^^^^^^
|
note: the lint level is defined here
--> $DIR/unused-macro.rs:14:12
LL | #[deny(unused_macros)]
| ^^^^^^^^^^^^^
-error: unused macro definition
- --> $DIR/unused-macro.rs:21:5
+error: unused macro definition: `unused`
+ --> $DIR/unused-macro.rs:21:22
|
-LL | / pub(crate) macro unused {
-LL | | () => {}
-LL | | }
- | |_____^
+LL | pub(crate) macro unused {
+ | ^^^^^^
error: aborting due to 3 previous errors
--- /dev/null
+// force-host
+// no-prefer-dynamic
+
+#![crate_type = "proc-macro"]
+#![deny(warnings)]
+#![feature(proc_macro_expand, proc_macro_span)]
+
+extern crate proc_macro;
+
+use proc_macro::*;
+use std::str::FromStr;
+
+#[proc_macro]
+pub fn expand_expr_is(input: TokenStream) -> TokenStream {
+ let mut iter = input.into_iter();
+ let mut expected_tts = Vec::new();
+ loop {
+ match iter.next() {
+ Some(TokenTree::Punct(ref p)) if p.as_char() == ',' => break,
+ Some(tt) => expected_tts.push(tt),
+ None => panic!("expected comma"),
+ }
+ }
+
+ let expected = expected_tts.into_iter().collect::<TokenStream>();
+ let expanded = iter.collect::<TokenStream>().expand_expr().expect("expand_expr failed");
+ assert!(
+ expected.to_string() == expanded.to_string(),
+ "assert failed\nexpected: `{}`\nexpanded: `{}`",
+ expected.to_string(),
+ expanded.to_string()
+ );
+
+ TokenStream::new()
+}
+
+#[proc_macro]
+pub fn expand_expr_fail(input: TokenStream) -> TokenStream {
+ match input.expand_expr() {
+ Ok(ts) => panic!("expand_expr unexpectedly succeeded: `{}`", ts),
+ Err(_) => TokenStream::new(),
+ }
+}
+
+#[proc_macro]
+pub fn check_expand_expr_file(ts: TokenStream) -> TokenStream {
+ // Check that the passed in `file!()` invocation and a parsed `file!`
+ // invocation expand to the same literal.
+ let input_t = ts.expand_expr().expect("expand_expr failed on macro input").to_string();
+ let parse_t = TokenStream::from_str("file!{}")
+ .unwrap()
+ .expand_expr()
+ .expect("expand_expr failed on internal macro")
+ .to_string();
+ assert_eq!(input_t, parse_t);
+
+ // Check that the literal matches `Span::call_site().source_file().path()`
+ let expect_t =
+ Literal::string(&Span::call_site().source_file().path().to_string_lossy()).to_string();
+ assert_eq!(input_t, expect_t);
+
+ TokenStream::new()
+}
+
+#[proc_macro]
+pub fn recursive_expand(_: TokenStream) -> TokenStream {
+ // Recursively call until we hit the recursion limit and get an error.
+ //
+ // NOTE: This doesn't panic if expansion fails because that'll cause a very
+ // large number of errors to fill the output.
+ TokenStream::from_str("recursive_expand!{}")
+ .unwrap()
+ .expand_expr()
+ .unwrap_or(std::iter::once(TokenTree::Literal(Literal::u32_suffixed(0))).collect())
+}
+
+#[proc_macro]
+pub fn echo_pm(input: TokenStream) -> TokenStream {
+ input
+}
--- /dev/null
+Included file contents
--- /dev/null
+// aux-build:expand-expr.rs
+
+extern crate expand_expr;
+
+use expand_expr::{
+ check_expand_expr_file, echo_pm, expand_expr_fail, expand_expr_is, recursive_expand,
+};
+
+// Check builtin macros can be expanded.
+
+expand_expr_is!(11u32, line!());
+expand_expr_is!(24u32, column!());
+
+expand_expr_is!("Hello, World!", concat!("Hello, ", "World", "!"));
+expand_expr_is!("int10floats5.3booltrue", concat!("int", 10, "floats", 5.3, "bool", true));
+expand_expr_is!("Hello", concat!(r##"Hello"##));
+
+expand_expr_is!("Included file contents\n", include_str!("auxiliary/included-file.txt"));
+expand_expr_is!(b"Included file contents\n", include_bytes!("auxiliary/included-file.txt"));
+
+expand_expr_is!(
+ "contents: Included file contents\n",
+ concat!("contents: ", include_str!("auxiliary/included-file.txt"))
+);
+
+// Correct value is checked for multiple sources.
+check_expand_expr_file!(file!());
+
+expand_expr_is!("hello", stringify!(hello));
+expand_expr_is!("10 + 20", stringify!(10 + 20));
+
+macro_rules! echo_tts {
+ ($($t:tt)*) => { $($t)* }; //~ ERROR: expected expression, found `$`
+}
+
+macro_rules! echo_lit {
+ ($l:literal) => {
+ $l
+ };
+}
+
+macro_rules! echo_expr {
+ ($e:expr) => {
+ $e
+ };
+}
+
+macro_rules! simple_lit {
+ ($l:literal) => {
+ expand_expr_is!($l, $l);
+ expand_expr_is!($l, echo_lit!($l));
+ expand_expr_is!($l, echo_expr!($l));
+ expand_expr_is!($l, echo_tts!($l));
+ expand_expr_is!($l, echo_pm!($l));
+ const _: () = {
+ macro_rules! mac {
+ () => {
+ $l
+ };
+ }
+ expand_expr_is!($l, mac!());
+ expand_expr_is!($l, echo_expr!(mac!()));
+ expand_expr_is!($l, echo_tts!(mac!()));
+ expand_expr_is!($l, echo_pm!(mac!()));
+ };
+ };
+}
+
+simple_lit!("Hello, World");
+simple_lit!('c');
+simple_lit!(b'c');
+simple_lit!(10);
+simple_lit!(10.0);
+simple_lit!(10.0f64);
+simple_lit!(-3.14159);
+simple_lit!(-3.5e10);
+simple_lit!(0xFEED);
+simple_lit!(-0xFEED);
+simple_lit!(0b0100);
+simple_lit!(-0b0100);
+simple_lit!("string");
+simple_lit!(r##"raw string"##);
+simple_lit!(b"byte string");
+simple_lit!(br##"raw byte string"##);
+simple_lit!(true);
+simple_lit!(false);
+
+// Ensure char escapes aren't normalized by expansion
+simple_lit!("\u{0}");
+simple_lit!("\0");
+simple_lit!("\x00");
+simple_lit!('\u{0}');
+simple_lit!('\0');
+simple_lit!('\x00');
+simple_lit!(b"\x00");
+simple_lit!(b"\0");
+simple_lit!(b'\x00');
+simple_lit!(b'\0');
+
+// Extra tokens after the string literal aren't ignored
+expand_expr_fail!("string"; hello); //~ ERROR: expected one of `.`, `?`, or an operator, found `;`
+
+// Invalid expressions produce errors in addition to returning `Err(())`.
+expand_expr_fail!($); //~ ERROR: expected expression, found `$`
+expand_expr_fail!(echo_tts!($));
+expand_expr_fail!(echo_pm!($)); //~ ERROR: expected expression, found `$`
+
+// We get errors reported and recover during macro expansion if the macro
+// doesn't produce a valid expression.
+expand_expr_is!("string", echo_tts!("string"; hello)); //~ ERROR: macro expansion ignores token `hello` and any following
+expand_expr_is!("string", echo_pm!("string"; hello)); //~ ERROR: macro expansion ignores token `;` and any following
+
+// For now, fail if a non-literal expression is expanded.
+expand_expr_fail!(arbitrary_expression() + "etc");
+expand_expr_fail!(echo_tts!(arbitrary_expression() + "etc"));
+expand_expr_fail!(echo_expr!(arbitrary_expression() + "etc"));
+expand_expr_fail!(echo_pm!(arbitrary_expression() + "etc"));
+
+const _: u32 = recursive_expand!(); //~ ERROR: recursion limit reached while expanding `recursive_expand!`
+
+fn main() {}
--- /dev/null
+error: expected one of `.`, `?`, or an operator, found `;`
+ --> $DIR/expand-expr.rs:101:27
+ |
+LL | expand_expr_fail!("string"; hello);
+ | ^ expected one of `.`, `?`, or an operator
+
+error: expected expression, found `$`
+ --> $DIR/expand-expr.rs:104:19
+ |
+LL | expand_expr_fail!($);
+ | ^ expected expression
+
+error: expected expression, found `$`
+ --> $DIR/expand-expr.rs:33:23
+ |
+LL | ($($t:tt)*) => { $($t)* };
+ | ^^^^ expected expression
+
+error: expected expression, found `$`
+ --> $DIR/expand-expr.rs:106:28
+ |
+LL | expand_expr_fail!(echo_pm!($));
+ | ^ expected expression
+
+error: macro expansion ignores token `hello` and any following
+ --> $DIR/expand-expr.rs:110:47
+ |
+LL | expand_expr_is!("string", echo_tts!("string"; hello));
+ | --------------------^^^^^-- help: you might be missing a semicolon here: `;`
+ | |
+ | caused by the macro expansion here
+ |
+ = note: the usage of `echo_tts!` is likely invalid in expression context
+
+error: macro expansion ignores token `;` and any following
+ --> $DIR/expand-expr.rs:111:44
+ |
+LL | expand_expr_is!("string", echo_pm!("string"; hello));
+ | -----------------^-------- help: you might be missing a semicolon here: `;`
+ | |
+ | caused by the macro expansion here
+ |
+ = note: the usage of `echo_pm!` is likely invalid in expression context
+
+error: recursion limit reached while expanding `recursive_expand!`
+ --> $DIR/expand-expr.rs:119:16
+ |
+LL | const _: u32 = recursive_expand!();
+ | ^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`expand_expr`)
+ = note: this error originates in the macro `recursive_expand` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 7 previous errors
+
// run-pass
-#![allow(dead_code)]
+#![allow(dead_code, unused_macros)]
// aux-build:issue-39889.rs
extern crate issue_39889;
use libc::c_int;
-#[cfg(not(target_os = "linux"))]
-fn getpid() -> u32 {
- process::id()
-}
-
-/// We need to directly use the getpid syscall instead of using `process::id()`
-/// because the libc wrapper might return incorrect values after a process was
-/// forked.
-#[cfg(target_os = "linux")]
-fn getpid() -> u32 {
- unsafe {
- libc::syscall(libc::SYS_getpid) as _
- }
-}
-
/// This stunt allocator allows us to spot heap allocations in the child.
struct PidChecking<A> {
parent: A,
fn check(&self) {
let require_pid = self.require_pid.load(Ordering::Acquire);
if require_pid != 0 {
- let actual_pid = getpid();
+ let actual_pid = process::id();
if require_pid != actual_pid {
unsafe {
libc::raise(libc::SIGUSR1);
pub struct Int(i32);
-impl const std::ops::Add for i32 {
- //~^ ERROR conflicting implementations of trait
- //~| ERROR only traits defined in the current crate can be implemented for arbitrary types
+impl const std::ops::Add for i32 { //~ ERROR type annotations needed
+ //~^ ERROR only traits defined in the current crate can be implemented for arbitrary types
type Output = Self;
fn add(self, rhs: Self) -> Self {
}
}
-impl std::ops::Add for Int {
+impl std::ops::Add for Int { //~ ERROR type annotations needed
type Output = Self;
fn add(self, rhs: Self) -> Self {
}
}
-impl const std::ops::Add for Int {
+impl const std::ops::Add for Int { //~ ERROR type annotations needed
//~^ ERROR conflicting implementations of trait
type Output = Self;
-error[E0119]: conflicting implementations of trait `std::ops::Add` for type `i32`
+error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
--> $DIR/const-and-non-const-impl.rs:5:1
|
LL | impl const std::ops::Add for i32 {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^^^^^^^^^^-------------^^^^^---
+ | | | |
+ | | | `i32` is not defined in the current crate
+ | | `i32` is not defined in the current crate
+ | impl doesn't use only types from inside the current crate
|
- = note: conflicting implementation in crate `core`:
- - impl Add for i32;
+ = note: define and implement a trait or new type instead
error[E0119]: conflicting implementations of trait `std::ops::Add` for type `Int`
- --> $DIR/const-and-non-const-impl.rs:23:1
+ --> $DIR/const-and-non-const-impl.rs:22:1
|
LL | impl std::ops::Add for Int {
| -------------------------- first implementation here
LL | impl const std::ops::Add for Int {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Int`
-error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
+error[E0283]: type annotations needed
+ --> $DIR/const-and-non-const-impl.rs:5:12
+ |
+LL | impl const std::ops::Add for i32 {
+ | ^^^^^^^^^^^^^ cannot infer type for type `i32`
+ |
+note: multiple `impl`s satisfying `i32: Add` found
--> $DIR/const-and-non-const-impl.rs:5:1
|
LL | impl const std::ops::Add for i32 {
- | ^^^^^^^^^^^-------------^^^^^---
- | | | |
- | | | `i32` is not defined in the current crate
- | | `i32` is not defined in the current crate
- | impl doesn't use only types from inside the current crate
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = note: and another `impl` found in the `core` crate: `impl Add for i32;`
+note: required by a bound in `Add`
+ --> $SRC_DIR/core/src/ops/arith.rs:LL:COL
|
- = note: define and implement a trait or new type instead
+LL | / pub trait Add<Rhs = Self> {
+LL | | /// The resulting type after applying the `+` operator.
+LL | | #[stable(feature = "rust1", since = "1.0.0")]
+LL | | type Output;
+... |
+LL | | fn add(self, rhs: Rhs) -> Self::Output;
+LL | | }
+ | |_^ required by this bound in `Add`
+
+error[E0283]: type annotations needed
+ --> $DIR/const-and-non-const-impl.rs:14:6
+ |
+LL | impl std::ops::Add for Int {
+ | ^^^^^^^^^^^^^ cannot infer type for struct `Int`
+ |
+note: multiple `impl`s satisfying `Int: Add` found
+ --> $DIR/const-and-non-const-impl.rs:14:1
+ |
+LL | impl std::ops::Add for Int {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | impl const std::ops::Add for Int {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: required by a bound in `Add`
+ --> $SRC_DIR/core/src/ops/arith.rs:LL:COL
+ |
+LL | / pub trait Add<Rhs = Self> {
+LL | | /// The resulting type after applying the `+` operator.
+LL | | #[stable(feature = "rust1", since = "1.0.0")]
+LL | | type Output;
+... |
+LL | | fn add(self, rhs: Rhs) -> Self::Output;
+LL | | }
+ | |_^ required by this bound in `Add`
+
+error[E0283]: type annotations needed
+ --> $DIR/const-and-non-const-impl.rs:22:12
+ |
+LL | impl const std::ops::Add for Int {
+ | ^^^^^^^^^^^^^ cannot infer type for struct `Int`
+ |
+note: multiple `impl`s satisfying `Int: Add` found
+ --> $DIR/const-and-non-const-impl.rs:14:1
+ |
+LL | impl std::ops::Add for Int {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | impl const std::ops::Add for Int {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: required by a bound in `Add`
+ --> $SRC_DIR/core/src/ops/arith.rs:LL:COL
+ |
+LL | / pub trait Add<Rhs = Self> {
+LL | | /// The resulting type after applying the `+` operator.
+LL | | #[stable(feature = "rust1", since = "1.0.0")]
+LL | | type Output;
+... |
+LL | | fn add(self, rhs: Rhs) -> Self::Output;
+LL | | }
+ | |_^ required by this bound in `Add`
-error: aborting due to 3 previous errors
+error: aborting due to 5 previous errors
-Some errors have detailed explanations: E0117, E0119.
+Some errors have detailed explanations: E0117, E0119, E0283.
For more information about an error, try `rustc --explain E0117`.
--- /dev/null
+// gate-test-type_changing_struct_update
+
+#[derive(Debug)]
+struct Machine<S> {
+ state: S,
+ common_field1: &'static str,
+ common_field2: i32,
+}
+#[derive(Debug)]
+struct State1;
+#[derive(Debug, PartialEq)]
+struct State2;
+
+fn update_to_state2() {
+ let m1: Machine<State1> = Machine {
+ state: State1,
+ common_field1: "hello",
+ common_field2: 2,
+ };
+ let m2: Machine<State2> = Machine {
+ state: State2,
+ ..m1
+ //~^ ERROR type changing struct updating is experimental [E0658]
+ //~| ERROR mismatched types [E0308]
+ };
+ assert_eq!(State2, m2.state);
+}
+
+fn main() {}
--- /dev/null
+error[E0658]: type changing struct updating is experimental
+ --> $DIR/feature-gate.rs:22:11
+ |
+LL | ..m1
+ | ^^
+ |
+ = note: see issue #86555 <https://github.com/rust-lang/rust/issues/86555> for more information
+ = help: add `#![feature(type_changing_struct_update)]` to the crate attributes to enable
+
+error[E0308]: mismatched types
+ --> $DIR/feature-gate.rs:22:11
+ |
+LL | ..m1
+ | ^^ expected struct `State2`, found struct `State1`
+ |
+ = note: expected struct `Machine<State2>`
+ found struct `Machine<State1>`
+
+error: aborting due to 2 previous errors
+
+Some errors have detailed explanations: E0308, E0658.
+For more information about an error, try `rustc --explain E0308`.
--- /dev/null
+#![feature(type_changing_struct_update)]
+#![allow(incomplete_features)]
+
+#[derive(Clone)]
+struct Machine<'a, S> {
+ state: S,
+ lt_str: &'a str,
+ common_field: i32,
+}
+
+#[derive(Clone)]
+struct State1;
+#[derive(Clone)]
+struct State2;
+
+fn update_to_state2() {
+ let s = String::from("hello");
+ let m1: Machine<State1> = Machine {
+ state: State1,
+ lt_str: &s,
+ //~^ ERROR `s` does not live long enough [E0597]
+ // FIXME: The error here actually comes from line 34. The
+ // span of the error message should be corrected to line 34
+ common_field: 2,
+ };
+ // update lifetime
+ let m3: Machine<'static, State1> = Machine {
+ lt_str: "hello, too",
+ ..m1.clone()
+ };
+ // update lifetime and type
+ let m4: Machine<'static, State2> = Machine {
+ state: State2,
+ lt_str: "hello, again",
+ ..m1.clone()
+ };
+ // updating to `static should fail.
+ let m2: Machine<'static, State1> = Machine {
+ ..m1
+ };
+}
+
+fn main() {}
--- /dev/null
+error[E0597]: `s` does not live long enough
+ --> $DIR/lifetime-update.rs:20:17
+ |
+LL | lt_str: &s,
+ | ^^ borrowed value does not live long enough
+...
+LL | let m2: Machine<'static, State1> = Machine {
+ | ------------------------ type annotation requires that `s` is borrowed for `'static`
+...
+LL | }
+ | - `s` dropped here while still borrowed
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0597`.
--- /dev/null
+#![feature(type_changing_struct_update)]
+#![allow(incomplete_features)]
+
+struct Machine<'a, S, M> {
+ state: S,
+ message: M,
+ lt_str: &'a str,
+ common_field: i32,
+}
+
+struct State1;
+struct State2;
+
+struct Message1;
+struct Message2;
+
+fn update() {
+ let m1: Machine<State1, Message1> = Machine {
+ state: State1,
+ message: Message1,
+ lt_str: "hello",
+ common_field: 2,
+ };
+ // single type update
+ let m2: Machine<State2, Message1> = Machine {
+ state: State2,
+ ..m1
+ };
+ // multiple type update
+ let m3: Machine<State2, Message2> = Machine {
+ state: State2,
+ message: Message2,
+ ..m1
+ };
+}
+
+fn fail_update() {
+ let m1: Machine<f64, f64> = Machine {
+ state: 3.2,
+ message: 6.4,
+ lt_str: "hello",
+ common_field: 2,
+ };
+ // single type update fail
+ let m2: Machine<i32, f64> = Machine {
+ ..m1
+ //~^ ERROR mismatched types [E0308]
+ };
+ // multiple type update fail
+ let m3 = Machine::<i32, i32> {
+ ..m1
+ //~^ ERROR mismatched types [E0308]
+ //~| ERROR mismatched types [E0308]
+ };
+}
+
+fn main() {}
--- /dev/null
+error[E0308]: mismatched types
+ --> $DIR/type-generic-update.rs:46:11
+ |
+LL | ..m1
+ | ^^ field type mismatch: Machine.state
+ |
+ = note: expected type `i32`
+ found type `f64`
+
+error[E0308]: mismatched types
+ --> $DIR/type-generic-update.rs:51:11
+ |
+LL | ..m1
+ | ^^ field type mismatch: Machine.state
+ |
+ = note: expected type `i32`
+ found type `f64`
+
+error[E0308]: mismatched types
+ --> $DIR/type-generic-update.rs:51:11
+ |
+LL | ..m1
+ | ^^ field type mismatch: Machine.message
+ |
+ = note: expected type `i32`
+ found type `f64`
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
let _: u64 = simd_bitmask(m64);
let _: u16 = simd_bitmask(m2);
- //~^ ERROR bitmask `u16`, expected `u8`
+ //~^ ERROR invalid monomorphization of `simd_bitmask` intrinsic
let _: u16 = simd_bitmask(m8);
- //~^ ERROR bitmask `u16`, expected `u8`
+ //~^ ERROR invalid monomorphization of `simd_bitmask` intrinsic
let _: u32 = simd_bitmask(m16);
- //~^ ERROR bitmask `u32`, expected `u16`
+ //~^ ERROR invalid monomorphization of `simd_bitmask` intrinsic
let _: u64 = simd_bitmask(m32);
- //~^ ERROR bitmask `u64`, expected `u32`
+ //~^ ERROR invalid monomorphization of `simd_bitmask` intrinsic
let _: u128 = simd_bitmask(m64);
- //~^ ERROR bitmask `u128`, expected `u64`
+ //~^ ERROR invalid monomorphization of `simd_bitmask` intrinsic
}
}
-error[E0511]: invalid monomorphization of `simd_bitmask` intrinsic: bitmask `u16`, expected `u8`
+error[E0511]: invalid monomorphization of `simd_bitmask` intrinsic: cannot return `u16`, expected `u8` or `[u8; 1]`
--> $DIR/generic-bitmask.rs:53:22
|
LL | let _: u16 = simd_bitmask(m2);
| ^^^^^^^^^^^^^^^^
-error[E0511]: invalid monomorphization of `simd_bitmask` intrinsic: bitmask `u16`, expected `u8`
+error[E0511]: invalid monomorphization of `simd_bitmask` intrinsic: cannot return `u16`, expected `u8` or `[u8; 1]`
--> $DIR/generic-bitmask.rs:56:22
|
LL | let _: u16 = simd_bitmask(m8);
| ^^^^^^^^^^^^^^^^
-error[E0511]: invalid monomorphization of `simd_bitmask` intrinsic: bitmask `u32`, expected `u16`
+error[E0511]: invalid monomorphization of `simd_bitmask` intrinsic: cannot return `u32`, expected `u16` or `[u8; 2]`
--> $DIR/generic-bitmask.rs:59:22
|
LL | let _: u32 = simd_bitmask(m16);
| ^^^^^^^^^^^^^^^^^
-error[E0511]: invalid monomorphization of `simd_bitmask` intrinsic: bitmask `u64`, expected `u32`
+error[E0511]: invalid monomorphization of `simd_bitmask` intrinsic: cannot return `u64`, expected `u32` or `[u8; 4]`
--> $DIR/generic-bitmask.rs:62:22
|
LL | let _: u64 = simd_bitmask(m32);
| ^^^^^^^^^^^^^^^^^
-error[E0511]: invalid monomorphization of `simd_bitmask` intrinsic: bitmask `u128`, expected `u64`
+error[E0511]: invalid monomorphization of `simd_bitmask` intrinsic: cannot return `u128`, expected `u64` or `[u8; 8]`
--> $DIR/generic-bitmask.rs:65:23
|
LL | let _: u128 = simd_bitmask(m64);
#[repr(simd)]
#[derive(Copy, Clone, PartialEq)]
-struct b8x8(pub i8, pub i8, pub i8, pub i8,
- pub i8, pub i8, pub i8, pub i8);
+struct b8x8(pub i8, pub i8, pub i8, pub i8, pub i8, pub i8, pub i8, pub i8);
extern "platform-intrinsic" {
fn simd_select<T, U>(x: T, a: U, b: U) -> U;
//~^ ERROR found non-SIMD `u32`
simd_select_bitmask(0u16, x, x);
- //~^ ERROR mask length `16` != other vector length `4`
- //
+ //~^ ERROR invalid bitmask `u16`, expected `u8` or `[u8; 1]`
+
simd_select_bitmask(0u8, 1u32, 2u32);
//~^ ERROR found non-SIMD `u32`
simd_select_bitmask(0.0f32, x, x);
- //~^ ERROR `f32` is not an integral type
+ //~^ ERROR invalid bitmask `f32`, expected `u8` or `[u8; 1]`
simd_select_bitmask("x", x, x);
- //~^ ERROR `&str` is not an integral type
+ //~^ ERROR invalid bitmask `&str`, expected `u8` or `[u8; 1]`
}
}
error[E0511]: invalid monomorphization of `simd_select` intrinsic: mismatched lengths: mask length `8` != other vector length `4`
- --> $DIR/generic-select.rs:40:9
+ --> $DIR/generic-select.rs:39:9
|
LL | simd_select(m8, x, x);
| ^^^^^^^^^^^^^^^^^^^^^
error[E0511]: invalid monomorphization of `simd_select` intrinsic: mask element type is `u32`, expected `i_`
- --> $DIR/generic-select.rs:43:9
+ --> $DIR/generic-select.rs:42:9
|
LL | simd_select(x, x, x);
| ^^^^^^^^^^^^^^^^^^^^
error[E0511]: invalid monomorphization of `simd_select` intrinsic: mask element type is `f32`, expected `i_`
- --> $DIR/generic-select.rs:46:9
+ --> $DIR/generic-select.rs:45:9
|
LL | simd_select(z, z, z);
| ^^^^^^^^^^^^^^^^^^^^
error[E0511]: invalid monomorphization of `simd_select` intrinsic: expected SIMD argument type, found non-SIMD `u32`
- --> $DIR/generic-select.rs:49:9
+ --> $DIR/generic-select.rs:48:9
|
LL | simd_select(m4, 0u32, 1u32);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
-error[E0511]: invalid monomorphization of `simd_select_bitmask` intrinsic: mismatched lengths: mask length `16` != other vector length `4`
- --> $DIR/generic-select.rs:52:9
+error[E0511]: invalid monomorphization of `simd_select_bitmask` intrinsic: invalid bitmask `u16`, expected `u8` or `[u8; 1]`
+ --> $DIR/generic-select.rs:51:9
|
LL | simd_select_bitmask(0u16, x, x);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error[E0511]: invalid monomorphization of `simd_select_bitmask` intrinsic: expected SIMD argument type, found non-SIMD `u32`
- --> $DIR/generic-select.rs:55:9
+ --> $DIR/generic-select.rs:54:9
|
LL | simd_select_bitmask(0u8, 1u32, 2u32);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-error[E0511]: invalid monomorphization of `simd_select_bitmask` intrinsic: `f32` is not an integral type
- --> $DIR/generic-select.rs:58:9
+error[E0511]: invalid monomorphization of `simd_select_bitmask` intrinsic: invalid bitmask `f32`, expected `u8` or `[u8; 1]`
+ --> $DIR/generic-select.rs:57:9
|
LL | simd_select_bitmask(0.0f32, x, x);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-error[E0511]: invalid monomorphization of `simd_select_bitmask` intrinsic: `&str` is not an integral type
- --> $DIR/generic-select.rs:61:9
+error[E0511]: invalid monomorphization of `simd_select_bitmask` intrinsic: invalid bitmask `&str`, expected `u8` or `[u8; 1]`
+ --> $DIR/generic-select.rs:60:9
|
LL | simd_select_bitmask("x", x, x);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
--- /dev/null
+#![crate_type = "rlib"]
+#![no_std]
+#![feature(portable_simd)]
+use core::simd::f32x4;
+
+// For SIMD float ops, the LLIR version which is used to implement the portable
+// forms of them may become calls to math.h AKA libm. So, we can't guarantee
+// we can compile them for #![no_std] crates.
+// Someday we may solve this.
+// Until then, this test at least guarantees these functions require std.
+fn guarantee_no_std_nolibm_calls() -> f32x4 {
+ let x = f32x4::from_array([0.1, 0.5, 0.6, -1.5]);
+ let x2 = x + x;
+ let _xc = x.ceil(); //~ ERROR E0599
+ let _xf = x.floor(); //~ ERROR E0599
+ let _xr = x.round(); //~ ERROR E0599
+ let _xt = x.trunc(); //~ ERROR E0599
+ let _xfma = x.mul_add(x, x); //~ ERROR E0599
+ let _xsqrt = x.sqrt(); //~ ERROR E0599
+ x2.abs() * x2
+}
--- /dev/null
+error[E0599]: no method named `ceil` found for struct `Simd` in the current scope
+ --> $DIR/libm_no_std_cant_float.rs:14:17
+ |
+LL | let _xc = x.ceil();
+ | ^^^^ method not found in `Simd<f32, 4_usize>`
+
+error[E0599]: no method named `floor` found for struct `Simd` in the current scope
+ --> $DIR/libm_no_std_cant_float.rs:15:17
+ |
+LL | let _xf = x.floor();
+ | ^^^^^ method not found in `Simd<f32, 4_usize>`
+
+error[E0599]: no method named `round` found for struct `Simd` in the current scope
+ --> $DIR/libm_no_std_cant_float.rs:16:17
+ |
+LL | let _xr = x.round();
+ | ^^^^^ method not found in `Simd<f32, 4_usize>`
+
+error[E0599]: no method named `trunc` found for struct `Simd` in the current scope
+ --> $DIR/libm_no_std_cant_float.rs:17:17
+ |
+LL | let _xt = x.trunc();
+ | ^^^^^ method not found in `Simd<f32, 4_usize>`
+
+error[E0599]: no method named `mul_add` found for struct `Simd` in the current scope
+ --> $DIR/libm_no_std_cant_float.rs:18:19
+ |
+LL | let _xfma = x.mul_add(x, x);
+ | ^^^^^^^ method not found in `Simd<f32, 4_usize>`
+
+error[E0599]: no method named `sqrt` found for struct `Simd` in the current scope
+ --> $DIR/libm_no_std_cant_float.rs:19:20
+ |
+LL | let _xsqrt = x.sqrt();
+ | ^^^^ method not found in `Simd<f32, 4_usize>`
+
+error: aborting due to 6 previous errors
+
+For more information about this error, try `rustc --explain E0599`.
--- /dev/null
+// May not matter, since people can use them with a nightly feature.
+// However this tests to guarantee they don't leak out via portable_simd,
+// and thus don't accidentally get stabilized.
+use std::simd::intrinsics; //~ERROR E0603
+
+fn main() {
+ ()
+}
--- /dev/null
+error[E0603]: module `intrinsics` is private
+ --> $DIR/portable-intrinsics-arent-exposed.rs:4:16
+ |
+LL | use std::simd::intrinsics;
+ | ^^^^^^^^^^ private module
+ |
+note: the module `intrinsics` is defined here
+ --> $SRC_DIR/core/src/lib.rs:LL:COL
+ |
+LL | pub use crate::core_simd::simd::*;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0603`.
--- /dev/null
+//run-pass
+//ignore-endian-big behavior of simd_select_bitmask is endian-specific
+#![feature(repr_simd, platform_intrinsics)]
+
+extern "platform-intrinsic" {
+ fn simd_bitmask<T, U>(v: T) -> U;
+ fn simd_select_bitmask<T, U>(m: T, a: U, b: U) -> U;
+}
+
+#[derive(Copy, Clone)]
+#[repr(simd)]
+struct Simd<T, const N: usize>([T; N]);
+
+fn main() {
+ unsafe {
+ let v = Simd::<i8, 4>([-1, 0, -1, 0]);
+ let i: u8 = simd_bitmask(v);
+ let a: [u8; 1] = simd_bitmask(v);
+
+ assert_eq!(i, 0b0101);
+ assert_eq!(a, [0b0101]);
+
+ let v = Simd::<i8, 16>([0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, -1, 0]);
+ let i: u16 = simd_bitmask(v);
+ let a: [u8; 2] = simd_bitmask(v);
+
+ assert_eq!(i, 0b0101000000001100);
+ assert_eq!(a, [0b1100, 0b01010000]);
+ }
+
+ unsafe {
+ let a = Simd::<i32, 8>([0, 1, 2, 3, 4, 5, 6, 7]);
+ let b = Simd::<i32, 8>([8, 9, 10, 11, 12, 13, 14, 15]);
+ let e = [0, 9, 2, 11, 12, 13, 14, 15];
+
+ let r = simd_select_bitmask(0b0101u8, a, b);
+ assert_eq!(r.0, e);
+
+ let r = simd_select_bitmask([0b0101u8], a, b);
+ assert_eq!(r.0, e);
+
+ let a = Simd::<i32, 16>([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]);
+ let b = Simd::<i32, 16>([16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]);
+ let e = [16, 17, 2, 3, 20, 21, 22, 23, 24, 25, 26, 27, 12, 29, 14, 31];
+
+ let r = simd_select_bitmask(0b0101000000001100u16, a, b);
+ assert_eq!(r.0, e);
+
+ let r = simd_select_bitmask([0b1100u8, 0b01010000u8], a, b);
+ assert_eq!(r.0, e);
+ }
+}
LL | let _: &[i8] = data.into();
| ^^^^ the trait `From<&[u8]>` is not implemented for `&[i8]`
|
+ = help: the following implementations were found:
+ <[T; LANES] as From<Simd<T, LANES>>>
+ <[bool; LANES] as From<Mask<T, LANES>>>
= note: required because of the requirements on the impl of `Into<&[i8]>` for `&[u8]`
error: aborting due to previous error
-error[E0119]: conflicting implementations of trait `AnotherTrait` for type `impl OpaqueTrait`
- --> $DIR/issue-83613.rs:10:1
- |
-LL | impl<T: Send> AnotherTrait for T {}
- | -------------------------------- first implementation here
-LL | impl AnotherTrait for OpaqueType {}
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `impl OpaqueTrait`
-
error: cannot implement trait on type alias impl trait
--> $DIR/issue-83613.rs:10:1
|
LL | type OpaqueType = impl OpaqueTrait;
| ^^^^^^^^^^^^^^^^
+error[E0119]: conflicting implementations of trait `AnotherTrait` for type `impl OpaqueTrait`
+ --> $DIR/issue-83613.rs:10:1
+ |
+LL | impl<T: Send> AnotherTrait for T {}
+ | -------------------------------- first implementation here
+LL | impl AnotherTrait for OpaqueType {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `impl OpaqueTrait`
+
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0119`.
|
= help: add `#![feature(dispatch_from_dyn)]` to the crate attributes to enable
-error[E0378]: the trait `DispatchFromDyn` may only be implemented for a coercion between structures
- --> $DIR/issue-78372.rs:3:1
- |
-LL | impl<T> DispatchFromDyn<Smaht<U, MISC>> for T {}
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
error[E0210]: type parameter `T` must be covered by another type when it appears before the first local type (`Smaht<[type error], [type error]>`)
--> $DIR/issue-78372.rs:3:6
|
= note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type
= note: in this case, 'before' refers to the following order: `impl<..> ForeignTrait<T1, ..., Tn> for T0`, where `T0` is the first and `Tn` is the last
+error[E0378]: the trait `DispatchFromDyn` may only be implemented for a coercion between structures
+ --> $DIR/issue-78372.rs:3:1
+ |
+LL | impl<T> DispatchFromDyn<Smaht<U, MISC>> for T {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
error: aborting due to 7 previous errors
Some errors have detailed explanations: E0210, E0378, E0412, E0658.
impl MyTrait for () {}
impl<F> FnOnce<()> for &F {
- //~^ ERROR conflicting implementations
- //~| ERROR type parameter `F` must be used
+ //~^ ERROR type parameter `F` must be used
type Output = impl MyTrait;
extern "rust-call" fn call_once(self, _: ()) -> Self::Output {}
}
-error[E0119]: conflicting implementations of trait `std::ops::FnOnce<()>` for type `&_`
- --> $DIR/incoherent-assoc-imp-trait.rs:10:1
- |
-LL | impl<F> FnOnce<()> for &F {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = note: conflicting implementation in crate `core`:
- - impl<A, F> FnOnce<A> for &F
- where F: Fn<A>, F: ?Sized;
-
error[E0210]: type parameter `F` must be used as the type parameter for some local type (e.g., `MyStruct<F>`)
--> $DIR/incoherent-assoc-imp-trait.rs:10:6
|
= note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local
= note: only traits defined in the current crate can be implemented for a type parameter
-error: aborting due to 2 previous errors
+error: aborting due to previous error
-Some errors have detailed explanations: E0119, E0210.
-For more information about an error, try `rustc --explain E0119`.
+For more information about this error, try `rustc --explain E0210`.
--- /dev/null
+fn take_str_maybe(x: Option<&str>) -> Option<&str> { None }
+
+fn main() {
+ let string = String::from("Hello, world");
+ let option = Some(&string);
+ take_str_maybe(option);
+ //~^ ERROR: mismatched types [E0308]
+}
--- /dev/null
+error[E0308]: mismatched types
+ --> $DIR/issue-89856.rs:6:20
+ |
+LL | take_str_maybe(option);
+ | ^^^^^^ expected `str`, found struct `String`
+ |
+ = note: expected enum `Option<&str>`
+ found enum `Option<&String>`
+help: try converting the passed type into a `&str`
+ |
+LL | take_str_maybe(option.map(|x| &**x));
+ | ++++++++++++++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.
error[E0133]: dereference of raw pointer is unsafe and requires unsafe function or block
- --> $DIR/unsafe-unstable-const-fn.rs:11:5
+ --> $DIR/unsafe-unstable-const-fn.rs:10:5
|
LL | *a == b
| ^^ dereference of raw pointer
#![stable(feature = "foo", since = "1.33.0")]
#![feature(staged_api)]
-#![feature(const_raw_ptr_deref)]
#[stable(feature = "foo", since = "1.33.0")]
#[rustc_const_unstable(feature = "const_foo", issue = "none")]
error[E0133]: dereference of raw pointer is unsafe and requires unsafe function or block
- --> $DIR/unsafe-unstable-const-fn.rs:11:5
+ --> $DIR/unsafe-unstable-const-fn.rs:10:5
|
LL | *a == b
| ^^ dereference of raw pointer
PatKind::Path(path) => {
#[allow(clippy::match_same_arms)]
let id = match cx.qpath_res(path, pat.hir_id) {
- Res::Def(DefKind::Const | DefKind::ConstParam | DefKind::AnonConst, _) => return,
+ Res::Def(
+ DefKind::Const | DefKind::ConstParam | DefKind::AnonConst | DefKind::InlineConst,
+ _,
+ ) => return,
Res::Def(_, id) => id,
_ => return,
};
(files_read, errors)
}
+/// Default `tidy` command for macOS is too old that it does not have `mute-id` and `mute` options.
+/// `tidy` on macOS Monterey was released on 31 October 2006, and the same date can be seen seven
+/// years ago at <https://stackoverflow.com/questions/22283382/overwrite-osx-tidy>. Accordingly,
+/// the macOS environment using pre-installed `tidy` should immediately suspend HTML checker process
+/// and show a hint to install a newer one.
+#[cfg(target_os = "macos")]
+fn check_tidy_version() -> Result<(), String> {
+ let output = Command::new("tidy").arg("-v").output().expect("failed to run tidy command");
+ let version = String::from_utf8(output.stdout).expect("failed to read version of tidy command");
+ if version.contains("HTML Tidy for Mac OS X released on 31 October 2006") {
+ eprintln!("The pre-installed HTML Tidy for macOS is not supported.");
+ eprintln!("Consider installing a newer one and re-running.");
+ eprintln!("If you're using Homebrew, you can install it by the following command:");
+ eprintln!(" brew install tidy-html5");
+ eprintln!();
+ Err("HTML check failed: 1 error".to_string())
+ } else {
+ Ok(())
+ }
+}
+
fn main() -> Result<(), String> {
let args = env::args().collect::<Vec<_>>();
if args.len() != 2 {
return Err(format!("Usage: {} <doc folder>", args[0]));
}
+ #[cfg(target_os = "macos")]
+ check_tidy_version()?;
println!("Running HTML checker...");
-Subproject commit 9c18177cd36fe07a3c251234240a9c77a4e66785
+Subproject commit a8b976eb350acec83280a0cd1ca3ac99faff67bc
console.log(" --doc-folder [PATH] : location of the generated doc folder");
console.log(" --help : show this message then quit");
console.log(" --crate-name [STRING] : crate name to be used");
- console.log(" --test-file [PATH] : location of the JS test file");
+ console.log(" --test-file [PATHs] : location of the JS test files (can be called " +
+ "multiple times)");
console.log(" --test-folder [PATH] : location of the JS tests folder");
console.log(" --resource-suffix [STRING] : suffix to refer to the correct files");
}
"resource_suffix": "",
"doc_folder": "",
"test_folder": "",
- "test_file": "",
+ "test_file": [],
};
var correspondences = {
"--resource-suffix": "resource_suffix",
console.log("Missing argument after `" + args[i - 1] + "` option.");
return null;
}
- opts[correspondences[args[i - 1]]] = args[i];
+ if (args[i - 1] !== "--test-file") {
+ opts[correspondences[args[i - 1]]] = args[i];
+ } else {
+ opts[correspondences[args[i - 1]]].push(args[i]);
+ }
} else if (args[i] === "--help") {
showHelp();
process.exit(0);
var errors = 0;
if (opts["test_file"].length !== 0) {
- errors += checkFile(opts["test_file"], opts, loaded, index);
- }
- if (opts["test_folder"].length !== 0) {
+ opts["test_file"].forEach(function(file) {
+ errors += checkFile(file, opts, loaded, index);
+ });
+ } else if (opts["test_folder"].length !== 0) {
fs.readdirSync(opts["test_folder"]).forEach(function(file) {
if (!file.endsWith(".js")) {
return;
"compiler/rustc_codegen_gcc",
"src/llvm-project",
"library/backtrace",
+ "library/portable-simd",
"library/stdarch",
"src/tools/cargo",
"src/tools/clippy",