From fba4177d0fd07fc3f2d2202b39199ab913fb6164 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Mon, 23 Jun 2025 16:08:01 +0000 Subject: [PATCH 01/36] Simplify assignments. --- compiler/rustc_mir_transform/src/gvn.rs | 61 +++++++++++++------------ 1 file changed, 33 insertions(+), 28 deletions(-) diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index bda71ceaa551a..b19323239b6f8 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -1755,7 +1755,7 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, 'tcx> { fn visit_place(&mut self, place: &mut Place<'tcx>, context: PlaceContext, location: Location) { self.simplify_place_projection(place, location); - if context.is_mutating_use() && !place.projection.is_empty() { + if context.is_mutating_use() && place.is_indirect() { // Non-local mutation maybe invalidate deref. self.invalidate_derefs(); } @@ -1767,36 +1767,41 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, 'tcx> { self.super_operand(operand, location); } - fn visit_statement(&mut self, stmt: &mut Statement<'tcx>, location: Location) { - if let StatementKind::Assign(box (ref mut lhs, ref mut rvalue)) = stmt.kind { - self.simplify_place_projection(lhs, location); - - let value = self.simplify_rvalue(lhs, rvalue, location); - let value = if let Some(local) = lhs.as_local() - && self.ssa.is_ssa(local) - // FIXME(#112651) `rvalue` may have a subtype to `local`. We can only mark - // `local` as reusable if we have an exact type match. - && self.local_decls[local].ty == rvalue.ty(self.local_decls, self.tcx) + fn visit_assign( + &mut self, + lhs: &mut Place<'tcx>, + rvalue: &mut Rvalue<'tcx>, + location: Location, + ) { + self.simplify_place_projection(lhs, location); + + let value = self.simplify_rvalue(lhs, rvalue, location); + if let Some(value) = value { + if let Some(const_) = self.try_as_constant(value) { + *rvalue = Rvalue::Use(Operand::Constant(Box::new(const_))); + } else if let Some(place) = self.try_as_place(value, location, false) + && *rvalue != Rvalue::Use(Operand::Move(place)) + && *rvalue != Rvalue::Use(Operand::Copy(place)) { - let value = value.unwrap_or_else(|| self.new_opaque()); - self.assign(local, value); - Some(value) - } else { - value - }; - if let Some(value) = value { - if let Some(const_) = self.try_as_constant(value) { - *rvalue = Rvalue::Use(Operand::Constant(Box::new(const_))); - } else if let Some(place) = self.try_as_place(value, location, false) - && *rvalue != Rvalue::Use(Operand::Move(place)) - && *rvalue != Rvalue::Use(Operand::Copy(place)) - { - *rvalue = Rvalue::Use(Operand::Copy(place)); - self.reused_locals.insert(place.local); - } + *rvalue = Rvalue::Use(Operand::Copy(place)); + self.reused_locals.insert(place.local); } } - self.super_statement(stmt, location); + + if lhs.is_indirect() { + // Non-local mutation maybe invalidate deref. + self.invalidate_derefs(); + } + + if let Some(local) = lhs.as_local() + && self.ssa.is_ssa(local) + // FIXME(#112651) `rvalue` may have a subtype to `local`. We can only mark + // `local` as reusable if we have an exact type match. + && self.local_decls[local].ty == rvalue.ty(self.local_decls, self.tcx) + { + let value = value.unwrap_or_else(|| self.new_opaque()); + self.assign(local, value); + } } fn visit_terminator(&mut self, terminator: &mut Terminator<'tcx>, location: Location) { From ac70dc85e74d789b82b8352a895335b18e1a569e Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Mon, 30 Jun 2025 18:26:37 +0000 Subject: [PATCH 02/36] Introduce Value::RawPtr as it behaves differently from other aggregates. --- compiler/rustc_mir_transform/src/gvn.rs | 122 +++++++++++++----------- 1 file changed, 67 insertions(+), 55 deletions(-) diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index b19323239b6f8..b59b73531129e 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -163,12 +163,6 @@ enum AggregateTy<'tcx> { Array, Tuple, Def(DefId, ty::GenericArgsRef<'tcx>), - RawPtr { - /// Needed for cast propagation. - data_pointer_ty: Ty<'tcx>, - /// The data pointer can be anything thin, so doesn't determine the output. - output_pointer_ty: Ty<'tcx>, - }, } #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] @@ -194,6 +188,17 @@ enum Value<'tcx> { /// An aggregate value, either tuple/closure/struct/enum. /// This does not contain unions, as we cannot reason with the value. Aggregate(AggregateTy<'tcx>, VariantIdx, Vec), + /// A raw pointer aggregate built from a thin pointer and metadata. + RawPtr { + /// Thin pointer component. This is field 0 in MIR. + pointer: VnIndex, + /// Metadata component. This is field 1 in MIR. + metadata: VnIndex, + /// Needed for cast propagation. + data_pointer_ty: Ty<'tcx>, + /// The data pointer can be anything thin, so doesn't determine the output. + output_pointer_ty: Ty<'tcx>, + }, /// This corresponds to a `[value; count]` expression. Repeat(VnIndex, ty::Const<'tcx>), /// The address of a place. @@ -402,22 +407,11 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { AggregateTy::Def(def_id, args) => { self.tcx.type_of(def_id).instantiate(self.tcx, args) } - AggregateTy::RawPtr { output_pointer_ty, .. } => output_pointer_ty, }; let variant = if ty.is_enum() { Some(variant) } else { None }; let ty = self.ecx.layout_of(ty).ok()?; if ty.is_zst() { ImmTy::uninit(ty).into() - } else if matches!(kind, AggregateTy::RawPtr { .. }) { - // Pointers don't have fields, so don't `project_field` them. - let data = self.ecx.read_pointer(fields[0]).discard_err()?; - let meta = if fields[1].layout.is_zst() { - MemPlaceMeta::None - } else { - MemPlaceMeta::Meta(self.ecx.read_scalar(fields[1]).discard_err()?) - }; - let ptr_imm = Immediate::new_pointer_with_meta(data, meta, &self.ecx); - ImmTy::from_immediate(ptr_imm, ty).into() } else if matches!( ty.backend_repr, BackendRepr::Scalar(..) | BackendRepr::ScalarPair(..) @@ -446,6 +440,22 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { return None; } } + RawPtr { pointer, metadata, output_pointer_ty, data_pointer_ty: _ } => { + let pointer = self.evaluated[pointer].as_ref()?; + let metadata = self.evaluated[metadata].as_ref()?; + let output_pointer_ty = self.ecx.layout_of(output_pointer_ty).ok()?; + debug_assert!(!output_pointer_ty.is_zst()); + + // Pointers don't have fields, so don't `project_field` them. + let data = self.ecx.read_pointer(pointer).discard_err()?; + let meta = if metadata.layout.is_zst() { + MemPlaceMeta::None + } else { + MemPlaceMeta::Meta(self.ecx.read_scalar(metadata).discard_err()?) + }; + let ptr_imm = Immediate::new_pointer_with_meta(data, meta, &self.ecx); + ImmTy::from_immediate(ptr_imm, output_pointer_ty).into() + } Projection(base, elem) => { let value = self.evaluated[base].as_ref()?; @@ -1026,7 +1036,12 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } } - let (mut ty, variant_index) = match *kind { + let fields: Vec<_> = field_ops + .iter_mut() + .map(|op| self.simplify_operand(op, location).unwrap_or_else(|| self.new_opaque())) + .collect(); + + let (ty, variant_index) = match *kind { AggregateKind::Array(..) => { assert!(!field_ops.is_empty()); (AggregateTy::Array, FIRST_VARIANT) @@ -1045,41 +1060,41 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { AggregateKind::Adt(_, _, _, _, Some(_)) => return None, AggregateKind::RawPtr(pointee_ty, mtbl) => { assert_eq!(field_ops.len(), 2); - let data_pointer_ty = field_ops[FieldIdx::ZERO].ty(self.local_decls, self.tcx); + let mut data_pointer_ty = field_ops[FieldIdx::ZERO].ty(self.local_decls, self.tcx); let output_pointer_ty = Ty::new_ptr(self.tcx, pointee_ty, mtbl); - (AggregateTy::RawPtr { data_pointer_ty, output_pointer_ty }, FIRST_VARIANT) - } - }; - - let mut fields: Vec<_> = field_ops - .iter_mut() - .map(|op| self.simplify_operand(op, location).unwrap_or_else(|| self.new_opaque())) - .collect(); - if let AggregateTy::RawPtr { data_pointer_ty, output_pointer_ty } = &mut ty { - let mut was_updated = false; + let [mut pointer, metadata] = fields.try_into().unwrap(); + + // Any thin pointer of matching mutability is fine as the data pointer. + let mut was_updated = false; + while let Value::Cast { + kind: CastKind::PtrToPtr, + value: cast_value, + from: cast_from, + to: _, + } = self.get(pointer) + && let ty::RawPtr(from_pointee_ty, from_mtbl) = cast_from.kind() + && let ty::RawPtr(_, output_mtbl) = output_pointer_ty.kind() + && from_mtbl == output_mtbl + && from_pointee_ty.is_sized(self.tcx, self.typing_env()) + { + pointer = *cast_value; + data_pointer_ty = *cast_from; + was_updated = true; + } - // Any thin pointer of matching mutability is fine as the data pointer. - while let Value::Cast { - kind: CastKind::PtrToPtr, - value: cast_value, - from: cast_from, - to: _, - } = self.get(fields[0]) - && let ty::RawPtr(from_pointee_ty, from_mtbl) = cast_from.kind() - && let ty::RawPtr(_, output_mtbl) = output_pointer_ty.kind() - && from_mtbl == output_mtbl - && from_pointee_ty.is_sized(self.tcx, self.typing_env()) - { - fields[0] = *cast_value; - *data_pointer_ty = *cast_from; - was_updated = true; - } + if was_updated && let Some(op) = self.try_as_operand(pointer, location) { + field_ops[FieldIdx::ZERO] = op; + } - if was_updated && let Some(op) = self.try_as_operand(fields[0], location) { - field_ops[FieldIdx::ZERO] = op; + return Some(self.insert(Value::RawPtr { + output_pointer_ty, + data_pointer_ty, + pointer, + metadata, + })); } - } + }; if let AggregateTy::Array = ty && fields.len() > 4 @@ -1165,9 +1180,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { (UnOp::Not, Value::BinaryOp(BinOp::Ne, lhs, rhs)) => { Value::BinaryOp(BinOp::Eq, *lhs, *rhs) } - (UnOp::PtrMetadata, Value::Aggregate(AggregateTy::RawPtr { .. }, _, fields)) => { - return Some(fields[1]); - } + (UnOp::PtrMetadata, Value::RawPtr { metadata, .. }) => return Some(*metadata), // We have an unsizing cast, which assigns the length to wide pointer metadata. ( UnOp::PtrMetadata, @@ -1399,16 +1412,15 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { // If a cast just casts away the metadata again, then we can get it by // casting the original thin pointer passed to `from_raw_parts` if let PtrToPtr = kind - && let Value::Aggregate(AggregateTy::RawPtr { data_pointer_ty, .. }, _, fields) = - self.get(value) + && let Value::RawPtr { data_pointer_ty, pointer, .. } = self.get(value) && let ty::RawPtr(to_pointee, _) = to.kind() && to_pointee.is_sized(self.tcx, self.typing_env()) { from = *data_pointer_ty; - value = fields[0]; + value = *pointer; was_updated_this_iteration = true; if *data_pointer_ty == to { - return Some(fields[0]); + return Some(*pointer); } } From be41333604c15acef2766619beac604bb3726538 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Mon, 30 Jun 2025 19:27:27 +0000 Subject: [PATCH 03/36] Store a full Ty with each Value. --- compiler/rustc_mir_transform/src/gvn.rs | 244 ++++++++---------- tests/mir-opt/const_prop/transmute.rs | 2 +- ...ransmute.unreachable_direct.GVN.32bit.diff | 2 +- ...ransmute.unreachable_direct.GVN.64bit.diff | 2 +- ...generic_cast_metadata.GVN.panic-abort.diff | 3 +- ...eneric_cast_metadata.GVN.panic-unwind.diff | 3 +- tests/mir-opt/gvn.rs | 2 +- 7 files changed, 122 insertions(+), 136 deletions(-) diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index b59b73531129e..3e4c05b355f6b 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -130,7 +130,7 @@ impl<'tcx> crate::MirPass<'tcx> for GVN { let mut state = VnState::new(tcx, body, typing_env, &ssa, dominators, &body.local_decls); for local in body.args_iter().filter(|&local| ssa.is_ssa(local)) { - let opaque = state.new_opaque(); + let opaque = state.new_opaque(body.local_decls[local].ty); state.assign(local, opaque); } @@ -238,7 +238,7 @@ struct VnState<'body, 'tcx> { /// Locals that are assigned that value. // This vector does not hold all the values of `VnIndex` that we create. rev_locals: IndexVec>, - values: FxIndexSet>, + values: FxIndexSet<(Value<'tcx>, Ty<'tcx>)>, /// Values evaluated as constants if possible. evaluated: IndexVec>>, /// Counter to generate different values. @@ -287,8 +287,8 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } #[instrument(level = "trace", skip(self), ret)] - fn insert(&mut self, value: Value<'tcx>) -> VnIndex { - let (index, new) = self.values.insert_full(value); + fn insert(&mut self, ty: Ty<'tcx>, value: Value<'tcx>) -> VnIndex { + let (index, new) = self.values.insert_full((value, ty)); let index = VnIndex::from_usize(index); if new { // Grow `evaluated` and `rev_locals` here to amortize the allocations. @@ -310,20 +310,33 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { /// Create a new `Value` for which we have no information at all, except that it is distinct /// from all the others. #[instrument(level = "trace", skip(self), ret)] - fn new_opaque(&mut self) -> VnIndex { + fn new_opaque(&mut self, ty: Ty<'tcx>) -> VnIndex { let value = Value::Opaque(self.next_opaque()); - self.insert(value) + self.insert(ty, value) } /// Create a new `Value::Address` distinct from all the others. #[instrument(level = "trace", skip(self), ret)] fn new_pointer(&mut self, place: Place<'tcx>, kind: AddressKind) -> VnIndex { + let pty = place.ty(self.local_decls, self.tcx).ty; + let ty = match kind { + AddressKind::Ref(bk) => { + Ty::new_ref(self.tcx, self.tcx.lifetimes.re_erased, pty, bk.to_mutbl_lossy()) + } + AddressKind::Address(mutbl) => Ty::new_ptr(self.tcx, pty, mutbl.to_mutbl_lossy()), + }; let value = Value::Address { place, kind, provenance: self.next_opaque() }; - self.insert(value) + self.insert(ty, value) } + #[inline] fn get(&self, index: VnIndex) -> &Value<'tcx> { - self.values.get_index(index.as_usize()).unwrap() + &self.values.get_index(index.as_usize()).unwrap().0 + } + + #[inline] + fn ty(&self, index: VnIndex) -> Ty<'tcx> { + self.values.get_index(index.as_usize()).unwrap().1 } /// Record that `local` is assigned `value`. `local` must be SSA. @@ -346,29 +359,29 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { debug_assert_ne!(disambiguator, 0); disambiguator }; - self.insert(Value::Constant { value, disambiguator }) + self.insert(value.ty(), Value::Constant { value, disambiguator }) } fn insert_bool(&mut self, flag: bool) -> VnIndex { // Booleans are deterministic. let value = Const::from_bool(self.tcx, flag); debug_assert!(value.is_deterministic()); - self.insert(Value::Constant { value, disambiguator: 0 }) + self.insert(self.tcx.types.bool, Value::Constant { value, disambiguator: 0 }) } - fn insert_scalar(&mut self, scalar: Scalar, ty: Ty<'tcx>) -> VnIndex { + fn insert_scalar(&mut self, ty: Ty<'tcx>, scalar: Scalar) -> VnIndex { // Scalars are deterministic. let value = Const::from_scalar(self.tcx, scalar, ty); debug_assert!(value.is_deterministic()); - self.insert(Value::Constant { value, disambiguator: 0 }) + self.insert(ty, Value::Constant { value, disambiguator: 0 }) } - fn insert_tuple(&mut self, values: Vec) -> VnIndex { - self.insert(Value::Aggregate(AggregateTy::Tuple, VariantIdx::ZERO, values)) + fn insert_tuple(&mut self, ty: Ty<'tcx>, values: Vec) -> VnIndex { + self.insert(ty, Value::Aggregate(AggregateTy::Tuple, VariantIdx::ZERO, values)) } - fn insert_deref(&mut self, value: VnIndex) -> VnIndex { - let value = self.insert(Value::Projection(value, ProjectionElem::Deref)); + fn insert_deref(&mut self, ty: Ty<'tcx>, value: VnIndex) -> VnIndex { + let value = self.insert(ty, Value::Projection(value, ProjectionElem::Deref)); self.derefs.push(value); value } @@ -376,14 +389,18 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { fn invalidate_derefs(&mut self) { for deref in std::mem::take(&mut self.derefs) { let opaque = self.next_opaque(); - *self.values.get_index_mut2(deref.index()).unwrap() = Value::Opaque(opaque); + self.values.get_index_mut2(deref.index()).unwrap().0 = Value::Opaque(opaque); } } #[instrument(level = "trace", skip(self), ret)] fn eval_to_const(&mut self, value: VnIndex) -> Option> { use Value::*; + let ty = self.ty(value); + let ty = self.ecx.layout_of(ty).ok()?; let op = match *self.get(value) { + _ if ty.is_zst() => ImmTy::uninit(ty).into(), + Opaque(_) => return None, // Do not bother evaluating repeat expressions. This would uselessly consume memory. Repeat(..) => return None, @@ -391,31 +408,14 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { Constant { ref value, disambiguator: _ } => { self.ecx.eval_mir_constant(value, DUMMY_SP, None).discard_err()? } - Aggregate(kind, variant, ref fields) => { + Aggregate(_, variant, ref fields) => { let fields = fields .iter() .map(|&f| self.evaluated[f].as_ref()) .collect::>>()?; - let ty = match kind { - AggregateTy::Array => { - assert!(fields.len() > 0); - Ty::new_array(self.tcx, fields[0].layout.ty, fields.len() as u64) - } - AggregateTy::Tuple => { - Ty::new_tup_from_iter(self.tcx, fields.iter().map(|f| f.layout.ty)) - } - AggregateTy::Def(def_id, args) => { - self.tcx.type_of(def_id).instantiate(self.tcx, args) - } - }; - let variant = if ty.is_enum() { Some(variant) } else { None }; - let ty = self.ecx.layout_of(ty).ok()?; - if ty.is_zst() { - ImmTy::uninit(ty).into() - } else if matches!( - ty.backend_repr, - BackendRepr::Scalar(..) | BackendRepr::ScalarPair(..) - ) { + let variant = if ty.ty.is_enum() { Some(variant) } else { None }; + if matches!(ty.backend_repr, BackendRepr::Scalar(..) | BackendRepr::ScalarPair(..)) + { let dest = self.ecx.allocate(ty, MemoryKind::Stack).discard_err()?; let variant_dest = if let Some(variant) = variant { self.ecx.project_downcast(&dest, variant).discard_err()? @@ -440,11 +440,9 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { return None; } } - RawPtr { pointer, metadata, output_pointer_ty, data_pointer_ty: _ } => { + RawPtr { pointer, metadata, output_pointer_ty: _, data_pointer_ty: _ } => { let pointer = self.evaluated[pointer].as_ref()?; let metadata = self.evaluated[metadata].as_ref()?; - let output_pointer_ty = self.ecx.layout_of(output_pointer_ty).ok()?; - debug_assert!(!output_pointer_ty.is_zst()); // Pointers don't have fields, so don't `project_field` them. let data = self.ecx.read_pointer(pointer).discard_err()?; @@ -454,7 +452,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { MemPlaceMeta::Meta(self.ecx.read_scalar(metadata).discard_err()?) }; let ptr_imm = Immediate::new_pointer_with_meta(data, meta, &self.ecx); - ImmTy::from_immediate(ptr_imm, output_pointer_ty).into() + ImmTy::from_immediate(ptr_imm, ty).into() } Projection(base, elem) => { @@ -481,7 +479,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { }; self.ecx.project(value, elem).discard_err()? } - Address { place, kind, provenance: _ } => { + Address { place, kind: _, provenance: _ } => { if !place.is_indirect_first_projection() { return None; } @@ -497,19 +495,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { mplace = self.ecx.project(&mplace, proj).discard_err()?; } let pointer = mplace.to_ref(&self.ecx); - let ty = match kind { - AddressKind::Ref(bk) => Ty::new_ref( - self.tcx, - self.tcx.lifetimes.re_erased, - mplace.layout.ty, - bk.to_mutbl_lossy(), - ), - AddressKind::Address(mutbl) => { - Ty::new_ptr(self.tcx, mplace.layout.ty, mutbl.to_mutbl_lossy()) - } - }; - let layout = self.ecx.layout_of(ty).ok()?; - ImmTy::from_immediate(pointer, layout).into() + ImmTy::from_immediate(pointer, ty).into() } Discriminant(base) => { @@ -521,32 +507,28 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } Len(slice) => { let slice = self.evaluated[slice].as_ref()?; - let usize_layout = self.ecx.layout_of(self.tcx.types.usize).unwrap(); let len = slice.len(&self.ecx).discard_err()?; - let imm = ImmTy::from_uint(len, usize_layout); - imm.into() + ImmTy::from_uint(len, ty).into() } - NullaryOp(null_op, ty) => { - let layout = self.ecx.layout_of(ty).ok()?; + NullaryOp(null_op, arg_ty) => { + let arg_layout = self.ecx.layout_of(arg_ty).ok()?; if let NullOp::SizeOf | NullOp::AlignOf = null_op - && layout.is_unsized() + && arg_layout.is_unsized() { return None; } let val = match null_op { - NullOp::SizeOf => layout.size.bytes(), - NullOp::AlignOf => layout.align.abi.bytes(), + NullOp::SizeOf => arg_layout.size.bytes(), + NullOp::AlignOf => arg_layout.align.abi.bytes(), NullOp::OffsetOf(fields) => self .ecx .tcx - .offset_of_subfield(self.typing_env(), layout, fields.iter()) + .offset_of_subfield(self.typing_env(), arg_layout, fields.iter()) .bytes(), NullOp::UbChecks => return None, NullOp::ContractChecks => return None, }; - let usize_layout = self.ecx.layout_of(self.tcx.types.usize).unwrap(); - let imm = ImmTy::from_uint(val, usize_layout); - imm.into() + ImmTy::from_uint(val, ty).into() } UnaryOp(un_op, operand) => { let operand = self.evaluated[operand].as_ref()?; @@ -562,30 +544,27 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { let val = self.ecx.binary_op(bin_op, &lhs, &rhs).discard_err()?; val.into() } - Cast { kind, value, from: _, to } => match kind { + Cast { kind, value, from: _, to: _ } => match kind { CastKind::IntToInt | CastKind::IntToFloat => { let value = self.evaluated[value].as_ref()?; let value = self.ecx.read_immediate(value).discard_err()?; - let to = self.ecx.layout_of(to).ok()?; - let res = self.ecx.int_to_int_or_float(&value, to).discard_err()?; + let res = self.ecx.int_to_int_or_float(&value, ty).discard_err()?; res.into() } CastKind::FloatToFloat | CastKind::FloatToInt => { let value = self.evaluated[value].as_ref()?; let value = self.ecx.read_immediate(value).discard_err()?; - let to = self.ecx.layout_of(to).ok()?; - let res = self.ecx.float_to_float_or_int(&value, to).discard_err()?; + let res = self.ecx.float_to_float_or_int(&value, ty).discard_err()?; res.into() } CastKind::Transmute => { let value = self.evaluated[value].as_ref()?; - let to = self.ecx.layout_of(to).ok()?; // `offset` for immediates generally only supports projections that match the // type of the immediate. However, as a HACK, we exploit that it can also do // limited transmutes: it only works between types with the same layout, and // cannot transmute pointers to integers. if value.as_mplace_or_imm().is_right() { - let can_transmute = match (value.layout.backend_repr, to.backend_repr) { + let can_transmute = match (value.layout.backend_repr, ty.backend_repr) { (BackendRepr::Scalar(s1), BackendRepr::Scalar(s2)) => { s1.size(&self.ecx) == s2.size(&self.ecx) && !matches!(s1.primitive(), Primitive::Pointer(..)) @@ -605,13 +584,12 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { return None; } } - value.offset(Size::ZERO, to, &self.ecx).discard_err()? + value.offset(Size::ZERO, ty, &self.ecx).discard_err()? } CastKind::PointerCoercion(ty::adjustment::PointerCoercion::Unsize, _) => { let src = self.evaluated[value].as_ref()?; - let to = self.ecx.layout_of(to).ok()?; - let dest = self.ecx.allocate(to, MemoryKind::Stack).discard_err()?; - self.ecx.unsize_into(src, to, &dest.clone().into()).discard_err()?; + let dest = self.ecx.allocate(ty, MemoryKind::Stack).discard_err()?; + self.ecx.unsize_into(src, ty, &dest.clone().into()).discard_err()?; self.ecx .alloc_mark_immutable(dest.ptr().provenance.unwrap().alloc_id()) .discard_err()?; @@ -620,15 +598,13 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { CastKind::FnPtrToPtr | CastKind::PtrToPtr => { let src = self.evaluated[value].as_ref()?; let src = self.ecx.read_immediate(src).discard_err()?; - let to = self.ecx.layout_of(to).ok()?; - let ret = self.ecx.ptr_to_ptr(&src, to).discard_err()?; + let ret = self.ecx.ptr_to_ptr(&src, ty).discard_err()?; ret.into() } CastKind::PointerCoercion(ty::adjustment::PointerCoercion::UnsafeFnPointer, _) => { let src = self.evaluated[value].as_ref()?; let src = self.ecx.read_immediate(src).discard_err()?; - let to = self.ecx.layout_of(to).ok()?; - ImmTy::from_immediate(*src, to).into() + ImmTy::from_immediate(*src, ty).into() } _ => return None, }, @@ -638,21 +614,20 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { fn project( &mut self, - place: PlaceRef<'tcx>, + place_ty: PlaceTy<'tcx>, value: VnIndex, proj: PlaceElem<'tcx>, from_non_ssa_index: &mut bool, - ) -> Option { + ) -> Option<(PlaceTy<'tcx>, VnIndex)> { + let projection_ty = place_ty.projection_ty(self.tcx, proj); let proj = match proj { ProjectionElem::Deref => { - let ty = place.ty(self.local_decls, self.tcx).ty; - if let Some(Mutability::Not) = ty.ref_mutability() - && let Some(pointee_ty) = ty.builtin_deref(true) - && pointee_ty.is_freeze(self.tcx, self.typing_env()) + if let Some(Mutability::Not) = place_ty.ty.ref_mutability() + && projection_ty.ty.is_freeze(self.tcx, self.typing_env()) { // An immutable borrow `_x` always points to the same value for the // lifetime of the borrow, so we can merge all instances of `*_x`. - return Some(self.insert_deref(value)); + return Some((projection_ty, self.insert_deref(projection_ty.ty, value))); } else { return None; } @@ -660,7 +635,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { ProjectionElem::Downcast(name, index) => ProjectionElem::Downcast(name, index), ProjectionElem::Field(f, ty) => { if let Value::Aggregate(_, _, fields) = self.get(value) { - return Some(fields[f.as_usize()]); + return Some((projection_ty, fields[f.as_usize()])); } else if let Value::Projection(outer_value, ProjectionElem::Downcast(_, read_variant)) = self.get(value) && let Value::Aggregate(_, written_variant, fields) = self.get(*outer_value) // This pass is not aware of control-flow, so we do not know whether the @@ -680,14 +655,14 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { // a downcast to an inactive variant. && written_variant == read_variant { - return Some(fields[f.as_usize()]); + return Some((projection_ty, fields[f.as_usize()])); } ProjectionElem::Field(f, ty) } ProjectionElem::Index(idx) => { if let Value::Repeat(inner, _) = self.get(value) { *from_non_ssa_index |= self.locals[idx].is_none(); - return Some(*inner); + return Some((projection_ty, *inner)); } let idx = self.locals[idx]?; ProjectionElem::Index(idx) @@ -695,7 +670,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { ProjectionElem::ConstantIndex { offset, min_length, from_end } => { match self.get(value) { Value::Repeat(inner, _) => { - return Some(*inner); + return Some((projection_ty, *inner)); } Value::Aggregate(AggregateTy::Array, _, operands) => { let offset = if from_end { @@ -703,7 +678,8 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } else { offset as usize }; - return operands.get(offset).copied(); + let value = operands.get(offset).copied()?; + return Some((projection_ty, value)); } _ => {} }; @@ -717,7 +693,8 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { ProjectionElem::UnwrapUnsafeBinder(ty) => ProjectionElem::UnwrapUnsafeBinder(ty), }; - Some(self.insert(Value::Projection(value, proj))) + let value = self.insert(projection_ty.ty, Value::Projection(value, proj)); + Some((projection_ty, value)) } /// Simplify the projection chain if we know better. @@ -779,6 +756,8 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { // Invariant: `value` holds the value up-to the `index`th projection excluded. let mut value = self.locals[place.local]?; + // Invariant: `value` has type `place_ty`, with optional downcast variant if needed. + let mut place_ty = PlaceTy::from_ty(self.local_decls[place.local].ty); let mut from_non_ssa_index = false; for (index, proj) in place.projection.iter().enumerate() { if let Value::Projection(pointer, ProjectionElem::Deref) = *self.get(value) @@ -796,8 +775,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { place_ref = PlaceRef { local, projection: &place.projection[index..] }; } - let base = PlaceRef { local: place.local, projection: &place.projection[..index] }; - value = self.project(base, value, proj, &mut from_non_ssa_index)?; + (place_ty, value) = self.project(place_ty, value, proj, &mut from_non_ssa_index)?; } if let Value::Projection(pointer, ProjectionElem::Deref) = *self.get(value) @@ -906,8 +884,8 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { // Unsupported values. Rvalue::ThreadLocalRef(..) | Rvalue::ShallowInitBox(..) => return None, }; - debug!(?value); - Some(self.insert(value)) + let ty = rvalue.ty(self.local_decls, self.tcx); + Some(self.insert(ty, value)) } fn simplify_discriminant(&mut self, place: VnIndex) -> Option { @@ -917,7 +895,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { { let enum_ty = self.tcx.type_of(enum_did).instantiate(self.tcx, enum_args); let discr = self.ecx.discriminant_for_variant(enum_ty, variant).discard_err()?; - return Some(self.insert_scalar(discr.to_scalar(), discr.layout.ty)); + return Some(self.insert_scalar(discr.layout.ty, discr.to_scalar())); } None @@ -1014,9 +992,11 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { rvalue: &mut Rvalue<'tcx>, location: Location, ) -> Option { + let tcx = self.tcx; + let ty = rvalue.ty(self.local_decls, tcx); + let Rvalue::Aggregate(box ref kind, ref mut field_ops) = *rvalue else { bug!() }; - let tcx = self.tcx; if field_ops.is_empty() { let is_zst = match *kind { AggregateKind::Array(..) @@ -1031,17 +1011,19 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { }; if is_zst { - let ty = rvalue.ty(self.local_decls, tcx); return Some(self.insert_constant(Const::zero_sized(ty))); } } let fields: Vec<_> = field_ops .iter_mut() - .map(|op| self.simplify_operand(op, location).unwrap_or_else(|| self.new_opaque())) + .map(|op| { + self.simplify_operand(op, location) + .unwrap_or_else(|| self.new_opaque(op.ty(self.local_decls, self.tcx))) + }) .collect(); - let (ty, variant_index) = match *kind { + let (aty, variant_index) = match *kind { AggregateKind::Array(..) => { assert!(!field_ops.is_empty()); (AggregateTy::Array, FIRST_VARIANT) @@ -1058,10 +1040,9 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } // Do not track unions. AggregateKind::Adt(_, _, _, _, Some(_)) => return None, - AggregateKind::RawPtr(pointee_ty, mtbl) => { + AggregateKind::RawPtr(..) => { assert_eq!(field_ops.len(), 2); let mut data_pointer_ty = field_ops[FieldIdx::ZERO].ty(self.local_decls, self.tcx); - let output_pointer_ty = Ty::new_ptr(self.tcx, pointee_ty, mtbl); let [mut pointer, metadata] = fields.try_into().unwrap(); @@ -1074,7 +1055,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { to: _, } = self.get(pointer) && let ty::RawPtr(from_pointee_ty, from_mtbl) = cast_from.kind() - && let ty::RawPtr(_, output_mtbl) = output_pointer_ty.kind() + && let ty::RawPtr(_, output_mtbl) = ty.kind() && from_mtbl == output_mtbl && from_pointee_ty.is_sized(self.tcx, self.typing_env()) { @@ -1087,16 +1068,14 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { field_ops[FieldIdx::ZERO] = op; } - return Some(self.insert(Value::RawPtr { - output_pointer_ty, - data_pointer_ty, - pointer, - metadata, - })); + return Some(self.insert( + ty, + Value::RawPtr { output_pointer_ty: ty, data_pointer_ty, pointer, metadata }, + )); } }; - if let AggregateTy::Array = ty + if let AggregateTy::Array = aty && fields.len() > 4 { let first = fields[0]; @@ -1105,18 +1084,18 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { if let Some(op) = self.try_as_operand(first, location) { *rvalue = Rvalue::Repeat(op, len); } - return Some(self.insert(Value::Repeat(first, len))); + return Some(self.insert(ty, Value::Repeat(first, len))); } } - if let AggregateTy::Def(_, _) = ty + if let AggregateTy::Def(_, _) = aty && let Some(value) = self.simplify_aggregate_to_copy(lhs, rvalue, location, &fields, variant_index) { return Some(value); } - Some(self.insert(Value::Aggregate(ty, variant_index, fields))) + Some(self.insert(ty, Value::Aggregate(aty, variant_index, fields))) } #[instrument(level = "trace", skip(self), ret)] @@ -1197,7 +1176,8 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } _ => Value::UnaryOp(op, arg_index), }; - Some(self.insert(value)) + let ty = op.ty(self.tcx, self.ty(arg_index)); + Some(self.insert(ty, value)) } #[instrument(level = "trace", skip(self), ret)] @@ -1243,8 +1223,9 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { if let Some(value) = self.simplify_binary_inner(op, lhs_ty, lhs, rhs) { return Some(value); } + let ty = op.ty(self.tcx, self.ty(lhs), self.ty(rhs)); let value = Value::BinaryOp(op, lhs, rhs); - Some(self.insert(value)) + Some(self.insert(ty, value)) } fn simplify_binary_inner( @@ -1336,19 +1317,19 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { | BinOp::Shr, Left(0), _, - ) => self.insert_scalar(Scalar::from_uint(0u128, layout.size), lhs_ty), + ) => self.insert_scalar(lhs_ty, Scalar::from_uint(0u128, layout.size)), // Attempt to simplify `x | ALL_ONES` to `ALL_ONES`. (BinOp::BitOr, _, Left(ones)) | (BinOp::BitOr, Left(ones), _) if ones == layout.size.truncate(u128::MAX) || (layout.ty.is_bool() && ones == 1) => { - self.insert_scalar(Scalar::from_uint(ones, layout.size), lhs_ty) + self.insert_scalar(lhs_ty, Scalar::from_uint(ones, layout.size)) } // Sub/Xor with itself. (BinOp::Sub | BinOp::SubWithOverflow | BinOp::SubUnchecked | BinOp::BitXor, a, b) if a == b => { - self.insert_scalar(Scalar::from_uint(0u128, layout.size), lhs_ty) + self.insert_scalar(lhs_ty, Scalar::from_uint(0u128, layout.size)) } // Comparison: // - if both operands can be computed as bits, just compare the bits; @@ -1362,8 +1343,9 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { }; if op.is_overflowing() { + let ty = Ty::new_tup(self.tcx, &[self.ty(result), self.tcx.types.bool]); let false_val = self.insert_bool(false); - Some(self.insert_tuple(vec![result, false_val])) + Some(self.insert_tuple(ty, vec![result, false_val])) } else { Some(result) } @@ -1389,7 +1371,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { if let CastKind::PointerCoercion(ReifyFnPointer | ClosureFnPointer(_), _) = kind { // Each reification of a generic fn may get a different pointer. // Do not try to merge them. - return Some(self.new_opaque()); + return Some(self.new_opaque(to)); } let mut was_ever_updated = false; @@ -1497,7 +1479,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { *initial_kind = kind; } - Some(self.insert(Value::Cast { kind, value, from, to })) + Some(self.insert(to, Value::Cast { kind, value, from, to })) } fn simplify_len(&mut self, place: &mut Place<'tcx>, location: Location) -> Option { @@ -1530,7 +1512,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } // Fallback: a symbolic `Len`. - Some(self.insert(Value::Len(inner))) + Some(self.insert(self.tcx.types.usize, Value::Len(inner))) } fn pointers_have_same_metadata(&self, left_ptr_ty: Ty<'tcx>, right_ptr_ty: Ty<'tcx>) -> bool { @@ -1807,11 +1789,12 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, 'tcx> { if let Some(local) = lhs.as_local() && self.ssa.is_ssa(local) + && let rvalue_ty = rvalue.ty(self.local_decls, self.tcx) // FIXME(#112651) `rvalue` may have a subtype to `local`. We can only mark // `local` as reusable if we have an exact type match. - && self.local_decls[local].ty == rvalue.ty(self.local_decls, self.tcx) + && self.local_decls[local].ty == rvalue_ty { - let value = value.unwrap_or_else(|| self.new_opaque()); + let value = value.unwrap_or_else(|| self.new_opaque(rvalue_ty)); self.assign(local, value); } } @@ -1821,7 +1804,8 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, 'tcx> { if let Some(local) = destination.as_local() && self.ssa.is_ssa(local) { - let opaque = self.new_opaque(); + let ty = self.local_decls[local].ty; + let opaque = self.new_opaque(ty); self.assign(local, opaque); } } diff --git a/tests/mir-opt/const_prop/transmute.rs b/tests/mir-opt/const_prop/transmute.rs index 892b91a5414c3..33cbefbf053f5 100644 --- a/tests/mir-opt/const_prop/transmute.rs +++ b/tests/mir-opt/const_prop/transmute.rs @@ -56,7 +56,7 @@ pub unsafe fn undef_union_as_integer() -> u32 { pub unsafe fn unreachable_direct() -> ! { // CHECK-LABEL: fn unreachable_direct( // CHECK: = const (); - // CHECK: = const () as Never (Transmute); + // CHECK: = const ZeroSized: Never; let x: Never = unsafe { transmute(()) }; match x {} } diff --git a/tests/mir-opt/const_prop/transmute.unreachable_direct.GVN.32bit.diff b/tests/mir-opt/const_prop/transmute.unreachable_direct.GVN.32bit.diff index 3364782022dc7..5aeb55a5a6a53 100644 --- a/tests/mir-opt/const_prop/transmute.unreachable_direct.GVN.32bit.diff +++ b/tests/mir-opt/const_prop/transmute.unreachable_direct.GVN.32bit.diff @@ -15,7 +15,7 @@ - _2 = (); - _1 = move _2 as Never (Transmute); + _2 = const (); -+ _1 = const () as Never (Transmute); ++ _1 = const ZeroSized: Never; unreachable; } } diff --git a/tests/mir-opt/const_prop/transmute.unreachable_direct.GVN.64bit.diff b/tests/mir-opt/const_prop/transmute.unreachable_direct.GVN.64bit.diff index 3364782022dc7..5aeb55a5a6a53 100644 --- a/tests/mir-opt/const_prop/transmute.unreachable_direct.GVN.64bit.diff +++ b/tests/mir-opt/const_prop/transmute.unreachable_direct.GVN.64bit.diff @@ -15,7 +15,7 @@ - _2 = (); - _1 = move _2 as Never (Transmute); + _2 = const (); -+ _1 = const () as Never (Transmute); ++ _1 = const ZeroSized: Never; unreachable; } } diff --git a/tests/mir-opt/gvn.generic_cast_metadata.GVN.panic-abort.diff b/tests/mir-opt/gvn.generic_cast_metadata.GVN.panic-abort.diff index 770c673077533..ffe4a0272b4b1 100644 --- a/tests/mir-opt/gvn.generic_cast_metadata.GVN.panic-abort.diff +++ b/tests/mir-opt/gvn.generic_cast_metadata.GVN.panic-abort.diff @@ -18,7 +18,8 @@ bb0: { _4 = copy _1 as *const T (PtrToPtr); - _5 = PtrMetadata(copy _4); +- _5 = PtrMetadata(copy _4); ++ _5 = const (); _6 = copy _1 as *const (&A, [T]) (PtrToPtr); - _7 = PtrMetadata(copy _6); + _7 = PtrMetadata(copy _1); diff --git a/tests/mir-opt/gvn.generic_cast_metadata.GVN.panic-unwind.diff b/tests/mir-opt/gvn.generic_cast_metadata.GVN.panic-unwind.diff index 770c673077533..ffe4a0272b4b1 100644 --- a/tests/mir-opt/gvn.generic_cast_metadata.GVN.panic-unwind.diff +++ b/tests/mir-opt/gvn.generic_cast_metadata.GVN.panic-unwind.diff @@ -18,7 +18,8 @@ bb0: { _4 = copy _1 as *const T (PtrToPtr); - _5 = PtrMetadata(copy _4); +- _5 = PtrMetadata(copy _4); ++ _5 = const (); _6 = copy _1 as *const (&A, [T]) (PtrToPtr); - _7 = PtrMetadata(copy _6); + _7 = PtrMetadata(copy _1); diff --git a/tests/mir-opt/gvn.rs b/tests/mir-opt/gvn.rs index 6ef320c90de12..5d348bc3c1e40 100644 --- a/tests/mir-opt/gvn.rs +++ b/tests/mir-opt/gvn.rs @@ -869,7 +869,7 @@ fn generic_cast_metadata(ps: *const [T], pa: *const A, // Metadata usize -> (), do not optimize. // CHECK: [[T:_.+]] = copy _1 as - // CHECK-NEXT: PtrMetadata(copy [[T]]) + // CHECK-NEXT: const (); let t1 = CastPtrToPtr::<_, *const T>(ps); let m1 = PtrMetadata(t1); From 4750dff54eab52a308a1a1afd85b4614d652841a Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Mon, 30 Jun 2025 19:50:11 +0000 Subject: [PATCH 04/36] Remove extraneous types. --- compiler/rustc_mir_transform/src/gvn.rs | 209 +++++++----------- ...ted_loop.PreCodegen.after.panic-unwind.mir | 5 +- 2 files changed, 80 insertions(+), 134 deletions(-) diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 3e4c05b355f6b..e081d1fde93bf 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -105,7 +105,6 @@ use rustc_middle::mir::*; use rustc_middle::ty::layout::{HasTypingEnv, LayoutOf}; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::DUMMY_SP; -use rustc_span::def_id::DefId; use smallvec::SmallVec; use tracing::{debug, instrument, trace}; @@ -155,16 +154,6 @@ newtype_index! { struct VnIndex {} } -/// Computing the aggregate's type can be quite slow, so we only keep the minimal amount of -/// information to reconstruct it when needed. -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] -enum AggregateTy<'tcx> { - /// Invariant: this must not be used for an empty array. - Array, - Tuple, - Def(DefId, ty::GenericArgsRef<'tcx>), -} - #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] enum AddressKind { Ref(BorrowKind), @@ -187,17 +176,13 @@ enum Value<'tcx> { }, /// An aggregate value, either tuple/closure/struct/enum. /// This does not contain unions, as we cannot reason with the value. - Aggregate(AggregateTy<'tcx>, VariantIdx, Vec), + Aggregate(VariantIdx, Vec), /// A raw pointer aggregate built from a thin pointer and metadata. RawPtr { /// Thin pointer component. This is field 0 in MIR. pointer: VnIndex, /// Metadata component. This is field 1 in MIR. metadata: VnIndex, - /// Needed for cast propagation. - data_pointer_ty: Ty<'tcx>, - /// The data pointer can be anything thin, so doesn't determine the output. - output_pointer_ty: Ty<'tcx>, }, /// This corresponds to a `[value; count]` expression. Repeat(VnIndex, ty::Const<'tcx>), @@ -211,7 +196,7 @@ enum Value<'tcx> { // Extractions. /// This is the *value* obtained by projecting another value. - Projection(VnIndex, ProjectionElem>), + Projection(VnIndex, ProjectionElem), /// Discriminant of the given value. Discriminant(VnIndex), /// Length of an array or slice. @@ -224,8 +209,6 @@ enum Value<'tcx> { Cast { kind: CastKind, value: VnIndex, - from: Ty<'tcx>, - to: Ty<'tcx>, }, } @@ -377,7 +360,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } fn insert_tuple(&mut self, ty: Ty<'tcx>, values: Vec) -> VnIndex { - self.insert(ty, Value::Aggregate(AggregateTy::Tuple, VariantIdx::ZERO, values)) + self.insert(ty, Value::Aggregate(VariantIdx::ZERO, values)) } fn insert_deref(&mut self, ty: Ty<'tcx>, value: VnIndex) -> VnIndex { @@ -408,7 +391,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { Constant { ref value, disambiguator: _ } => { self.ecx.eval_mir_constant(value, DUMMY_SP, None).discard_err()? } - Aggregate(_, variant, ref fields) => { + Aggregate(variant, ref fields) => { let fields = fields .iter() .map(|&f| self.evaluated[f].as_ref()) @@ -440,7 +423,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { return None; } } - RawPtr { pointer, metadata, output_pointer_ty: _, data_pointer_ty: _ } => { + RawPtr { pointer, metadata } => { let pointer = self.evaluated[pointer].as_ref()?; let metadata = self.evaluated[metadata].as_ref()?; @@ -456,28 +439,28 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } Projection(base, elem) => { - let value = self.evaluated[base].as_ref()?; + let base = self.evaluated[base].as_ref()?; let elem = match elem { ProjectionElem::Deref => ProjectionElem::Deref, ProjectionElem::Downcast(name, read_variant) => { ProjectionElem::Downcast(name, read_variant) } - ProjectionElem::Field(f, ty) => ProjectionElem::Field(f, ty), + ProjectionElem::Field(f, ()) => ProjectionElem::Field(f, ty.ty), ProjectionElem::ConstantIndex { offset, min_length, from_end } => { ProjectionElem::ConstantIndex { offset, min_length, from_end } } ProjectionElem::Subslice { from, to, from_end } => { ProjectionElem::Subslice { from, to, from_end } } - ProjectionElem::OpaqueCast(ty) => ProjectionElem::OpaqueCast(ty), - ProjectionElem::Subtype(ty) => ProjectionElem::Subtype(ty), - ProjectionElem::UnwrapUnsafeBinder(ty) => { - ProjectionElem::UnwrapUnsafeBinder(ty) + ProjectionElem::OpaqueCast(()) => ProjectionElem::OpaqueCast(ty.ty), + ProjectionElem::Subtype(()) => ProjectionElem::Subtype(ty.ty), + ProjectionElem::UnwrapUnsafeBinder(()) => { + ProjectionElem::UnwrapUnsafeBinder(ty.ty) } // This should have been replaced by a `ConstantIndex` earlier. ProjectionElem::Index(_) => return None, }; - self.ecx.project(value, elem).discard_err()? + self.ecx.project(base, elem).discard_err()? } Address { place, kind: _, provenance: _ } => { if !place.is_indirect_first_projection() { @@ -544,7 +527,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { let val = self.ecx.binary_op(bin_op, &lhs, &rhs).discard_err()?; val.into() } - Cast { kind, value, from: _, to: _ } => match kind { + Cast { kind, value } => match kind { CastKind::IntToInt | CastKind::IntToFloat => { let value = self.evaluated[value].as_ref()?; let value = self.ecx.read_immediate(value).discard_err()?; @@ -633,11 +616,11 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } } ProjectionElem::Downcast(name, index) => ProjectionElem::Downcast(name, index), - ProjectionElem::Field(f, ty) => { - if let Value::Aggregate(_, _, fields) = self.get(value) { + ProjectionElem::Field(f, _) => { + if let Value::Aggregate(_, fields) = self.get(value) { return Some((projection_ty, fields[f.as_usize()])); } else if let Value::Projection(outer_value, ProjectionElem::Downcast(_, read_variant)) = self.get(value) - && let Value::Aggregate(_, written_variant, fields) = self.get(*outer_value) + && let Value::Aggregate(written_variant, fields) = self.get(*outer_value) // This pass is not aware of control-flow, so we do not know whether the // replacement we are doing is actually reachable. We could be in any arm of // ``` @@ -657,7 +640,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { { return Some((projection_ty, fields[f.as_usize()])); } - ProjectionElem::Field(f, ty) + ProjectionElem::Field(f, ()) } ProjectionElem::Index(idx) => { if let Value::Repeat(inner, _) = self.get(value) { @@ -672,7 +655,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { Value::Repeat(inner, _) => { return Some((projection_ty, *inner)); } - Value::Aggregate(AggregateTy::Array, _, operands) => { + Value::Aggregate(_, operands) => { let offset = if from_end { operands.len() - offset as usize } else { @@ -688,9 +671,9 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { ProjectionElem::Subslice { from, to, from_end } => { ProjectionElem::Subslice { from, to, from_end } } - ProjectionElem::OpaqueCast(ty) => ProjectionElem::OpaqueCast(ty), - ProjectionElem::Subtype(ty) => ProjectionElem::Subtype(ty), - ProjectionElem::UnwrapUnsafeBinder(ty) => ProjectionElem::UnwrapUnsafeBinder(ty), + ProjectionElem::OpaqueCast(_) => ProjectionElem::OpaqueCast(()), + ProjectionElem::Subtype(_) => ProjectionElem::Subtype(()), + ProjectionElem::UnwrapUnsafeBinder(_) => ProjectionElem::UnwrapUnsafeBinder(()), }; let value = self.insert(projection_ty.ty, Value::Projection(value, proj)); @@ -852,14 +835,9 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { self.simplify_place_projection(place, location); return Some(self.new_pointer(*place, AddressKind::Address(mutbl))); } - Rvalue::WrapUnsafeBinder(ref mut op, ty) => { + Rvalue::WrapUnsafeBinder(ref mut op, _) => { let value = self.simplify_operand(op, location)?; - Value::Cast { - kind: CastKind::Transmute, - value, - from: op.ty(self.local_decls, self.tcx), - to: ty, - } + Value::Cast { kind: CastKind::Transmute, value } } // Operations. @@ -889,11 +867,10 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } fn simplify_discriminant(&mut self, place: VnIndex) -> Option { - if let Value::Aggregate(enum_ty, variant, _) = *self.get(place) - && let AggregateTy::Def(enum_did, enum_args) = enum_ty - && let DefKind::Enum = self.tcx.def_kind(enum_did) + let enum_ty = self.ty(place); + if enum_ty.is_enum() + && let Value::Aggregate(variant, _) = *self.get(place) { - let enum_ty = self.tcx.type_of(enum_did).instantiate(self.tcx, enum_args); let discr = self.ecx.discriminant_for_variant(enum_ty, variant).discard_err()?; return Some(self.insert_scalar(discr.layout.ty, discr.to_scalar())); } @@ -903,12 +880,13 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { fn try_as_place_elem( &mut self, - proj: ProjectionElem>, + ty: Ty<'tcx>, + proj: ProjectionElem, loc: Location, ) -> Option> { Some(match proj { ProjectionElem::Deref => ProjectionElem::Deref, - ProjectionElem::Field(idx, ty) => ProjectionElem::Field(idx, ty), + ProjectionElem::Field(idx, ()) => ProjectionElem::Field(idx, ty), ProjectionElem::Index(idx) => { let Some(local) = self.try_as_local(idx, loc) else { return None; @@ -923,9 +901,9 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { ProjectionElem::Subslice { from, to, from_end } } ProjectionElem::Downcast(symbol, idx) => ProjectionElem::Downcast(symbol, idx), - ProjectionElem::OpaqueCast(idx) => ProjectionElem::OpaqueCast(idx), - ProjectionElem::Subtype(idx) => ProjectionElem::Subtype(idx), - ProjectionElem::UnwrapUnsafeBinder(ty) => ProjectionElem::UnwrapUnsafeBinder(ty), + ProjectionElem::OpaqueCast(()) => ProjectionElem::OpaqueCast(ty), + ProjectionElem::Subtype(()) => ProjectionElem::Subtype(ty), + ProjectionElem::UnwrapUnsafeBinder(()) => ProjectionElem::UnwrapUnsafeBinder(ty), }) } @@ -971,8 +949,8 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { // Allow introducing places with non-constant offsets, as those are still better than // reconstructing an aggregate. - if let Some(place) = self.try_as_place(copy_from_local_value, location, true) - && rvalue.ty(self.local_decls, self.tcx) == place.ty(self.local_decls, self.tcx).ty + if self.ty(copy_from_local_value) == rvalue.ty(self.local_decls, self.tcx) + && let Some(place) = self.try_as_place(copy_from_local_value, location, true) { // Avoid creating `*a = copy (*b)`, as they might be aliases resulting in overlapping assignments. // FIXME: This also avoids any kind of projection, not just derefs. We can add allowed projections. @@ -1023,44 +1001,31 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { }) .collect(); - let (aty, variant_index) = match *kind { - AggregateKind::Array(..) => { + let variant_index = match *kind { + AggregateKind::Array(..) | AggregateKind::Tuple => { assert!(!field_ops.is_empty()); - (AggregateTy::Array, FIRST_VARIANT) - } - AggregateKind::Tuple => { - assert!(!field_ops.is_empty()); - (AggregateTy::Tuple, FIRST_VARIANT) - } - AggregateKind::Closure(did, args) - | AggregateKind::CoroutineClosure(did, args) - | AggregateKind::Coroutine(did, args) => (AggregateTy::Def(did, args), FIRST_VARIANT), - AggregateKind::Adt(did, variant_index, args, _, None) => { - (AggregateTy::Def(did, args), variant_index) + FIRST_VARIANT } + AggregateKind::Closure(..) + | AggregateKind::CoroutineClosure(..) + | AggregateKind::Coroutine(..) => FIRST_VARIANT, + AggregateKind::Adt(_, variant_index, _, _, None) => variant_index, // Do not track unions. AggregateKind::Adt(_, _, _, _, Some(_)) => return None, AggregateKind::RawPtr(..) => { assert_eq!(field_ops.len(), 2); - let mut data_pointer_ty = field_ops[FieldIdx::ZERO].ty(self.local_decls, self.tcx); - let [mut pointer, metadata] = fields.try_into().unwrap(); // Any thin pointer of matching mutability is fine as the data pointer. let mut was_updated = false; - while let Value::Cast { - kind: CastKind::PtrToPtr, - value: cast_value, - from: cast_from, - to: _, - } = self.get(pointer) - && let ty::RawPtr(from_pointee_ty, from_mtbl) = cast_from.kind() + while let Value::Cast { kind: CastKind::PtrToPtr, value: cast_value } = + self.get(pointer) + && let ty::RawPtr(from_pointee_ty, from_mtbl) = self.ty(*cast_value).kind() && let ty::RawPtr(_, output_mtbl) = ty.kind() && from_mtbl == output_mtbl && from_pointee_ty.is_sized(self.tcx, self.typing_env()) { pointer = *cast_value; - data_pointer_ty = *cast_from; was_updated = true; } @@ -1068,16 +1033,11 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { field_ops[FieldIdx::ZERO] = op; } - return Some(self.insert( - ty, - Value::RawPtr { output_pointer_ty: ty, data_pointer_ty, pointer, metadata }, - )); + return Some(self.insert(ty, Value::RawPtr { pointer, metadata })); } }; - if let AggregateTy::Array = aty - && fields.len() > 4 - { + if ty.is_array() && fields.len() > 4 { let first = fields[0]; if fields.iter().all(|&v| v == first) { let len = ty::Const::from_target_usize(self.tcx, fields.len().try_into().unwrap()); @@ -1088,14 +1048,13 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } } - if let AggregateTy::Def(_, _) = aty - && let Some(value) = - self.simplify_aggregate_to_copy(lhs, rvalue, location, &fields, variant_index) + if let Some(value) = + self.simplify_aggregate_to_copy(lhs, rvalue, location, &fields, variant_index) { return Some(value); } - Some(self.insert(ty, Value::Aggregate(aty, variant_index, fields))) + Some(self.insert(ty, Value::Aggregate(variant_index, fields))) } #[instrument(level = "trace", skip(self), ret)] @@ -1106,6 +1065,8 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { location: Location, ) -> Option { let mut arg_index = self.simplify_operand(arg_op, location)?; + let arg_ty = self.ty(arg_index); + let ret_ty = op.ty(self.tcx, arg_ty); // PtrMetadata doesn't care about *const vs *mut vs & vs &mut, // so start by removing those distinctions so we can update the `Operand` @@ -1121,8 +1082,8 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { // we can't always know exactly what the metadata are. // To allow things like `*mut (?A, ?T)` <-> `*mut (?B, ?T)`, // it's fine to get a projection as the type. - Value::Cast { kind: CastKind::PtrToPtr, value: inner, from, to } - if self.pointers_have_same_metadata(*from, *to) => + Value::Cast { kind: CastKind::PtrToPtr, value: inner } + if self.pointers_have_same_metadata(self.ty(*inner), arg_ty) => { arg_index = *inner; was_updated = true; @@ -1165,19 +1126,16 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { UnOp::PtrMetadata, Value::Cast { kind: CastKind::PointerCoercion(ty::adjustment::PointerCoercion::Unsize, _), - from, - to, - .. + value: inner, }, - ) if let ty::Slice(..) = to.builtin_deref(true).unwrap().kind() - && let ty::Array(_, len) = from.builtin_deref(true).unwrap().kind() => + ) if let ty::Slice(..) = arg_ty.builtin_deref(true).unwrap().kind() + && let ty::Array(_, len) = self.ty(*inner).builtin_deref(true).unwrap().kind() => { return Some(self.insert_constant(Const::Ty(self.tcx.types.usize, *len))); } _ => Value::UnaryOp(op, arg_index), }; - let ty = op.ty(self.tcx, self.ty(arg_index)); - Some(self.insert(ty, value)) + Some(self.insert(ret_ty, value)) } #[instrument(level = "trace", skip(self), ret)] @@ -1190,25 +1148,23 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { ) -> Option { let lhs = self.simplify_operand(lhs_operand, location); let rhs = self.simplify_operand(rhs_operand, location); + // Only short-circuit options after we called `simplify_operand` // on both operands for side effect. let mut lhs = lhs?; let mut rhs = rhs?; - let lhs_ty = lhs_operand.ty(self.local_decls, self.tcx); + let lhs_ty = self.ty(lhs); // If we're comparing pointers, remove `PtrToPtr` casts if the from // types of both casts and the metadata all match. if let BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge = op && lhs_ty.is_any_ptr() - && let Value::Cast { - kind: CastKind::PtrToPtr, value: lhs_value, from: lhs_from, .. - } = self.get(lhs) - && let Value::Cast { - kind: CastKind::PtrToPtr, value: rhs_value, from: rhs_from, .. - } = self.get(rhs) - && lhs_from == rhs_from - && self.pointers_have_same_metadata(*lhs_from, lhs_ty) + && let Value::Cast { kind: CastKind::PtrToPtr, value: lhs_value } = self.get(lhs) + && let Value::Cast { kind: CastKind::PtrToPtr, value: rhs_value } = self.get(rhs) + && let lhs_from = self.ty(*lhs_value) + && lhs_from == self.ty(*rhs_value) + && self.pointers_have_same_metadata(lhs_from, lhs_ty) { lhs = *lhs_value; rhs = *rhs_value; @@ -1223,7 +1179,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { if let Some(value) = self.simplify_binary_inner(op, lhs_ty, lhs, rhs) { return Some(value); } - let ty = op.ty(self.tcx, self.ty(lhs), self.ty(rhs)); + let ty = op.ty(self.tcx, lhs_ty, self.ty(rhs)); let value = Value::BinaryOp(op, lhs, rhs); Some(self.insert(ty, value)) } @@ -1361,9 +1317,9 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { use CastKind::*; use rustc_middle::ty::adjustment::PointerCoercion::*; - let mut from = initial_operand.ty(self.local_decls, self.tcx); let mut kind = *initial_kind; let mut value = self.simplify_operand(initial_operand, location)?; + let mut from = self.ty(value); if from == to { return Some(value); } @@ -1394,14 +1350,14 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { // If a cast just casts away the metadata again, then we can get it by // casting the original thin pointer passed to `from_raw_parts` if let PtrToPtr = kind - && let Value::RawPtr { data_pointer_ty, pointer, .. } = self.get(value) + && let Value::RawPtr { pointer, .. } = self.get(value) && let ty::RawPtr(to_pointee, _) = to.kind() && to_pointee.is_sized(self.tcx, self.typing_env()) { - from = *data_pointer_ty; + from = self.ty(*pointer); value = *pointer; was_updated_this_iteration = true; - if *data_pointer_ty == to { + if from == to { return Some(*pointer); } } @@ -1409,7 +1365,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { // Aggregate-then-Transmute can just transmute the original field value, // so long as the bytes of a value from only from a single field. if let Transmute = kind - && let Value::Aggregate(_aggregate_ty, variant_idx, field_values) = self.get(value) + && let Value::Aggregate(variant_idx, field_values) = self.get(value) && let Some((field_idx, field_ty)) = self.value_is_all_in_one_field(from, *variant_idx) { @@ -1422,13 +1378,8 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } // Various cast-then-cast cases can be simplified. - if let Value::Cast { - kind: inner_kind, - value: inner_value, - from: inner_from, - to: inner_to, - } = *self.get(value) - { + if let Value::Cast { kind: inner_kind, value: inner_value } = *self.get(value) { + let inner_from = self.ty(inner_value); let new_kind = match (inner_kind, kind) { // Even if there's a narrowing cast in here that's fine, because // things like `*mut [i32] -> *mut i32 -> *const i32` and @@ -1437,9 +1388,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { // PtrToPtr-then-Transmute is fine so long as the pointer cast is identity: // `*const T -> *mut T -> NonNull` is fine, but we need to check for narrowing // to skip things like `*const [i32] -> *const i32 -> NonNull`. - (PtrToPtr, Transmute) - if self.pointers_have_same_metadata(inner_from, inner_to) => - { + (PtrToPtr, Transmute) if self.pointers_have_same_metadata(inner_from, from) => { Some(Transmute) } // Similarly, for Transmute-then-PtrToPtr. Note that we need to check different @@ -1450,7 +1399,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { // If would be legal to always do this, but we don't want to hide information // from the backend that it'd otherwise be able to use for optimizations. (Transmute, Transmute) - if !self.type_may_have_niche_of_interest_to_backend(inner_to) => + if !self.type_may_have_niche_of_interest_to_backend(from) => { Some(Transmute) } @@ -1479,7 +1428,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { *initial_kind = kind; } - Some(self.insert(to, Value::Cast { kind, value, from, to })) + Some(self.insert(to, Value::Cast { kind, value })) } fn simplify_len(&mut self, place: &mut Place<'tcx>, location: Location) -> Option { @@ -1501,11 +1450,11 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } // We have an unsizing cast, which assigns the length to wide pointer metadata. - if let Value::Cast { kind, from, to, .. } = self.get(inner) + if let Value::Cast { kind, value: from } = self.get(inner) && let CastKind::PointerCoercion(ty::adjustment::PointerCoercion::Unsize, _) = kind - && let Some(from) = from.builtin_deref(true) + && let Some(from) = self.ty(*from).builtin_deref(true) && let ty::Array(_, len) = from.kind() - && let Some(to) = to.builtin_deref(true) + && let Some(to) = self.ty(inner).builtin_deref(true) && let ty::Slice(..) = to.kind() { return Some(self.insert_constant(Const::Ty(self.tcx.types.usize, *len))); @@ -1721,7 +1670,7 @@ impl<'tcx> VnState<'_, 'tcx> { return Some(place); } else if let Value::Projection(pointer, proj) = *self.get(index) && (allow_complex_projection || proj.is_stable_offset()) - && let Some(proj) = self.try_as_place_elem(proj, loc) + && let Some(proj) = self.try_as_place_elem(self.ty(index), proj, loc) { projection.push(proj); index = pointer; diff --git a/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-unwind.mir index 3b58f1d61f4bb..8c5fbda63921f 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-unwind.mir @@ -109,7 +109,6 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { } bb4: { - StorageLive(_15); _14 = &mut _13; _15 = > as Iterator>::next(move _14) -> [return: bb5, unwind: bb11]; } @@ -120,7 +119,6 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { } bb6: { - StorageDead(_15); StorageDead(_13); drop(_2) -> [return: bb7, unwind continue]; } @@ -135,14 +133,13 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { StorageLive(_19); _19 = &_2; StorageLive(_20); - _20 = (copy _17, copy _18); + _20 = copy ((_15 as Some).0: (usize, &T)); _21 = >::call(move _19, move _20) -> [return: bb9, unwind: bb11]; } bb9: { StorageDead(_20); StorageDead(_19); - StorageDead(_15); goto -> bb4; } From cbc3cf716e16c20ba8fb652f8a41dc1d62f03f66 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Tue, 1 Jul 2025 12:06:39 +0000 Subject: [PATCH 05/36] Avoid computing layouts inside coroutines. --- compiler/rustc_mir_transform/src/gvn.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index e081d1fde93bf..461aa7e5284f9 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -216,6 +216,7 @@ struct VnState<'body, 'tcx> { tcx: TyCtxt<'tcx>, ecx: InterpCx<'tcx, DummyMachine>, local_decls: &'body LocalDecls<'tcx>, + is_coroutine: bool, /// Value stored in each local. locals: IndexVec>, /// Locals that are assigned that value. @@ -253,6 +254,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { tcx, ecx: InterpCx::new(tcx, DUMMY_SP, typing_env, DummyMachine), local_decls, + is_coroutine: body.coroutine.is_some(), locals: IndexVec::from_elem(None, local_decls), rev_locals: IndexVec::with_capacity(num_values), values: FxIndexSet::with_capacity_and_hasher(num_values, Default::default()), @@ -380,7 +382,12 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { fn eval_to_const(&mut self, value: VnIndex) -> Option> { use Value::*; let ty = self.ty(value); - let ty = self.ecx.layout_of(ty).ok()?; + // Avoid computing layouts inside a coroutine, as that can cause cycles. + let ty = if !self.is_coroutine || ty.is_scalar() { + self.ecx.layout_of(ty).ok()? + } else { + return None; + }; let op = match *self.get(value) { _ if ty.is_zst() => ImmTy::uninit(ty).into(), From 4a95b165274f9de5c6a0d3c13ae3103e2ac23eb9 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Wed, 2 Jul 2025 10:42:07 +0000 Subject: [PATCH 06/36] Move crashes tests. --- .../mir/gvn-nonsensical-coroutine-layout.rs} | 6 ++++- .../gvn-nonsensical-coroutine-layout.stderr | 26 +++++++++++++++++++ .../mir/gvn-nonsensical-sized-str.rs} | 5 +++- tests/ui/mir/gvn-nonsensical-sized-str.stderr | 10 +++++++ 4 files changed, 45 insertions(+), 2 deletions(-) rename tests/{crashes/128094.rs => ui/mir/gvn-nonsensical-coroutine-layout.rs} (53%) create mode 100644 tests/ui/mir/gvn-nonsensical-coroutine-layout.stderr rename tests/{crashes/135128.rs => ui/mir/gvn-nonsensical-sized-str.rs} (51%) create mode 100644 tests/ui/mir/gvn-nonsensical-sized-str.stderr diff --git a/tests/crashes/128094.rs b/tests/ui/mir/gvn-nonsensical-coroutine-layout.rs similarity index 53% rename from tests/crashes/128094.rs rename to tests/ui/mir/gvn-nonsensical-coroutine-layout.rs index 56d09d78bed91..f0d174cd01b0a 100644 --- a/tests/crashes/128094.rs +++ b/tests/ui/mir/gvn-nonsensical-coroutine-layout.rs @@ -1,15 +1,19 @@ -//@ known-bug: rust-lang/rust#128094 +//! Verify that we do not ICE when a coroutine body is malformed. //@ compile-flags: -Zmir-enable-passes=+GVN //@ edition: 2018 pub enum Request { TestSome(T), + //~^ ERROR cannot find type `T` in this scope [E0412] } pub async fn handle_event(event: Request) { async move { static instance: Request = Request { bar: 17 }; + //~^ ERROR expected struct, variant or union type, found enum `Request` [E0574] &instance } .await; } + +fn main() {} diff --git a/tests/ui/mir/gvn-nonsensical-coroutine-layout.stderr b/tests/ui/mir/gvn-nonsensical-coroutine-layout.stderr new file mode 100644 index 0000000000000..abc7b16ca7415 --- /dev/null +++ b/tests/ui/mir/gvn-nonsensical-coroutine-layout.stderr @@ -0,0 +1,26 @@ +error[E0412]: cannot find type `T` in this scope + --> $DIR/gvn-nonsensical-coroutine-layout.rs:6:14 + | +LL | TestSome(T), + | ^ not found in this scope + | +help: you might be missing a type parameter + | +LL | pub enum Request { + | +++ + +error[E0574]: expected struct, variant or union type, found enum `Request` + --> $DIR/gvn-nonsensical-coroutine-layout.rs:12:36 + | +LL | static instance: Request = Request { bar: 17 }; + | ^^^^^^^ not a struct, variant or union type + | +help: consider importing this struct instead + | +LL + use std::error::Request; + | + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0412, E0574. +For more information about an error, try `rustc --explain E0412`. diff --git a/tests/crashes/135128.rs b/tests/ui/mir/gvn-nonsensical-sized-str.rs similarity index 51% rename from tests/crashes/135128.rs rename to tests/ui/mir/gvn-nonsensical-sized-str.rs index c718b758dc69b..29a82f8ce2a16 100644 --- a/tests/crashes/135128.rs +++ b/tests/ui/mir/gvn-nonsensical-sized-str.rs @@ -1,13 +1,16 @@ -//@ known-bug: #135128 +//! Verify that we do not ICE when optimizing bodies with nonsensical bounds. //@ compile-flags: -Copt-level=1 //@ edition: 2021 +//@ build-pass #![feature(trivial_bounds)] async fn return_str() -> str where str: Sized, + //~^ WARN trait bound str: Sized does not depend on any type or lifetime parameters { *"Sized".to_string().into_boxed_str() } + fn main() {} diff --git a/tests/ui/mir/gvn-nonsensical-sized-str.stderr b/tests/ui/mir/gvn-nonsensical-sized-str.stderr new file mode 100644 index 0000000000000..08f0193e9f61c --- /dev/null +++ b/tests/ui/mir/gvn-nonsensical-sized-str.stderr @@ -0,0 +1,10 @@ +warning: trait bound str: Sized does not depend on any type or lifetime parameters + --> $DIR/gvn-nonsensical-sized-str.rs:10:10 + | +LL | str: Sized, + | ^^^^^ + | + = note: `#[warn(trivial_bounds)]` on by default + +warning: 1 warning emitted + From 92144e1ee183fc8ed097c937fcc4754242b69a99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Fri, 4 Jul 2025 11:41:19 +0200 Subject: [PATCH 07/36] Remove `josh-sync` tooling and update README --- src/doc/rustc-dev-guide/README.md | 35 +- src/doc/rustc-dev-guide/josh-sync/Cargo.lock | 430 ------------------ src/doc/rustc-dev-guide/josh-sync/Cargo.toml | 9 - src/doc/rustc-dev-guide/josh-sync/README.md | 4 - src/doc/rustc-dev-guide/josh-sync/src/main.rs | 41 -- src/doc/rustc-dev-guide/josh-sync/src/sync.rs | 275 ----------- 6 files changed, 5 insertions(+), 789 deletions(-) delete mode 100644 src/doc/rustc-dev-guide/josh-sync/Cargo.lock delete mode 100644 src/doc/rustc-dev-guide/josh-sync/Cargo.toml delete mode 100644 src/doc/rustc-dev-guide/josh-sync/README.md delete mode 100644 src/doc/rustc-dev-guide/josh-sync/src/main.rs delete mode 100644 src/doc/rustc-dev-guide/josh-sync/src/sync.rs diff --git a/src/doc/rustc-dev-guide/README.md b/src/doc/rustc-dev-guide/README.md index 0425c15f83c1c..f11290e44d8f5 100644 --- a/src/doc/rustc-dev-guide/README.md +++ b/src/doc/rustc-dev-guide/README.md @@ -72,49 +72,24 @@ including the `` marker at the place where you want the TOC. ## Synchronizing josh subtree with rustc -This repository is linked to `rust-lang/rust` as a [josh](https://josh-project.github.io/josh/intro.html) subtree. You can use the following commands to synchronize the subtree in both directions. +This repository is linked to `rust-lang/rust` as a [josh](https://josh-project.github.io/josh/intro.html) subtree. You can use the [rustc-josh-sync](https://github.com/rust-lang/josh-sync) tool to perform synchronization. -You'll need to install `josh-proxy` locally via - -``` -cargo install josh-proxy --git https://github.com/josh-project/josh --tag r24.10.04 -``` -Older versions of `josh-proxy` may not round trip commits losslessly so it is important to install this exact version. +You can install the tool using `cargo install --locked --git https://github.com/rust-lang/josh-sync`. ### Pull changes from `rust-lang/rust` into this repository 1) Checkout a new branch that will be used to create a PR into `rust-lang/rustc-dev-guide` 2) Run the pull command ``` - cargo run --manifest-path josh-sync/Cargo.toml rustc-pull + rustc-josh-sync pull ``` 3) Push the branch to your fork and create a PR into `rustc-dev-guide` + - If you have `gh` CLI installed, `rustc-josh-sync` can create the PR for you. ### Push changes from this repository into `rust-lang/rust` -NOTE: If you use Git protocol to push to your fork of `rust-lang/rust`, -ensure that you have this entry in your Git config, -else the 2 steps that follow would prompt for a username and password: - -``` -[url "git@github.com:"] -insteadOf = "https://github.com/" -``` - 1) Run the push command to create a branch named `` in a `rustc` fork under the `` account ``` - cargo run --manifest-path josh-sync/Cargo.toml rustc-push + rustc-josh-sync push ``` 2) Create a PR from `` into `rust-lang/rust` - -#### Minimal git config - -For simplicity (ease of implementation purposes), the josh-sync script simply calls out to system git. This means that the git invocation may be influenced by global (or local) git configuration. - -You may observe "Nothing to pull" even if you *know* rustc-pull has something to pull if your global git config sets `fetch.prunetags = true` (and possibly other configurations may cause unexpected outcomes). - -To minimize the likelihood of this happening, you may wish to keep a separate *minimal* git config that *only* has `[user]` entries from global git config, then repoint system git to use the minimal git config instead. E.g. - -``` -GIT_CONFIG_GLOBAL=/path/to/minimal/gitconfig GIT_CONFIG_SYSTEM='' cargo run --manifest-path josh-sync/Cargo.toml -- rustc-pull -``` diff --git a/src/doc/rustc-dev-guide/josh-sync/Cargo.lock b/src/doc/rustc-dev-guide/josh-sync/Cargo.lock deleted file mode 100644 index a8183a740db3b..0000000000000 --- a/src/doc/rustc-dev-guide/josh-sync/Cargo.lock +++ /dev/null @@ -1,430 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 4 - -[[package]] -name = "anstream" -version = "0.6.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" -dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "is_terminal_polyfill", - "utf8parse", -] - -[[package]] -name = "anstyle" -version = "1.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" - -[[package]] -name = "anstyle-parse" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" -dependencies = [ - "utf8parse", -] - -[[package]] -name = "anstyle-query" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" -dependencies = [ - "windows-sys 0.59.0", -] - -[[package]] -name = "anstyle-wincon" -version = "3.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" -dependencies = [ - "anstyle", - "windows-sys 0.59.0", -] - -[[package]] -name = "anyhow" -version = "1.0.95" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" - -[[package]] -name = "bitflags" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "clap" -version = "4.5.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84" -dependencies = [ - "clap_builder", - "clap_derive", -] - -[[package]] -name = "clap_builder" -version = "4.5.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838" -dependencies = [ - "anstream", - "anstyle", - "clap_lex", - "strsim", -] - -[[package]] -name = "clap_derive" -version = "4.5.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "clap_lex" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" - -[[package]] -name = "colorchoice" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" - -[[package]] -name = "directories" -version = "5.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a49173b84e034382284f27f1af4dcbbd231ffa358c0fe316541a7337f376a35" -dependencies = [ - "dirs-sys", -] - -[[package]] -name = "dirs-sys" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" -dependencies = [ - "libc", - "option-ext", - "redox_users", - "windows-sys 0.48.0", -] - -[[package]] -name = "getrandom" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - -[[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" - -[[package]] -name = "is_terminal_polyfill" -version = "1.70.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" - -[[package]] -name = "josh-sync" -version = "0.0.0" -dependencies = [ - "anyhow", - "clap", - "directories", - "xshell", -] - -[[package]] -name = "libc" -version = "0.2.169" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" - -[[package]] -name = "libredox" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" -dependencies = [ - "bitflags", - "libc", -] - -[[package]] -name = "option-ext" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" - -[[package]] -name = "proc-macro2" -version = "1.0.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "redox_users" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" -dependencies = [ - "getrandom", - "libredox", - "thiserror", -] - -[[package]] -name = "strsim" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" - -[[package]] -name = "syn" -version = "2.0.93" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c786062daee0d6db1132800e623df74274a0a87322d8e183338e01b3d98d058" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "thiserror" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "unicode-ident" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" - -[[package]] -name = "utf8parse" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", -] - -[[package]] -name = "windows-sys" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" -dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", -] - -[[package]] -name = "windows-targets" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" -dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" - -[[package]] -name = "xshell" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e7290c623014758632efe00737145b6867b66292c42167f2ec381eb566a373d" -dependencies = [ - "xshell-macros", -] - -[[package]] -name = "xshell-macros" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32ac00cd3f8ec9c1d33fb3e7958a82df6989c42d747bd326c822b1d625283547" diff --git a/src/doc/rustc-dev-guide/josh-sync/Cargo.toml b/src/doc/rustc-dev-guide/josh-sync/Cargo.toml deleted file mode 100644 index 1f8bf2a009333..0000000000000 --- a/src/doc/rustc-dev-guide/josh-sync/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "josh-sync" -edition = "2024" - -[dependencies] -anyhow = "1.0.95" -clap = { version = "4.5.21", features = ["derive"] } -directories = "5" -xshell = "0.2.6" diff --git a/src/doc/rustc-dev-guide/josh-sync/README.md b/src/doc/rustc-dev-guide/josh-sync/README.md deleted file mode 100644 index a3dd876e8b87b..0000000000000 --- a/src/doc/rustc-dev-guide/josh-sync/README.md +++ /dev/null @@ -1,4 +0,0 @@ -# Git josh sync -This utility serves for syncing the josh git subtree to and from the rust-lang/rust repository. - -See CLI help for usage. diff --git a/src/doc/rustc-dev-guide/josh-sync/src/main.rs b/src/doc/rustc-dev-guide/josh-sync/src/main.rs deleted file mode 100644 index aeedee5be22d1..0000000000000 --- a/src/doc/rustc-dev-guide/josh-sync/src/main.rs +++ /dev/null @@ -1,41 +0,0 @@ -use clap::Parser; - -use crate::sync::{GitSync, RustcPullError}; - -mod sync; - -#[derive(clap::Parser)] -enum Args { - /// Pull changes from the main `rustc` repository. - /// This creates new commits that should be then merged into `rustc-dev-guide`. - RustcPull, - /// Push changes from `rustc-dev-guide` to the given `branch` of a `rustc` fork under the given - /// GitHub `username`. - /// The pushed branch should then be merged into the `rustc` repository. - RustcPush { branch: String, github_username: String }, -} - -fn main() -> anyhow::Result<()> { - let args = Args::parse(); - let sync = GitSync::from_current_dir()?; - match args { - Args::RustcPull => { - if let Err(error) = sync.rustc_pull(None) { - match error { - RustcPullError::NothingToPull => { - eprintln!("Nothing to pull"); - std::process::exit(2); - } - RustcPullError::PullFailed(error) => { - eprintln!("Pull failure: {error:?}"); - std::process::exit(1); - } - } - } - } - Args::RustcPush { github_username, branch } => { - sync.rustc_push(github_username, branch)?; - } - } - Ok(()) -} diff --git a/src/doc/rustc-dev-guide/josh-sync/src/sync.rs b/src/doc/rustc-dev-guide/josh-sync/src/sync.rs deleted file mode 100644 index ed38d1403a02b..0000000000000 --- a/src/doc/rustc-dev-guide/josh-sync/src/sync.rs +++ /dev/null @@ -1,275 +0,0 @@ -use std::io::Write; -use std::ops::Not; -use std::path::PathBuf; -use std::time::Duration; -use std::{env, net, process}; - -use anyhow::{Context, anyhow, bail}; -use xshell::{Shell, cmd}; - -/// Used for rustc syncs. -const JOSH_FILTER: &str = ":/src/doc/rustc-dev-guide"; -const JOSH_PORT: u16 = 42042; -const UPSTREAM_REPO: &str = "rust-lang/rust"; - -pub enum RustcPullError { - /// No changes are available to be pulled. - NothingToPull, - /// A rustc-pull has failed, probably a git operation error has occurred. - PullFailed(anyhow::Error), -} - -impl From for RustcPullError -where - E: Into, -{ - fn from(error: E) -> Self { - Self::PullFailed(error.into()) - } -} - -pub struct GitSync { - dir: PathBuf, -} - -/// This code was adapted from the miri repository -/// (https://github.com/rust-lang/miri/blob/6a68a79f38064c3bc30617cca4bdbfb2c336b140/miri-script/src/commands.rs#L236). -impl GitSync { - pub fn from_current_dir() -> anyhow::Result { - Ok(Self { dir: std::env::current_dir()? }) - } - - pub fn rustc_pull(&self, commit: Option) -> Result<(), RustcPullError> { - let sh = Shell::new()?; - sh.change_dir(&self.dir); - let commit = commit.map(Ok).unwrap_or_else(|| { - let rust_repo_head = - cmd!(sh, "git ls-remote https://github.com/{UPSTREAM_REPO}/ HEAD").read()?; - rust_repo_head - .split_whitespace() - .next() - .map(|front| front.trim().to_owned()) - .ok_or_else(|| anyhow!("Could not obtain Rust repo HEAD from remote.")) - })?; - // Make sure the repo is clean. - if cmd!(sh, "git status --untracked-files=no --porcelain").read()?.is_empty().not() { - return Err(anyhow::anyhow!( - "working directory must be clean before performing rustc pull" - ) - .into()); - } - // Make sure josh is running. - let josh = Self::start_josh()?; - let josh_url = - format!("http://localhost:{JOSH_PORT}/{UPSTREAM_REPO}.git@{commit}{JOSH_FILTER}.git"); - - let previous_base_commit = sh.read_file("rust-version")?.trim().to_string(); - if previous_base_commit == commit { - return Err(RustcPullError::NothingToPull); - } - - // Update rust-version file. As a separate commit, since making it part of - // the merge has confused the heck out of josh in the past. - // We pass `--no-verify` to avoid running git hooks. - // We do this before the merge so that if there are merge conflicts, we have - // the right rust-version file while resolving them. - sh.write_file("rust-version", format!("{commit}\n"))?; - const PREPARING_COMMIT_MESSAGE: &str = "Preparing for merge from rustc"; - cmd!(sh, "git commit rust-version --no-verify -m {PREPARING_COMMIT_MESSAGE}") - .run() - .context("FAILED to commit rust-version file, something went wrong")?; - - // Fetch given rustc commit. - cmd!(sh, "git fetch {josh_url}") - .run() - .inspect_err(|_| { - // Try to un-do the previous `git commit`, to leave the repo in the state we found it. - cmd!(sh, "git reset --hard HEAD^") - .run() - .expect("FAILED to clean up again after failed `git fetch`, sorry for that"); - }) - .context("FAILED to fetch new commits, something went wrong (committing the rust-version file has been undone)")?; - - // This should not add any new root commits. So count those before and after merging. - let num_roots = || -> anyhow::Result { - Ok(cmd!(sh, "git rev-list HEAD --max-parents=0 --count") - .read() - .context("failed to determine the number of root commits")? - .parse::()?) - }; - let num_roots_before = num_roots()?; - - let sha = - cmd!(sh, "git rev-parse HEAD").output().context("FAILED to get current commit")?.stdout; - - // Merge the fetched commit. - const MERGE_COMMIT_MESSAGE: &str = "Merge from rustc"; - cmd!(sh, "git merge FETCH_HEAD --no-verify --no-ff -m {MERGE_COMMIT_MESSAGE}") - .run() - .context("FAILED to merge new commits, something went wrong")?; - - let current_sha = - cmd!(sh, "git rev-parse HEAD").output().context("FAILED to get current commit")?.stdout; - if current_sha == sha { - cmd!(sh, "git reset --hard HEAD^") - .run() - .expect("FAILED to clean up after creating the preparation commit"); - eprintln!( - "No merge was performed, no changes to pull were found. Rolled back the preparation commit." - ); - return Err(RustcPullError::NothingToPull); - } - - // Check that the number of roots did not increase. - if num_roots()? != num_roots_before { - return Err(anyhow::anyhow!( - "Josh created a new root commit. This is probably not the history you want." - ) - .into()); - } - - drop(josh); - Ok(()) - } - - pub fn rustc_push(&self, github_user: String, branch: String) -> anyhow::Result<()> { - let sh = Shell::new()?; - sh.change_dir(&self.dir); - let base = sh.read_file("rust-version")?.trim().to_owned(); - // Make sure the repo is clean. - if cmd!(sh, "git status --untracked-files=no --porcelain").read()?.is_empty().not() { - bail!("working directory must be clean before running `rustc-push`"); - } - // Make sure josh is running. - let josh = Self::start_josh()?; - let josh_url = - format!("http://localhost:{JOSH_PORT}/{github_user}/rust.git{JOSH_FILTER}.git"); - - // Find a repo we can do our preparation in. - if let Ok(rustc_git) = env::var("RUSTC_GIT") { - // If rustc_git is `Some`, we'll use an existing fork for the branch updates. - sh.change_dir(rustc_git); - } else { - // Otherwise, do this in the local repo. - println!( - "This will pull a copy of the rust-lang/rust history into this checkout, growing it by about 1GB." - ); - print!( - "To avoid that, abort now and set the `RUSTC_GIT` environment variable to an existing rustc checkout. Proceed? [y/N] " - ); - std::io::stdout().flush()?; - let mut answer = String::new(); - std::io::stdin().read_line(&mut answer)?; - if answer.trim().to_lowercase() != "y" { - std::process::exit(1); - } - }; - // Prepare the branch. Pushing works much better if we use as base exactly - // the commit that we pulled from last time, so we use the `rust-version` - // file to find out which commit that would be. - println!("Preparing {github_user}/rust (base: {base})..."); - if cmd!(sh, "git fetch https://github.com/{github_user}/rust {branch}") - .ignore_stderr() - .read() - .is_ok() - { - println!( - "The branch '{branch}' seems to already exist in 'https://github.com/{github_user}/rust'. Please delete it and try again." - ); - std::process::exit(1); - } - cmd!(sh, "git fetch https://github.com/{UPSTREAM_REPO} {base}").run()?; - cmd!(sh, "git push https://github.com/{github_user}/rust {base}:refs/heads/{branch}") - .ignore_stdout() - .ignore_stderr() // silence the "create GitHub PR" message - .run()?; - println!(); - - // Do the actual push. - sh.change_dir(&self.dir); - println!("Pushing changes..."); - cmd!(sh, "git push {josh_url} HEAD:{branch}").run()?; - println!(); - - // Do a round-trip check to make sure the push worked as expected. - cmd!(sh, "git fetch {josh_url} {branch}").ignore_stderr().read()?; - let head = cmd!(sh, "git rev-parse HEAD").read()?; - let fetch_head = cmd!(sh, "git rev-parse FETCH_HEAD").read()?; - if head != fetch_head { - bail!( - "Josh created a non-roundtrip push! Do NOT merge this into rustc!\n\ - Expected {head}, got {fetch_head}." - ); - } - println!( - "Confirmed that the push round-trips back to rustc-dev-guide properly. Please create a rustc PR:" - ); - println!( - // Open PR with `subtree update` title to silence the `no-merges` triagebot check - " https://github.com/{UPSTREAM_REPO}/compare/{github_user}:{branch}?quick_pull=1&title=rustc-dev-guide+subtree+update&body=r?+@ghost" - ); - - drop(josh); - Ok(()) - } - - fn start_josh() -> anyhow::Result { - // Determine cache directory. - let local_dir = { - let user_dirs = - directories::ProjectDirs::from("org", "rust-lang", "rustc-dev-guide-josh").unwrap(); - user_dirs.cache_dir().to_owned() - }; - - // Start josh, silencing its output. - let mut cmd = process::Command::new("josh-proxy"); - cmd.arg("--local").arg(local_dir); - cmd.arg("--remote").arg("https://github.com"); - cmd.arg("--port").arg(JOSH_PORT.to_string()); - cmd.arg("--no-background"); - cmd.stdout(process::Stdio::null()); - cmd.stderr(process::Stdio::null()); - let josh = cmd.spawn().context("failed to start josh-proxy, make sure it is installed")?; - - // Create a wrapper that stops it on drop. - struct Josh(process::Child); - impl Drop for Josh { - fn drop(&mut self) { - #[cfg(unix)] - { - // Try to gracefully shut it down. - process::Command::new("kill") - .args(["-s", "INT", &self.0.id().to_string()]) - .output() - .expect("failed to SIGINT josh-proxy"); - // Sadly there is no "wait with timeout"... so we just give it some time to finish. - std::thread::sleep(Duration::from_millis(100)); - // Now hopefully it is gone. - if self.0.try_wait().expect("failed to wait for josh-proxy").is_some() { - return; - } - } - // If that didn't work (or we're not on Unix), kill it hard. - eprintln!( - "I have to kill josh-proxy the hard way, let's hope this does not break anything." - ); - self.0.kill().expect("failed to SIGKILL josh-proxy"); - } - } - - // Wait until the port is open. We try every 10ms until 1s passed. - for _ in 0..100 { - // This will generally fail immediately when the port is still closed. - let josh_ready = net::TcpStream::connect_timeout( - &net::SocketAddr::from(([127, 0, 0, 1], JOSH_PORT)), - Duration::from_millis(1), - ); - if josh_ready.is_ok() { - return Ok(Josh(josh)); - } - // Not ready yet. - std::thread::sleep(Duration::from_millis(10)); - } - bail!("Even after waiting for 1s, josh-proxy is still not available.") - } -} From 13dca5e1baede8fe0df76ccba1875a14dda10269 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Fri, 4 Jul 2025 11:44:58 +0200 Subject: [PATCH 08/36] Update CI workflow to use rustc-josh-sync --- src/doc/rustc-dev-guide/.github/workflows/rustc-pull.yml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/doc/rustc-dev-guide/.github/workflows/rustc-pull.yml b/src/doc/rustc-dev-guide/.github/workflows/rustc-pull.yml index 1e430d8b4e65a..7190fc733db01 100644 --- a/src/doc/rustc-dev-guide/.github/workflows/rustc-pull.yml +++ b/src/doc/rustc-dev-guide/.github/workflows/rustc-pull.yml @@ -24,11 +24,10 @@ jobs: run: rustup update stable - uses: Swatinem/rust-cache@v2 with: - workspaces: "josh-sync" # Cache the josh directory with checked out rustc - cache-directories: "/home/runner/.cache/rustc-dev-guide-josh" - - name: Install josh - run: RUSTFLAGS="--cap-lints warn" cargo install josh-proxy --git https://github.com/josh-project/josh --tag r24.10.04 + cache-directories: "/home/runner/.cache/rustc-josh" + - name: Install rustc-josh-sync + run: cargo install --locked --git https://github.com/rust-lang/josh-sync - name: Setup bot git name and email run: | git config --global user.name 'The rustc-dev-guide Cronjob Bot' @@ -38,7 +37,7 @@ jobs: # Turn off -e to disable early exit shell: bash {0} run: | - cargo run --manifest-path josh-sync/Cargo.toml -- rustc-pull + rustc-josh-sync pull exitcode=$? # If no pull was performed, we want to mark this job as successful, From 9d83749cdd02531b000eead51cd0de40ec9a59e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Fri, 4 Jul 2025 13:14:23 +0200 Subject: [PATCH 09/36] Document `rustc-josh-sync` --- src/doc/rustc-dev-guide/README.md | 20 +----- src/doc/rustc-dev-guide/src/external-repos.md | 62 +++++++++++++++---- 2 files changed, 52 insertions(+), 30 deletions(-) diff --git a/src/doc/rustc-dev-guide/README.md b/src/doc/rustc-dev-guide/README.md index f11290e44d8f5..5932da467ab2d 100644 --- a/src/doc/rustc-dev-guide/README.md +++ b/src/doc/rustc-dev-guide/README.md @@ -74,22 +74,4 @@ including the `` marker at the place where you want the TOC. This repository is linked to `rust-lang/rust` as a [josh](https://josh-project.github.io/josh/intro.html) subtree. You can use the [rustc-josh-sync](https://github.com/rust-lang/josh-sync) tool to perform synchronization. -You can install the tool using `cargo install --locked --git https://github.com/rust-lang/josh-sync`. - -### Pull changes from `rust-lang/rust` into this repository - -1) Checkout a new branch that will be used to create a PR into `rust-lang/rustc-dev-guide` -2) Run the pull command - ``` - rustc-josh-sync pull - ``` -3) Push the branch to your fork and create a PR into `rustc-dev-guide` - - If you have `gh` CLI installed, `rustc-josh-sync` can create the PR for you. - -### Push changes from this repository into `rust-lang/rust` - -1) Run the push command to create a branch named `` in a `rustc` fork under the `` account - ``` - rustc-josh-sync push - ``` -2) Create a PR from `` into `rust-lang/rust` +You can find a guide on how to perform the synchronization [here](./src/external-repos.md#synchronizing-a-josh-subtree). diff --git a/src/doc/rustc-dev-guide/src/external-repos.md b/src/doc/rustc-dev-guide/src/external-repos.md index f3170c9222d88..a19b1116de82c 100644 --- a/src/doc/rustc-dev-guide/src/external-repos.md +++ b/src/doc/rustc-dev-guide/src/external-repos.md @@ -9,7 +9,7 @@ There are three main ways we use dependencies: As a general rule: - Use crates.io for libraries that could be useful for others in the ecosystem - Use subtrees for tools that depend on compiler internals and need to be updated if there are breaking -changes + changes - Use submodules for tools that are independent of the compiler ## External Dependencies (subtrees) @@ -23,6 +23,8 @@ The following external projects are managed using some form of a `subtree`: * [rust-analyzer](https://github.com/rust-lang/rust-analyzer) * [rustc_codegen_cranelift](https://github.com/rust-lang/rustc_codegen_cranelift) * [rustc-dev-guide](https://github.com/rust-lang/rustc-dev-guide) +* [compiler-builtins](https://github.com/rust-lang/compiler-builtins) +* [stdarch](https://github.com/rust-lang/stdarch) In contrast to `submodule` dependencies (see below for those), the `subtree` dependencies are just regular files and directories which can @@ -34,20 +36,57 @@ implement a new tool feature or test, that should happen in one collective rustc `subtree` dependencies are currently managed by two distinct approaches: * Using `git subtree` - * `clippy` ([sync guide](https://doc.rust-lang.org/nightly/clippy/development/infrastructure/sync.html#performing-the-sync-from-rust-langrust-to-clippy)) - * `portable-simd` ([sync script](https://github.com/rust-lang/portable-simd/blob/master/subtree-sync.sh)) - * `rustfmt` - * `rustc_codegen_cranelift` ([sync script](https://github.com/rust-lang/rustc_codegen_cranelift/blob/113af154d459e41b3dc2c5d7d878e3d3a8f33c69/scripts/rustup.sh#L7)) + * `clippy` ([sync guide](https://doc.rust-lang.org/nightly/clippy/development/infrastructure/sync.html#performing-the-sync-from-rust-langrust-to-clippy)) + * `portable-simd` ([sync script](https://github.com/rust-lang/portable-simd/blob/master/subtree-sync.sh)) + * `rustfmt` + * `rustc_codegen_cranelift` ([sync script](https://github.com/rust-lang/rustc_codegen_cranelift/blob/113af154d459e41b3dc2c5d7d878e3d3a8f33c69/scripts/rustup.sh#L7)) * Using the [josh] tool - * `miri` ([sync guide](https://github.com/rust-lang/miri/blob/master/CONTRIBUTING.md#advanced-topic-syncing-with-the-rustc-repo)) - * `rust-analyzer` ([sync script](https://github.com/rust-lang/rust-analyzer/blob/2e13684be123eca7181aa48e043e185d8044a84a/xtask/src/release.rs#L147)) - * `rustc-dev-guide` ([sync guide](https://github.com/rust-lang/rustc-dev-guide#synchronizing-josh-subtree-with-rustc)) + * `miri` ([sync guide](https://github.com/rust-lang/miri/blob/master/CONTRIBUTING.md#advanced-topic-syncing-with-the-rustc-repo)) + * `rust-analyzer` ([sync script](https://github.com/rust-lang/rust-analyzer/blob/2e13684be123eca7181aa48e043e185d8044a84a/xtask/src/release.rs#L147)) + * `rustc-dev-guide` + * `compiler-builtins` + * `stdarch` -The [josh] tool is an alternative to git subtrees, which manages git history in a different way and scales better to larger repositories. Specific tooling is required to work with josh, you can check out the `miri` or `rust-analyzer` scripts linked above for inspiration. If you want to migrate a repository dependency from `git subtree` or `git submodule` to josh, you can check out [this guide](https://hackmd.io/7pOuxnkdQDaL1Y1FQr65xg). +### Josh subtrees -Below you can find a guide on how to perform push and pull synchronization with the main rustc repo using `git subtree`, although these instructions might differ repo from repo. +The [josh] tool is an alternative to git subtrees, which manages git history in a different way and scales better to larger repositories. Specific tooling is required to work with josh, you can check out the `miri` or `rust-analyzer` scripts linked above for inspiration. We provide a helper [`rustc-josh-sync`][josh-sync] tool to help with the synchronization, it is described [below](#synchronizing-a-josh-subtree). -### Synchronizing a subtree +### Synchronizing a Josh subtree + +We use a dedicated tool called [`rustc-josh-sync`][josh-sync] for performing Josh subtree updates. +Currently, we are migrating Josh repositories to it. So far, it is used in: + +- rustc-dev-guide + +To use the tool, first install it with `cargo install --locked --git https://github. +com/rust-lang/josh-sync`. + +Both pulls (synchronize changes from rust-lang/rust into the subtree) and pushes (synchronize +changes from the subtree to rust-lang/rust) are performed from the subtree repository (so first +switch to its repository checkout directory in your terminal). + +#### Performing pull +1) Checkout a new branch that will be used to create a PR into the subtree +2) Run the pull command + ``` + rustc-josh-sync pull + ``` +3) Push the branch to your fork and create a PR into the subtree repository + - If you have `gh` CLI installed, `rustc-josh-sync` can create the PR for you. + +#### Performing push + +1) Run the push command to create a branch named `` in a `rustc` fork under the `` account + ``` + rustc-josh-sync push + ``` +2) Create a PR from `` into `rust-lang/rust` + +### Creating a new Josh subtree dependency + +If you want to migrate a repository dependency from `git subtree` or `git submodule` to josh, you can check out [this guide](https://hackmd.io/7pOuxnkdQDaL1Y1FQr65xg). + +### Synchronizing a git subtree Periodically the changes made to subtree based dependencies need to be synchronized between this repository and the upstream tool repositories. @@ -129,3 +168,4 @@ the week leading up to the beta cut. [toolstate website]: https://rust-lang-nursery.github.io/rust-toolstate/ [Toolstate chapter]: https://forge.rust-lang.org/infra/toolstate.html [josh]: https://josh-project.github.io/josh/intro.html +[josh-sync]: https://github.com/rust-lang/josh-sync From f167f9b49ec85dae8756c21f5183cdb1f43c6d4d Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Fri, 4 Jul 2025 14:13:15 +0200 Subject: [PATCH 10/36] external-repos.md: small fixes --- src/doc/rustc-dev-guide/src/external-repos.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/external-repos.md b/src/doc/rustc-dev-guide/src/external-repos.md index a19b1116de82c..fc40b5d2bfa5a 100644 --- a/src/doc/rustc-dev-guide/src/external-repos.md +++ b/src/doc/rustc-dev-guide/src/external-repos.md @@ -49,7 +49,7 @@ implement a new tool feature or test, that should happen in one collective rustc ### Josh subtrees -The [josh] tool is an alternative to git subtrees, which manages git history in a different way and scales better to larger repositories. Specific tooling is required to work with josh, you can check out the `miri` or `rust-analyzer` scripts linked above for inspiration. We provide a helper [`rustc-josh-sync`][josh-sync] tool to help with the synchronization, it is described [below](#synchronizing-a-josh-subtree). +The [josh] tool is an alternative to git subtrees, which manages git history in a different way and scales better to larger repositories. Specific tooling is required to work with josh; you can check out the `miri` or `rust-analyzer` scripts linked above for inspiration. We provide a helper [`rustc-josh-sync`][josh-sync] tool to help with the synchronization, described [below](#synchronizing-a-josh-subtree). ### Synchronizing a Josh subtree @@ -58,8 +58,7 @@ Currently, we are migrating Josh repositories to it. So far, it is used in: - rustc-dev-guide -To use the tool, first install it with `cargo install --locked --git https://github. -com/rust-lang/josh-sync`. +To use the tool, first install it with `cargo install --locked --git https://github.com/rust-lang/josh-sync`. Both pulls (synchronize changes from rust-lang/rust into the subtree) and pushes (synchronize changes from the subtree to rust-lang/rust) are performed from the subtree repository (so first From 922ee88762e36dea38113f84ca3898b2e59531ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Tue, 8 Jul 2025 08:57:05 +0200 Subject: [PATCH 11/36] Mention that stdarch is managed by josh-sync --- src/doc/rustc-dev-guide/src/external-repos.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/external-repos.md b/src/doc/rustc-dev-guide/src/external-repos.md index fc40b5d2bfa5a..0f7322357b9d4 100644 --- a/src/doc/rustc-dev-guide/src/external-repos.md +++ b/src/doc/rustc-dev-guide/src/external-repos.md @@ -43,9 +43,9 @@ implement a new tool feature or test, that should happen in one collective rustc * Using the [josh] tool * `miri` ([sync guide](https://github.com/rust-lang/miri/blob/master/CONTRIBUTING.md#advanced-topic-syncing-with-the-rustc-repo)) * `rust-analyzer` ([sync script](https://github.com/rust-lang/rust-analyzer/blob/2e13684be123eca7181aa48e043e185d8044a84a/xtask/src/release.rs#L147)) - * `rustc-dev-guide` + * `rustc-dev-guide` ([josh sync](#synchronizing-a-josh-subtree)) * `compiler-builtins` - * `stdarch` + * `stdarch` ([josh sync](#synchronizing-a-josh-subtree)) ### Josh subtrees @@ -57,6 +57,7 @@ We use a dedicated tool called [`rustc-josh-sync`][josh-sync] for performing Jos Currently, we are migrating Josh repositories to it. So far, it is used in: - rustc-dev-guide +- stdarch To use the tool, first install it with `cargo install --locked --git https://github.com/rust-lang/josh-sync`. From 24a25dbb88bc6a6dafe7bad2337948f0a2b37c0f Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Tue, 10 Jun 2025 23:46:39 +0800 Subject: [PATCH 12/36] Revert `tests/run-make/mte-ffi` changes Partially reverts commit 40311c4dcf666e8bddcec4a8059e7d9f12bbd363. --- tests/run-make/mte-ffi/bar.h | 2 -- tests/run-make/mte-ffi/bar_float.c | 4 ++-- tests/run-make/mte-ffi/bar_int.c | 2 +- tests/run-make/mte-ffi/bar_string.c | 3 +-- 4 files changed, 4 insertions(+), 7 deletions(-) diff --git a/tests/run-make/mte-ffi/bar.h b/tests/run-make/mte-ffi/bar.h index 9b030c618d184..a2292ae02a308 100644 --- a/tests/run-make/mte-ffi/bar.h +++ b/tests/run-make/mte-ffi/bar.h @@ -1,5 +1,3 @@ -// FIXME(#141600) the mte-ffi test doesn't fail in aarch64-gnu - #ifndef __BAR_H #define __BAR_H diff --git a/tests/run-make/mte-ffi/bar_float.c b/tests/run-make/mte-ffi/bar_float.c index acc2f5d9266d0..a1590f62765a6 100644 --- a/tests/run-make/mte-ffi/bar_float.c +++ b/tests/run-make/mte-ffi/bar_float.c @@ -3,9 +3,9 @@ #include #include "bar.h" -extern void foo(float*); +extern void foo(char*); -void bar(float *ptr) { +void bar(char *ptr) { if (((uintptr_t)ptr >> 56) != 0x1f) { fprintf(stderr, "Top byte corrupted on Rust -> C FFI boundary!\n"); exit(1); diff --git a/tests/run-make/mte-ffi/bar_int.c b/tests/run-make/mte-ffi/bar_int.c index c92e765302c1e..d1c79e95dc9cb 100644 --- a/tests/run-make/mte-ffi/bar_int.c +++ b/tests/run-make/mte-ffi/bar_int.c @@ -5,7 +5,7 @@ extern void foo(unsigned int *); -void bar(unsigned int *ptr) { +void bar(char *ptr) { if (((uintptr_t)ptr >> 56) != 0x1f) { fprintf(stderr, "Top byte corrupted on Rust -> C FFI boundary!\n"); exit(1); diff --git a/tests/run-make/mte-ffi/bar_string.c b/tests/run-make/mte-ffi/bar_string.c index 8e1202f6fd159..5669ffd6695e7 100644 --- a/tests/run-make/mte-ffi/bar_string.c +++ b/tests/run-make/mte-ffi/bar_string.c @@ -1,7 +1,6 @@ #include #include #include -#include #include "bar.h" extern void foo(char*); @@ -33,7 +32,7 @@ int main(void) // Store an arbitrary tag in bits 56-59 of the pointer (where an MTE tag may be), // and a different value in the ignored top 4 bits. - ptr = (char *)((uintptr_t)ptr | 0x1fl << 56); + ptr = (unsigned int *)((uintptr_t)ptr | 0x1fl << 56); if (mte_enabled()) { set_tag(ptr); From 37136dd9748736d68c050d2dc132045b8b57ce3e Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Tue, 8 Jul 2025 16:39:03 +0800 Subject: [PATCH 13/36] Disable `tests/run-make/mte-ffi` See RUST-141600: this test is broken in two ways: 1. This test triggers `-Wincompatible-pointer-types` on GCC 14. 2. This test requires ARMv8.5+ w/ MTE extensions enabled, but GHA CI runner hardware do not have this enabled. --- tests/run-make/mte-ffi/rmake.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/run-make/mte-ffi/rmake.rs b/tests/run-make/mte-ffi/rmake.rs index a8da0dc0ee039..71d8318d9465c 100644 --- a/tests/run-make/mte-ffi/rmake.rs +++ b/tests/run-make/mte-ffi/rmake.rs @@ -2,6 +2,13 @@ //! FFI boundaries (C <-> Rust). This test does not require MTE: whilst the test will use MTE if //! available, if it is not, arbitrary tag bits are set using TBI. +//@ ignore-test (FIXME #141600) +// +// FIXME(#141600): this test is broken in two ways: +// 1. This test triggers `-Wincompatible-pointer-types` on GCC 14. +// 2. This test requires ARMv8.5+ w/ MTE extensions enabled, but GHA CI runner hardware do not have +// this enabled. + //@ only-aarch64-unknown-linux-gnu // Reason: this test is only valid for AArch64 with `gcc`. The linker must be explicitly specified // when cross-compiling, so it is limited to `aarch64-unknown-linux-gnu`. From 6b8bc68a503e0b27828404270759ab3523b1c91a Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Wed, 9 Jul 2025 07:13:44 +0200 Subject: [PATCH 14/36] use a consistent (and recommended) invocation ./x is recommended over running ./x.py directly, and is the more commonly-used invocation of bootstrap in the guide --- src/doc/rustc-dev-guide/src/autodiff/installation.md | 10 +++++----- src/doc/rustc-dev-guide/src/offload/installation.md | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/autodiff/installation.md b/src/doc/rustc-dev-guide/src/autodiff/installation.md index c9b28dc43a6d1..92e79b2f3831d 100644 --- a/src/doc/rustc-dev-guide/src/autodiff/installation.md +++ b/src/doc/rustc-dev-guide/src/autodiff/installation.md @@ -13,7 +13,7 @@ cd rust Afterwards you can build rustc using: ```bash -./x.py build --stage 1 library +./x build --stage 1 library ``` Afterwards rustc toolchain link will allow you to use it through cargo: @@ -25,10 +25,10 @@ rustup toolchain install nightly # enables -Z unstable-options You can then run our test cases: ```bash -./x.py test --stage 1 tests/codegen/autodiff -./x.py test --stage 1 tests/pretty/autodiff -./x.py test --stage 1 tests/ui/autodiff -./x.py test --stage 1 tests/ui/feature-gates/feature-gate-autodiff.rs +./x test --stage 1 tests/codegen/autodiff +./x test --stage 1 tests/pretty/autodiff +./x test --stage 1 tests/ui/autodiff +./x test --stage 1 tests/ui/feature-gates/feature-gate-autodiff.rs ``` Autodiff is still experimental, so if you want to use it in your own projects, you will need to add `lto="fat"` to your Cargo.toml diff --git a/src/doc/rustc-dev-guide/src/offload/installation.md b/src/doc/rustc-dev-guide/src/offload/installation.md index 2536af09a2369..8cfec44ec8e7d 100644 --- a/src/doc/rustc-dev-guide/src/offload/installation.md +++ b/src/doc/rustc-dev-guide/src/offload/installation.md @@ -13,7 +13,7 @@ cd rust Afterwards you can build rustc using: ```bash -./x.py build --stage 1 library +./x build --stage 1 library ``` Afterwards rustc toolchain link will allow you to use it through cargo: @@ -40,7 +40,7 @@ This gives you a working LLVM build. ## Testing run ``` -./x.py test --stage 1 tests/codegen/gpu_offload +./x test --stage 1 tests/codegen/gpu_offload ``` ## Usage From 7b394d2650472866162adb1e3e775d7c43ed8854 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Wed, 9 Jul 2025 07:28:16 +0200 Subject: [PATCH 15/36] tweak some git clone commands - --depth=1 is more useful for once-off uses, like on ci - .git postfix on github repo url is not needed --- src/doc/rustc-dev-guide/src/autodiff/installation.md | 8 ++++---- src/doc/rustc-dev-guide/src/offload/installation.md | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/autodiff/installation.md b/src/doc/rustc-dev-guide/src/autodiff/installation.md index 92e79b2f3831d..a550f6d233eb5 100644 --- a/src/doc/rustc-dev-guide/src/autodiff/installation.md +++ b/src/doc/rustc-dev-guide/src/autodiff/installation.md @@ -6,7 +6,7 @@ In the near future, `std::autodiff` should become available in nightly builds fo First you need to clone and configure the Rust repository: ```bash -git clone --depth=1 git@github.com:rust-lang/rust.git +git clone git@github.com:rust-lang/rust cd rust ./configure --enable-llvm-link-shared --enable-llvm-plugins --enable-llvm-enzyme --release-channel=nightly --enable-llvm-assertions --enable-clang --enable-lld --enable-option-checking --enable-ninja --disable-docs ``` @@ -45,7 +45,7 @@ apt install wget vim python3 git curl libssl-dev pkg-config lld ninja-build cmak ``` Then build rustc in a slightly altered way: ```bash -git clone --depth=1 https://github.com/rust-lang/rust.git +git clone https://github.com/rust-lang/rust cd rust ./configure --enable-llvm-link-shared --enable-llvm-plugins --enable-llvm-enzyme --release-channel=nightly --enable-llvm-assertions --enable-clang --enable-lld --enable-option-checking --enable-ninja --disable-docs ./x dist @@ -66,7 +66,7 @@ We recommend that approach, if you just want to use any of them and have no expe However, if you prefer to just build Enzyme without Rust, then these instructions might help. ```bash -git clone --depth=1 git@github.com:llvm/llvm-project.git +git clone git@github.com:llvm/llvm-project cd llvm-project mkdir build cd build @@ -77,7 +77,7 @@ ninja install This gives you a working LLVM build, now we can continue with building Enzyme. Leave the `llvm-project` folder, and execute the following commands: ```bash -git clone git@github.com:EnzymeAD/Enzyme.git +git clone git@github.com:EnzymeAD/Enzyme cd Enzyme/enzyme mkdir build cd build diff --git a/src/doc/rustc-dev-guide/src/offload/installation.md b/src/doc/rustc-dev-guide/src/offload/installation.md index 8cfec44ec8e7d..1962314c70ac8 100644 --- a/src/doc/rustc-dev-guide/src/offload/installation.md +++ b/src/doc/rustc-dev-guide/src/offload/installation.md @@ -6,7 +6,7 @@ In the future, `std::offload` should become available in nightly builds for user First you need to clone and configure the Rust repository: ```bash -git clone --depth=1 git@github.com:rust-lang/rust.git +git clone git@github.com:rust-lang/rust cd rust ./configure --enable-llvm-link-shared --release-channel=nightly --enable-llvm-assertions --enable-offload --enable-enzyme --enable-clang --enable-lld --enable-option-checking --enable-ninja --disable-docs ``` @@ -26,7 +26,7 @@ rustup toolchain install nightly # enables -Z unstable-options ## Build instruction for LLVM itself ```bash -git clone --depth=1 git@github.com:llvm/llvm-project.git +git clone git@github.com:llvm/llvm-project cd llvm-project mkdir build cd build From 42472f26452505cb42477990e6450bed21b3a50d Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Wed, 9 Jul 2025 22:49:41 +0200 Subject: [PATCH 16/36] add missing word --- src/doc/rustc-dev-guide/src/tests/misc.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/src/tests/misc.md b/src/doc/rustc-dev-guide/src/tests/misc.md index c0288b3dd10cd..39f881748792f 100644 --- a/src/doc/rustc-dev-guide/src/tests/misc.md +++ b/src/doc/rustc-dev-guide/src/tests/misc.md @@ -9,7 +9,7 @@ for testing: - `RUSTC_BOOTSTRAP=1` will "cheat" and bypass usual stability checking, allowing you to use unstable features and cli flags on a stable `rustc`. -- `RUSTC_BOOTSTRAP=-1` will force a given `rustc` to pretend that is a stable +- `RUSTC_BOOTSTRAP=-1` will force a given `rustc` to pretend it is a stable compiler, even if it's actually a nightly `rustc`. This is useful because some behaviors of the compiler (e.g. diagnostics) can differ depending on whether the compiler is nightly or not. From d99af0b77cde2cc7d13419d7ca9b244afbcd365f Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Wed, 9 Jul 2025 23:02:16 +0200 Subject: [PATCH 17/36] distcheck had only one possible invocation That is, calling it an example is misleading --- src/doc/rustc-dev-guide/src/tests/intro.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/src/tests/intro.md b/src/doc/rustc-dev-guide/src/tests/intro.md index c55d60f4a5c7b..e97574f02de2f 100644 --- a/src/doc/rustc-dev-guide/src/tests/intro.md +++ b/src/doc/rustc-dev-guide/src/tests/intro.md @@ -116,7 +116,9 @@ This requires building all of the documentation, which might take a while. `distcheck` verifies that the source distribution tarball created by the build system will unpack, build, and run all tests. -> Example: `./x test distcheck` +```console +./x test distcheck +``` ### Tool tests From 99fc05bb1830f6c6132ff7b1e48e2e655c863ad6 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Wed, 9 Jul 2025 23:03:37 +0200 Subject: [PATCH 18/36] do not invent a name Nowhere else is this called "Dist check" --- src/doc/rustc-dev-guide/src/tests/intro.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/src/tests/intro.md b/src/doc/rustc-dev-guide/src/tests/intro.md index c55d60f4a5c7b..84f99e9461e87 100644 --- a/src/doc/rustc-dev-guide/src/tests/intro.md +++ b/src/doc/rustc-dev-guide/src/tests/intro.md @@ -111,7 +111,7 @@ and it can be invoked so: This requires building all of the documentation, which might take a while. -### Dist check +### `distcheck` `distcheck` verifies that the source distribution tarball created by the build system will unpack, build, and run all tests. From f1ae43f14d2b1737b71fc14c20b1242a74d66c2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Thu, 10 Jul 2025 09:56:59 +0200 Subject: [PATCH 19/36] Mention that compiler-builtins is now using `rustc-josh-sync` --- src/doc/rustc-dev-guide/src/external-repos.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/src/external-repos.md b/src/doc/rustc-dev-guide/src/external-repos.md index 0f7322357b9d4..68986a0bc6886 100644 --- a/src/doc/rustc-dev-guide/src/external-repos.md +++ b/src/doc/rustc-dev-guide/src/external-repos.md @@ -44,7 +44,7 @@ implement a new tool feature or test, that should happen in one collective rustc * `miri` ([sync guide](https://github.com/rust-lang/miri/blob/master/CONTRIBUTING.md#advanced-topic-syncing-with-the-rustc-repo)) * `rust-analyzer` ([sync script](https://github.com/rust-lang/rust-analyzer/blob/2e13684be123eca7181aa48e043e185d8044a84a/xtask/src/release.rs#L147)) * `rustc-dev-guide` ([josh sync](#synchronizing-a-josh-subtree)) - * `compiler-builtins` + * `compiler-builtins` ([josh sync](#synchronizing-a-josh-subtree)) * `stdarch` ([josh sync](#synchronizing-a-josh-subtree)) ### Josh subtrees @@ -56,6 +56,7 @@ The [josh] tool is an alternative to git subtrees, which manages git history in We use a dedicated tool called [`rustc-josh-sync`][josh-sync] for performing Josh subtree updates. Currently, we are migrating Josh repositories to it. So far, it is used in: +- compiler-builtins - rustc-dev-guide - stdarch From c6bedc1bf9752c482e7a2fd58e24c7569fb58555 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Thu, 10 Jul 2025 10:02:39 +0200 Subject: [PATCH 20/36] Migrate rustc-pull to CI workflow from `josh-sync` --- .../.github/workflows/rustc-pull.yml | 111 ++---------------- 1 file changed, 9 insertions(+), 102 deletions(-) diff --git a/src/doc/rustc-dev-guide/.github/workflows/rustc-pull.yml b/src/doc/rustc-dev-guide/.github/workflows/rustc-pull.yml index 7190fc733db01..ad570ee4595d4 100644 --- a/src/doc/rustc-dev-guide/.github/workflows/rustc-pull.yml +++ b/src/doc/rustc-dev-guide/.github/workflows/rustc-pull.yml @@ -9,105 +9,12 @@ on: jobs: pull: if: github.repository == 'rust-lang/rustc-dev-guide' - runs-on: ubuntu-latest - outputs: - pr_url: ${{ steps.update-pr.outputs.pr_url }} - permissions: - contents: write - pull-requests: write - steps: - - uses: actions/checkout@v4 - with: - # We need the full history for josh to work - fetch-depth: '0' - - name: Install stable Rust toolchain - run: rustup update stable - - uses: Swatinem/rust-cache@v2 - with: - # Cache the josh directory with checked out rustc - cache-directories: "/home/runner/.cache/rustc-josh" - - name: Install rustc-josh-sync - run: cargo install --locked --git https://github.com/rust-lang/josh-sync - - name: Setup bot git name and email - run: | - git config --global user.name 'The rustc-dev-guide Cronjob Bot' - git config --global user.email 'github-actions@github.com' - - name: Perform rustc-pull - id: rustc-pull - # Turn off -e to disable early exit - shell: bash {0} - run: | - rustc-josh-sync pull - exitcode=$? - - # If no pull was performed, we want to mark this job as successful, - # but we do not want to perform the follow-up steps. - if [ $exitcode -eq 0 ]; then - echo "pull_result=pull-finished" >> $GITHUB_OUTPUT - elif [ $exitcode -eq 2 ]; then - echo "pull_result=skipped" >> $GITHUB_OUTPUT - exitcode=0 - fi - - exit ${exitcode} - - name: Push changes to a branch - if: ${{ steps.rustc-pull.outputs.pull_result == 'pull-finished' }} - run: | - # Update a sticky branch that is used only for rustc pulls - BRANCH="rustc-pull" - git switch -c $BRANCH - git push -u origin $BRANCH --force - - name: Create pull request - id: update-pr - if: ${{ steps.rustc-pull.outputs.pull_result == 'pull-finished' }} - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - # Check if an open pull request for an rustc pull update already exists - # If it does, the previous push has just updated it - # If not, we create it now - RESULT=`gh pr list --author github-actions[bot] --state open -q 'map(select(.title=="Rustc pull update")) | length' --json title` - if [[ "$RESULT" -eq 0 ]]; then - echo "Creating new pull request" - PR_URL=`gh pr create -B master --title 'Rustc pull update' --body 'Latest update from rustc.'` - echo "pr_url=$PR_URL" >> $GITHUB_OUTPUT - else - PR_URL=`gh pr list --author github-actions[bot] --state open -q 'map(select(.title=="Rustc pull update")) | .[0].url' --json url,title` - echo "Updating pull request ${PR_URL}" - echo "pr_url=$PR_URL" >> $GITHUB_OUTPUT - fi - send-zulip-message: - needs: [pull] - if: ${{ !cancelled() }} - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Compute message - id: create-message - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - if [ "${{ needs.pull.result }}" == "failure" ]; then - WORKFLOW_URL="${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" - echo "message=Rustc pull sync failed. Check out the [workflow URL]($WORKFLOW_URL)." >> $GITHUB_OUTPUT - else - CREATED_AT=`gh pr list --author github-actions[bot] --state open -q 'map(select(.title=="Rustc pull update")) | .[0].createdAt' --json createdAt,title` - PR_URL=`gh pr list --author github-actions[bot] --state open -q 'map(select(.title=="Rustc pull update")) | .[0].url' --json url,title` - week_ago=$(date +%F -d '7 days ago') - - # If there is an open PR that is at least a week old, post a message about it - if [[ -n $DATE_GH && $DATE_GH < $week_ago ]]; then - echo "message=A PR with a Rustc pull has been opened for more a week. Check out the [PR](${PR_URL})." >> $GITHUB_OUTPUT - fi - fi - - name: Send a Zulip message about updated PR - if: ${{ steps.create-message.outputs.message != '' }} - uses: zulip/github-actions-zulip/send-message@e4c8f27c732ba9bd98ac6be0583096dea82feea5 - with: - api-key: ${{ secrets.ZULIP_API_TOKEN }} - email: "rustc-dev-guide-gha-notif-bot@rust-lang.zulipchat.com" - organization-url: "https://rust-lang.zulipchat.com" - to: 196385 - type: "stream" - topic: "Subtree sync automation" - content: ${{ steps.create-message.outputs.message }} + uses: rust-lang/josh-sync/.github/workflows/rustc-pull.yml@main + with: + zulip-stream-id: 196385 + zulip-bot-email: "rustc-dev-guide-gha-notif-bot@rust-lang.zulipchat.com" + pr-base-branch: master + branch-name: rustc-pull + secrets: + zulip-api-token: ${{ secrets.ZULIP_API_TOKEN }} + token: ${{ secrets.GITHUB_TOKEN }} From 7803c25deaa3f330a952a18c7c3d5ea3850e9ff7 Mon Sep 17 00:00:00 2001 From: lolbinarycat Date: Fri, 11 Jul 2025 13:39:39 -0500 Subject: [PATCH 21/36] directives.md: build-aux-docs does not work with aux-crate --- src/doc/rustc-dev-guide/src/tests/directives.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/src/tests/directives.md b/src/doc/rustc-dev-guide/src/tests/directives.md index 839076b809df8..63aa08c389cca 100644 --- a/src/doc/rustc-dev-guide/src/tests/directives.md +++ b/src/doc/rustc-dev-guide/src/tests/directives.md @@ -59,7 +59,7 @@ not be exhaustive. Directives can generally be found by browsing the | `aux-crate` | Like `aux-build` but makes available as extern prelude | All except `run-make` | `=` | | `aux-codegen-backend` | Similar to `aux-build` but pass the compiled dylib to `-Zcodegen-backend` when building the main file | `ui-fulldeps` | Path to codegen backend file | | `proc-macro` | Similar to `aux-build`, but for aux forces host and don't use `-Cprefer-dynamic`[^pm]. | All except `run-make` | Path to auxiliary proc-macro `.rs` file | -| `build-aux-docs` | Build docs for auxiliaries as well | All except `run-make` | N/A | +| `build-aux-docs` | Build docs for auxiliaries as well. Note that this only works with `aux-build`, not `aux-crate`. | All except `run-make` | N/A | [^pm]: please see the Auxiliary proc-macro section in the [compiletest](./compiletest.md) chapter for specifics. From f58accb8f370a0a9e89548f2d890bd09ede3e613 Mon Sep 17 00:00:00 2001 From: usamoi Date: Sun, 13 Jul 2025 00:12:33 +0800 Subject: [PATCH 22/36] pass --gc-sections if -Zexport-executable-symbols is enabled and improve tests --- compiler/rustc_codegen_ssa/src/back/link.rs | 9 ++---- .../export-executable-symbols/rmake.rs | 25 ++++++++-------- tests/ui/linking/export-executable-symbols.rs | 29 +++++++++++++++---- 3 files changed, 40 insertions(+), 23 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index b46773396fce1..ae148287a6017 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -2542,12 +2542,9 @@ fn add_order_independent_options( // sections to ensure we have all the data for PGO. let keep_metadata = crate_type == CrateType::Dylib || sess.opts.cg.profile_generate.enabled(); - if crate_type != CrateType::Executable || !sess.opts.unstable_opts.export_executable_symbols - { - cmd.gc_sections(keep_metadata); - } else { - cmd.no_gc_sections(); - } + cmd.gc_sections(keep_metadata); + } else { + cmd.no_gc_sections(); } cmd.set_output_kind(link_output_kind, crate_type, out_filename); diff --git a/tests/run-make/export-executable-symbols/rmake.rs b/tests/run-make/export-executable-symbols/rmake.rs index 884c7362822f7..c602a43f95723 100644 --- a/tests/run-make/export-executable-symbols/rmake.rs +++ b/tests/run-make/export-executable-symbols/rmake.rs @@ -4,20 +4,21 @@ // symbol. // See https://github.com/rust-lang/rust/pull/85673 -//@ only-unix -// Reason: the export-executable-symbols flag only works on Unix -// due to hardcoded platform-specific implementation -// (See #85673) -//@ ignore-cross-compile //@ ignore-wasm -use run_make_support::{bin_name, llvm_readobj, rustc}; +use run_make_support::object::Object; +use run_make_support::{bin_name, is_darwin, object, rustc}; fn main() { - rustc().arg("-Zexport-executable-symbols").input("main.rs").crate_type("bin").run(); - llvm_readobj() - .symbols() - .input(bin_name("main")) - .run() - .assert_stdout_contains("exported_symbol"); + rustc() + .arg("-Ctarget-feature=-crt-static") + .arg("-Zexport-executable-symbols") + .input("main.rs") + .crate_type("bin") + .run(); + let name: &[u8] = if is_darwin() { b"_exported_symbol" } else { b"exported_symbol" }; + let contents = std::fs::read(bin_name("main")).unwrap(); + let object = object::File::parse(contents.as_slice()).unwrap(); + let found = object.exports().unwrap().iter().any(|x| x.name() == name); + assert!(found); } diff --git a/tests/ui/linking/export-executable-symbols.rs b/tests/ui/linking/export-executable-symbols.rs index aea5527b6a1c1..edaf312f19284 100644 --- a/tests/ui/linking/export-executable-symbols.rs +++ b/tests/ui/linking/export-executable-symbols.rs @@ -1,22 +1,21 @@ //@ run-pass -//@ only-linux -//@ only-gnu -//@ compile-flags: -Zexport-executable-symbols +//@ compile-flags: -Ctarget-feature=-crt-static -Zexport-executable-symbols +//@ ignore-wasm //@ edition: 2024 // Regression test for . #![feature(rustc_private)] -extern crate libc; - #[unsafe(no_mangle)] fn hack() -> u64 { 998244353 } fn main() { + #[cfg(unix)] unsafe { + extern crate libc; let handle = libc::dlopen(std::ptr::null(), libc::RTLD_NOW); let ptr = libc::dlsym(handle, c"hack".as_ptr()); let ptr: Option u64> = std::mem::transmute(ptr); @@ -27,4 +26,24 @@ fn main() { panic!("symbol `hack` is not found"); } } + #[cfg(windows)] + unsafe { + type PCSTR = *const u8; + type HMODULE = *mut core::ffi::c_void; + type FARPROC = Option isize>; + #[link(name = "kernel32", kind = "raw-dylib")] + unsafe extern "system" { + fn GetModuleHandleA(lpmodulename: PCSTR) -> HMODULE; + fn GetProcAddress(hmodule: HMODULE, lpprocname: PCSTR) -> FARPROC; + } + let handle = GetModuleHandleA(std::ptr::null_mut()); + let ptr = GetProcAddress(handle, b"hack\0".as_ptr()); + let ptr: Option u64> = std::mem::transmute(ptr); + if let Some(f) = ptr { + assert!(f() == 998244353); + println!("symbol `hack` is found successfully"); + } else { + panic!("symbol `hack` is not found"); + } + } } From 0e423f441ade436bd732ececd3dd6c79885dd07c Mon Sep 17 00:00:00 2001 From: Edoardo Marangoni Date: Wed, 16 Jul 2025 09:24:30 +0200 Subject: [PATCH 23/36] Boostrap: add warning on `optimize = false` --- src/bootstrap/src/core/config/toml/rust.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/bootstrap/src/core/config/toml/rust.rs b/src/bootstrap/src/core/config/toml/rust.rs index 0fae235bb93fb..01fbd83317ecb 100644 --- a/src/bootstrap/src/core/config/toml/rust.rs +++ b/src/bootstrap/src/core/config/toml/rust.rs @@ -541,6 +541,14 @@ impl Config { lld_enabled = lld_enabled_toml; std_features = std_features_toml; + if optimize_toml.as_ref().is_some_and(|v| matches!(v, RustOptimize::Bool(false))) { + eprintln!( + "WARNING: setting `optimize` to `false` is known to cause errors and \ + should be considered unsupported. Refer to `bootstrap.example.toml` \ + for more details." + ); + } + optimize = optimize_toml; self.rust_new_symbol_mangling = new_symbol_mangling; set(&mut self.rust_optimize_tests, optimize_tests); From 4e054fc4c4809a3c1df60ea99077f4d662b79750 Mon Sep 17 00:00:00 2001 From: Sasha Pourcelot Date: Sun, 13 Jul 2025 11:51:22 +0200 Subject: [PATCH 24/36] Port `#[coverage]` to the new attribute system --- .../src/attributes.rs | 19 ++ .../src/encode_cross_crate.rs | 1 + .../src/attributes/codegen_attrs.rs | 41 +++- compiler/rustc_attr_parsing/src/context.rs | 26 ++- .../src/session_diagnostics.rs | 49 ++++- .../rustc_mir_transform/src/coverage/query.rs | 32 ++- compiler/rustc_parse/src/validate_attr.rs | 1 + compiler/rustc_passes/src/check_attr.rs | 8 +- tests/ui/attributes/malformed-attrs.stderr | 26 +-- .../ui/coverage-attr/bad-attr-ice.feat.stderr | 7 +- .../coverage-attr/bad-attr-ice.nofeat.stderr | 27 +-- tests/ui/coverage-attr/bad-syntax.stderr | 134 +++++++------ tests/ui/coverage-attr/name-value.rs | 7 - tests/ui/coverage-attr/name-value.stderr | 179 +++++------------ tests/ui/coverage-attr/subword.stderr | 33 ++-- tests/ui/coverage-attr/word-only.rs | 7 - tests/ui/coverage-attr/word-only.stderr | 185 +++++------------- 17 files changed, 377 insertions(+), 405 deletions(-) diff --git a/compiler/rustc_attr_data_structures/src/attributes.rs b/compiler/rustc_attr_data_structures/src/attributes.rs index 3dedeb1b3726f..24f52d6f5002f 100644 --- a/compiler/rustc_attr_data_structures/src/attributes.rs +++ b/compiler/rustc_attr_data_structures/src/attributes.rs @@ -110,6 +110,22 @@ pub enum DeprecatedSince { Err, } +#[derive( + Copy, + Debug, + Eq, + PartialEq, + Encodable, + Decodable, + Clone, + HashStable_Generic, + PrintAttribute +)] +pub enum CoverageStatus { + On, + Off, +} + impl Deprecation { /// Whether an item marked with #[deprecated(since = "X")] is currently /// deprecated (i.e., whether X is not greater than the current rustc @@ -274,6 +290,9 @@ pub enum AttributeKind { /// Represents `#[const_trait]`. ConstTrait(Span), + /// Represents `#[coverage]`. + Coverage(Span, CoverageStatus), + ///Represents `#[rustc_deny_explicit_impl]`. DenyExplicitImpl(Span), diff --git a/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs b/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs index 3e2dc0a15b2f3..8105e4fb1255e 100644 --- a/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs +++ b/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs @@ -28,6 +28,7 @@ impl AttributeKind { ConstStability { .. } => Yes, ConstStabilityIndirect => No, ConstTrait(..) => No, + Coverage(..) => No, DenyExplicitImpl(..) => No, Deprecation { .. } => Yes, DoNotImplementViaObject(..) => No, diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs index 34d9b04834825..3e542771d58c9 100644 --- a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs @@ -1,4 +1,4 @@ -use rustc_attr_data_structures::{AttributeKind, OptimizeAttr, UsedBy}; +use rustc_attr_data_structures::{AttributeKind, CoverageStatus, OptimizeAttr, UsedBy}; use rustc_feature::{AttributeTemplate, template}; use rustc_session::parse::feature_err; use rustc_span::{Span, Symbol, sym}; @@ -52,6 +52,45 @@ impl NoArgsAttributeParser for ColdParser { const CREATE: fn(Span) -> AttributeKind = AttributeKind::Cold; } +pub(crate) struct CoverageParser; + +impl SingleAttributeParser for CoverageParser { + const PATH: &[Symbol] = &[sym::coverage]; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const TEMPLATE: AttributeTemplate = template!(OneOf: &[sym::off, sym::on]); + + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { + let Some(args) = args.list() else { + cx.expected_specific_argument_and_list(cx.attr_span, vec!["on", "off"]); + return None; + }; + + let Some(arg) = args.single() else { + cx.expected_single_argument(args.span); + return None; + }; + + let fail_incorrect_argument = |span| cx.expected_specific_argument(span, vec!["on", "off"]); + + let Some(arg) = arg.meta_item() else { + fail_incorrect_argument(args.span); + return None; + }; + + let status = match arg.path().word_sym() { + Some(sym::off) => CoverageStatus::Off, + Some(sym::on) => CoverageStatus::On, + None | Some(_) => { + fail_incorrect_argument(arg.span()); + return None; + } + }; + + Some(AttributeKind::Coverage(cx.attr_span, status)) + } +} + pub(crate) struct ExportNameParser; impl SingleAttributeParser for ExportNameParser { diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 567341d1517dd..043a0524269ad 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -15,8 +15,9 @@ use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym}; use crate::attributes::allow_unstable::{AllowConstFnUnstableParser, AllowInternalUnstableParser}; use crate::attributes::codegen_attrs::{ - ColdParser, ExportNameParser, NakedParser, NoMangleParser, OmitGdbPrettyPrinterSectionParser, - OptimizeParser, TargetFeatureParser, TrackCallerParser, UsedParser, + ColdParser, CoverageParser, ExportNameParser, NakedParser, NoMangleParser, + OmitGdbPrettyPrinterSectionParser, OptimizeParser, TargetFeatureParser, TrackCallerParser, + UsedParser, }; use crate::attributes::confusables::ConfusablesParser; use crate::attributes::deprecation::DeprecationParser; @@ -136,6 +137,7 @@ attribute_parsers!( // tidy-alphabetical-end // tidy-alphabetical-start + Single, Single, Single, Single, @@ -449,6 +451,25 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { reason: AttributeParseErrorReason::ExpectedSpecificArgument { possibilities, strings: false, + list: false, + }, + }) + } + + pub(crate) fn expected_specific_argument_and_list( + &self, + span: Span, + possibilities: Vec<&'static str>, + ) -> ErrorGuaranteed { + self.emit_err(AttributeParseError { + span, + attr_span: self.attr_span, + template: self.template.clone(), + attribute: self.attr_path.clone(), + reason: AttributeParseErrorReason::ExpectedSpecificArgument { + possibilities, + strings: false, + list: true, }, }) } @@ -466,6 +487,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { reason: AttributeParseErrorReason::ExpectedSpecificArgument { possibilities, strings: true, + list: false, }, }) } diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index 97bf3d1c54955..67dac8da3842f 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -525,7 +525,9 @@ pub(crate) struct LinkOrdinalOutOfRange { pub(crate) enum AttributeParseErrorReason { ExpectedNoArgs, - ExpectedStringLiteral { byte_string: Option }, + ExpectedStringLiteral { + byte_string: Option, + }, ExpectedIntegerLiteral, ExpectedAtLeastOneArgument, ExpectedSingleArgument, @@ -533,7 +535,12 @@ pub(crate) enum AttributeParseErrorReason { UnexpectedLiteral, ExpectedNameValue(Option), DuplicateKey(Symbol), - ExpectedSpecificArgument { possibilities: Vec<&'static str>, strings: bool }, + ExpectedSpecificArgument { + possibilities: Vec<&'static str>, + strings: bool, + /// Should we tell the user to write a list when they didn't? + list: bool, + }, } pub(crate) struct AttributeParseError { @@ -607,7 +614,11 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError { format!("expected this to be of the form `{name} = \"...\"`"), ); } - AttributeParseErrorReason::ExpectedSpecificArgument { possibilities, strings } => { + AttributeParseErrorReason::ExpectedSpecificArgument { + possibilities, + strings, + list: false, + } => { let quote = if strings { '"' } else { '`' }; match possibilities.as_slice() { &[] => {} @@ -633,6 +644,38 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError { } } } + AttributeParseErrorReason::ExpectedSpecificArgument { + possibilities, + strings, + list: true, + } => { + let quote = if strings { '"' } else { '`' }; + match possibilities.as_slice() { + &[] => {} + &[x] => { + diag.span_label( + self.span, + format!( + "this attribute is only valid with {quote}{x}{quote} as an argument" + ), + ); + } + [first, second] => { + diag.span_label(self.span, format!("this attribute is only valid with either {quote}{first}{quote} or {quote}{second}{quote} as an argument")); + } + [first @ .., second_to_last, last] => { + let mut res = String::new(); + for i in first { + res.push_str(&format!("{quote}{i}{quote}, ")); + } + res.push_str(&format!( + "{quote}{second_to_last}{quote} or {quote}{last}{quote}" + )); + + diag.span_label(self.span, format!("this attribute is only valid with one of the following arguments: {res}")); + } + } + } } let suggestions = self.template.suggestions(false, &name); diff --git a/compiler/rustc_mir_transform/src/coverage/query.rs b/compiler/rustc_mir_transform/src/coverage/query.rs index ccf76dc710874..986c001de5e80 100644 --- a/compiler/rustc_mir_transform/src/coverage/query.rs +++ b/compiler/rustc_mir_transform/src/coverage/query.rs @@ -1,3 +1,4 @@ +use rustc_attr_data_structures::{AttributeKind, CoverageStatus, find_attr}; use rustc_index::bit_set::DenseBitSet; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::mir::coverage::{BasicCoverageBlock, CoverageIdsInfo, CoverageKind, MappingKind}; @@ -5,7 +6,6 @@ use rustc_middle::mir::{Body, Statement, StatementKind}; use rustc_middle::ty::{self, TyCtxt}; use rustc_middle::util::Providers; use rustc_span::def_id::LocalDefId; -use rustc_span::sym; use tracing::trace; use crate::coverage::counters::node_flow::make_node_counters; @@ -58,26 +58,20 @@ fn is_eligible_for_coverage(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { /// Query implementation for `coverage_attr_on`. fn coverage_attr_on(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { // Check for annotations directly on this def. - if let Some(attr) = tcx.get_attr(def_id, sym::coverage) { - match attr.meta_item_list().as_deref() { - Some([item]) if item.has_name(sym::off) => return false, - Some([item]) if item.has_name(sym::on) => return true, - Some(_) | None => { - // Other possibilities should have been rejected by `rustc_parse::validate_attr`. - // Use `span_delayed_bug` to avoid an ICE in failing builds (#127880). - tcx.dcx().span_delayed_bug(attr.span(), "unexpected value of coverage attribute"); - } + if let Some(coverage_status) = + find_attr!(tcx.get_all_attrs(def_id), AttributeKind::Coverage(_, status) => status) + { + *coverage_status == CoverageStatus::On + } else { + match tcx.opt_local_parent(def_id) { + // Check the parent def (and so on recursively) until we find an + // enclosing attribute or reach the crate root. + Some(parent) => tcx.coverage_attr_on(parent), + // We reached the crate root without seeing a coverage attribute, so + // allow coverage instrumentation by default. + None => true, } } - - match tcx.opt_local_parent(def_id) { - // Check the parent def (and so on recursively) until we find an - // enclosing attribute or reach the crate root. - Some(parent) => tcx.coverage_attr_on(parent), - // We reached the crate root without seeing a coverage attribute, so - // allow coverage instrumentation by default. - None => true, - } } /// Query implementation for `coverage_ids_info`. diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs index 783d79d978ab6..a476f0db37e0d 100644 --- a/compiler/rustc_parse/src/validate_attr.rs +++ b/compiler/rustc_parse/src/validate_attr.rs @@ -318,6 +318,7 @@ pub fn check_builtin_meta_item( | sym::rustc_layout_scalar_valid_range_end | sym::no_implicit_prelude | sym::automatically_derived + | sym::coverage ) { return; } diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 2009ddc1e2d59..810472eb2dedb 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -285,6 +285,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> { &Attribute::Parsed(AttributeKind::StdInternalSymbol(attr_span)) => { self.check_rustc_std_internal_symbol(attr_span, span, target) } + &Attribute::Parsed(AttributeKind::Coverage(attr_span, _)) => { + self.check_coverage(attr_span, span, target) + } Attribute::Unparsed(attr_item) => { style = Some(attr_item.style); match attr.path().as_slice() { @@ -294,7 +297,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { [sym::diagnostic, sym::on_unimplemented, ..] => { self.check_diagnostic_on_unimplemented(attr.span(), hir_id, target) } - [sym::coverage, ..] => self.check_coverage(attr, span, target), [sym::no_sanitize, ..] => { self.check_no_sanitize(attr, span, target) } @@ -585,7 +587,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { /// Checks that `#[coverage(..)]` is applied to a function/closure/method, /// or to an impl block or module. - fn check_coverage(&self, attr: &Attribute, target_span: Span, target: Target) { + fn check_coverage(&self, attr_span: Span, target_span: Span, target: Target) { let mut not_fn_impl_mod = None; let mut no_body = None; @@ -608,7 +610,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } self.dcx().emit_err(errors::CoverageAttributeNotAllowed { - attr_span: attr.span(), + attr_span, not_fn_impl_mod, no_body, help: (), diff --git a/tests/ui/attributes/malformed-attrs.stderr b/tests/ui/attributes/malformed-attrs.stderr index 56f2be353e768..0d0c338d30269 100644 --- a/tests/ui/attributes/malformed-attrs.stderr +++ b/tests/ui/attributes/malformed-attrs.stderr @@ -37,19 +37,6 @@ error: malformed `crate_name` attribute input LL | #[crate_name] | ^^^^^^^^^^^^^ help: must be of the form: `#[crate_name = "name"]` -error: malformed `coverage` attribute input - --> $DIR/malformed-attrs.rs:90:1 - | -LL | #[coverage] - | ^^^^^^^^^^^ - | -help: the following are the possible correct uses - | -LL | #[coverage(off)] - | +++++ -LL | #[coverage(on)] - | ++++ - error: malformed `no_sanitize` attribute input --> $DIR/malformed-attrs.rs:92:1 | @@ -460,6 +447,19 @@ error[E0539]: malformed `link_section` attribute input LL | #[link_section] | ^^^^^^^^^^^^^^^ help: must be of the form: `#[link_section = "name"]` +error[E0539]: malformed `coverage` attribute input + --> $DIR/malformed-attrs.rs:90:1 + | +LL | #[coverage] + | ^^^^^^^^^^^ this attribute is only valid with either `on` or `off` as an argument + | +help: try changing it to one of the following valid forms of the attribute + | +LL | #[coverage(off)] + | +++++ +LL | #[coverage(on)] + | ++++ + error[E0565]: malformed `no_implicit_prelude` attribute input --> $DIR/malformed-attrs.rs:97:1 | diff --git a/tests/ui/coverage-attr/bad-attr-ice.feat.stderr b/tests/ui/coverage-attr/bad-attr-ice.feat.stderr index dc84394fe3c0c..5a003af42da57 100644 --- a/tests/ui/coverage-attr/bad-attr-ice.feat.stderr +++ b/tests/ui/coverage-attr/bad-attr-ice.feat.stderr @@ -1,10 +1,10 @@ -error: malformed `coverage` attribute input +error[E0539]: malformed `coverage` attribute input --> $DIR/bad-attr-ice.rs:11:1 | LL | #[coverage] - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ this attribute is only valid with either `on` or `off` as an argument | -help: the following are the possible correct uses +help: try changing it to one of the following valid forms of the attribute | LL | #[coverage(off)] | +++++ @@ -13,3 +13,4 @@ LL | #[coverage(on)] error: aborting due to 1 previous error +For more information about this error, try `rustc --explain E0539`. diff --git a/tests/ui/coverage-attr/bad-attr-ice.nofeat.stderr b/tests/ui/coverage-attr/bad-attr-ice.nofeat.stderr index 49b8974bfdfb8..4501e5e9dc821 100644 --- a/tests/ui/coverage-attr/bad-attr-ice.nofeat.stderr +++ b/tests/ui/coverage-attr/bad-attr-ice.nofeat.stderr @@ -1,26 +1,27 @@ -error: malformed `coverage` attribute input +error[E0658]: the `#[coverage]` attribute is an experimental feature --> $DIR/bad-attr-ice.rs:11:1 | LL | #[coverage] | ^^^^^^^^^^^ | -help: the following are the possible correct uses - | -LL | #[coverage(off)] - | +++++ -LL | #[coverage(on)] - | ++++ + = note: see issue #84605 for more information + = help: add `#![feature(coverage_attribute)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: the `#[coverage]` attribute is an experimental feature +error[E0539]: malformed `coverage` attribute input --> $DIR/bad-attr-ice.rs:11:1 | LL | #[coverage] - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ this attribute is only valid with either `on` or `off` as an argument | - = note: see issue #84605 for more information - = help: add `#![feature(coverage_attribute)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date +help: try changing it to one of the following valid forms of the attribute + | +LL | #[coverage(off)] + | +++++ +LL | #[coverage(on)] + | ++++ error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0658`. +Some errors have detailed explanations: E0539, E0658. +For more information about an error, try `rustc --explain E0539`. diff --git a/tests/ui/coverage-attr/bad-syntax.stderr b/tests/ui/coverage-attr/bad-syntax.stderr index fa500b542097d..927f61da08db8 100644 --- a/tests/ui/coverage-attr/bad-syntax.stderr +++ b/tests/ui/coverage-attr/bad-syntax.stderr @@ -1,23 +1,59 @@ -error: malformed `coverage` attribute input +error: expected identifier, found `,` + --> $DIR/bad-syntax.rs:44:12 + | +LL | #[coverage(,off)] + | ^ expected identifier + | +help: remove this comma + | +LL - #[coverage(,off)] +LL + #[coverage(off)] + | + +error: multiple `coverage` attributes + --> $DIR/bad-syntax.rs:9:1 + | +LL | #[coverage(off)] + | ^^^^^^^^^^^^^^^^ help: remove this attribute + | +note: attribute also specified here + --> $DIR/bad-syntax.rs:10:1 + | +LL | #[coverage(off)] + | ^^^^^^^^^^^^^^^^ + +error: multiple `coverage` attributes + --> $DIR/bad-syntax.rs:13:1 + | +LL | #[coverage(off)] + | ^^^^^^^^^^^^^^^^ help: remove this attribute + | +note: attribute also specified here + --> $DIR/bad-syntax.rs:14:1 + | +LL | #[coverage(on)] + | ^^^^^^^^^^^^^^^ + +error[E0539]: malformed `coverage` attribute input --> $DIR/bad-syntax.rs:17:1 | LL | #[coverage] - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ this attribute is only valid with either `on` or `off` as an argument | -help: the following are the possible correct uses +help: try changing it to one of the following valid forms of the attribute | LL | #[coverage(off)] | +++++ LL | #[coverage(on)] | ++++ -error: malformed `coverage` attribute input +error[E0539]: malformed `coverage` attribute input --> $DIR/bad-syntax.rs:20:1 | LL | #[coverage = true] - | ^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^ this attribute is only valid with either `on` or `off` as an argument | -help: the following are the possible correct uses +help: try changing it to one of the following valid forms of the attribute | LL - #[coverage = true] LL + #[coverage(off)] @@ -26,26 +62,30 @@ LL - #[coverage = true] LL + #[coverage(on)] | -error: malformed `coverage` attribute input +error[E0805]: malformed `coverage` attribute input --> $DIR/bad-syntax.rs:23:1 | LL | #[coverage()] - | ^^^^^^^^^^^^^ + | ^^^^^^^^^^--^ + | | + | expected a single argument here | -help: the following are the possible correct uses +help: try changing it to one of the following valid forms of the attribute | LL | #[coverage(off)] | +++ LL | #[coverage(on)] | ++ -error: malformed `coverage` attribute input +error[E0805]: malformed `coverage` attribute input --> $DIR/bad-syntax.rs:26:1 | LL | #[coverage(off, off)] - | ^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^----------^ + | | + | expected a single argument here | -help: the following are the possible correct uses +help: try changing it to one of the following valid forms of the attribute | LL - #[coverage(off, off)] LL + #[coverage(off)] @@ -54,13 +94,15 @@ LL - #[coverage(off, off)] LL + #[coverage(on)] | -error: malformed `coverage` attribute input +error[E0805]: malformed `coverage` attribute input --> $DIR/bad-syntax.rs:29:1 | LL | #[coverage(off, on)] - | ^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^---------^ + | | + | expected a single argument here | -help: the following are the possible correct uses +help: try changing it to one of the following valid forms of the attribute | LL - #[coverage(off, on)] LL + #[coverage(off)] @@ -69,13 +111,15 @@ LL - #[coverage(off, on)] LL + #[coverage(on)] | -error: malformed `coverage` attribute input +error[E0539]: malformed `coverage` attribute input --> $DIR/bad-syntax.rs:32:1 | LL | #[coverage(bogus)] - | ^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^-----^^ + | | + | valid arguments are `on` or `off` | -help: the following are the possible correct uses +help: try changing it to one of the following valid forms of the attribute | LL - #[coverage(bogus)] LL + #[coverage(off)] @@ -84,13 +128,15 @@ LL - #[coverage(bogus)] LL + #[coverage(on)] | -error: malformed `coverage` attribute input +error[E0805]: malformed `coverage` attribute input --> $DIR/bad-syntax.rs:35:1 | LL | #[coverage(bogus, off)] - | ^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^------------^ + | | + | expected a single argument here | -help: the following are the possible correct uses +help: try changing it to one of the following valid forms of the attribute | LL - #[coverage(bogus, off)] LL + #[coverage(off)] @@ -99,13 +145,15 @@ LL - #[coverage(bogus, off)] LL + #[coverage(on)] | -error: malformed `coverage` attribute input +error[E0805]: malformed `coverage` attribute input --> $DIR/bad-syntax.rs:38:1 | LL | #[coverage(off, bogus)] - | ^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^------------^ + | | + | expected a single argument here | -help: the following are the possible correct uses +help: try changing it to one of the following valid forms of the attribute | LL - #[coverage(off, bogus)] LL + #[coverage(off)] @@ -114,41 +162,7 @@ LL - #[coverage(off, bogus)] LL + #[coverage(on)] | -error: expected identifier, found `,` - --> $DIR/bad-syntax.rs:44:12 - | -LL | #[coverage(,off)] - | ^ expected identifier - | -help: remove this comma - | -LL - #[coverage(,off)] -LL + #[coverage(off)] - | - -error: multiple `coverage` attributes - --> $DIR/bad-syntax.rs:9:1 - | -LL | #[coverage(off)] - | ^^^^^^^^^^^^^^^^ help: remove this attribute - | -note: attribute also specified here - --> $DIR/bad-syntax.rs:10:1 - | -LL | #[coverage(off)] - | ^^^^^^^^^^^^^^^^ - -error: multiple `coverage` attributes - --> $DIR/bad-syntax.rs:13:1 - | -LL | #[coverage(off)] - | ^^^^^^^^^^^^^^^^ help: remove this attribute - | -note: attribute also specified here - --> $DIR/bad-syntax.rs:14:1 - | -LL | #[coverage(on)] - | ^^^^^^^^^^^^^^^ - error: aborting due to 11 previous errors +Some errors have detailed explanations: E0539, E0805. +For more information about an error, try `rustc --explain E0539`. diff --git a/tests/ui/coverage-attr/name-value.rs b/tests/ui/coverage-attr/name-value.rs index ffd9afe2ce186..8171dbbf69278 100644 --- a/tests/ui/coverage-attr/name-value.rs +++ b/tests/ui/coverage-attr/name-value.rs @@ -20,7 +20,6 @@ mod my_mod_inner { #[coverage = "off"] //~^ ERROR malformed `coverage` attribute input -//~| ERROR [E0788] struct MyStruct; #[coverage = "off"] @@ -28,22 +27,18 @@ struct MyStruct; impl MyStruct { #[coverage = "off"] //~^ ERROR malformed `coverage` attribute input - //~| ERROR [E0788] const X: u32 = 7; } #[coverage = "off"] //~^ ERROR malformed `coverage` attribute input -//~| ERROR [E0788] trait MyTrait { #[coverage = "off"] //~^ ERROR malformed `coverage` attribute input - //~| ERROR [E0788] const X: u32; #[coverage = "off"] //~^ ERROR malformed `coverage` attribute input - //~| ERROR [E0788] type T; } @@ -52,12 +47,10 @@ trait MyTrait { impl MyTrait for MyStruct { #[coverage = "off"] //~^ ERROR malformed `coverage` attribute input - //~| ERROR [E0788] const X: u32 = 8; #[coverage = "off"] //~^ ERROR malformed `coverage` attribute input - //~| ERROR [E0788] type T = (); } diff --git a/tests/ui/coverage-attr/name-value.stderr b/tests/ui/coverage-attr/name-value.stderr index f24db78415e39..a838ec5df8ead 100644 --- a/tests/ui/coverage-attr/name-value.stderr +++ b/tests/ui/coverage-attr/name-value.stderr @@ -1,10 +1,10 @@ -error: malformed `coverage` attribute input +error[E0539]: malformed `coverage` attribute input --> $DIR/name-value.rs:12:1 | LL | #[coverage = "off"] - | ^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^ this attribute is only valid with either `on` or `off` as an argument | -help: the following are the possible correct uses +help: try changing it to one of the following valid forms of the attribute | LL - #[coverage = "off"] LL + #[coverage(off)] @@ -13,28 +13,28 @@ LL - #[coverage = "off"] LL + #[coverage(on)] | -error: malformed `coverage` attribute input +error[E0539]: malformed `coverage` attribute input --> $DIR/name-value.rs:17:5 | LL | #![coverage = "off"] - | ^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^ this attribute is only valid with either `on` or `off` as an argument | -help: the following are the possible correct uses +help: try changing it to one of the following valid forms of the attribute | LL - #![coverage = "off"] -LL + #![coverage(off)] +LL + #[coverage(off)] | LL - #![coverage = "off"] -LL + #![coverage(on)] +LL + #[coverage(on)] | -error: malformed `coverage` attribute input +error[E0539]: malformed `coverage` attribute input --> $DIR/name-value.rs:21:1 | LL | #[coverage = "off"] - | ^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^ this attribute is only valid with either `on` or `off` as an argument | -help: the following are the possible correct uses +help: try changing it to one of the following valid forms of the attribute | LL - #[coverage = "off"] LL + #[coverage(off)] @@ -43,13 +43,13 @@ LL - #[coverage = "off"] LL + #[coverage(on)] | -error: malformed `coverage` attribute input - --> $DIR/name-value.rs:26:1 +error[E0539]: malformed `coverage` attribute input + --> $DIR/name-value.rs:25:1 | LL | #[coverage = "off"] - | ^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^ this attribute is only valid with either `on` or `off` as an argument | -help: the following are the possible correct uses +help: try changing it to one of the following valid forms of the attribute | LL - #[coverage = "off"] LL + #[coverage(off)] @@ -58,13 +58,13 @@ LL - #[coverage = "off"] LL + #[coverage(on)] | -error: malformed `coverage` attribute input - --> $DIR/name-value.rs:29:5 +error[E0539]: malformed `coverage` attribute input + --> $DIR/name-value.rs:28:5 | LL | #[coverage = "off"] - | ^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^ this attribute is only valid with either `on` or `off` as an argument | -help: the following are the possible correct uses +help: try changing it to one of the following valid forms of the attribute | LL - #[coverage = "off"] LL + #[coverage(off)] @@ -73,13 +73,13 @@ LL - #[coverage = "off"] LL + #[coverage(on)] | -error: malformed `coverage` attribute input - --> $DIR/name-value.rs:35:1 +error[E0539]: malformed `coverage` attribute input + --> $DIR/name-value.rs:33:1 | LL | #[coverage = "off"] - | ^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^ this attribute is only valid with either `on` or `off` as an argument | -help: the following are the possible correct uses +help: try changing it to one of the following valid forms of the attribute | LL - #[coverage = "off"] LL + #[coverage(off)] @@ -88,13 +88,13 @@ LL - #[coverage = "off"] LL + #[coverage(on)] | -error: malformed `coverage` attribute input - --> $DIR/name-value.rs:39:5 +error[E0539]: malformed `coverage` attribute input + --> $DIR/name-value.rs:36:5 | LL | #[coverage = "off"] - | ^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^ this attribute is only valid with either `on` or `off` as an argument | -help: the following are the possible correct uses +help: try changing it to one of the following valid forms of the attribute | LL - #[coverage = "off"] LL + #[coverage(off)] @@ -103,13 +103,13 @@ LL - #[coverage = "off"] LL + #[coverage(on)] | -error: malformed `coverage` attribute input - --> $DIR/name-value.rs:44:5 +error[E0539]: malformed `coverage` attribute input + --> $DIR/name-value.rs:40:5 | LL | #[coverage = "off"] - | ^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^ this attribute is only valid with either `on` or `off` as an argument | -help: the following are the possible correct uses +help: try changing it to one of the following valid forms of the attribute | LL - #[coverage = "off"] LL + #[coverage(off)] @@ -118,13 +118,13 @@ LL - #[coverage = "off"] LL + #[coverage(on)] | -error: malformed `coverage` attribute input - --> $DIR/name-value.rs:50:1 +error[E0539]: malformed `coverage` attribute input + --> $DIR/name-value.rs:45:1 | LL | #[coverage = "off"] - | ^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^ this attribute is only valid with either `on` or `off` as an argument | -help: the following are the possible correct uses +help: try changing it to one of the following valid forms of the attribute | LL - #[coverage = "off"] LL + #[coverage(off)] @@ -133,13 +133,13 @@ LL - #[coverage = "off"] LL + #[coverage(on)] | -error: malformed `coverage` attribute input - --> $DIR/name-value.rs:53:5 +error[E0539]: malformed `coverage` attribute input + --> $DIR/name-value.rs:48:5 | LL | #[coverage = "off"] - | ^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^ this attribute is only valid with either `on` or `off` as an argument | -help: the following are the possible correct uses +help: try changing it to one of the following valid forms of the attribute | LL - #[coverage = "off"] LL + #[coverage(off)] @@ -148,13 +148,13 @@ LL - #[coverage = "off"] LL + #[coverage(on)] | -error: malformed `coverage` attribute input - --> $DIR/name-value.rs:58:5 +error[E0539]: malformed `coverage` attribute input + --> $DIR/name-value.rs:52:5 | LL | #[coverage = "off"] - | ^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^ this attribute is only valid with either `on` or `off` as an argument | -help: the following are the possible correct uses +help: try changing it to one of the following valid forms of the attribute | LL - #[coverage = "off"] LL + #[coverage(off)] @@ -163,13 +163,13 @@ LL - #[coverage = "off"] LL + #[coverage(on)] | -error: malformed `coverage` attribute input - --> $DIR/name-value.rs:64:1 +error[E0539]: malformed `coverage` attribute input + --> $DIR/name-value.rs:57:1 | LL | #[coverage = "off"] - | ^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^ this attribute is only valid with either `on` or `off` as an argument | -help: the following are the possible correct uses +help: try changing it to one of the following valid forms of the attribute | LL - #[coverage = "off"] LL + #[coverage(off)] @@ -178,87 +178,6 @@ LL - #[coverage = "off"] LL + #[coverage(on)] | -error[E0788]: coverage attribute not allowed here - --> $DIR/name-value.rs:21:1 - | -LL | #[coverage = "off"] - | ^^^^^^^^^^^^^^^^^^^ -... -LL | struct MyStruct; - | ---------------- not a function, impl block, or module - | - = help: coverage attribute can be applied to a function (with body), impl block, or module - -error[E0788]: coverage attribute not allowed here - --> $DIR/name-value.rs:35:1 - | -LL | #[coverage = "off"] - | ^^^^^^^^^^^^^^^^^^^ -... -LL | / trait MyTrait { -LL | | #[coverage = "off"] -... | -LL | | type T; -LL | | } - | |_- not a function, impl block, or module - | - = help: coverage attribute can be applied to a function (with body), impl block, or module - -error[E0788]: coverage attribute not allowed here - --> $DIR/name-value.rs:39:5 - | -LL | #[coverage = "off"] - | ^^^^^^^^^^^^^^^^^^^ -... -LL | const X: u32; - | ------------- not a function, impl block, or module - | - = help: coverage attribute can be applied to a function (with body), impl block, or module - -error[E0788]: coverage attribute not allowed here - --> $DIR/name-value.rs:44:5 - | -LL | #[coverage = "off"] - | ^^^^^^^^^^^^^^^^^^^ -... -LL | type T; - | ------- not a function, impl block, or module - | - = help: coverage attribute can be applied to a function (with body), impl block, or module - -error[E0788]: coverage attribute not allowed here - --> $DIR/name-value.rs:29:5 - | -LL | #[coverage = "off"] - | ^^^^^^^^^^^^^^^^^^^ -... -LL | const X: u32 = 7; - | ----------------- not a function, impl block, or module - | - = help: coverage attribute can be applied to a function (with body), impl block, or module - -error[E0788]: coverage attribute not allowed here - --> $DIR/name-value.rs:53:5 - | -LL | #[coverage = "off"] - | ^^^^^^^^^^^^^^^^^^^ -... -LL | const X: u32 = 8; - | ----------------- not a function, impl block, or module - | - = help: coverage attribute can be applied to a function (with body), impl block, or module - -error[E0788]: coverage attribute not allowed here - --> $DIR/name-value.rs:58:5 - | -LL | #[coverage = "off"] - | ^^^^^^^^^^^^^^^^^^^ -... -LL | type T = (); - | ------------ not a function, impl block, or module - | - = help: coverage attribute can be applied to a function (with body), impl block, or module - -error: aborting due to 19 previous errors +error: aborting due to 12 previous errors -For more information about this error, try `rustc --explain E0788`. +For more information about this error, try `rustc --explain E0539`. diff --git a/tests/ui/coverage-attr/subword.stderr b/tests/ui/coverage-attr/subword.stderr index a5d1a492181be..32a09a10c8492 100644 --- a/tests/ui/coverage-attr/subword.stderr +++ b/tests/ui/coverage-attr/subword.stderr @@ -1,10 +1,12 @@ -error: malformed `coverage` attribute input +error[E0539]: malformed `coverage` attribute input --> $DIR/subword.rs:8:1 | LL | #[coverage(yes(milord))] - | ^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^-----------^^ + | | + | valid arguments are `on` or `off` | -help: the following are the possible correct uses +help: try changing it to one of the following valid forms of the attribute | LL - #[coverage(yes(milord))] LL + #[coverage(off)] @@ -13,13 +15,15 @@ LL - #[coverage(yes(milord))] LL + #[coverage(on)] | -error: malformed `coverage` attribute input +error[E0539]: malformed `coverage` attribute input --> $DIR/subword.rs:11:1 | LL | #[coverage(no(milord))] - | ^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^----------^^ + | | + | valid arguments are `on` or `off` | -help: the following are the possible correct uses +help: try changing it to one of the following valid forms of the attribute | LL - #[coverage(no(milord))] LL + #[coverage(off)] @@ -28,13 +32,15 @@ LL - #[coverage(no(milord))] LL + #[coverage(on)] | -error: malformed `coverage` attribute input +error[E0539]: malformed `coverage` attribute input --> $DIR/subword.rs:14:1 | LL | #[coverage(yes = "milord")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^--------------^^ + | | + | valid arguments are `on` or `off` | -help: the following are the possible correct uses +help: try changing it to one of the following valid forms of the attribute | LL - #[coverage(yes = "milord")] LL + #[coverage(off)] @@ -43,13 +49,15 @@ LL - #[coverage(yes = "milord")] LL + #[coverage(on)] | -error: malformed `coverage` attribute input +error[E0539]: malformed `coverage` attribute input --> $DIR/subword.rs:17:1 | LL | #[coverage(no = "milord")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^-------------^^ + | | + | valid arguments are `on` or `off` | -help: the following are the possible correct uses +help: try changing it to one of the following valid forms of the attribute | LL - #[coverage(no = "milord")] LL + #[coverage(off)] @@ -60,3 +68,4 @@ LL + #[coverage(on)] error: aborting due to 4 previous errors +For more information about this error, try `rustc --explain E0539`. diff --git a/tests/ui/coverage-attr/word-only.rs b/tests/ui/coverage-attr/word-only.rs index d0f743938f3af..81bd558b8b002 100644 --- a/tests/ui/coverage-attr/word-only.rs +++ b/tests/ui/coverage-attr/word-only.rs @@ -20,7 +20,6 @@ mod my_mod_inner { #[coverage] //~^ ERROR malformed `coverage` attribute input -//~| ERROR [E0788] struct MyStruct; #[coverage] @@ -28,22 +27,18 @@ struct MyStruct; impl MyStruct { #[coverage] //~^ ERROR malformed `coverage` attribute input - //~| ERROR [E0788] const X: u32 = 7; } #[coverage] //~^ ERROR malformed `coverage` attribute input -//~| ERROR [E0788] trait MyTrait { #[coverage] //~^ ERROR malformed `coverage` attribute input - //~| ERROR [E0788] const X: u32; #[coverage] //~^ ERROR malformed `coverage` attribute input - //~| ERROR [E0788] type T; } @@ -52,12 +47,10 @@ trait MyTrait { impl MyTrait for MyStruct { #[coverage] //~^ ERROR malformed `coverage` attribute input - //~| ERROR [E0788] const X: u32 = 8; #[coverage] //~^ ERROR malformed `coverage` attribute input - //~| ERROR [E0788] type T = (); } diff --git a/tests/ui/coverage-attr/word-only.stderr b/tests/ui/coverage-attr/word-only.stderr index 2773db9c85786..dd161360a5c4b 100644 --- a/tests/ui/coverage-attr/word-only.stderr +++ b/tests/ui/coverage-attr/word-only.stderr @@ -1,240 +1,161 @@ -error: malformed `coverage` attribute input +error[E0539]: malformed `coverage` attribute input --> $DIR/word-only.rs:12:1 | LL | #[coverage] - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ this attribute is only valid with either `on` or `off` as an argument | -help: the following are the possible correct uses +help: try changing it to one of the following valid forms of the attribute | LL | #[coverage(off)] | +++++ LL | #[coverage(on)] | ++++ -error: malformed `coverage` attribute input +error[E0539]: malformed `coverage` attribute input --> $DIR/word-only.rs:17:5 | LL | #![coverage] - | ^^^^^^^^^^^^ + | ^^^^^^^^^^^^ this attribute is only valid with either `on` or `off` as an argument | -help: the following are the possible correct uses +help: try changing it to one of the following valid forms of the attribute + | +LL - #![coverage] +LL + #[coverage(off)] + | +LL - #![coverage] +LL + #[coverage(on)] | -LL | #![coverage(off)] - | +++++ -LL | #![coverage(on)] - | ++++ -error: malformed `coverage` attribute input +error[E0539]: malformed `coverage` attribute input --> $DIR/word-only.rs:21:1 | LL | #[coverage] - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ this attribute is only valid with either `on` or `off` as an argument | -help: the following are the possible correct uses +help: try changing it to one of the following valid forms of the attribute | LL | #[coverage(off)] | +++++ LL | #[coverage(on)] | ++++ -error: malformed `coverage` attribute input - --> $DIR/word-only.rs:26:1 +error[E0539]: malformed `coverage` attribute input + --> $DIR/word-only.rs:25:1 | LL | #[coverage] - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ this attribute is only valid with either `on` or `off` as an argument | -help: the following are the possible correct uses +help: try changing it to one of the following valid forms of the attribute | LL | #[coverage(off)] | +++++ LL | #[coverage(on)] | ++++ -error: malformed `coverage` attribute input - --> $DIR/word-only.rs:29:5 +error[E0539]: malformed `coverage` attribute input + --> $DIR/word-only.rs:28:5 | LL | #[coverage] - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ this attribute is only valid with either `on` or `off` as an argument | -help: the following are the possible correct uses +help: try changing it to one of the following valid forms of the attribute | LL | #[coverage(off)] | +++++ LL | #[coverage(on)] | ++++ -error: malformed `coverage` attribute input - --> $DIR/word-only.rs:35:1 +error[E0539]: malformed `coverage` attribute input + --> $DIR/word-only.rs:33:1 | LL | #[coverage] - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ this attribute is only valid with either `on` or `off` as an argument | -help: the following are the possible correct uses +help: try changing it to one of the following valid forms of the attribute | LL | #[coverage(off)] | +++++ LL | #[coverage(on)] | ++++ -error: malformed `coverage` attribute input - --> $DIR/word-only.rs:39:5 +error[E0539]: malformed `coverage` attribute input + --> $DIR/word-only.rs:36:5 | LL | #[coverage] - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ this attribute is only valid with either `on` or `off` as an argument | -help: the following are the possible correct uses +help: try changing it to one of the following valid forms of the attribute | LL | #[coverage(off)] | +++++ LL | #[coverage(on)] | ++++ -error: malformed `coverage` attribute input - --> $DIR/word-only.rs:44:5 +error[E0539]: malformed `coverage` attribute input + --> $DIR/word-only.rs:40:5 | LL | #[coverage] - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ this attribute is only valid with either `on` or `off` as an argument | -help: the following are the possible correct uses +help: try changing it to one of the following valid forms of the attribute | LL | #[coverage(off)] | +++++ LL | #[coverage(on)] | ++++ -error: malformed `coverage` attribute input - --> $DIR/word-only.rs:50:1 +error[E0539]: malformed `coverage` attribute input + --> $DIR/word-only.rs:45:1 | LL | #[coverage] - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ this attribute is only valid with either `on` or `off` as an argument | -help: the following are the possible correct uses +help: try changing it to one of the following valid forms of the attribute | LL | #[coverage(off)] | +++++ LL | #[coverage(on)] | ++++ -error: malformed `coverage` attribute input - --> $DIR/word-only.rs:53:5 +error[E0539]: malformed `coverage` attribute input + --> $DIR/word-only.rs:48:5 | LL | #[coverage] - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ this attribute is only valid with either `on` or `off` as an argument | -help: the following are the possible correct uses +help: try changing it to one of the following valid forms of the attribute | LL | #[coverage(off)] | +++++ LL | #[coverage(on)] | ++++ -error: malformed `coverage` attribute input - --> $DIR/word-only.rs:58:5 +error[E0539]: malformed `coverage` attribute input + --> $DIR/word-only.rs:52:5 | LL | #[coverage] - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ this attribute is only valid with either `on` or `off` as an argument | -help: the following are the possible correct uses +help: try changing it to one of the following valid forms of the attribute | LL | #[coverage(off)] | +++++ LL | #[coverage(on)] | ++++ -error: malformed `coverage` attribute input - --> $DIR/word-only.rs:64:1 +error[E0539]: malformed `coverage` attribute input + --> $DIR/word-only.rs:57:1 | LL | #[coverage] - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ this attribute is only valid with either `on` or `off` as an argument | -help: the following are the possible correct uses +help: try changing it to one of the following valid forms of the attribute | LL | #[coverage(off)] | +++++ LL | #[coverage(on)] | ++++ -error[E0788]: coverage attribute not allowed here - --> $DIR/word-only.rs:21:1 - | -LL | #[coverage] - | ^^^^^^^^^^^ -... -LL | struct MyStruct; - | ---------------- not a function, impl block, or module - | - = help: coverage attribute can be applied to a function (with body), impl block, or module - -error[E0788]: coverage attribute not allowed here - --> $DIR/word-only.rs:35:1 - | -LL | #[coverage] - | ^^^^^^^^^^^ -... -LL | / trait MyTrait { -LL | | #[coverage] -... | -LL | | type T; -LL | | } - | |_- not a function, impl block, or module - | - = help: coverage attribute can be applied to a function (with body), impl block, or module - -error[E0788]: coverage attribute not allowed here - --> $DIR/word-only.rs:39:5 - | -LL | #[coverage] - | ^^^^^^^^^^^ -... -LL | const X: u32; - | ------------- not a function, impl block, or module - | - = help: coverage attribute can be applied to a function (with body), impl block, or module - -error[E0788]: coverage attribute not allowed here - --> $DIR/word-only.rs:44:5 - | -LL | #[coverage] - | ^^^^^^^^^^^ -... -LL | type T; - | ------- not a function, impl block, or module - | - = help: coverage attribute can be applied to a function (with body), impl block, or module - -error[E0788]: coverage attribute not allowed here - --> $DIR/word-only.rs:29:5 - | -LL | #[coverage] - | ^^^^^^^^^^^ -... -LL | const X: u32 = 7; - | ----------------- not a function, impl block, or module - | - = help: coverage attribute can be applied to a function (with body), impl block, or module - -error[E0788]: coverage attribute not allowed here - --> $DIR/word-only.rs:53:5 - | -LL | #[coverage] - | ^^^^^^^^^^^ -... -LL | const X: u32 = 8; - | ----------------- not a function, impl block, or module - | - = help: coverage attribute can be applied to a function (with body), impl block, or module - -error[E0788]: coverage attribute not allowed here - --> $DIR/word-only.rs:58:5 - | -LL | #[coverage] - | ^^^^^^^^^^^ -... -LL | type T = (); - | ------------ not a function, impl block, or module - | - = help: coverage attribute can be applied to a function (with body), impl block, or module - -error: aborting due to 19 previous errors +error: aborting due to 12 previous errors -For more information about this error, try `rustc --explain E0788`. +For more information about this error, try `rustc --explain E0539`. From af39c0c57cb23678daaeb75ea729edd71aba14b8 Mon Sep 17 00:00:00 2001 From: xizheyin Date: Thu, 10 Jul 2025 14:17:10 +0800 Subject: [PATCH 25/36] Emit warning when there is no space between `-o` and confusing arg Signed-off-by: xizheyin --- compiler/rustc_driver_impl/src/lib.rs | 46 +++++++++ compiler/rustc_session/src/config.rs | 5 + tests/run-make/option-output-no-space/main.rs | 1 + .../run-make/option-output-no-space/rmake.rs | 95 +++++++++++++++++++ 4 files changed, 147 insertions(+) create mode 100644 tests/run-make/option-output-no-space/main.rs create mode 100644 tests/run-make/option-output-no-space/rmake.rs diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 1849038545556..f3ed604210565 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -1237,9 +1237,55 @@ pub fn handle_options(early_dcx: &EarlyDiagCtxt, args: &[String]) -> Option bool { + let s1 = s1.replace('-', "_"); + let s2 = s2.replace('-', "_"); + s1 == s2 + } + + if let Some(name) = matches.opt_str("o") + && let Some(suspect) = args.iter().find(|arg| arg.starts_with("-o") && *arg != "-o") + { + let filename = suspect.strip_prefix("-").unwrap_or(suspect); + let optgroups = config::rustc_optgroups(); + let fake_args = ["optimize", "o0", "o1", "o2", "o3", "ofast", "og", "os", "oz"]; + + // Check if provided filename might be confusing in conjunction with `-o` flag, + // i.e. consider `-o{filename}` such as `-optimize` with `filename` being `ptimize`. + // There are high-value confusables, for example: + // - Long name of flags, e.g. `--out-dir` vs `-out-dir` + // - C compiler flag, e.g. `optimize`, `o0`, `o1`, `o2`, `o3`, `ofast`. + // - Codegen flags, e.g. `pt-level` of `-opt-level`. + if optgroups.iter().any(|option| eq_ignore_separators(option.long_name(), filename)) + || config::CG_OPTIONS.iter().any(|option| eq_ignore_separators(option.name(), filename)) + || fake_args.iter().any(|arg| eq_ignore_separators(arg, filename)) + { + early_dcx.early_warn( + "option `-o` has no space between flag name and value, which can be confusing", + ); + early_dcx.early_note(format!( + "output filename `-o {name}` is applied instead of a flag named `o{name}`" + )); + early_dcx.early_help(format!( + "insert a space between `-o` and `{name}` if this is intentional: `-o {name}`" + )); + } + } +} + fn parse_crate_attrs<'a>(sess: &'a Session) -> PResult<'a, ast::AttrVec> { let mut parser = unwrap_or_emit_fatal(match &sess.io.input { Input::File(file) => new_parser_from_file(&sess.psess, file, None), diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 4627c2978fc07..7f2f5f1c5a47f 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -1653,6 +1653,11 @@ impl RustcOptGroup { OptionKind::FlagMulti => options.optflagmulti(short_name, long_name, desc), }; } + + /// This is for diagnostics-only. + pub fn long_name(&self) -> &str { + self.long_name + } } pub fn make_opt( diff --git a/tests/run-make/option-output-no-space/main.rs b/tests/run-make/option-output-no-space/main.rs new file mode 100644 index 0000000000000..f328e4d9d04c3 --- /dev/null +++ b/tests/run-make/option-output-no-space/main.rs @@ -0,0 +1 @@ +fn main() {} diff --git a/tests/run-make/option-output-no-space/rmake.rs b/tests/run-make/option-output-no-space/rmake.rs new file mode 100644 index 0000000000000..2c42f15aa89a6 --- /dev/null +++ b/tests/run-make/option-output-no-space/rmake.rs @@ -0,0 +1,95 @@ +// This test is to check if the warning is emitted when no space +// between `-o` and arg is applied, see issue #142812 + +//@ ignore-cross-compile +use run_make_support::rustc; + +fn main() { + // test fake args + rustc() + .input("main.rs") + .arg("-optimize") + .run() + .assert_stderr_contains( + "warning: option `-o` has no space between flag name and value, which can be confusing", + ) + .assert_stderr_contains( + "note: output filename `-o ptimize` is applied instead of a flag named `optimize`", + ); + rustc() + .input("main.rs") + .arg("-o0") + .run() + .assert_stderr_contains( + "warning: option `-o` has no space between flag name and value, which can be confusing", + ) + .assert_stderr_contains( + "note: output filename `-o 0` is applied instead of a flag named `o0`", + ); + rustc().input("main.rs").arg("-o1").run(); + // test real args by iter optgroups + rustc() + .input("main.rs") + .arg("-out-dir") + .run() + .assert_stderr_contains( + "warning: option `-o` has no space between flag name and value, which can be confusing", + ) + .assert_stderr_contains( + "note: output filename `-o ut-dir` is applied instead of a flag named `out-dir`", + ) + .assert_stderr_contains( + "help: insert a space between `-o` and `ut-dir` if this is intentional: `-o ut-dir`", + ); + // test real args by iter CG_OPTIONS + rustc() + .input("main.rs") + .arg("-opt_level") + .run() + .assert_stderr_contains( + "warning: option `-o` has no space between flag name and value, which can be confusing", + ) + .assert_stderr_contains( + "note: output filename `-o pt_level` is applied instead of a flag named `opt_level`", + ) + .assert_stderr_contains( + "help: insert a space between `-o` and `pt_level` if this is intentional: `-o pt_level`" + ); + // separater in-sensitive + rustc() + .input("main.rs") + .arg("-opt-level") + .run() + .assert_stderr_contains( + "warning: option `-o` has no space between flag name and value, which can be confusing", + ) + .assert_stderr_contains( + "note: output filename `-o pt-level` is applied instead of a flag named `opt-level`", + ) + .assert_stderr_contains( + "help: insert a space between `-o` and `pt-level` if this is intentional: `-o pt-level`" + ); + rustc() + .input("main.rs") + .arg("-overflow-checks") + .run() + .assert_stderr_contains( + "warning: option `-o` has no space between flag name and value, which can be confusing", + ) + .assert_stderr_contains( + "note: output filename `-o verflow-checks` \ + is applied instead of a flag named `overflow-checks`", + ) + .assert_stderr_contains( + "help: insert a space between `-o` and `verflow-checks` \ + if this is intentional: `-o verflow-checks`", + ); + + // No warning for Z_OPTIONS + rustc().input("main.rs").arg("-oom").run().assert_stderr_equals(""); + + // test no warning when there is space between `-o` and arg + rustc().input("main.rs").arg("-o").arg("ptimize").run().assert_stderr_equals(""); + rustc().input("main.rs").arg("--out-dir").arg("xxx").run().assert_stderr_equals(""); + rustc().input("main.rs").arg("-o").arg("out-dir").run().assert_stderr_equals(""); +} From 9ef3be2f8662a6f65c02f7551bae0f7476da8211 Mon Sep 17 00:00:00 2001 From: lcnr Date: Wed, 16 Jul 2025 21:29:16 +0200 Subject: [PATCH 26/36] go over invariants again :3 Co-authored-by: Boxy --- .../rustc-dev-guide/src/solve/invariants.md | 81 +++++++++++-------- 1 file changed, 48 insertions(+), 33 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/solve/invariants.md b/src/doc/rustc-dev-guide/src/solve/invariants.md index fd12b19575748..2f5a134f4dfed 100644 --- a/src/doc/rustc-dev-guide/src/solve/invariants.md +++ b/src/doc/rustc-dev-guide/src/solve/invariants.md @@ -11,10 +11,8 @@ It is important to know about the things you can assume while working on - and w type system, so here's an incomplete and unofficial list of invariants of the core type system: -- ✅: this invariant mostly holds, with some weird exceptions, you can rely on it outside -of these cases -- ❌: this invariant does not hold, either due to bugs or by design, you must not rely on -it for soundness or have to be incredibly careful when doing so +- ✅: this invariant mostly holds, with some weird exceptions or current bugs +- ❌: this invariant does not hold, and is unlikely to do so in the future; do not rely on it for soundness or have to be incredibly careful when doing so ### `wf(X)` implies `wf(normalize(X))` ✅ @@ -23,6 +21,8 @@ well-formed after normalizing said aliases. We rely on this as otherwise we would have to re-check for well-formedness for these types. +This currently does not hold due to a type system unsoundness: [#84533](https://github.com/rust-lang/rust/issues/84533). + ### Structural equality modulo regions implies semantic equality ✅ If you have a some type and equate it to itself after replacing any regions with unique @@ -36,7 +36,7 @@ If this invariant is broken MIR typeck ends up failing with an ICE. TODO: this invariant is formulated in a weird way and needs to be elaborated. Pretty much: I would like this check to only fail if there's a solver bug: -https://github.com/rust-lang/rust/blob/2ffeb4636b4ae376f716dc4378a7efb37632dc2d/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs#L391-L407 +https://github.com/rust-lang/rust/blob/2ffeb4636b4ae376f716dc4378a7efb37632dc2d/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs#L391-L407 We should readd this check and see where it breaks :3 If we prove some goal/equate types/whatever, apply the resulting inference constraints, and then redo the original action, the result should be the same. @@ -73,40 +73,47 @@ Many of the currently known unsound issues end up relying on this invariant bein It is however very difficult to imagine a sound type system without this invariant, so the issue is that the invariant is broken, not that we incorrectly rely on it. -### Generic goals and their instantiations have the same result ✅ +### The type system is complete ❌ + +The type system is not complete, it often adds unnecessary inference constraints, and errors +even though the goal could hold. + +- method selection +- opaque type inference +- handling type outlives constraints +- preferring `ParamEnv` candidates over `Impl` candidates during candidate selection +in the trait solver + +### Goals keep their result from HIR typeck afterwards ✅ -Pretty much: If we successfully typecheck a generic function concrete instantiations -of that function should also typeck. We should not get errors post-monomorphization. -We can however get overflow errors at that point. +Having a goal which succeeds during HIR typeck but fails when being reevaluated during MIR borrowck causes ICE, e.g. [#140211](https://github.com/rust-lang/rust/issues/140211). -TODO: example for overflow error post-monomorphization +Having a goal which succeeds during HIR typeck but fails after being instantiated is unsound, e.g. [#140212](https://github.com/rust-lang/rust/issues/140212). + +It is interesting that we allow some incompleteness in the trait solver while still maintaining this limitation. It would be nice if there was a clear way to separate the "allowed incompleteness" from behavior which would break this invariant. + +#### Normalization must not change results This invariant is relied on to allow the normalization of generic aliases. Breaking it can easily result in unsoundness, e.g. [#57893](https://github.com/rust-lang/rust/issues/57893) +#### Goals may still overflow after instantiation + +As they start to hit the recursion limit. We also have diverging aliases which are scuffed. It's unclear how these should be handled :3 + ### Trait goals in empty environments are proven by a unique impl ✅ If a trait goal holds with an empty environment, there should be a unique `impl`, either user-defined or builtin, which is used to prove that goal. This is -necessary to select a unique method. +necessary to select unique methods and associated items. We do however break this invariant in few cases, some of which are due to bugs, some by design: - *marker traits* are allowed to overlap as they do not have associated items - *specialization* allows specializing impls to overlap with their parent - the builtin trait object trait implementation can overlap with a user-defined impl: -[#57893] +[#57893](https://github.com/rust-lang/rust/issues/57893) -### The type system is complete ❌ - -The type system is not complete, it often adds unnecessary inference constraints, and errors -even though the goal could hold. - -- method selection -- opaque type inference -- handling type outlives constraints -- preferring `ParamEnv` candidates over `Impl` candidates during candidate selection -in the trait solver #### The type system is complete during the implicit negative overlap check in coherence ✅ @@ -121,18 +128,19 @@ We have to be careful as it is quite easy to break: - generalization of aliases - generalization during subtyping binders (luckily not exploitable in coherence) -### Trait solving must be (free) lifetime agnostic ✅ +### Trait solving must not depend on lifetimes being different ✅ + +If a goal holds with lifetimes being different, it must also hold with these lifetimes being the same. We otherwise get post-monomorphization errors during codegen or unsoundness due to invalid vtables. + +We could also just get inconsistent behavior when first proving a goal with different lifetimes which are later constrained to be equal. -Trait solving during codegen should have the same result as during typeck. As we erase -all free regions during codegen we must not rely on them during typeck. A noteworthy example -is special behavior for `'static`. +### Trait solving in bodies must not depend on lifetimes being equal ✅ We also have to be careful with relying on equality of regions in the trait solver. This is fine for codegen, as we treat all erased regions as equal. We can however lose equality information from HIR to MIR typeck. -The new solver "uniquifies regions" during canonicalization, canonicalizing `u32: Trait<'x, 'x>` -as `exists<'0, '1> u32: Trait<'0, '1>`, to make it harder to rely on this property. +This currently does not hold with the new solver: [trait-system-refactor-initiative#27](https://github.com/rust-lang/trait-system-refactor-initiative/issues/27). ### Removing ambiguity makes strictly more things compile ❌ @@ -146,9 +154,16 @@ changes, breaking existing projects. Two types being equal in the type system must mean that they have the same `TypeId` after instantiating their generic parameters with concrete -arguments. This currently does not hold: [#97156]. +arguments. We can otherwise use their different `TypeId`s to impact trait selection. + +We lookup types using structural equality during codegen, but this shouldn't necessarily be unsound +- may result in redundant method codegen or backend type check errors? +- we also rely on it in CTFE assertions + +### Semantically different types have different `TypeId`s ✅ + +Semantically different `'static` types need different `TypeId`s to avoid transmutes, +for example `for<'a> fn(&'a str)` vs `fn(&'static str)` must have a different `TypeId`. + -[#57893]: https://github.com/rust-lang/rust/issues/57893 -[#97156]: https://github.com/rust-lang/rust/issues/97156 -[#114936]: https://github.com/rust-lang/rust/issues/114936 -[coherence]: ../coherence.md +[coherence]: ../coherence.md From f100767dce1fcc0287047053cc29659ac5321a6b Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Tue, 1 Jul 2025 20:02:31 +0200 Subject: [PATCH 27/36] fix `-Zsanitizer=kcfi` on `#[naked]` functions And more broadly only codegen `InstanceKind::Item` using the naked function codegen code. Other instance kinds should follow the normal path. --- .../rustc_codegen_cranelift/src/driver/aot.rs | 8 +++- compiler/rustc_codegen_ssa/src/mono_item.rs | 13 ++--- .../codegen/sanitizer/kcfi/naked-function.rs | 47 +++++++++++++++++++ tests/ui/asm/naked-function-shim.rs | 31 ++++++++++++ 4 files changed, 91 insertions(+), 8 deletions(-) create mode 100644 tests/codegen/sanitizer/kcfi/naked-function.rs create mode 100644 tests/ui/asm/naked-function-shim.rs diff --git a/compiler/rustc_codegen_cranelift/src/driver/aot.rs b/compiler/rustc_codegen_cranelift/src/driver/aot.rs index 442151fe32de8..727f2760c0f93 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/aot.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/aot.rs @@ -530,8 +530,12 @@ fn codegen_cgu_content( for (mono_item, item_data) in mono_items { match mono_item { MonoItem::Fn(instance) => { - if tcx.codegen_fn_attrs(instance.def_id()).flags.contains(CodegenFnAttrFlags::NAKED) - { + // Other `InstanceKind`s (e.g. `ReifyShim` generated by indirect calls) should be + // codegened like a normal function. + let is_item_instance = matches!(instance.def, InstanceKind::Item(_)); + + let flags = tcx.codegen_fn_attrs(instance.def_id()).flags; + if is_item_instance && flags.contains(CodegenFnAttrFlags::NAKED) { rustc_codegen_ssa::mir::naked_asm::codegen_naked_asm( &mut GlobalAsmContext { tcx, global_asm: &mut cx.global_asm }, instance, diff --git a/compiler/rustc_codegen_ssa/src/mono_item.rs b/compiler/rustc_codegen_ssa/src/mono_item.rs index 7b4268abe4b14..fa0ef04b2e672 100644 --- a/compiler/rustc_codegen_ssa/src/mono_item.rs +++ b/compiler/rustc_codegen_ssa/src/mono_item.rs @@ -1,5 +1,6 @@ use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::mir::mono::{Linkage, MonoItem, MonoItemData, Visibility}; +use rustc_middle::ty::InstanceKind; use rustc_middle::ty::layout::HasTyCtxt; use tracing::debug; @@ -41,12 +42,12 @@ impl<'a, 'tcx: 'a> MonoItemExt<'a, 'tcx> for MonoItem<'tcx> { base::codegen_global_asm(cx, item_id); } MonoItem::Fn(instance) => { - if cx - .tcx() - .codegen_fn_attrs(instance.def_id()) - .flags - .contains(CodegenFnAttrFlags::NAKED) - { + // Other `InstanceKind`s (e.g. `ReifyShim` generated by indirect calls) should be + // codegened like a normal function. + let is_item_instance = matches!(instance.def, InstanceKind::Item(_)); + + let flags = cx.tcx().codegen_fn_attrs(instance.def_id()).flags; + if is_item_instance && flags.contains(CodegenFnAttrFlags::NAKED) { naked_asm::codegen_naked_asm::(cx, instance, item_data); } else { base::codegen_instance::(cx, instance); diff --git a/tests/codegen/sanitizer/kcfi/naked-function.rs b/tests/codegen/sanitizer/kcfi/naked-function.rs new file mode 100644 index 0000000000000..92431fc7d6339 --- /dev/null +++ b/tests/codegen/sanitizer/kcfi/naked-function.rs @@ -0,0 +1,47 @@ +//@ add-core-stubs +//@ revisions: aarch64 x86_64 +//@ [aarch64] compile-flags: --target aarch64-unknown-none +//@ [aarch64] needs-llvm-components: aarch64 +//@ [x86_64] compile-flags: --target x86_64-unknown-none +//@ [x86_64] needs-llvm-components: x86 +//@ compile-flags: -Ctarget-feature=-crt-static -Zsanitizer=kcfi -Cno-prepopulate-passes -Copt-level=0 + +#![feature(no_core, lang_items)] +#![crate_type = "lib"] +#![no_core] + +extern crate minicore; +use minicore::*; + +struct Thing; +trait MyTrait { + #[unsafe(naked)] + extern "C" fn my_naked_function() { + // the real function is defined + // CHECK: .globl + // CHECK-SAME: my_naked_function + naked_asm!("ret") + } +} +impl MyTrait for Thing {} + +// CHECK-LABEL: main +#[unsafe(no_mangle)] +pub fn main() { + // Trick the compiler into generating an indirect call. + const F: extern "C" fn() = Thing::my_naked_function; + + // main calls the shim function + // CHECK: call + // CHECK-SAME: my_naked_function + // CHECK-SAME: reify.shim.fnptr + (F)(); +} + +// the shim calls the real function +// CHECK: define +// CHECK-SAME: my_naked_function +// CHECK-SAME: reify.shim.fnptr + +// CHECK: declare !kcfi_type +// CHECK-SAME: my_naked_function diff --git a/tests/ui/asm/naked-function-shim.rs b/tests/ui/asm/naked-function-shim.rs new file mode 100644 index 0000000000000..4694d0cd963e3 --- /dev/null +++ b/tests/ui/asm/naked-function-shim.rs @@ -0,0 +1,31 @@ +// The indirect call will generate a shim that then calls the actual function. Test that +// this is handled correctly. See also https://github.com/rust-lang/rust/issues/143266. + +//@ build-pass +//@ add-core-stubs +//@ revisions: aarch64 x86_64 +//@ [aarch64] compile-flags: --target aarch64-unknown-none +//@ [aarch64] needs-llvm-components: aarch64 +//@ [x86_64] compile-flags: --target x86_64-unknown-none +//@ [x86_64] needs-llvm-components: x86 + +#![feature(no_core, lang_items)] +#![crate_type = "lib"] +#![no_core] + +extern crate minicore; +use minicore::*; + +trait MyTrait { + #[unsafe(naked)] + extern "C" fn foo(&self) { + naked_asm!("ret") + } +} + +impl MyTrait for i32 {} + +fn main() { + let x: extern "C" fn(&_) = ::foo; + x(&1); +} From ec0ff720d1a89cb51edd90116c6e70051affa95f Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Wed, 2 Jul 2025 10:46:15 +0200 Subject: [PATCH 28/36] add `codegen_instance_attrs` query and use it for naked functions --- .../rustc_codegen_cranelift/src/driver/aot.rs | 8 ++------ .../rustc_codegen_ssa/src/codegen_attrs.rs | 1 + .../rustc_codegen_ssa/src/mir/debuginfo.rs | 2 +- compiler/rustc_codegen_ssa/src/mir/mod.rs | 5 ++--- compiler/rustc_codegen_ssa/src/mono_item.rs | 9 ++------- .../src/middle/codegen_fn_attrs.rs | 20 +++++++++++++++++++ .../codegen/sanitizer/kcfi/naked-function.rs | 12 +++++------ 7 files changed, 34 insertions(+), 23 deletions(-) diff --git a/compiler/rustc_codegen_cranelift/src/driver/aot.rs b/compiler/rustc_codegen_cranelift/src/driver/aot.rs index 727f2760c0f93..8ec3599b63d89 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/aot.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/aot.rs @@ -530,12 +530,8 @@ fn codegen_cgu_content( for (mono_item, item_data) in mono_items { match mono_item { MonoItem::Fn(instance) => { - // Other `InstanceKind`s (e.g. `ReifyShim` generated by indirect calls) should be - // codegened like a normal function. - let is_item_instance = matches!(instance.def, InstanceKind::Item(_)); - - let flags = tcx.codegen_fn_attrs(instance.def_id()).flags; - if is_item_instance && flags.contains(CodegenFnAttrFlags::NAKED) { + let flags = tcx.codegen_instance_attrs(instance.def).flags; + if flags.contains(CodegenFnAttrFlags::NAKED) { rustc_codegen_ssa::mir::naked_asm::codegen_naked_asm( &mut GlobalAsmContext { tcx, global_asm: &mut cx.global_asm }, instance, diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 85d01d4f93898..a1dd64736fc2c 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -114,6 +114,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { codegen_fn_attrs.export_name = Some(*name); } AttributeKind::Naked(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED, + AttributeKind::Align { align, .. } => codegen_fn_attrs.alignment = Some(*align), AttributeKind::LinkName { name, .. } => codegen_fn_attrs.link_name = Some(*name), AttributeKind::LinkSection { name, .. } => { diff --git a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs index 025f5fb54f428..b8f635ab78161 100644 --- a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs +++ b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs @@ -356,7 +356,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { LocalRef::Operand(operand) => { // Don't spill operands onto the stack in naked functions. // See: https://github.com/rust-lang/rust/issues/42779 - let attrs = bx.tcx().codegen_fn_attrs(self.instance.def_id()); + let attrs = bx.tcx().codegen_instance_attrs(self.instance.def); if attrs.flags.contains(CodegenFnAttrFlags::NAKED) { return; } diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index fa69820d5d2ed..50d0f91074450 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -390,9 +390,8 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let mut num_untupled = None; - let codegen_fn_attrs = bx.tcx().codegen_fn_attrs(fx.instance.def_id()); - let naked = codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED); - if naked { + let codegen_fn_attrs = bx.tcx().codegen_instance_attrs(fx.instance.def); + if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) { return vec![]; } diff --git a/compiler/rustc_codegen_ssa/src/mono_item.rs b/compiler/rustc_codegen_ssa/src/mono_item.rs index fa0ef04b2e672..2c3a6de750e2b 100644 --- a/compiler/rustc_codegen_ssa/src/mono_item.rs +++ b/compiler/rustc_codegen_ssa/src/mono_item.rs @@ -1,6 +1,5 @@ use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::mir::mono::{Linkage, MonoItem, MonoItemData, Visibility}; -use rustc_middle::ty::InstanceKind; use rustc_middle::ty::layout::HasTyCtxt; use tracing::debug; @@ -42,12 +41,8 @@ impl<'a, 'tcx: 'a> MonoItemExt<'a, 'tcx> for MonoItem<'tcx> { base::codegen_global_asm(cx, item_id); } MonoItem::Fn(instance) => { - // Other `InstanceKind`s (e.g. `ReifyShim` generated by indirect calls) should be - // codegened like a normal function. - let is_item_instance = matches!(instance.def, InstanceKind::Item(_)); - - let flags = cx.tcx().codegen_fn_attrs(instance.def_id()).flags; - if is_item_instance && flags.contains(CodegenFnAttrFlags::NAKED) { + let flags = cx.tcx().codegen_instance_attrs(instance.def).flags; + if flags.contains(CodegenFnAttrFlags::NAKED) { naked_asm::codegen_naked_asm::(cx, instance, item_data); } else { base::codegen_instance::(cx, instance); diff --git a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs index 2f16d385efb34..92670e7e018f9 100644 --- a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs +++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs @@ -1,3 +1,5 @@ +use std::borrow::Cow; + use rustc_abi::Align; use rustc_ast::expand::autodiff_attrs::AutoDiffAttrs; use rustc_attr_data_structures::{InlineAttr, InstructionSetAttr, OptimizeAttr}; @@ -6,6 +8,24 @@ use rustc_span::Symbol; use rustc_target::spec::SanitizerSet; use crate::mir::mono::Linkage; +use crate::ty::{InstanceKind, TyCtxt}; + +impl<'tcx> TyCtxt<'tcx> { + pub fn codegen_instance_attrs( + self, + instance_kind: InstanceKind<'_>, + ) -> Cow<'tcx, CodegenFnAttrs> { + let mut attrs = Cow::Borrowed(self.codegen_fn_attrs(instance_kind.def_id())); + + // Drop the `#[naked]` attribute on non-item `InstanceKind`s, like the shims that + // are generated for indirect function calls. + if !matches!(instance_kind, InstanceKind::Item(_)) { + attrs.to_mut().flags.remove(CodegenFnAttrFlags::NAKED); + } + + attrs + } +} #[derive(Clone, TyEncodable, TyDecodable, HashStable, Debug)] pub struct CodegenFnAttrs { diff --git a/tests/codegen/sanitizer/kcfi/naked-function.rs b/tests/codegen/sanitizer/kcfi/naked-function.rs index 92431fc7d6339..2c8cdc919b85d 100644 --- a/tests/codegen/sanitizer/kcfi/naked-function.rs +++ b/tests/codegen/sanitizer/kcfi/naked-function.rs @@ -25,6 +25,11 @@ trait MyTrait { } impl MyTrait for Thing {} +// the shim calls the real function +// CHECK-LABEL: define +// CHECK-SAME: my_naked_function +// CHECK-SAME: reify.shim.fnptr + // CHECK-LABEL: main #[unsafe(no_mangle)] pub fn main() { @@ -32,16 +37,11 @@ pub fn main() { const F: extern "C" fn() = Thing::my_naked_function; // main calls the shim function - // CHECK: call + // CHECK: call void // CHECK-SAME: my_naked_function // CHECK-SAME: reify.shim.fnptr (F)(); } -// the shim calls the real function -// CHECK: define -// CHECK-SAME: my_naked_function -// CHECK-SAME: reify.shim.fnptr - // CHECK: declare !kcfi_type // CHECK-SAME: my_naked_function From 9c8ab891876b37aac458a7461d904fe593856745 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Wed, 2 Jul 2025 11:12:54 +0200 Subject: [PATCH 29/36] use `codegen_instance_attrs` where an instance is (easily) available --- compiler/rustc_codegen_cranelift/src/driver/jit.rs | 2 +- compiler/rustc_codegen_cranelift/src/driver/mod.rs | 2 +- compiler/rustc_codegen_gcc/src/attributes.rs | 2 +- compiler/rustc_codegen_gcc/src/callee.rs | 2 +- compiler/rustc_codegen_gcc/src/mono_item.rs | 2 +- compiler/rustc_codegen_llvm/src/attributes.rs | 2 +- compiler/rustc_codegen_llvm/src/callee.rs | 2 +- compiler/rustc_codegen_llvm/src/mono_item.rs | 4 ++-- compiler/rustc_codegen_ssa/src/codegen_attrs.rs | 1 - compiler/rustc_codegen_ssa/src/mir/naked_asm.rs | 2 +- compiler/rustc_codegen_ssa/src/mir/rvalue.rs | 12 ++++++++++-- compiler/rustc_codegen_ssa/src/mono_item.rs | 2 +- compiler/rustc_const_eval/src/interpret/memory.rs | 2 +- .../rustc_middle/src/middle/codegen_fn_attrs.rs | 4 +++- compiler/rustc_middle/src/mir/mono.rs | 13 ++++++------- compiler/rustc_middle/src/query/mod.rs | 9 +++++++++ compiler/rustc_symbol_mangling/src/lib.rs | 2 +- src/tools/miri/src/machine.rs | 4 ++-- 18 files changed, 43 insertions(+), 26 deletions(-) diff --git a/compiler/rustc_codegen_cranelift/src/driver/jit.rs b/compiler/rustc_codegen_cranelift/src/driver/jit.rs index b1f185b551c3c..b3497503bf068 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/jit.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/jit.rs @@ -127,7 +127,7 @@ fn codegen_and_compile_fn<'tcx>( module: &mut dyn Module, instance: Instance<'tcx>, ) { - if tcx.codegen_fn_attrs(instance.def_id()).flags.contains(CodegenFnAttrFlags::NAKED) { + if tcx.codegen_instance_attrs(instance.def).flags.contains(CodegenFnAttrFlags::NAKED) { tcx.dcx() .span_fatal(tcx.def_span(instance.def_id()), "Naked asm is not supported in JIT mode"); } diff --git a/compiler/rustc_codegen_cranelift/src/driver/mod.rs b/compiler/rustc_codegen_cranelift/src/driver/mod.rs index ffd47cace3807..8f83c30b598d8 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/mod.rs @@ -35,7 +35,7 @@ fn predefine_mono_items<'tcx>( is_compiler_builtins, ); let is_naked = tcx - .codegen_fn_attrs(instance.def_id()) + .codegen_instance_attrs(instance.def) .flags .contains(CodegenFnAttrFlags::NAKED); module diff --git a/compiler/rustc_codegen_gcc/src/attributes.rs b/compiler/rustc_codegen_gcc/src/attributes.rs index bf0927dc590ba..7a1ae6ca9c8b7 100644 --- a/compiler/rustc_codegen_gcc/src/attributes.rs +++ b/compiler/rustc_codegen_gcc/src/attributes.rs @@ -87,7 +87,7 @@ pub fn from_fn_attrs<'gcc, 'tcx>( #[cfg_attr(not(feature = "master"), allow(unused_variables))] func: Function<'gcc>, instance: ty::Instance<'tcx>, ) { - let codegen_fn_attrs = cx.tcx.codegen_fn_attrs(instance.def_id()); + let codegen_fn_attrs = cx.tcx.codegen_instance_attrs(instance.def); #[cfg(feature = "master")] { diff --git a/compiler/rustc_codegen_gcc/src/callee.rs b/compiler/rustc_codegen_gcc/src/callee.rs index 189ac7cd77928..e7ca95af594c6 100644 --- a/compiler/rustc_codegen_gcc/src/callee.rs +++ b/compiler/rustc_codegen_gcc/src/callee.rs @@ -105,7 +105,7 @@ pub fn get_fn<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, instance: Instance<'tcx>) let is_hidden = if is_generic { // This is a monomorphization of a generic function. if !(cx.tcx.sess.opts.share_generics() - || tcx.codegen_fn_attrs(instance_def_id).inline + || tcx.codegen_instance_attrs(instance.def).inline == rustc_attr_data_structures::InlineAttr::Never) { // When not sharing generics, all instances are in the same diff --git a/compiler/rustc_codegen_gcc/src/mono_item.rs b/compiler/rustc_codegen_gcc/src/mono_item.rs index 539e3ac850763..51f35cbdee472 100644 --- a/compiler/rustc_codegen_gcc/src/mono_item.rs +++ b/compiler/rustc_codegen_gcc/src/mono_item.rs @@ -53,7 +53,7 @@ impl<'gcc, 'tcx> PreDefineCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { let fn_abi = self.fn_abi_of_instance(instance, ty::List::empty()); self.linkage.set(base::linkage_to_gcc(linkage)); let decl = self.declare_fn(symbol_name, fn_abi); - //let attrs = self.tcx.codegen_fn_attrs(instance.def_id()); + //let attrs = self.tcx.codegen_instance_attrs(instance.def); attributes::from_fn_attrs(self, decl, instance); diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index 1ea5a062254a3..c32f11b27f3de 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -344,7 +344,7 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>( llfn: &'ll Value, instance: ty::Instance<'tcx>, ) { - let codegen_fn_attrs = cx.tcx.codegen_fn_attrs(instance.def_id()); + let codegen_fn_attrs = cx.tcx.codegen_instance_attrs(instance.def); let mut to_add = SmallVec::<[_; 16]>::new(); diff --git a/compiler/rustc_codegen_llvm/src/callee.rs b/compiler/rustc_codegen_llvm/src/callee.rs index 6d68eca60afc1..5a3dd90ab2405 100644 --- a/compiler/rustc_codegen_llvm/src/callee.rs +++ b/compiler/rustc_codegen_llvm/src/callee.rs @@ -102,7 +102,7 @@ pub(crate) fn get_fn<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, instance: Instance<'t let is_hidden = if is_generic { // This is a monomorphization of a generic function. if !(cx.tcx.sess.opts.share_generics() - || tcx.codegen_fn_attrs(instance_def_id).inline + || tcx.codegen_instance_attrs(instance.def).inline == rustc_attr_data_structures::InlineAttr::Never) { // When not sharing generics, all instances are in the same diff --git a/compiler/rustc_codegen_llvm/src/mono_item.rs b/compiler/rustc_codegen_llvm/src/mono_item.rs index 3f38e1e191bf2..c471a02baa261 100644 --- a/compiler/rustc_codegen_llvm/src/mono_item.rs +++ b/compiler/rustc_codegen_llvm/src/mono_item.rs @@ -55,8 +55,8 @@ impl<'tcx> PreDefineCodegenMethods<'tcx> for CodegenCx<'_, 'tcx> { let fn_abi = self.fn_abi_of_instance(instance, ty::List::empty()); let lldecl = self.declare_fn(symbol_name, fn_abi, Some(instance)); llvm::set_linkage(lldecl, base::linkage_to_llvm(linkage)); - let attrs = self.tcx.codegen_fn_attrs(instance.def_id()); - base::set_link_section(lldecl, attrs); + let attrs = self.tcx.codegen_instance_attrs(instance.def); + base::set_link_section(lldecl, &attrs); if (linkage == Linkage::LinkOnceODR || linkage == Linkage::WeakODR) && self.tcx.sess.target.supports_comdat() { diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index a1dd64736fc2c..85d01d4f93898 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -114,7 +114,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { codegen_fn_attrs.export_name = Some(*name); } AttributeKind::Naked(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED, - AttributeKind::Align { align, .. } => codegen_fn_attrs.alignment = Some(*align), AttributeKind::LinkName { name, .. } => codegen_fn_attrs.link_name = Some(*name), AttributeKind::LinkSection { name, .. } => { diff --git a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs index beaf895097842..42e435cf0a32d 100644 --- a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs +++ b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs @@ -128,7 +128,7 @@ fn prefix_and_suffix<'tcx>( let is_arm = tcx.sess.target.arch == "arm"; let is_thumb = tcx.sess.unstable_target_features.contains(&sym::thumb_mode); - let attrs = tcx.codegen_fn_attrs(instance.def_id()); + let attrs = tcx.codegen_instance_attrs(instance.def); let link_section = attrs.link_section.map(|symbol| symbol.as_str().to_string()); // If no alignment is specified, an alignment of 4 bytes is used. diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index e90463aacc8a1..2587e89417a6a 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -611,11 +611,19 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let fn_abi = bx.fn_abi_of_instance(instance, ty::List::empty()); let fn_ty = bx.fn_decl_backend_type(fn_abi); let fn_attrs = if bx.tcx().def_kind(instance.def_id()).has_codegen_attrs() { - Some(bx.tcx().codegen_fn_attrs(instance.def_id())) + Some(bx.tcx().codegen_instance_attrs(instance.def)) } else { None }; - bx.call(fn_ty, fn_attrs, Some(fn_abi), fn_ptr, &[], None, Some(instance)) + bx.call( + fn_ty, + fn_attrs.as_deref(), + Some(fn_abi), + fn_ptr, + &[], + None, + Some(instance), + ) } else { bx.get_static(def_id) }; diff --git a/compiler/rustc_codegen_ssa/src/mono_item.rs b/compiler/rustc_codegen_ssa/src/mono_item.rs index 2c3a6de750e2b..b9040c330fb10 100644 --- a/compiler/rustc_codegen_ssa/src/mono_item.rs +++ b/compiler/rustc_codegen_ssa/src/mono_item.rs @@ -71,7 +71,7 @@ impl<'a, 'tcx: 'a> MonoItemExt<'a, 'tcx> for MonoItem<'tcx> { cx.predefine_static(def_id, linkage, visibility, symbol_name); } MonoItem::Fn(instance) => { - let attrs = cx.tcx().codegen_fn_attrs(instance.def_id()); + let attrs = cx.tcx().codegen_instance_attrs(instance.def); if attrs.flags.contains(CodegenFnAttrFlags::NAKED) { // do not define this function; it will become a global assembly block diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index 6414821e21d02..0dd274b1b000a 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -890,7 +890,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { if let Some(fn_val) = self.get_fn_alloc(id) { let align = match fn_val { FnVal::Instance(instance) => { - self.tcx.codegen_fn_attrs(instance.def_id()).alignment.unwrap_or(Align::ONE) + self.tcx.codegen_instance_attrs(instance.def).alignment.unwrap_or(Align::ONE) } // Machine-specific extra functions currently do not support alignment restrictions. FnVal::Other(_) => Align::ONE, diff --git a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs index 92670e7e018f9..6eae3b51e29f4 100644 --- a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs +++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs @@ -20,7 +20,9 @@ impl<'tcx> TyCtxt<'tcx> { // Drop the `#[naked]` attribute on non-item `InstanceKind`s, like the shims that // are generated for indirect function calls. if !matches!(instance_kind, InstanceKind::Item(_)) { - attrs.to_mut().flags.remove(CodegenFnAttrFlags::NAKED); + if attrs.flags.contains(CodegenFnAttrFlags::NAKED) { + attrs.to_mut().flags.remove(CodegenFnAttrFlags::NAKED); + } } attrs diff --git a/compiler/rustc_middle/src/mir/mono.rs b/compiler/rustc_middle/src/mir/mono.rs index 47ba850d50dd4..2d7ddd105bd4f 100644 --- a/compiler/rustc_middle/src/mir/mono.rs +++ b/compiler/rustc_middle/src/mir/mono.rs @@ -152,7 +152,7 @@ impl<'tcx> MonoItem<'tcx> { // If the function is #[naked] or contains any other attribute that requires exactly-once // instantiation: // We emit an unused_attributes lint for this case, which should be kept in sync if possible. - let codegen_fn_attrs = tcx.codegen_fn_attrs(instance.def_id()); + let codegen_fn_attrs = tcx.codegen_instance_attrs(instance.def); if codegen_fn_attrs.contains_extern_indicator() || codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) { @@ -219,7 +219,7 @@ impl<'tcx> MonoItem<'tcx> { // functions the same as those that unconditionally get LocalCopy codegen. It's only when // we get here that we can at least not codegen a #[inline(never)] generic function in all // of our CGUs. - if let InlineAttr::Never = tcx.codegen_fn_attrs(instance.def_id()).inline + if let InlineAttr::Never = codegen_fn_attrs.inline && self.is_generic_fn() { return InstantiationMode::GloballyShared { may_conflict: true }; @@ -234,14 +234,13 @@ impl<'tcx> MonoItem<'tcx> { } pub fn explicit_linkage(&self, tcx: TyCtxt<'tcx>) -> Option { - let def_id = match *self { - MonoItem::Fn(ref instance) => instance.def_id(), - MonoItem::Static(def_id) => def_id, + let instance_kind = match *self { + MonoItem::Fn(ref instance) => instance.def, + MonoItem::Static(def_id) => InstanceKind::Item(def_id), MonoItem::GlobalAsm(..) => return None, }; - let codegen_fn_attrs = tcx.codegen_fn_attrs(def_id); - codegen_fn_attrs.linkage + tcx.codegen_instance_attrs(instance_kind).linkage } /// Returns `true` if this instance is instantiable - whether it has no unsatisfied diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 842d118449a55..ec9f6d918e2c3 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1505,6 +1505,15 @@ rustc_queries! { separate_provide_extern } + /// Returns the `CodegenFnAttrs` for the item at `def_id`. + /// + /// If possible, use `tcx.codegen_instance_attrs` instead. That function takes the + /// instance kind into account. + /// + /// For example, the `#[naked]` attribute should be applied for `InstanceKind::Item`, + /// but should not be applied if the instance kind is `InstanceKind::ReifyShim`. + /// Using this query would include the attribute regardless of the actual instance + /// kind at the call site. query codegen_fn_attrs(def_id: DefId) -> &'tcx CodegenFnAttrs { desc { |tcx| "computing codegen attributes of `{}`", tcx.def_path_str(def_id) } arena_cache diff --git a/compiler/rustc_symbol_mangling/src/lib.rs b/compiler/rustc_symbol_mangling/src/lib.rs index a9bf5eae445e4..6bcb7f6e093c4 100644 --- a/compiler/rustc_symbol_mangling/src/lib.rs +++ b/compiler/rustc_symbol_mangling/src/lib.rs @@ -180,7 +180,7 @@ fn compute_symbol_name<'tcx>( // FIXME(eddyb) Precompute a custom symbol name based on attributes. let attrs = if tcx.def_kind(def_id).has_codegen_attrs() { - tcx.codegen_fn_attrs(def_id) + &tcx.codegen_instance_attrs(instance.def) } else { CodegenFnAttrs::EMPTY }; diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index 35399dbf4cb0c..e8ae3350e5630 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -1086,7 +1086,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { ecx: &MiriInterpCx<'tcx>, instance: ty::Instance<'tcx>, ) -> InterpResult<'tcx> { - let attrs = ecx.tcx.codegen_fn_attrs(instance.def_id()); + let attrs = ecx.tcx.codegen_instance_attrs(instance.def); if attrs .target_features .iter() @@ -1790,7 +1790,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { ecx.tcx.sess.opts.unstable_opts.cross_crate_inline_threshold, InliningThreshold::Always ) || !matches!( - ecx.tcx.codegen_fn_attrs(instance.def_id()).inline, + ecx.tcx.codegen_instance_attrs(instance.def).inline, InlineAttr::Never ); !is_generic && !can_be_inlined From fad8dec970f51a2d4c23593b059b1684bd85472a Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Wed, 16 Jul 2025 15:39:31 +0900 Subject: [PATCH 30/36] fix: false positive double_negations when it jumps macro boundary --- compiler/rustc_lint/src/builtin.rs | 2 ++ tests/ui/lint/lint-double-negations-macro.rs | 16 +++++++++++++++ .../lint/lint-double-negations-macro.stderr | 20 +++++++++++++++++++ 3 files changed, 38 insertions(+) create mode 100644 tests/ui/lint/lint-double-negations-macro.rs create mode 100644 tests/ui/lint/lint-double-negations-macro.stderr diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 255ff56f62b1d..93df056c43b92 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -1584,6 +1584,8 @@ impl EarlyLintPass for DoubleNegations { if let ExprKind::Unary(UnOp::Neg, ref inner) = expr.kind && let ExprKind::Unary(UnOp::Neg, ref inner2) = inner.kind && !matches!(inner2.kind, ExprKind::Unary(UnOp::Neg, _)) + // Don't lint if this jumps macro expansion boundary (Issue #143980) + && expr.span.eq_ctxt(inner.span) { cx.emit_span_lint( DOUBLE_NEGATIONS, diff --git a/tests/ui/lint/lint-double-negations-macro.rs b/tests/ui/lint/lint-double-negations-macro.rs new file mode 100644 index 0000000000000..a6583271d5a2a --- /dev/null +++ b/tests/ui/lint/lint-double-negations-macro.rs @@ -0,0 +1,16 @@ +//@ check-pass +macro_rules! neg { + ($e: expr) => { + -$e + }; +} +macro_rules! bad_macro { + ($e: expr) => { + --$e //~ WARN use of a double negation + }; +} + +fn main() { + neg!(-1); + bad_macro!(1); +} diff --git a/tests/ui/lint/lint-double-negations-macro.stderr b/tests/ui/lint/lint-double-negations-macro.stderr new file mode 100644 index 0000000000000..d6ac9be48f304 --- /dev/null +++ b/tests/ui/lint/lint-double-negations-macro.stderr @@ -0,0 +1,20 @@ +warning: use of a double negation + --> $DIR/lint-double-negations-macro.rs:9:9 + | +LL | --$e + | ^^^^ +... +LL | bad_macro!(1); + | ------------- in this macro invocation + | + = note: the prefix `--` could be misinterpreted as a decrement operator which exists in other languages + = note: use `-= 1` if you meant to decrement the value + = note: `#[warn(double_negations)]` on by default + = note: this warning originates in the macro `bad_macro` (in Nightly builds, run with -Z macro-backtrace for more info) +help: add parentheses for clarity + | +LL | -(-$e) + | + + + +warning: 1 warning emitted + From 8df6a1d26636bb60fd9f8d45e3da9c0c2529ee29 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Thu, 17 Jul 2025 05:42:53 +0200 Subject: [PATCH 31/36] some improvements to "Invariants of the type system" --- .../rustc-dev-guide/src/solve/invariants.md | 41 ++++++++++--------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/solve/invariants.md b/src/doc/rustc-dev-guide/src/solve/invariants.md index 2f5a134f4dfed..8ec15f339e51b 100644 --- a/src/doc/rustc-dev-guide/src/solve/invariants.md +++ b/src/doc/rustc-dev-guide/src/solve/invariants.md @@ -4,10 +4,10 @@ FIXME: This file talks about invariants of the type system as a whole, not only There are a lot of invariants - things the type system guarantees to be true at all times - which are desirable or expected from other languages and type systems. Unfortunately, quite -a few of them do not hold in Rust right now. This is either a fundamental to its design or -caused by bugs and something that may change in the future. +a few of them do not hold in Rust right now. This is either fundamental to its design or +caused by bugs, and something that may change in the future. -It is important to know about the things you can assume while working on - and with - the +It is important to know about the things you can assume while working on, and with, the type system, so here's an incomplete and unofficial list of invariants of the core type system: @@ -29,14 +29,15 @@ If you have a some type and equate it to itself after replacing any regions with inference variables in both the lhs and rhs, the now potentially structurally different types should still be equal to each other. -Needed to prevent goals from succeeding in HIR typeck and then failing in MIR borrowck. -If this invariant is broken MIR typeck ends up failing with an ICE. +This is needed to prevent goals from succeeding in HIR typeck and then failing in MIR borrowck. +If this invariant is broken, MIR typeck ends up failing with an ICE. ### Applying inference results from a goal does not change its result ❌ TODO: this invariant is formulated in a weird way and needs to be elaborated. Pretty much: I would like this check to only fail if there's a solver bug: -https://github.com/rust-lang/rust/blob/2ffeb4636b4ae376f716dc4378a7efb37632dc2d/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs#L391-L407 We should readd this check and see where it breaks :3 +. +We should readd this check and see where it breaks :3 If we prove some goal/equate types/whatever, apply the resulting inference constraints, and then redo the original action, the result should be the same. @@ -75,8 +76,8 @@ the issue is that the invariant is broken, not that we incorrectly rely on it. ### The type system is complete ❌ -The type system is not complete, it often adds unnecessary inference constraints, and errors -even though the goal could hold. +The type system is not complete. +It often adds unnecessary inference constraints, and errors even though the goal could hold. - method selection - opaque type inference @@ -95,11 +96,13 @@ It is interesting that we allow some incompleteness in the trait solver while st #### Normalization must not change results This invariant is relied on to allow the normalization of generic aliases. Breaking -it can easily result in unsoundness, e.g. [#57893](https://github.com/rust-lang/rust/issues/57893) +it can easily result in unsoundness, e.g. [#57893](https://github.com/rust-lang/rust/issues/57893). #### Goals may still overflow after instantiation -As they start to hit the recursion limit. We also have diverging aliases which are scuffed. It's unclear how these should be handled :3 +This happens they start to hit the recursion limit. +We also have diverging aliases which are scuffed. +It's unclear how these should be handled :3 ### Trait goals in empty environments are proven by a unique impl ✅ @@ -107,8 +110,7 @@ If a trait goal holds with an empty environment, there should be a unique `impl` either user-defined or builtin, which is used to prove that goal. This is necessary to select unique methods and associated items. -We do however break this invariant in few cases, some of which are due to bugs, -some by design: +We do however break this invariant in a few cases, some of which are due to bugs, some by design: - *marker traits* are allowed to overlap as they do not have associated items - *specialization* allows specializing impls to overlap with their parent - the builtin trait object trait implementation can overlap with a user-defined impl: @@ -117,11 +119,12 @@ some by design: #### The type system is complete during the implicit negative overlap check in coherence ✅ -For more on overlap checking: [coherence] +For more on overlap checking, see [Coherence chapter]. -During the implicit negative overlap check in coherence we must never return *error* for -goals which can be proven. This would allow for overlapping impls with potentially different -associated items, breaking a bunch of other invariants. +During the implicit negative overlap check in coherence, +we must never return *error* for goals which can be proven. +This would allow for overlapping impls with potentially different associated items, +breaking a bunch of other invariants. This invariant is currently broken in many different ways while actually something we rely on. We have to be careful as it is quite easy to break: @@ -147,8 +150,8 @@ This currently does not hold with the new solver: [trait-system-refactor-initiat Ideally we *should* not rely on ambiguity for things to compile. Not doing that will cause future improvements to be breaking changes. -Due to *incompleteness* this is not the case and improving inference can result in inference -changes, breaking existing projects. +Due to *incompleteness* this is not the case, +and improving inference can result in inference changes, breaking existing projects. ### Semantic equality implies structural equality ✅ @@ -166,4 +169,4 @@ Semantically different `'static` types need different `TypeId`s to avoid transmu for example `for<'a> fn(&'a str)` vs `fn(&'static str)` must have a different `TypeId`. -[coherence]: ../coherence.md +[coherence chapter]: ../coherence.md From 2faf5ed0cd20debd5eb6efe1795badccf6390c60 Mon Sep 17 00:00:00 2001 From: The rustc-josh-sync Cronjob Bot Date: Thu, 17 Jul 2025 04:14:28 +0000 Subject: [PATCH 32/36] Prepare for merging from rust-lang/rust This updates the rust-version file to fd2eb391d032181459773f3498c17b198513e0d0. --- src/doc/rustc-dev-guide/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/rust-version b/src/doc/rustc-dev-guide/rust-version index e444613e6311c..3f10132b684b7 100644 --- a/src/doc/rustc-dev-guide/rust-version +++ b/src/doc/rustc-dev-guide/rust-version @@ -1 +1 @@ -c96a69059ecc618b519da385a6ccd03155aa0237 +fd2eb391d032181459773f3498c17b198513e0d0 From dd364bd2261b37b6cd0d7f823b158a0c2903746b Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Thu, 17 Jul 2025 06:32:23 +0200 Subject: [PATCH 33/36] copy-paste convenience --- src/doc/rustc-dev-guide/src/external-repos.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/src/external-repos.md b/src/doc/rustc-dev-guide/src/external-repos.md index 68986a0bc6886..ecc65b26ab7a0 100644 --- a/src/doc/rustc-dev-guide/src/external-repos.md +++ b/src/doc/rustc-dev-guide/src/external-repos.md @@ -60,7 +60,10 @@ Currently, we are migrating Josh repositories to it. So far, it is used in: - rustc-dev-guide - stdarch -To use the tool, first install it with `cargo install --locked --git https://github.com/rust-lang/josh-sync`. +To install the tool: +``` +cargo install --locked --git https://github.com/rust-lang/josh-sync +``` Both pulls (synchronize changes from rust-lang/rust into the subtree) and pushes (synchronize changes from the subtree to rust-lang/rust) are performed from the subtree repository (so first From 5bb6b9db300870f436e8a45ffbe11efa41e44cad Mon Sep 17 00:00:00 2001 From: usamoi Date: Mon, 14 Jul 2025 16:41:42 +0800 Subject: [PATCH 34/36] remove no_gc_sections --- compiler/rustc_codegen_ssa/src/back/link.rs | 2 -- compiler/rustc_codegen_ssa/src/back/linker.rs | 33 ------------------- .../export-executable-symbols/rmake.rs | 1 + tests/ui/linking/export-executable-symbols.rs | 1 + 4 files changed, 2 insertions(+), 35 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index ae148287a6017..5ce301c0eb989 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -2543,8 +2543,6 @@ fn add_order_independent_options( let keep_metadata = crate_type == CrateType::Dylib || sess.opts.cg.profile_generate.enabled(); cmd.gc_sections(keep_metadata); - } else { - cmd.no_gc_sections(); } cmd.set_output_kind(link_output_kind, crate_type, out_filename); diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index e0a3ad55be08a..050797354b4a7 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -326,7 +326,6 @@ pub(crate) trait Linker { link_or_cc_args(self, &[path]); } fn gc_sections(&mut self, keep_metadata: bool); - fn no_gc_sections(&mut self); fn full_relro(&mut self); fn partial_relro(&mut self); fn no_relro(&mut self); @@ -688,12 +687,6 @@ impl<'a> Linker for GccLinker<'a> { } } - fn no_gc_sections(&mut self) { - if self.is_gnu || self.sess.target.is_like_wasm { - self.link_arg("--no-gc-sections"); - } - } - fn optimize(&mut self) { if !self.is_gnu && !self.sess.target.is_like_wasm { return; @@ -1010,10 +1003,6 @@ impl<'a> Linker for MsvcLinker<'a> { } } - fn no_gc_sections(&mut self) { - self.link_arg("/OPT:NOREF,NOICF"); - } - fn full_relro(&mut self) { // noop } @@ -1243,10 +1232,6 @@ impl<'a> Linker for EmLinker<'a> { // noop } - fn no_gc_sections(&mut self) { - // noop - } - fn optimize(&mut self) { // Emscripten performs own optimizations self.cc_arg(match self.sess.opts.optimize { @@ -1418,10 +1403,6 @@ impl<'a> Linker for WasmLd<'a> { self.link_arg("--gc-sections"); } - fn no_gc_sections(&mut self) { - self.link_arg("--no-gc-sections"); - } - fn optimize(&mut self) { // The -O flag is, as of late 2023, only used for merging of strings and debuginfo, and // only differentiates -O0 and -O1. It does not apply to LTO. @@ -1567,10 +1548,6 @@ impl<'a> Linker for L4Bender<'a> { } } - fn no_gc_sections(&mut self) { - self.link_arg("--no-gc-sections"); - } - fn optimize(&mut self) { // GNU-style linkers support optimization with -O. GNU ld doesn't // need a numeric argument, but other linkers do. @@ -1734,10 +1711,6 @@ impl<'a> Linker for AixLinker<'a> { self.link_arg("-bgc"); } - fn no_gc_sections(&mut self) { - self.link_arg("-bnogc"); - } - fn optimize(&mut self) {} fn pgo_gen(&mut self) { @@ -1982,8 +1955,6 @@ impl<'a> Linker for PtxLinker<'a> { fn gc_sections(&mut self, _keep_metadata: bool) {} - fn no_gc_sections(&mut self) {} - fn pgo_gen(&mut self) {} fn no_crt_objects(&mut self) {} @@ -2057,8 +2028,6 @@ impl<'a> Linker for LlbcLinker<'a> { fn gc_sections(&mut self, _keep_metadata: bool) {} - fn no_gc_sections(&mut self) {} - fn pgo_gen(&mut self) {} fn no_crt_objects(&mut self) {} @@ -2139,8 +2108,6 @@ impl<'a> Linker for BpfLinker<'a> { fn gc_sections(&mut self, _keep_metadata: bool) {} - fn no_gc_sections(&mut self) {} - fn pgo_gen(&mut self) {} fn no_crt_objects(&mut self) {} diff --git a/tests/run-make/export-executable-symbols/rmake.rs b/tests/run-make/export-executable-symbols/rmake.rs index c602a43f95723..aa130b0855b73 100644 --- a/tests/run-make/export-executable-symbols/rmake.rs +++ b/tests/run-make/export-executable-symbols/rmake.rs @@ -5,6 +5,7 @@ // See https://github.com/rust-lang/rust/pull/85673 //@ ignore-wasm +//@ ignore-cross-compile use run_make_support::object::Object; use run_make_support::{bin_name, is_darwin, object, rustc}; diff --git a/tests/ui/linking/export-executable-symbols.rs b/tests/ui/linking/export-executable-symbols.rs index edaf312f19284..2bff58ca38a8c 100644 --- a/tests/ui/linking/export-executable-symbols.rs +++ b/tests/ui/linking/export-executable-symbols.rs @@ -1,6 +1,7 @@ //@ run-pass //@ compile-flags: -Ctarget-feature=-crt-static -Zexport-executable-symbols //@ ignore-wasm +//@ ignore-cross-compile //@ edition: 2024 // Regression test for . From 6645bf54c159e5b06f829dbf04b6f6360a832993 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Thu, 17 Jul 2025 10:12:55 +0200 Subject: [PATCH 35/36] Copy GCC sources into the build directory even outside CI --- src/bootstrap/src/core/build_steps/gcc.rs | 25 ++++++++++------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/gcc.rs b/src/bootstrap/src/core/build_steps/gcc.rs index 899e3fd9a45a8..d4cbbe60921fb 100644 --- a/src/bootstrap/src/core/build_steps/gcc.rs +++ b/src/bootstrap/src/core/build_steps/gcc.rs @@ -220,21 +220,18 @@ fn build_gcc(metadata: &Meta, builder: &Builder<'_>, target: TargetSelection) { t!(fs::create_dir_all(install_dir)); // GCC creates files (e.g. symlinks to the downloaded dependencies) - // in the source directory, which does not work with our CI setup, where we mount + // in the source directory, which does not work with our CI/Docker setup, where we mount // source directories as read-only on Linux. - // Therefore, as a part of the build in CI, we first copy the whole source directory - // to the build directory, and perform the build from there. - let src_dir = if builder.config.is_running_on_ci { - let src_dir = builder.gcc_out(target).join("src"); - if src_dir.exists() { - builder.remove_dir(&src_dir); - } - builder.create_dir(&src_dir); - builder.cp_link_r(root, &src_dir); - src_dir - } else { - root.clone() - }; + // And in general, we shouldn't be modifying the source directories if possible, even for local + // builds. + // Therefore, we first copy the whole source directory to the build directory, and perform the + // build from there. + let src_dir = builder.gcc_out(target).join("src"); + if src_dir.exists() { + builder.remove_dir(&src_dir); + } + builder.create_dir(&src_dir); + builder.cp_link_r(root, &src_dir); command(src_dir.join("contrib/download_prerequisites")).current_dir(&src_dir).run(builder); let mut configure_cmd = command(src_dir.join("configure")); From 853333d4bdc7570b74e4bf02d506c2951a9d3e4b Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 15 Jul 2025 09:57:44 +0000 Subject: [PATCH 36/36] constify `Option` methods --- library/core/src/option.rs | 137 ++++++++++++++++++++++++++----------- 1 file changed, 97 insertions(+), 40 deletions(-) diff --git a/library/core/src/option.rs b/library/core/src/option.rs index 9f432758a66f6..7633429fc5c0d 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -577,6 +577,7 @@ #![stable(feature = "rust1", since = "1.0.0")] use crate::iter::{self, FusedIterator, TrustedLen}; +use crate::marker::Destruct; use crate::ops::{self, ControlFlow, Deref, DerefMut}; use crate::panicking::{panic, panic_display}; use crate::pin::Pin; @@ -649,7 +650,8 @@ impl Option { #[must_use] #[inline] #[stable(feature = "is_some_and", since = "1.70.0")] - pub fn is_some_and(self, f: impl FnOnce(T) -> bool) -> bool { + #[rustc_const_unstable(feature = "const_option_ops", issue = "143956")] + pub const fn is_some_and(self, f: impl ~const FnOnce(T) -> bool + ~const Destruct) -> bool { match self { None => false, Some(x) => f(x), @@ -697,7 +699,8 @@ impl Option { #[must_use] #[inline] #[stable(feature = "is_none_or", since = "1.82.0")] - pub fn is_none_or(self, f: impl FnOnce(T) -> bool) -> bool { + #[rustc_const_unstable(feature = "const_option_ops", issue = "143956")] + pub const fn is_none_or(self, f: impl ~const FnOnce(T) -> bool + ~const Destruct) -> bool { match self { None => true, Some(x) => f(x), @@ -1023,7 +1026,12 @@ impl Option { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] - pub fn unwrap_or(self, default: T) -> T { + #[rustc_allow_const_fn_unstable(const_precise_live_drops)] + #[rustc_const_unstable(feature = "const_option_ops", issue = "143956")] + pub const fn unwrap_or(self, default: T) -> T + where + T: ~const Destruct, + { match self { Some(x) => x, None => default, @@ -1042,9 +1050,10 @@ impl Option { #[inline] #[track_caller] #[stable(feature = "rust1", since = "1.0.0")] - pub fn unwrap_or_else(self, f: F) -> T + #[rustc_const_unstable(feature = "const_option_ops", issue = "143956")] + pub const fn unwrap_or_else(self, f: F) -> T where - F: FnOnce() -> T, + F: ~const FnOnce() -> T + ~const Destruct, { match self { Some(x) => x, @@ -1073,9 +1082,10 @@ impl Option { /// [`FromStr`]: crate::str::FromStr #[inline] #[stable(feature = "rust1", since = "1.0.0")] - pub fn unwrap_or_default(self) -> T + #[rustc_const_unstable(feature = "const_option_ops", issue = "143956")] + pub const fn unwrap_or_default(self) -> T where - T: Default, + T: ~const Default, { match self { Some(x) => x, @@ -1139,9 +1149,10 @@ impl Option { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] - pub fn map(self, f: F) -> Option + #[rustc_const_unstable(feature = "const_option_ops", issue = "143956")] + pub const fn map(self, f: F) -> Option where - F: FnOnce(T) -> U, + F: ~const FnOnce(T) -> U + ~const Destruct, { match self { Some(x) => Some(f(x)), @@ -1169,7 +1180,11 @@ impl Option { /// ``` #[inline] #[stable(feature = "result_option_inspect", since = "1.76.0")] - pub fn inspect(self, f: F) -> Self { + #[rustc_const_unstable(feature = "const_option_ops", issue = "143956")] + pub const fn inspect(self, f: F) -> Self + where + F: ~const FnOnce(&T) + ~const Destruct, + { if let Some(ref x) = self { f(x); } @@ -1198,9 +1213,11 @@ impl Option { #[inline] #[stable(feature = "rust1", since = "1.0.0")] #[must_use = "if you don't need the returned value, use `if let` instead"] - pub fn map_or(self, default: U, f: F) -> U + #[rustc_const_unstable(feature = "const_option_ops", issue = "143956")] + pub const fn map_or(self, default: U, f: F) -> U where - F: FnOnce(T) -> U, + F: ~const FnOnce(T) -> U + ~const Destruct, + U: ~const Destruct, { match self { Some(t) => f(t), @@ -1243,10 +1260,11 @@ impl Option { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] - pub fn map_or_else(self, default: D, f: F) -> U + #[rustc_const_unstable(feature = "const_option_ops", issue = "143956")] + pub const fn map_or_else(self, default: D, f: F) -> U where - D: FnOnce() -> U, - F: FnOnce(T) -> U, + D: ~const FnOnce() -> U + ~const Destruct, + F: ~const FnOnce(T) -> U + ~const Destruct, { match self { Some(t) => f(t), @@ -1273,10 +1291,11 @@ impl Option { /// [default value]: Default::default #[inline] #[unstable(feature = "result_option_map_or_default", issue = "138099")] - pub fn map_or_default(self, f: F) -> U + #[rustc_const_unstable(feature = "const_option_ops", issue = "143956")] + pub const fn map_or_default(self, f: F) -> U where - U: Default, - F: FnOnce(T) -> U, + U: ~const Default, + F: ~const FnOnce(T) -> U + ~const Destruct, { match self { Some(t) => f(t), @@ -1307,7 +1326,8 @@ impl Option { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] - pub fn ok_or(self, err: E) -> Result { + #[rustc_const_unstable(feature = "const_option_ops", issue = "143956")] + pub const fn ok_or(self, err: E) -> Result { match self { Some(v) => Ok(v), None => Err(err), @@ -1332,9 +1352,10 @@ impl Option { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] - pub fn ok_or_else(self, err: F) -> Result + #[rustc_const_unstable(feature = "const_option_ops", issue = "143956")] + pub const fn ok_or_else(self, err: F) -> Result where - F: FnOnce() -> E, + F: ~const FnOnce() -> E + ~const Destruct, { match self { Some(v) => Ok(v), @@ -1463,7 +1484,12 @@ impl Option { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] - pub fn and(self, optb: Option) -> Option { + #[rustc_const_unstable(feature = "const_option_ops", issue = "143956")] + pub const fn and(self, optb: Option) -> Option + where + T: ~const Destruct, + U: ~const Destruct, + { match self { Some(_) => optb, None => None, @@ -1502,9 +1528,10 @@ impl Option { #[inline] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_confusables("flat_map", "flatmap")] - pub fn and_then(self, f: F) -> Option + #[rustc_const_unstable(feature = "const_option_ops", issue = "143956")] + pub const fn and_then(self, f: F) -> Option where - F: FnOnce(T) -> Option, + F: ~const FnOnce(T) -> Option + ~const Destruct, { match self { Some(x) => f(x), @@ -1538,9 +1565,11 @@ impl Option { /// [`Some(t)`]: Some #[inline] #[stable(feature = "option_filter", since = "1.27.0")] - pub fn filter

(self, predicate: P) -> Self + #[rustc_const_unstable(feature = "const_option_ops", issue = "143956")] + pub const fn filter

(self, predicate: P) -> Self where - P: FnOnce(&T) -> bool, + P: ~const FnOnce(&T) -> bool + ~const Destruct, + T: ~const Destruct, { if let Some(x) = self { if predicate(&x) { @@ -1579,7 +1608,11 @@ impl Option { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] - pub fn or(self, optb: Option) -> Option { + #[rustc_const_unstable(feature = "const_option_ops", issue = "143956")] + pub const fn or(self, optb: Option) -> Option + where + T: ~const Destruct, + { match self { x @ Some(_) => x, None => optb, @@ -1601,9 +1634,13 @@ impl Option { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] - pub fn or_else(self, f: F) -> Option + #[rustc_const_unstable(feature = "const_option_ops", issue = "143956")] + pub const fn or_else(self, f: F) -> Option where - F: FnOnce() -> Option, + F: ~const FnOnce() -> Option + ~const Destruct, + //FIXME(const_hack): this `T: ~const Destruct` is unnecessary, but even precise live drops can't tell + // no value of type `T` gets dropped here + T: ~const Destruct, { match self { x @ Some(_) => x, @@ -1634,7 +1671,11 @@ impl Option { /// ``` #[inline] #[stable(feature = "option_xor", since = "1.37.0")] - pub fn xor(self, optb: Option) -> Option { + #[rustc_const_unstable(feature = "const_option_ops", issue = "143956")] + pub const fn xor(self, optb: Option) -> Option + where + T: ~const Destruct, + { match (self, optb) { (a @ Some(_), None) => a, (None, b @ Some(_)) => b, @@ -1668,7 +1709,11 @@ impl Option { #[must_use = "if you intended to set a value, consider assignment instead"] #[inline] #[stable(feature = "option_insert", since = "1.53.0")] - pub fn insert(&mut self, value: T) -> &mut T { + #[rustc_const_unstable(feature = "const_option_ops", issue = "143956")] + pub const fn insert(&mut self, value: T) -> &mut T + where + T: ~const Destruct, + { *self = Some(value); // SAFETY: the code above just filled the option @@ -1720,9 +1765,10 @@ impl Option { /// ``` #[inline] #[stable(feature = "option_get_or_insert_default", since = "1.83.0")] - pub fn get_or_insert_default(&mut self) -> &mut T + #[rustc_const_unstable(feature = "const_option_ops", issue = "143956")] + pub const fn get_or_insert_default(&mut self) -> &mut T where - T: Default, + T: ~const Default + ~const Destruct, { self.get_or_insert_with(T::default) } @@ -1746,9 +1792,11 @@ impl Option { /// ``` #[inline] #[stable(feature = "option_entry", since = "1.20.0")] - pub fn get_or_insert_with(&mut self, f: F) -> &mut T + #[rustc_const_unstable(feature = "const_option_ops", issue = "143956")] + pub const fn get_or_insert_with(&mut self, f: F) -> &mut T where - F: FnOnce() -> T, + F: ~const FnOnce() -> T + ~const Destruct, + T: ~const Destruct, { if let None = self { *self = Some(f()); @@ -1812,9 +1860,10 @@ impl Option { /// ``` #[inline] #[stable(feature = "option_take_if", since = "1.80.0")] - pub fn take_if

(&mut self, predicate: P) -> Option + #[rustc_const_unstable(feature = "const_option_ops", issue = "143956")] + pub const fn take_if

(&mut self, predicate: P) -> Option where - P: FnOnce(&mut T) -> bool, + P: ~const FnOnce(&mut T) -> bool + ~const Destruct, { if self.as_mut().map_or(false, predicate) { self.take() } else { None } } @@ -1859,7 +1908,12 @@ impl Option { /// assert_eq!(x.zip(z), None); /// ``` #[stable(feature = "option_zip_option", since = "1.46.0")] - pub fn zip(self, other: Option) -> Option<(T, U)> { + #[rustc_const_unstable(feature = "const_option_ops", issue = "143956")] + pub const fn zip(self, other: Option) -> Option<(T, U)> + where + T: ~const Destruct, + U: ~const Destruct, + { match (self, other) { (Some(a), Some(b)) => Some((a, b)), _ => None, @@ -1895,9 +1949,12 @@ impl Option { /// assert_eq!(x.zip_with(None, Point::new), None); /// ``` #[unstable(feature = "option_zip", issue = "70086")] - pub fn zip_with(self, other: Option, f: F) -> Option + #[rustc_const_unstable(feature = "const_option_ops", issue = "143956")] + pub const fn zip_with(self, other: Option, f: F) -> Option where - F: FnOnce(T, U) -> R, + F: ~const FnOnce(T, U) -> R + ~const Destruct, + T: ~const Destruct, + U: ~const Destruct, { match (self, other) { (Some(a), Some(b)) => Some(f(a, b)),