From eb729380499b12cb539d665221d5930a06467024 Mon Sep 17 00:00:00 2001 From: Markus Mayer Date: Wed, 22 May 2024 11:07:08 +0200 Subject: [PATCH 01/10] Add FRAC_1_SQRT_2PI constant to f16/f32/f64/f128 --- library/core/src/num/f128.rs | 6 ++++++ library/core/src/num/f16.rs | 5 +++++ library/core/src/num/f32.rs | 4 ++++ library/core/src/num/f64.rs | 4 ++++ 4 files changed, 19 insertions(+) diff --git a/library/core/src/num/f128.rs b/library/core/src/num/f128.rs index 9362dc8765492..75eed26485881 100644 --- a/library/core/src/num/f128.rs +++ b/library/core/src/num/f128.rs @@ -68,6 +68,12 @@ pub mod consts { pub const FRAC_1_SQRT_PI: f128 = 0.564189583547756286948079451560772585844050629328998856844086_f128; + /// 1/sqrt(2π) + #[unstable(feature = "f128", issue = "116909")] + // Also, #[unstable(feature = "more_float_constants", issue = "103883")] + pub const FRAC_1_SQRT_2PI: f128 = + 0.398942280401432677939946059934381868475858631164934657665926_f128; + /// 2/π #[unstable(feature = "f128", issue = "116909")] pub const FRAC_2_PI: f128 = 0.636619772367581343075535053490057448137838582961825794990669_f128; diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs index c4d4584544bad..955314516a4a7 100644 --- a/library/core/src/num/f16.rs +++ b/library/core/src/num/f16.rs @@ -67,6 +67,11 @@ pub mod consts { // Also, #[unstable(feature = "more_float_constants", issue = "103883")] pub const FRAC_1_SQRT_PI: f16 = 0.564189583547756286948079451560772586_f16; + /// 1/sqrt(2π) + #[unstable(feature = "f16", issue = "116909")] + // Also, #[unstable(feature = "more_float_constants", issue = "103883")] + pub const FRAC_1_SQRT_2PI: f16 = 0.398942280401432677939946059934381868_f16; + /// 2/π #[unstable(feature = "f16", issue = "116909")] pub const FRAC_2_PI: f16 = 0.636619772367581343075535053490057448_f16; diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index 2e715fb0bdde7..8c591af3c0d0f 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -327,6 +327,10 @@ pub mod consts { #[unstable(feature = "more_float_constants", issue = "103883")] pub const FRAC_1_SQRT_PI: f32 = 0.564189583547756286948079451560772586_f32; + /// 1/sqrt(2π) + #[unstable(feature = "more_float_constants", issue = "103883")] + pub const FRAC_1_SQRT_2PI: f32 = 0.398942280401432677939946059934381868_f32; + /// 2/π #[stable(feature = "rust1", since = "1.0.0")] pub const FRAC_2_PI: f32 = 0.636619772367581343075535053490057448_f32; diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index db8e1f318adba..db458c4375ce7 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -327,6 +327,10 @@ pub mod consts { #[unstable(feature = "more_float_constants", issue = "103883")] pub const FRAC_1_SQRT_PI: f64 = 0.564189583547756286948079451560772586_f64; + /// 1/sqrt(2π) + #[unstable(feature = "more_float_constants", issue = "103883")] + pub const FRAC_1_SQRT_2PI: f64 = 0.398942280401432677939946059934381868_f64; + /// 2/π #[stable(feature = "rust1", since = "1.0.0")] pub const FRAC_2_PI: f64 = 0.636619772367581343075535053490057448_f64; From 54bb08d53881da7c43b91fd5a6bd8ef9a3b754e6 Mon Sep 17 00:00:00 2001 From: Markus Mayer Date: Wed, 29 May 2024 14:58:34 +0200 Subject: [PATCH 02/10] Add FRAC_1_SQRT_2PI doc alias to FRAC_1_SQRT_TAU This is create symmetry between the already existing TAU constant (2pi) and the newly-introduced FRAC_1_SQRT_2PI, keeping the more common name while increasing visibility. --- library/core/src/num/f128.rs | 1 + library/core/src/num/f16.rs | 1 + library/core/src/num/f32.rs | 1 + library/core/src/num/f64.rs | 1 + 4 files changed, 4 insertions(+) diff --git a/library/core/src/num/f128.rs b/library/core/src/num/f128.rs index 75eed26485881..129f62fb43d1a 100644 --- a/library/core/src/num/f128.rs +++ b/library/core/src/num/f128.rs @@ -69,6 +69,7 @@ pub mod consts { 0.564189583547756286948079451560772585844050629328998856844086_f128; /// 1/sqrt(2π) + #[doc(alias = "FRAC_1_SQRT_TAU")] #[unstable(feature = "f128", issue = "116909")] // Also, #[unstable(feature = "more_float_constants", issue = "103883")] pub const FRAC_1_SQRT_2PI: f128 = diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs index 955314516a4a7..7a488cd6bf6fa 100644 --- a/library/core/src/num/f16.rs +++ b/library/core/src/num/f16.rs @@ -68,6 +68,7 @@ pub mod consts { pub const FRAC_1_SQRT_PI: f16 = 0.564189583547756286948079451560772586_f16; /// 1/sqrt(2π) + #[doc(alias = "FRAC_1_SQRT_TAU")] #[unstable(feature = "f16", issue = "116909")] // Also, #[unstable(feature = "more_float_constants", issue = "103883")] pub const FRAC_1_SQRT_2PI: f16 = 0.398942280401432677939946059934381868_f16; diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index 8c591af3c0d0f..8e9c7b4d11f32 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -328,6 +328,7 @@ pub mod consts { pub const FRAC_1_SQRT_PI: f32 = 0.564189583547756286948079451560772586_f32; /// 1/sqrt(2π) + #[doc(alias = "FRAC_1_SQRT_TAU")] #[unstable(feature = "more_float_constants", issue = "103883")] pub const FRAC_1_SQRT_2PI: f32 = 0.398942280401432677939946059934381868_f32; diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index db458c4375ce7..5247897437a0c 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -328,6 +328,7 @@ pub mod consts { pub const FRAC_1_SQRT_PI: f64 = 0.564189583547756286948079451560772586_f64; /// 1/sqrt(2π) + #[doc(alias = "FRAC_1_SQRT_TAU")] #[unstable(feature = "more_float_constants", issue = "103883")] pub const FRAC_1_SQRT_2PI: f64 = 0.398942280401432677939946059934381868_f64; From fb6fbf02a65ad1fc766aeee7c7698907d56f8e12 Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Tue, 4 Jun 2024 01:22:46 -0700 Subject: [PATCH 03/10] Use FileCheck to parameterize codegen tests over hashes When things like our internal hashing or representations change, it is inappropriate for these tests to suddenly fail for no reason. The chance of error is reduced if we instead pattern-match. --- tests/assembly/asm/global_asm.rs | 4 ++-- tests/codegen/consts.rs | 4 ++-- tests/codegen/pattern_type_symbols.rs | 4 ++-- tests/codegen/virtual-function-elimination.rs | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/assembly/asm/global_asm.rs b/tests/assembly/asm/global_asm.rs index 8f824563e8ae5..22cf4bdb15b70 100644 --- a/tests/assembly/asm/global_asm.rs +++ b/tests/assembly/asm/global_asm.rs @@ -25,9 +25,9 @@ global_asm!("movl ${}, %ecx", const 5, options(att_syntax)); global_asm!("call {}", sym my_func); // CHECK: lea rax, [rip + MY_STATIC] global_asm!("lea rax, [rip + {}]", sym MY_STATIC); -// CHECK: call _RNvCsddMtV7nAi4C_10global_asm6foobar +// CHECK: call _RNvC[[CRATE_IDENT:[a-zA-Z0-9]{12}]]_10global_asm6foobar global_asm!("call {}", sym foobar); -// CHECK: _RNvCsddMtV7nAi4C_10global_asm6foobar: +// CHECK: _RNvC[[CRATE_IDENT]]_10global_asm6foobar: fn foobar() { loop {} } diff --git a/tests/codegen/consts.rs b/tests/codegen/consts.rs index 93c58c37c2873..42ce7679d1acb 100644 --- a/tests/codegen/consts.rs +++ b/tests/codegen/consts.rs @@ -9,11 +9,11 @@ // CHECK: @STATIC = {{.*}}, align 4 // This checks the constants from inline_enum_const -// CHECK: @alloc_af1f8e8e6f4b341431a1d405e652df2d = {{.*}}, align 2 +// CHECK: @alloc_[[INLINE_ENUM_HASH:[a-f0-9]{32}]] = {{.*}}, align 2 // This checks the constants from {low,high}_align_const, they share the same // constant, but the alignment differs, so the higher one should be used -// CHECK: [[LOW_HIGH:@alloc_[a-f0-9]+]] = {{.*}}, align 4 +// CHECK: [[LOW_HIGH:@alloc_[a-f0-9]{32}]] = {{.*}}, align 4 #[derive(Copy, Clone)] // repr(i16) is required for the {low,high}_align_const test diff --git a/tests/codegen/pattern_type_symbols.rs b/tests/codegen/pattern_type_symbols.rs index dfe8344334870..a99b3efca415b 100644 --- a/tests/codegen/pattern_type_symbols.rs +++ b/tests/codegen/pattern_type_symbols.rs @@ -15,9 +15,9 @@ fn foo() {} pub fn bar() { // CHECK: call pattern_type_symbols::foo:: - // CHECK: call void @_RINvCs3QvG2ESzx2Q_20pattern_type_symbols3foomEB2_ + // CHECK: call void @_RINvC[[CRATE_IDENT:[a-zA-Z0-9]{12}]]_20pattern_type_symbols3foomEB2_ foo::(); // CHECK: call pattern_type_symbols::foo::<(u32, [(); 0], [(); 999999999], [(); true])> - // CHECK: call void @_RINvCs3QvG2ESzx2Q_20pattern_type_symbols3fooTmAum0_Aum3b9ac9ff_Aub1_EEB2_ + // CHECK: call void @_RINvC[[CRATE_IDENT]]_20pattern_type_symbols3fooTmAum0_Aum3b9ac9ff_Aub1_EEB2_ foo::(); } diff --git a/tests/codegen/virtual-function-elimination.rs b/tests/codegen/virtual-function-elimination.rs index 6c391d9114b42..23d7657baa99b 100644 --- a/tests/codegen/virtual-function-elimination.rs +++ b/tests/codegen/virtual-function-elimination.rs @@ -81,7 +81,7 @@ fn taking_u(u: &dyn U) -> i32 { } pub fn taking_v(v: &dyn V) -> i32 { - // CHECK: @llvm.type.checked.load({{.*}}, i32 24, metadata !"NtCs64ITQYi9761_28virtual_function_elimination1V") + // CHECK: @llvm.type.checked.load({{.*}}, i32 24, metadata !"NtC[[CRATE_IDENT:[a-zA-Z0-9]{12}]]_28virtual_function_elimination1V") v.public_function() } @@ -96,5 +96,5 @@ pub fn main() { // CHECK: ![[TYPE0]] = !{i64 0, !"[[MANGLED_TYPE0]]"} // CHECK: ![[VCALL_VIS0]] = !{i64 2} // CHECK: ![[TYPE1]] = !{i64 0, !"[[MANGLED_TYPE1]]"} -// CHECK: ![[TYPE2]] = !{i64 0, !"NtCs64ITQYi9761_28virtual_function_elimination1V"} +// CHECK: ![[TYPE2]] = !{i64 0, !"NtC[[CRATE_IDENT]]_28virtual_function_elimination1V"} // CHECK: ![[VCALL_VIS2]] = !{i64 1} From 8a0f1957982a6042ec550147243f811f25889d9c Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Thu, 23 May 2024 16:48:52 -0400 Subject: [PATCH 04/10] docs: add README.md for build_helper --- src/tools/build_helper/README.md | 1 + src/tools/build_helper/src/lib.rs | 2 ++ 2 files changed, 3 insertions(+) create mode 100644 src/tools/build_helper/README.md diff --git a/src/tools/build_helper/README.md b/src/tools/build_helper/README.md new file mode 100644 index 0000000000000..f81b631c3fdbf --- /dev/null +++ b/src/tools/build_helper/README.md @@ -0,0 +1 @@ +Types and functions shared across tools in this workspace. diff --git a/src/tools/build_helper/src/lib.rs b/src/tools/build_helper/src/lib.rs index 2abda5d3ebf27..d831cfa0d79a6 100644 --- a/src/tools/build_helper/src/lib.rs +++ b/src/tools/build_helper/src/lib.rs @@ -1,3 +1,5 @@ +//! Types and functions shared across tools in this workspace. + pub mod ci; pub mod git; pub mod metrics; From 021ccf6c4ed0ae32f66b06a06f542c83f4e81670 Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Sun, 12 May 2024 00:22:42 -0700 Subject: [PATCH 05/10] Enable GVN for `AggregateKind::RawPtr` & `UnOp::PtrMetadata` --- compiler/rustc_mir_transform/src/gvn.rs | 99 ++++++++++++++++--- ...ore_aggregate_raw_ptr.GVN.panic-abort.diff | 60 +++++++++++ ...re_aggregate_raw_ptr.GVN.panic-unwind.diff | 60 +++++++++++ ....meta_of_ref_to_slice.GVN.panic-abort.diff | 32 ++++++ ...meta_of_ref_to_slice.GVN.panic-unwind.diff | 32 ++++++ tests/mir-opt/gvn.rs | 40 ++++++++ ...vn.slice_const_length.GVN.panic-abort.diff | 48 +++++++++ ...n.slice_const_length.GVN.panic-unwind.diff | 48 +++++++++ ...from_raw_parts_as_ptr.GVN.panic-abort.diff | 51 ++++++++++ ...rom_raw_parts_as_ptr.GVN.panic-unwind.diff | 51 ++++++++++ ...e_add_fat.PreCodegen.after.panic-abort.mir | 10 +- ..._add_fat.PreCodegen.after.panic-unwind.mir | 10 +- ..._add_thin.PreCodegen.after.panic-abort.mir | 2 + ...add_thin.PreCodegen.after.panic-unwind.mir | 2 + 14 files changed, 519 insertions(+), 26 deletions(-) create mode 100644 tests/mir-opt/gvn.casts_before_aggregate_raw_ptr.GVN.panic-abort.diff create mode 100644 tests/mir-opt/gvn.casts_before_aggregate_raw_ptr.GVN.panic-unwind.diff create mode 100644 tests/mir-opt/gvn.meta_of_ref_to_slice.GVN.panic-abort.diff create mode 100644 tests/mir-opt/gvn.meta_of_ref_to_slice.GVN.panic-unwind.diff create mode 100644 tests/mir-opt/gvn.slice_const_length.GVN.panic-abort.diff create mode 100644 tests/mir-opt/gvn.slice_const_length.GVN.panic-unwind.diff create mode 100644 tests/mir-opt/gvn.slice_from_raw_parts_as_ptr.GVN.panic-abort.diff create mode 100644 tests/mir-opt/gvn.slice_from_raw_parts_as_ptr.GVN.panic-unwind.diff diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index acde16fcb7579..aba5ec8813a6b 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -83,8 +83,8 @@ //! that contain `AllocId`s. use rustc_const_eval::const_eval::DummyMachine; -use rustc_const_eval::interpret::{intern_const_alloc_for_constprop, MemoryKind}; -use rustc_const_eval::interpret::{ImmTy, InterpCx, OpTy, Projectable, Scalar}; +use rustc_const_eval::interpret::{intern_const_alloc_for_constprop, MemPlaceMeta, MemoryKind}; +use rustc_const_eval::interpret::{ImmTy, Immediate, InterpCx, OpTy, Projectable, Scalar}; use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::graph::dominators::Dominators; use rustc_hir::def::DefKind; @@ -99,7 +99,7 @@ use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::def_id::DefId; use rustc_span::DUMMY_SP; -use rustc_target::abi::{self, Abi, Size, VariantIdx, FIRST_VARIANT}; +use rustc_target::abi::{self, Abi, FieldIdx, Size, VariantIdx, FIRST_VARIANT}; use smallvec::SmallVec; use std::borrow::Cow; @@ -177,6 +177,12 @@ 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)] @@ -385,11 +391,22 @@ 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]).ok()?; + let meta = if fields[1].layout.is_zst() { + MemPlaceMeta::None + } else { + MemPlaceMeta::Meta(self.ecx.read_scalar(fields[1]).ok()?) + }; + let ptr_imm = Immediate::new_pointer_with_meta(data, meta, &self.ecx); + ImmTy::from_immediate(ptr_imm, ty).into() } else if matches!(ty.abi, Abi::Scalar(..) | Abi::ScalarPair(..)) { let dest = self.ecx.allocate(ty, MemoryKind::Stack).ok()?; let variant_dest = if let Some(variant) = variant { @@ -862,10 +879,10 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { rvalue: &mut Rvalue<'tcx>, location: Location, ) -> Option { - let Rvalue::Aggregate(box ref kind, ref mut fields) = *rvalue else { bug!() }; + let Rvalue::Aggregate(box ref kind, ref mut field_ops) = *rvalue else { bug!() }; let tcx = self.tcx; - if fields.is_empty() { + if field_ops.is_empty() { let is_zst = match *kind { AggregateKind::Array(..) | AggregateKind::Tuple @@ -884,13 +901,13 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } } - let (ty, variant_index) = match *kind { + let (mut ty, variant_index) = match *kind { AggregateKind::Array(..) => { - assert!(!fields.is_empty()); + assert!(!field_ops.is_empty()); (AggregateTy::Array, FIRST_VARIANT) } AggregateKind::Tuple => { - assert!(!fields.is_empty()); + assert!(!field_ops.is_empty()); (AggregateTy::Tuple, FIRST_VARIANT) } AggregateKind::Closure(did, args) @@ -901,15 +918,49 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } // Do not track unions. AggregateKind::Adt(_, _, _, _, Some(_)) => return None, - // FIXME: Do the extra work to GVN `from_raw_parts` - AggregateKind::RawPtr(..) => 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 output_pointer_ty = Ty::new_ptr(self.tcx, pointee_ty, mtbl); + (AggregateTy::RawPtr { data_pointer_ty, output_pointer_ty }, FIRST_VARIANT) + } }; - let fields: Option> = fields + let fields: Option> = field_ops .iter_mut() .map(|op| self.simplify_operand(op, location).or_else(|| self.new_opaque())) .collect(); - let fields = fields?; + let mut fields = fields?; + + if let AggregateTy::RawPtr { data_pointer_ty, output_pointer_ty } = &mut ty { + let mut was_updated = false; + + // 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.param_env) + { + fields[0] = *cast_value; + *data_pointer_ty = *cast_from; + was_updated = true; + } + + if was_updated { + if let Some(const_) = self.try_as_constant(fields[0]) { + field_ops[FieldIdx::ZERO] = Operand::Constant(Box::new(const_)); + } else if let Some(local) = self.try_as_local(fields[0], location) { + field_ops[FieldIdx::ZERO] = Operand::Copy(Place::from(local)); + self.reused_locals.insert(local); + } + } + } if let AggregateTy::Array = ty && fields.len() > 4 @@ -941,6 +992,9 @@ 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]); + } _ => return None, }; @@ -1092,6 +1146,23 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { return self.new_opaque(); } + let mut was_updated = false; + + // If that cast just casts away the metadata again, + if let PtrToPtr = kind + && let Value::Aggregate(AggregateTy::RawPtr { data_pointer_ty, .. }, _, fields) = + self.get(value) + && let ty::RawPtr(to_pointee, _) = to.kind() + && to_pointee.is_sized(self.tcx, self.param_env) + { + from = *data_pointer_ty; + value = fields[0]; + was_updated = true; + if *data_pointer_ty == to { + return Some(fields[0]); + } + } + if let PtrToPtr | PointerCoercion(MutToConstPointer) = kind && let Value::Cast { kind: inner_kind, value: inner_value, from: inner_from, to: _ } = *self.get(value) @@ -1100,9 +1171,13 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { from = inner_from; value = inner_value; *kind = PtrToPtr; + was_updated = true; if inner_from == to { return Some(inner_value); } + } + + if was_updated { if let Some(const_) = self.try_as_constant(value) { *operand = Operand::Constant(Box::new(const_)); } else if let Some(local) = self.try_as_local(value, location) { diff --git a/tests/mir-opt/gvn.casts_before_aggregate_raw_ptr.GVN.panic-abort.diff b/tests/mir-opt/gvn.casts_before_aggregate_raw_ptr.GVN.panic-abort.diff new file mode 100644 index 0000000000000..6e5bdb16595be --- /dev/null +++ b/tests/mir-opt/gvn.casts_before_aggregate_raw_ptr.GVN.panic-abort.diff @@ -0,0 +1,60 @@ +- // MIR for `casts_before_aggregate_raw_ptr` before GVN ++ // MIR for `casts_before_aggregate_raw_ptr` after GVN + + fn casts_before_aggregate_raw_ptr(_1: *const u32) -> *const [u8] { + debug x => _1; + let mut _0: *const [u8]; + let _2: *const [u8; 4]; + let mut _3: *const u32; + let mut _5: *const [u8; 4]; + let mut _7: *const u8; + let mut _8: *const (); + scope 1 { + debug x => _2; + let _4: *const u8; + scope 2 { + debug x => _4; + let _6: *const (); + scope 3 { + debug x => _6; + } + } + } + + bb0: { +- StorageLive(_2); ++ nop; + StorageLive(_3); + _3 = _1; +- _2 = move _3 as *const [u8; 4] (PtrToPtr); ++ _2 = _1 as *const [u8; 4] (PtrToPtr); + StorageDead(_3); +- StorageLive(_4); ++ nop; + StorageLive(_5); + _5 = _2; +- _4 = move _5 as *const u8 (PtrToPtr); ++ _4 = _1 as *const u8 (PtrToPtr); + StorageDead(_5); +- StorageLive(_6); ++ nop; + StorageLive(_7); + _7 = _4; +- _6 = move _7 as *const () (PtrToPtr); ++ _6 = _1 as *const () (PtrToPtr); + StorageDead(_7); + StorageLive(_8); + _8 = _6; +- _0 = *const [u8] from (move _8, const 4_usize); ++ _0 = *const [u8] from (_1, const 4_usize); + StorageDead(_8); +- StorageDead(_6); +- StorageDead(_4); +- StorageDead(_2); ++ nop; ++ nop; ++ nop; + return; + } + } + diff --git a/tests/mir-opt/gvn.casts_before_aggregate_raw_ptr.GVN.panic-unwind.diff b/tests/mir-opt/gvn.casts_before_aggregate_raw_ptr.GVN.panic-unwind.diff new file mode 100644 index 0000000000000..6e5bdb16595be --- /dev/null +++ b/tests/mir-opt/gvn.casts_before_aggregate_raw_ptr.GVN.panic-unwind.diff @@ -0,0 +1,60 @@ +- // MIR for `casts_before_aggregate_raw_ptr` before GVN ++ // MIR for `casts_before_aggregate_raw_ptr` after GVN + + fn casts_before_aggregate_raw_ptr(_1: *const u32) -> *const [u8] { + debug x => _1; + let mut _0: *const [u8]; + let _2: *const [u8; 4]; + let mut _3: *const u32; + let mut _5: *const [u8; 4]; + let mut _7: *const u8; + let mut _8: *const (); + scope 1 { + debug x => _2; + let _4: *const u8; + scope 2 { + debug x => _4; + let _6: *const (); + scope 3 { + debug x => _6; + } + } + } + + bb0: { +- StorageLive(_2); ++ nop; + StorageLive(_3); + _3 = _1; +- _2 = move _3 as *const [u8; 4] (PtrToPtr); ++ _2 = _1 as *const [u8; 4] (PtrToPtr); + StorageDead(_3); +- StorageLive(_4); ++ nop; + StorageLive(_5); + _5 = _2; +- _4 = move _5 as *const u8 (PtrToPtr); ++ _4 = _1 as *const u8 (PtrToPtr); + StorageDead(_5); +- StorageLive(_6); ++ nop; + StorageLive(_7); + _7 = _4; +- _6 = move _7 as *const () (PtrToPtr); ++ _6 = _1 as *const () (PtrToPtr); + StorageDead(_7); + StorageLive(_8); + _8 = _6; +- _0 = *const [u8] from (move _8, const 4_usize); ++ _0 = *const [u8] from (_1, const 4_usize); + StorageDead(_8); +- StorageDead(_6); +- StorageDead(_4); +- StorageDead(_2); ++ nop; ++ nop; ++ nop; + return; + } + } + diff --git a/tests/mir-opt/gvn.meta_of_ref_to_slice.GVN.panic-abort.diff b/tests/mir-opt/gvn.meta_of_ref_to_slice.GVN.panic-abort.diff new file mode 100644 index 0000000000000..73dbabb56b353 --- /dev/null +++ b/tests/mir-opt/gvn.meta_of_ref_to_slice.GVN.panic-abort.diff @@ -0,0 +1,32 @@ +- // MIR for `meta_of_ref_to_slice` before GVN ++ // MIR for `meta_of_ref_to_slice` after GVN + + fn meta_of_ref_to_slice(_1: *const i32) -> usize { + debug x => _1; + let mut _0: usize; + let _2: *const [i32]; + let mut _3: *const i32; + let mut _4: *const [i32]; + scope 1 { + debug ptr => _2; + } + + bb0: { +- StorageLive(_2); ++ nop; + StorageLive(_3); + _3 = _1; +- _2 = *const [i32] from (move _3, const 1_usize); ++ _2 = *const [i32] from (_1, const 1_usize); + StorageDead(_3); + StorageLive(_4); + _4 = _2; +- _0 = PtrMetadata(move _4); ++ _0 = const 1_usize; + StorageDead(_4); +- StorageDead(_2); ++ nop; + return; + } + } + diff --git a/tests/mir-opt/gvn.meta_of_ref_to_slice.GVN.panic-unwind.diff b/tests/mir-opt/gvn.meta_of_ref_to_slice.GVN.panic-unwind.diff new file mode 100644 index 0000000000000..73dbabb56b353 --- /dev/null +++ b/tests/mir-opt/gvn.meta_of_ref_to_slice.GVN.panic-unwind.diff @@ -0,0 +1,32 @@ +- // MIR for `meta_of_ref_to_slice` before GVN ++ // MIR for `meta_of_ref_to_slice` after GVN + + fn meta_of_ref_to_slice(_1: *const i32) -> usize { + debug x => _1; + let mut _0: usize; + let _2: *const [i32]; + let mut _3: *const i32; + let mut _4: *const [i32]; + scope 1 { + debug ptr => _2; + } + + bb0: { +- StorageLive(_2); ++ nop; + StorageLive(_3); + _3 = _1; +- _2 = *const [i32] from (move _3, const 1_usize); ++ _2 = *const [i32] from (_1, const 1_usize); + StorageDead(_3); + StorageLive(_4); + _4 = _2; +- _0 = PtrMetadata(move _4); ++ _0 = const 1_usize; + StorageDead(_4); +- StorageDead(_2); ++ nop; + return; + } + } + diff --git a/tests/mir-opt/gvn.rs b/tests/mir-opt/gvn.rs index 315377e4356f7..720018f112e5a 100644 --- a/tests/mir-opt/gvn.rs +++ b/tests/mir-opt/gvn.rs @@ -783,6 +783,39 @@ fn non_freeze(x: T) { } } +// Check that we can const-prop into `from_raw_parts` +fn slice_const_length(x: &[i32]) -> *const [i32] { + // CHECK-LABEL: fn slice_const_length( + // CHECK: _0 = *const [i32] from ({{_[0-9]+}}, const 123_usize); + let ptr = x.as_ptr(); + let len = 123; + std::intrinsics::aggregate_raw_ptr(ptr, len) +} + +fn meta_of_ref_to_slice(x: *const i32) -> usize { + // CHECK-LABEL: fn meta_of_ref_to_slice + // CHECK: _0 = const 1_usize + let ptr: *const [i32] = std::intrinsics::aggregate_raw_ptr(x, 1); + std::intrinsics::ptr_metadata(ptr) +} + +fn slice_from_raw_parts_as_ptr(x: *const u16, n: usize) -> (*const u16, *const f32) { + // CHECK-LABEL: fn slice_from_raw_parts_as_ptr + // CHECK: _8 = _1 as *const f32 (PtrToPtr); + // CHECK: _0 = (_1, move _8); + let ptr: *const [u16] = std::intrinsics::aggregate_raw_ptr(x, n); + (ptr as *const u16, ptr as *const f32) +} + +fn casts_before_aggregate_raw_ptr(x: *const u32) -> *const [u8] { + // CHECK-LABEL: fn casts_before_aggregate_raw_ptr + // CHECK: _0 = *const [u8] from (_1, const 4_usize); + let x = x as *const [u8; 4]; + let x = x as *const u8; + let x = x as *const (); + std::intrinsics::aggregate_raw_ptr(x, 4) +} + fn main() { subexpression_elimination(2, 4, 5); wrap_unwrap(5); @@ -807,6 +840,9 @@ fn main() { wide_ptr_integer(); borrowed(5); non_freeze(5); + slice_const_length(&[1]); + meta_of_ref_to_slice(&42); + slice_from_raw_parts_as_ptr(&123, 456); } #[inline(never)] @@ -840,3 +876,7 @@ fn identity(x: T) -> T { // EMIT_MIR gvn.wide_ptr_integer.GVN.diff // EMIT_MIR gvn.borrowed.GVN.diff // EMIT_MIR gvn.non_freeze.GVN.diff +// EMIT_MIR gvn.slice_const_length.GVN.diff +// EMIT_MIR gvn.meta_of_ref_to_slice.GVN.diff +// EMIT_MIR gvn.slice_from_raw_parts_as_ptr.GVN.diff +// EMIT_MIR gvn.casts_before_aggregate_raw_ptr.GVN.diff diff --git a/tests/mir-opt/gvn.slice_const_length.GVN.panic-abort.diff b/tests/mir-opt/gvn.slice_const_length.GVN.panic-abort.diff new file mode 100644 index 0000000000000..fd5fa035d816c --- /dev/null +++ b/tests/mir-opt/gvn.slice_const_length.GVN.panic-abort.diff @@ -0,0 +1,48 @@ +- // MIR for `slice_const_length` before GVN ++ // MIR for `slice_const_length` after GVN + + fn slice_const_length(_1: &[i32]) -> *const [i32] { + debug x => _1; + let mut _0: *const [i32]; + let _2: *const i32; + let mut _3: &[i32]; + let mut _5: *const i32; + let mut _6: usize; + scope 1 { + debug ptr => _2; + let _4: usize; + scope 2 { + debug len => _4; + } + } + + bb0: { +- StorageLive(_2); ++ nop; + StorageLive(_3); + _3 = &(*_1); + _2 = core::slice::::as_ptr(move _3) -> [return: bb1, unwind unreachable]; + } + + bb1: { + StorageDead(_3); +- StorageLive(_4); ++ nop; + _4 = const 123_usize; + StorageLive(_5); + _5 = _2; + StorageLive(_6); +- _6 = _4; +- _0 = *const [i32] from (move _5, move _6); ++ _6 = const 123_usize; ++ _0 = *const [i32] from (_2, const 123_usize); + StorageDead(_6); + StorageDead(_5); +- StorageDead(_4); +- StorageDead(_2); ++ nop; ++ nop; + return; + } + } + diff --git a/tests/mir-opt/gvn.slice_const_length.GVN.panic-unwind.diff b/tests/mir-opt/gvn.slice_const_length.GVN.panic-unwind.diff new file mode 100644 index 0000000000000..98945cf9724db --- /dev/null +++ b/tests/mir-opt/gvn.slice_const_length.GVN.panic-unwind.diff @@ -0,0 +1,48 @@ +- // MIR for `slice_const_length` before GVN ++ // MIR for `slice_const_length` after GVN + + fn slice_const_length(_1: &[i32]) -> *const [i32] { + debug x => _1; + let mut _0: *const [i32]; + let _2: *const i32; + let mut _3: &[i32]; + let mut _5: *const i32; + let mut _6: usize; + scope 1 { + debug ptr => _2; + let _4: usize; + scope 2 { + debug len => _4; + } + } + + bb0: { +- StorageLive(_2); ++ nop; + StorageLive(_3); + _3 = &(*_1); + _2 = core::slice::::as_ptr(move _3) -> [return: bb1, unwind continue]; + } + + bb1: { + StorageDead(_3); +- StorageLive(_4); ++ nop; + _4 = const 123_usize; + StorageLive(_5); + _5 = _2; + StorageLive(_6); +- _6 = _4; +- _0 = *const [i32] from (move _5, move _6); ++ _6 = const 123_usize; ++ _0 = *const [i32] from (_2, const 123_usize); + StorageDead(_6); + StorageDead(_5); +- StorageDead(_4); +- StorageDead(_2); ++ nop; ++ nop; + return; + } + } + diff --git a/tests/mir-opt/gvn.slice_from_raw_parts_as_ptr.GVN.panic-abort.diff b/tests/mir-opt/gvn.slice_from_raw_parts_as_ptr.GVN.panic-abort.diff new file mode 100644 index 0000000000000..75bcd2a8d72cf --- /dev/null +++ b/tests/mir-opt/gvn.slice_from_raw_parts_as_ptr.GVN.panic-abort.diff @@ -0,0 +1,51 @@ +- // MIR for `slice_from_raw_parts_as_ptr` before GVN ++ // MIR for `slice_from_raw_parts_as_ptr` after GVN + + fn slice_from_raw_parts_as_ptr(_1: *const u16, _2: usize) -> (*const u16, *const f32) { + debug x => _1; + debug n => _2; + let mut _0: (*const u16, *const f32); + let _3: *const [u16]; + let mut _4: *const u16; + let mut _5: usize; + let mut _6: *const u16; + let mut _7: *const [u16]; + let mut _8: *const f32; + let mut _9: *const [u16]; + scope 1 { + debug ptr => _3; + } + + bb0: { +- StorageLive(_3); ++ nop; + StorageLive(_4); + _4 = _1; + StorageLive(_5); + _5 = _2; +- _3 = *const [u16] from (move _4, move _5); ++ _3 = *const [u16] from (_1, _2); + StorageDead(_5); + StorageDead(_4); + StorageLive(_6); + StorageLive(_7); + _7 = _3; +- _6 = move _7 as *const u16 (PtrToPtr); ++ _6 = _1; + StorageDead(_7); + StorageLive(_8); + StorageLive(_9); + _9 = _3; +- _8 = move _9 as *const f32 (PtrToPtr); ++ _8 = _1 as *const f32 (PtrToPtr); + StorageDead(_9); +- _0 = (move _6, move _8); ++ _0 = (_1, move _8); + StorageDead(_8); + StorageDead(_6); +- StorageDead(_3); ++ nop; + return; + } + } + diff --git a/tests/mir-opt/gvn.slice_from_raw_parts_as_ptr.GVN.panic-unwind.diff b/tests/mir-opt/gvn.slice_from_raw_parts_as_ptr.GVN.panic-unwind.diff new file mode 100644 index 0000000000000..75bcd2a8d72cf --- /dev/null +++ b/tests/mir-opt/gvn.slice_from_raw_parts_as_ptr.GVN.panic-unwind.diff @@ -0,0 +1,51 @@ +- // MIR for `slice_from_raw_parts_as_ptr` before GVN ++ // MIR for `slice_from_raw_parts_as_ptr` after GVN + + fn slice_from_raw_parts_as_ptr(_1: *const u16, _2: usize) -> (*const u16, *const f32) { + debug x => _1; + debug n => _2; + let mut _0: (*const u16, *const f32); + let _3: *const [u16]; + let mut _4: *const u16; + let mut _5: usize; + let mut _6: *const u16; + let mut _7: *const [u16]; + let mut _8: *const f32; + let mut _9: *const [u16]; + scope 1 { + debug ptr => _3; + } + + bb0: { +- StorageLive(_3); ++ nop; + StorageLive(_4); + _4 = _1; + StorageLive(_5); + _5 = _2; +- _3 = *const [u16] from (move _4, move _5); ++ _3 = *const [u16] from (_1, _2); + StorageDead(_5); + StorageDead(_4); + StorageLive(_6); + StorageLive(_7); + _7 = _3; +- _6 = move _7 as *const u16 (PtrToPtr); ++ _6 = _1; + StorageDead(_7); + StorageLive(_8); + StorageLive(_9); + _9 = _3; +- _8 = move _9 as *const f32 (PtrToPtr); ++ _8 = _1 as *const f32 (PtrToPtr); + StorageDead(_9); +- _0 = (move _6, move _8); ++ _0 = (_1, move _8); + StorageDead(_8); + StorageDead(_6); +- StorageDead(_3); ++ nop; + return; + } + } + diff --git a/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_fat.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_fat.PreCodegen.after.panic-abort.mir index 2c6d93e10c11e..2f6139712ffc1 100644 --- a/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_fat.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_fat.PreCodegen.after.panic-abort.mir @@ -12,8 +12,7 @@ fn demo_byte_add_fat(_1: *const [u32], _2: usize) -> *const [u32] { scope 3 (inlined std::ptr::const_ptr::::add) { } scope 4 (inlined std::ptr::const_ptr::::with_metadata_of::<[u32]>) { - let mut _5: *const (); - let mut _6: usize; + let mut _5: usize; scope 5 (inlined std::ptr::metadata::<[u32]>) { } scope 6 (inlined std::ptr::from_raw_parts::<[u32], ()>) { @@ -28,11 +27,8 @@ fn demo_byte_add_fat(_1: *const [u32], _2: usize) -> *const [u32] { _4 = Offset(_3, _2); StorageDead(_3); StorageLive(_5); - _5 = _4 as *const () (PtrToPtr); - StorageLive(_6); - _6 = PtrMetadata(_1); - _0 = *const [u32] from (_5, _6); - StorageDead(_6); + _5 = PtrMetadata(_1); + _0 = *const [u32] from (_4, _5); StorageDead(_5); StorageDead(_4); return; diff --git a/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_fat.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_fat.PreCodegen.after.panic-unwind.mir index 2c6d93e10c11e..2f6139712ffc1 100644 --- a/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_fat.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_fat.PreCodegen.after.panic-unwind.mir @@ -12,8 +12,7 @@ fn demo_byte_add_fat(_1: *const [u32], _2: usize) -> *const [u32] { scope 3 (inlined std::ptr::const_ptr::::add) { } scope 4 (inlined std::ptr::const_ptr::::with_metadata_of::<[u32]>) { - let mut _5: *const (); - let mut _6: usize; + let mut _5: usize; scope 5 (inlined std::ptr::metadata::<[u32]>) { } scope 6 (inlined std::ptr::from_raw_parts::<[u32], ()>) { @@ -28,11 +27,8 @@ fn demo_byte_add_fat(_1: *const [u32], _2: usize) -> *const [u32] { _4 = Offset(_3, _2); StorageDead(_3); StorageLive(_5); - _5 = _4 as *const () (PtrToPtr); - StorageLive(_6); - _6 = PtrMetadata(_1); - _0 = *const [u32] from (_5, _6); - StorageDead(_6); + _5 = PtrMetadata(_1); + _0 = *const [u32] from (_4, _5); StorageDead(_5); StorageDead(_4); return; diff --git a/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_thin.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_thin.PreCodegen.after.panic-abort.mir index 04fb6b838f024..8d47e63eff2ac 100644 --- a/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_thin.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_thin.PreCodegen.after.panic-abort.mir @@ -20,11 +20,13 @@ fn demo_byte_add_thin(_1: *const u32, _2: usize) -> *const u32 { } bb0: { + StorageLive(_4); StorageLive(_3); _3 = _1 as *const u8 (PtrToPtr); _4 = Offset(_3, _2); StorageDead(_3); _0 = _4 as *const u32 (PtrToPtr); + StorageDead(_4); return; } } diff --git a/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_thin.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_thin.PreCodegen.after.panic-unwind.mir index 04fb6b838f024..8d47e63eff2ac 100644 --- a/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_thin.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_thin.PreCodegen.after.panic-unwind.mir @@ -20,11 +20,13 @@ fn demo_byte_add_thin(_1: *const u32, _2: usize) -> *const u32 { } bb0: { + StorageLive(_4); StorageLive(_3); _3 = _1 as *const u8 (PtrToPtr); _4 = Offset(_3, _2); StorageDead(_3); _0 = _4 as *const u32 (PtrToPtr); + StorageDead(_4); return; } } From fbe5015480f44cad99f9005a48f9699a3f633a96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Sun, 9 Jun 2024 11:52:16 +0200 Subject: [PATCH 06/10] Fix documentation for `impl_common_helpers` in `run-make-support` --- src/tools/run-make-support/src/lib.rs | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/src/tools/run-make-support/src/lib.rs b/src/tools/run-make-support/src/lib.rs index b17f217c133b8..cea1313e29d75 100644 --- a/src/tools/run-make-support/src/lib.rs +++ b/src/tools/run-make-support/src/lib.rs @@ -318,38 +318,30 @@ pub fn run_in_tmpdir(callback: F) { } /// Implement common helpers for command wrappers. This assumes that the command wrapper is a struct -/// containing a `cmd: Command` field and a `output` function. The provided helpers are: +/// containing a `cmd: Command` field. The provided helpers are: /// /// 1. Generic argument acceptors: `arg` and `args` (delegated to [`Command`]). These are intended /// to be *fallback* argument acceptors, when specific helpers don't make sense. Prefer to add /// new specific helper methods over relying on these generic argument providers. /// 2. Environment manipulation methods: `env`, `env_remove` and `env_clear`: these delegate to /// methods of the same name on [`Command`]. -/// 3. Output and execution: `output`, `run` and `run_fail` are provided. `output` waits for the -/// command to finish running and returns the process's [`Output`]. `run` and `run_fail` are -/// higher-level convenience methods which waits for the command to finish running and assert -/// that the command successfully ran or failed as expected. Prefer `run` and `run_fail` when -/// possible. +/// 3. Output and execution: `run` and `run_fail` are provided. These are +/// higher-level convenience methods which wait for the command to finish running and assert +/// that the command successfully ran or failed as expected. They return +/// [`CompletedProcess`], which can be used to assert the stdout/stderr/exit code of the executed +/// process. /// /// Example usage: /// /// ```ignore (illustrative) /// struct CommandWrapper { cmd: Command } // <- required `cmd` field /// -/// impl CommandWrapper { -/// /// Get the [`Output`][::std::process::Output] of the finished process. -/// pub fn command_output(&mut self) -> Output { /* ... */ } // <- required `command_output()` method -/// } -/// /// crate::impl_common_helpers!(CommandWrapper); /// /// impl CommandWrapper { /// // ... additional specific helper methods /// } /// ``` -/// -/// [`Command`]: ::std::process::Command -/// [`Output`]: ::std::process::Output macro_rules! impl_common_helpers { ($wrapper: ident) => { impl $wrapper { From 4977608f72e97f498d6be312860753d0d131314b Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Thu, 23 May 2024 18:13:50 -0700 Subject: [PATCH 07/10] Add release notes for 1.79.0 --- RELEASES.md | 128 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) diff --git a/RELEASES.md b/RELEASES.md index 3080f03c7210b..c1311ab14c534 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,131 @@ +Version 1.79.0 (2024-06-13) +========================== + + + +Language +-------- +- [Stabilize inline `const {}` expressions.](https://github.com/rust-lang/rust/pull/104087/) +- [Prevent opaque types being instantiated twice with different regions within the same function.](https://github.com/rust-lang/rust/pull/116935/) +- [Stabilize WebAssembly target features that are in phase 4 and 5.](https://github.com/rust-lang/rust/pull/117457/) +- [Add the `redundant_lifetimes` lint to detect lifetimes which are semantically redundant.](https://github.com/rust-lang/rust/pull/118391/) +- [Stabilize the `unnameable_types` lint for public types that can't be named.](https://github.com/rust-lang/rust/pull/120144/) +- [Enable debuginfo in macros, and stabilize `-C collapse-macro-debuginfo` and `#[collapse_debuginfo]`.](https://github.com/rust-lang/rust/pull/120845/) +- [Propagate temporary lifetime extension into `if` and `match` expressions.](https://github.com/rust-lang/rust/pull/121346/) +- [Restrict promotion of `const fn` calls.](https://github.com/rust-lang/rust/pull/121557/) +- [Warn against refining impls of crate-private traits with `refining_impl_trait` lint.](https://github.com/rust-lang/rust/pull/121720/) +- [Stabilize associated type bounds (RFC 2289).](https://github.com/rust-lang/rust/pull/122055/) +- [Stabilize importing `main` from other modules or crates.](https://github.com/rust-lang/rust/pull/122060/) +- [Check return types of function types for well-formedness](https://github.com/rust-lang/rust/pull/115538) +- [Rework `impl Trait` lifetime inference](https://github.com/rust-lang/rust/pull/116891/) +- [Change inductive trait solver cycles to be ambiguous](https://github.com/rust-lang/rust/pull/122791) + + + +Compiler +-------- +- [Define `-C strip` to only affect binaries, not artifacts like `.pdb`.](https://github.com/rust-lang/rust/pull/115120/) +- [Stabilize `-Crelro-level` for controlling runtime link hardening.](https://github.com/rust-lang/rust/pull/121694/) +- [Stabilize checking of `cfg` names and values at compile-time with `--check-cfg`.](https://github.com/rust-lang/rust/pull/123501/) + *Note that this only stabilizes the compiler part, the Cargo part is still unstable in this release.* +- [Add `aarch64-apple-visionos` and `aarch64-apple-visionos-sim` tier 3 targets.](https://github.com/rust-lang/rust/pull/121419/) +- [Add `riscv32ima-unknown-none-elf` tier 3 target.](https://github.com/rust-lang/rust/pull/122696/) +- [Promote several Windows targets to tier 2](https://github.com/rust-lang/rust/pull/121712): `aarch64-pc-windows-gnullvm`, `i686-pc-windows-gnullvm`, and `x86_64-pc-windows-gnullvm`. + +Refer to Rust's [platform support page][platform-support-doc] +for more information on Rust's tiered platform support. + + + +Libraries +--------- + +- [Implement `FromIterator` for `(impl Default + Extend, impl Default + Extend)`.](https://github.com/rust-lang/rust/pull/107462/) +- [Implement `{Div,Rem}Assign>` on `X`.](https://github.com/rust-lang/rust/pull/121952/) +- [Document overrides of `clone_from()` in core/std.](https://github.com/rust-lang/rust/pull/122201/) +- [Link MSVC default lib in core.](https://github.com/rust-lang/rust/pull/122268/) +- [Caution against using `transmute` between pointers and integers.](https://github.com/rust-lang/rust/pull/122379/) +- [Enable frame pointers for the standard library.](https://github.com/rust-lang/rust/pull/122646/) + + + +Stabilized APIs +--------------- + +- [`{integer}::unchecked_add`](https://doc.rust-lang.org/stable/core/primitive.i32.html#method.unchecked_add) +- [`{integer}::unchecked_mul`](https://doc.rust-lang.org/stable/core/primitive.i32.html#method.unchecked_mul) +- [`{integer}::unchecked_sub`](https://doc.rust-lang.org/stable/core/primitive.i32.html#method.unchecked_sub) +- [`<[T]>::split_at_unchecked`](https://doc.rust-lang.org/stable/core/primitive.slice.html#method.split_at_unchecked) +- [`<[T]>::split_at_mut_unchecked`](https://doc.rust-lang.org/stable/core/primitive.slice.html#method.split_at_mut_unchecked) +- [`<[u8]>::utf8_chunks`](https://doc.rust-lang.org/stable/core/primitive.slice.html#method.utf8_chunks) +- [`str::Utf8Chunks`](https://doc.rust-lang.org/stable/core/str/struct.Utf8Chunks.html) +- [`str::Utf8Chunk`](https://doc.rust-lang.org/stable/core/str/struct.Utf8Chunk.html) +- [`<*const T>::is_aligned`](https://doc.rust-lang.org/stable/core/primitive.pointer.html#method.is_aligned) +- [`<*mut T>::is_aligned`](https://doc.rust-lang.org/stable/core/primitive.pointer.html#method.is_aligned-1) +- [`NonNull::is_aligned`](https://doc.rust-lang.org/stable/core/ptr/struct.NonNull.html#method.is_aligned) +- [`<*const [T]>::len`](https://doc.rust-lang.org/stable/core/primitive.pointer.html#method.len) +- [`<*mut [T]>::len`](https://doc.rust-lang.org/stable/core/primitive.pointer.html#method.len-1) +- [`<*const [T]>::is_empty`](https://doc.rust-lang.org/stable/core/primitive.pointer.html#method.is_empty) +- [`<*mut [T]>::is_empty`](https://doc.rust-lang.org/stable/core/primitive.pointer.html#method.is_empty-1) +- [`NonNull::<[T]>::is_empty`](https://doc.rust-lang.org/stable/core/ptr/struct.NonNull.html#method.is_empty) +- [`CStr::count_bytes`](https://doc.rust-lang.org/stable/core/ffi/c_str/struct.CStr.html#method.count_bytes) +- [`io::Error::downcast`](https://doc.rust-lang.org/stable/std/io/struct.Error.html#method.downcast) +- [`num::NonZero`](https://doc.rust-lang.org/stable/core/num/struct.NonZero.html) +- [`path::absolute`](https://doc.rust-lang.org/stable/std/path/fn.absolute.html) +- [`proc_macro::Literal::byte_character`](https://doc.rust-lang.org/stable/proc_macro/struct.Literal.html#method.byte_character) +- [`proc_macro::Literal::c_string`](https://doc.rust-lang.org/stable/proc_macro/struct.Literal.html#method.c_string) + +These APIs are now stable in const contexts: + +- [`Atomic*::into_inner`](https://doc.rust-lang.org/stable/core/sync/atomic/struct.AtomicUsize.html#method.into_inner) +- [`io::Cursor::new`](https://doc.rust-lang.org/stable/std/io/struct.Cursor.html#method.new) +- [`io::Cursor::get_ref`](https://doc.rust-lang.org/stable/std/io/struct.Cursor.html#method.get_ref) +- [`io::Cursor::position`](https://doc.rust-lang.org/stable/std/io/struct.Cursor.html#method.position) +- [`io::empty`](https://doc.rust-lang.org/stable/std/io/fn.empty.html) +- [`io::repeat`](https://doc.rust-lang.org/stable/std/io/fn.repeat.html) +- [`io::sink`](https://doc.rust-lang.org/stable/std/io/fn.sink.html) +- [`panic::Location::caller`](https://doc.rust-lang.org/stable/std/panic/struct.Location.html#method.caller) +- [`panic::Location::file`](https://doc.rust-lang.org/stable/std/panic/struct.Location.html#method.file) +- [`panic::Location::line`](https://doc.rust-lang.org/stable/std/panic/struct.Location.html#method.line) +- [`panic::Location::column`](https://doc.rust-lang.org/stable/std/panic/struct.Location.html#method.column) + + + +Cargo +----- + +- [Prevent dashes in `lib.name`, always normalizing to `_`.](https://github.com/rust-lang/cargo/pull/12783/) +- [Stabilize MSRV-aware version requirement selection in `cargo add`.](https://github.com/rust-lang/cargo/pull/13608/) +- [Switch to using `gitoxide` by default for listing files.](https://github.com/rust-lang/cargo/pull/13696/) +- [Error on `[project]` in Edition 2024; `cargo fix --edition` will change it to `[package]`.](https://github.com/rust-lang/cargo/pull/13747/) + + + +Rustdoc +----- + +- [Always display stability version even if it's the same as the containing item.](https://github.com/rust-lang/rust/pull/118441/) +- [Show a single search result for items with multiple paths.](https://github.com/rust-lang/rust/pull/119912/) +- [Support typing `/` in docs to begin a search.](https://github.com/rust-lang/rust/pull/123355/) + + + +Misc +---- + + + +Compatibility Notes +------------------- + +- [Update the minimum external LLVM to 17.](https://github.com/rust-lang/rust/pull/122649/) +- [`RustcEncodable` and `RustcDecodable` are soft-destabilized, to be removed + from the prelude in next edition.](https://github.com/rust-lang/rust/pull/116016/) +- [The `wasm_c_abi` future-incompatibility lint will warn about use of the + non-spec-compliant C ABI.](https://github.com/rust-lang/rust/pull/117918/) + Use `wasm-bindgen v0.2.88` to generate forward-compatible bindings. +- [Check return types of function types for well-formedness](https://github.com/rust-lang/rust/pull/115538) + Version 1.78.0 (2024-05-02) ========================== From e24be071e3bef8dfb84b082f1478e9d33a108901 Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Thu, 23 May 2024 20:36:39 +0000 Subject: [PATCH 08/10] feat: vendor crates required by opt-dist to collect profiles These are the default package set required by opt-dist to correctly work, hence for people wanting to build a production grade of rustc in a sandboxed / air-gapped environment, these need to be vendored. The size of `rustc-src-nightly.tar.xz` before and after this change: * Before: 298M * After: 323M (+8%) These crates are the default set of packages required by opt-dist to correctly work, hence for people wanting to build a production grade of rustc in an sandboxed / air-gapped environment, these need to be vendored. The size of `rustc-src-nightly.tar.xz` before and after this change: * Before: 298M * After: 323M (+8%) Size change might or might not be a concern. See the previous discussion: https://github.com/rust-lang/rust/pull/125166#issuecomment-2113626468 Previous efforts on making: * https://github.com/rust-lang/rust/pull/125125 * https://github.com/rust-lang/rust/pull/125166 --- Note that extra works still need to be done to make it fully vendored. * The current pinned rustc-perf uses `tempfile::Tempdir` as the working directory when collecting profiles from some of these packages. This "tmp" working directory usage make it impossible for Cargo to pick up the correct vendor sources setting in `.cargo/config.toml` bundled in the rustc-src tarball. [^1] * opt-dist verifies the final built rustc against a subset of rustc test suite. However it rolls out its own `config.toml` without setting `vendor = true`, and that results in `./vendor/` directory removed. [^2] [^1]: https://github.com/rust-lang/rustc-perf/blob/4f313add609f43e928e98132358e8426ed3969ae/collector/src/compile/benchmark/mod.rs#L164-L173 [^2]: https://github.com/rust-lang/rust/blob/606afbb617a2949a4e35c4b0258ff94c980b9451/src/tools/opt-dist/src/tests.rs#L62-L77 --- src/bootstrap/src/core/build_steps/dist.rs | 15 ++++++++++++++ src/tools/build_helper/src/lib.rs | 23 ++++++++++++++++++++++ src/tools/opt-dist/src/training.rs | 22 +-------------------- 3 files changed, 39 insertions(+), 21 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index 91039d0c8dcc1..60b1ff3e4413e 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -1041,6 +1041,21 @@ impl Step for PlainSourceTarball { .env("RUSTC_BOOTSTRAP", "1") .current_dir(plain_dst_src); + // Vendor packages that are required by opt-dist to collect PGO profiles. + let pkgs_for_pgo_training = build_helper::LLVM_PGO_CRATES + .iter() + .chain(build_helper::RUSTC_PGO_CRATES) + .map(|pkg| { + let mut manifest_path = + builder.src.join("./src/tools/rustc-perf/collector/compile-benchmarks"); + manifest_path.push(pkg); + manifest_path.push("Cargo.toml"); + manifest_path + }); + for manifest_path in pkgs_for_pgo_training { + cmd.arg("--sync").arg(manifest_path); + } + let config = if !builder.config.dry_run() { t!(String::from_utf8(t!(cmd.output()).stdout)) } else { diff --git a/src/tools/build_helper/src/lib.rs b/src/tools/build_helper/src/lib.rs index d831cfa0d79a6..15807d1c0d8f8 100644 --- a/src/tools/build_helper/src/lib.rs +++ b/src/tools/build_helper/src/lib.rs @@ -5,3 +5,26 @@ pub mod git; pub mod metrics; pub mod stage0_parser; pub mod util; + +/// The default set of crates for opt-dist to collect LLVM profiles. +pub const LLVM_PGO_CRATES: &[&str] = &[ + "syn-1.0.89", + "cargo-0.60.0", + "serde-1.0.136", + "ripgrep-13.0.0", + "regex-1.5.5", + "clap-3.1.6", + "hyper-0.14.18", +]; + +/// The default set of crates for opt-dist to collect rustc profiles. +pub const RUSTC_PGO_CRATES: &[&str] = &[ + "externs", + "ctfe-stress-5", + "cargo-0.60.0", + "token-stream-stress", + "match-stress", + "tuple-stress", + "diesel-1.4.8", + "bitmaps-3.1.0", +]; diff --git a/src/tools/opt-dist/src/training.rs b/src/tools/opt-dist/src/training.rs index 09263dc645e2d..89f4d8957c8bf 100644 --- a/src/tools/opt-dist/src/training.rs +++ b/src/tools/opt-dist/src/training.rs @@ -3,30 +3,10 @@ use crate::exec::{cmd, CmdBuilder}; use crate::utils::io::{count_files, delete_directory}; use crate::utils::with_log_group; use anyhow::Context; +use build_helper::{LLVM_PGO_CRATES, RUSTC_PGO_CRATES}; use camino::{Utf8Path, Utf8PathBuf}; use humansize::BINARY; -const LLVM_PGO_CRATES: &[&str] = &[ - "syn-1.0.89", - "cargo-0.60.0", - "serde-1.0.136", - "ripgrep-13.0.0", - "regex-1.5.5", - "clap-3.1.6", - "hyper-0.14.18", -]; - -const RUSTC_PGO_CRATES: &[&str] = &[ - "externs", - "ctfe-stress-5", - "cargo-0.60.0", - "token-stream-stress", - "match-stress", - "tuple-stress", - "diesel-1.4.8", - "bitmaps-3.1.0", -]; - fn init_compiler_benchmarks( env: &Environment, profiles: &[&str], From 377870386f99ee811e8945fade761a3ea8944db1 Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Sat, 25 May 2024 00:06:00 -0400 Subject: [PATCH 09/10] tidy: add license exceptions for rustc-perf `ring` is included because it is an optional dependency of `hyper`, which is a training data in rustc-pref for optimized build. The license of it is generally `ISC AND MIT AND OpenSSL`, though the `package.license` field is not set. See https://github.com/briansmith/ring/issues/902 `git+https://github.com/rust-lang/team` git source is from `rust_team_data`, which is used by `site` in src/tools/rustc-perf. This doesn't really a part of the compiler, so doesn't really affect the bootstrapping process. See https://github.com/rust-lang/rustc-perf/pull/1914. --- src/tools/tidy/src/deps.rs | 36 +++++++++++++++++++++++++++++++++++ src/tools/tidy/src/extdeps.rs | 6 +++++- 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index efcd2a181ffa2..7b93b09dda24b 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -67,6 +67,7 @@ pub(crate) const WORKSPACES: &[(&str, ExceptionList, Option<(&[&str], &[&str])>) //("src/tools/miri/test-cargo-miri", &[], None), // FIXME uncomment once all deps are vendored //("src/tools/miri/test_dependencies", &[], None), // FIXME uncomment once all deps are vendored ("src/tools/rust-analyzer", EXCEPTIONS_RUST_ANALYZER, None), + ("src/tools/rustc-perf", EXCEPTIONS_RUSTC_PERF, None), ("src/tools/x", &[], None), // tidy-alphabetical-end ]; @@ -142,6 +143,22 @@ const EXCEPTIONS_RUST_ANALYZER: ExceptionList = &[ // tidy-alphabetical-end ]; +const EXCEPTIONS_RUSTC_PERF: ExceptionList = &[ + // tidy-alphabetical-start + ("alloc-no-stdlib", "BSD-3-Clause"), + ("alloc-stdlib", "BSD-3-Clause"), + ("brotli", "BSD-3-Clause/MIT"), + ("brotli-decompressor", "BSD-3-Clause/MIT"), + ("encoding_rs", "(Apache-2.0 OR MIT) AND BSD-3-Clause"), + ("inferno", "CDDL-1.0"), + ("instant", "BSD-3-Clause"), + ("ring", NON_STANDARD_LICENSE), // see EXCEPTIONS_NON_STANDARD_LICENSE_DEPS for more. + ("ryu", "Apache-2.0 OR BSL-1.0"), + ("snap", "BSD-3-Clause"), + ("subtle", "BSD-3-Clause"), + // tidy-alphabetical-end +]; + const EXCEPTIONS_CRANELIFT: ExceptionList = &[ // tidy-alphabetical-start ("cranelift-bforest", "Apache-2.0 WITH LLVM-exception"), @@ -178,6 +195,20 @@ const EXCEPTIONS_UEFI_QEMU_TEST: ExceptionList = &[ ("r-efi", "MIT OR Apache-2.0 OR LGPL-2.1-or-later"), // LGPL is not acceptible, but we use it under MIT OR Apache-2.0 ]; +/// Placeholder for non-standard license file. +const NON_STANDARD_LICENSE: &str = "NON_STANDARD_LICENSE"; + +/// These dependencies have non-standard licenses but are genenrally permitted. +const EXCEPTIONS_NON_STANDARD_LICENSE_DEPS: &[&str] = &[ + // `ring` is included because it is an optional dependency of `hyper`, + // which is a training data in rustc-perf for optimized build. + // The license of it is generally `ISC AND MIT AND OpenSSL`, + // though the `package.license` field is not set. + // + // See https://github.com/briansmith/ring/issues/902 + "ring", +]; + /// These are the root crates that are part of the runtime. The licenses for /// these and all their dependencies *must not* be in the exception list. const RUNTIME_CRATES: &[&str] = &["std", "core", "alloc", "test", "panic_abort", "panic_unwind"]; @@ -610,6 +641,11 @@ fn check_license_exceptions(metadata: &Metadata, exceptions: &[(&str, &str)], ba for pkg in metadata.packages.iter().filter(|p| p.name == *name) { match &pkg.license { None => { + if *license == NON_STANDARD_LICENSE + && EXCEPTIONS_NON_STANDARD_LICENSE_DEPS.contains(&pkg.name.as_str()) + { + continue; + } tidy_error!( bad, "dependency exception `{}` does not declare a license expression", diff --git a/src/tools/tidy/src/extdeps.rs b/src/tools/tidy/src/extdeps.rs index ff71ca537256f..2118de5f20411 100644 --- a/src/tools/tidy/src/extdeps.rs +++ b/src/tools/tidy/src/extdeps.rs @@ -4,7 +4,11 @@ use std::fs; use std::path::Path; /// List of allowed sources for packages. -const ALLOWED_SOURCES: &[&str] = &["\"registry+https://github.com/rust-lang/crates.io-index\""]; +const ALLOWED_SOURCES: &[&str] = &[ + r#""registry+https://github.com/rust-lang/crates.io-index""#, + // This is `rust_team_data` used by `site` in src/tools/rustc-perf, + r#""git+https://github.com/rust-lang/team#a5260e76d3aa894c64c56e6ddc8545b9a98043ec""#, +]; /// Checks for external package sources. `root` is the path to the directory that contains the /// workspace `Cargo.toml`. From f2d707dabde9c89dfe0b1c728dc0c686b8732d6a Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Sat, 8 Jun 2024 15:18:23 -0700 Subject: [PATCH 10/10] rustdoc-search: use lowercase, non-normalized name for type search The type name ID map has underscores in its names, so the query element should have them, too. --- src/librustdoc/html/static/js/search.js | 12 +++-- tests/rustdoc-js/underscoredtype.js | 62 +++++++++++++++++++++++++ tests/rustdoc-js/underscoredtype.rs | 6 +++ 3 files changed, 76 insertions(+), 4 deletions(-) create mode 100644 tests/rustdoc-js/underscoredtype.js create mode 100644 tests/rustdoc-js/underscoredtype.rs diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 8ac4b53673f4c..a0ab262bf0b02 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -2399,15 +2399,19 @@ function initSearch(rawSearchIndex) { * @param {boolean} isAssocType */ function convertNameToId(elem, isAssocType) { - if (typeNameIdMap.has(elem.normalizedPathLast) && - (isAssocType || !typeNameIdMap.get(elem.normalizedPathLast).assocOnly)) { - elem.id = typeNameIdMap.get(elem.normalizedPathLast).id; + const loweredName = elem.pathLast.toLowerCase(); + if (typeNameIdMap.has(loweredName) && + (isAssocType || !typeNameIdMap.get(loweredName).assocOnly)) { + elem.id = typeNameIdMap.get(loweredName).id; } else if (!parsedQuery.literalSearch) { let match = null; let matchDist = maxEditDistance + 1; let matchName = ""; for (const [name, {id, assocOnly}] of typeNameIdMap) { - const dist = editDistance(name, elem.normalizedPathLast, maxEditDistance); + const dist = Math.min( + editDistance(name, loweredName, maxEditDistance), + editDistance(name, elem.normalizedPathLast, maxEditDistance), + ); if (dist <= matchDist && dist <= maxEditDistance && (isAssocType || !assocOnly)) { if (dist === matchDist && matchName > name) { diff --git a/tests/rustdoc-js/underscoredtype.js b/tests/rustdoc-js/underscoredtype.js new file mode 100644 index 0000000000000..06e3b07b0960c --- /dev/null +++ b/tests/rustdoc-js/underscoredtype.js @@ -0,0 +1,62 @@ +const EXPECTED = [ + { + 'query': 'pid_t', + 'correction': null, + 'proposeCorrectionFrom': null, + 'proposeCorrectionTo': null, + 'others': [ + { 'path': 'underscoredtype::unix', 'name': 'pid_t' }, + ], + 'returned': [ + { 'path': 'underscoredtype::unix', 'name': 'set_pid' }, + ], + 'returned': [ + { 'path': 'underscoredtype::unix', 'name': 'get_pid' }, + ], + }, + { + 'query': 'pidt', + 'correction': 'pid_t', + 'proposeCorrectionFrom': null, + 'proposeCorrectionTo': null, + 'others': [ + { 'path': 'underscoredtype::unix', 'name': 'pid_t' }, + ], + 'returned': [ + { 'path': 'underscoredtype::unix', 'name': 'set_pid' }, + ], + 'returned': [ + { 'path': 'underscoredtype::unix', 'name': 'get_pid' }, + ], + }, + { + 'query': 'unix::pid_t', + 'correction': null, + 'proposeCorrectionFrom': null, + 'proposeCorrectionTo': null, + 'others': [ + { 'path': 'underscoredtype::unix', 'name': 'pid_t' }, + ], + 'returned': [ + { 'path': 'underscoredtype::unix', 'name': 'set_pid' }, + ], + 'returned': [ + { 'path': 'underscoredtype::unix', 'name': 'get_pid' }, + ], + }, + { + 'query': 'unix::pidt', + 'correction': 'pid_t', + 'proposeCorrectionFrom': null, + 'proposeCorrectionTo': null, + 'others': [ + { 'path': 'underscoredtype::unix', 'name': 'pid_t' }, + ], + 'returned': [ + { 'path': 'underscoredtype::unix', 'name': 'set_pid' }, + ], + 'returned': [ + { 'path': 'underscoredtype::unix', 'name': 'get_pid' }, + ], + }, +]; diff --git a/tests/rustdoc-js/underscoredtype.rs b/tests/rustdoc-js/underscoredtype.rs new file mode 100644 index 0000000000000..99eed42ad78ac --- /dev/null +++ b/tests/rustdoc-js/underscoredtype.rs @@ -0,0 +1,6 @@ +pub mod unix { + #[allow(non_camel_case_types)] + pub type pid_t = i32; + pub fn get_pid() -> pid_t { 0 } + pub fn set_pid(_: pid_t) {} +}