Merge ref '27050c0d15' from rust-lang/rust

Pull recent changes from https://github.com/rust-lang/rust via Josh.

Upstream ref: 27050c0d15
Filtered ref: 2bccf110c70c6f6ec349ede9fbf0b4cdcf77eac6
Upstream diff: 6244effd03...27050c0d15

This merge was created using https://github.com/rust-lang/josh-sync.
This commit is contained in:
The Miri Cronjob Bot 2025-10-24 05:00:24 +00:00
commit 77e76518a3
171 changed files with 1814 additions and 1376 deletions

View file

@ -427,6 +427,7 @@ Marcell Pardavi <marcell.pardavi@gmail.com>
Marco Ieni <11428655+MarcoIeni@users.noreply.github.com>
Marcus Klaas de Vries <mail@marcusklaas.nl>
Margaret Meyerhofer <mmeyerho@andrew.cmu.edu> <mmeyerho@andrew>
Marijn Schouten <mhkbst@gmail.com> <hkBst@users.noreply.github.com>
Mark Mansi <markm@cs.wisc.edu>
Mark Mansi <markm@cs.wisc.edu> <m.mim95@gmail.com>
Mark Rousskov <mark.simulacrum@gmail.com>

View file

@ -3636,49 +3636,26 @@ pub struct Trait {
pub items: ThinVec<Box<AssocItem>>,
}
/// The location of a where clause on a `TyAlias` (`Span`) and whether there was
/// a `where` keyword (`bool`). This is split out from `WhereClause`, since there
/// are two locations for where clause on type aliases, but their predicates
/// are concatenated together.
///
/// Take this example:
/// ```ignore (only-for-syntax-highlight)
/// trait Foo {
/// type Assoc<'a, 'b> where Self: 'a, Self: 'b;
/// }
/// impl Foo for () {
/// type Assoc<'a, 'b> where Self: 'a = () where Self: 'b;
/// // ^^^^^^^^^^^^^^ first where clause
/// // ^^^^^^^^^^^^^^ second where clause
/// }
/// ```
///
/// If there is no where clause, then this is `false` with `DUMMY_SP`.
#[derive(Copy, Clone, Encodable, Decodable, Debug, Default, Walkable)]
pub struct TyAliasWhereClause {
pub has_where_token: bool,
pub span: Span,
}
/// The span information for the two where clauses on a `TyAlias`.
#[derive(Copy, Clone, Encodable, Decodable, Debug, Default, Walkable)]
pub struct TyAliasWhereClauses {
/// Before the equals sign.
pub before: TyAliasWhereClause,
/// After the equals sign.
pub after: TyAliasWhereClause,
/// The index in `TyAlias.generics.where_clause.predicates` that would split
/// into predicates from the where clause before the equals sign and the ones
/// from the where clause after the equals sign.
pub split: usize,
}
#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
pub struct TyAlias {
pub defaultness: Defaultness,
pub ident: Ident,
pub generics: Generics,
pub where_clauses: TyAliasWhereClauses,
/// There are two locations for where clause on type aliases. This represents the second
/// where clause, before the semicolon. The first where clause is stored inside `generics`.
///
/// Take this example:
/// ```ignore (only-for-syntax-highlight)
/// trait Foo {
/// type Assoc<'a, 'b> where Self: 'a, Self: 'b;
/// }
/// impl Foo for () {
/// type Assoc<'a, 'b> where Self: 'a = () where Self: 'b;
/// // ^^^^^^^^^^^^^^ before where clause
/// // ^^^^^^^^^^^^^^ after where clause
/// }
/// ```
pub after_where_clause: WhereClause,
#[visitable(extra = BoundKind::Bound)]
pub bounds: GenericBounds,
pub ty: Option<Box<Ty>>,

View file

@ -471,8 +471,6 @@ macro_rules! common_visitor_and_walkers {
TraitBoundModifiers,
TraitObjectSyntax,
TyAlias,
TyAliasWhereClause,
TyAliasWhereClauses,
TyKind,
TyPatKind,
UnOp,

View file

@ -36,20 +36,18 @@ pub(super) struct ItemLowerer<'a, 'hir> {
/// clause if it exists.
fn add_ty_alias_where_clause(
generics: &mut ast::Generics,
mut where_clauses: TyAliasWhereClauses,
after_where_clause: &ast::WhereClause,
prefer_first: bool,
) {
generics.where_clause.predicates.extend_from_slice(&after_where_clause.predicates);
let mut before = (generics.where_clause.has_where_token, generics.where_clause.span);
let mut after = (after_where_clause.has_where_token, after_where_clause.span);
if !prefer_first {
(where_clauses.before, where_clauses.after) = (where_clauses.after, where_clauses.before);
(before, after) = (after, before);
}
let where_clause =
if where_clauses.before.has_where_token || !where_clauses.after.has_where_token {
where_clauses.before
} else {
where_clauses.after
};
generics.where_clause.has_where_token = where_clause.has_where_token;
generics.where_clause.span = where_clause.span;
(generics.where_clause.has_where_token, generics.where_clause.span) =
if before.0 || !after.0 { before } else { after };
}
impl<'a, 'hir> ItemLowerer<'a, 'hir> {
@ -271,7 +269,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
self.lower_body(|this| (&[], this.expr(span, hir::ExprKind::InlineAsm(asm))));
hir::ItemKind::GlobalAsm { asm, fake_body }
}
ItemKind::TyAlias(box TyAlias { ident, generics, where_clauses, ty, .. }) => {
ItemKind::TyAlias(box TyAlias { ident, generics, after_where_clause, ty, .. }) => {
// We lower
//
// type Foo = impl Trait
@ -282,7 +280,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
// opaque type Foo1: Trait
let ident = self.lower_ident(*ident);
let mut generics = generics.clone();
add_ty_alias_where_clause(&mut generics, *where_clauses, true);
add_ty_alias_where_clause(&mut generics, after_where_clause, true);
let (generics, ty) = self.lower_generics(
&generics,
id,
@ -901,10 +899,15 @@ impl<'hir> LoweringContext<'_, 'hir> {
)
}
AssocItemKind::Type(box TyAlias {
ident, generics, where_clauses, bounds, ty, ..
ident,
generics,
after_where_clause,
bounds,
ty,
..
}) => {
let mut generics = generics.clone();
add_ty_alias_where_clause(&mut generics, *where_clauses, false);
add_ty_alias_where_clause(&mut generics, after_where_clause, false);
let (generics, kind) = self.lower_generics(
&generics,
i.id,
@ -1070,9 +1073,11 @@ impl<'hir> LoweringContext<'_, 'hir> {
(*ident, (generics, hir::ImplItemKind::Fn(sig, body_id)))
}
AssocItemKind::Type(box TyAlias { ident, generics, where_clauses, ty, .. }) => {
AssocItemKind::Type(box TyAlias {
ident, generics, after_where_clause, ty, ..
}) => {
let mut generics = generics.clone();
add_ty_alias_where_clause(&mut generics, *where_clauses, false);
add_ty_alias_where_clause(&mut generics, after_where_clause, false);
(
*ident,
self.lower_generics(

View file

@ -145,25 +145,24 @@ impl<'a> AstValidator<'a> {
&mut self,
ty_alias: &TyAlias,
) -> Result<(), errors::WhereClauseBeforeTypeAlias> {
if ty_alias.ty.is_none() || !ty_alias.where_clauses.before.has_where_token {
if ty_alias.ty.is_none() || !ty_alias.generics.where_clause.has_where_token {
return Ok(());
}
let (before_predicates, after_predicates) =
ty_alias.generics.where_clause.predicates.split_at(ty_alias.where_clauses.split);
let span = ty_alias.where_clauses.before.span;
let span = ty_alias.generics.where_clause.span;
let sugg = if !before_predicates.is_empty() || !ty_alias.where_clauses.after.has_where_token
let sugg = if !ty_alias.generics.where_clause.predicates.is_empty()
|| !ty_alias.after_where_clause.has_where_token
{
let mut state = State::new();
if !ty_alias.where_clauses.after.has_where_token {
if !ty_alias.after_where_clause.has_where_token {
state.space();
state.word_space("where");
}
let mut first = after_predicates.is_empty();
for p in before_predicates {
let mut first = ty_alias.after_where_clause.predicates.is_empty();
for p in &ty_alias.generics.where_clause.predicates {
if !first {
state.word_space(",");
}
@ -174,7 +173,7 @@ impl<'a> AstValidator<'a> {
errors::WhereClauseBeforeTypeAliasSugg::Move {
left: span,
snippet: state.s.eof(),
right: ty_alias.where_clauses.after.span.shrink_to_hi(),
right: ty_alias.after_where_clause.span.shrink_to_hi(),
}
} else {
errors::WhereClauseBeforeTypeAliasSugg::Remove { span }
@ -566,11 +565,7 @@ impl<'a> AstValidator<'a> {
self.dcx().emit_err(errors::BoundInContext { span, ctx });
}
fn check_foreign_ty_genericless(
&self,
generics: &Generics,
where_clauses: &TyAliasWhereClauses,
) {
fn check_foreign_ty_genericless(&self, generics: &Generics, after_where_clause: &WhereClause) {
let cannot_have = |span, descr, remove_descr| {
self.dcx().emit_err(errors::ExternTypesCannotHave {
span,
@ -584,14 +579,14 @@ impl<'a> AstValidator<'a> {
cannot_have(generics.span, "generic parameters", "generic parameters");
}
let check_where_clause = |where_clause: TyAliasWhereClause| {
let check_where_clause = |where_clause: &WhereClause| {
if where_clause.has_where_token {
cannot_have(where_clause.span, "`where` clauses", "`where` clause");
}
};
check_where_clause(where_clauses.before);
check_where_clause(where_clauses.after);
check_where_clause(&generics.where_clause);
check_where_clause(&after_where_clause);
}
fn check_foreign_kind_bodyless(&self, ident: Ident, kind: &str, body_span: Option<Span>) {
@ -1261,7 +1256,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
visit::walk_item(self, item);
}
ItemKind::TyAlias(
ty_alias @ box TyAlias { defaultness, bounds, where_clauses, ty, .. },
ty_alias @ box TyAlias { defaultness, bounds, after_where_clause, ty, .. },
) => {
self.check_defaultness(item.span, *defaultness);
if ty.is_none() {
@ -1276,9 +1271,9 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
if let Err(err) = self.check_type_alias_where_clause_location(ty_alias) {
self.dcx().emit_err(err);
}
} else if where_clauses.after.has_where_token {
} else if after_where_clause.has_where_token {
self.dcx().emit_err(errors::WhereClauseAfterTypeAlias {
span: where_clauses.after.span,
span: after_where_clause.span,
help: self.sess.is_nightly_build(),
});
}
@ -1308,7 +1303,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
defaultness,
ident,
generics,
where_clauses,
after_where_clause,
bounds,
ty,
..
@ -1316,7 +1311,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
self.check_defaultness(fi.span, *defaultness);
self.check_foreign_kind_bodyless(*ident, "type", ty.as_ref().map(|b| b.span));
self.check_type_no_bounds(bounds, "`extern` blocks");
self.check_foreign_ty_genericless(generics, where_clauses);
self.check_foreign_ty_genericless(generics, after_where_clause);
self.check_foreign_item_ascii_only(*ident);
}
ForeignItemKind::Static(box StaticItem { ident, safety, expr, .. }) => {

View file

@ -59,14 +59,14 @@ impl<'a> State<'a> {
defaultness,
ident,
generics,
where_clauses,
after_where_clause,
bounds,
ty,
}) => {
self.print_associated_type(
*ident,
generics,
*where_clauses,
after_where_clause,
bounds,
ty.as_deref(),
vis,
@ -127,14 +127,12 @@ impl<'a> State<'a> {
&mut self,
ident: Ident,
generics: &ast::Generics,
where_clauses: ast::TyAliasWhereClauses,
after_where_clause: &ast::WhereClause,
bounds: &ast::GenericBounds,
ty: Option<&ast::Ty>,
vis: &ast::Visibility,
defaultness: ast::Defaultness,
) {
let (before_predicates, after_predicates) =
generics.where_clause.predicates.split_at(where_clauses.split);
let (cb, ib) = self.head("");
self.print_visibility(vis);
self.print_defaultness(defaultness);
@ -145,13 +143,13 @@ impl<'a> State<'a> {
self.word_nbsp(":");
self.print_type_bounds(bounds);
}
self.print_where_clause_parts(where_clauses.before.has_where_token, before_predicates);
self.print_where_clause(&generics.where_clause);
if let Some(ty) = ty {
self.space();
self.word_space("=");
self.print_type(ty);
}
self.print_where_clause_parts(where_clauses.after.has_where_token, after_predicates);
self.print_where_clause(&after_where_clause);
self.word(";");
self.end(ib);
self.end(cb);
@ -283,14 +281,14 @@ impl<'a> State<'a> {
defaultness,
ident,
generics,
where_clauses,
after_where_clause,
bounds,
ty,
}) => {
self.print_associated_type(
*ident,
generics,
*where_clauses,
after_where_clause,
bounds,
ty.as_deref(),
&item.vis,
@ -585,14 +583,14 @@ impl<'a> State<'a> {
defaultness,
ident,
generics,
where_clauses,
after_where_clause,
bounds,
ty,
}) => {
self.print_associated_type(
*ident,
generics,
*where_clauses,
after_where_clause,
bounds,
ty.as_deref(),
vis,
@ -759,14 +757,7 @@ impl<'a> State<'a> {
}
fn print_where_clause(&mut self, where_clause: &ast::WhereClause) {
self.print_where_clause_parts(where_clause.has_where_token, &where_clause.predicates);
}
fn print_where_clause_parts(
&mut self,
has_where_token: bool,
predicates: &[ast::WherePredicate],
) {
let ast::WhereClause { has_where_token, ref predicates, span: _ } = *where_clause;
if predicates.is_empty() && !has_where_token {
return;
}

View file

@ -1046,16 +1046,6 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
}
}
&Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf, ty) => {
let trait_ref =
ty::TraitRef::new(tcx, tcx.require_lang_item(LangItem::Sized, span), [ty]);
self.prove_trait_ref(
trait_ref,
location.to_locations(),
ConstraintCategory::SizedBound,
);
}
&Rvalue::NullaryOp(NullOp::ContractChecks, _) => {}
&Rvalue::NullaryOp(NullOp::UbChecks, _) => {}

View file

@ -610,7 +610,7 @@ impl<'a> TraitDef<'a> {
defaultness: ast::Defaultness::Final,
ident,
generics: Generics::default(),
where_clauses: ast::TyAliasWhereClauses::default(),
after_where_clause: ast::WhereClause::default(),
bounds: Vec::new(),
ty: Some(type_def.to_ty(cx, self.span, type_ident, generics)),
})),

View file

@ -72,10 +72,6 @@ pub fn debug_tuple() -> DebugTuple {
DebugTuple(())
}
pub fn size_of<T>() -> usize {
intrinsics::size_of::<T>()
}
pub fn use_size_of() -> usize {
size_of::<u64>()
}

View file

@ -6,6 +6,7 @@
extern_types,
decl_macro,
rustc_attrs,
rustc_private,
transparent_unions,
auto_traits,
freeze_impls,
@ -594,7 +595,7 @@ impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<Box<U>> for Box<T> {}
impl<T> Box<T> {
pub fn new(val: T) -> Box<T> {
unsafe {
let size = intrinsics::size_of::<T>();
let size = size_of::<T>();
let ptr = libc::malloc(size);
intrinsics::copy(&val as *const T as *const u8, ptr, size);
Box(Unique { pointer: NonNull(ptr as *const T), _marker: PhantomData }, Global)
@ -646,11 +647,11 @@ pub mod intrinsics {
#[rustc_intrinsic]
pub fn abort() -> !;
#[rustc_intrinsic]
pub fn size_of<T>() -> usize;
pub const fn size_of<T>() -> usize;
#[rustc_intrinsic]
pub unsafe fn size_of_val<T: ?::Sized>(val: *const T) -> usize;
#[rustc_intrinsic]
pub fn align_of<T>() -> usize;
pub const fn align_of<T>() -> usize;
#[rustc_intrinsic]
pub unsafe fn align_of_val<T: ?::Sized>(val: *const T) -> usize;
#[rustc_intrinsic]
@ -715,6 +716,23 @@ impl<T> Index<usize> for [T] {
}
}
pub const fn size_of<T>() -> usize {
<T as SizedTypeProperties>::SIZE
}
pub const fn align_of<T>() -> usize {
<T as SizedTypeProperties>::ALIGN
}
trait SizedTypeProperties: Sized {
#[lang = "mem_size_const"]
const SIZE: usize = intrinsics::size_of::<Self>();
#[lang = "mem_align_const"]
const ALIGN: usize = intrinsics::align_of::<Self>();
}
impl<T> SizedTypeProperties for T {}
extern "C" {
type VaListImpl;
}

View file

@ -109,10 +109,10 @@ fn start<T: Termination + 'static>(
puts(*argv as *const i8);
}
unsafe {
puts(*((argv as usize + intrinsics::size_of::<*const u8>()) as *const *const i8));
puts(*((argv as usize + size_of::<*const u8>()) as *const *const i8));
}
unsafe {
puts(*((argv as usize + 2 * intrinsics::size_of::<*const u8>()) as *const *const i8));
puts(*((argv as usize + 2 * size_of::<*const u8>()) as *const *const i8));
}
}
@ -213,8 +213,8 @@ fn main() {
assert_eq!(intrinsics::size_of_val(a) as u8, 16);
assert_eq!(intrinsics::size_of_val(&0u32) as u8, 4);
assert_eq!(intrinsics::align_of::<u16>() as u8, 2);
assert_eq!(intrinsics::align_of_val(&a) as u8, intrinsics::align_of::<&str>() as u8);
assert_eq!(align_of::<u16>() as u8, 2);
assert_eq!(intrinsics::align_of_val(&a) as u8, align_of::<&str>() as u8);
let u8_needs_drop = const { intrinsics::needs_drop::<u8>() };
assert!(!u8_needs_drop);

View file

@ -833,8 +833,6 @@ fn codegen_stmt<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, cur_block: Block, stmt:
assert!(lval.layout().ty.is_sized(fx.tcx, fx.typing_env()));
let layout = fx.layout_of(fx.monomorphize(ty));
let val = match null_op {
NullOp::SizeOf => layout.size.bytes(),
NullOp::AlignOf => layout.align.bytes(),
NullOp::OffsetOf(fields) => fx
.tcx
.offset_of_subfield(

View file

@ -1325,6 +1325,8 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
};
}
let llvm_version = crate::llvm_util::get_version();
/// Converts a vector mask, where each element has a bit width equal to the data elements it is used with,
/// down to an i1 based mask that can be used by llvm intrinsics.
///
@ -1808,7 +1810,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
);
// Alignment of T, must be a constant integer value:
let alignment = bx.const_i32(bx.align_of(in_elem).bytes() as i32);
let alignment = bx.align_of(in_elem).bytes();
// Truncate the mask vector to a vector of i1s:
let mask = vector_mask_to_bitmask(bx, args[2].immediate(), mask_elem_bitwidth, in_len);
@ -1819,11 +1821,23 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
// Type of the vector of elements:
let llvm_elem_vec_ty = llvm_vector_ty(bx, element_ty0, in_len);
return Ok(bx.call_intrinsic(
"llvm.masked.gather",
&[llvm_elem_vec_ty, llvm_pointer_vec_ty],
&[args[1].immediate(), alignment, mask, args[0].immediate()],
));
let args: &[&'ll Value] = if llvm_version < (22, 0, 0) {
let alignment = bx.const_i32(alignment as i32);
&[args[1].immediate(), alignment, mask, args[0].immediate()]
} else {
&[args[1].immediate(), mask, args[0].immediate()]
};
let call =
bx.call_intrinsic("llvm.masked.gather", &[llvm_elem_vec_ty, llvm_pointer_vec_ty], args);
if llvm_version >= (22, 0, 0) {
crate::attributes::apply_to_callsite(
call,
crate::llvm::AttributePlace::Argument(0),
&[crate::llvm::CreateAlignmentAttr(bx.llcx, alignment)],
)
}
return Ok(call);
}
if name == sym::simd_masked_load {
@ -1891,18 +1905,30 @@ 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.const_i32(bx.align_of(values_elem).bytes() as i32);
let alignment = bx.align_of(values_elem).bytes();
let llvm_pointer = bx.type_ptr();
// Type of the vector of elements:
let llvm_elem_vec_ty = llvm_vector_ty(bx, values_elem, values_len);
return Ok(bx.call_intrinsic(
"llvm.masked.load",
&[llvm_elem_vec_ty, llvm_pointer],
&[args[1].immediate(), alignment, mask, args[2].immediate()],
));
let args: &[&'ll Value] = if llvm_version < (22, 0, 0) {
let alignment = bx.const_i32(alignment as i32);
&[args[1].immediate(), alignment, mask, args[2].immediate()]
} else {
&[args[1].immediate(), mask, args[2].immediate()]
};
let call = bx.call_intrinsic("llvm.masked.load", &[llvm_elem_vec_ty, llvm_pointer], args);
if llvm_version >= (22, 0, 0) {
crate::attributes::apply_to_callsite(
call,
crate::llvm::AttributePlace::Argument(0),
&[crate::llvm::CreateAlignmentAttr(bx.llcx, alignment)],
)
}
return Ok(call);
}
if name == sym::simd_masked_store {
@ -1964,18 +1990,29 @@ 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.const_i32(bx.align_of(values_elem).bytes() as i32);
let alignment = bx.align_of(values_elem).bytes();
let llvm_pointer = bx.type_ptr();
// Type of the vector of elements:
let llvm_elem_vec_ty = llvm_vector_ty(bx, values_elem, values_len);
return Ok(bx.call_intrinsic(
"llvm.masked.store",
&[llvm_elem_vec_ty, llvm_pointer],
&[args[2].immediate(), args[1].immediate(), alignment, mask],
));
let args: &[&'ll Value] = if llvm_version < (22, 0, 0) {
let alignment = bx.const_i32(alignment as i32);
&[args[2].immediate(), args[1].immediate(), alignment, mask]
} else {
&[args[2].immediate(), args[1].immediate(), mask]
};
let call = bx.call_intrinsic("llvm.masked.store", &[llvm_elem_vec_ty, llvm_pointer], args);
if llvm_version >= (22, 0, 0) {
crate::attributes::apply_to_callsite(
call,
crate::llvm::AttributePlace::Argument(1),
&[crate::llvm::CreateAlignmentAttr(bx.llcx, alignment)],
)
}
return Ok(call);
}
if name == sym::simd_scatter {
@ -2040,7 +2077,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
);
// Alignment of T, must be a constant integer value:
let alignment = bx.const_i32(bx.align_of(in_elem).bytes() as i32);
let alignment = bx.align_of(in_elem).bytes();
// Truncate the mask vector to a vector of i1s:
let mask = vector_mask_to_bitmask(bx, args[2].immediate(), mask_elem_bitwidth, in_len);
@ -2050,12 +2087,25 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
// Type of the vector of elements:
let llvm_elem_vec_ty = llvm_vector_ty(bx, element_ty0, in_len);
return Ok(bx.call_intrinsic(
let args: &[&'ll Value] = if llvm_version < (22, 0, 0) {
let alignment = bx.const_i32(alignment as i32);
&[args[0].immediate(), args[1].immediate(), alignment, mask]
} else {
&[args[0].immediate(), args[1].immediate(), mask]
};
let call = bx.call_intrinsic(
"llvm.masked.scatter",
&[llvm_elem_vec_ty, llvm_pointer_vec_ty],
&[args[0].immediate(), args[1].immediate(), alignment, mask],
));
args,
);
if llvm_version >= (22, 0, 0) {
crate::attributes::apply_to_callsite(
call,
crate::llvm::AttributePlace::Argument(1),
&[crate::llvm::CreateAlignmentAttr(bx.llcx, alignment)],
)
}
return Ok(call);
}
macro_rules! arith_red {

View file

@ -611,16 +611,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
let ty = self.monomorphize(ty);
let layout = bx.cx().layout_of(ty);
let val = match null_op {
mir::NullOp::SizeOf => {
assert!(bx.cx().type_is_sized(ty));
let val = layout.size.bytes();
bx.cx().const_usize(val)
}
mir::NullOp::AlignOf => {
assert!(bx.cx().type_is_sized(ty));
let val = layout.align.bytes();
bx.cx().const_usize(val)
}
mir::NullOp::OffsetOf(fields) => {
let val = bx
.tcx()

View file

@ -646,11 +646,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
Rvalue::Cast(_, _, _) => {}
Rvalue::NullaryOp(
NullOp::SizeOf
| NullOp::AlignOf
| NullOp::OffsetOf(_)
| NullOp::UbChecks
| NullOp::ContractChecks,
NullOp::OffsetOf(_) | NullOp::UbChecks | NullOp::ContractChecks,
_,
) => {}
Rvalue::ShallowInitBox(_, _) => {}

View file

@ -414,8 +414,6 @@ fn report_eval_error<'tcx>(
let (error, backtrace) = error.into_parts();
backtrace.print_backtrace();
let instance = with_no_trimmed_paths!(cid.instance.to_string());
super::report(
ecx,
error,
@ -430,7 +428,7 @@ fn report_eval_error<'tcx>(
diag.subdiagnostic(frame);
}
// Add after the frame rendering above, as it adds its own `instance` args.
diag.arg("instance", instance);
diag.arg("instance", with_no_trimmed_paths!(cid.instance.to_string()));
diag.arg("num_frames", num_frames);
},
)

View file

@ -156,6 +156,24 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
let b_ty = self.read_type_id(&args[1])?;
self.write_scalar(Scalar::from_bool(a_ty == b_ty), dest)?;
}
sym::size_of => {
let tp_ty = instance.args.type_at(0);
let layout = self.layout_of(tp_ty)?;
if !layout.is_sized() {
span_bug!(self.cur_span(), "unsized type for `size_of`");
}
let val = layout.size.bytes();
self.write_scalar(Scalar::from_target_usize(val, self), dest)?;
}
sym::align_of => {
let tp_ty = instance.args.type_at(0);
let layout = self.layout_of(tp_ty)?;
if !layout.is_sized() {
span_bug!(self.cur_span(), "unsized type for `align_of`");
}
let val = layout.align.bytes();
self.write_scalar(Scalar::from_target_usize(val, self), dest)?;
}
sym::variant_count => {
let tp_ty = instance.args.type_at(0);
let ty = match tp_ty.kind() {

View file

@ -517,20 +517,6 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
let usize_layout = || self.layout_of(self.tcx.types.usize).unwrap();
interp_ok(match null_op {
SizeOf => {
if !layout.is_sized() {
span_bug!(self.cur_span(), "unsized type for `NullaryOp::SizeOf`");
}
let val = layout.size.bytes();
ImmTy::from_uint(val, usize_layout())
}
AlignOf => {
if !layout.is_sized() {
span_bug!(self.cur_span(), "unsized type for `NullaryOp::AlignOf`");
}
let val = layout.align.bytes();
ImmTy::from_uint(val, usize_layout())
}
OffsetOf(fields) => {
let val =
self.tcx.offset_of_subfield(self.typing_env, layout, fields.iter()).bytes();

View file

@ -945,6 +945,11 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> {
None,
"Span must not be empty and have no suggestion",
);
debug_assert_eq!(
parts.array_windows().find(|[a, b]| a.span.overlaps(b.span)),
None,
"suggestion must not have overlapping parts",
);
self.push_suggestion(CodeSuggestion {
substitutions: vec![Substitution { parts }],

View file

@ -2350,6 +2350,7 @@ impl HumanEmitter {
.sum();
let underline_start = (span_start_pos + start) as isize + offset;
let underline_end = (span_start_pos + start + sub_len) as isize + offset;
assert!(underline_start >= 0 && underline_end >= 0);
let padding: usize = max_line_num_len + 3;
for p in underline_start..underline_end {
if let DisplaySuggestion::Underline = show_code_change

View file

@ -400,17 +400,6 @@ impl CodeSuggestion {
// Assumption: all spans are in the same file, and all spans
// are disjoint. Sort in ascending order.
substitution.parts.sort_by_key(|part| part.span.lo());
// Verify the assumption that all spans are disjoint
assert_eq!(
substitution.parts.array_windows().find(|[a, b]| a.span.overlaps(b.span)),
None,
"all spans must be disjoint",
);
// Account for cases where we are suggesting the same code that's already
// there. This shouldn't happen often, but in some cases for multipart
// suggestions it's much easier to handle it here than in the origin.
substitution.parts.retain(|p| is_different(sm, &p.snippet, p.span));
// Find the bounding span.
let lo = substitution.parts.iter().map(|part| part.span.lo()).min()?;
@ -505,12 +494,16 @@ impl CodeSuggestion {
_ => 1,
})
.sum();
line_highlight.push(SubstitutionHighlight {
start: (cur_lo.col.0 as isize + acc) as usize,
end: (cur_lo.col.0 as isize + acc + len) as usize,
});
if !is_different(sm, &part.snippet, part.span) {
// Account for cases where we are suggesting the same code that's already
// there. This shouldn't happen often, but in some cases for multipart
// suggestions it's much easier to handle it here than in the origin.
} else {
line_highlight.push(SubstitutionHighlight {
start: (cur_lo.col.0 as isize + acc) as usize,
end: (cur_lo.col.0 as isize + acc + len) as usize,
});
}
buf.push_str(&part.snippet);
let cur_hi = sm.lookup_char_pos(part.span.hi());
// Account for the difference between the width of the current code and the

View file

@ -168,6 +168,8 @@ language_item_table! {
MetaSized, sym::meta_sized, meta_sized_trait, Target::Trait, GenericRequirement::Exact(0);
PointeeSized, sym::pointee_sized, pointee_sized_trait, Target::Trait, GenericRequirement::Exact(0);
Unsize, sym::unsize, unsize_trait, Target::Trait, GenericRequirement::Minimum(1);
AlignOf, sym::mem_align_const, align_const, Target::AssocConst, GenericRequirement::Exact(0);
SizeOf, sym::mem_size_const, size_const, Target::AssocConst, GenericRequirement::Exact(0);
/// Trait injected by `#[derive(PartialEq)]`, (i.e. "Partial EQ").
StructuralPeq, sym::structural_peq, structural_peq_trait, Target::Trait, GenericRequirement::None;
Copy, sym::copy, copy_trait, Target::Trait, GenericRequirement::Exact(0);

View file

@ -1092,8 +1092,6 @@ impl<'tcx> Debug for Rvalue<'tcx> {
NullaryOp(ref op, ref t) => {
let t = with_no_trimmed_paths!(format!("{}", t));
match op {
NullOp::SizeOf => write!(fmt, "SizeOf({t})"),
NullOp::AlignOf => write!(fmt, "AlignOf({t})"),
NullOp::OffsetOf(fields) => write!(fmt, "OffsetOf({t}, {fields:?})"),
NullOp::UbChecks => write!(fmt, "UbChecks()"),
NullOp::ContractChecks => write!(fmt, "ContractChecks()"),

View file

@ -597,6 +597,18 @@ impl<'tcx> Operand<'tcx> {
}))
}
/// Convenience helper to make a constant that refers to the given `DefId` and args. Since this
/// is used to synthesize MIR, assumes `user_ty` is None.
pub fn unevaluated_constant(
tcx: TyCtxt<'tcx>,
def_id: DefId,
args: &[GenericArg<'tcx>],
span: Span,
) -> Self {
let const_ = Const::from_unevaluated(tcx, def_id).instantiate(tcx, args);
Operand::Constant(Box::new(ConstOperand { span, user_ty: None, const_ }))
}
pub fn is_move(&self) -> bool {
matches!(self, Operand::Move(..))
}
@ -782,9 +794,7 @@ impl<'tcx> Rvalue<'tcx> {
op.ty(tcx, arg_ty)
}
Rvalue::Discriminant(ref place) => place.ty(local_decls, tcx).ty.discriminant_ty(tcx),
Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..), _) => {
tcx.types.usize
}
Rvalue::NullaryOp(NullOp::OffsetOf(..), _) => tcx.types.usize,
Rvalue::NullaryOp(NullOp::ContractChecks, _)
| Rvalue::NullaryOp(NullOp::UbChecks, _) => tcx.types.bool,
Rvalue::Aggregate(ref ak, ref ops) => match **ak {
@ -853,7 +863,7 @@ impl BorrowKind {
impl<'tcx> NullOp<'tcx> {
pub fn ty(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
match self {
NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(_) => tcx.types.usize,
NullOp::OffsetOf(_) => tcx.types.usize,
NullOp::UbChecks | NullOp::ContractChecks => tcx.types.bool,
}
}

View file

@ -1563,10 +1563,6 @@ pub enum AggregateKind<'tcx> {
#[derive(Copy, Clone, Debug, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable)]
pub enum NullOp<'tcx> {
/// Returns the size of a value of that type
SizeOf,
/// Returns the minimum alignment of a type
AlignOf,
/// Returns the offset of a field
OffsetOf(&'tcx List<(VariantIdx, FieldIdx)>),
/// Returns whether we should perform some UB-checking at runtime.

View file

@ -501,6 +501,8 @@ pub use helper::*;
mod helper {
use super::*;
// Note: the methods below use a `slice.chain(Option).chain(Option)` pattern so that all paths
// produce an iterator with the same concrete type.
pub type Successors<'a> = impl DoubleEndedIterator<Item = BasicBlock> + 'a;
impl SwitchTargets {
@ -510,7 +512,7 @@ mod helper {
#[define_opaque(Successors)]
pub fn successors_for_value(&self, value: u128) -> Successors<'_> {
let target = self.target_for_value(value);
(&[]).into_iter().copied().chain(Some(target).into_iter().chain(None))
(&[]).into_iter().copied().chain(Some(target)).chain(None)
}
}
@ -522,10 +524,7 @@ mod helper {
match *self {
// 3-successors for async drop: target, unwind, dropline (parent coroutine drop)
Drop { target: ref t, unwind: UnwindAction::Cleanup(u), drop: Some(d), .. } => {
slice::from_ref(t)
.into_iter()
.copied()
.chain(Some(u).into_iter().chain(Some(d)))
slice::from_ref(t).into_iter().copied().chain(Some(u)).chain(Some(d))
}
// 2-successors
Call { target: Some(ref t), unwind: UnwindAction::Cleanup(u), .. }
@ -534,7 +533,7 @@ mod helper {
| Drop { target: ref t, unwind: _, drop: Some(u), .. }
| Assert { target: ref t, unwind: UnwindAction::Cleanup(u), .. }
| FalseUnwind { real_target: ref t, unwind: UnwindAction::Cleanup(u) } => {
slice::from_ref(t).into_iter().copied().chain(Some(u).into_iter().chain(None))
slice::from_ref(t).into_iter().copied().chain(Some(u)).chain(None)
}
// single successor
Goto { target: ref t }
@ -544,7 +543,7 @@ mod helper {
| Drop { target: ref t, unwind: _, .. }
| Assert { target: ref t, unwind: _, .. }
| FalseUnwind { real_target: ref t, unwind: _ } => {
slice::from_ref(t).into_iter().copied().chain(None.into_iter().chain(None))
slice::from_ref(t).into_iter().copied().chain(None).chain(None)
}
// No successors
UnwindResume
@ -554,23 +553,24 @@ mod helper {
| Unreachable
| TailCall { .. }
| Call { target: None, unwind: _, .. } => {
(&[]).into_iter().copied().chain(None.into_iter().chain(None))
(&[]).into_iter().copied().chain(None).chain(None)
}
// Multiple successors
InlineAsm { ref targets, unwind: UnwindAction::Cleanup(u), .. } => {
targets.iter().copied().chain(Some(u).into_iter().chain(None))
targets.iter().copied().chain(Some(u)).chain(None)
}
InlineAsm { ref targets, unwind: _, .. } => {
targets.iter().copied().chain(None.into_iter().chain(None))
targets.iter().copied().chain(None).chain(None)
}
SwitchInt { ref targets, .. } => {
targets.targets.iter().copied().chain(None.into_iter().chain(None))
targets.targets.iter().copied().chain(None).chain(None)
}
// FalseEdge
FalseEdge { ref real_target, imaginary_target } => slice::from_ref(real_target)
.into_iter()
.copied()
.chain(Some(imaginary_target).into_iter().chain(None)),
.chain(Some(imaginary_target))
.chain(None),
}
}

View file

@ -126,21 +126,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let tcx = this.tcx;
let source_info = this.source_info(expr_span);
let size = this.temp(tcx.types.usize, expr_span);
this.cfg.push_assign(
block,
source_info,
size,
Rvalue::NullaryOp(NullOp::SizeOf, value_ty),
);
let size = tcx.require_lang_item(LangItem::SizeOf, expr_span);
let size = Operand::unevaluated_constant(tcx, size, &[value_ty.into()], expr_span);
let align = this.temp(tcx.types.usize, expr_span);
this.cfg.push_assign(
block,
source_info,
align,
Rvalue::NullaryOp(NullOp::AlignOf, value_ty),
);
let align = tcx.require_lang_item(LangItem::AlignOf, expr_span);
let align =
Operand::unevaluated_constant(tcx, align, &[value_ty.into()], expr_span);
// malloc some memory of suitable size and align:
let exchange_malloc = Operand::function_handle(
@ -157,8 +148,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
TerminatorKind::Call {
func: exchange_malloc,
args: [
Spanned { node: Operand::Move(size), span: DUMMY_SP },
Spanned { node: Operand::Move(align), span: DUMMY_SP },
Spanned { node: size, span: DUMMY_SP },
Spanned { node: align, span: DUMMY_SP },
]
.into(),
destination: storage,

View file

@ -452,11 +452,7 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> {
| Rvalue::RawPtr(..)
| Rvalue::Discriminant(..)
| Rvalue::NullaryOp(
NullOp::SizeOf
| NullOp::AlignOf
| NullOp::OffsetOf(..)
| NullOp::UbChecks
| NullOp::ContractChecks,
NullOp::OffsetOf(..) | NullOp::UbChecks | NullOp::ContractChecks,
_,
) => {}
}

View file

@ -1,4 +1,5 @@
use rustc_abi::Align;
use rustc_hir::LangItem;
use rustc_index::IndexVec;
use rustc_middle::mir::interpret::Scalar;
use rustc_middle::mir::visit::PlaceContext;
@ -59,10 +60,9 @@ fn insert_alignment_check<'tcx>(
stmts.push(Statement::new(source_info, StatementKind::Assign(Box::new((addr, rvalue)))));
// Get the alignment of the pointee
let align_def_id = tcx.require_lang_item(LangItem::AlignOf, source_info.span);
let alignment =
local_decls.push(LocalDecl::with_source_info(tcx.types.usize, source_info)).into();
let rvalue = Rvalue::NullaryOp(NullOp::AlignOf, pointee_ty);
stmts.push(Statement::new(source_info, StatementKind::Assign(Box::new((alignment, rvalue)))));
Operand::unevaluated_constant(tcx, align_def_id, &[pointee_ty.into()], source_info.span);
// Subtract 1 from the alignment to get the alignment mask
let alignment_mask =
@ -76,7 +76,7 @@ fn insert_alignment_check<'tcx>(
source_info,
StatementKind::Assign(Box::new((
alignment_mask,
Rvalue::BinaryOp(BinOp::Sub, Box::new((Operand::Copy(alignment), one))),
Rvalue::BinaryOp(BinOp::Sub, Box::new((alignment.clone(), one))),
))),
));
@ -141,7 +141,7 @@ fn insert_alignment_check<'tcx>(
PointerCheck {
cond: Operand::Copy(is_ok),
assert_kind: Box::new(AssertKind::MisalignedPointerDereference {
required: Operand::Copy(alignment),
required: alignment,
found: Operand::Copy(addr),
}),
}

View file

@ -1,3 +1,4 @@
use rustc_hir::LangItem;
use rustc_index::IndexVec;
use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext};
use rustc_middle::mir::*;
@ -62,35 +63,23 @@ fn insert_null_check<'tcx>(
Operand::Constant(Box::new(ConstOperand {
span: source_info.span,
user_ty: None,
const_: Const::Val(ConstValue::from_bool(true), tcx.types.bool),
const_: Const::from_bool(tcx, true),
}))
}
// Other usages of null pointers only are UB if the pointee is not a ZST.
_ => {
let rvalue = Rvalue::NullaryOp(NullOp::SizeOf, pointee_ty);
let sizeof_pointee =
local_decls.push(LocalDecl::with_source_info(tcx.types.usize, source_info)).into();
stmts.push(Statement::new(
source_info,
StatementKind::Assign(Box::new((sizeof_pointee, rvalue))),
));
let size_of = tcx.require_lang_item(LangItem::SizeOf, source_info.span);
let size_of =
Operand::unevaluated_constant(tcx, size_of, &[pointee_ty.into()], source_info.span);
// Check that the pointee is not a ZST.
let is_pointee_not_zst =
let pointee_should_be_checked =
local_decls.push(LocalDecl::with_source_info(tcx.types.bool, source_info)).into();
let rvalue = Rvalue::BinaryOp(BinOp::Ne, Box::new((size_of, zero.clone())));
stmts.push(Statement::new(
source_info,
StatementKind::Assign(Box::new((
is_pointee_not_zst,
Rvalue::BinaryOp(
BinOp::Ne,
Box::new((Operand::Copy(sizeof_pointee), zero.clone())),
),
))),
StatementKind::Assign(Box::new((pointee_should_be_checked, rvalue))),
));
// Pointer needs to be checked only if pointee is not a ZST.
Operand::Copy(is_pointee_not_zst)
Operand::Copy(pointee_should_be_checked.into())
}
};

View file

@ -466,8 +466,6 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> {
return ValueOrPlace::Value(FlatSet::Top);
};
let val = match null_op {
NullOp::SizeOf if layout.is_sized() => layout.size.bytes(),
NullOp::AlignOf if layout.is_sized() => layout.align.bytes(),
NullOp::OffsetOf(fields) => self
.ecx
.tcx

View file

@ -670,14 +670,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
}
NullaryOp(null_op, arg_ty) => {
let arg_layout = self.ecx.layout_of(arg_ty).ok()?;
if let NullOp::SizeOf | NullOp::AlignOf = null_op
&& arg_layout.is_unsized()
{
return None;
}
let val = match null_op {
NullOp::SizeOf => arg_layout.size.bytes(),
NullOp::AlignOf => arg_layout.align.bytes(),
NullOp::OffsetOf(fields) => self
.tcx
.offset_of_subfield(self.typing_env(), arg_layout, fields.iter())

View file

@ -264,6 +264,7 @@ impl<'tcx> InstSimplifyContext<'_, 'tcx> {
terminator: &mut Terminator<'tcx>,
statements: &mut Vec<Statement<'tcx>>,
) {
let source_info = terminator.source_info;
if let TerminatorKind::Call {
func, args, destination, target: Some(destination_block), ..
} = &terminator.kind
@ -272,12 +273,16 @@ impl<'tcx> InstSimplifyContext<'_, 'tcx> {
&& self.tcx.is_intrinsic(fn_def_id, sym::align_of_val)
&& let ty::Slice(elem_ty) = *generics.type_at(0).kind()
{
let align_def_id = self.tcx.require_lang_item(LangItem::AlignOf, source_info.span);
let align_const = Operand::unevaluated_constant(
self.tcx,
align_def_id,
&[elem_ty.into()],
source_info.span,
);
statements.push(Statement::new(
terminator.source_info,
StatementKind::Assign(Box::new((
*destination,
Rvalue::NullaryOp(NullOp::AlignOf, elem_ty),
))),
source_info,
StatementKind::Assign(Box::new((*destination, Rvalue::Use(align_const)))),
));
terminator.kind = TerminatorKind::Goto { target: *destination_block };
}

View file

@ -608,8 +608,6 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
NullaryOp(ref null_op, ty) => {
let op_layout = self.ecx.layout_of(ty).ok()?;
let val = match null_op {
NullOp::SizeOf => op_layout.size.bytes(),
NullOp::AlignOf => op_layout.align.bytes(),
NullOp::OffsetOf(fields) => self
.tcx
.offset_of_subfield(self.typing_env, op_layout, fields.iter())

View file

@ -139,23 +139,6 @@ impl<'tcx> crate::MirPass<'tcx> for LowerIntrinsics {
));
terminator.kind = TerminatorKind::Goto { target };
}
sym::size_of | sym::align_of => {
let target = target.unwrap();
let tp_ty = generic_args.type_at(0);
let null_op = match intrinsic.name {
sym::size_of => NullOp::SizeOf,
sym::align_of => NullOp::AlignOf,
_ => bug!("unexpected intrinsic"),
};
block.statements.push(Statement::new(
terminator.source_info,
StatementKind::Assign(Box::new((
*destination,
Rvalue::NullaryOp(null_op, tp_ty),
))),
));
terminator.kind = TerminatorKind::Goto { target };
}
sym::read_via_copy => {
let Ok([arg]) = take_array(args) else {
span_bug!(terminator.source_info.span, "Wrong number of arguments");

View file

@ -450,8 +450,6 @@ impl<'tcx> Validator<'_, 'tcx> {
}
Rvalue::NullaryOp(op, _) => match op {
NullOp::SizeOf => {}
NullOp::AlignOf => {}
NullOp::OffsetOf(_) => {}
NullOp::UbChecks => {}
NullOp::ContractChecks => {}

View file

@ -1478,10 +1478,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
Rvalue::Repeat(_, _)
| Rvalue::ThreadLocalRef(_)
| Rvalue::RawPtr(_, _)
| Rvalue::NullaryOp(
NullOp::SizeOf | NullOp::AlignOf | NullOp::UbChecks | NullOp::ContractChecks,
_,
)
| Rvalue::NullaryOp(NullOp::UbChecks | NullOp::ContractChecks, _)
| Rvalue::Discriminant(_) => {}
Rvalue::WrapUnsafeBinder(op, ty) => {

View file

@ -1041,40 +1041,19 @@ impl<'a> Parser<'a> {
// Parse optional colon and param bounds.
let bounds = if self.eat(exp!(Colon)) { self.parse_generic_bounds()? } else { Vec::new() };
let before_where_clause = self.parse_where_clause()?;
generics.where_clause = self.parse_where_clause()?;
let ty = if self.eat(exp!(Eq)) { Some(self.parse_ty()?) } else { None };
let after_where_clause = self.parse_where_clause()?;
let where_clauses = TyAliasWhereClauses {
before: TyAliasWhereClause {
has_where_token: before_where_clause.has_where_token,
span: before_where_clause.span,
},
after: TyAliasWhereClause {
has_where_token: after_where_clause.has_where_token,
span: after_where_clause.span,
},
split: before_where_clause.predicates.len(),
};
let mut predicates = before_where_clause.predicates;
predicates.extend(after_where_clause.predicates);
let where_clause = WhereClause {
has_where_token: before_where_clause.has_where_token
|| after_where_clause.has_where_token,
predicates,
span: DUMMY_SP,
};
generics.where_clause = where_clause;
self.expect_semi()?;
Ok(ItemKind::TyAlias(Box::new(TyAlias {
defaultness,
ident,
generics,
where_clauses,
after_where_clause,
bounds,
ty,
})))

View file

@ -75,6 +75,9 @@ pub trait DefIdVisitor<'tcx> {
}
fn tcx(&self) -> TyCtxt<'tcx>;
/// NOTE: Def-id visiting should be idempotent (or at least produce duplicated errors),
/// because `DefIdVisitorSkeleton` will use caching and sometimes avoid visiting duplicate
/// def-ids. All the current visitors follow this rule.
fn visit_def_id(&mut self, def_id: DefId, kind: &str, descr: &dyn fmt::Display)
-> Self::Result;
@ -82,7 +85,7 @@ pub trait DefIdVisitor<'tcx> {
fn skeleton(&mut self) -> DefIdVisitorSkeleton<'_, 'tcx, Self> {
DefIdVisitorSkeleton {
def_id_visitor: self,
visited_opaque_tys: Default::default(),
visited_tys: Default::default(),
dummy: Default::default(),
}
}
@ -102,7 +105,7 @@ pub trait DefIdVisitor<'tcx> {
pub struct DefIdVisitorSkeleton<'v, 'tcx, V: ?Sized> {
def_id_visitor: &'v mut V,
visited_opaque_tys: FxHashSet<DefId>,
visited_tys: FxHashSet<Ty<'tcx>>,
dummy: PhantomData<TyCtxt<'tcx>>,
}
@ -183,7 +186,8 @@ where
let tcx = self.def_id_visitor.tcx();
// GenericArgs are not visited here because they are visited below
// in `super_visit_with`.
match *ty.kind() {
let ty_kind = *ty.kind();
match ty_kind {
ty::Adt(ty::AdtDef(Interned(&ty::AdtDefData { did: def_id, .. }, _)), ..)
| ty::Foreign(def_id)
| ty::FnDef(def_id, ..)
@ -197,7 +201,7 @@ where
// Default type visitor doesn't visit signatures of fn types.
// Something like `fn() -> Priv {my_func}` is considered a private type even if
// `my_func` is public, so we need to visit signatures.
if let ty::FnDef(..) = ty.kind() {
if let ty::FnDef(..) = ty_kind {
// FIXME: this should probably use `args` from `FnDef`
try_visit!(tcx.fn_sig(def_id).instantiate_identity().visit_with(self));
}
@ -220,6 +224,12 @@ where
// free type aliases, but this isn't done yet.
return V::Result::output();
}
if !self.visited_tys.insert(ty) {
// Avoid repeatedly visiting alias types (including projections).
// This helps with special cases like #145741, but doesn't introduce
// too much overhead in general case, unlike caching for other types.
return V::Result::output();
}
try_visit!(self.def_id_visitor.visit_def_id(
data.def_id,
@ -259,7 +269,7 @@ where
}
ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) => {
// Skip repeated `Opaque`s to avoid infinite recursion.
if self.visited_opaque_tys.insert(def_id) {
if self.visited_tys.insert(ty) {
// The intent is to treat `impl Trait1 + Trait2` identically to
// `dyn Trait1 + Trait2`. Therefore we ignore def-id of the opaque type itself
// (it either has no visibility, or its visibility is insignificant, like
@ -929,7 +939,7 @@ impl<'tcx> NamePrivacyVisitor<'tcx> {
// Checks that a field in a struct constructor (expression or pattern) is accessible.
fn check_field(
&mut self,
&self,
hir_id: hir::HirId, // ID of the field use
use_ctxt: Span, // syntax context of the field name at the use site
def: ty::AdtDef<'tcx>, // definition of the struct or enum
@ -947,7 +957,7 @@ impl<'tcx> NamePrivacyVisitor<'tcx> {
// Checks that a field in a struct constructor (expression or pattern) is accessible.
fn emit_unreachable_field_error(
&mut self,
&self,
fields: Vec<(Symbol, Span, bool /* field is present */)>,
def: ty::AdtDef<'tcx>, // definition of the struct or enum
update_syntax: Option<Span>,
@ -1010,7 +1020,7 @@ impl<'tcx> NamePrivacyVisitor<'tcx> {
}
fn check_expanded_fields(
&mut self,
&self,
adt: ty::AdtDef<'tcx>,
variant: &'tcx ty::VariantDef,
fields: &[hir::ExprField<'tcx>],
@ -1148,7 +1158,7 @@ impl<'tcx> TypePrivacyVisitor<'tcx> {
result.is_break()
}
fn check_def_id(&mut self, def_id: DefId, kind: &str, descr: &dyn fmt::Display) -> bool {
fn check_def_id(&self, def_id: DefId, kind: &str, descr: &dyn fmt::Display) -> bool {
let is_error = !self.item_is_accessible(def_id);
if is_error {
self.tcx.dcx().emit_err(ItemIsPrivate { span: self.span, kind, descr: descr.into() });
@ -1405,7 +1415,7 @@ impl SearchInterfaceForPrivateItemsVisitor<'_> {
self
}
fn check_def_id(&mut self, def_id: DefId, kind: &str, descr: &dyn fmt::Display) -> bool {
fn check_def_id(&self, def_id: DefId, kind: &str, descr: &dyn fmt::Display) -> bool {
if self.leaks_private_dep(def_id) {
self.tcx.emit_node_span_lint(
lint::builtin::EXPORTED_PRIVATE_DEPENDENCIES,

View file

@ -641,9 +641,7 @@ impl Rvalue {
.discriminant_ty()
.ok_or_else(|| error!("Expected a `RigidTy` but found: {place_ty:?}"))
}
Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..), _) => {
Ok(Ty::usize_ty())
}
Rvalue::NullaryOp(NullOp::OffsetOf(..), _) => Ok(Ty::usize_ty()),
Rvalue::NullaryOp(NullOp::ContractChecks, _)
| Rvalue::NullaryOp(NullOp::UbChecks, _) => Ok(Ty::bool_ty()),
Rvalue::Aggregate(ak, ops) => match *ak {
@ -1024,10 +1022,6 @@ pub enum CastKind {
#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize)]
pub enum NullOp {
/// Returns the size of a value of that type.
SizeOf,
/// Returns the minimum alignment of a type.
AlignOf,
/// Returns the offset of a field.
OffsetOf(Vec<(VariantIdx, FieldIdx)>),
/// cfg!(ub_checks), but at codegen time

View file

@ -323,8 +323,6 @@ impl<'tcx> Stable<'tcx> for mir::NullOp<'tcx> {
) -> Self::T {
use rustc_middle::mir::NullOp::*;
match self {
SizeOf => crate::mir::NullOp::SizeOf,
AlignOf => crate::mir::NullOp::AlignOf,
OffsetOf(indices) => crate::mir::NullOp::OffsetOf(
indices.iter().map(|idx| idx.stable(tables, cx)).collect(),
),

View file

@ -10,9 +10,8 @@ use rustc_hir as hir;
use rustc_hir::LangItem;
use rustc_middle::bug;
use rustc_middle::ty::{
self, AssocContainer, ExistentialPredicateStableCmpExt as _, Instance, InstanceKind, IntTy,
List, TraitRef, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt,
UintTy,
self, AssocContainer, ExistentialPredicateStableCmpExt as _, Instance, IntTy, List, TraitRef,
Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, UintTy,
};
use rustc_span::def_id::DefId;
use rustc_span::{DUMMY_SP, sym};
@ -459,6 +458,30 @@ pub(crate) fn transform_instance<'tcx>(
instance
}
fn default_or_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> Option<DefId> {
match instance.def {
ty::InstanceKind::Item(def_id) | ty::InstanceKind::FnPtrShim(def_id, _) => {
tcx.opt_associated_item(def_id).map(|item| item.def_id)
}
_ => None,
}
}
/// Determines if an instance represents a trait method implementation and returns the necessary
/// information for type erasure.
///
/// This function handles two main cases:
///
/// * **Implementation in an `impl` block**: When the instance represents a concrete implementation
/// of a trait method in an `impl` block, it extracts the trait reference, method ID, and trait
/// ID from the implementation. The method ID is obtained from the `trait_item_def_id` field of
/// the associated item, which points to the original trait method definition.
///
/// * **Provided method in a `trait` block or synthetic `shim`**: When the instance represents a
/// default implementation provided in the trait definition itself or a synthetic shim, it uses
/// the instance's own `def_id` as the method ID and determines the trait ID from the associated
/// item.
///
fn implemented_method<'tcx>(
tcx: TyCtxt<'tcx>,
instance: Instance<'tcx>,
@ -476,10 +499,11 @@ fn implemented_method<'tcx>(
trait_id = trait_ref.skip_binder().def_id;
impl_id
} else if let AssocContainer::Trait = assoc.container
&& let InstanceKind::Item(def_id) = instance.def
&& let Some(trait_method_def_id) = default_or_shim(tcx, instance)
{
// Provided method in a `trait` block or a synthetic `shim`
trait_method = assoc;
method_id = def_id;
method_id = trait_method_def_id;
trait_id = tcx.parent(method_id);
trait_ref = ty::EarlyBinder::bind(TraitRef::from_assoc(tcx, trait_id, instance.args));
trait_id

View file

@ -1387,11 +1387,13 @@ symbols! {
maybe_uninit,
maybe_uninit_uninit,
maybe_uninit_zeroed,
mem_align_const,
mem_align_of,
mem_discriminant,
mem_drop,
mem_forget,
mem_replace,
mem_size_const,
mem_size_of,
mem_size_of_val,
mem_swap,

View file

@ -16,12 +16,13 @@ use crate::fmt;
#[cfg(not(no_global_oom_handling))]
use crate::string::String;
// FIXME(inference): const bounds removed due to inference regressions found by crater;
// see https://github.com/rust-lang/rust/issues/147964
// #[rustc_const_unstable(feature = "const_convert", issue = "143773")]
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_unstable(feature = "const_convert", issue = "143773")]
impl<'a, B: ?Sized> const Borrow<B> for Cow<'a, B>
where
B: ToOwned,
B::Owned: [const] Borrow<B>,
impl<'a, B: ?Sized + ToOwned> Borrow<B> for Cow<'a, B>
// where
// B::Owned: [const] Borrow<B>,
{
fn borrow(&self) -> &B {
&**self
@ -327,11 +328,13 @@ impl<B: ?Sized + ToOwned> Cow<'_, B> {
}
}
// FIXME(inference): const bounds removed due to inference regressions found by crater;
// see https://github.com/rust-lang/rust/issues/147964
// #[rustc_const_unstable(feature = "const_convert", issue = "143773")]
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_unstable(feature = "const_convert", issue = "143773")]
impl<B: ?Sized + ToOwned> const Deref for Cow<'_, B>
where
B::Owned: [const] Borrow<B>,
impl<B: ?Sized + ToOwned> Deref for Cow<'_, B>
// where
// B::Owned: [const] Borrow<B>,
{
type Target = B;
@ -441,11 +444,13 @@ where
}
}
// FIXME(inference): const bounds removed due to inference regressions found by crater;
// see https://github.com/rust-lang/rust/issues/147964
// #[rustc_const_unstable(feature = "const_convert", issue = "143773")]
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_unstable(feature = "const_convert", issue = "143773")]
impl<T: ?Sized + ToOwned> const AsRef<T> for Cow<'_, T>
where
T::Owned: [const] Borrow<T>,
impl<T: ?Sized + ToOwned> AsRef<T> for Cow<'_, T>
// where
// T::Owned: [const] Borrow<T>,
{
fn as_ref(&self) -> &T {
self

View file

@ -85,6 +85,7 @@
//
// Library features:
// tidy-alphabetical-start
#![cfg_attr(not(no_global_oom_handling), feature(string_replace_in_place))]
#![feature(alloc_layout_extra)]
#![feature(allocator_api)]
#![feature(array_into_iter_constructors)]

View file

@ -2090,6 +2090,67 @@ impl String {
unsafe { self.as_mut_vec() }.splice((start, end), replace_with.bytes());
}
/// Replaces the leftmost occurrence of a pattern with another string, in-place.
///
/// This method can be preferred over [`string = string.replacen(..., 1);`][replacen],
/// as it can use the `String`'s existing capacity to prevent a reallocation if
/// sufficient space is available.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// #![feature(string_replace_in_place)]
///
/// let mut s = String::from("Test Results: ❌❌❌");
///
/// // Replace the leftmost ❌ with a ✅
/// s.replace_first('❌', "✅");
/// assert_eq!(s, "Test Results: ✅❌❌");
/// ```
///
/// [replacen]: ../../std/primitive.str.html#method.replacen
#[cfg(not(no_global_oom_handling))]
#[unstable(feature = "string_replace_in_place", issue = "147949")]
pub fn replace_first<P: Pattern>(&mut self, from: P, to: &str) {
let range = match self.match_indices(from).next() {
Some((start, match_str)) => start..start + match_str.len(),
None => return,
};
self.replace_range(range, to);
}
/// Replaces the rightmost occurrence of a pattern with another string, in-place.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// #![feature(string_replace_in_place)]
///
/// let mut s = String::from("Test Results: ❌❌❌");
///
/// // Replace the rightmost ❌ with a ✅
/// s.replace_last('❌', "✅");
/// assert_eq!(s, "Test Results: ❌❌✅");
/// ```
#[cfg(not(no_global_oom_handling))]
#[unstable(feature = "string_replace_in_place", issue = "147949")]
pub fn replace_last<P: Pattern>(&mut self, from: P, to: &str)
where
for<'a> P::Searcher<'a>: core::str::pattern::ReverseSearcher<'a>,
{
let range = match self.rmatch_indices(from).next() {
Some((start, match_str)) => start..start + match_str.len(),
None => return,
};
self.replace_range(range, to);
}
/// Converts this `String` into a <code>[Box]<[str]></code>.
///
/// Before doing the conversion, this method discards excess capacity like [`shrink_to_fit`].

View file

@ -36,6 +36,7 @@
#![feature(local_waker)]
#![feature(str_as_str)]
#![feature(strict_provenance_lints)]
#![feature(string_replace_in_place)]
#![feature(vec_deque_pop_if)]
#![feature(vec_deque_truncate_front)]
#![feature(unique_rc_arc)]

View file

@ -719,6 +719,40 @@ fn test_replace_range_evil_end_bound() {
assert_eq!(Ok(""), str::from_utf8(s.as_bytes()));
}
#[test]
fn test_replace_first() {
let mut s = String::from("~ First ❌ Middle ❌ Last ❌ ~");
s.replace_first("", "✅✅");
assert_eq!(s, "~ First ✅✅ Middle ❌ Last ❌ ~");
s.replace_first("🦀", "😳");
assert_eq!(s, "~ First ✅✅ Middle ❌ Last ❌ ~");
let mut s = String::from("");
s.replace_first('❌', "✅✅");
assert_eq!(s, "✅✅");
let mut s = String::from("");
s.replace_first('🌌', "");
assert_eq!(s, "");
}
#[test]
fn test_replace_last() {
let mut s = String::from("~ First ❌ Middle ❌ Last ❌ ~");
s.replace_last("", "✅✅");
assert_eq!(s, "~ First ❌ Middle ❌ Last ✅✅ ~");
s.replace_last("🦀", "😳");
assert_eq!(s, "~ First ❌ Middle ❌ Last ✅✅ ~");
let mut s = String::from("");
s.replace_last::<char>('❌', "✅✅");
assert_eq!(s, "✅✅");
let mut s = String::from("");
s.replace_last::<char>('🌌', "");
assert_eq!(s, "");
}
#[test]
fn test_extend_ref() {
let mut a = "foo".to_string();

View file

@ -253,6 +253,89 @@ use crate::{fmt, intrinsics, ptr, slice};
/// std::process::exit(*code); // UB! Accessing uninitialized memory.
/// }
/// ```
///
/// # Validity
///
/// `MaybeUninit<T>` has no validity requirements - any sequence of [bytes] of
/// the appropriate length, initialized or uninitialized, are a valid
/// representation.
///
/// Moving or copying a value of type `MaybeUninit<T>` (i.e., performing a
/// "typed copy") will exactly preserve the contents, including the
/// [provenance], of all non-padding bytes of type `T` in the value's
/// representation.
///
/// Therefore `MaybeUninit` can be used to perform a round trip of a value from
/// type `T` to type `MaybeUninit<U>` then back to type `T`, while preserving
/// the original value, if two conditions are met. One, type `U` must have the
/// same size as type `T`. Two, for all byte offsets where type `U` has padding,
/// the corresponding bytes in the representation of the value must be
/// uninitialized.
///
/// For example, due to the fact that the type `[u8; size_of::<T>]` has no
/// padding, the following is sound for any type `T` and will return the
/// original value:
///
/// ```rust,no_run
/// # use core::mem::{MaybeUninit, transmute};
/// # struct T;
/// fn identity(t: T) -> T {
/// unsafe {
/// let u: MaybeUninit<[u8; size_of::<T>()]> = transmute(t);
/// transmute(u) // OK.
/// }
/// }
/// ```
///
/// Note: Copying a value that contains references may implicitly reborrow them
/// causing the provenance of the returned value to differ from that of the
/// original. This applies equally to the trivial identity function:
///
/// ```rust,no_run
/// fn trivial_identity<T>(t: T) -> T { t }
/// ```
///
/// Note: Moving or copying a value whose representation has initialized bytes
/// at byte offsets where the type has padding may lose the value of those
/// bytes, so while the original value will be preserved, the original
/// *representation* of that value as bytes may not be. Again, this applies
/// equally to `trivial_identity`.
///
/// Note: Performing this round trip when type `U` has padding at byte offsets
/// where the representation of the original value has initialized bytes may
/// produce undefined behavior or a different value. For example, the following
/// is unsound since `T` requires all bytes to be initialized:
///
/// ```rust,no_run
/// # use core::mem::{MaybeUninit, transmute};
/// #[repr(C)] struct T([u8; 4]);
/// #[repr(C)] struct U(u8, u16);
/// fn unsound_identity(t: T) -> T {
/// unsafe {
/// let u: MaybeUninit<U> = transmute(t);
/// transmute(u) // UB.
/// }
/// }
/// ```
///
/// Conversely, the following is sound since `T` allows uninitialized bytes in
/// the representation of a value, but the round trip may alter the value:
///
/// ```rust,no_run
/// # use core::mem::{MaybeUninit, transmute};
/// #[repr(C)] struct T(MaybeUninit<[u8; 4]>);
/// #[repr(C)] struct U(u8, u16);
/// fn non_identity(t: T) -> T {
/// unsafe {
/// // May lose an initialized byte.
/// let u: MaybeUninit<U> = transmute(t);
/// transmute(u)
/// }
/// }
/// ```
///
/// [bytes]: ../../reference/memory-model.html#bytes
/// [provenance]: crate::ptr#provenance
#[stable(feature = "maybe_uninit", since = "1.36.0")]
// Lang item so we can wrap other types in it. This is useful for coroutines.
#[lang = "maybe_uninit"]

View file

@ -333,7 +333,7 @@ pub fn forget_unsized<T: ?Sized>(t: T) {
#[rustc_const_stable(feature = "const_mem_size_of", since = "1.24.0")]
#[rustc_diagnostic_item = "mem_size_of"]
pub const fn size_of<T>() -> usize {
intrinsics::size_of::<T>()
<T as SizedTypeProperties>::SIZE
}
/// Returns the size of the pointed-to value in bytes.
@ -441,7 +441,7 @@ pub const unsafe fn size_of_val_raw<T: ?Sized>(val: *const T) -> usize {
#[stable(feature = "rust1", since = "1.0.0")]
#[deprecated(note = "use `align_of` instead", since = "1.2.0", suggestion = "align_of")]
pub fn min_align_of<T>() -> usize {
intrinsics::align_of::<T>()
<T as SizedTypeProperties>::ALIGN
}
/// Returns the [ABI]-required minimum alignment of the type of the value that `val` points to in
@ -488,7 +488,7 @@ pub fn min_align_of_val<T: ?Sized>(val: &T) -> usize {
#[rustc_const_stable(feature = "const_align_of", since = "1.24.0")]
#[rustc_diagnostic_item = "mem_align_of"]
pub const fn align_of<T>() -> usize {
intrinsics::align_of::<T>()
<T as SizedTypeProperties>::ALIGN
}
/// Returns the [ABI]-required minimum alignment of the type of the value that `val` points to in
@ -1236,6 +1236,16 @@ pub const fn variant_count<T>() -> usize {
#[doc(hidden)]
#[unstable(feature = "sized_type_properties", issue = "none")]
pub trait SizedTypeProperties: Sized {
#[doc(hidden)]
#[unstable(feature = "sized_type_properties", issue = "none")]
#[lang = "mem_size_const"]
const SIZE: usize = intrinsics::size_of::<Self>();
#[doc(hidden)]
#[unstable(feature = "sized_type_properties", issue = "none")]
#[lang = "mem_align_const"]
const ALIGN: usize = intrinsics::align_of::<Self>();
/// `true` if this type requires no storage.
/// `false` if its [size](size_of) is greater than zero.
///
@ -1263,7 +1273,7 @@ pub trait SizedTypeProperties: Sized {
/// ```
#[doc(hidden)]
#[unstable(feature = "sized_type_properties", issue = "none")]
const IS_ZST: bool = size_of::<Self>() == 0;
const IS_ZST: bool = Self::SIZE == 0;
#[doc(hidden)]
#[unstable(feature = "sized_type_properties", issue = "none")]
@ -1275,7 +1285,7 @@ pub trait SizedTypeProperties: Sized {
/// which is never allowed for a single object.
#[doc(hidden)]
#[unstable(feature = "sized_type_properties", issue = "none")]
const MAX_SLICE_LEN: usize = match size_of::<Self>() {
const MAX_SLICE_LEN: usize = match Self::SIZE {
0 => usize::MAX,
n => (isize::MAX as usize) / n,
};

View file

@ -290,8 +290,6 @@
#![feature(ffi_const)]
#![feature(formatting_options)]
#![feature(funnel_shifts)]
#![feature(hash_map_internals)]
#![feature(hash_map_macro)]
#![feature(if_let_guard)]
#![feature(intra_doc_pointers)]
#![feature(iter_advance_by)]

View file

@ -379,77 +379,3 @@ macro_rules! dbg {
($($crate::dbg!($val)),+,)
};
}
#[doc(hidden)]
#[macro_export]
#[allow_internal_unstable(hash_map_internals)]
#[unstable(feature = "hash_map_internals", issue = "none")]
macro_rules! repetition_utils {
(@count $($tokens:tt),*) => {{
[$($crate::repetition_utils!(@replace $tokens => ())),*].len()
}};
(@replace $x:tt => $y:tt) => { $y }
}
/// Creates a [`HashMap`] containing the arguments.
///
/// `hash_map!` allows specifying the entries that make
/// up the [`HashMap`] where the key and value are separated by a `=>`.
///
/// The entries are separated by commas with a trailing comma being allowed.
///
/// It is semantically equivalent to using repeated [`HashMap::insert`]
/// on a newly created hashmap.
///
/// `hash_map!` will attempt to avoid repeated reallocations by
/// using [`HashMap::with_capacity`].
///
/// # Examples
///
/// ```rust
/// #![feature(hash_map_macro)]
///
/// let map = hash_map! {
/// "key" => "value",
/// "key1" => "value1"
/// };
///
/// assert_eq!(map.get("key"), Some(&"value"));
/// assert_eq!(map.get("key1"), Some(&"value1"));
/// assert!(map.get("brrrrrrooooommm").is_none());
/// ```
///
/// And with a trailing comma
///
///```rust
/// #![feature(hash_map_macro)]
///
/// let map = hash_map! {
/// "key" => "value", // notice the ,
/// };
///
/// assert_eq!(map.get("key"), Some(&"value"));
/// ```
///
/// The key and value are moved into the HashMap.
///
/// [`HashMap`]: crate::collections::HashMap
/// [`HashMap::insert`]: crate::collections::HashMap::insert
/// [`HashMap::with_capacity`]: crate::collections::HashMap::with_capacity
#[macro_export]
#[allow_internal_unstable(hash_map_internals)]
#[unstable(feature = "hash_map_macro", issue = "144032")]
macro_rules! hash_map {
() => {{
$crate::collections::HashMap::new()
}};
( $( $key:expr => $value:expr ),* $(,)? ) => {{
let mut map = $crate::collections::HashMap::with_capacity(
const { $crate::repetition_utils!(@count $($key),*) }
);
$( map.insert($key, $value); )*
map
}}
}

View file

@ -0,0 +1,17 @@
//! Make sure that a `std` macro `hash_map!` does not cause ambiguity
//! with a local glob import with the same name.
//!
//! See regression https://github.com/rust-lang/rust/issues/147971
mod module {
macro_rules! hash_map {
() => {};
}
pub(crate) use hash_map;
}
use module::*;
fn main() {
hash_map! {}
}

View file

@ -5,7 +5,7 @@ use std::iter;
use askama::Template;
use rustc_abi::VariantIdx;
use rustc_ast::join_path_syms;
use rustc_data_structures::fx::{FxHashMap, FxIndexSet};
use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet};
use rustc_hir as hir;
use rustc_hir::def::CtorKind;
use rustc_hir::def_id::DefId;
@ -307,8 +307,12 @@ fn item_module(cx: &Context<'_>, item: &clean::Item, items: &[clean::Item]) -> i
fmt::from_fn(|w| {
write!(w, "{}", document(cx, item, None, HeadingOffset::H2))?;
let mut not_stripped_items =
items.iter().filter(|i| !i.is_stripped()).enumerate().collect::<Vec<_>>();
let mut not_stripped_items: FxIndexMap<ItemType, Vec<(usize, &clean::Item)>> =
FxIndexMap::default();
for (index, item) in items.iter().filter(|i| !i.is_stripped()).enumerate() {
not_stripped_items.entry(item.type_()).or_default().push((index, item));
}
// the order of item types in the listing
fn reorder(ty: ItemType) -> u8 {
@ -331,11 +335,6 @@ fn item_module(cx: &Context<'_>, item: &clean::Item, items: &[clean::Item]) -> i
}
fn cmp(i1: &clean::Item, i2: &clean::Item, tcx: TyCtxt<'_>) -> Ordering {
let rty1 = reorder(i1.type_());
let rty2 = reorder(i2.type_());
if rty1 != rty2 {
return rty1.cmp(&rty2);
}
let is_stable1 =
i1.stability(tcx).as_ref().map(|s| s.level.is_stable()).unwrap_or(true);
let is_stable2 =
@ -357,7 +356,9 @@ fn item_module(cx: &Context<'_>, item: &clean::Item, items: &[clean::Item]) -> i
match cx.shared.module_sorting {
ModuleSorting::Alphabetical => {
not_stripped_items.sort_by(|(_, i1), (_, i2)| cmp(i1, i2, tcx));
for items in not_stripped_items.values_mut() {
items.sort_by(|(_, i1), (_, i2)| cmp(i1, i2, tcx));
}
}
ModuleSorting::DeclarationOrder => {}
}
@ -380,155 +381,152 @@ fn item_module(cx: &Context<'_>, item: &clean::Item, items: &[clean::Item]) -> i
// can be identical even if the elements are different (mostly in imports).
// So in case this is an import, we keep everything by adding a "unique id"
// (which is the position in the vector).
not_stripped_items.dedup_by_key(|(idx, i)| {
(
i.item_id,
if i.name.is_some() { Some(full_path(cx, i)) } else { None },
i.type_(),
if i.is_import() { *idx } else { 0 },
)
});
for items in not_stripped_items.values_mut() {
items.dedup_by_key(|(idx, i)| {
(
i.item_id,
if i.name.is_some() { Some(full_path(cx, i)) } else { None },
i.type_(),
if i.is_import() { *idx } else { 0 },
)
});
}
debug!("{not_stripped_items:?}");
let mut last_section = None;
for (_, myitem) in &not_stripped_items {
let my_section = item_ty_to_section(myitem.type_());
if Some(my_section) != last_section {
if last_section.is_some() {
w.write_str(ITEM_TABLE_CLOSE)?;
}
last_section = Some(my_section);
let section_id = my_section.id();
let tag =
if section_id == "reexports" { REEXPORTS_TABLE_OPEN } else { ITEM_TABLE_OPEN };
write!(
w,
"{}",
write_section_heading(my_section.name(), &cx.derive_id(section_id), None, tag)
)?;
}
let mut types = not_stripped_items.keys().copied().collect::<Vec<_>>();
types.sort_unstable_by(|a, b| reorder(*a).cmp(&reorder(*b)));
match myitem.kind {
clean::ExternCrateItem { ref src } => {
use crate::html::format::print_anchor;
for type_ in types {
let my_section = item_ty_to_section(type_);
let tag = if my_section == super::ItemSection::Reexports {
REEXPORTS_TABLE_OPEN
} else {
ITEM_TABLE_OPEN
};
write!(
w,
"{}",
write_section_heading(my_section.name(), &cx.derive_id(my_section.id()), None, tag)
)?;
match *src {
Some(src) => {
write!(
w,
"<dt><code>{}extern crate {} as {};",
visibility_print_with_space(myitem, cx),
print_anchor(myitem.item_id.expect_def_id(), src, cx),
EscapeBodyTextWithWbr(myitem.name.unwrap().as_str())
)?;
}
None => {
write!(
w,
"<dt><code>{}extern crate {};",
visibility_print_with_space(myitem, cx),
print_anchor(
myitem.item_id.expect_def_id(),
myitem.name.unwrap(),
cx
)
)?;
}
}
w.write_str("</code></dt>")?;
}
for (_, myitem) in &not_stripped_items[&type_] {
match myitem.kind {
clean::ExternCrateItem { ref src } => {
use crate::html::format::print_anchor;
clean::ImportItem(ref import) => {
let stab_tags = import.source.did.map_or_else(String::new, |import_def_id| {
print_extra_info_tags(tcx, myitem, item, Some(import_def_id)).to_string()
});
let id = match import.kind {
clean::ImportKind::Simple(s) => {
format!(" id=\"{}\"", cx.derive_id(format!("reexport.{s}")))
}
clean::ImportKind::Glob => String::new(),
};
write!(
w,
"<dt{id}>\
<code>"
)?;
render_attributes_in_code(w, myitem, "", cx);
write!(
w,
"{vis}{imp}</code>{stab_tags}\
</dt>",
vis = visibility_print_with_space(myitem, cx),
imp = import.print(cx)
)?;
}
_ => {
if myitem.name.is_none() {
continue;
}
let unsafety_flag = match myitem.kind {
clean::FunctionItem(_) | clean::ForeignFunctionItem(..)
if myitem.fn_header(tcx).unwrap().safety
== hir::HeaderSafety::Normal(hir::Safety::Unsafe) =>
{
"<sup title=\"unsafe function\">⚠</sup>"
}
clean::ForeignStaticItem(_, hir::Safety::Unsafe) => {
"<sup title=\"unsafe static\">⚠</sup>"
}
_ => "",
};
let visibility_and_hidden = match myitem.visibility(tcx) {
Some(ty::Visibility::Restricted(_)) => {
if myitem.is_doc_hidden() {
// Don't separate with a space when there are two of them
"<span title=\"Restricted Visibility\">&nbsp;🔒</span><span title=\"Hidden item\">👻</span> "
} else {
"<span title=\"Restricted Visibility\">&nbsp;🔒</span> "
match *src {
Some(src) => {
write!(
w,
"<dt><code>{}extern crate {} as {};",
visibility_print_with_space(myitem, cx),
print_anchor(myitem.item_id.expect_def_id(), src, cx),
EscapeBodyTextWithWbr(myitem.name.unwrap().as_str())
)?;
}
None => {
write!(
w,
"<dt><code>{}extern crate {};",
visibility_print_with_space(myitem, cx),
print_anchor(
myitem.item_id.expect_def_id(),
myitem.name.unwrap(),
cx
)
)?;
}
}
_ if myitem.is_doc_hidden() => {
"<span title=\"Hidden item\">&nbsp;👻</span> "
}
clean::ImportItem(ref import) => {
let stab_tags =
import.source.did.map_or_else(String::new, |import_def_id| {
print_extra_info_tags(tcx, myitem, item, Some(import_def_id))
.to_string()
});
let id = match import.kind {
clean::ImportKind::Simple(s) => {
format!(" id=\"{}\"", cx.derive_id(format!("reexport.{s}")))
}
clean::ImportKind::Glob => String::new(),
};
write!(
w,
"<dt{id}>\
<code>"
)?;
render_attributes_in_code(w, myitem, "", cx);
write!(
w,
"{vis}{imp}</code>{stab_tags}\
</dt>",
vis = visibility_print_with_space(myitem, cx),
imp = import.print(cx)
)?;
}
_ => {
if myitem.name.is_none() {
continue;
}
_ => "",
};
let docs =
MarkdownSummaryLine(&myitem.doc_value(), &myitem.links(cx)).into_string();
let (docs_before, docs_after) =
if docs.is_empty() { ("", "") } else { ("<dd>", "</dd>") };
write!(
w,
"<dt>\
<a class=\"{class}\" href=\"{href}\" title=\"{title1} {title2}\">\
{name}\
</a>\
{visibility_and_hidden}\
{unsafety_flag}\
{stab_tags}\
</dt>\
{docs_before}{docs}{docs_after}",
name = EscapeBodyTextWithWbr(myitem.name.unwrap().as_str()),
visibility_and_hidden = visibility_and_hidden,
stab_tags = print_extra_info_tags(tcx, myitem, item, None),
class = myitem.type_(),
unsafety_flag = unsafety_flag,
href = print_item_path(myitem.type_(), myitem.name.unwrap().as_str()),
title1 = myitem.type_(),
title2 = full_path(cx, myitem),
)?;
let unsafety_flag = match myitem.kind {
clean::FunctionItem(_) | clean::ForeignFunctionItem(..)
if myitem.fn_header(tcx).unwrap().safety
== hir::HeaderSafety::Normal(hir::Safety::Unsafe) =>
{
"<sup title=\"unsafe function\">⚠</sup>"
}
clean::ForeignStaticItem(_, hir::Safety::Unsafe) => {
"<sup title=\"unsafe static\">⚠</sup>"
}
_ => "",
};
let visibility_and_hidden = match myitem.visibility(tcx) {
Some(ty::Visibility::Restricted(_)) => {
if myitem.is_doc_hidden() {
// Don't separate with a space when there are two of them
"<span title=\"Restricted Visibility\">&nbsp;🔒</span><span title=\"Hidden item\">👻</span> "
} else {
"<span title=\"Restricted Visibility\">&nbsp;🔒</span> "
}
}
_ if myitem.is_doc_hidden() => {
"<span title=\"Hidden item\">&nbsp;👻</span> "
}
_ => "",
};
let docs = MarkdownSummaryLine(&myitem.doc_value(), &myitem.links(cx))
.into_string();
let (docs_before, docs_after) =
if docs.is_empty() { ("", "") } else { ("<dd>", "</dd>") };
write!(
w,
"<dt>\
<a class=\"{class}\" href=\"{href}\" title=\"{title1} {title2}\">\
{name}\
</a>\
{visibility_and_hidden}\
{unsafety_flag}\
{stab_tags}\
</dt>\
{docs_before}{docs}{docs_after}",
name = EscapeBodyTextWithWbr(myitem.name.unwrap().as_str()),
visibility_and_hidden = visibility_and_hidden,
stab_tags = print_extra_info_tags(tcx, myitem, item, None),
class = type_,
unsafety_flag = unsafety_flag,
href = print_item_path(type_, myitem.name.unwrap().as_str()),
title1 = myitem.type_(),
title2 = full_path(cx, myitem),
)?;
}
}
}
}
if last_section.is_some() {
w.write_str(ITEM_TABLE_CLOSE)?;
}
Ok(())
})
}

View file

@ -562,7 +562,7 @@ pub fn eq_foreign_item_kind(l: &ForeignItemKind, r: &ForeignItemKind) -> bool {
defaultness: ld,
ident: li,
generics: lg,
where_clauses: _,
after_where_clause: lw,
bounds: lb,
ty: lt,
}),
@ -570,7 +570,7 @@ pub fn eq_foreign_item_kind(l: &ForeignItemKind, r: &ForeignItemKind) -> bool {
defaultness: rd,
ident: ri,
generics: rg,
where_clauses: _,
after_where_clause: rw,
bounds: rb,
ty: rt,
}),
@ -578,6 +578,7 @@ pub fn eq_foreign_item_kind(l: &ForeignItemKind, r: &ForeignItemKind) -> bool {
eq_defaultness(*ld, *rd)
&& eq_id(*li, *ri)
&& eq_generics(lg, rg)
&& over(&lw.predicates, &rw.predicates, eq_where_predicate)
&& over(lb, rb, eq_generic_bound)
&& both(lt.as_ref(), rt.as_ref(), |l, r| eq_ty(l, r))
},
@ -645,7 +646,7 @@ pub fn eq_assoc_item_kind(l: &AssocItemKind, r: &AssocItemKind) -> bool {
defaultness: ld,
ident: li,
generics: lg,
where_clauses: _,
after_where_clause: lw,
bounds: lb,
ty: lt,
}),
@ -653,7 +654,7 @@ pub fn eq_assoc_item_kind(l: &AssocItemKind, r: &AssocItemKind) -> bool {
defaultness: rd,
ident: ri,
generics: rg,
where_clauses: _,
after_where_clause: rw,
bounds: rb,
ty: rt,
}),
@ -661,6 +662,7 @@ pub fn eq_assoc_item_kind(l: &AssocItemKind, r: &AssocItemKind) -> bool {
eq_defaultness(*ld, *rd)
&& eq_id(*li, *ri)
&& eq_generics(lg, rg)
&& over(&lw.predicates, &rw.predicates, eq_where_predicate)
&& over(lb, rb, eq_generic_bound)
&& both(lt.as_ref(), rt.as_ref(), |l, r| eq_ty(l, r))
},

View file

@ -194,10 +194,7 @@ fn check_rvalue<'tcx>(
))
}
},
Rvalue::NullaryOp(
NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(_) | NullOp::UbChecks | NullOp::ContractChecks,
_,
)
Rvalue::NullaryOp(NullOp::OffsetOf(_) | NullOp::UbChecks | NullOp::ContractChecks, _)
| Rvalue::ShallowInitBox(_, _) => Ok(()),
Rvalue::UnaryOp(_, operand) => {
let ty = operand.ty(body, cx.tcx);

View file

@ -272,8 +272,10 @@ LL | assert_eq!(a!(), true);
|
help: replace it with `assert!(..)`
|
LL - assert_eq!(a!(), true);
LL + assert!(a!());
LL | true
...
LL |
LL ~ assert!(a!());
|
error: used `assert_eq!` with a literal bool
@ -284,8 +286,10 @@ LL | assert_eq!(true, b!());
|
help: replace it with `assert!(..)`
|
LL - assert_eq!(true, b!());
LL + assert!(b!());
LL | true
...
LL |
LL ~ assert!(b!());
|
error: used `debug_assert_eq!` with a literal bool

View file

@ -2,29 +2,20 @@ error[E0391]: cycle detected when computing layout of `S<S<()>>`
|
= note: ...which requires computing layout of `<S<()> as Tr>::I`...
= note: ...which again requires computing layout of `S<S<()>>`, completing the cycle
= note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information
error: post-monomorphization error: a cycle occurred during layout computation
note: cycle used when const-evaluating + checking `core::mem::SizedTypeProperties::SIZE`
--> RUSTLIB/core/src/mem/mod.rs:LL:CC
|
LL | intrinsics::size_of::<T>()
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ post-monomorphization error occurred here
|
= note: BACKTRACE:
= note: inside `std::mem::size_of::<S<S<()>>>` at RUSTLIB/core/src/mem/mod.rs:LL:CC
note: inside `foo::<S<()>>`
--> tests/fail/layout_cycle.rs:LL:CC
|
LL | mem::size_of::<S<T>>()
| ^^^^^^^^^^^^^^^^^^^^^^
note: inside `main`
--> tests/fail/layout_cycle.rs:LL:CC
|
LL | println!("{}", foo::<S<()>>());
| ^^^^^^^^^^^^^^
LL | const SIZE: usize = intrinsics::size_of::<Self>();
| ^^^^^^^^^^^^^^^^^
= note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
error[E0080]: a cycle occurred during layout computation
--> RUSTLIB/core/src/mem/mod.rs:LL:CC
|
LL | const SIZE: usize = intrinsics::size_of::<Self>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `<S<S<()>> as std::mem::SizedTypeProperties>::SIZE` failed here
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0391`.
Some errors have detailed explanations: E0080, E0391.
For more information about an error, try `rustc --explain E0080`.

View file

@ -836,8 +836,7 @@ pub(crate) fn format_impl(
let where_span_end = context.snippet_provider.opt_span_before(missing_span, "{");
let where_clause_str = rewrite_where_clause(
context,
&generics.where_clause.predicates,
generics.where_clause.span,
&generics.where_clause,
context.config.brace_style(),
Shape::legacy(where_budget, offset.block_only()),
false,
@ -1224,8 +1223,7 @@ pub(crate) fn format_trait(
let option = WhereClauseOption::snuggled(&generics_str);
let where_clause_str = rewrite_where_clause(
context,
&generics.where_clause.predicates,
generics.where_clause.span,
&generics.where_clause,
context.config.brace_style(),
Shape::legacy(where_budget, offset.block_only()),
where_on_new_line,
@ -1350,8 +1348,7 @@ impl<'a> Rewrite for TraitAliasBounds<'a> {
let where_str = rewrite_where_clause(
context,
&self.generics.where_clause.predicates,
self.generics.where_clause.span,
&self.generics.where_clause,
context.config.brace_style(),
shape,
false,
@ -1621,8 +1618,7 @@ fn format_tuple_struct(
let option = WhereClauseOption::new(true, WhereClauseSpace::Newline);
rewrite_where_clause(
context,
&generics.where_clause.predicates,
generics.where_clause.span,
&generics.where_clause,
context.config.brace_style(),
Shape::legacy(where_budget, offset.block_only()),
false,
@ -1691,7 +1687,7 @@ struct TyAliasRewriteInfo<'c, 'g>(
&'c RewriteContext<'c>,
Indent,
&'g ast::Generics,
ast::TyAliasWhereClauses,
&'g ast::WhereClause,
symbol::Ident,
Span,
);
@ -1712,13 +1708,13 @@ pub(crate) fn rewrite_type_alias<'a>(
ref generics,
ref bounds,
ref ty,
where_clauses,
ref after_where_clause,
} = *ty_alias_kind;
let ty_opt = ty.as_ref();
let rhs_hi = ty
.as_ref()
.map_or(where_clauses.before.span.hi(), |ty| ty.span.hi());
let rw_info = &TyAliasRewriteInfo(context, indent, generics, where_clauses, ident, span);
.map_or(generics.where_clause.span.hi(), |ty| ty.span.hi());
let rw_info = &TyAliasRewriteInfo(context, indent, generics, after_where_clause, ident, span);
let op_ty = opaque_ty(ty);
// Type Aliases are formatted slightly differently depending on the context
// in which they appear, whether they are opaque, and whether they are associated.
@ -1762,11 +1758,7 @@ fn rewrite_ty<R: Rewrite>(
vis: &ast::Visibility,
) -> RewriteResult {
let mut result = String::with_capacity(128);
let TyAliasRewriteInfo(context, indent, generics, where_clauses, ident, span) = *rw_info;
let (before_where_predicates, after_where_predicates) = generics
.where_clause
.predicates
.split_at(where_clauses.split);
let TyAliasRewriteInfo(context, indent, generics, after_where_clause, ident, span) = *rw_info;
result.push_str(&format!("{}type ", format_visibility(context, vis)));
let ident_str = rewrite_ident(context, ident);
@ -1804,8 +1796,7 @@ fn rewrite_ty<R: Rewrite>(
}
let before_where_clause_str = rewrite_where_clause(
context,
before_where_predicates,
where_clauses.before.span,
&generics.where_clause,
context.config.brace_style(),
Shape::legacy(where_budget, indent),
false,
@ -1820,9 +1811,9 @@ fn rewrite_ty<R: Rewrite>(
// If there are any where clauses, add a newline before the assignment.
// If there is a before where clause, do not indent, but if there is
// only an after where clause, additionally indent the type.
if !before_where_predicates.is_empty() {
if !generics.where_clause.predicates.is_empty() {
result.push_str(&indent.to_string_with_newline(context.config));
} else if !after_where_predicates.is_empty() {
} else if !after_where_clause.predicates.is_empty() {
result.push_str(
&indent
.block_indent(context.config)
@ -1835,7 +1826,7 @@ fn rewrite_ty<R: Rewrite>(
let comment_span = context
.snippet_provider
.opt_span_before(span, "=")
.map(|op_lo| mk_sp(where_clauses.before.span.hi(), op_lo));
.map(|op_lo| mk_sp(generics.where_clause.span.hi(), op_lo));
let lhs = match comment_span {
Some(comment_span)
@ -1846,7 +1837,7 @@ fn rewrite_ty<R: Rewrite>(
.unknown_error()?,
) =>
{
let comment_shape = if !before_where_predicates.is_empty() {
let comment_shape = if !generics.where_clause.predicates.is_empty() {
Shape::indented(indent, context.config)
} else {
let shape = Shape::indented(indent, context.config);
@ -1869,7 +1860,7 @@ fn rewrite_ty<R: Rewrite>(
// 1 = `;` unless there's a trailing where clause
let shape = Shape::indented(indent, context.config);
let shape = if after_where_predicates.is_empty() {
let shape = if after_where_clause.predicates.is_empty() {
Shape::indented(indent, context.config)
.sub_width(1)
.max_width_error(shape.width, span)?
@ -1881,12 +1872,11 @@ fn rewrite_ty<R: Rewrite>(
result
};
if !after_where_predicates.is_empty() {
if !after_where_clause.predicates.is_empty() {
let option = WhereClauseOption::new(true, WhereClauseSpace::Newline);
let after_where_clause_str = rewrite_where_clause(
context,
after_where_predicates,
where_clauses.after.span,
&after_where_clause,
context.config.brace_style(),
Shape::indented(indent, context.config),
false,
@ -2728,8 +2718,7 @@ fn rewrite_fn_base(
}
let where_clause_str = rewrite_where_clause(
context,
&where_clause.predicates,
where_clause.span,
&where_clause,
context.config.brace_style(),
Shape::indented(indent, context.config),
true,
@ -3158,8 +3147,7 @@ fn rewrite_bounds_on_where_clause(
fn rewrite_where_clause(
context: &RewriteContext<'_>,
predicates: &[ast::WherePredicate],
where_span: Span,
where_clause: &ast::WhereClause,
brace_style: BraceStyle,
shape: Shape,
on_new_line: bool,
@ -3168,6 +3156,12 @@ fn rewrite_where_clause(
span_end_before_where: BytePos,
where_clause_option: WhereClauseOption,
) -> RewriteResult {
let ast::WhereClause {
ref predicates,
span: where_span,
has_where_token: _,
} = *where_clause;
if predicates.is_empty() {
return Ok(String::new());
}
@ -3354,8 +3348,7 @@ fn format_generics(
}
let where_clause_str = rewrite_where_clause(
context,
&generics.where_clause.predicates,
generics.where_clause.span,
&generics.where_clause,
brace_style,
Shape::legacy(budget, offset.block_only()),
true,

View file

@ -24,7 +24,7 @@ use std::fmt::Display;
use std::iter::Peekable;
use std::path::Path;
use crate::diagnostics::{CheckId, DiagCtx, RunningCheck};
use crate::diagnostics::{CheckId, RunningCheck, TidyCtx};
use crate::walk::{filter_dirs, walk};
#[cfg(test)]
@ -130,8 +130,8 @@ fn check_lines<'a>(
}
}
pub fn check(path: &Path, diag_ctx: DiagCtx) {
let mut check = diag_ctx.start_check(CheckId::new("alphabetical").path(path));
pub fn check(path: &Path, tidy_ctx: TidyCtx) {
let mut check = tidy_ctx.start_check(CheckId::new("alphabetical").path(path));
let skip =
|path: &_, _is_dir| filter_dirs(path) || path.ends_with("tidy/src/alphabetical/tests.rs");

View file

@ -1,12 +1,12 @@
use std::path::Path;
use crate::alphabetical::check_lines;
use crate::diagnostics::DiagCtx;
use crate::diagnostics::{TidyCtx, TidyFlags};
#[track_caller]
fn test(lines: &str, name: &str, expected_msg: &str, expected_bad: bool) {
let diag_ctx = DiagCtx::new(Path::new("/"), false);
let mut check = diag_ctx.start_check("alphabetical-test");
let tidy_ctx = TidyCtx::new(Path::new("/"), false, TidyFlags::default());
let mut check = tidy_ctx.start_check("alphabetical-test");
check_lines(&name, lines.lines().enumerate(), &mut check);
assert_eq!(expected_bad, check.is_bad());

View file

@ -12,13 +12,13 @@ pub use os_impl::*;
mod os_impl {
use std::path::Path;
use crate::diagnostics::DiagCtx;
use crate::diagnostics::TidyCtx;
pub fn check_filesystem_support(_sources: &[&Path], _output: &Path) -> bool {
return false;
}
pub fn check(_path: &Path, _diag_ctx: DiagCtx) {}
pub fn check(_path: &Path, _tidy_ctx: TidyCtx) {}
}
#[cfg(unix)]
@ -38,7 +38,7 @@ mod os_impl {
use FilesystemSupport::*;
use crate::diagnostics::DiagCtx;
use crate::diagnostics::TidyCtx;
fn is_executable(path: &Path) -> std::io::Result<bool> {
Ok(path.metadata()?.mode() & 0o111 != 0)
@ -110,8 +110,8 @@ mod os_impl {
}
#[cfg(unix)]
pub fn check(path: &Path, diag_ctx: DiagCtx) {
let mut check = diag_ctx.start_check("bins");
pub fn check(path: &Path, tidy_ctx: TidyCtx) {
let mut check = tidy_ctx.start_check("bins");
use std::ffi::OsStr;

View file

@ -2,13 +2,13 @@
use std::path::Path;
use crate::diagnostics::{CheckId, DiagCtx};
use crate::diagnostics::{CheckId, TidyCtx};
use crate::walk::{filter_dirs, filter_not_rust, walk};
const GRAPHVIZ_POSTFLOW_MSG: &str = "`borrowck_graphviz_postflow` attribute in test";
pub fn check(test_dir: &Path, diag_ctx: DiagCtx) {
let mut check = diag_ctx.start_check(CheckId::new("debug_artifacts").path(test_dir));
pub fn check(test_dir: &Path, tidy_ctx: TidyCtx) {
let mut check = tidy_ctx.start_check(CheckId::new("debug_artifacts").path(test_dir));
walk(
test_dir,

View file

@ -9,7 +9,7 @@ use build_helper::ci::CiEnv;
use cargo_metadata::semver::Version;
use cargo_metadata::{Metadata, Package, PackageId};
use crate::diagnostics::{DiagCtx, RunningCheck};
use crate::diagnostics::{RunningCheck, TidyCtx};
#[path = "../../../bootstrap/src/utils/proc_macro_deps.rs"]
mod proc_macro_deps;
@ -615,8 +615,9 @@ const PERMITTED_CRANELIFT_DEPENDENCIES: &[&str] = &[
///
/// `root` is path to the directory with the root `Cargo.toml` (for the workspace). `cargo` is path
/// to the cargo executable.
pub fn check(root: &Path, cargo: &Path, bless: bool, diag_ctx: DiagCtx) {
let mut check = diag_ctx.start_check("deps");
pub fn check(root: &Path, cargo: &Path, tidy_ctx: TidyCtx) {
let mut check = tidy_ctx.start_check("deps");
let bless = tidy_ctx.is_bless_enabled();
let mut checked_runtime_licenses = false;

View file

@ -5,28 +5,59 @@ use std::sync::{Arc, Mutex};
use termcolor::{Color, WriteColor};
#[derive(Clone, Default)]
///CLI flags used by tidy.
pub struct TidyFlags {
///Applies style and formatting changes during a tidy run.
bless: bool,
}
impl TidyFlags {
pub fn new(cfg_args: &[String]) -> Self {
let mut flags = Self::default();
for arg in cfg_args {
match arg.as_str() {
"--bless" => flags.bless = true,
_ => continue,
}
}
flags
}
}
/// Collects diagnostics from all tidy steps, and contains shared information
/// that determines how should message and logs be presented.
///
/// Since checks are executed in parallel, the context is internally synchronized, to avoid
/// all checks to lock it explicitly.
#[derive(Clone)]
pub struct DiagCtx(Arc<Mutex<DiagCtxInner>>);
pub struct TidyCtx {
tidy_flags: TidyFlags,
diag_ctx: Arc<Mutex<DiagCtxInner>>,
}
impl DiagCtx {
pub fn new(root_path: &Path, verbose: bool) -> Self {
Self(Arc::new(Mutex::new(DiagCtxInner {
running_checks: Default::default(),
finished_checks: Default::default(),
root_path: root_path.to_path_buf(),
verbose,
})))
impl TidyCtx {
pub fn new(root_path: &Path, verbose: bool, tidy_flags: TidyFlags) -> Self {
Self {
diag_ctx: Arc::new(Mutex::new(DiagCtxInner {
running_checks: Default::default(),
finished_checks: Default::default(),
root_path: root_path.to_path_buf(),
verbose,
})),
tidy_flags,
}
}
pub fn is_bless_enabled(&self) -> bool {
self.tidy_flags.bless
}
pub fn start_check<Id: Into<CheckId>>(&self, id: Id) -> RunningCheck {
let mut id = id.into();
let mut ctx = self.0.lock().unwrap();
let mut ctx = self.diag_ctx.lock().unwrap();
// Shorten path for shorter diagnostics
id.path = match id.path {
@ -38,14 +69,14 @@ impl DiagCtx {
RunningCheck {
id,
bad: false,
ctx: self.0.clone(),
ctx: self.diag_ctx.clone(),
#[cfg(test)]
errors: vec![],
}
}
pub fn into_failed_checks(self) -> Vec<FinishedCheck> {
let ctx = Arc::into_inner(self.0).unwrap().into_inner().unwrap();
let ctx = Arc::into_inner(self.diag_ctx).unwrap().into_inner().unwrap();
assert!(ctx.running_checks.is_empty(), "Some checks are still running");
ctx.finished_checks.into_iter().filter(|c| c.bad).collect()
}
@ -151,7 +182,7 @@ impl RunningCheck {
/// Useful if you want to run some functions from tidy without configuring
/// diagnostics.
pub fn new_noop() -> Self {
let ctx = DiagCtx::new(Path::new(""), false);
let ctx = TidyCtx::new(Path::new(""), false, TidyFlags::default());
ctx.start_check("noop")
}

View file

@ -2,11 +2,11 @@
use std::path::Path;
use crate::diagnostics::{CheckId, DiagCtx};
use crate::diagnostics::{CheckId, TidyCtx};
use crate::walk::{filter_dirs, walk};
pub fn check(path: &Path, diag_ctx: DiagCtx) {
let mut check = diag_ctx.start_check(CheckId::new("edition").path(path));
pub fn check(path: &Path, tidy_ctx: TidyCtx) {
let mut check = tidy_ctx.start_check(CheckId::new("edition").path(path));
walk(path, |path, _is_dir| filter_dirs(path), &mut |entry, contents| {
let file = entry.path();
let filename = file.file_name().unwrap();

View file

@ -22,7 +22,7 @@ use std::path::Path;
use regex::Regex;
use crate::diagnostics::{DiagCtx, RunningCheck};
use crate::diagnostics::{RunningCheck, TidyCtx};
use crate::walk::{filter_dirs, walk, walk_many};
const ERROR_CODES_PATH: &str = "compiler/rustc_error_codes/src/lib.rs";
@ -36,8 +36,8 @@ const IGNORE_DOCTEST_CHECK: &[&str] = &["E0464", "E0570", "E0601", "E0602", "E07
const IGNORE_UI_TEST_CHECK: &[&str] =
&["E0461", "E0465", "E0514", "E0554", "E0640", "E0717", "E0729"];
pub fn check(root_path: &Path, search_paths: &[&Path], ci_info: &crate::CiInfo, diag_ctx: DiagCtx) {
let mut check = diag_ctx.start_check("error_codes");
pub fn check(root_path: &Path, search_paths: &[&Path], ci_info: &crate::CiInfo, tidy_ctx: TidyCtx) {
let mut check = tidy_ctx.start_check("error_codes");
// Check that no error code explanation was removed.
check_removed_error_code_explanation(ci_info, &mut check);

View file

@ -4,7 +4,7 @@ use std::fs;
use std::path::Path;
use crate::deps::WorkspaceInfo;
use crate::diagnostics::DiagCtx;
use crate::diagnostics::TidyCtx;
/// List of allowed sources for packages.
const ALLOWED_SOURCES: &[&str] = &[
@ -15,8 +15,8 @@ const ALLOWED_SOURCES: &[&str] = &[
/// Checks for external package sources. `root` is the path to the directory that contains the
/// workspace `Cargo.toml`.
pub fn check(root: &Path, diag_ctx: DiagCtx) {
let mut check = diag_ctx.start_check("extdeps");
pub fn check(root: &Path, tidy_ctx: TidyCtx) {
let mut check = tidy_ctx.start_check("extdeps");
for &WorkspaceInfo { path, submodules, .. } in crate::deps::WORKSPACES {
if crate::deps::has_missing_submodule(root, submodules) {

View file

@ -24,7 +24,7 @@ use std::str::FromStr;
use std::{fmt, fs, io};
use crate::CiInfo;
use crate::diagnostics::DiagCtx;
use crate::diagnostics::TidyCtx;
mod rustdoc_js;
@ -52,12 +52,11 @@ pub fn check(
tools_path: &Path,
npm: &Path,
cargo: &Path,
bless: bool,
extra_checks: Option<&str>,
pos_args: &[String],
diag_ctx: DiagCtx,
tidy_ctx: TidyCtx,
) {
let mut check = diag_ctx.start_check("extra_checks");
let mut check = tidy_ctx.start_check("extra_checks");
if let Err(e) = check_impl(
root_path,
@ -67,9 +66,9 @@ pub fn check(
tools_path,
npm,
cargo,
bless,
extra_checks,
pos_args,
&tidy_ctx,
) {
check.error(e);
}
@ -83,12 +82,13 @@ fn check_impl(
tools_path: &Path,
npm: &Path,
cargo: &Path,
bless: bool,
extra_checks: Option<&str>,
pos_args: &[String],
tidy_ctx: &TidyCtx,
) -> Result<(), Error> {
let show_diff =
std::env::var("TIDY_PRINT_DIFF").is_ok_and(|v| v.eq_ignore_ascii_case("true") || v == "1");
let bless = tidy_ctx.is_bless_enabled();
// Split comma-separated args up
let mut lint_args = match extra_checks {

View file

@ -16,7 +16,7 @@ use std::num::NonZeroU32;
use std::path::{Path, PathBuf};
use std::{fmt, fs};
use crate::diagnostics::{DiagCtx, RunningCheck};
use crate::diagnostics::{RunningCheck, TidyCtx};
use crate::walk::{filter_dirs, filter_not_rust, walk, walk_many};
#[cfg(test)]
@ -92,9 +92,9 @@ pub fn check(
tests_path: &Path,
compiler_path: &Path,
lib_path: &Path,
diag_ctx: DiagCtx,
tidy_ctx: TidyCtx,
) -> CollectedFeatures {
let mut check = diag_ctx.start_check("features");
let mut check = tidy_ctx.start_check("features");
let mut features = collect_lang_features(compiler_path, &mut check);
assert!(!features.is_empty());

View file

@ -10,10 +10,10 @@
use std::path::Path;
use std::process::Command;
use crate::diagnostics::DiagCtx;
use crate::diagnostics::TidyCtx;
pub fn check(root_path: &Path, diag_ctx: DiagCtx) {
let mut check = diag_ctx.start_check("filenames");
pub fn check(root_path: &Path, tidy_ctx: TidyCtx) {
let mut check = tidy_ctx.start_check("filenames");
let stat_output = Command::new("git")
.arg("-C")
.arg(root_path)

View file

@ -9,7 +9,7 @@ use fluent_syntax::ast::Entry;
use fluent_syntax::parser;
use regex::Regex;
use crate::diagnostics::{CheckId, DiagCtx, RunningCheck};
use crate::diagnostics::{CheckId, RunningCheck, TidyCtx};
use crate::walk::{filter_dirs, walk};
fn message() -> &'static Regex {
@ -87,8 +87,9 @@ fn sort_messages(
out
}
pub fn check(path: &Path, bless: bool, diag_ctx: DiagCtx) {
let mut check = diag_ctx.start_check(CheckId::new("fluent_alphabetical").path(path));
pub fn check(path: &Path, tidy_ctx: TidyCtx) {
let mut check = tidy_ctx.start_check(CheckId::new("fluent_alphabetical").path(path));
let bless = tidy_ctx.is_bless_enabled();
let mut all_defined_msgs = HashMap::new();
walk(
@ -120,5 +121,5 @@ pub fn check(path: &Path, bless: bool, diag_ctx: DiagCtx) {
assert!(!all_defined_msgs.is_empty());
crate::fluent_used::check(path, all_defined_msgs, diag_ctx);
crate::fluent_used::check(path, all_defined_msgs, tidy_ctx);
}

View file

@ -4,7 +4,7 @@ use std::path::Path;
use fluent_syntax::ast::{Entry, Message, PatternElement};
use crate::diagnostics::{CheckId, DiagCtx, RunningCheck};
use crate::diagnostics::{CheckId, RunningCheck, TidyCtx};
use crate::walk::{filter_dirs, walk};
#[rustfmt::skip]
@ -53,8 +53,8 @@ fn check_lowercase(filename: &str, contents: &str, check: &mut RunningCheck) {
}
}
pub fn check(path: &Path, diag_ctx: DiagCtx) {
let mut check = diag_ctx.start_check(CheckId::new("fluent_lowercase").path(path));
pub fn check(path: &Path, tidy_ctx: TidyCtx) {
let mut check = tidy_ctx.start_check(CheckId::new("fluent_lowercase").path(path));
walk(
path,
|path, is_dir| filter_dirs(path) || (!is_dir && filter_fluent(path)),

View file

@ -4,7 +4,7 @@ use std::path::Path;
use fluent_syntax::ast::{Entry, PatternElement};
use crate::diagnostics::{CheckId, DiagCtx, RunningCheck};
use crate::diagnostics::{CheckId, RunningCheck, TidyCtx};
use crate::walk::{filter_dirs, walk};
fn filter_fluent(path: &Path) -> bool {
@ -75,8 +75,8 @@ fn find_line(haystack: &str, needle: &str) -> usize {
1
}
pub fn check(path: &Path, diag_ctx: DiagCtx) {
let mut check = diag_ctx.start_check(CheckId::new("fluent_period").path(path));
pub fn check(path: &Path, tidy_ctx: TidyCtx) {
let mut check = tidy_ctx.start_check(CheckId::new("fluent_period").path(path));
walk(
path,

View file

@ -3,7 +3,7 @@
use std::collections::HashMap;
use std::path::Path;
use crate::diagnostics::{CheckId, DiagCtx};
use crate::diagnostics::{CheckId, TidyCtx};
use crate::walk::{filter_dirs, walk};
fn filter_used_messages(
@ -28,8 +28,8 @@ fn filter_used_messages(
}
}
pub fn check(path: &Path, mut all_defined_msgs: HashMap<String, String>, diag_ctx: DiagCtx) {
let mut check = diag_ctx.start_check(CheckId::new("fluent_used").path(path));
pub fn check(path: &Path, mut all_defined_msgs: HashMap<String, String>, tidy_ctx: TidyCtx) {
let mut check = tidy_ctx.start_check(CheckId::new("fluent_used").path(path));
let mut msgs_appear_only_once = HashMap::new();
walk(path, |path, _| filter_dirs(path), &mut |_, contents| {

View file

@ -4,10 +4,10 @@
use std::path::Path;
use std::process::Command;
use crate::diagnostics::DiagCtx;
use crate::diagnostics::TidyCtx;
pub fn check(root_path: &Path, compiler_path: &Path, diag_ctx: DiagCtx) {
let mut check = diag_ctx.start_check("gcc_submodule");
pub fn check(root_path: &Path, compiler_path: &Path, tidy_ctx: TidyCtx) {
let mut check = tidy_ctx.start_check("gcc_submodule");
let cg_gcc_version_path = compiler_path.join("rustc_codegen_gcc/libgccjit.version");
let cg_gcc_version = std::fs::read_to_string(&cg_gcc_version_path)

View file

@ -2,11 +2,11 @@
use std::path::Path;
use crate::diagnostics::{CheckId, DiagCtx};
use crate::diagnostics::{CheckId, TidyCtx};
use crate::walk::*;
pub fn check(filepath: &Path, diag_ctx: DiagCtx) {
let mut check = diag_ctx.start_check(CheckId::new("known_bug").path(filepath));
pub fn check(filepath: &Path, tidy_ctx: TidyCtx) {
let mut check = tidy_ctx.start_check(CheckId::new("known_bug").path(filepath));
walk(filepath, |path, _is_dir| filter_not_rust(path), &mut |entry, contents| {
let file: &Path = entry.path();

View file

@ -12,7 +12,7 @@ use build_helper::ci::CiEnv;
use build_helper::git::{GitConfig, get_closest_upstream_commit};
use build_helper::stage0_parser::{Stage0Config, parse_stage0_file};
use crate::diagnostics::{DiagCtx, RunningCheck};
use crate::diagnostics::{RunningCheck, TidyCtx};
macro_rules! static_regex {
($re:literal) => {{
@ -52,8 +52,8 @@ pub struct CiInfo {
}
impl CiInfo {
pub fn new(diag_ctx: DiagCtx) -> Self {
let mut check = diag_ctx.start_check("CI history");
pub fn new(tidy_ctx: TidyCtx) -> Self {
let mut check = tidy_ctx.start_check("CI history");
let stage0 = parse_stage0_file();
let Stage0Config { nightly_branch, git_merge_commit_email, .. } = stage0.config;

View file

@ -11,7 +11,7 @@ use std::str::FromStr;
use std::thread::{self, ScopedJoinHandle, scope};
use std::{env, process};
use tidy::diagnostics::{COLOR_ERROR, COLOR_SUCCESS, DiagCtx, output_message};
use tidy::diagnostics::{COLOR_ERROR, COLOR_SUCCESS, TidyCtx, TidyFlags, output_message};
use tidy::*;
fn main() {
@ -46,12 +46,12 @@ fn main() {
None => (&args[..], [].as_slice()),
};
let verbose = cfg_args.iter().any(|s| *s == "--verbose");
let bless = cfg_args.iter().any(|s| *s == "--bless");
let extra_checks =
cfg_args.iter().find(|s| s.starts_with("--extra-checks=")).map(String::as_str);
let diag_ctx = DiagCtx::new(&root_path, verbose);
let ci_info = CiInfo::new(diag_ctx.clone());
let tidy_flags = TidyFlags::new(cfg_args);
let tidy_ctx = TidyCtx::new(&root_path, verbose, tidy_flags);
let ci_info = CiInfo::new(tidy_ctx.clone());
let drain_handles = |handles: &mut VecDeque<ScopedJoinHandle<'_, ()>>| {
// poll all threads for completion before awaiting the oldest one
@ -86,9 +86,9 @@ fn main() {
(@ $p:ident, name=$name:expr $(, $args:expr)* ) => {
drain_handles(&mut handles);
let diag_ctx = diag_ctx.clone();
let tidy_ctx = tidy_ctx.clone();
let handle = thread::Builder::new().name($name).spawn_scoped(s, || {
$p::check($($args, )* diag_ctx);
$p::check($($args, )* tidy_ctx);
}).unwrap();
handles.push_back(handle);
}
@ -97,15 +97,15 @@ fn main() {
check!(target_specific_tests, &tests_path);
// Checks that are done on the cargo workspace.
check!(deps, &root_path, &cargo, bless);
check!(deps, &root_path, &cargo);
check!(extdeps, &root_path);
// Checks over tests.
check!(tests_placement, &root_path);
check!(tests_revision_unpaired_stdout_stderr, &tests_path);
check!(debug_artifacts, &tests_path);
check!(ui_tests, &root_path, bless);
check!(mir_opt_tests, &tests_path, bless);
check!(ui_tests, &root_path);
check!(mir_opt_tests, &tests_path);
check!(rustdoc_gui_tests, &tests_path);
check!(rustdoc_css_themes, &librustdoc_path);
check!(rustdoc_templates, &librustdoc_path);
@ -115,7 +115,7 @@ fn main() {
// Checks that only make sense for the compiler.
check!(error_codes, &root_path, &[&compiler_path, &librustdoc_path], &ci_info);
check!(fluent_alphabetical, &compiler_path, bless);
check!(fluent_alphabetical, &compiler_path);
check!(fluent_period, &compiler_path);
check!(fluent_lowercase, &compiler_path);
check!(target_policy, &root_path);
@ -156,7 +156,7 @@ fn main() {
let collected = {
drain_handles(&mut handles);
features::check(&src_path, &tests_path, &compiler_path, &library_path, diag_ctx.clone())
features::check(&src_path, &tests_path, &compiler_path, &library_path, tidy_ctx.clone())
};
check!(unstable_book, &src_path, collected);
@ -169,13 +169,12 @@ fn main() {
&tools_path,
&npm,
&cargo,
bless,
extra_checks,
pos_args
);
});
let failed_checks = diag_ctx.into_failed_checks();
let failed_checks = tidy_ctx.into_failed_checks();
if !failed_checks.is_empty() {
let mut failed: Vec<String> =
failed_checks.into_iter().map(|c| c.id().to_string()).collect();

View file

@ -5,7 +5,7 @@ use std::path::{Path, PathBuf};
use miropt_test_tools::PanicStrategy;
use crate::diagnostics::{CheckId, DiagCtx, RunningCheck};
use crate::diagnostics::{CheckId, RunningCheck, TidyCtx};
use crate::walk::walk_no_read;
fn check_unused_files(path: &Path, bless: bool, check: &mut RunningCheck) {
@ -74,8 +74,9 @@ fn check_dash_files(path: &Path, bless: bool, check: &mut RunningCheck) {
}
}
pub fn check(path: &Path, bless: bool, diag_ctx: DiagCtx) {
let mut check = diag_ctx.start_check(CheckId::new("mir_opt_tests").path(path));
pub fn check(path: &Path, tidy_ctx: TidyCtx) {
let mut check = tidy_ctx.start_check(CheckId::new("mir_opt_tests").path(path));
let bless = tidy_ctx.is_bless_enabled();
check_unused_files(path, bless, &mut check);
check_dash_files(path, bless, &mut check);

View file

@ -32,7 +32,7 @@
use std::path::Path;
use crate::diagnostics::{CheckId, DiagCtx, RunningCheck};
use crate::diagnostics::{CheckId, RunningCheck, TidyCtx};
use crate::walk::{filter_dirs, walk};
// Paths that may contain platform-specific code.
@ -68,8 +68,8 @@ const EXCEPTION_PATHS: &[&str] = &[
"library/std/src/io/error.rs", // Repr unpacked needed for UEFI
];
pub fn check(path: &Path, diag_ctx: DiagCtx) {
let mut check = diag_ctx.start_check(CheckId::new("pal").path(path));
pub fn check(path: &Path, tidy_ctx: TidyCtx) {
let mut check = tidy_ctx.start_check(CheckId::new("pal").path(path));
// Sanity check that the complex parsing here works.
let mut saw_target_arch = false;

View file

@ -3,10 +3,10 @@
use std::path::Path;
use crate::diagnostics::{CheckId, DiagCtx, RunningCheck};
use crate::diagnostics::{CheckId, RunningCheck, TidyCtx};
pub fn check(librustdoc_path: &Path, diag_ctx: DiagCtx) {
let mut check = diag_ctx.start_check(CheckId::new("rustdoc_css_themes").path(librustdoc_path));
pub fn check(librustdoc_path: &Path, tidy_ctx: TidyCtx) {
let mut check = tidy_ctx.start_check(CheckId::new("rustdoc_css_themes").path(librustdoc_path));
let rustdoc_css = "html/static/css/rustdoc.css";
let noscript_css = "html/static/css/noscript.css";

View file

@ -2,10 +2,10 @@
use std::path::Path;
use crate::diagnostics::{CheckId, DiagCtx};
use crate::diagnostics::{CheckId, TidyCtx};
pub fn check(path: &Path, diag_ctx: DiagCtx) {
let mut check = diag_ctx.start_check(CheckId::new("rustdoc_gui_tests").path(path));
pub fn check(path: &Path, tidy_ctx: TidyCtx) {
let mut check = tidy_ctx.start_check(CheckId::new("rustdoc_gui_tests").path(path));
crate::walk::walk(
&path.join("rustdoc-gui"),

View file

@ -4,12 +4,12 @@
use std::path::Path;
use std::str::FromStr;
use crate::diagnostics::{CheckId, DiagCtx};
use crate::diagnostics::{CheckId, TidyCtx};
const RUSTDOC_JSON_TYPES: &str = "src/rustdoc-json-types";
pub fn check(src_path: &Path, ci_info: &crate::CiInfo, diag_ctx: DiagCtx) {
let mut check = diag_ctx.start_check(CheckId::new("rustdoc_json").path(src_path));
pub fn check(src_path: &Path, ci_info: &crate::CiInfo, tidy_ctx: TidyCtx) {
let mut check = tidy_ctx.start_check(CheckId::new("rustdoc_json").path(src_path));
let Some(base_commit) = &ci_info.base_commit else {
check.verbose_msg("No base commit, skipping rustdoc_json check");

View file

@ -6,14 +6,14 @@ use std::path::Path;
use ignore::DirEntry;
use crate::diagnostics::{CheckId, DiagCtx};
use crate::diagnostics::{CheckId, TidyCtx};
use crate::walk::walk;
// Array containing `("beginning of tag", "end of tag")`.
const TAGS: &[(&str, &str)] = &[("{#", "#}"), ("{%", "%}"), ("{{", "}}")];
pub fn check(librustdoc_path: &Path, diag_ctx: DiagCtx) {
let mut check = diag_ctx.start_check(CheckId::new("rustdoc_templates").path(librustdoc_path));
pub fn check(librustdoc_path: &Path, tidy_ctx: TidyCtx) {
let mut check = tidy_ctx.start_check(CheckId::new("rustdoc_templates").path(librustdoc_path));
walk(
&librustdoc_path.join("html/templates"),

View file

@ -24,7 +24,7 @@ use std::sync::LazyLock;
use regex::RegexSetBuilder;
use rustc_hash::FxHashMap;
use crate::diagnostics::{CheckId, DiagCtx};
use crate::diagnostics::{CheckId, TidyCtx};
use crate::walk::{filter_dirs, walk};
#[cfg(test)]
@ -339,8 +339,8 @@ fn is_unexplained_ignore(extension: &str, line: &str) -> bool {
true
}
pub fn check(path: &Path, diag_ctx: DiagCtx) {
let mut check = diag_ctx.start_check(CheckId::new("style").path(path));
pub fn check(path: &Path, tidy_ctx: TidyCtx) {
let mut check = tidy_ctx.start_check(CheckId::new("style").path(path));
fn skip(path: &Path, is_dir: bool) -> bool {
if path.file_name().is_some_and(|name| name.to_string_lossy().starts_with(".#")) {

View file

@ -5,7 +5,7 @@
use std::collections::HashSet;
use std::path::Path;
use crate::diagnostics::DiagCtx;
use crate::diagnostics::TidyCtx;
use crate::walk::{filter_not_rust, walk};
const TARGET_DEFINITIONS_PATH: &str = "compiler/rustc_target/src/spec/targets/";
@ -24,8 +24,8 @@ const EXCEPTIONS: &[&str] = &[
"xtensa_esp32s3_espidf",
];
pub fn check(root_path: &Path, diag_ctx: DiagCtx) {
let mut check = diag_ctx.start_check("target_policy");
pub fn check(root_path: &Path, tidy_ctx: TidyCtx) {
let mut check = tidy_ctx.start_check("target_policy");
let mut targets_to_find = HashSet::new();

View file

@ -4,7 +4,7 @@
use std::collections::BTreeMap;
use std::path::Path;
use crate::diagnostics::{CheckId, DiagCtx};
use crate::diagnostics::{CheckId, TidyCtx};
use crate::iter_header::{HeaderLine, iter_header};
use crate::walk::filter_not_rust;
@ -17,8 +17,8 @@ struct RevisionInfo<'a> {
llvm_components: Option<Vec<&'a str>>,
}
pub fn check(tests_path: &Path, diag_ctx: DiagCtx) {
let mut check = diag_ctx.start_check(CheckId::new("target-specific-tests").path(tests_path));
pub fn check(tests_path: &Path, tidy_ctx: TidyCtx) {
let mut check = tidy_ctx.start_check(CheckId::new("target-specific-tests").path(tests_path));
crate::walk::walk(tests_path, |path, _is_dir| filter_not_rust(path), &mut |entry, content| {
if content.contains("// ignore-tidy-target-specific-tests") {

View file

@ -1,12 +1,12 @@
use std::path::Path;
use crate::diagnostics::DiagCtx;
use crate::diagnostics::TidyCtx;
const FORBIDDEN_PATH: &str = "src/test";
const ALLOWED_PATH: &str = "tests";
pub fn check(root_path: &Path, diag_ctx: DiagCtx) {
let mut check = diag_ctx.start_check("tests_placement");
pub fn check(root_path: &Path, tidy_ctx: TidyCtx) {
let mut check = tidy_ctx.start_check("tests_placement");
if root_path.join(FORBIDDEN_PATH).exists() {
check.error(format!(

View file

@ -4,7 +4,7 @@ use std::collections::{BTreeMap, BTreeSet};
use std::ffi::OsStr;
use std::path::Path;
use crate::diagnostics::{CheckId, DiagCtx};
use crate::diagnostics::{CheckId, TidyCtx};
use crate::iter_header::*;
use crate::walk::*;
@ -22,8 +22,8 @@ const IGNORES: &[&str] = &[
const EXTENSIONS: &[&str] = &["stdout", "stderr"];
const SPECIAL_TEST: &str = "tests/ui/command/need-crate-arg-ignore-tidy.x.rs";
pub fn check(tests_path: &Path, diag_ctx: DiagCtx) {
let mut check = diag_ctx
pub fn check(tests_path: &Path, tidy_ctx: TidyCtx) {
let mut check = tidy_ctx
.start_check(CheckId::new("tests_revision_unpaired_stdout_stderr").path(tests_path));
// Recurse over subdirectories under `tests/`

View file

@ -4,10 +4,10 @@ use std::path::Path;
use toml::Value;
use crate::diagnostics::DiagCtx;
use crate::diagnostics::TidyCtx;
pub fn check(path: &Path, diag_ctx: DiagCtx) {
let mut check = diag_ctx.start_check("triagebot");
pub fn check(path: &Path, tidy_ctx: TidyCtx) {
let mut check = tidy_ctx.start_check("triagebot");
let triagebot_path = path.join("triagebot.toml");
// This check is mostly to catch broken path filters *within* `triagebot.toml`, and not enforce

View file

@ -7,16 +7,17 @@ use std::fs;
use std::io::Write;
use std::path::{Path, PathBuf};
use crate::diagnostics::{CheckId, DiagCtx, RunningCheck};
use crate::diagnostics::{CheckId, RunningCheck, TidyCtx};
const ISSUES_TXT_HEADER: &str = r#"============================================================
NOTHING SHOULD EVER BE ADDED TO THIS LIST
============================================================
"#;
pub fn check(root_path: &Path, bless: bool, diag_ctx: DiagCtx) {
pub fn check(root_path: &Path, tidy_ctx: TidyCtx) {
let path = &root_path.join("tests");
let mut check = diag_ctx.start_check(CheckId::new("ui_tests").path(path));
let mut check = tidy_ctx.start_check(CheckId::new("ui_tests").path(path));
let bless = tidy_ctx.is_bless_enabled();
// the list of files in ui tests that are allowed to start with `issue-XXXX`
// BTreeSet because we would like a stable ordering so --bless works

View file

@ -11,11 +11,11 @@
use std::path::Path;
use crate::diagnostics::{CheckId, DiagCtx};
use crate::diagnostics::{CheckId, TidyCtx};
use crate::walk::{filter_dirs, walk};
pub fn check(root_path: &Path, stdlib: bool, diag_ctx: DiagCtx) {
let mut check = diag_ctx.start_check(CheckId::new("unit_tests").path(root_path));
pub fn check(root_path: &Path, stdlib: bool, tidy_ctx: TidyCtx) {
let mut check = tidy_ctx.start_check(CheckId::new("unit_tests").path(root_path));
let skip = move |path: &Path, is_dir| {
let file_name = path.file_name().unwrap_or_default();

View file

@ -12,12 +12,12 @@ use std::sync::OnceLock;
use ignore::DirEntry;
use regex::Regex;
use crate::diagnostics::{CheckId, DiagCtx, RunningCheck};
use crate::diagnostics::{CheckId, RunningCheck, TidyCtx};
use crate::iter_header::{HeaderLine, iter_header};
use crate::walk::{filter_dirs, filter_not_rust, walk};
pub fn check(tests_path: &Path, diag_ctx: DiagCtx) {
let mut check = diag_ctx.start_check(CheckId::new("unknown_revision").path(tests_path));
pub fn check(tests_path: &Path, tidy_ctx: TidyCtx) {
let mut check = tidy_ctx.start_check(CheckId::new("unknown_revision").path(tests_path));
walk(
tests_path,
|path, is_dir| {

View file

@ -2,7 +2,7 @@ use std::collections::BTreeSet;
use std::fs;
use std::path::{Path, PathBuf};
use crate::diagnostics::{DiagCtx, RunningCheck};
use crate::diagnostics::{RunningCheck, TidyCtx};
use crate::features::{CollectedFeatures, Features, Status};
pub const PATH_STR: &str = "doc/unstable-book";
@ -85,8 +85,8 @@ fn maybe_suggest_dashes(names: &BTreeSet<String>, feature_name: &str, check: &mu
}
}
pub fn check(path: &Path, features: CollectedFeatures, diag_ctx: DiagCtx) {
let mut check = diag_ctx.start_check("unstable_book");
pub fn check(path: &Path, features: CollectedFeatures, tidy_ctx: TidyCtx) {
let mut check = tidy_ctx.start_check("unstable_book");
let lang_features = features.lang;
let lib_features = features

View file

@ -3,10 +3,10 @@ use std::process::{Command, Stdio};
use semver::Version;
use crate::diagnostics::{CheckId, DiagCtx};
use crate::diagnostics::{CheckId, TidyCtx};
pub fn check(root: &Path, cargo: &Path, diag_ctx: DiagCtx) {
let mut check = diag_ctx.start_check(CheckId::new("x_version").path(root));
pub fn check(root: &Path, cargo: &Path, tidy_ctx: TidyCtx) {
let mut check = tidy_ctx.start_check(CheckId::new("x_version").path(root));
let cargo_list = Command::new(cargo).args(["install", "--list"]).stdout(Stdio::piped()).spawn();
let child = match cargo_list {

View file

@ -25,7 +25,7 @@ trait Copy {}
impl<T> Copy for *mut T {}
#[rustc_intrinsic]
fn size_of<T>() -> usize {
const fn size_of<T>() -> usize {
loop {}
}
@ -40,7 +40,7 @@ unsafe fn catch_unwind(
#[no_mangle]
pub fn ptr_size() -> usize {
// CHECK: ret [[PTR_SIZE:.*]]
size_of::<*mut u8>()
const { size_of::<*mut u8>() }
}
// CHECK-LABEL: @test_catch_unwind

View file

@ -24,7 +24,7 @@ trait Copy {}
impl<T> Copy for *mut T {}
#[rustc_intrinsic]
fn size_of<T>() -> usize {
const fn size_of<T>() -> usize {
loop {}
}
#[rustc_intrinsic]
@ -38,7 +38,7 @@ unsafe fn catch_unwind(
#[no_mangle]
pub fn ptr_size() -> usize {
// CHECK: ret [[PTR_SIZE:.*]]
size_of::<*mut u8>()
const { size_of::<*mut u8>() }
}
// CHECK-LABEL: @test_catch_unwind

View file

@ -1,6 +1,10 @@
//
//@ compile-flags: -C no-prepopulate-passes
//@ revisions: LLVM21 LLVM22
//@ [LLVM22] min-llvm-version: 22
//@ [LLVM21] max-llvm-major-version: 21
// ignore-tidy-linelength
#![crate_type = "lib"]
#![feature(repr_simd, core_intrinsics)]
@ -24,7 +28,8 @@ pub unsafe fn gather_f32x2(
) -> 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>
// CHECK: call <2 x float> @llvm.masked.gather.v2f32.v2p0(<2 x ptr> {{.*}}, i32 {{.*}}, <2 x i1> [[B]], <2 x float> {{.*}})
// LLVM21: call <2 x float> @llvm.masked.gather.v2f32.v2p0(<2 x ptr> {{.*}}, i32 {{.*}}, <2 x i1> [[B]], <2 x float> {{.*}})
// LLVM22: call <2 x float> @llvm.masked.gather.v2f32.v2p0(<2 x ptr> align {{.*}} {{.*}}, <2 x i1> [[B]], <2 x float> {{.*}})
simd_gather(values, pointers, mask)
}
@ -37,7 +42,8 @@ pub unsafe fn gather_f32x2_unsigned(
) -> 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>
// CHECK: call <2 x float> @llvm.masked.gather.v2f32.v2p0(<2 x ptr> {{.*}}, i32 {{.*}}, <2 x i1> [[B]], <2 x float> {{.*}})
// LLVM21: call <2 x float> @llvm.masked.gather.v2f32.v2p0(<2 x ptr> {{.*}}, i32 {{.*}}, <2 x i1> [[B]], <2 x float> {{.*}})
// LLVM22: call <2 x float> @llvm.masked.gather.v2f32.v2p0(<2 x ptr> align {{.*}} {{.*}}, <2 x i1> [[B]], <2 x float> {{.*}})
simd_gather(values, pointers, mask)
}
@ -50,6 +56,7 @@ pub unsafe fn gather_pf32x2(
) -> Vec2<*const 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>
// CHECK: call <2 x ptr> @llvm.masked.gather.v2p0.v2p0(<2 x ptr> {{.*}}, i32 {{.*}}, <2 x i1> [[B]], <2 x ptr> {{.*}})
// LLVM21: call <2 x ptr> @llvm.masked.gather.v2p0.v2p0(<2 x ptr> {{.*}}, i32 {{.*}}, <2 x i1> [[B]], <2 x ptr> {{.*}})
// LLVM22: call <2 x ptr> @llvm.masked.gather.v2p0.v2p0(<2 x ptr> align {{.*}} {{.*}}, <2 x i1> [[B]], <2 x ptr> {{.*}})
simd_gather(values, pointers, mask)
}

View file

@ -1,4 +1,8 @@
//@ compile-flags: -C no-prepopulate-passes
//@ revisions: LLVM21 LLVM22
//@ [LLVM22] min-llvm-version: 22
//@ [LLVM21] max-llvm-major-version: 21
// ignore-tidy-linelength
#![crate_type = "lib"]
#![feature(repr_simd, core_intrinsics)]
@ -18,7 +22,8 @@ pub type Vec4<T> = Simd<T, 4>;
pub unsafe fn load_f32x2(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>
// CHECK: call <2 x float> @llvm.masked.load.v2f32.p0(ptr {{.*}}, i32 4, <2 x i1> [[B]], <2 x float> {{.*}})
// 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)
}
@ -31,7 +36,8 @@ pub unsafe fn load_f32x2_unsigned(
) -> 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>
// CHECK: call <2 x float> @llvm.masked.load.v2f32.p0(ptr {{.*}}, i32 4, <2 x i1> [[B]], <2 x float> {{.*}})
// 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)
}
@ -44,6 +50,7 @@ pub unsafe fn load_pf32x4(
) -> Vec4<*const f32> {
// CHECK: [[A:%[0-9]+]] = lshr <4 x i32> {{.*}}, {{<i32 31, i32 31, i32 31, i32 31>|splat \(i32 31\)}}
// CHECK: [[B:%[0-9]+]] = trunc <4 x i32> [[A]] to <4 x i1>
// CHECK: call <4 x ptr> @llvm.masked.load.v4p0.p0(ptr {{.*}}, i32 {{.*}}, <4 x i1> [[B]], <4 x ptr> {{.*}})
// 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)
}

Some files were not shown because too many files have changed in this diff Show more