Auto merge of #148507 - Zalathar:rollup-vvz4knr, r=Zalathar

Rollup of 6 pull requests

Successful merges:

 - rust-lang/rust#147355 (Add alignment parameter to `simd_masked_{load,store}`)
 - rust-lang/rust#147925 (Fix tests for big-endian)
 - rust-lang/rust#148341 (compiler: Fix a couple issues around cargo feature unification)
 - rust-lang/rust#148371 (Dogfood `trim_{suffix|prefix}` in compiler)
 - rust-lang/rust#148495 (Implement Path::is_empty)
 - rust-lang/rust#148502 (rustc-dev-guide subtree update)

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2025-11-05 07:25:39 +00:00
commit 8e0b68e63c
59 changed files with 1889 additions and 474 deletions

View file

@ -2,6 +2,7 @@
use cranelift_codegen::ir::immediates::Offset32;
use rustc_abi::Endian;
use rustc_middle::ty::SimdAlign;
use super::*;
use crate::prelude::*;
@ -960,6 +961,15 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
let lane_clif_ty = fx.clif_type(val_lane_ty).unwrap();
let ptr_val = ptr.load_scalar(fx);
let alignment = generic_args[3].expect_const().to_value().valtree.unwrap_branch()[0]
.unwrap_leaf()
.to_simd_alignment();
let memflags = match alignment {
SimdAlign::Unaligned => MemFlags::new().with_notrap(),
_ => MemFlags::trusted(),
};
for lane_idx in 0..val_lane_count {
let val_lane = val.value_lane(fx, lane_idx).load_scalar(fx);
let mask_lane = mask.value_lane(fx, lane_idx).load_scalar(fx);
@ -972,7 +982,7 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
fx.bcx.switch_to_block(if_enabled);
let offset = lane_idx as i32 * lane_clif_ty.bytes() as i32;
fx.bcx.ins().store(MemFlags::trusted(), val_lane, ptr_val, Offset32::new(offset));
fx.bcx.ins().store(memflags, val_lane, ptr_val, Offset32::new(offset));
fx.bcx.ins().jump(next, &[]);
fx.bcx.seal_block(next);
@ -996,6 +1006,15 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
let lane_clif_ty = fx.clif_type(val_lane_ty).unwrap();
let ret_lane_layout = fx.layout_of(ret_lane_ty);
let alignment = generic_args[3].expect_const().to_value().valtree.unwrap_branch()[0]
.unwrap_leaf()
.to_simd_alignment();
let memflags = match alignment {
SimdAlign::Unaligned => MemFlags::new().with_notrap(),
_ => MemFlags::trusted(),
};
for lane_idx in 0..ptr_lane_count {
let val_lane = val.value_lane(fx, lane_idx).load_scalar(fx);
let ptr_lane = ptr.value_lane(fx, lane_idx).load_scalar(fx);
@ -1011,7 +1030,7 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
fx.bcx.seal_block(if_disabled);
fx.bcx.switch_to_block(if_enabled);
let res = fx.bcx.ins().load(lane_clif_ty, MemFlags::trusted(), ptr_lane, 0);
let res = fx.bcx.ins().load(lane_clif_ty, memflags, ptr_lane, 0);
fx.bcx.ins().jump(next, &[res.into()]);
fx.bcx.switch_to_block(if_disabled);

View file

@ -453,7 +453,7 @@ fn report_inline_asm(
llvm::DiagnosticLevel::Warning => Level::Warning,
llvm::DiagnosticLevel::Note | llvm::DiagnosticLevel::Remark => Level::Note,
};
let msg = msg.strip_prefix("error: ").unwrap_or(&msg).to_string();
let msg = msg.trim_prefix("error: ").to_string();
cgcx.diag_emitter.inline_asm_error(span, msg, level, source);
}

View file

@ -13,7 +13,7 @@ use rustc_hir::def_id::LOCAL_CRATE;
use rustc_hir::{self as hir};
use rustc_middle::mir::BinOp;
use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt, HasTypingEnv, LayoutOf};
use rustc_middle::ty::{self, GenericArgsRef, Instance, Ty, TyCtxt, TypingEnv};
use rustc_middle::ty::{self, GenericArgsRef, Instance, SimdAlign, Ty, TyCtxt, TypingEnv};
use rustc_middle::{bug, span_bug};
use rustc_span::{Span, Symbol, sym};
use rustc_symbol_mangling::{mangle_internal_symbol, symbol_name_for_instance_in_crate};
@ -1840,8 +1840,21 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
return Ok(call);
}
fn llvm_alignment<'ll, 'tcx>(
bx: &mut Builder<'_, 'll, 'tcx>,
alignment: SimdAlign,
vector_ty: Ty<'tcx>,
element_ty: Ty<'tcx>,
) -> u64 {
match alignment {
SimdAlign::Unaligned => 1,
SimdAlign::Element => bx.align_of(element_ty).bytes(),
SimdAlign::Vector => bx.align_of(vector_ty).bytes(),
}
}
if name == sym::simd_masked_load {
// simd_masked_load(mask: <N x i{M}>, pointer: *_ T, values: <N x T>) -> <N x T>
// simd_masked_load<_, _, _, const ALIGN: SimdAlign>(mask: <N x i{M}>, pointer: *_ T, values: <N x T>) -> <N x T>
// * N: number of elements in the input vectors
// * T: type of the element to load
// * M: any integer width is supported, will be truncated to i1
@ -1849,6 +1862,10 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
// those lanes whose `mask` bit is enabled.
// The memory addresses corresponding to the “off” lanes are not accessed.
let alignment = fn_args[3].expect_const().to_value().valtree.unwrap_branch()[0]
.unwrap_leaf()
.to_simd_alignment();
// The element type of the "mask" argument must be a signed integer type of any width
let mask_ty = in_ty;
let (mask_len, mask_elem) = (in_len, in_elem);
@ -1905,7 +1922,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
let mask = vector_mask_to_bitmask(bx, args[0].immediate(), m_elem_bitwidth, mask_len);
// Alignment of T, must be a constant integer value:
let alignment = bx.align_of(values_elem).bytes();
let alignment = llvm_alignment(bx, alignment, values_ty, values_elem);
let llvm_pointer = bx.type_ptr();
@ -1932,7 +1949,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
}
if name == sym::simd_masked_store {
// simd_masked_store(mask: <N x i{M}>, pointer: *mut T, values: <N x T>) -> ()
// simd_masked_store<_, _, _, const ALIGN: SimdAlign>(mask: <N x i{M}>, pointer: *mut T, values: <N x T>) -> ()
// * N: number of elements in the input vectors
// * T: type of the element to load
// * M: any integer width is supported, will be truncated to i1
@ -1940,6 +1957,10 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
// those lanes whose `mask` bit is enabled.
// The memory addresses corresponding to the “off” lanes are not accessed.
let alignment = fn_args[3].expect_const().to_value().valtree.unwrap_branch()[0]
.unwrap_leaf()
.to_simd_alignment();
// The element type of the "mask" argument must be a signed integer type of any width
let mask_ty = in_ty;
let (mask_len, mask_elem) = (in_len, in_elem);
@ -1990,7 +2011,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
let mask = vector_mask_to_bitmask(bx, args[0].immediate(), m_elem_bitwidth, mask_len);
// Alignment of T, must be a constant integer value:
let alignment = bx.align_of(values_elem).bytes();
let alignment = llvm_alignment(bx, alignment, values_ty, values_elem);
let llvm_pointer = bx.type_ptr();

View file

@ -17,6 +17,7 @@
#![feature(macro_derive)]
#![feature(rustdoc_internals)]
#![feature(slice_as_array)]
#![feature(trim_prefix_suffix)]
#![feature(try_blocks)]
// tidy-alphabetical-end

View file

@ -1,16 +1,16 @@
use either::Either;
use rustc_abi::Endian;
use rustc_abi::{BackendRepr, Endian};
use rustc_apfloat::ieee::{Double, Half, Quad, Single};
use rustc_apfloat::{Float, Round};
use rustc_middle::mir::interpret::{InterpErrorKind, UndefinedBehaviorInfo};
use rustc_middle::ty::FloatTy;
use rustc_middle::mir::interpret::{InterpErrorKind, Pointer, UndefinedBehaviorInfo};
use rustc_middle::ty::{FloatTy, SimdAlign};
use rustc_middle::{bug, err_ub_format, mir, span_bug, throw_unsup_format, ty};
use rustc_span::{Symbol, sym};
use tracing::trace;
use super::{
ImmTy, InterpCx, InterpResult, Machine, MinMax, MulAddType, OpTy, PlaceTy, Provenance, Scalar,
Size, interp_ok, throw_ub_format,
Size, TyAndLayout, assert_matches, interp_ok, throw_ub_format,
};
use crate::interpret::Writeable;
@ -644,6 +644,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
}
}
sym::simd_masked_load => {
let dest_layout = dest.layout;
let (mask, mask_len) = self.project_to_simd(&args[0])?;
let ptr = self.read_pointer(&args[1])?;
let (default, default_len) = self.project_to_simd(&args[2])?;
@ -652,6 +654,14 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
assert_eq!(dest_len, mask_len);
assert_eq!(dest_len, default_len);
self.check_simd_ptr_alignment(
ptr,
dest_layout,
generic_args[3].expect_const().to_value().valtree.unwrap_branch()[0]
.unwrap_leaf()
.to_simd_alignment(),
)?;
for i in 0..dest_len {
let mask = self.read_immediate(&self.project_index(&mask, i)?)?;
let default = self.read_immediate(&self.project_index(&default, i)?)?;
@ -660,7 +670,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
let val = if simd_element_to_bool(mask)? {
// Size * u64 is implemented as always checked
let ptr = ptr.wrapping_offset(dest.layout.size * i, self);
let place = self.ptr_to_mplace(ptr, dest.layout);
// we have already checked the alignment requirements
let place = self.ptr_to_mplace_unaligned(ptr, dest.layout);
self.read_immediate(&place)?
} else {
default
@ -675,6 +686,14 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
assert_eq!(mask_len, vals_len);
self.check_simd_ptr_alignment(
ptr,
args[2].layout,
generic_args[3].expect_const().to_value().valtree.unwrap_branch()[0]
.unwrap_leaf()
.to_simd_alignment(),
)?;
for i in 0..vals_len {
let mask = self.read_immediate(&self.project_index(&mask, i)?)?;
let val = self.read_immediate(&self.project_index(&vals, i)?)?;
@ -682,7 +701,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
if simd_element_to_bool(mask)? {
// Size * u64 is implemented as always checked
let ptr = ptr.wrapping_offset(val.layout.size * i, self);
let place = self.ptr_to_mplace(ptr, val.layout);
// we have already checked the alignment requirements
let place = self.ptr_to_mplace_unaligned(ptr, val.layout);
self.write_immediate(*val, &place)?
};
}
@ -753,6 +773,30 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
FloatTy::F128 => self.float_minmax::<Quad>(left, right, op)?,
})
}
fn check_simd_ptr_alignment(
&self,
ptr: Pointer<Option<M::Provenance>>,
vector_layout: TyAndLayout<'tcx>,
alignment: SimdAlign,
) -> InterpResult<'tcx> {
assert_matches!(vector_layout.backend_repr, BackendRepr::SimdVector { .. });
let align = match alignment {
ty::SimdAlign::Unaligned => {
// The pointer is supposed to be unaligned, so no check is required.
return interp_ok(());
}
ty::SimdAlign::Element => {
// Take the alignment of the only field, which is an array and therefore has the same
// alignment as the element type.
vector_layout.field(self, 0).align.abi
}
ty::SimdAlign::Vector => vector_layout.align.abi,
};
self.check_ptr_align(ptr, align)
}
}
fn simd_bitmask_index(idx: u32, vec_len: u32, endianness: Endian) -> u32 {

View file

@ -13,6 +13,7 @@
#![feature(panic_backtrace_config)]
#![feature(panic_update_hook)]
#![feature(rustdoc_internals)]
#![feature(trim_prefix_suffix)]
#![feature(try_blocks)]
// tidy-alphabetical-end
@ -466,7 +467,7 @@ pub enum Compilation {
fn handle_explain(early_dcx: &EarlyDiagCtxt, registry: Registry, code: &str, color: ColorConfig) {
// Allow "E0123" or "0123" form.
let upper_cased_code = code.to_ascii_uppercase();
if let Ok(code) = upper_cased_code.strip_prefix('E').unwrap_or(&upper_cased_code).parse::<u32>()
if let Ok(code) = upper_cased_code.trim_prefix('E').parse::<u32>()
&& code <= ErrCode::MAX_AS_U32
&& let Ok(description) = registry.try_find_description(ErrCode::from_u32(code))
{
@ -1267,7 +1268,7 @@ fn warn_on_confusing_output_filename_flag(
if let Some(name) = matches.opt_str("o")
&& let Some(suspect) = args.iter().find(|arg| arg.starts_with("-o") && *arg != "-o")
{
let filename = suspect.strip_prefix("-").unwrap_or(suspect);
let filename = suspect.trim_prefix("-");
let optgroups = config::rustc_optgroups();
let fake_args = ["optimize", "o0", "o1", "o2", "o3", "ofast", "og", "os", "oz"];

View file

@ -695,8 +695,8 @@ pub(crate) fn check_intrinsic_type(
(1, 0, vec![param(0), param(0), param(0)], param(0))
}
sym::simd_gather => (3, 0, vec![param(0), param(1), param(2)], param(0)),
sym::simd_masked_load => (3, 0, vec![param(0), param(1), param(2)], param(2)),
sym::simd_masked_store => (3, 0, vec![param(0), param(1), param(2)], tcx.types.unit),
sym::simd_masked_load => (3, 1, vec![param(0), param(1), param(2)], param(2)),
sym::simd_masked_store => (3, 1, vec![param(0), param(1), param(2)], tcx.types.unit),
sym::simd_scatter => (3, 0, vec![param(0), param(1), param(2)], tcx.types.unit),
sym::simd_insert | sym::simd_insert_dyn => {
(2, 0, vec![param(0), tcx.types.u32, param(1)], param(0))

View file

@ -7,6 +7,7 @@
#![feature(iter_intersperse)]
#![feature(iter_order_by)]
#![feature(never_type)]
#![feature(trim_prefix_suffix)]
// tidy-alphabetical-end
mod _match;

View file

@ -2551,7 +2551,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// If this is a floating point literal that ends with '.',
// get rid of it to stop this from becoming a member access.
let snippet = snippet.strip_suffix('.').unwrap_or(&snippet);
let snippet = snippet.trim_suffix('.');
err.span_suggestion(
lit.span,
format!(

View file

@ -8,7 +8,7 @@ edition = "2024"
rustc_index_macros = { path = "../rustc_index_macros" }
rustc_macros = { path = "../rustc_macros", optional = true }
rustc_serialize = { path = "../rustc_serialize", optional = true }
smallvec = "1.8.1"
smallvec = { version = "1.8.1", optional = true }
# tidy-alphabetical-end
[features]
@ -17,6 +17,7 @@ default = ["nightly"]
nightly = [
"dep:rustc_macros",
"dep:rustc_serialize",
"dep:smallvec",
"rustc_index_macros/nightly",
]
rustc_randomized_layouts = []

View file

@ -39,6 +39,15 @@ pub enum AtomicOrdering {
SeqCst = 4,
}
/// An enum to represent the compiler-side view of `intrinsics::simd::SimdAlign`.
#[derive(Debug, Copy, Clone)]
pub enum SimdAlign {
// These values must match `intrinsics::simd::SimdAlign`!
Unaligned = 0,
Element = 1,
Vector = 2,
}
impl std::fmt::Debug for ConstInt {
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { int, signed, is_ptr_sized_integral } = *self;
@ -350,6 +359,21 @@ impl ScalarInt {
}
}
#[inline]
pub fn to_simd_alignment(self) -> SimdAlign {
use SimdAlign::*;
let val = self.to_u32();
if val == Unaligned as u32 {
Unaligned
} else if val == Element as u32 {
Element
} else if val == Vector as u32 {
Vector
} else {
panic!("not a valid simd alignment")
}
}
/// Converts the `ScalarInt` to `bool`.
/// Panics if the `size` of the `ScalarInt` is not equal to 1 byte.
/// Errors if it is not a valid `bool`.

View file

@ -74,7 +74,7 @@ pub use self::closure::{
};
pub use self::consts::{
AnonConstKind, AtomicOrdering, Const, ConstInt, ConstKind, ConstToValTreeResult, Expr,
ExprKind, ScalarInt, UnevaluatedConst, ValTree, ValTreeKind, Value,
ExprKind, ScalarInt, SimdAlign, UnevaluatedConst, ValTree, ValTreeKind, Value,
};
pub use self::context::{
CtxtInterners, CurrentGcx, Feed, FreeRegionInfo, GlobalCtxt, Lift, TyCtxt, TyCtxtFeed, tls,

View file

@ -2608,7 +2608,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
let (span, text) = match path.segments.first() {
Some(seg) if let Some(name) = seg.ident.as_str().strip_prefix("let") => {
// a special case for #117894
let name = name.strip_prefix('_').unwrap_or(name);
let name = name.trim_prefix('_');
(ident_span, format!("let {name}"))
}
_ => (ident_span.shrink_to_lo(), "let ".to_string()),

View file

@ -22,6 +22,7 @@
#![feature(ptr_as_ref_unchecked)]
#![feature(rustc_attrs)]
#![feature(rustdoc_internals)]
#![feature(trim_prefix_suffix)]
#![recursion_limit = "256"]
// tidy-alphabetical-end

View file

@ -400,10 +400,10 @@ fn preprocess_link(link: &str) -> Box<str> {
let link = link.split('#').next().unwrap();
let link = link.trim();
let link = link.rsplit('@').next().unwrap();
let link = link.strip_suffix("()").unwrap_or(link);
let link = link.strip_suffix("{}").unwrap_or(link);
let link = link.strip_suffix("[]").unwrap_or(link);
let link = if link != "!" { link.strip_suffix('!').unwrap_or(link) } else { link };
let link = link.trim_suffix("()");
let link = link.trim_suffix("{}");
let link = link.trim_suffix("[]");
let link = if link != "!" { link.trim_suffix('!') } else { link };
let link = link.trim();
strip_generics_from_path(link).unwrap_or_else(|_| link.into())
}

View file

@ -10,6 +10,6 @@ proc-macro = true
# tidy-alphabetical-start
proc-macro2 = "1"
quote = "1"
syn = { version = "2.0.9", features = ["full"] }
syn = { version = "2.0.9", features = ["full", "visit-mut"] }
synstructure = "0.13.0"
# tidy-alphabetical-end

View file

@ -2,6 +2,8 @@
//!
//! In this module, a "vector" is any `repr(simd)` type.
use crate::marker::ConstParamTy;
/// Inserts an element into a vector, returning the updated vector.
///
/// `T` must be a vector with element type `U`, and `idx` must be `const`.
@ -377,6 +379,19 @@ pub unsafe fn simd_gather<T, U, V>(val: T, ptr: U, mask: V) -> T;
#[rustc_nounwind]
pub unsafe fn simd_scatter<T, U, V>(val: T, ptr: U, mask: V);
/// A type for alignment options for SIMD masked load/store intrinsics.
#[derive(Debug, ConstParamTy, PartialEq, Eq)]
pub enum SimdAlign {
// These values must match the compiler's `SimdAlign` defined in
// `rustc_middle/src/ty/consts/int.rs`!
/// No alignment requirements on the pointer
Unaligned = 0,
/// The pointer must be aligned to the element type of the SIMD vector
Element = 1,
/// The pointer must be aligned to the SIMD vector type
Vector = 2,
}
/// Reads a vector of pointers.
///
/// `T` must be a vector.
@ -392,13 +407,12 @@ pub unsafe fn simd_scatter<T, U, V>(val: T, ptr: U, mask: V);
/// `val`.
///
/// # Safety
/// Unmasked values in `T` must be readable as if by `<ptr>::read` (e.g. aligned to the element
/// type).
/// `ptr` must be aligned according to the `ALIGN` parameter, see [`SimdAlign`] for details.
///
/// `mask` must only contain `0` or `!0` values.
#[rustc_intrinsic]
#[rustc_nounwind]
pub unsafe fn simd_masked_load<V, U, T>(mask: V, ptr: U, val: T) -> T;
pub unsafe fn simd_masked_load<V, U, T, const ALIGN: SimdAlign>(mask: V, ptr: U, val: T) -> T;
/// Writes to a vector of pointers.
///
@ -414,13 +428,12 @@ pub unsafe fn simd_masked_load<V, U, T>(mask: V, ptr: U, val: T) -> T;
/// Otherwise if the corresponding value in `mask` is `0`, do nothing.
///
/// # Safety
/// Unmasked values in `T` must be writeable as if by `<ptr>::write` (e.g. aligned to the element
/// type).
/// `ptr` must be aligned according to the `ALIGN` parameter, see [`SimdAlign`] for details.
///
/// `mask` must only contain `0` or `!0` values.
#[rustc_intrinsic]
#[rustc_nounwind]
pub unsafe fn simd_masked_store<V, U, T>(mask: V, ptr: U, val: T);
pub unsafe fn simd_masked_store<V, U, T, const ALIGN: SimdAlign>(mask: V, ptr: U, val: T);
/// Adds two simd vectors elementwise, with saturation.
///

View file

@ -474,7 +474,14 @@ where
or: Self,
) -> Self {
// SAFETY: The safety of reading elements through `ptr` is ensured by the caller.
unsafe { core::intrinsics::simd::simd_masked_load(enable.to_int(), ptr, or) }
unsafe {
core::intrinsics::simd::simd_masked_load::<
_,
_,
_,
{ core::intrinsics::simd::SimdAlign::Element },
>(enable.to_int(), ptr, or)
}
}
/// Reads from potentially discontiguous indices in `slice` to construct a SIMD vector.
@ -723,7 +730,14 @@ where
#[inline]
pub unsafe fn store_select_ptr(self, ptr: *mut T, enable: Mask<<T as SimdElement>::Mask, N>) {
// SAFETY: The safety of writing elements through `ptr` is ensured by the caller.
unsafe { core::intrinsics::simd::simd_masked_store(enable.to_int(), ptr, self) }
unsafe {
core::intrinsics::simd::simd_masked_store::<
_,
_,
_,
{ core::intrinsics::simd::SimdAlign::Element },
>(enable.to_int(), ptr, self)
}
}
/// Writes the values in a SIMD vector to potentially discontiguous indices in `slice`.

View file

@ -2756,6 +2756,28 @@ impl Path {
iter_after(self.components().rev(), child.components().rev()).is_some()
}
/// Checks whether the `Path` is empty.
///
/// # Examples
///
/// ```
/// #![feature(path_is_empty)]
/// use std::path::Path;
///
/// let path = Path::new("");
/// assert!(path.is_empty());
///
/// let path = Path::new("foo");
/// assert!(!path.is_empty());
///
/// let path = Path::new(".");
/// assert!(!path.is_empty());
/// ```
#[unstable(feature = "path_is_empty", issue = "148494")]
pub fn is_empty(&self) -> bool {
self.as_os_str().is_empty()
}
/// Extracts the stem (non-extension) portion of [`self.file_name`].
///
/// [`self.file_name`]: Path::file_name

View file

@ -82,3 +82,9 @@ jobs:
git add .
git commit -m "Deploy ${GITHUB_SHA} to gh-pages"
git push --quiet -f "https://x-token:${{ secrets.GITHUB_TOKEN }}@github.com/${GITHUB_REPOSITORY}" HEAD:gh-pages
- name: Check if files comply with semantic line breaks
continue-on-error: true
run: |
# using split_inclusive that uses regex feature that uses an unstable feature
RUSTC_BOOTSTRAP=1 cargo run --manifest-path ci/sembr/Cargo.toml src

View file

@ -1,6 +1,7 @@
book
ci/date-check/target/
ci/sembr/target/
# Generated by check-in.sh
pulls.json

View file

@ -0,0 +1,466 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "aho-corasick"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
dependencies = [
"memchr",
]
[[package]]
name = "anstream"
version = "0.6.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
"is_terminal_polyfill",
"utf8parse",
]
[[package]]
name = "anstyle"
version = "1.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78"
[[package]]
name = "anstyle-parse"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2"
dependencies = [
"windows-sys 0.60.2",
]
[[package]]
name = "anstyle-wincon"
version = "3.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a"
dependencies = [
"anstyle",
"once_cell_polyfill",
"windows-sys 0.60.2",
]
[[package]]
name = "anyhow"
version = "1.0.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
[[package]]
name = "bstr"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4"
dependencies = [
"memchr",
"serde",
]
[[package]]
name = "clap"
version = "4.5.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c2cfd7bf8a6017ddaa4e32ffe7403d547790db06bd171c1c53926faab501623"
dependencies = [
"clap_builder",
"clap_derive",
]
[[package]]
name = "clap_builder"
version = "4.5.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a4c05b9e80c5ccd3a7ef080ad7b6ba7d6fc00a985b8b157197075677c82c7a0"
dependencies = [
"anstream",
"anstyle",
"clap_lex",
"strsim",
]
[[package]]
name = "clap_derive"
version = "4.5.49"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671"
dependencies = [
"heck",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "clap_lex"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d"
[[package]]
name = "colorchoice"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
[[package]]
name = "crossbeam-deque"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
dependencies = [
"crossbeam-epoch",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-epoch"
version = "0.9.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
dependencies = [
"crossbeam-utils",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
[[package]]
name = "foldhash"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
[[package]]
name = "globset"
version = "0.4.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52dfc19153a48bde0cbd630453615c8151bce3a5adfac7a0aebfbf0a1e1f57e3"
dependencies = [
"aho-corasick",
"bstr",
"log",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "hashbrown"
version = "0.15.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
dependencies = [
"foldhash",
]
[[package]]
name = "heck"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "ignore"
version = "0.4.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81776e6f9464432afcc28d03e52eb101c93b6f0566f52aef2427663e700f0403"
dependencies = [
"crossbeam-deque",
"globset",
"log",
"memchr",
"regex-automata",
"same-file",
"walkdir",
"winapi-util",
]
[[package]]
name = "imara-diff"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f01d462f766df78ab820dd06f5eb700233c51f0f4c2e846520eaf4ba6aa5c5c"
dependencies = [
"hashbrown",
"memchr",
]
[[package]]
name = "is_terminal_polyfill"
version = "1.70.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
[[package]]
name = "log"
version = "0.4.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432"
[[package]]
name = "memchr"
version = "2.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
[[package]]
name = "once_cell_polyfill"
version = "1.70.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
[[package]]
name = "proc-macro2"
version = "1.0.103"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1"
dependencies = [
"proc-macro2",
]
[[package]]
name = "regex"
version = "1.12.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
[[package]]
name = "same-file"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
dependencies = [
"winapi-util",
]
[[package]]
name = "sembr"
version = "0.0.0"
dependencies = [
"anyhow",
"clap",
"ignore",
"imara-diff",
"regex",
]
[[package]]
name = "serde"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
dependencies = [
"serde_core",
]
[[package]]
name = "serde_core"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "strsim"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "syn"
version = "2.0.108"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da58917d35242480a05c2897064da0a80589a2a0476c9a3f2fdc83b53502e917"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "unicode-ident"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "462eeb75aeb73aea900253ce739c8e18a67423fadf006037cd3ff27e82748a06"
[[package]]
name = "utf8parse"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "walkdir"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
dependencies = [
"same-file",
"winapi-util",
]
[[package]]
name = "winapi-util"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
dependencies = [
"windows-sys 0.61.2",
]
[[package]]
name = "windows-link"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
name = "windows-sys"
version = "0.60.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-sys"
version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
dependencies = [
"windows-link",
]
[[package]]
name = "windows-targets"
version = "0.53.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3"
dependencies = [
"windows-link",
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
[[package]]
name = "windows_aarch64_msvc"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
[[package]]
name = "windows_i686_gnu"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3"
[[package]]
name = "windows_i686_gnullvm"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
[[package]]
name = "windows_i686_msvc"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
[[package]]
name = "windows_x86_64_gnu"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
[[package]]
name = "windows_x86_64_msvc"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"

View file

@ -0,0 +1,16 @@
[package]
name = "sembr"
edition = "2024"
[dependencies]
anyhow = "1"
ignore = "0.4"
imara-diff = "0.2"
[dependencies.regex]
version = "1"
features = ["pattern"]
[dependencies.clap]
version = "4"
features = ["derive"]

View file

@ -0,0 +1,291 @@
use std::path::PathBuf;
use std::sync::LazyLock;
use std::{fs, process};
use anyhow::Result;
use clap::Parser;
use ignore::Walk;
use imara_diff::{Algorithm, BasicLineDiffPrinter, Diff, InternedInput, UnifiedDiffConfig};
use regex::Regex;
#[derive(Parser)]
struct Cli {
/// File or directory to check
path: PathBuf,
#[arg(long)]
/// Modify files that do not comply
overwrite: bool,
/// Applies to lines that are to be split
#[arg(long, default_value_t = 100)]
line_length_limit: usize,
#[arg(long)]
show_diff: bool,
}
static REGEX_IGNORE: LazyLock<Regex> =
LazyLock::new(|| Regex::new(r"^\s*(\d\.|\-|\*)\s+").unwrap());
static REGEX_IGNORE_END: LazyLock<Regex> = LazyLock::new(|| Regex::new(r"(\.|\?|;|!)$").unwrap());
static REGEX_IGNORE_LINK_TARGETS: LazyLock<Regex> =
LazyLock::new(|| Regex::new(r"^\[.+\]: ").unwrap());
static REGEX_SPLIT: LazyLock<Regex> = LazyLock::new(|| Regex::new(r"(\.|[^r]\?|;|!)\s+").unwrap());
fn main() -> Result<()> {
let cli = Cli::parse();
let mut compliant = Vec::new();
let mut not_compliant = Vec::new();
let mut made_compliant = Vec::new();
for result in Walk::new(cli.path) {
let entry = result?;
if entry.file_type().expect("no stdin").is_dir() {
continue;
}
let path = entry.into_path();
if let Some(extension) = path.extension() {
if extension != "md" {
continue;
}
let old = fs::read_to_string(&path)?;
let new = lengthen_lines(&comply(&old), cli.line_length_limit);
if new == old {
compliant.push(path.clone());
} else if cli.overwrite {
fs::write(&path, new)?;
made_compliant.push(path.clone());
} else if cli.show_diff {
println!("{}:", path.display());
show_diff(&old, &new);
println!("---");
} else {
not_compliant.push(path.clone());
}
}
}
if !compliant.is_empty() {
display("compliant", &compliant);
}
if !made_compliant.is_empty() {
display("made compliant", &made_compliant);
}
if !not_compliant.is_empty() {
display("not compliant", &not_compliant);
process::exit(1);
}
Ok(())
}
fn show_diff(old: &str, new: &str) {
let input = InternedInput::new(old, new);
let mut diff = Diff::compute(Algorithm::Histogram, &input);
diff.postprocess_lines(&input);
let diff = diff
.unified_diff(&BasicLineDiffPrinter(&input.interner), UnifiedDiffConfig::default(), &input)
.to_string();
print!("{diff}");
}
fn display(header: &str, paths: &[PathBuf]) {
println!("{header}:");
for element in paths {
println!("- {}", element.display());
}
}
fn ignore(line: &str, in_code_block: bool) -> bool {
in_code_block
|| line.to_lowercase().contains("e.g.")
|| line.contains("i.e.")
|| line.contains('|')
|| line.trim_start().starts_with('>')
|| line.starts_with('#')
|| line.trim().is_empty()
|| REGEX_IGNORE.is_match(line)
|| REGEX_IGNORE_LINK_TARGETS.is_match(line)
}
fn comply(content: &str) -> String {
let content: Vec<_> = content.lines().map(std::borrow::ToOwned::to_owned).collect();
let mut new_content = content.clone();
let mut new_n = 0;
let mut in_code_block = false;
for (n, line) in content.into_iter().enumerate() {
if n != 0 {
new_n += 1;
}
if line.trim_start().starts_with("```") {
in_code_block = !in_code_block;
continue;
}
if ignore(&line, in_code_block) {
continue;
}
if REGEX_SPLIT.is_match(&line) {
let indent = line.find(|ch: char| !ch.is_whitespace()).unwrap();
let new_lines: Vec<_> = line
.split_inclusive(&*REGEX_SPLIT)
.map(|portion| format!("{:indent$}{}", "", portion.trim()))
.collect();
new_content.splice(new_n..=new_n, new_lines.clone());
new_n += new_lines.len() - 1;
}
}
new_content.join("\n") + "\n"
}
fn lengthen_lines(content: &str, limit: usize) -> String {
let content: Vec<_> = content.lines().map(std::borrow::ToOwned::to_owned).collect();
let mut new_content = content.clone();
let mut new_n = 0;
let mut in_code_block = false;
let mut skip_next = false;
for (n, line) in content.iter().enumerate() {
if skip_next {
skip_next = false;
continue;
}
if n != 0 {
new_n += 1;
}
if line.trim_start().starts_with("```") {
in_code_block = !in_code_block;
continue;
}
if ignore(line, in_code_block) || REGEX_SPLIT.is_match(line) {
continue;
}
let Some(next_line) = content.get(n + 1) else {
continue;
};
if ignore(next_line, in_code_block) || REGEX_IGNORE_END.is_match(line) {
continue;
}
if line.len() + next_line.len() < limit {
new_content[new_n] = format!("{line} {}", next_line.trim_start());
new_content.remove(new_n + 1);
skip_next = true;
}
}
new_content.join("\n") + "\n"
}
#[test]
fn test_sembr() {
let original = "\
# some. heading
must! be; split? and. normalizes space
1. ignore numbered
ignore | tables
ignore e.g. and
ignore i.e. and
ignore E.g. too
- ignore. list
* ignore. list
```
some code. block
```
sentence with *italics* should not be ignored. truly.
";
let expected = "\
# some. heading
must!
be;
split?
and.
normalizes space
1. ignore numbered
ignore | tables
ignore e.g. and
ignore i.e. and
ignore E.g. too
- ignore. list
* ignore. list
```
some code. block
```
sentence with *italics* should not be ignored.
truly.
";
assert_eq!(expected, comply(original));
}
#[test]
fn test_prettify() {
let original = "\
do not split
short sentences
";
let expected = "\
do not split short sentences
";
assert_eq!(expected, lengthen_lines(original, 50));
}
#[test]
fn test_prettify_prefix_spaces() {
let original = "\
do not split
short sentences
";
let expected = "\
do not split short sentences
";
assert_eq!(expected, lengthen_lines(original, 50));
}
#[test]
fn test_prettify_ignore_link_targets() {
let original = "\
[a target]: https://example.com
[another target]: https://example.com
";
assert_eq!(original, lengthen_lines(original, 100));
}
#[test]
fn test_sembr_then_prettify() {
let original = "\
hi there. do
not split
short sentences.
hi again.
";
let expected = "\
hi there.
do
not split
short sentences.
hi again.
";
let processed = comply(original);
assert_eq!(expected, processed);
let expected = "\
hi there.
do not split
short sentences.
hi again.
";
let processed = lengthen_lines(&processed, 50);
assert_eq!(expected, processed);
let expected = "\
hi there.
do not split short sentences.
hi again.
";
let processed = lengthen_lines(&processed, 50);
assert_eq!(expected, processed);
}
#[test]
fn test_sembr_question_mark() {
let original = "\
o? whatever
r? @reviewer
r? @reviewer
";
let expected = "\
o?
whatever
r? @reviewer
r? @reviewer
";
assert_eq!(expected, comply(original));
}

View file

@ -1 +1 @@
b1b464d6f61ec8c4e609c1328106378c066a9729
c5dabe8cf798123087d094f06417f5a767ca73e8

View file

@ -20,7 +20,7 @@ The detailed documentation for the `std::autodiff` module is available at [std::
Differentiable programming is used in various fields like numerical computing, [solid mechanics][ratel], [computational chemistry][molpipx], [fluid dynamics][waterlily] or for Neural Network training via Backpropagation, [ODE solver][diffsol], [differentiable rendering][libigl], [quantum computing][catalyst], and climate simulations.
[ratel]: https://gitlab.com/micromorph/ratel
[molpipx]: https://arxiv.org/abs/2411.17011v
[molpipx]: https://arxiv.org/abs/2411.17011
[waterlily]: https://github.com/WaterLily-jl/WaterLily.jl
[diffsol]: https://github.com/martinjrobins/diffsol
[libigl]: https://github.com/alecjacobson/libigl-enzyme-example?tab=readme-ov-file#run

View file

@ -2,9 +2,9 @@
## Bug reports
While bugs are unfortunate, they're a reality in software. We can't fix what we
don't know about, so please report liberally. If you're not sure if something
is a bug or not, feel free to file a bug anyway.
While bugs are unfortunate, they're a reality in software.
We can't fix what we don't know about, so please report liberally.
If you're not sure if something is a bug, feel free to open an issue anyway.
**If you believe reporting your bug publicly represents a security risk to Rust users,
please follow our [instructions for reporting security vulnerabilities][vuln]**.
@ -12,16 +12,18 @@ please follow our [instructions for reporting security vulnerabilities][vuln]**.
[vuln]: https://www.rust-lang.org/policies/security
If you're using the nightly channel, please check if the bug exists in the
latest toolchain before filing your bug. It might be fixed already.
latest toolchain before filing your bug.
It might be fixed already.
If you have the chance, before reporting a bug, please [search existing issues],
as it's possible that someone else has already reported your error. This doesn't
always work, and sometimes it's hard to know what to search for, so consider this
extra credit. We won't mind if you accidentally file a duplicate report.
as it's possible that someone else has already reported your error.
This doesn't always work, and sometimes it's hard to know what to search for, so consider this
extra credit.
We won't mind if you accidentally file a duplicate report.
Similarly, to help others who encountered the bug find your issue, consider
filing an issue with a descriptive title, which contains information that might
be unique to it. This can be the language or compiler feature used, the
filing an issue with a descriptive title, which contains information that might be unique to it.
This can be the language or compiler feature used, the
conditions that trigger the bug, or part of the error message if there is any.
An example could be: **"impossible case reached" on lifetime inference for impl
Trait in return position**.
@ -31,14 +33,15 @@ in the appropriate provided template.
## Bug fixes or "normal" code changes
For most PRs, no special procedures are needed. You can just [open a PR], and it
will be reviewed, approved, and merged. This includes most bug fixes,
refactorings, and other user-invisible changes. The next few sections talk
about exceptions to this rule.
For most PRs, no special procedures are needed.
You can just [open a PR], and it will be reviewed, approved, and merged.
This includes most bug fixes, refactorings, and other user-invisible changes.
The next few sections talk about exceptions to this rule.
Also, note that it is perfectly acceptable to open WIP PRs or GitHub [Draft PRs].
Some people prefer to do this so they can get feedback along the
way or share their code with a collaborator. Others do this so they can utilize
way or share their code with a collaborator.
Others do this so they can utilize
the CI to build and test their PR (e.g. when developing on a slow machine).
[open a PR]: #pull-requests
@ -46,9 +49,9 @@ the CI to build and test their PR (e.g. when developing on a slow machine).
## New features
Rust has strong backwards-compatibility guarantees. Thus, new features can't
just be implemented directly in stable Rust. Instead, we have 3 release
channels: stable, beta, and nightly.
Rust has strong backwards-compatibility guarantees.
Thus, new features can't just be implemented directly in stable Rust.
Instead, we have 3 release channels: stable, beta, and nightly.
- **Stable**: this is the latest stable release for general usage.
- **Beta**: this is the next release (will be stable within 6 weeks).
@ -65,35 +68,36 @@ Breaking changes have a [dedicated section][Breaking Changes] in the dev-guide.
### Major changes
The compiler team has a special process for large changes, whether or not they
cause breakage. This process is called a Major Change Proposal (MCP). MCP is a
relatively lightweight mechanism for getting feedback on large changes to the
The compiler team has a special process for large changes, whether or not they cause breakage.
This process is called a Major Change Proposal (MCP).
MCP is a relatively lightweight mechanism for getting feedback on large changes to the
compiler (as opposed to a full RFC or a design meeting with the team).
Example of things that might require MCPs include major refactorings, changes
to important types, or important changes to how the compiler does something, or
smaller user-facing changes.
**When in doubt, ask on [Zulip]. It would be a shame to put a lot of work
into a PR that ends up not getting merged!** [See this document][mcpinfo] for
more info on MCPs.
**When in doubt, ask [on Zulip].
It would be a shame to put a lot of work
into a PR that ends up not getting merged!** [See this document][mcpinfo] for more info on MCPs.
[mcpinfo]: https://forge.rust-lang.org/compiler/proposals-and-stabilization.html#how-do-i-submit-an-mcp
[zulip]: https://rust-lang.zulipchat.com/#narrow/stream/131828-t-compiler
[on Zulip]: https://rust-lang.zulipchat.com/#narrow/stream/131828-t-compiler
### Performance
Compiler performance is important. We have put a lot of effort over the last
few years into [gradually improving it][perfdash].
Compiler performance is important.
We have put a lot of effort over the last few years into [gradually improving it][perfdash].
[perfdash]: https://perf.rust-lang.org/dashboard.html
If you suspect that your change may cause a performance regression (or
improvement), you can request a "perf run" (and your reviewer may also request one
before approving). This is yet another bot that will compile a collection of
benchmarks on a compiler with your changes. The numbers are reported
[here][perf], and you can see a comparison of your changes against the latest
master.
before approving).
This is yet another bot that will compile a collection of
benchmarks on a compiler with your changes.
The numbers are reported
[here][perf], and you can see a comparison of your changes against the latest master.
> For an introduction to the performance of Rust code in general
> which would also be useful in rustc development, see [The Rust Performance Book].
@ -104,11 +108,11 @@ master.
## Pull requests
Pull requests (or PRs for short) are the primary mechanism we use to change Rust.
GitHub itself has some [great documentation][about-pull-requests] on using the
Pull Request feature. We use the "fork and pull" model [described here][development-models],
GitHub itself has some [great documentation][about-pull-requests] on using the Pull Request feature.
We use the ["fork and pull" model][development-models],
where contributors push changes to their personal fork and create pull requests to
bring those changes into the source repository. We have more info about how to use git
when contributing to Rust under [the git section](./git.md).
bring those changes into the source repository.
We have [a chapter](git.md) on how to use Git when contributing to Rust.
> **Advice for potentially large, complex, cross-cutting and/or very domain-specific changes**
>
@ -150,7 +154,8 @@ when contributing to Rust under [the git section](./git.md).
### Keeping your branch up-to-date
The CI in rust-lang/rust applies your patches directly against the current master,
not against the commit your branch is based on. This can lead to unexpected failures
not against the commit your branch is based on.
This can lead to unexpected failures
if your branch is outdated, even when there are no explicit merge conflicts.
Update your branch only when needed: when you have merge conflicts, upstream CI is broken and blocking your green PR, or a maintainer requests it.
@ -159,30 +164,31 @@ During review, make incremental commits to address feedback.
Prefer to squash or rebase only at the end, or when a reviewer requests it.
When updating, use `git push --force-with-lease` and leave a brief comment explaining what changed.
Some repos prefer merging from `upstream/master` instead of rebasing; follow the project's conventions.
Some repos prefer merging from `upstream/master` instead of rebasing;
follow the project's conventions.
See [keeping things up to date](git.md#keeping-things-up-to-date) for detailed instructions.
After rebasing, it's recommended to [run the relevant tests locally](tests/intro.md) to catch any issues before CI runs.
### r?
All pull requests are reviewed by another person. We have a bot,
[@rustbot], that will automatically assign a random person
All pull requests are reviewed by another person.
We have a bot, [@rustbot], that will automatically assign a random person
to review your request based on which files you changed.
If you want to request that a specific person reviews your pull request, you
can add an `r?` to the pull request description or in a comment. For example,
if you want to ask a review to @awesome-reviewer, add
can add an `r?` to the pull request description or in a comment.
For example, if you want to ask a review by @awesome-reviewer,
add the following to the end of the pull request description:
r? @awesome-reviewer
to the end of the pull request description, and [@rustbot] will assign
them instead of a random person. This is entirely optional.
[@rustbot] will then assign the PR to that reviewer instead of a random person.
This is entirely optional.
You can also assign a random reviewer from a specific team by writing `r? rust-lang/groupname`.
As an example,
if you were making a diagnostics change,
then you could get a reviewer from the diagnostics team by adding:
As an example, if you were making a diagnostics change,
you could get a reviewer from the diagnostics team by adding:
r? rust-lang/diagnostics
@ -209,15 +215,15 @@ or the list of teams in the [rust-lang teams database].
> the author is ready for a review,
> and this PR will be queued again in the reviewer's queue.
Please note that the reviewers are humans, who for the most part work on `rustc`
in their free time. This means that they can take some time to respond and review
your PR. It also means that reviewers can miss some PRs that are assigned to them.
Please note that the reviewers are humans, who for the most part work on `rustc` in their free time.
This means that they can take some time to respond and review your PR.
It also means that reviewers can miss some PRs that are assigned to them.
To try to move PRs forward, the Triage WG regularly goes through all PRs that
are waiting for review and haven't been discussed for at least 2 weeks. If you
don't get a review within 2 weeks, feel free to ask the Triage WG on
Zulip ([#t-release/triage]). They have knowledge of when to ping, who might be
on vacation, etc.
are waiting for review and haven't been discussed for at least 2 weeks.
If you don't get a review within 2 weeks, feel free to ask the Triage WG on
Zulip ([#t-release/triage]).
They have knowledge of when to ping, who might be on vacation, etc.
The reviewer may request some changes using the GitHub code review interface.
They may also request special procedures for some PRs.
@ -230,7 +236,8 @@ See [Crater] and [Breaking Changes] chapters for some examples of such procedure
### CI
In addition to being reviewed by a human, pull requests are automatically tested,
thanks to continuous integration (CI). Basically, every time you open and update
thanks to continuous integration (CI).
Basically, every time you open and update
a pull request, CI builds the compiler and tests it against the
[compiler test suite], and also performs other tests such as checking that
your pull request is in compliance with Rust's style guidelines.
@ -240,58 +247,64 @@ without going through a first review cycle, and also helps reviewers stay aware
of the status of a particular pull request.
Rust has plenty of CI capacity, and you should never have to worry about wasting
computational resources each time you push a change. It is also perfectly fine
(and even encouraged!) to use the CI to test your changes if it can help your
productivity. In particular, we don't recommend running the full `./x test` suite locally,
computational resources each time you push a change.
It is also perfectly fine
(and even encouraged!) to use the CI to test your changes if it can help your productivity.
In particular, we don't recommend running the full `./x test` suite locally,
since it takes a very long time to execute.
### r+
After someone has reviewed your pull request, they will leave an annotation
on the pull request with an `r+`. It will look something like this:
on the pull request with an `r+`.
It will look something like this:
@bors r+
This tells [@bors], our lovable integration bot, that your pull request has
been approved. The PR then enters the [merge queue], where [@bors]
will run *all* the tests on *every* platform we support. If it all works out,
[@bors] will merge your code into `master` and close the pull request.
This tells [@bors], our lovable integration bot, that your pull request has been approved.
The PR then enters the [merge queue], where [@bors]
will run *all* the tests on *every* platform we support.
If it all works out, [@bors] will merge your code into `master` and close the pull request.
Depending on the scale of the change, you may see a slightly different form of `r+`:
@bors r+ rollup
The additional `rollup` tells [@bors] that this change should always be "rolled up".
Changes that are rolled up are tested and merged alongside other PRs, to
speed the process up. Typically only small changes that are expected not to conflict
Changes that are rolled up are tested and merged alongside other PRs, to speed the process up.
Typically, only small changes that are expected not to conflict
with one another are marked as "always roll up".
Be patient; this can take a while and the queue can sometimes be long. PRs are never merged by hand.
Be patient;
this can take a while and the queue can sometimes be long.
Also, note that PRs are never merged by hand.
[@rustbot]: https://github.com/rustbot
[@bors]: https://github.com/bors
### Opening a PR
You are now ready to file a pull request? Great! Here are a few points you
should be aware of.
You are now ready to file a pull request (PR)?
Great!
Here are a few points you should be aware of.
All pull requests should be filed against the `master` branch,
unless you know for sure that you should target a different branch.
Make sure your pull request is in compliance with Rust's style guidelines by running
Run some style checks before you submit the PR:
$ ./x test tidy --bless
./x test tidy --bless
We recommend to make this check before every pull request (and every new commit
in a pull request); you can add [git hooks]
before every push to make sure you never forget to make this check.
We recommend to make this check before every pull request (and every new commit in a pull request);
you can add [git hooks] before every push to make sure you never forget to make this check.
The CI will also run tidy and will fail if tidy fails.
Rust follows a _no merge-commit policy_, meaning, when you encounter merge
conflicts you are expected to always rebase instead of merging. E.g. always use
rebase when bringing the latest changes from the master branch to your feature
branch. If your PR contains merge commits, it will get marked as `has-merge-commits`.
Rust follows a _no merge-commit policy_,
meaning that when you encounter merge conflicts,
you are expected to always rebase instead of merging.
For example,
always use rebase when bringing the latest changes from the master branch to your feature branch.
If your PR contains merge commits, it will get marked as `has-merge-commits`.
Once you have removed the merge commits, e.g., through an interactive rebase, you
should remove the label again:
@ -300,13 +313,14 @@ should remove the label again:
See [this chapter][labeling] for more details.
If you encounter merge conflicts or when a reviewer asks you to perform some
changes, your PR will get marked as `S-waiting-on-author`. When you resolve
them, you should use `@rustbot` to mark it as `S-waiting-on-review`:
changes, your PR will get marked as `S-waiting-on-author`.
When you resolve them, you should use `@rustbot` to mark it as `S-waiting-on-review`:
@rustbot ready
GitHub allows [closing issues using keywords][closing-keywords]. This feature
should be used to keep the issue tracker tidy. However, it is generally preferred
GitHub allows [closing issues using keywords][closing-keywords].
This feature should be used to keep the issue tracker tidy.
However, it is generally preferred
to put the "closes #123" text in the PR description rather than the issue commit;
particularly during rebasing, citing the issue number in the commit can "spam"
the issue in question.
@ -319,9 +333,10 @@ Please update the PR description while still mentioning the issue somewhere.
For example, you could write `Fixes (after beta backport) #NNN.`.
As for further actions, please keep a sharp look-out for a PR whose title begins with
`[beta]` or `[stable]` and which backports the PR in question. When that one gets
merged, the relevant issue can be closed. The closing comment should mention all
PRs that were involved. If you don't have the permissions to close the issue, please
`[beta]` or `[stable]` and which backports the PR in question.
When that one gets merged, the relevant issue can be closed.
The closing comment should mention all PRs that were involved.
If you don't have the permissions to close the issue, please
leave a comment on the original PR asking the reviewer to close it for you.
[labeling]: ./rustbot.md#issue-relabeling
@ -330,11 +345,13 @@ leave a comment on the original PR asking the reviewer to close it for you.
### Reverting a PR
When a PR leads to miscompile, significant performance regressions, or other critical issues, we may
want to revert that PR with a regression test case. You can also check out the [revert policy] on
want to revert that PR with a regression test case.
You can also check out the [revert policy] on
Forge docs (which is mainly targeted for reviewers, but contains useful info for PR authors too).
If the PR contains huge changes, it can be challenging to revert, making it harder to review
incremental fixes in subsequent updates. Or if certain code in that PR is heavily depended upon by
incremental fixes in subsequent updates.
Or if certain code in that PR is heavily depended upon by
subsequent PRs, reverting it can become difficult.
In such cases, we can identify the problematic code and disable it for some input, as shown in [#128271][#128271].
@ -352,7 +369,8 @@ This section has moved to ["Using External Repositories"](./external-repos.md).
## Writing documentation
Documentation improvements are very welcome. The source of `doc.rust-lang.org`
Documentation improvements are very welcome.
The source of `doc.rust-lang.org`
is located in [`src/doc`] in the tree, and standard API documentation is generated
from the source code itself (e.g. [`library/std/src/lib.rs`][std-root]). Documentation pull requests
function in the same way as other pull requests.
@ -370,8 +388,8 @@ Results should appear in `build/host/doc`, as well as automatically open in your
See [Building Documentation](./building/compiler-documenting.md#building-documentation) for more
information.
You can also use `rustdoc` directly to check small fixes. For example,
`rustdoc src/doc/reference.md` will render reference to `doc/reference.html`.
You can also use `rustdoc` directly to check small fixes.
For example, `rustdoc src/doc/reference.md` will render reference to `doc/reference.html`.
The CSS might be messed up, but you can verify that the HTML is right.
Please notice that we don't accept typography/spellcheck fixes to **internal documentation**
@ -389,18 +407,25 @@ There are issues for beginners and advanced compiler devs alike!
Just a few things to keep in mind:
- Please try to avoid overly long lines and use semantic line breaks (where you break the line after each sentence).
There is no strict limit on line lengths; let the sentence or part of the sentence flow to its proper end on the same line.
There is no strict limit on line lengths;
let the sentence or part of the sentence flow to its proper end on the same line.
You can use a tool in ci/sembr to help with this.
Its help output can be seen with this command:
```console
cargo run --manifest-path ci/sembr/Cargo.toml -- --help
```
- When contributing text to the guide, please contextualize the information with some time period
and/or a reason so that the reader knows how much to trust the information.
Aim to provide a reasonable amount of context, possibly including but not limited to:
- A reason for why the data may be out of date other than "change",
- A reason for why the text may be out of date other than "change",
as change is a constant across the project.
- The date the comment was added, e.g. instead of writing _"Currently, ..."_
or _"As of now, ..."_,
consider adding the date, in one of the following formats:
or _"As of now, ..."_, consider adding the date, in one of the following formats:
- Jan 2021
- January 2021
- jan 2021
@ -410,24 +435,23 @@ Just a few things to keep in mind:
that generates a monthly report showing those that are over 6 months old
([example](https://github.com/rust-lang/rustc-dev-guide/issues/2052)).
For the action to pick the date,
add a special annotation before specifying the date:
For the action to pick the date, add a special annotation before specifying the date:
```md
<!-- date-check --> Apr 2025
<!-- date-check --> Nov 2025
```
Example:
```md
As of <!-- date-check --> Apr 2025, the foo did the bar.
As of <!-- date-check --> Nov 2025, the foo did the bar.
```
For cases where the date should not be part of the visible rendered output,
use the following instead:
```md
<!-- date-check: Apr 2025 -->
<!-- date-check: Nov 2025 -->
```
- A link to a relevant WG, tracking issue, `rustc` rustdoc page, or similar, that may provide
@ -435,8 +459,7 @@ Just a few things to keep in mind:
outdated.
- If a text grows rather long (more than a few page scrolls) or complicated (more than four
subsections),
it might benefit from having a Table of Contents at the beginning,
subsections), it might benefit from having a Table of Contents at the beginning,
which you can auto-generate by including the `<!-- toc -->` marker at the top.
#### ⚠️ Note: Where to contribute `rustc-dev-guide` changes

View file

@ -7,18 +7,18 @@ From a high-level point of view, when you open a pull request at
`rust-lang/rust`, the following will happen:
- A small [subset](#pull-request-builds) of tests and checks are run after each
push to the PR. This should help catch common errors.
push to the PR.
This should help catch common errors.
- When the PR is approved, the [bors] bot enqueues the PR into a [merge queue].
- Once the PR gets to the front of the queue, bors will create a merge commit
and run the [full test suite](#auto-builds) on it. The merge commit either
contains only one specific PR or it can be a ["rollup"](#rollups) which
and run the [full test suite](#auto-builds) on it.
The merge commit either contains only one specific PR or it can be a ["rollup"](#rollups) which
combines multiple PRs together, to reduce CI costs and merge delays.
- Once the whole test suite finishes, two things can happen. Either CI fails
with an error that needs to be addressed by the developer, or CI succeeds and
the merge commit is then pushed to the `master` branch.
If you want to modify what gets executed on CI, see [Modifying CI
jobs](#modifying-ci-jobs).
If you want to modify what gets executed on CI, see [Modifying CI jobs](#modifying-ci-jobs).
## CI workflow
@ -26,10 +26,10 @@ jobs](#modifying-ci-jobs).
Our CI is primarily executed on [GitHub Actions], with a single workflow defined
in [`.github/workflows/ci.yml`], which contains a bunch of steps that are
unified for all CI jobs that we execute. When a commit is pushed to a
corresponding branch or a PR, the workflow executes the
[`src/ci/citool`] crate, which dynamically generates the specific CI
jobs that should be executed. This script uses the [`jobs.yml`] file as an
unified for all CI jobs that we execute.
When a commit is pushed to a corresponding branch or a PR, the workflow executes the
[`src/ci/citool`] crate, which dynamically generates the specific CI jobs that should be executed.
This script uses the [`jobs.yml`] file as an
input, which contains a declarative configuration of all our CI jobs.
> Almost all build steps shell out to separate scripts. This keeps the CI fairly
@ -38,21 +38,22 @@ input, which contains a declarative configuration of all our CI jobs.
> orchestrating the scripts that drive the process.
In essence, all CI jobs run `./x test`, `./x dist` or some other command with
different configurations, across various operating systems, targets, and
platforms. There are two broad categories of jobs that are executed, `dist` and
non-`dist` jobs.
different configurations, across various operating systems, targets, and platforms.
There are two broad categories of jobs that are executed, `dist` and non-`dist` jobs.
- Dist jobs build a full release of the compiler for a specific platform,
including all the tools we ship through rustup. Those builds are then uploaded
including all the tools we ship through rustup.
Those builds are then uploaded
to the `rust-lang-ci2` S3 bucket and are available to be locally installed
with the [rustup-toolchain-install-master] tool. The same builds are also used
with the [rustup-toolchain-install-master] tool.
The same builds are also used
for actual releases: our release process basically consists of copying those
artifacts from `rust-lang-ci2` to the production endpoint and signing them.
- Non-dist jobs run our full test suite on the platform, and the test suite of
all the tools we ship through rustup; The amount of stuff we test depends on
all the tools we ship through rustup;
The amount of stuff we test depends on
the platform (for example some tests are run only on Tier 1 platforms), and
some quicker platforms are grouped together on the same builder to avoid
wasting CI resources.
some quicker platforms are grouped together on the same builder to avoid wasting CI resources.
Based on an input event (usually a push to a branch), we execute one of three
kinds of builds (sets of jobs).
@ -65,13 +66,15 @@ kinds of builds (sets of jobs).
### Pull Request builds
After each push to a pull request, a set of `pr` jobs are executed. Currently,
these execute the `x86_64-gnu-llvm-X`, `x86_64-gnu-tools`, `pr-check-1`, `pr-check-2`
and `tidy` jobs, all running on Linux. These execute a relatively short
(~40 minutes) and lightweight test suite that should catch common issues. More
specifically, they run a set of lints, they try to perform a cross-compile check
After each push to a pull request, a set of `pr` jobs are executed.
Currently, these execute the `x86_64-gnu-llvm-X`, `x86_64-gnu-tools`, `pr-check-1`, `pr-check-2`
and `tidy` jobs, all running on Linux.
These execute a relatively short
(~40 minutes) and lightweight test suite that should catch common issues.
More specifically, they run a set of lints, they try to perform a cross-compile check
build to Windows mingw (without producing any artifacts), and they test the
compiler using a *system* version of LLVM. Unfortunately, it would take too many
compiler using a *system* version of LLVM.
Unfortunately, it would take too many
resources to run the full test suite for each commit on every PR.
> **Note on doc comments**
@ -84,27 +87,28 @@ resources to run the full test suite for each commit on every PR.
> Thus, it is a good idea to run `./x doc xxx` locally for any doc comment
> changes to help catch these early.
PR jobs are defined in the `pr` section of [`jobs.yml`]. Their results can be observed
PR jobs are defined in the `pr` section of [`jobs.yml`].
Their results can be observed
directly on the PR, in the "CI checks" section at the bottom of the PR page.
### Auto builds
Before a commit can be merged into the `master` branch, it needs to pass our
complete test suite. We call this an `auto` build. This build runs tens of CI
jobs that exercise various tests across operating systems and targets. The full
test suite is quite slow; it can take several hours until all the `auto` CI
jobs finish.
Before a commit can be merged into the `master` branch, it needs to pass our complete test suite.
We call this an `auto` build.
This build runs tens of CI jobs that exercise various tests across operating systems and targets.
The full test suite is quite slow;
it can take several hours until all the `auto` CI jobs finish.
Most platforms only run the build steps, some run a restricted set of tests;
only a subset run the full suite of tests (see Rust's [platform tiers]).
Auto jobs are defined in the `auto` section of [`jobs.yml`]. They are executed
on the `auto` branch under the `rust-lang/rust` repository,
Auto jobs are defined in the `auto` section of [`jobs.yml`].
They are executed on the `auto` branch under the `rust-lang/rust` repository,
and the final result will be reported via a comment made by bors on the corresponding PR.
The live results can be seen on [the GitHub Actions workflows page].
At any given time, at most a single `auto` build is being executed. Find out
more in [Merging PRs serially with bors](#merging-prs-serially-with-bors).
At any given time, at most a single `auto` build is being executed.
Find out more in [Merging PRs serially with bors](#merging-prs-serially-with-bors).
[platform tiers]: https://forge.rust-lang.org/release/platform-support.html#rust-platform-support
@ -112,7 +116,8 @@ more in [Merging PRs serially with bors](#merging-prs-serially-with-bors).
Sometimes we want to run a subset of the test suite on CI for a given PR, or
build a set of compiler artifacts from that PR, without attempting to merge it.
We call this a "try build". A try build is started after a user with the proper
We call this a "try build".
A try build is started after a user with the proper
permissions posts a PR comment with the `@bors try` command.
There are several use-cases for try builds:
@ -121,9 +126,9 @@ There are several use-cases for try builds:
For this, a working compiler build is needed, which can be generated with a
try build that runs the [dist-x86_64-linux] CI job, which builds an optimized
version of the compiler on Linux (this job is currently executed by default
when you start a try build). To create a try build and schedule it for a
performance benchmark, you can use the `@bors try @rust-timer queue` command
combination.
when you start a try build).
To create a try build and schedule it for a
performance benchmark, you can use the `@bors try @rust-timer queue` command combination.
- Check the impact of the PR across the Rust ecosystem, using a [Crater](crater.md) run.
Again, a working compiler build is needed for this, which can be produced by
the [dist-x86_64-linux] CI job.
@ -131,25 +136,32 @@ There are several use-cases for try builds:
passes the test suite executed by that job.
By default, if you send a comment with `@bors try`, the jobs defined in the `try` section of
[`jobs.yml`] will be executed. We call this mode a "fast try build". Such a try build
will not execute any tests, and it will allow compilation warnings. It is useful when you want to
[`jobs.yml`] will be executed.
We call this mode a "fast try build".
Such a try build will not execute any tests, and it will allow compilation warnings.
It is useful when you want to
get an optimized toolchain as fast as possible, for a Crater run or performance benchmarks,
even if it might not be working fully correctly. If you want to do a full build for the default try job,
even if it might not be working fully correctly.
If you want to do a full build for the default try job,
specify its job name in a job pattern (explained below).
If you want to run custom CI jobs in a try build and make sure that they pass all tests and do
not produce any compilation warnings, you can select CI jobs to be executed by specifying a *job pattern*,
which can be used in one of two ways:
- You can add a set of `try-job: <job pattern>` directives to the PR description (described below) and then
simply run `@bors try`. CI will read these directives and run the jobs that you have specified. This is
simply run `@bors try`.
CI will read these directives and run the jobs that you have specified.
This is
useful if you want to rerun the same set of try jobs multiple times, after incrementally modifying a PR.
- You can specify the job pattern using the `jobs` parameter of the try command: `@bors try jobs=<job pattern>`.
This is useful for one-off try builds with specific jobs. Note that the `jobs` parameter has a higher priority
than the PR description directives.
This is useful for one-off try builds with specific jobs.
Note that the `jobs` parameter has a higher priority than the PR description directives.
- There can also be multiple patterns specified, e.g. `@bors try jobs=job1,job2,job3`.
Each job pattern can either be an exact name of a job or a glob pattern that matches multiple jobs,
for example `*msvc*` or `*-alt`. You can start at most 20 jobs in a single try build. When using
for example `*msvc*` or `*-alt`.
You can start at most 20 jobs in a single try build.
When using
glob patterns in the PR description, you can optionally wrap them in backticks (`` ` ``) to avoid GitHub rendering
the pattern as Markdown if it contains e.g. an asterisk. Note that this escaping will not work when using
the `@bors jobs=` parameter.
@ -208,20 +220,20 @@ If you want to modify what gets executed on our CI, you can simply modify the
You can also modify what gets executed temporarily, for example to test a
particular platform or configuration that is challenging to test locally (for
example, if a Windows build fails, but you don't have access to a Windows
machine). Don't hesitate to use CI resources in such situations.
example, if a Windows build fails, but you don't have access to a Windows machine).
Don't hesitate to use CI resources in such situations.
You can perform an arbitrary CI job in two ways:
- Use the [try build](#try-builds) functionality, and specify the CI jobs that
you want to be executed in try builds in your PR description.
- Modify the [`pr`](#pull-request-builds) section of `jobs.yml` to specify which
CI jobs should be executed after each push to your PR. This might be faster
than repeatedly starting try builds.
CI jobs should be executed after each push to your PR.
This might be faster than repeatedly starting try builds.
To modify the jobs executed after each push to a PR, you can simply copy one of
the job definitions from the `auto` section to the `pr` section. For example,
the `x86_64-msvc` job is responsible for running the 64-bit MSVC tests. You can
copy it to the `pr` section to cause it to be executed after a commit is pushed
the job definitions from the `auto` section to the `pr` section.
For example, the `x86_64-msvc` job is responsible for running the 64-bit MSVC tests.
You can copy it to the `pr` section to cause it to be executed after a commit is pushed
to your PR, like this:
```yaml
@ -238,8 +250,8 @@ pr:
<<: *job-windows-8c
```
Then you can commit the file and push it to your PR branch on GitHub. GitHub
Actions should then execute this CI job after each push to your PR.
Then you can commit the file and push it to your PR branch on GitHub.
GitHub Actions should then execute this CI job after each push to your PR.
<div class="warning">
@ -247,12 +259,12 @@ Actions should then execute this CI job after each push to your PR.
you have made to `jobs.yml`, if they were supposed to be temporary!**
A good practice is to prefix `[WIP]` in PR title while still running try jobs
and `[DO NOT MERGE]` in the commit that modifies the CI jobs for testing
purposes.
and `[DO NOT MERGE]` in the commit that modifies the CI jobs for testing purposes.
</div>
Although you are welcome to use CI, just be conscious that this is a shared
resource with limited concurrency. Try not to enable too many jobs at once;
resource with limited concurrency.
Try not to enable too many jobs at once;
one or two should be sufficient in most cases.
## Merging PRs serially with bors
@ -265,26 +277,28 @@ after the build happened.
To ensure a `master` branch that works all the time, we forbid manual merges.
Instead, all PRs have to be approved through our bot, [bors] (the software
behind it is called [homu]). All the approved PRs are put in a [merge queue]
(sorted by priority and creation date) and are automatically tested one at the
time. If all the builders are green, the PR is merged, otherwise the failure is
behind it is called [homu]).
All the approved PRs are put in a [merge queue]
(sorted by priority and creation date) and are automatically tested one at the time.
If all the builders are green, the PR is merged, otherwise the failure is
recorded and the PR will have to be re-approved again.
Bors doesnt interact with CI services directly, but it works by pushing the
merge commit it wants to test to specific branches (like `auto` or `try`), which
are configured to execute CI checks. Bors then detects the outcome of the build
by listening for either Commit Statuses or Check Runs. Since the merge commit is
are configured to execute CI checks.
Bors then detects the outcome of the build by listening for either Commit Statuses or Check Runs.
Since the merge commit is
based on the latest `master` and only one can be tested at the same time, when
the results are green, `master` is fast-forwarded to that merge commit.
Unfortunately, testing a single PR at a time, combined with our long CI (~2
hours for a full run), means we cant merge a lot of PRs in a single day, and a
single failure greatly impacts our throughput. The maximum number of
PRs we can merge in a day is around ~10.
single failure greatly impacts our throughput.
The maximum number of PRs we can merge in a day is around ~10.
The long CI run times, and requirement for a large builder pool, is largely due
to the fact that full release artifacts are built in the `dist-` builders. This
is worth it because these release artifacts:
to the fact that full release artifacts are built in the `dist-` builders.
This is worth it because these release artifacts:
- Allow perf testing even at a later date.
- Allow bisection when bugs are discovered later.
@ -295,23 +309,23 @@ is worth it because these release artifacts:
Some PRs dont need the full test suite to be executed: trivial changes like
typo fixes or README improvements *shouldnt* break the build, and testing every
single one of them for 2+ hours would be wasteful. To solve this, we
regularly create a "rollup", a PR where we merge several pending trivial PRs so
they can be tested together. Rollups are created manually by a team member using
the "create a rollup" button on the [merge queue]. The team member uses their
judgment to decide if a PR is risky or not.
single one of them for 2+ hours would be wasteful.
To solve this, we regularly create a "rollup", a PR where we merge several pending trivial PRs so
they can be tested together.
Rollups are created manually by a team member using
the "create a rollup" button on the [merge queue].
The team member uses their judgment to decide if a PR is risky or not.
## Docker
All CI jobs, except those on macOS and Windows, are executed inside that
platforms custom [Docker container]. This has a lot of advantages for us:
platforms custom [Docker container].
This has a lot of advantages for us:
- The build environment is consistent regardless of the changes of the
underlying image (switching from the trusty image to xenial was painless for
us).
underlying image (switching from the trusty image to xenial was painless for us).
- We can use ancient build environments to ensure maximum binary compatibility,
for example [using older CentOS releases][dist-x86_64-linux] on our Linux
builders.
for example [using older CentOS releases][dist-x86_64-linux] on our Linux builders.
- We can avoid reinstalling tools (like QEMU or the Android emulator) every time,
thanks to Docker image caching.
- Users can run the same tests in the same environment locally by just running this command:
@ -325,13 +339,11 @@ platforms custom [Docker container]. This has a lot of advantages for us:
The Docker images prefixed with `dist-` are used for building artifacts while
those without that prefix run tests and checks.
We also run tests for less common architectures (mainly Tier 2 and Tier 3
platforms) in CI. Since those platforms are not x86, we either run everything
inside QEMU, or we just cross-compile if we dont want to run the tests for that
platform.
We also run tests for less common architectures (mainly Tier 2 and Tier 3 platforms) in CI.
Since those platforms are not x86, we either run everything
inside QEMU, or we just cross-compile if we dont want to run the tests for that platform.
These builders are running on a special pool of builders set up and maintained
for us by GitHub.
These builders are running on a special pool of builders set up and maintained for us by GitHub.
[Docker container]: https://github.com/rust-lang/rust/tree/HEAD/src/ci/docker
@ -341,16 +353,16 @@ Our CI workflow uses various caching mechanisms, mainly for two things:
### Docker images caching
The Docker images we use to run most of the Linux-based builders take a *long*
time to fully build. To speed up the build, we cache them using [Docker registry
caching], with the intermediate artifacts being stored on [ghcr.io]. We also
push the built Docker images to ghcr, so that they can be reused by other tools
(rustup) or by developers running the Docker build locally (to speed up their
build).
The Docker images we use to run most of the Linux-based builders take a *long* time to fully build.
To speed up the build, we cache them using [Docker registry
caching], with the intermediate artifacts being stored on [ghcr.io].
We also push the built Docker images to ghcr, so that they can be reused by other tools
(rustup) or by developers running the Docker build locally (to speed up their build).
Since we test multiple, diverged branches (`master`, `beta` and `stable`), we
cant rely on a single cache for the images, otherwise builds on a branch would
override the cache for the others. Instead, we store the images under different
override the cache for the others.
Instead, we store the images under different
tags, identifying them with a custom hash made from the contents of all the
Dockerfiles and related scripts.
@ -367,17 +379,17 @@ invalidated if one of the following changes:
### LLVM caching with Sccache
We build some C/C++ stuff in various CI jobs, and we rely on [Sccache] to cache
the intermediate LLVM artifacts. Sccache is a distributed ccache developed by
the intermediate LLVM artifacts.
Sccache is a distributed ccache developed by
Mozilla, which can use an object storage bucket as the storage backend.
With Sccache there's no need to calculate the hash key ourselves. Sccache
invalidates the cache automatically when it detects changes to relevant inputs,
such as the source code, the version of the compiler, and important environment
variables.
With Sccache there's no need to calculate the hash key ourselves.
Sccache invalidates the cache automatically when it detects changes to relevant inputs,
such as the source code, the version of the compiler, and important environment variables.
So we just pass the Sccache wrapper on top of Cargo and Sccache does the rest.
We store the persistent artifacts on the S3 bucket, `rust-lang-ci-sccache2`. So
when the CI runs, if Sccache sees that LLVM is being compiled with the same C/C++
We store the persistent artifacts on the S3 bucket, `rust-lang-ci-sccache2`.
So when the CI runs, if Sccache sees that LLVM is being compiled with the same C/C++
compiler and the LLVM source code is the same, Sccache retrieves the individual
compiled translation units from S3.
@ -396,26 +408,28 @@ receives the build logs on failure, and extracts the error message automatically
posting it on the PR thread.
The bot is not hardcoded to look for error strings, but was trained with a bunch
of build failures to recognize which lines are common between builds and which
are not. While the generated snippets can be weird sometimes, the bot is pretty
good at identifying the relevant lines, even if its an error we've never seen
before.
of build failures to recognize which lines are common between builds and which are not.
While the generated snippets can be weird sometimes, the bot is pretty
good at identifying the relevant lines, even if its an error we've never seen before.
[rla]: https://github.com/rust-lang/rust-log-analyzer
### Toolstate to support allowed failures
The `rust-lang/rust` repo doesnt only test the compiler on its CI, but also a
variety of tools and documentation. Some documentation is pulled in via git
submodules. If we blocked merging rustc PRs on the documentation being fixed, we
variety of tools and documentation.
Some documentation is pulled in via git submodules.
If we blocked merging rustc PRs on the documentation being fixed, we
would be stuck in a chicken-and-egg problem, because the documentation's CI
would not pass since updating it would need the not-yet-merged version of rustc
to test against (and we usually require CI to be passing).
To avoid the problem, submodules are allowed to fail, and their status is
recorded in [rust-toolstate]. When a submodule breaks, a bot automatically pings
recorded in [rust-toolstate].
When a submodule breaks, a bot automatically pings
the maintainers so they know about the breakage, and it records the failure on
the toolstate repository. The release process will then ignore broken tools on
the toolstate repository.
The release process will then ignore broken tools on
nightly, removing them from the shipped nightlies.
While tool failures are allowed most of the time, theyre automatically
@ -448,8 +462,8 @@ To learn more about the dashboard, see the [Datadog CI docs].
## Determining the CI configuration
If you want to determine which `bootstrap.toml` settings are used in CI for a
particular job, it is probably easiest to just look at the build log. To do
this:
particular job, it is probably easiest to just look at the build log.
To do this:
1. Go to
<https://github.com/rust-lang/rust/actions?query=branch%3Aauto+is%3Asuccess>

View file

@ -21,7 +21,8 @@ you can use the following command to run UI tests locally using the GCC backend,
If a different test suite has failed on CI, you will have to modify the `tests/ui` part.
To reproduce the whole CI job locally, you can run `cargo run --manifest-path src/ci/citool/Cargo.toml run-local x86_64-gnu-gcc`. See [Testing with Docker](../docker.md) for more information.
To reproduce the whole CI job locally, you can run `cargo run --manifest-path src/ci/citool/Cargo.toml run-local x86_64-gnu-gcc`.
See [Testing with Docker](../docker.md) for more information.
### What to do in case of a GCC job failure?
@ -32,23 +33,26 @@ If fixing a compiler test that fails with the GCC backend is non-trivial, you ca
## Choosing which codegen backends are built
The `rust.codegen-backends = [...]` bootstrap option affects which codegen backends will be built and
included in the sysroot of the produced `rustc`. To use the GCC codegen backend, `"gcc"` has to
be included in this array in `bootstrap.toml`:
included in the sysroot of the produced `rustc`.
To use the GCC codegen backend, `"gcc"` has to be included in this array in `bootstrap.toml`:
```toml
rust.codegen-backends = ["llvm", "gcc"]
```
If you don't want to change your `bootstrap.toml` file, you can alternatively run your `x`
commands with `--set 'rust.codegen-backends=["llvm", "gcc"]'`. For example:
commands with `--set 'rust.codegen-backends=["llvm", "gcc"]'`.
For example:
```bash
./x build --set 'rust.codegen-backends=["llvm", "gcc"]'
```
The first backend in the `codegen-backends` array will determine which backend will be used as the
*default backend* of the built `rustc`. This also determines which backend will be used to compile the
stage 1 standard library (or anything built in stage 2+). To produce `rustc` that uses the GCC backend
*default backend* of the built `rustc`.
This also determines which backend will be used to compile the
stage 1 standard library (or anything built in stage 2+).
To produce `rustc` that uses the GCC backend
by default, you can thus put `"gcc"` as the first element of this array:
```bash
@ -69,7 +73,8 @@ Note that in order for this to work, the tested compiler must have the GCC codeg
## Downloading GCC from CI
The `gcc.download-ci-gcc` bootstrap option controls if GCC (which is a dependency of the GCC codegen backend)
will be downloaded from CI or built locally. The default value is `true`, which will download GCC from CI
will be downloaded from CI or built locally.
The default value is `true`, which will download GCC from CI
if there are no local changes to the GCC sources and the given host target is available on CI.
## Running tests of the backend itself

View file

@ -1,16 +1,15 @@
# `minicore` test auxiliary: using `core` stubs
<!-- date-check Oct 2024 -->
<!-- date-check: Oct 2025 -->
[`tests/auxiliary/minicore.rs`][`minicore`] is a test auxiliary for
ui/codegen/assembly test suites. It provides `core` stubs for tests that need to
[`tests/auxiliary/minicore.rs`][`minicore`] is a test auxiliary for ui/codegen/assembly test suites.
It provides `core` stubs for tests that need to
build for cross-compiled targets but do not need/want to run.
<div class="warning">
Please note that [`minicore`] is only intended for `core` items, and explicitly
**not** `std` or `alloc` items because `core` items are applicable to a wider
range of tests.
**not** `std` or `alloc` items because `core` items are applicable to a wider range of tests.
</div>
@ -41,8 +40,8 @@ by more than one test.
## Staying in sync with `core`
The `minicore` items must be kept up to date with `core`. For consistent
diagnostic output between using `core` and `minicore`, any `diagnostic`
The `minicore` items must be kept up to date with `core`.
For consistent diagnostic output between using `core` and `minicore`, any `diagnostic`
attributes (e.g. `on_unimplemented`) should be replicated exactly in `minicore`.
## Example codegen test that uses `minicore`

View file

@ -1,10 +1,10 @@
# Walkthrough: a typical contribution
There are _a lot_ of ways to contribute to the Rust compiler, including fixing
bugs, improving performance, helping design features, providing feedback on
existing features, etc. This chapter does not claim to scratch the surface.
Instead, it walks through the design and implementation of a new feature. Not
all of the steps and processes described here are needed for every
bugs, improving performance, helping design features, providing feedback on existing features, etc.
This chapter does not claim to scratch the surface.
Instead, it walks through the design and implementation of a new feature.
Not all of the steps and processes described here are needed for every
contribution, and I will try to point those out as they arise.
In general, if you are interested in making a contribution and aren't sure
@ -12,8 +12,8 @@ where to start, please feel free to ask!
## Overview
The feature I will discuss in this chapter is the `?` Kleene operator for
macros. Basically, we want to be able to write something like this:
The feature I will discuss in this chapter is the `?` Kleene operator for macros.
Basically, we want to be able to write something like this:
```rust,ignore
macro_rules! foo {
@ -36,25 +36,30 @@ fn main() {
So basically, the `$(pat)?` matcher in the macro means "this pattern can occur
0 or 1 times", similar to other regex syntaxes.
There were a number of steps to go from an idea to stable Rust feature. Here is
a quick list. We will go through each of these in order below. As I mentioned
before, not all of these are needed for every type of contribution.
There were a number of steps to go from an idea to stable Rust feature.
Here is a quick list.
We will go through each of these in order below.
As I mentioned before, not all of these are needed for every type of contribution.
- **Idea discussion/Pre-RFC** A Pre-RFC is an early draft or design discussion
of a feature. This stage is intended to flesh out the design space a bit and
get a grasp on the different merits and problems with an idea. It's a great
way to get early feedback on your idea before presenting it to the wider
audience. You can find the original discussion [here][prerfc].
of a feature.
This stage is intended to flesh out the design space a bit and
get a grasp on the different merits and problems with an idea.
It's a great way to get early feedback on your idea before presenting it to the wider
audience.
You can find the original discussion [here][prerfc].
- **RFC** This is when you formally present your idea to the community for
consideration. You can find the RFC [here][rfc].
consideration.
You can find the RFC [here][rfc].
- **Implementation** Implement your idea unstably in the compiler. You can
find the original implementation [here][impl1].
- **Possibly iterate/refine** As the community gets experience with your
feature on the nightly compiler and in `std`, there may be additional
feedback about design choice that might be adjusted. This particular feature
went [through][impl2] a [number][impl3] of [iterations][impl4].
feedback about design choice that might be adjusted.
This particular feature went [through][impl2] a [number][impl3] of [iterations][impl4].
- **Stabilization** When your feature has baked enough, a Rust team member may
[propose to stabilize it][merge]. If there is consensus, this is done.
[propose to stabilize it][merge].
If there is consensus, this is done.
- **Relax** Your feature is now a stable Rust feature!
[prerfc]: https://internals.rust-lang.org/t/pre-rfc-at-most-one-repetition-macro-patterns/6557
@ -75,58 +80,63 @@ before, not all of these are needed for every type of contribution.
[rfcwhen]: https://github.com/rust-lang/rfcs#when-you-need-to-follow-this-process
An RFC is a document that describes the feature or change you are proposing in
detail. Anyone can write an RFC; the process is the same for everyone,
including Rust team members.
An RFC is a document that describes the feature or change you are proposing in detail.
Anyone can write an RFC;
the process is the same for everyone, including Rust team members.
To open an RFC, open a PR on the
[rust-lang/rfcs](https://github.com/rust-lang/rfcs) repo on GitHub. You can
find detailed instructions in the
To open an RFC, open a PR on the [rust-lang/rfcs](https://github.com/rust-lang/rfcs) repo on GitHub.
You can find detailed instructions in the
[README](https://github.com/rust-lang/rfcs#what-the-process-is).
Before opening an RFC, you should do the research to "flesh out" your idea.
Hastily-proposed RFCs tend not to be accepted. You should generally have a good
description of the motivation, impact, disadvantages, and potential
Hastily-proposed RFCs tend not to be accepted.
You should generally have a good description of the motivation, impact, disadvantages, and potential
interactions with other features.
If that sounds like a lot of work, it's because it is. But no fear! Even if
you're not a compiler hacker, you can get great feedback by doing a _pre-RFC_.
This is an _informal_ discussion of the idea. The best place to do this is
internals.rust-lang.org. Your post doesn't have to follow any particular
structure. It doesn't even need to be a cohesive idea. Generally, you will get
tons of feedback that you can integrate back to produce a good RFC.
If that sounds like a lot of work, it's because it is.
But no fear!
Even if you're not a compiler hacker, you can get great feedback by doing a _pre-RFC_.
This is an _informal_ discussion of the idea.
The best place to do this is internals.rust-lang.org.
Your post doesn't have to follow any particular structure.
It doesn't even need to be a cohesive idea.
Generally, you will get tons of feedback that you can integrate back to produce a good RFC.
(Another pro-tip: try searching the RFCs repo and internals for prior related
ideas. A lot of times an idea has already been considered and was either
rejected or postponed to be tried again later. This can save you and everybody
else some time)
(Another pro-tip: try searching the RFCs repo and internals for prior related ideas.
A lot of times an idea has already been considered and was either
rejected or postponed to be tried again later.
This can save you and everybody else some time)
In the case of our example, a participant in the pre-RFC thread pointed out a
syntax ambiguity and a potential resolution. Also, the overall feedback seemed
positive. In this case, the discussion converged pretty quickly, but for some
syntax ambiguity and a potential resolution.
Also, the overall feedback seemed positive.
In this case, the discussion converged pretty quickly, but for some
ideas, a lot more discussion can happen (e.g. see [this RFC][nonascii] which
received a whopping 684 comments!). If that happens, don't be discouraged; it
means the community is interested in your idea, but it perhaps needs some
received a whopping 684 comments!).
If that happens, don't be discouraged;
it means the community is interested in your idea, but it perhaps needs some
adjustments.
[nonascii]: https://github.com/rust-lang/rfcs/pull/2457
The RFC for our `?` macro feature did receive some discussion on the RFC thread
too. As with most RFCs, there were a few questions that we couldn't answer by
discussion: we needed experience using the feature to decide. Such questions
are listed in the "Unresolved Questions" section of the RFC. Also, over the
course of the RFC discussion, you will probably want to update the RFC document
The RFC for our `?` macro feature did receive some discussion on the RFC thread too.
As with most RFCs, there were a few questions that we couldn't answer by
discussion: we needed experience using the feature to decide.
Such questions are listed in the "Unresolved Questions" section of the RFC.
Also, over the course of the RFC discussion, you will probably want to update the RFC document
itself to reflect the course of the discussion (e.g. new alternatives or prior
work may be added or you may decide to change parts of the proposal itself).
In the end, when the discussion seems to reach a consensus and die down a bit,
a Rust team member may propose to move to "final comment period" (FCP) with one
of three possible dispositions. This means that they want the other members of
the appropriate teams to review and comment on the RFC. More discussion may
ensue, which may result in more changes or unresolved questions being added. At
some point, when everyone is satisfied, the RFC enters the FCP, which is the
last chance for people to bring up objections. When the FCP is over, the
disposition is adopted. Here are the three possible dispositions:
of three possible dispositions.
This means that they want the other members of
the appropriate teams to review and comment on the RFC.
More discussion may ensue, which may result in more changes or unresolved questions being added.
At some point, when everyone is satisfied, the RFC enters the FCP, which is the
last chance for people to bring up objections.
When the FCP is over, the disposition is adopted.
Here are the three possible dispositions:
- _Merge_: accept the feature. Here is the proposal to merge for our [`?` macro
feature][rfcmerge].
@ -136,14 +146,14 @@ disposition is adopted. Here are the three possible dispositions:
will go a different direction.
- _Postpone_: there is interest in going this direction but not at the moment.
This happens most often because the appropriate Rust team doesn't have the
bandwidth to shepherd the feature through the process to stabilization. Often
this is the case when the feature doesn't fit into the team's roadmap.
bandwidth to shepherd the feature through the process to stabilization.
Often this is the case when the feature doesn't fit into the team's roadmap.
Postponed ideas may be revisited later.
[rfcmerge]: https://github.com/rust-lang/rfcs/pull/2298#issuecomment-360582667
When an RFC is merged, the PR is merged into the RFCs repo. A new _tracking
issue_ is created in the [rust-lang/rust] repo to track progress on the feature
When an RFC is merged, the PR is merged into the RFCs repo.
A new _tracking issue_ is created in the [rust-lang/rust] repo to track progress on the feature
and discuss unresolved questions, implementation progress and blockers, etc.
Here is the tracking issue on for our [`?` macro feature][tracking].
@ -158,93 +168,98 @@ To make a change to the compiler, open a PR against the [rust-lang/rust] repo.
[rust-lang/rust]: https://github.com/rust-lang/rust
Depending on the feature/change/bug fix/improvement, implementation may be
relatively-straightforward or it may be a major undertaking. You can always ask
for help or mentorship from more experienced compiler devs. Also, you don't
have to be the one to implement your feature; but keep in mind that if you
don't, it might be a while before someone else does.
relatively-straightforward or it may be a major undertaking.
You can always ask for help or mentorship from more experienced compiler devs.
Also, you don't have to be the one to implement your feature;
but keep in mind that if you don't, it might be a while before someone else does.
For the `?` macro feature, I needed to go understand the relevant parts of
macro expansion in the compiler. Personally, I find that [improving the
macro expansion in the compiler.
Personally, I find that [improving the
comments][comments] in the code is a helpful way of making sure I understand
it, but you don't have to do that if you don't want to.
[comments]: https://github.com/rust-lang/rust/pull/47732
I then [implemented][impl1] the original feature, as described in the RFC. When
a new feature is implemented, it goes behind a _feature gate_, which means that
you have to use `#![feature(my_feature_name)]` to use the feature. The feature
gate is removed when the feature is stabilized.
I then [implemented][impl1] the original feature, as described in the RFC.
When a new feature is implemented, it goes behind a _feature gate_, which means that
you have to use `#![feature(my_feature_name)]` to use the feature.
The feature gate is removed when the feature is stabilized.
**Most bug fixes and improvements** don't require a feature gate. You can just
make your changes/improvements.
When you open a PR on the [rust-lang/rust], a bot will assign your PR to a
reviewer. If there is a particular Rust team member you are working with, you can
When you open a PR on the [rust-lang/rust], a bot will assign your PR to a reviewer.
If there is a particular Rust team member you are working with, you can
request that reviewer by leaving a comment on the thread with `r?
@reviewer-github-id` (e.g. `r? @eddyb`). If you don't know who to request,
don't request anyone; the bot will assign someone automatically based on which files you changed.
don't request anyone;
the bot will assign someone automatically based on which files you changed.
The reviewer may request changes before they approve your PR, they may mark the PR with label
"S-waiting-on-author" after leaving comments, this means that the PR is blocked on you to make
some requested changes. When you finished iterating on the changes, you can mark the PR as
some requested changes.
When you finished iterating on the changes, you can mark the PR as
`S-waiting-on-review` again by leaving a comment with `@rustbot ready`, this will remove the
`S-waiting-on-author` label and add the `S-waiting-on-review` label.
Feel free to ask questions or discuss things you don't understand or disagree with. However,
recognize that the PR won't be merged unless someone on the Rust team approves
it. If a reviewer leave a comment like `r=me after fixing ...`, that means they approve the PR and
Feel free to ask questions or discuss things you don't understand or disagree with.
However, recognize that the PR won't be merged unless someone on the Rust team approves
it.
If a reviewer leave a comment like `r=me after fixing ...`, that means they approve the PR and
you can merge it with comment with `@bors r=reviewer-github-id`(e.g. `@bors r=eddyb`) to merge it
after fixing trivial issues. Note that `r=someone` requires permission and bors could say
something like "🔑 Insufficient privileges..." when commenting `r=someone`. In that case,
you have to ask the reviewer to revisit your PR.
after fixing trivial issues.
Note that `r=someone` requires permission and bors could say
something like "🔑 Insufficient privileges..." when commenting `r=someone`.
In that case, you have to ask the reviewer to revisit your PR.
When your reviewer approves the PR, it will go into a queue for yet another bot
called `@bors`. `@bors` manages the CI build/merge queue. When your PR reaches
the head of the `@bors` queue, `@bors` will test out the merge by running all
tests against your PR on GitHub Actions. This takes a lot of time to
finish. If all tests pass, the PR is merged and becomes part of the next
nightly compiler!
When your reviewer approves the PR, it will go into a queue for yet another bot called `@bors`.
`@bors` manages the CI build/merge queue.
When your PR reaches the head of the `@bors` queue, `@bors` will test out the merge by running all
tests against your PR on GitHub Actions.
This takes a lot of time to finish.
If all tests pass, the PR is merged and becomes part of the next nightly compiler!
There are a couple of things that may happen for some PRs during the review process
- If the change is substantial enough, the reviewer may request an FCP on
the PR. This gives all members of the appropriate team a chance to review the
changes.
the PR.
This gives all members of the appropriate team a chance to review the changes.
- If the change may cause breakage, the reviewer may request a [crater] run.
This compiles the compiler with your changes and then attempts to compile all
crates on crates.io with your modified compiler. This is a great smoke test
crates on crates.io with your modified compiler.
This is a great smoke test
to check if you introduced a change to compiler behavior that affects a large
portion of the ecosystem.
- If the diff of your PR is large or the reviewer is busy, your PR may have
some merge conflicts with other PRs that happen to get merged first. You
should fix these merge conflicts using the normal git procedures.
some merge conflicts with other PRs that happen to get merged first.
You should fix these merge conflicts using the normal git procedures.
[crater]: ./tests/crater.html
If you are not doing a new feature or something like that (e.g. if you are
fixing a bug), then that's it! Thanks for your contribution :)
fixing a bug), then that's it!
Thanks for your contribution :)
## Refining your implementation
As people get experience with your new feature on nightly, slight changes may
be proposed and unresolved questions may become resolved. Updates/changes go
through the same process for implementing any other changes, as described
be proposed and unresolved questions may become resolved.
Updates/changes go through the same process for implementing any other changes, as described
above (i.e. submit a PR, go through review, wait for `@bors`, etc).
Some changes may be major enough to require an FCP and some review by Rust team
members.
Some changes may be major enough to require an FCP and some review by Rust team members.
For the `?` macro feature, we went through a few different iterations after the
original implementation: [1][impl2], [2][impl3], [3][impl4].
Along the way, we decided that `?` should not take a separator, which was
previously an unresolved question listed in the RFC. We also changed the
disambiguation strategy: we decided to remove the ability to use `?` as a
previously an unresolved question listed in the RFC.
We also changed the disambiguation strategy: we decided to remove the ability to use `?` as a
separator token for other repetition operators (e.g. `+` or `*`). However,
since this was a breaking change, we decided to do it over an edition boundary.
Thus, the new feature can be enabled only in edition 2018. These deviations
from the original RFC required [another
FCP](https://github.com/rust-lang/rust/issues/51934).
from the original RFC required [another FCP](https://github.com/rust-lang/rust/issues/51934).
## Stabilization
@ -264,8 +279,8 @@ The stabilization report for our feature is [here][stabrep].
[stabrep]: https://github.com/rust-lang/rust/issues/48075#issuecomment-433243048
After this, [a PR is made][stab] to remove the feature gate, enabling the feature by
default (on the 2018 edition). A note is added to the [Release notes][relnotes]
about the feature.
default (on the 2018 edition).
A note is added to the [Release notes][relnotes] about the feature.
[stab]: https://github.com/rust-lang/rust/pull/56245

View file

@ -264,9 +264,7 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'_, 'a, I> {
</pre>\
</div>",
added_classes = added_classes.join(" "),
text = Escape(
original_text.strip_suffix('\n').unwrap_or(&original_text)
),
text = Escape(original_text.trim_suffix('\n')),
)
.into(),
));

View file

@ -185,7 +185,7 @@ impl SourceCollector<'_, '_> {
};
// Remove the utf-8 BOM if any
let contents = contents.strip_prefix('\u{feff}').unwrap_or(&contents);
let contents = contents.trim_prefix('\u{feff}');
let shared = &self.cx.shared;
// Create the intermediate directories

View file

@ -17,6 +17,7 @@
#![feature(iter_order_by)]
#![feature(rustc_private)]
#![feature(test)]
#![feature(trim_prefix_suffix)]
#![warn(rustc::internal)]
// tidy-alphabetical-end

View file

@ -0,0 +1,17 @@
#![feature(core_intrinsics, portable_simd)]
use std::intrinsics::simd::*;
use std::simd::*;
fn main() {
unsafe {
let buf = [0u32; 5];
//~v ERROR: accessing memory with alignment
simd_masked_load::<_, _, _, { SimdAlign::Element }>(
i32x4::splat(-1),
// This is not i32-aligned
buf.as_ptr().byte_offset(1),
i32x4::splat(0),
);
}
}

View file

@ -0,0 +1,18 @@
error: Undefined Behavior: accessing memory with alignment ALIGN, but alignment ALIGN is required
--> tests/fail/intrinsics/simd_masked_load_element_misaligned.rs:LL:CC
|
LL | / simd_masked_load::<_, _, _, { SimdAlign::Element }>(
LL | | i32x4::splat(-1),
LL | | // This is not i32-aligned
LL | | buf.as_ptr().byte_offset(1),
LL | | i32x4::splat(0),
LL | | );
| |_________^ Undefined Behavior occurred here
|
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
error: aborting due to 1 previous error

View file

@ -0,0 +1,17 @@
#![feature(core_intrinsics, portable_simd)]
use std::intrinsics::simd::*;
use std::simd::*;
fn main() {
unsafe {
let buf = Simd::<i32, 8>::splat(0);
//~v ERROR: accessing memory with alignment
simd_masked_load::<_, _, _, { SimdAlign::Vector }>(
i32x4::splat(-1),
// This is i32-aligned but not i32x4-aligned.
buf.as_array()[1..].as_ptr(),
i32x4::splat(0),
);
}
}

View file

@ -0,0 +1,18 @@
error: Undefined Behavior: accessing memory with alignment ALIGN, but alignment ALIGN is required
--> tests/fail/intrinsics/simd_masked_load_vector_misaligned.rs:LL:CC
|
LL | / simd_masked_load::<_, _, _, { SimdAlign::Vector }>(
LL | | i32x4::splat(-1),
LL | | // This is i32-aligned but not i32x4-aligned.
LL | | buf.as_array()[1..].as_ptr(),
LL | | i32x4::splat(0),
LL | | );
| |_________^ Undefined Behavior occurred here
|
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
error: aborting due to 1 previous error

View file

@ -0,0 +1,17 @@
#![feature(core_intrinsics, portable_simd)]
use std::intrinsics::simd::*;
use std::simd::*;
fn main() {
unsafe {
let mut buf = [0u32; 5];
//~v ERROR: accessing memory with alignment
simd_masked_store::<_, _, _, { SimdAlign::Element }>(
i32x4::splat(-1),
// This is not i32-aligned
buf.as_mut_ptr().byte_offset(1),
i32x4::splat(0),
);
}
}

View file

@ -0,0 +1,18 @@
error: Undefined Behavior: accessing memory with alignment ALIGN, but alignment ALIGN is required
--> tests/fail/intrinsics/simd_masked_store_element_misaligned.rs:LL:CC
|
LL | / simd_masked_store::<_, _, _, { SimdAlign::Element }>(
LL | | i32x4::splat(-1),
LL | | // This is not i32-aligned
LL | | buf.as_mut_ptr().byte_offset(1),
LL | | i32x4::splat(0),
LL | | );
| |_________^ Undefined Behavior occurred here
|
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
error: aborting due to 1 previous error

View file

@ -0,0 +1,17 @@
#![feature(core_intrinsics, portable_simd)]
use std::intrinsics::simd::*;
use std::simd::*;
fn main() {
unsafe {
let mut buf = Simd::<i32, 8>::splat(0);
//~v ERROR: accessing memory with alignment
simd_masked_store::<_, _, _, { SimdAlign::Vector }>(
i32x4::splat(-1),
// This is i32-aligned but not i32x4-aligned.
buf.as_mut_array()[1..].as_mut_ptr(),
i32x4::splat(0),
);
}
}

View file

@ -0,0 +1,18 @@
error: Undefined Behavior: accessing memory with alignment ALIGN, but alignment ALIGN is required
--> tests/fail/intrinsics/simd_masked_store_vector_misaligned.rs:LL:CC
|
LL | / simd_masked_store::<_, _, _, { SimdAlign::Vector }>(
LL | | i32x4::splat(-1),
LL | | // This is i32-aligned but not i32x4-aligned.
LL | | buf.as_mut_array()[1..].as_mut_ptr(),
LL | | i32x4::splat(0),
LL | | );
| |_________^ Undefined Behavior occurred here
|
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
error: aborting due to 1 previous error

View file

@ -936,26 +936,93 @@ fn simd_float_intrinsics() {
}
fn simd_masked_loadstore() {
use intrinsics::*;
// The buffer is deliberarely too short, so reading the last element would be UB.
let buf = [3i32; 3];
let default = i32x4::splat(0);
let mask = i32x4::from_array([!0, !0, !0, 0]);
let vals = unsafe { intrinsics::simd_masked_load(mask, buf.as_ptr(), default) };
let vals =
unsafe { simd_masked_load::<_, _, _, { SimdAlign::Element }>(mask, buf.as_ptr(), default) };
assert_eq!(vals, i32x4::from_array([3, 3, 3, 0]));
// Also read in a way that the *first* element is OOB.
let mask2 = i32x4::from_array([0, !0, !0, !0]);
let vals =
unsafe { intrinsics::simd_masked_load(mask2, buf.as_ptr().wrapping_sub(1), default) };
let vals = unsafe {
simd_masked_load::<_, _, _, { SimdAlign::Element }>(
mask2,
buf.as_ptr().wrapping_sub(1),
default,
)
};
assert_eq!(vals, i32x4::from_array([0, 3, 3, 3]));
// The buffer is deliberarely too short, so writing the last element would be UB.
let mut buf = [42i32; 3];
let vals = i32x4::from_array([1, 2, 3, 4]);
unsafe { intrinsics::simd_masked_store(mask, buf.as_mut_ptr(), vals) };
unsafe { simd_masked_store::<_, _, _, { SimdAlign::Element }>(mask, buf.as_mut_ptr(), vals) };
assert_eq!(buf, [1, 2, 3]);
// Also write in a way that the *first* element is OOB.
unsafe { intrinsics::simd_masked_store(mask2, buf.as_mut_ptr().wrapping_sub(1), vals) };
unsafe {
simd_masked_store::<_, _, _, { SimdAlign::Element }>(
mask2,
buf.as_mut_ptr().wrapping_sub(1),
vals,
)
};
assert_eq!(buf, [2, 3, 4]);
// we use a purposely misaliged buffer to make sure Miri doesn't error in this case
let buf = [0x03030303_i32; 5];
let default = i32x4::splat(0);
let mask = i32x4::splat(!0);
let vals = unsafe {
simd_masked_load::<_, _, _, { SimdAlign::Unaligned }>(
mask,
buf.as_ptr().byte_offset(1), // this is guaranteed to be unaligned
default,
)
};
assert_eq!(vals, i32x4::splat(0x03030303));
let mut buf = [0i32; 5];
let mask = i32x4::splat(!0);
unsafe {
simd_masked_store::<_, _, _, { SimdAlign::Unaligned }>(
mask,
buf.as_mut_ptr().byte_offset(1), // this is guaranteed to be unaligned
vals,
)
};
assert_eq!(
buf,
[
i32::from_ne_bytes([0, 3, 3, 3]),
0x03030303,
0x03030303,
0x03030303,
i32::from_ne_bytes([3, 0, 0, 0])
]
);
// `repr(simd)` types like `Simd<T, N>` have the correct alignment for vectors
let buf = i32x4::splat(3);
let default = i32x4::splat(0);
let mask = i32x4::splat(!0);
let vals = unsafe {
simd_masked_load::<_, _, _, { SimdAlign::Vector }>(
mask,
&raw const buf as *const i32,
default,
)
};
assert_eq!(vals, buf);
let mut buf = i32x4::splat(0);
let mask = i32x4::splat(!0);
unsafe {
simd_masked_store::<_, _, _, { SimdAlign::Vector }>(mask, &raw mut buf as *mut i32, vals)
};
assert_eq!(buf, vals);
}
fn simd_ops_non_pow2() {

View file

@ -9,7 +9,7 @@
//@ assembly-output: emit-asm
//@ compile-flags: --crate-type=lib -Copt-level=3 -C panic=abort
#![feature(no_core, lang_items, repr_simd, intrinsics)]
#![feature(no_core, lang_items, repr_simd, intrinsics, adt_const_params)]
#![no_core]
#![allow(non_camel_case_types)]
@ -35,7 +35,7 @@ pub struct f64x4([f64; 4]);
pub struct m64x4([i64; 4]);
#[rustc_intrinsic]
unsafe fn simd_masked_load<M, P, T>(mask: M, pointer: P, values: T) -> T;
unsafe fn simd_masked_load<M, P, T, const ALIGN: SimdAlign>(mask: M, pointer: P, values: T) -> T;
// CHECK-LABEL: load_i8x16
#[no_mangle]
@ -56,7 +56,11 @@ pub unsafe extern "C" fn load_i8x16(mask: m8x16, pointer: *const i8) -> i8x16 {
// x86-avx512-NOT: vpsllw
// x86-avx512: vpmovb2m k1, xmm0
// x86-avx512-NEXT: vmovdqu8 xmm0 {k1} {z}, xmmword ptr [rdi]
simd_masked_load(mask, pointer, i8x16([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]))
simd_masked_load::<_, _, _, { SimdAlign::Element }>(
mask,
pointer,
i8x16([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]),
)
}
// CHECK-LABEL: load_f32x8
@ -68,7 +72,29 @@ pub unsafe extern "C" fn load_f32x8(mask: m32x8, pointer: *const f32) -> f32x8 {
// x86-avx512-NOT: vpslld
// x86-avx512: vpmovd2m k1, ymm0
// x86-avx512-NEXT: vmovups ymm0 {k1} {z}, ymmword ptr [rdi]
simd_masked_load(mask, pointer, f32x8([0_f32, 0_f32, 0_f32, 0_f32, 0_f32, 0_f32, 0_f32, 0_f32]))
simd_masked_load::<_, _, _, { SimdAlign::Element }>(
mask,
pointer,
f32x8([0_f32, 0_f32, 0_f32, 0_f32, 0_f32, 0_f32, 0_f32, 0_f32]),
)
}
// CHECK-LABEL: load_f32x8_aligned
#[no_mangle]
pub unsafe extern "C" fn load_f32x8_aligned(mask: m32x8, pointer: *const f32) -> f32x8 {
// x86-avx2-NOT: vpslld
// x86-avx2: vmaskmovps ymm0, ymm0, ymmword ptr [rdi]
//
// x86-avx512-NOT: vpslld
// x86-avx512: vpmovd2m k1, ymm0
// x86-avx512-NEXT: vmovaps ymm0 {k1} {z}, ymmword ptr [rdi]
//
// this aligned version should generate `movaps` instead of `movups`
simd_masked_load::<_, _, _, { SimdAlign::Vector }>(
mask,
pointer,
f32x8([0_f32, 0_f32, 0_f32, 0_f32, 0_f32, 0_f32, 0_f32, 0_f32]),
)
}
// CHECK-LABEL: load_f64x4
@ -79,5 +105,9 @@ pub unsafe extern "C" fn load_f64x4(mask: m64x4, pointer: *const f64) -> f64x4 {
//
// x86-avx512-NOT: vpsllq
// x86-avx512: vpmovq2m k1, ymm0
simd_masked_load(mask, pointer, f64x4([0_f64, 0_f64, 0_f64, 0_f64]))
simd_masked_load::<_, _, _, { SimdAlign::Element }>(
mask,
pointer,
f64x4([0_f64, 0_f64, 0_f64, 0_f64]),
)
}

View file

@ -9,7 +9,7 @@
//@ assembly-output: emit-asm
//@ compile-flags: --crate-type=lib -Copt-level=3 -C panic=abort
#![feature(no_core, lang_items, repr_simd, intrinsics)]
#![feature(no_core, lang_items, repr_simd, intrinsics, adt_const_params)]
#![no_core]
#![allow(non_camel_case_types)]
@ -35,7 +35,7 @@ pub struct f64x4([f64; 4]);
pub struct m64x4([i64; 4]);
#[rustc_intrinsic]
unsafe fn simd_masked_store<M, P, T>(mask: M, pointer: P, values: T);
unsafe fn simd_masked_store<M, P, T, const ALIGN: SimdAlign>(mask: M, pointer: P, values: T);
// CHECK-LABEL: store_i8x16
#[no_mangle]
@ -54,7 +54,7 @@ pub unsafe extern "C" fn store_i8x16(mask: m8x16, pointer: *mut i8, value: i8x16
// x86-avx512-NOT: vpsllw
// x86-avx512: vpmovb2m k1, xmm0
// x86-avx512-NEXT: vmovdqu8 xmmword ptr [rdi] {k1}, xmm1
simd_masked_store(mask, pointer, value)
simd_masked_store::<_, _, _, { SimdAlign::Element }>(mask, pointer, value)
}
// CHECK-LABEL: store_f32x8
@ -66,7 +66,21 @@ pub unsafe extern "C" fn store_f32x8(mask: m32x8, pointer: *mut f32, value: f32x
// x86-avx512-NOT: vpslld
// x86-avx512: vpmovd2m k1, ymm0
// x86-avx512-NEXT: vmovups ymmword ptr [rdi] {k1}, ymm1
simd_masked_store(mask, pointer, value)
simd_masked_store::<_, _, _, { SimdAlign::Element }>(mask, pointer, value)
}
// CHECK-LABEL: store_f32x8_aligned
#[no_mangle]
pub unsafe extern "C" fn store_f32x8_aligned(mask: m32x8, pointer: *mut f32, value: f32x8) {
// x86-avx2-NOT: vpslld
// x86-avx2: vmaskmovps ymmword ptr [rdi], ymm0, ymm1
//
// x86-avx512-NOT: vpslld
// x86-avx512: vpmovd2m k1, ymm0
// x86-avx512-NEXT: vmovaps ymmword ptr [rdi] {k1}, ymm1
//
// this aligned version should generate `movaps` instead of `movups`
simd_masked_store::<_, _, _, { SimdAlign::Vector }>(mask, pointer, value)
}
// CHECK-LABEL: store_f64x4
@ -78,5 +92,5 @@ pub unsafe extern "C" fn store_f64x4(mask: m64x4, pointer: *mut f64, value: f64x
// x86-avx512-NOT: vpsllq
// x86-avx512: vpmovq2m k1, ymm0
// x86-avx512-NEXT: vmovupd ymmword ptr [rdi] {k1}, ymm1
simd_masked_store(mask, pointer, value)
simd_masked_store::<_, _, _, { SimdAlign::Element }>(mask, pointer, value)
}

View file

@ -261,3 +261,17 @@ pub enum c_void {
__variant1,
__variant2,
}
#[lang = "const_param_ty"]
#[diagnostic::on_unimplemented(message = "`{Self}` can't be used as a const parameter type")]
pub trait ConstParamTy_ {}
pub enum SimdAlign {
// These values must match the compiler's `SimdAlign` defined in
// `rustc_middle/src/ty/consts/int.rs`!
Unaligned = 0,
Element = 1,
Vector = 2,
}
impl ConstParamTy_ for SimdAlign {}

View file

@ -12,7 +12,7 @@
mod minisimd;
use minisimd::*;
use std::intrinsics::simd::simd_masked_load;
use std::intrinsics::simd::{SimdAlign, simd_masked_load};
pub type Vec2<T> = Simd<T, 2>;
pub type Vec4<T> = Simd<T, 4>;
@ -23,8 +23,45 @@ pub unsafe fn load_f32x2(mask: Vec2<i32>, pointer: *const f32, values: Vec2<f32>
// CHECK: [[A:%[0-9]+]] = lshr <2 x i32> {{.*}}, {{<i32 31, i32 31>|splat \(i32 31\)}}
// CHECK: [[B:%[0-9]+]] = trunc <2 x i32> [[A]] to <2 x i1>
// LLVM21: call <2 x float> @llvm.masked.load.v2f32.p0(ptr {{.*}}, i32 4, <2 x i1> [[B]], <2 x float> {{.*}})
// ^^^^^
// LLVM22: call <2 x float> @llvm.masked.load.v2f32.p0(ptr align 4 {{.*}}, <2 x i1> [[B]], <2 x float> {{.*}})
simd_masked_load(mask, pointer, values)
// ^^^^^^^
// the align parameter should be equal to the alignment of the element type (assumed to be 4)
simd_masked_load::<_, _, _, { SimdAlign::Element }>(mask, pointer, values)
}
// CHECK-LABEL: @load_f32x2_aligned
#[no_mangle]
pub unsafe fn load_f32x2_aligned(
mask: Vec2<i32>,
pointer: *const f32,
values: Vec2<f32>,
) -> Vec2<f32> {
// CHECK: [[A:%[0-9]+]] = lshr <2 x i32> {{.*}}, {{<i32 31, i32 31>|splat \(i32 31\)}}
// CHECK: [[B:%[0-9]+]] = trunc <2 x i32> [[A]] to <2 x i1>
// LLVM21: call <2 x float> @llvm.masked.load.v2f32.p0(ptr {{.*}}, i32 8, <2 x i1> [[B]], <2 x float> {{.*}})
// ^^^^^
// LLVM22: call <2 x float> @llvm.masked.load.v2f32.p0(ptr align 8 {{.*}}, <2 x i1> [[B]], <2 x float> {{.*}})
// ^^^^^^^
// the align parameter should be equal to the size of the vector
simd_masked_load::<_, _, _, { SimdAlign::Vector }>(mask, pointer, values)
}
// CHECK-LABEL: @load_f32x2_unaligned
#[no_mangle]
pub unsafe fn load_f32x2_unaligned(
mask: Vec2<i32>,
pointer: *const f32,
values: Vec2<f32>,
) -> Vec2<f32> {
// CHECK: [[A:%[0-9]+]] = lshr <2 x i32> {{.*}}, {{<i32 31, i32 31>|splat \(i32 31\)}}
// CHECK: [[B:%[0-9]+]] = trunc <2 x i32> [[A]] to <2 x i1>
// LLVM21: call <2 x float> @llvm.masked.load.v2f32.p0(ptr {{.*}}, i32 1, <2 x i1> [[B]], <2 x float> {{.*}})
// ^^^^^
// LLVM22: call <2 x float> @llvm.masked.load.v2f32.p0(ptr align 1 {{.*}}, <2 x i1> [[B]], <2 x float> {{.*}})
// ^^^^^^^
// the align parameter should be 1
simd_masked_load::<_, _, _, { SimdAlign::Unaligned }>(mask, pointer, values)
}
// CHECK-LABEL: @load_f32x2_unsigned
@ -38,7 +75,7 @@ pub unsafe fn load_f32x2_unsigned(
// CHECK: [[B:%[0-9]+]] = trunc <2 x i32> [[A]] to <2 x i1>
// LLVM21: call <2 x float> @llvm.masked.load.v2f32.p0(ptr {{.*}}, i32 4, <2 x i1> [[B]], <2 x float> {{.*}})
// LLVM22: call <2 x float> @llvm.masked.load.v2f32.p0(ptr align 4 {{.*}}, <2 x i1> [[B]], <2 x float> {{.*}})
simd_masked_load(mask, pointer, values)
simd_masked_load::<_, _, _, { SimdAlign::Element }>(mask, pointer, values)
}
// CHECK-LABEL: @load_pf32x4
@ -52,5 +89,5 @@ pub unsafe fn load_pf32x4(
// CHECK: [[B:%[0-9]+]] = trunc <4 x i32> [[A]] to <4 x i1>
// LLVM21: call <4 x ptr> @llvm.masked.load.v4p0.p0(ptr {{.*}}, i32 {{.*}}, <4 x i1> [[B]], <4 x ptr> {{.*}})
// LLVM22: call <4 x ptr> @llvm.masked.load.v4p0.p0(ptr align {{.*}} {{.*}}, <4 x i1> [[B]], <4 x ptr> {{.*}})
simd_masked_load(mask, pointer, values)
simd_masked_load::<_, _, _, { SimdAlign::Element }>(mask, pointer, values)
}

View file

@ -12,7 +12,7 @@
mod minisimd;
use minisimd::*;
use std::intrinsics::simd::simd_masked_store;
use std::intrinsics::simd::{SimdAlign, simd_masked_store};
pub type Vec2<T> = Simd<T, 2>;
pub type Vec4<T> = Simd<T, 4>;
@ -23,8 +23,37 @@ pub unsafe fn store_f32x2(mask: Vec2<i32>, pointer: *mut f32, values: Vec2<f32>)
// CHECK: [[A:%[0-9]+]] = lshr <2 x i32> {{.*}}, {{<i32 31, i32 31>|splat \(i32 31\)}}
// CHECK: [[B:%[0-9]+]] = trunc <2 x i32> [[A]] to <2 x i1>
// LLVM21: call void @llvm.masked.store.v2f32.p0(<2 x float> {{.*}}, ptr {{.*}}, i32 4, <2 x i1> [[B]])
// ^^^^^
// LLVM22: call void @llvm.masked.store.v2f32.p0(<2 x float> {{.*}}, ptr align 4 {{.*}}, <2 x i1> [[B]])
simd_masked_store(mask, pointer, values)
// ^^^^^^^
// the align parameter should be equal to the alignment of the element type (assumed to be 4)
simd_masked_store::<_, _, _, { SimdAlign::Element }>(mask, pointer, values)
}
// CHECK-LABEL: @store_f32x2_aligned
#[no_mangle]
pub unsafe fn store_f32x2_aligned(mask: Vec2<i32>, pointer: *mut f32, values: Vec2<f32>) {
// CHECK: [[A:%[0-9]+]] = lshr <2 x i32> {{.*}}, {{<i32 31, i32 31>|splat \(i32 31\)}}
// CHECK: [[B:%[0-9]+]] = trunc <2 x i32> [[A]] to <2 x i1>
// LLVM21: call void @llvm.masked.store.v2f32.p0(<2 x float> {{.*}}, ptr {{.*}}, i32 8, <2 x i1> [[B]])
// ^^^^^
// LLVM22: call void @llvm.masked.store.v2f32.p0(<2 x float> {{.*}}, ptr align 8 {{.*}}, <2 x i1> [[B]])
// ^^^^^^^
// the align parameter should be equal to the size of the vector
simd_masked_store::<_, _, _, { SimdAlign::Vector }>(mask, pointer, values)
}
// CHECK-LABEL: @store_f32x2_unaligned
#[no_mangle]
pub unsafe fn store_f32x2_unaligned(mask: Vec2<i32>, pointer: *mut f32, values: Vec2<f32>) {
// CHECK: [[A:%[0-9]+]] = lshr <2 x i32> {{.*}}, {{<i32 31, i32 31>|splat \(i32 31\)}}
// CHECK: [[B:%[0-9]+]] = trunc <2 x i32> [[A]] to <2 x i1>
// LLVM21: call void @llvm.masked.store.v2f32.p0(<2 x float> {{.*}}, ptr {{.*}}, i32 1, <2 x i1> [[B]])
// ^^^^^
// LLVM22: call void @llvm.masked.store.v2f32.p0(<2 x float> {{.*}}, ptr align 1 {{.*}}, <2 x i1> [[B]])
// ^^^^^^^
// the align parameter should be 1
simd_masked_store::<_, _, _, { SimdAlign::Unaligned }>(mask, pointer, values)
}
// CHECK-LABEL: @store_f32x2_unsigned
@ -34,7 +63,7 @@ pub unsafe fn store_f32x2_unsigned(mask: Vec2<u32>, pointer: *mut f32, values: V
// CHECK: [[B:%[0-9]+]] = trunc <2 x i32> [[A]] to <2 x i1>
// LLVM21: call void @llvm.masked.store.v2f32.p0(<2 x float> {{.*}}, ptr {{.*}}, i32 4, <2 x i1> [[B]])
// LLVM22: call void @llvm.masked.store.v2f32.p0(<2 x float> {{.*}}, ptr align 4 {{.*}}, <2 x i1> [[B]])
simd_masked_store(mask, pointer, values)
simd_masked_store::<_, _, _, { SimdAlign::Element }>(mask, pointer, values)
}
// CHECK-LABEL: @store_pf32x4
@ -44,5 +73,5 @@ pub unsafe fn store_pf32x4(mask: Vec4<i32>, pointer: *mut *const f32, values: Ve
// CHECK: [[B:%[0-9]+]] = trunc <4 x i32> [[A]] to <4 x i1>
// LLVM21: call void @llvm.masked.store.v4p0.p0(<4 x ptr> {{.*}}, ptr {{.*}}, i32 {{.*}}, <4 x i1> [[B]])
// LLVM22: call void @llvm.masked.store.v4p0.p0(<4 x ptr> {{.*}}, ptr align {{.*}} {{.*}}, <4 x i1> [[B]])
simd_masked_store(mask, pointer, values)
simd_masked_store::<_, _, _, { SimdAlign::Element }>(mask, pointer, values)
}

View file

@ -61,17 +61,11 @@
StorageDead(_1);
return;
}
+ }
+
+ ALLOC0 (size: 4, align: 4) {
+ 00 00 00 00 │ ....
+ }
+
+ ALLOC1 (size: 4, align: 4) {
+ 04 00 00 00 │ ....
+ }
+
+ ALLOC2 (size: 4, align: 4) {
+ 01 00 11 00 │ ....
}
+
+ ALLOC0 (size: 4, align: 4) { .. }
+
+ ALLOC1 (size: 4, align: 4) { .. }
+
+ ALLOC2 (size: 4, align: 4) { .. }

View file

@ -1,6 +1,6 @@
// skip-filecheck
//@ test-mir-pass: GVN
//@ compile-flags: -Zmir-enable-passes=+RemoveZsts
//@ compile-flags: -Zmir-enable-passes=+RemoveZsts -Zdump-mir-exclude-alloc-bytes
// Verify that we can pretty print invalid constants.
#![feature(adt_const_params, unsized_const_params)]

View file

@ -34,9 +34,7 @@
StorageDead(_1);
return;
}
+ }
+
+ ALLOC0 (size: 4, align: 4) {
+ 01 00 00 00 │ ....
}
+
+ ALLOC0 (size: 4, align: 4) { .. }

View file

@ -1,6 +1,6 @@
//! Tests that we can propagate into places that are projections into unions
//@ test-mir-pass: GVN
//@ compile-flags: -Zinline-mir
//@ compile-flags: -Zinline-mir -Zdump-mir-exclude-alloc-bytes
fn val() -> u32 {
1

View file

@ -107,9 +107,7 @@
StorageDead(_11);
goto -> bb4;
}
+ }
+
+ ALLOC0 (size: 8, align: 4) {
+ 01 00 00 00 __ __ __ __ │ ....░░░░
}
+
+ ALLOC0 (size: 8, align: 4) { .. }

View file

@ -1,4 +1,5 @@
//@ test-mir-pass: GVN
//@ compile-flags: -Zdump-mir-exclude-alloc-bytes
#![crate_type = "lib"]
#![feature(core_intrinsics, rustc_attrs)]

View file

@ -2,7 +2,7 @@
//@ ignore-backends: gcc
#![feature(repr_simd, core_intrinsics)]
use std::intrinsics::simd::{simd_masked_load, simd_masked_store};
use std::intrinsics::simd::{SimdAlign, simd_masked_load, simd_masked_store};
#[derive(Copy, Clone)]
#[repr(simd)]
@ -13,28 +13,60 @@ fn main() {
let mut arr = [4u8, 5, 6, 7];
let default = Simd::<u8, 4>([9; 4]);
simd_masked_load(Simd::<i8, 8>([-1, 0, -1, -1, 0, 0, 0, 0]), arr.as_ptr(), default);
//~^ ERROR expected third argument with length 8 (same as input type `Simd<i8, 8>`), found `Simd<u8, 4>` with length 4
//~v ERROR expected third argument with length 8 (same as input type `Simd<i8, 8>`), found `Simd<u8, 4>` with length 4
simd_masked_load::<_, _, _, { SimdAlign::Element }>(
Simd::<i8, 8>([-1, 0, -1, -1, 0, 0, 0, 0]),
arr.as_ptr(),
default,
);
simd_masked_load(Simd::<i8, 4>([-1, 0, -1, -1]), arr.as_ptr() as *const i8, default);
//~^ ERROR expected element type `u8` of second argument `*const i8` to be a pointer to the element type `u8` of the first argument `Simd<u8, 4>`, found `u8` != `*_ u8`
//~v ERROR expected element type `u8` of second argument `*const i8` to be a pointer to the element type `u8` of the first argument `Simd<u8, 4>`, found `u8` != `*_ u8`
simd_masked_load::<_, _, _, { SimdAlign::Element }>(
Simd::<i8, 4>([-1, 0, -1, -1]),
arr.as_ptr() as *const i8,
default,
);
simd_masked_load(Simd::<i8, 4>([-1, 0, -1, -1]), arr.as_ptr(), Simd::<u32, 4>([9; 4]));
//~^ ERROR expected element type `u32` of second argument `*const u8` to be a pointer to the element type `u32` of the first argument `Simd<u32, 4>`, found `u32` != `*_ u32`
//~v ERROR expected element type `u32` of second argument `*const u8` to be a pointer to the element type `u32` of the first argument `Simd<u32, 4>`, found `u32` != `*_ u32`
simd_masked_load::<_, _, _, { SimdAlign::Element }>(
Simd::<i8, 4>([-1, 0, -1, -1]),
arr.as_ptr(),
Simd::<u32, 4>([9; 4]),
);
simd_masked_load(Simd::<f32, 4>([1.0, 0.0, 1.0, 1.0]), arr.as_ptr(), default);
//~^ ERROR expected mask element type to be an integer, found `f32`
//~v ERROR expected mask element type to be an integer, found `f32`
simd_masked_load::<_, _, _, { SimdAlign::Element }>(
Simd::<f32, 4>([1.0, 0.0, 1.0, 1.0]),
arr.as_ptr(),
default,
);
simd_masked_store(Simd([-1i8; 4]), arr.as_ptr(), Simd([5u32; 4]));
//~^ ERROR expected element type `u32` of second argument `*const u8` to be a pointer to the element type `u32` of the first argument `Simd<u32, 4>`, found `u32` != `*mut u32`
//~v ERROR expected element type `u32` of second argument `*const u8` to be a pointer to the element type `u32` of the first argument `Simd<u32, 4>`, found `u32` != `*mut u32`
simd_masked_store::<_, _, _, { SimdAlign::Element }>(
Simd([-1i8; 4]),
arr.as_ptr(),
Simd([5u32; 4]),
);
simd_masked_store(Simd([-1i8; 4]), arr.as_ptr(), Simd([5u8; 4]));
//~^ ERROR expected element type `u8` of second argument `*const u8` to be a pointer to the element type `u8` of the first argument `Simd<u8, 4>`, found `u8` != `*mut u8`
//~v ERROR expected element type `u8` of second argument `*const u8` to be a pointer to the element type `u8` of the first argument `Simd<u8, 4>`, found `u8` != `*mut u8`
simd_masked_store::<_, _, _, { SimdAlign::Element }>(
Simd([-1i8; 4]),
arr.as_ptr(),
Simd([5u8; 4]),
);
simd_masked_store(Simd([-1i8; 4]), arr.as_mut_ptr(), Simd([5u8; 2]));
//~^ ERROR expected third argument with length 4 (same as input type `Simd<i8, 4>`), found `Simd<u8, 2>` with length 2
//~v ERROR expected third argument with length 4 (same as input type `Simd<i8, 4>`), found `Simd<u8, 2>` with length 2
simd_masked_store::<_, _, _, { SimdAlign::Element }>(
Simd([-1i8; 4]),
arr.as_mut_ptr(),
Simd([5u8; 2]),
);
simd_masked_store(Simd([1f32; 4]), arr.as_mut_ptr(), Simd([5u8; 4]));
//~^ ERROR expected mask element type to be an integer, found `f32`
//~v ERROR expected mask element type to be an integer, found `f32`
simd_masked_store::<_, _, _, { SimdAlign::Element }>(
Simd([1f32; 4]),
arr.as_mut_ptr(),
Simd([5u8; 4]),
);
}
}

View file

@ -1,50 +1,82 @@
error[E0511]: invalid monomorphization of `simd_masked_load` intrinsic: expected third argument with length 8 (same as input type `Simd<i8, 8>`), found `Simd<u8, 4>` with length 4
--> $DIR/masked-load-store-build-fail.rs:16:9
--> $DIR/masked-load-store-build-fail.rs:17:9
|
LL | simd_masked_load(Simd::<i8, 8>([-1, 0, -1, -1, 0, 0, 0, 0]), arr.as_ptr(), default);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | / simd_masked_load::<_, _, _, { SimdAlign::Element }>(
LL | | Simd::<i8, 8>([-1, 0, -1, -1, 0, 0, 0, 0]),
LL | | arr.as_ptr(),
LL | | default,
LL | | );
| |_________^
error[E0511]: invalid monomorphization of `simd_masked_load` intrinsic: expected element type `u8` of second argument `*const i8` to be a pointer to the element type `u8` of the first argument `Simd<u8, 4>`, found `u8` != `*_ u8`
--> $DIR/masked-load-store-build-fail.rs:19:9
--> $DIR/masked-load-store-build-fail.rs:24:9
|
LL | simd_masked_load(Simd::<i8, 4>([-1, 0, -1, -1]), arr.as_ptr() as *const i8, default);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | / simd_masked_load::<_, _, _, { SimdAlign::Element }>(
LL | | Simd::<i8, 4>([-1, 0, -1, -1]),
LL | | arr.as_ptr() as *const i8,
LL | | default,
LL | | );
| |_________^
error[E0511]: invalid monomorphization of `simd_masked_load` intrinsic: expected element type `u32` of second argument `*const u8` to be a pointer to the element type `u32` of the first argument `Simd<u32, 4>`, found `u32` != `*_ u32`
--> $DIR/masked-load-store-build-fail.rs:22:9
|
LL | simd_masked_load(Simd::<i8, 4>([-1, 0, -1, -1]), arr.as_ptr(), Simd::<u32, 4>([9; 4]));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error[E0511]: invalid monomorphization of `simd_masked_load` intrinsic: expected mask element type to be an integer, found `f32`
--> $DIR/masked-load-store-build-fail.rs:25:9
|
LL | simd_masked_load(Simd::<f32, 4>([1.0, 0.0, 1.0, 1.0]), arr.as_ptr(), default);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error[E0511]: invalid monomorphization of `simd_masked_store` intrinsic: expected element type `u32` of second argument `*const u8` to be a pointer to the element type `u32` of the first argument `Simd<u32, 4>`, found `u32` != `*mut u32`
--> $DIR/masked-load-store-build-fail.rs:28:9
|
LL | simd_masked_store(Simd([-1i8; 4]), arr.as_ptr(), Simd([5u32; 4]));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error[E0511]: invalid monomorphization of `simd_masked_store` intrinsic: expected element type `u8` of second argument `*const u8` to be a pointer to the element type `u8` of the first argument `Simd<u8, 4>`, found `u8` != `*mut u8`
--> $DIR/masked-load-store-build-fail.rs:31:9
|
LL | simd_masked_store(Simd([-1i8; 4]), arr.as_ptr(), Simd([5u8; 4]));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | / simd_masked_load::<_, _, _, { SimdAlign::Element }>(
LL | | Simd::<i8, 4>([-1, 0, -1, -1]),
LL | | arr.as_ptr(),
LL | | Simd::<u32, 4>([9; 4]),
LL | | );
| |_________^
error[E0511]: invalid monomorphization of `simd_masked_load` intrinsic: expected mask element type to be an integer, found `f32`
--> $DIR/masked-load-store-build-fail.rs:38:9
|
LL | / simd_masked_load::<_, _, _, { SimdAlign::Element }>(
LL | | Simd::<f32, 4>([1.0, 0.0, 1.0, 1.0]),
LL | | arr.as_ptr(),
LL | | default,
LL | | );
| |_________^
error[E0511]: invalid monomorphization of `simd_masked_store` intrinsic: expected element type `u32` of second argument `*const u8` to be a pointer to the element type `u32` of the first argument `Simd<u32, 4>`, found `u32` != `*mut u32`
--> $DIR/masked-load-store-build-fail.rs:45:9
|
LL | / simd_masked_store::<_, _, _, { SimdAlign::Element }>(
LL | | Simd([-1i8; 4]),
LL | | arr.as_ptr(),
LL | | Simd([5u32; 4]),
LL | | );
| |_________^
error[E0511]: invalid monomorphization of `simd_masked_store` intrinsic: expected element type `u8` of second argument `*const u8` to be a pointer to the element type `u8` of the first argument `Simd<u8, 4>`, found `u8` != `*mut u8`
--> $DIR/masked-load-store-build-fail.rs:52:9
|
LL | / simd_masked_store::<_, _, _, { SimdAlign::Element }>(
LL | | Simd([-1i8; 4]),
LL | | arr.as_ptr(),
LL | | Simd([5u8; 4]),
LL | | );
| |_________^
error[E0511]: invalid monomorphization of `simd_masked_store` intrinsic: expected third argument with length 4 (same as input type `Simd<i8, 4>`), found `Simd<u8, 2>` with length 2
--> $DIR/masked-load-store-build-fail.rs:34:9
--> $DIR/masked-load-store-build-fail.rs:59:9
|
LL | simd_masked_store(Simd([-1i8; 4]), arr.as_mut_ptr(), Simd([5u8; 2]));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | / simd_masked_store::<_, _, _, { SimdAlign::Element }>(
LL | | Simd([-1i8; 4]),
LL | | arr.as_mut_ptr(),
LL | | Simd([5u8; 2]),
LL | | );
| |_________^
error[E0511]: invalid monomorphization of `simd_masked_store` intrinsic: expected mask element type to be an integer, found `f32`
--> $DIR/masked-load-store-build-fail.rs:37:9
--> $DIR/masked-load-store-build-fail.rs:66:9
|
LL | simd_masked_store(Simd([1f32; 4]), arr.as_mut_ptr(), Simd([5u8; 4]));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | / simd_masked_store::<_, _, _, { SimdAlign::Element }>(
LL | | Simd([1f32; 4]),
LL | | arr.as_mut_ptr(),
LL | | Simd([5u8; 4]),
LL | | );
| |_________^
error: aborting due to 8 previous errors

View file

@ -1,7 +1,7 @@
//@ check-fail
#![feature(repr_simd, core_intrinsics)]
use std::intrinsics::simd::{simd_masked_load, simd_masked_store};
use std::intrinsics::simd::{SimdAlign, simd_masked_load, simd_masked_store};
#[derive(Copy, Clone)]
#[repr(simd)]
@ -12,11 +12,18 @@ fn main() {
let mut arr = [4u8, 5, 6, 7];
let default = Simd::<u8, 4>([9; 4]);
let _x: Simd<u8, 2> =
simd_masked_load(Simd::<i8, 4>([-1, 0, -1, -1]), arr.as_ptr(), Simd::<u8, 4>([9; 4]));
//~^ ERROR mismatched types
let _x: Simd<u8, 2> = simd_masked_load::<_, _, _, { SimdAlign::Element }>(
Simd::<i8, 4>([-1, 0, -1, -1]),
arr.as_ptr(),
Simd::<u8, 4>([9; 4]),
);
//~^^ ERROR mismatched types
let _x: Simd<u32, 4> = simd_masked_load(Simd::<u8, 4>([1, 0, 1, 1]), arr.as_ptr(), default);
//~^ ERROR mismatched types
let _x: Simd<u32, 4> = simd_masked_load::<_, _, _, { SimdAlign::Element }>(
Simd::<u8, 4>([1, 0, 1, 1]),
arr.as_ptr(),
default,
);
//~^^ ERROR mismatched types
}
}

View file

@ -1,36 +1,50 @@
error[E0308]: mismatched types
--> $DIR/masked-load-store-check-fail.rs:16:76
--> $DIR/masked-load-store-check-fail.rs:18:13
|
LL | simd_masked_load(Simd::<i8, 4>([-1, 0, -1, -1]), arr.as_ptr(), Simd::<u8, 4>([9; 4]));
| ---------------- arguments to this function are incorrect ^^^^^^^^^^^^^^^^^^^^^ expected `2`, found `4`
LL | let _x: Simd<u8, 2> = simd_masked_load::<_, _, _, { SimdAlign::Element }>(
| --------------------------------------------------- arguments to this function are incorrect
...
LL | Simd::<u8, 4>([9; 4]),
| ^^^^^^^^^^^^^^^^^^^^^ expected `2`, found `4`
|
= note: expected struct `Simd<_, 2>`
found struct `Simd<_, 4>`
help: the return type of this call is `Simd<u8, 4>` due to the type of the argument passed
--> $DIR/masked-load-store-check-fail.rs:16:13
--> $DIR/masked-load-store-check-fail.rs:15:31
|
LL | simd_masked_load(Simd::<i8, 4>([-1, 0, -1, -1]), arr.as_ptr(), Simd::<u8, 4>([9; 4]));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^---------------------^
| |
| this argument influences the return type of `simd_masked_load`
LL | let _x: Simd<u8, 2> = simd_masked_load::<_, _, _, { SimdAlign::Element }>(
| _______________________________^
LL | | Simd::<i8, 4>([-1, 0, -1, -1]),
LL | | arr.as_ptr(),
LL | | Simd::<u8, 4>([9; 4]),
| | --------------------- this argument influences the return type of `simd_masked_load`
LL | | );
| |_________^
note: function defined here
--> $SRC_DIR/core/src/intrinsics/simd.rs:LL:COL
error[E0308]: mismatched types
--> $DIR/masked-load-store-check-fail.rs:19:92
--> $DIR/masked-load-store-check-fail.rs:25:13
|
LL | let _x: Simd<u32, 4> = simd_masked_load(Simd::<u8, 4>([1, 0, 1, 1]), arr.as_ptr(), default);
| ---------------- arguments to this function are incorrect ^^^^^^^ expected `Simd<u32, 4>`, found `Simd<u8, 4>`
LL | let _x: Simd<u32, 4> = simd_masked_load::<_, _, _, { SimdAlign::Element }>(
| --------------------------------------------------- arguments to this function are incorrect
...
LL | default,
| ^^^^^^^ expected `Simd<u32, 4>`, found `Simd<u8, 4>`
|
= note: expected struct `Simd<u32, _>`
found struct `Simd<u8, _>`
help: the return type of this call is `Simd<u8, 4>` due to the type of the argument passed
--> $DIR/masked-load-store-check-fail.rs:19:32
--> $DIR/masked-load-store-check-fail.rs:22:32
|
LL | let _x: Simd<u32, 4> = simd_masked_load(Simd::<u8, 4>([1, 0, 1, 1]), arr.as_ptr(), default);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-------^
| |
| this argument influences the return type of `simd_masked_load`
LL | let _x: Simd<u32, 4> = simd_masked_load::<_, _, _, { SimdAlign::Element }>(
| ________________________________^
LL | | Simd::<u8, 4>([1, 0, 1, 1]),
LL | | arr.as_ptr(),
LL | | default,
| | ------- this argument influences the return type of `simd_masked_load`
LL | | );
| |_________^
note: function defined here
--> $SRC_DIR/core/src/intrinsics/simd.rs:LL:COL

View file

@ -6,23 +6,34 @@
mod minisimd;
use minisimd::*;
use std::intrinsics::simd::{simd_masked_load, simd_masked_store};
use std::intrinsics::simd::{SimdAlign, simd_masked_load, simd_masked_store};
fn main() {
unsafe {
let a = Simd::<u8, 4>([0, 1, 2, 3]);
let b_src = [4u8, 5, 6, 7];
let b_default = Simd::<u8, 4>([9; 4]);
let b: Simd<u8, 4> =
simd_masked_load(Simd::<i8, 4>([-1, 0, -1, -1]), b_src.as_ptr(), b_default);
let b: Simd<u8, 4> = simd_masked_load::<_, _, _, { SimdAlign::Element }>(
Simd::<i8, 4>([-1, 0, -1, -1]),
b_src.as_ptr(),
b_default,
);
assert_eq!(b.as_array(), &[4, 9, 6, 7]);
let mut output = [u8::MAX; 5];
simd_masked_store(Simd::<i8, 4>([-1, -1, -1, 0]), output.as_mut_ptr(), a);
simd_masked_store::<_, _, _, { SimdAlign::Element }>(
Simd::<i8, 4>([-1, -1, -1, 0]),
output.as_mut_ptr(),
a,
);
assert_eq!(&output, &[0, 1, 2, u8::MAX, u8::MAX]);
simd_masked_store(Simd::<i8, 4>([0, -1, -1, 0]), output[1..].as_mut_ptr(), b);
simd_masked_store::<_, _, _, { SimdAlign::Element }>(
Simd::<i8, 4>([0, -1, -1, 0]),
output[1..].as_mut_ptr(),
b,
);
assert_eq!(&output, &[0, 1, 9, 6, u8::MAX]);
}
}