} pub fn metadata(&self) -> io::Result<FileAttr> { <del> Ok(FileAttr { attributes: self.data.dwFileAttributes, creation_time: self.data.ftCreationTime, last_access_time: self.data.ftLastAccessTime, last_write_time: self.data.ftLastWriteTime, file_size: ((self.data.nFileSizeHigh as u64) << 32) | (self.data.nFileSizeLow as u64), reparse_tag: if self.data.dwFileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 { // reserved unless this is a reparse point self.data.dwReserved0 } else { 0 }, volume_serial_number: None, number_of_links: None, file_index: None, }) </del> <ins> Ok(self.data.into()) </ins> } }


self.file_index } } <ins> impl From<c::WIN32_FIND_DATAW> for FileAttr { fn from(wfd: c::WIN32_FIND_DATAW) -> Self { FileAttr { attributes: wfd.dwFileAttributes, creation_time: wfd.ftCreationTime, last_access_time: wfd.ftLastAccessTime, last_write_time: wfd.ftLastWriteTime, file_size: ((wfd.nFileSizeHigh as u64) << 32) | (wfd.nFileSizeLow as u64), reparse_tag: if wfd.dwFileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 { // reserved unless this is a reparse point wfd.dwReserved0 } else { 0 }, volume_serial_number: None, number_of_links: None, file_index: None, } } } </ins> fn to_u64(ft: &c::FILETIME) -> u64 { (ft.dwLowDateTime as u64) | ((ft.dwHighDateTime as u64) << 32)


} pub fn stat(path: &Path) -> io::Result<FileAttr> { <del> let mut opts = OpenOptions::new(); // No read or write permissions are necessary opts.access_mode(0); // This flag is so we can open directories too opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS); let file = File::open(path, &opts)?; file.file_attr() </del> <ins> metadata(path, ReparsePoint::Follow) </ins> } pub fn lstat(path: &Path) -> io::Result<FileAttr> { <ins> metadata(path, ReparsePoint::Open) } #[repr(u32)] #[derive(Clone, Copy, PartialEq, Eq)] enum ReparsePoint { Follow = 0, Open = c::FILE_FLAG_OPEN_REPARSE_POINT, } impl ReparsePoint { fn as_flag(self) -> u32 { self as u32 } } fn metadata(path: &Path, reparse: ReparsePoint) -> io::Result<FileAttr> { </ins> let mut opts = OpenOptions::new(); // No read or write permissions are necessary opts.access_mode(0); <del> opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS | c::FILE_FLAG_OPEN_REPARSE_POINT); let file = File::open(path, &opts)?; file.file_attr() </del> <ins> opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS | reparse.as_flag()); // Attempt to open the file normally. // If that fails with `ERROR_SHARING_VIOLATION` then retry using `FindFirstFileW`. // If the fallback fails for any reason we return the original error. match File::open(path, &opts) { Ok(file) => file.file_attr(), Err(e) if e.raw_os_error() == Some(c::ERROR_SHARING_VIOLATION as _) => { // `ERROR_SHARING_VIOLATION` will almost never be returned. // Usually if a file is locked you can still read some metadata. // However, there are special system files, such as // `C:hiberfil.sys`, that are locked in a way that denies even that. unsafe { let path = maybe_verbatim(path)?; // `FindFirstFileW` accepts wildcard file names. // Fortunately wildcards are not valid file names and // `ERROR_SHARING_VIOLATION` means the file exists (but is locked) // therefore it's safe to assume the file name given does not // include wildcards. let mut wfd = mem::zeroed(); let handle = c::FindFirstFileW(path.as_ptr(), &mut wfd); if handle == c::INVALID_HANDLE_VALUE { // This can fail if the user does not have read access to the // directory. Err(e) } else { // We no longer need the find handle. c::FindClose(handle); // `FindFirstFileW` reads the cached file information from the // directory. The downside is that this metadata may be outdated. let attrs = FileAttr::from(wfd); if reparse == ReparsePoint::Follow && attrs.file_type().is_symlink() { Err(e) } else { Ok(attrs) } } } } Err(e) => Err(e), } </ins> } pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> {


false } <ins> /// Build a textual representation of an unevaluated constant expression. /// /// If the const expression is too complex, an underscore `_` is returned. /// For const arguments, it's `{ _ }` to be precise. /// This means that the output is not necessarily valid Rust code. /// /// Currently, only /// /// * literals (optionally with a leading `-`) /// * unit `()` /// * blocks (`{ … }`) around simple expressions and /// * paths without arguments /// /// are considered simple enough. Simple blocks are included since they are /// necessary to disambiguate unit from the unit type. /// This list might get extended in the future. /// /// Without this censoring, in a lot of cases the output would get too large /// and verbose. Consider `match` expressions, blocks and deeply nested ADTs. /// Further, private and `doc(hidden)` fields of structs would get leaked /// since HIR datatypes like the `body` parameter do not contain enough /// semantic information for this function to be able to hide them – /// at least not without significant performance overhead. /// /// Whenever possible, prefer to evaluate the constant first and try to /// use a different method for pretty-printing. Ideally this function /// should only ever be used as a fallback. </ins> pub(crate) fn print_const_expr(tcx: TyCtxt<'_>, body: hir::BodyId) -> String { let hir = tcx.hir(); let value = &hir.body(body).value; <del> let snippet = if !value.span.from_expansion() { tcx.sess.source_map().span_to_snippet(value.span).ok() } else { None }; </del> <ins> #[derive(PartialEq, Eq)] enum Classification { Literal, Simple, Complex, } </ins> <del> snippet.unwrap_or_else(|| rustc_hir_pretty::id_to_string(&hir, body.hir_id)) </del> <ins> use Classification::*; fn classify(expr: &hir::Expr<'_>) -> Classification { match &expr.kind { hir::ExprKind::Unary(hir::UnOp::Neg, expr) => { if matches!(expr.kind, hir::ExprKind::Lit(_)) { Literal } else { Complex } } hir::ExprKind::Lit(_) => Literal, hir::ExprKind::Tup([]) => Simple, hir::ExprKind::Block(hir::Block { stmts: [], expr: Some(expr), .. }, _) => { if classify(expr) == Complex { Complex } else { Simple } } // Paths with a self-type or arguments are too “complex” following our measure since // they may leak private fields of structs (with feature `adt_const_params`). // Consider: `<Self as Trait<{ Struct { private: () } }>>::CONSTANT`. // Paths without arguments are definitely harmless though. hir::ExprKind::Path(hir::QPath::Resolved(_, hir::Path { segments, .. })) => { if segments.iter().all(|segment| segment.args.is_none()) { Simple } else { Complex } } // FIXME: Claiming that those kinds of QPaths are simple is probably not true if the Ty //        contains const arguments. Is there a *concise* way to check for this? hir::ExprKind::Path(hir::QPath::TypeRelative(..)) => Simple, // FIXME: Can they contain const arguments and thus leak private struct fields? hir::ExprKind::Path(hir::QPath::LangItem(..)) => Simple, _ => Complex, } } let classification = classify(value); if classification == Literal && !value.span.from_expansion() && let Ok(snippet) = tcx.sess.source_map().span_to_snippet(value.span) { // For literals, we avoid invoking the pretty-printer and use the source snippet instead to // preserve certain stylistic choices the user likely made for the sake legibility like // // * hexadecimal notation // * underscores // * character escapes // // FIXME: This passes through `-/*spacer*/0` verbatim. snippet } else if classification == Simple { // Otherwise we prefer pretty-printing to get rid of extraneous whitespace, comments and // other formatting artifacts. rustc_hir_pretty::id_to_string(&hir, body.hir_id) } else if tcx.def_kind(hir.body_owner_def_id(body).to_def_id()) == DefKind::AnonConst { // FIXME: Omit the curly braces if the enclosing expression is an array literal //        with a repeated element (an `ExprKind::Repeat`) as in such case it //        would not actually need any disambiguation. "{ _ }".to_owned() } else { "_".to_owned() } </ins> } /// Given a type Path, resolve it to a Type using the TyCtxt


ty = ty.print(cx), ); if let Some(default) = default { <ins> write!(w, " = "); </ins> // FIXME: `.value()` uses `clean::utils::format_integer_with_underscore_sep` under the //        hood which adds noisy underscores and a type suffix to number literals. //        This hurts readability in this context especially when more complex expressions //        are involved and it doesn't add much of value. //        Find a way to print constants here without all that jazz. <del> write!(w, " = {}", default.value(cx.tcx()).unwrap_or_else(|| default.expr(cx.tcx()))); </del> <ins> write!(w, "{}", Escape(&default.value(cx.tcx()).unwrap_or_else(|| default.expr(cx.tcx())))); </ins> } }


typ = c.type_.print(cx), ); <ins> // FIXME: The code below now prints //            ` = _; // 100i32` //        if the expression is //            `50 + 50` //        which looks just wrong. //        Should we print //            ` = 100i32;` //        instead? </ins> let value = c.value(cx.tcx()); let is_literal = c.is_literal(cx.tcx()); let expr = c.expr(cx.tcx());


// @has assoc_consts/struct.Bar.html '//*[@id="associatedconstant.BAR"]'  //      'const BAR: usize' pub const BAR: usize = 3;  // @has - '//*[@id="associatedconstant.BAR_ESCAPED"]'  //      "const BAR_ESCAPED: &'static str = "markup"" pub const BAR_ESCAPED: &'static str = "markup";  } pub struct Baz<'a, U: 'a, T>(T, &'a [U]);


#![crate_name = "foo"] // @has 'foo/constant.HOUR_IN_SECONDS.html' <del> // @has - '//*[@class="docblock item-decl"]//code' 'pub const HOUR_IN_SECONDS: u64 = 60 * 60; // 3_600u64' </del> <ins> // @has - '//*[@class="docblock item-decl"]//code' 'pub const HOUR_IN_SECONDS: u64 = _; // 3_600u64' </ins> pub const HOUR_IN_SECONDS: u64 = 60 * 60; // @has 'foo/constant.NEGATIVE.html' <del> // @has - '//*[@class="docblock item-decl"]//code' 'pub const NEGATIVE: i64 = -60 * 60; // -3_600i64' </del> <ins> // @has - '//*[@class="docblock item-decl"]//code' 'pub const NEGATIVE: i64 = _; // -3_600i64' </ins> pub const NEGATIVE: i64 = -60 * 60;


<ins> // Test that certain unevaluated constant expression arguments that are // deemed too verbose or complex and that may leak private or // `doc(hidden)` struct fields are not displayed in the documentation. // // Read the documentation of `rustdoc::clean::utils::print_const_expr` // for further details. #![feature(const_trait_impl, generic_const_exprs)] #![allow(incomplete_features)] // @has hide_complex_unevaluated_const_arguments/trait.Stage.html pub trait Stage { // A helper constant that prevents const expressions containing it // from getting fully evaluated since it doesn't have a body and // thus is non-reducible. This allows us to specifically test the // pretty-printing of *unevaluated* consts. const ABSTRACT: usize; // Currently considered "overly complex" by the `generic_const_exprs` // feature. If / once this expression kind gets supported, this // unevaluated const expression could leak the private struct field. // // FIXME: Once the line below compiles, make this a test that //        ensures that the private field is not printed. // //const ARRAY0: [u8; Struct { private: () } + Self::ABSTRACT]; // This assoc. const could leak the private assoc. function `Struct::new`. // Ensure that this does not happen. // // @has - '//*[@id="associatedconstant.ARRAY1"]'  //        'const ARRAY1: [u8; { _ }]' const ARRAY1: [u8; Struct::new(/* ... */) + Self::ABSTRACT * 1_000]; // @has - '//*[@id="associatedconstant.VERBOSE"]'  //        'const VERBOSE: [u16; { _ }]' const VERBOSE: [u16; compute("thing", 9 + 9) * Self::ABSTRACT]; // Check that we do not leak the private struct field contained within // the path. The output could definitely be improved upon // (e.g. printing sth. akin to `<Self as Helper<{ _ }>>::OUT`) but // right now “safe is safe”. // // @has - '//*[@id="associatedconstant.PATH"]'  //        'const PATH: usize = _' const PATH: usize = <Self as Helper<{ Struct { private: () } }>>::OUT; } const fn compute(input: &str, extra: usize) -> usize { input.len() + extra } pub trait Helper<const S: Struct> { const OUT: usize; } impl<const S: Struct, St: Stage + ?Sized> Helper<S> for St { const OUT: usize = St::ABSTRACT; } // Currently in rustdoc, const arguments are not evaluated in this position // and therefore they fall under the realm of `print_const_expr`. // If rustdoc gets patched to evaluate const arguments, it is fine to replace // this test as long as one can ensure that private fields are not leaked! // // @has hide_complex_unevaluated_const_arguments/trait.Sub.html  //      '//*[@class="rust trait"]'  //      'pub trait Sub: Sup<{ _ }, { _ }> { }' pub trait Sub: Sup<{ 90 * 20 * 4 }, { Struct { private: () } }> {} pub trait Sup<const N: usize, const S: Struct> {} pub struct Struct { private: () } impl Struct { const fn new() -> Self { Self { private: () } } } impl const std::ops::Add<usize> for Struct { type Output = usize; fn add(self, _: usize) -> usize { 0 } } </ins>


<ins> // Regression test for issue #97933. // // Test that certain unevaluated constant expressions that are // deemed too verbose or complex and that may leak private or // `doc(hidden)` struct fields are not displayed in the documentation. // // Read the documentation of `rustdoc::clean::utils::print_const_expr` // for further details. // @has hide_complex_unevaluated_consts/trait.Container.html pub trait Container { // A helper constant that prevents const expressions containing it // from getting fully evaluated since it doesn't have a body and // thus is non-reducible. This allows us to specifically test the // pretty-printing of *unevaluated* consts. const ABSTRACT: i32; // Ensure that the private field does not get leaked: // // @has - '//*[@id="associatedconstant.STRUCT0"]'  //        'const STRUCT0: Struct = _' const STRUCT0: Struct = Struct { private: () }; // @has - '//*[@id="associatedconstant.STRUCT1"]'  //        'const STRUCT1: (Struct,) = _' const STRUCT1: (Struct,) = (Struct{private: /**/()},); // Although the struct field is public here, check that it is not // displayed. In a future version of rustdoc, we definitely want to // show it. However for the time being, the printing logic is a bit // conservative. // // @has - '//*[@id="associatedconstant.STRUCT2"]'  //        'const STRUCT2: Record = _' const STRUCT2: Record = Record { public: 5 }; // Test that we do not show the incredibly verbose match expr: // // @has - '//*[@id="associatedconstant.MATCH0"]'  //        'const MATCH0: i32 = _' const MATCH0: i32 = match 234 { 0 => 1, _ => Self::ABSTRACT, }; // @has - '//*[@id="associatedconstant.MATCH1"]'  //        'const MATCH1: bool = _' const MATCH1: bool = match Self::ABSTRACT { _ => true, }; // Check that we hide complex (arithmetic) operations. // In this case, it is a bit unfortunate since the expression // is not *that* verbose and it might be quite useful to the reader. // // However in general, the expression might be quite large and // contain match expressions and structs with private fields. // We would need to recurse over the whole expression and even more // importantly respect operator precedence when pretty-printing // the potentially partially censored expression. // For now, the implementation is quite simple and the choices // rather conservative. // // @has - '//*[@id="associatedconstant.ARITH_OPS"]'  //        'const ARITH_OPS: i32 = _' const ARITH_OPS: i32 = Self::ABSTRACT * 2 + 1; } pub struct Struct { private: () } pub struct Record { pub public: i32 } </ins>


// @!has show_const_contents/constant.CONST_EQ_TO_VALUE_I32.html '// 42i32' pub const CONST_EQ_TO_VALUE_I32: i32 = 42i32; <del> // @has show_const_contents/constant.CONST_CALC_I32.html '= 42 + 1; // 43i32' </del> <ins> // @has show_const_contents/constant.CONST_CALC_I32.html '= _; // 43i32' </ins> pub const CONST_CALC_I32: i32 = 42 + 1; // @!has show_const_contents/constant.CONST_REF_I32.html '= &42;'


/// /// This trait can be used with `#[derive]`. When `derive`d on structs, two /// instances are equal if all fields are equal, and not equal if any fields <del> /// are not equal. When `derive`d on enums, each variant is equal to itself /// and not equal to the other variants. </del> <ins> /// are not equal. When `derive`d on enums, two instances are equal if they /// are the same variant and all fields are equal. </ins> /// /// ## How can I implement `PartialEq`? ///


use crate::ffi::{CStr, CString}; use crate::fmt; use crate::io; <ins> use crate::marker::PhantomData; </ins> use crate::mem; use crate::num::NonZeroU64; use crate::num::NonZeroUsize;


unsafe fn spawn_unchecked_<'a, 'scope, F, T>( self, f: F, <del> scope_data: Option<&'scope scoped::ScopeData>, </del> <ins> scope_data: Option<Arc<scoped::ScopeData>>, </ins> ) -> io::Result<JoinInner<'scope, T>> where F: FnOnce() -> T,


})); let their_thread = my_thread.clone(); <del> let my_packet: Arc<Packet<'scope, T>> = Arc::new(Packet { scope: scope_data, result: UnsafeCell::new(None) }); </del> <ins> let my_packet: Arc<Packet<'scope, T>> = Arc::new(Packet { scope: scope_data, result: UnsafeCell::new(None), _marker: PhantomData, }); </ins> let their_packet = my_packet.clone(); let output_capture = crate::io::set_output_capture(None);


unsafe { *their_packet.result.get() = Some(try_result) }; }; <del> if let Some(scope_data) = scope_data { </del> <ins> if let Some(scope_data) = &my_packet.scope { </ins> scope_data.increment_num_running_threads(); }


// An Arc to the packet is stored into a `JoinInner` which in turns is placed // in `JoinHandle`. struct Packet<'scope, T> { <del> scope: Option<&'scope scoped::ScopeData>, </del> <ins> scope: Option<Arc<scoped::ScopeData>>, </ins> result: UnsafeCell<Option<Result<T>>>, <ins> _marker: PhantomData<Option<&'scope scoped::ScopeData>>, </ins> } // Due to the usage of `UnsafeCell` we need to manually implement Sync.


rtabort!("thread result panicked on drop"); } // Book-keeping so the scope knows when it's done. <del> if let Some(scope) = self.scope { </del> <ins> if let Some(scope) = &self.scope { </ins> // Now that there will be no more user code running on this thread // that can use 'scope, mark the thread as 'finished'. // It's important we only do this after the `result` has been dropped,


/// See [`scope`] for details. #[stable(feature = "scoped_threads", since = "1.63.0")] pub struct Scope<'scope, 'env: 'scope> { <del> data: ScopeData, </del> <ins> data: Arc<ScopeData>, </ins> /// Invariance over 'scope, to make sure 'scope cannot shrink, /// which is necessary for soundness. ///


where F: for<'scope> FnOnce(&'scope Scope<'scope, 'env>) -> T, { <ins> // We put the `ScopeData` into an `Arc` so that other threads can finish their // `decrement_num_running_threads` even after this function returns. </ins> let scope = Scope { <del> data: ScopeData { </del> <ins> data: Arc::new(ScopeData { </ins> num_running_threads: AtomicUsize::new(0), main_thread: current(), a_thread_panicked: AtomicBool::new(false), <del> }, </del> <ins> }), </ins> env: PhantomData, scope: PhantomData, };


F: FnOnce() -> T + Send + 'scope, T: Send + 'scope, { <del> Ok(ScopedJoinHandle(unsafe { self.spawn_unchecked_(f, Some(&scope.data)) }?)) </del> <ins> Ok(ScopedJoinHandle(unsafe { self.spawn_unchecked_(f, Some(scope.data.clone())) }?)) </ins> } }


initializer_span, else_block, visibility_scope, <ins> *remainder_scope, </ins> remainder_span, pattern, )


initializer_span: Span, else_block: &Block, visibility_scope: Option<SourceScope>, <ins> remainder_scope: region::Scope, </ins> remainder_span: Span, pattern: &Pat<'tcx>, ) -> BlockAnd<()> { <del> let scrutinee = unpack!(block = self.lower_scrutinee(block, init, initializer_span)); let pat = Pat { ty: init.ty, span: else_block.span, kind: Box::new(PatKind::Wild) }; let mut wildcard = Candidate::new(scrutinee.clone(), &pat, false); self.declare_bindings( visibility_scope, remainder_span, pattern, ArmHasGuard(false), Some((None, initializer_span)), ); let mut candidate = Candidate::new(scrutinee.clone(), pattern, false); let fake_borrow_temps = self.lower_match_tree( block, initializer_span, pattern.span, false, &mut [&mut candidate, &mut wildcard], ); // This block is for the matching case let matching = self.bind_pattern( self.source_info(pattern.span), candidate, None, &fake_borrow_temps, initializer_span, None, None, None, ); // This block is for the failure case let failure = self.bind_pattern( self.source_info(else_block.span), wildcard, None, &fake_borrow_temps, initializer_span, None, None, None, ); </del> <ins> let (matching, failure) = self.in_if_then_scope(remainder_scope, |this| { let scrutinee = unpack!(block = this.lower_scrutinee(block, init, initializer_span)); let pat = Pat { ty: init.ty, span: else_block.span, kind: Box::new(PatKind::Wild) }; let mut wildcard = Candidate::new(scrutinee.clone(), &pat, false); this.declare_bindings( visibility_scope, remainder_span, pattern, ArmHasGuard(false), Some((None, initializer_span)), ); let mut candidate = Candidate::new(scrutinee.clone(), pattern, false); let fake_borrow_temps = this.lower_match_tree( block, initializer_span, pattern.span, false, &mut [&mut candidate, &mut wildcard], ); // This block is for the matching case let matching = this.bind_pattern( this.source_info(pattern.span), candidate, None, &fake_borrow_temps, initializer_span, None, None, None, ); // This block is for the failure case let failure = this.bind_pattern( this.source_info(else_block.span), wildcard, None, &fake_borrow_temps, initializer_span, None, None, None, ); this.break_for_else(failure, remainder_scope, this.source_info(initializer_span)); matching.unit() }); </ins> // This place is not really used because this destination place // should never be used to take values at the end of the failure // block.


} drops.add_entry(block, drop_idx); <del> // `build_drop_tree` doesn't have access to our source_info, so we </del> <ins> // `build_drop_trees` doesn't have access to our source_info, so we </ins> // create a dummy terminator now. `TerminatorKind::Resume` is used // because MIR type checking will panic if it hasn't been overwritten. self.cfg.terminate(block, source_info, TerminatorKind::Resume);


<ins> // run-pass // // from issue #93951, where borrowck complained the temporary that `foo(&x)` was stored in was to // be dropped sometime after `x` was. It then suggested adding a semicolon that was already there. #![feature(let_else)] use std::fmt::Debug; fn foo<'a>(x: &'a str) -> Result<impl Debug + 'a, ()> { Ok(x) } fn let_else() { let x = String::from("Hey"); let Ok(_) = foo(&x) else { return }; } fn if_let() { let x = String::from("Hey"); let _ = if let Ok(s) = foo(&x) { s } else { return }; } fn main() { let_else(); if_let(); } </ins>


// run-pass #![feature(let_else)] <ins> use std::fmt::Display; use std::rc::Rc; </ins> use std::sync::atomic::{AtomicU8, Ordering}; static TRACKER: AtomicU8 = AtomicU8::new(0);


} } <ins> fn foo<'a>(x: &'a str) -> Result<impl Display + 'a, ()> { Ok(x) } </ins> fn main() { assert_eq!(TRACKER.load(Ordering::Acquire), 0); let 0 = Droppy::default().inner else { return }; assert_eq!(TRACKER.load(Ordering::Acquire), 1); println!("Should have dropped 👆"); <ins> { // cf. https://github.com/rust-lang/rust/pull/99518#issuecomment-1191520030 struct Foo<'a>(&'a mut u32); impl<'a> Drop for Foo<'a> { fn drop(&mut self) { *self.0 = 0; } } let mut foo = 0; let Foo(0) = Foo(&mut foo) else { *&mut foo = 1; todo!() }; } { let x = String::from("Hey"); let Ok(s) = foo(&x) else { panic!() }; assert_eq!(s.to_string(), x); } { // test let-else drops temps after statement let rc = Rc::new(0); let 0 = *rc.clone() else { unreachable!() }; Rc::try_unwrap(rc).unwrap(); } { let mut rc = Rc::new(0); let mut i = 0; loop { if i > 3 { break; } let 1 = *rc.clone() else { if let Ok(v) = Rc::try_unwrap(rc) { rc = Rc::new(v); } else { panic!() } i += 1; continue }; } } { // test let-else drops temps before else block // NOTE: this test has to be the last block in the `main` // body. let rc = Rc::new(0); let 1 = *rc.clone() else { Rc::try_unwrap(rc).unwrap(); return; }; unreachable!(); } </ins> }


LazyLock::new(|| { let hook = panic::take_hook(); panic::set_hook(Box::new(|info| { <ins> // If the error was caused by a broken pipe then this is not a bug. // Write the error and return immediately. See #98700. #[cfg(windows)] if let Some(msg) = info.payload().downcast_ref::<String>() { if msg.starts_with("failed printing to stdout: ") && msg.ends_with("(os error 232)") { early_error_no_abort(ErrorOutputType::default(), &msg); return; } }; </ins> // Invoke the default handler, which prints the actual panic message and optionally a backtrace (*DEFAULT_HOOK)(info);


// FIXME(#75598): Direct use of these intrinsics improves codegen significantly at opt-level <= // 1, where the method versions of these operations are not inlined. use intrinsics::{ <del> unchecked_shl, unchecked_shr, unchecked_sub, wrapping_add, wrapping_mul, wrapping_sub, </del> <ins> cttz_nonzero, exact_div, unchecked_rem, unchecked_shl, unchecked_shr, unchecked_sub, wrapping_add, wrapping_mul, wrapping_sub, </ins> }; <del> let addr = p.addr(); </del> /// Calculate multiplicative modular inverse of `x` modulo `m`. /// /// This implementation is tailored for `align_offset` and has following preconditions:


} } <ins> let addr = p.addr(); </ins> let stride = mem::size_of::<T>(); // SAFETY: `a` is a power-of-two, therefore non-zero. let a_minus_one = unsafe { unchecked_sub(a, 1) }; <del> if stride == 1 { // `stride == 1` case can be computed more simply through `-p (mod a)`, but doing so // inhibits LLVM's ability to select instructions like `lea`. Instead we compute </del> <ins> if stride == 0 { // SPECIAL_CASE: handle 0-sized types. No matter how many times we step, the address will // stay the same, so no offset will be able to align the pointer unless it is already // aligned. This branch _will_ be optimized out as `stride` is known at compile-time. let p_mod_a = addr & a_minus_one; return if p_mod_a == 0 { 0 } else { usize::MAX }; } // SAFETY: `stride == 0` case has been handled by the special case above. let a_mod_stride = unsafe { unchecked_rem(a, stride) }; if a_mod_stride == 0 { // SPECIAL_CASE: In cases where the `a` is divisible by `stride`, byte offset to align a // pointer can be computed more simply through `-p (mod a)`. In the off-chance the byte // offset is not a multiple of `stride`, the input pointer was misaligned and no pointer // offset will be able to produce a `p` aligned to the specified `a`. </ins> // <del> //    round_up_to_next_alignment(p, a) - p </del> <ins> // The naive `-p (mod a)` equation  inhibits LLVM's ability to select instructions // like `lea`. We compute `(round_up_to_next_alignment(p, a) - p)` instead. This // redistributes operations around the load-bearing, but pessimizing `and` instruction // sufficiently for LLVM to be able to utilize the various optimizations it knows about. </ins> // <del> // which distributes operations around the load-bearing, but pessimizing `and` sufficiently // for LLVM to be able to utilize the various optimizations it knows about. return wrapping_sub(wrapping_add(addr, a_minus_one) & wrapping_sub(0, a), addr); } </del> <ins> // LLVM handles the branch here particularly nicely. If this branch needs to be evaluated // at runtime, it will produce a mask `if addr_mod_stride == 0 { 0 } else { usize::MAX }` // in a branch-free way and then bitwise-OR it with whatever result the `-p mod a` // computation produces. // SAFETY: `stride == 0` case has been handled by the special case above. let addr_mod_stride = unsafe { unchecked_rem(addr, stride) }; </ins> <del> let pmoda = addr & a_minus_one; if pmoda == 0 { // Already aligned. Yay! return 0; } else if stride == 0 { // If the pointer is not aligned, and the element is zero-sized, then no amount of // elements will ever align the pointer. return usize::MAX; </del> <ins> return if addr_mod_stride == 0 { let aligned_address = wrapping_add(addr, a_minus_one) & wrapping_sub(0, a); let byte_offset = wrapping_sub(aligned_address, addr); // SAFETY: `stride` is non-zero. This is guaranteed to divide exactly as well, because // addr has been verified to be aligned to the original type’s alignment requirements. unsafe { exact_div(byte_offset, stride) } } else { usize::MAX }; </ins> } <del> let smoda = stride & a_minus_one; </del> <ins> // GENERAL_CASE: From here on we’re handling the very general case where `addr` may be // misaligned, there isn’t an obvious relationship between `stride` and `a` that we can take an // advantage of, etc. This case produces machine code that isn’t particularly high quality, // compared to the special cases above. The code produced here is still within the realm of // miracles, given the situations this case has to deal with. </ins> // SAFETY: a is power-of-two hence non-zero. stride == 0 case is handled above. <del> let gcdpow = unsafe { intrinsics::cttz_nonzero(stride).min(intrinsics::cttz_nonzero(a)) }; </del> <ins> let gcdpow = unsafe { cttz_nonzero(stride).min(cttz_nonzero(a)) }; </ins> // SAFETY: gcdpow has an upper-bound that’s at most the number of bits in a usize. let gcd = unsafe { unchecked_shl(1usize, gcdpow) }; // SAFETY: gcd is always greater or equal to 1. if addr & unsafe { unchecked_sub(gcd, 1) } == 0 { // This branch solves for the following linear congruence equation:


// ` p' + s'o = 0 mod a' ` // ` o = (a' - (p' mod a')) * (s'^-1 mod a') ` // <del> // The first term is "the relative alignment of `p` to `a`" (divided by the `g`), the second // term is "how does incrementing `p` by `s` bytes change the relative alignment of `p`" (again // divided by `g`). // Division by `g` is necessary to make the inverse well formed if `a` and `s` are not // co-prime. </del> <ins> // The first term is "the relative alignment of `p` to `a`" (divided by the `g`), the // second term is "how does incrementing `p` by `s` bytes change the relative alignment of // `p`" (again divided by `g`). Division by `g` is necessary to make the inverse well // formed if `a` and `s` are not co-prime. </ins> // // Furthermore, the result produced by this solution is not "minimal", so it is necessary <del> // to take the result `o mod lcm(s, a)`. We can replace `lcm(s, a)` with just a `a'`. </del> <ins> // to take the result `o mod lcm(s, a)`. This `lcm(s, a)` is the same as `a'`. </ins> // SAFETY: `gcdpow` has an upper-bound not greater than the number of trailing 0-bits in // `a`.


let a2minus1 = unsafe { unchecked_sub(a2, 1) }; // SAFETY: `gcdpow` has an upper-bound not greater than the number of trailing 0-bits in // `a`. <del> let s2 = unsafe { unchecked_shr(smoda, gcdpow) }; </del> <ins> let s2 = unsafe { unchecked_shr(stride & a_minus_one, gcdpow) }; </ins> // SAFETY: `gcdpow` has an upper-bound not greater than the number of trailing 0-bits in // `a`. Furthermore, the subtraction cannot overflow, because `a2 = a >> gcdpow` will // always be strictly greater than `(p % a) >> gcdpow`. <del> let minusp2 = unsafe { unchecked_sub(a2, unchecked_shr(pmoda, gcdpow)) }; </del> <ins> let minusp2 = unsafe { unchecked_sub(a2, unchecked_shr(addr & a_minus_one, gcdpow)) }; </ins> // SAFETY: `a2` is a power-of-two, as proven above. `s2` is strictly less than `a2` // because `(s % a) >> gcdpow` is strictly less than `a >> gcdpow`. return wrapping_mul(minusp2, unsafe { mod_inv(s2, a2) }) & a2minus1;


} #[test] <del> fn align_offset_stride1() { </del> <ins> fn align_offset_stride_one() { </ins> // For pointers of stride = 1, the pointer can always be aligned. The offset is equal to // number of bytes. let mut align = 1;


} #[test] <del> fn align_offset_weird_strides() { #[repr(packed)] struct A3(u16, u8); struct A4(u32); #[repr(packed)] struct A5(u32, u8); #[repr(packed)] struct A6(u32, u16); #[repr(packed)] struct A7(u32, u16, u8); #[repr(packed)] struct A8(u32, u32); #[repr(packed)] struct A9(u32, u32, u8); #[repr(packed)] struct A10(u32, u32, u16); unsafe fn test_weird_stride<T>(ptr: *const T, align: usize) -> bool { </del> <ins> fn align_offset_various_strides() { unsafe fn test_stride<T>(ptr: *const T, align: usize) -> bool { </ins> let numptr = ptr as usize; let mut expected = usize::MAX; // Naive but definitely correct way to find the *first* aligned element of stride::<T>.


while align < limit { for ptr in 1usize..4 * align { unsafe { <del> x |= test_weird_stride::<A3>(ptr::invalid::<A3>(ptr), align); x |= test_weird_stride::<A4>(ptr::invalid::<A4>(ptr), align); x |= test_weird_stride::<A5>(ptr::invalid::<A5>(ptr), align); x |= test_weird_stride::<A6>(ptr::invalid::<A6>(ptr), align); x |= test_weird_stride::<A7>(ptr::invalid::<A7>(ptr), align); x |= test_weird_stride::<A8>(ptr::invalid::<A8>(ptr), align); x |= test_weird_stride::<A9>(ptr::invalid::<A9>(ptr), align); x |= test_weird_stride::<A10>(ptr::invalid::<A10>(ptr), align); </del> <ins> #[repr(packed)] struct A3(u16, u8); x |= test_stride::<A3>(ptr::invalid::<A3>(ptr), align); struct A4(u32); x |= test_stride::<A4>(ptr::invalid::<A4>(ptr), align); #[repr(packed)] struct A5(u32, u8); x |= test_stride::<A5>(ptr::invalid::<A5>(ptr), align); #[repr(packed)] struct A6(u32, u16); x |= test_stride::<A6>(ptr::invalid::<A6>(ptr), align); #[repr(packed)] struct A7(u32, u16, u8); x |= test_stride::<A7>(ptr::invalid::<A7>(ptr), align); #[repr(packed)] struct A8(u32, u32); x |= test_stride::<A8>(ptr::invalid::<A8>(ptr), align); #[repr(packed)] struct A9(u32, u32, u8); x |= test_stride::<A9>(ptr::invalid::<A9>(ptr), align); #[repr(packed)] struct A10(u32, u32, u16); x |= test_stride::<A10>(ptr::invalid::<A10>(ptr), align); x |= test_stride::<u32>(ptr::invalid::<u32>(ptr), align); x |= test_stride::<u128>(ptr::invalid::<u128>(ptr), align); </ins> } } align = (align + 1).next_power_of_two();


<ins> // assembly-output: emit-asm // compile-flags: -Copt-level=1 // only-x86_64 // min-llvm-version: 14.0 #![crate_type="rlib"] // CHECK-LABEL: align_offset_byte_ptr // CHECK: leaq 31 // CHECK: andq $-32 // CHECK: subq #[no_mangle] pub fn align_offset_byte_ptr(ptr: *const u8) -> usize { ptr.align_offset(32) } // CHECK-LABEL: align_offset_byte_slice // CHECK: leaq 31 // CHECK: andq $-32 // CHECK: subq #[no_mangle] pub fn align_offset_byte_slice(slice: &[u8]) -> usize { slice.as_ptr().align_offset(32) } // CHECK-LABEL: align_offset_word_ptr // CHECK: leaq 31 // CHECK: andq $-32 // CHECK: subq // CHECK: shrq // This `ptr` is not known to be aligned, so it is required to check if it is at all possible to // align. LLVM applies a simple mask. // CHECK: orq #[no_mangle] pub fn align_offset_word_ptr(ptr: *const u32) -> usize { ptr.align_offset(32) } // CHECK-LABEL: align_offset_word_slice // CHECK: leaq 31 // CHECK: andq $-32 // CHECK: subq // CHECK: shrq // `slice` is known to be aligned, so `!0` is not possible as a return // CHECK-NOT: orq #[no_mangle] pub fn align_offset_word_slice(slice: &[u32]) -> usize { slice.as_ptr().align_offset(32) } </ins>


if !self.report_similar_impl_candidates( impl_candidates, trait_ref, <ins> obligation.cause.body_id, </ins> &mut err, ) { // This is *almost* equivalent to


self.report_similar_impl_candidates( impl_candidates, trait_ref, <ins> obligation.cause.body_id, </ins> &mut err, ); }


&self, impl_candidates: Vec<ImplCandidate<'tcx>>, trait_ref: ty::PolyTraitRef<'tcx>, <ins> body_id: hir::HirId, </ins> err: &mut Diagnostic, ) -> bool;


&self, impl_candidates: Vec<ImplCandidate<'tcx>>, trait_ref: ty::PolyTraitRef<'tcx>, <ins> body_id: hir::HirId, </ins> err: &mut Diagnostic, ) -> bool { let report = |mut candidates: Vec<TraitRef<'tcx>>, err: &mut Diagnostic| {


|| self.tcx.is_builtin_derive(def_id) }) .filter_map(|def_id| self.tcx.impl_trait_ref(def_id)) <del> // Avoid mentioning type parameters. .filter(|trait_ref| !matches!(trait_ref.self_ty().kind(), ty::Param(_))) </del> <ins> .filter(|trait_ref| { let self_ty = trait_ref.self_ty(); // Avoid mentioning type parameters. if let ty::Param(_) = self_ty.kind() { false } // Avoid mentioning types that are private to another crate else if let ty::Adt(def, _) = self_ty.peel_refs().kind() { // FIXME(compiler-errors): This could be generalized, both to // be more granular, and probably look past other `#[fundamental]` // types, too. self.tcx .visibility(def.did()) .is_accessible_from(body_id.owner.to_def_id(), self.tcx) } else { true } }) </ins> .collect(); return report(normalized_impl_candidates, err); }


<ins> pub trait Meow { fn meow(&self) {} } pub struct GlobalMeow; impl Meow for GlobalMeow {} pub(crate) struct PrivateMeow; impl Meow for PrivateMeow {} </ins>


<ins> // aux-build:meow.rs extern crate meow; use meow::Meow; fn needs_meow<T: Meow>(t: T) {} fn main() { needs_meow(1usize); //~^ ERROR the trait bound `usize: Meow` is not satisfied } struct LocalMeow; impl Meow for LocalMeow {} </ins>


<ins> error[E0277]: the trait bound `usize: Meow` is not satisfied --> $DIR/issue-99080.rs:10:16 | LL |     needs_meow(1usize); |     ---------- ^^^^^^ the trait `Meow` is not implemented for `usize` |     | |     required by a bound introduced by this call | = help: the following other types implement trait `Meow`: GlobalMeow LocalMeow note: required by a bound in `needs_meow` --> $DIR/issue-99080.rs:7:18 | LL | fn needs_meow<T: Meow>(t: T) {} |                  ^^^^ required by this bound in `needs_meow` error: aborting due to previous error For more information about this error, try `rustc --explain E0277`. </ins>


&'c &'b str [char; N] char <del> pattern::MultiCharEqPattern<C> </del> = note: required because of the requirements on the impl of `Pattern<'_>` for `u8` error: aborting due to previous error


/// in the given deque. The collection may reserve more space to speculatively avoid /// frequent reallocations. After calling `try_reserve`, capacity will be /// greater than or equal to `self.len() + additional` if it returns <del> /// `Ok(())`. Does nothing if capacity is already sufficient. </del> <ins> /// `Ok(())`. Does nothing if capacity is already sufficient. This method /// preserves the contents even if an error occurs. </ins> /// /// # Errors ///


/// current length. The allocator may reserve more space to speculatively /// avoid frequent allocations. After calling `try_reserve`, capacity will be /// greater than or equal to `self.len() + additional` if it returns <del> /// `Ok(())`. Does nothing if capacity is already sufficient. </del> <ins> /// `Ok(())`. Does nothing if capacity is already sufficient. This method /// preserves the contents even if an error occurs. </ins> /// /// # Errors ///


/// in the given `Vec<T>`. The collection may reserve more space to speculatively avoid /// frequent reallocations. After calling `try_reserve`, capacity will be /// greater than or equal to `self.len() + additional` if it returns <del> /// `Ok(())`. Does nothing if capacity is already sufficient. </del> <ins> /// `Ok(())`. Does nothing if capacity is already sufficient. This method /// preserves the contents even if an error occurs. </ins> /// /// # Errors ///


/// in the given `OsString`. The string may reserve more space to speculatively avoid /// frequent reallocations. After calling `try_reserve`, capacity will be /// greater than or equal to `self.len() + additional` if it returns `Ok(())`. <del> /// Does nothing if capacity is already sufficient. </del> <ins> /// Does nothing if capacity is already sufficient. This method preserves /// the contents even if an error occurs. </ins> /// /// See the main `OsString` documentation information about encoding and capacity units. ///


/// in the given `Wtf8Buf`. The `Wtf8Buf` may reserve more space to avoid /// frequent reallocations. After calling `try_reserve`, capacity will be /// greater than or equal to `self.len() + additional`. Does nothing if <del> /// capacity is already sufficient. </del> <ins> /// capacity is already sufficient. This method preserves the contents even /// if an error occurs. </ins> /// /// # Errors ///


.collect::<Result<IndexVec<VariantIdx, _>, _>>()?; let offset = st[i].fields().offset(field_index) + niche.offset; <del> let size = st[i].size(); </del> <ins> // Align the total size to the largest alignment. let size = st[i].size().align_to(align.abi); </ins> let abi = if st.iter().all(|v| v.abi().is_uninhabited()) { Abi::Uninhabited <del> } else { </del> <ins> } else if align == st[i].align() && size == st[i].size() { // When the total alignment and size match, we can use the // same ABI as the scalar variant with the reserved niche. </ins> match st[i].abi() { Abi::Scalar(_) => Abi::Scalar(niche_scalar), Abi::ScalarPair(first, second) => {


} _ => Abi::Aggregate { sized: true }, } <ins> } else { Abi::Aggregate { sized: true } </ins> }; let largest_niche = Niche::from_scalar(dl, offset, niche_scalar);


<ins> // normalize-stderr-test "pref: Align([1-8] bytes)" -> "pref: $$PREF_ALIGN" #![feature(rustc_attrs)] #![crate_type = "lib"] // Various tests around the behavior of zero-sized arrays and // enum niches, especially that they have coherent size and alignment. // The original problem in #99836 came from ndarray's `TryFrom` for // `SliceInfo<[SliceInfoElem; 0], Din, Dout>`, where that returns // `Result<Self, ShapeError>` ~= `Result<AlignedZST, TypeWithNiche>`. // This is a close enough approximation: #[rustc_layout(debug)] type AlignedResult = Result<[u32; 0], bool>; //~ ERROR: layout_of // The bug gave that size 1 with align 4, but the size should also be 4. // It was also using the bool niche for the enum tag, which is fine, but // after the fix, layout decides to use a direct tagged repr instead. // Here's another case with multiple ZST alignments, where we should // get the maximal alignment and matching size. #[rustc_layout(debug)] enum MultipleAlignments { //~ ERROR: layout_of Align2([u16; 0]), Align4([u32; 0]), Niche(bool), } // Tagged repr is clever enough to grow tags to fill any padding, e.g.: // 1.   `T_FF` (one byte of Tag, one byte of padding, two bytes of align=2 Field) //   -> `TTFF` (Tag has expanded to two bytes, i.e. like `#[repr(u16)]`) // 2.    `TFF` (one byte of Tag, two bytes of align=1 Field) //   -> Tag has no room to expand! //   (this outcome can be forced onto 1. by wrapping Field in `Packed<...>`) #[repr(packed)] struct Packed<T>(T); #[rustc_layout(debug)] type NicheLosesToTagged = Result<[u32; 0], Packed<std::num::NonZeroU16>>; //~ ERROR: layout_of // Should get tag_encoding: Direct, size == align == 4. #[repr(u16)] enum U16IsZero { _Zero = 0 } #[rustc_layout(debug)] type NicheWinsOverTagged = Result<[u32; 0], Packed<U16IsZero>>; //~ ERROR: layout_of // Should get tag_encoding: Niche, size == align == 4. </ins>


<ins> error: layout_of(std::result::Result<[u32; 0], bool>) = Layout { fields: Arbitrary { offsets: [ Size(0 bytes), ], memory_index: [ 0, ], }, variants: Multiple { tag: Initialized { value: Int( I8, false, ), valid_range: 0..=1, }, tag_encoding: Direct, tag_field: 0, variants: [ Layout { fields: Arbitrary { offsets: [ Size(4 bytes), ], memory_index: [ 0, ], }, variants: Single { index: 0, }, abi: Aggregate { sized: true, }, largest_niche: None, align: AbiAndPrefAlign { abi: Align(4 bytes), pref: $PREF_ALIGN, }, size: Size(4 bytes), }, Layout { fields: Arbitrary { offsets: [ Size(1 bytes), ], memory_index: [ 0, ], }, variants: Single { index: 1, }, abi: Aggregate { sized: true, }, largest_niche: Some( Niche { offset: Size(1 bytes), value: Int( I8, false, ), valid_range: 0..=1, }, ), align: AbiAndPrefAlign { abi: Align(1 bytes), pref: $PREF_ALIGN, }, size: Size(2 bytes), }, ], }, abi: Aggregate { sized: true, }, largest_niche: Some( Niche { offset: Size(0 bytes), value: Int( I8, false, ), valid_range: 0..=1, }, ), align: AbiAndPrefAlign { abi: Align(4 bytes), pref: $PREF_ALIGN, }, size: Size(4 bytes), } --> $DIR/zero-sized-array-enum-niche.rs:13:1 | LL | type AlignedResult = Result<[u32; 0], bool>; | ^^^^^^^^^^^^^^^^^^ error: layout_of(MultipleAlignments) = Layout { fields: Arbitrary { offsets: [ Size(0 bytes), ], memory_index: [ 0, ], }, variants: Multiple { tag: Initialized { value: Int( I8, false, ), valid_range: 0..=2, }, tag_encoding: Direct, tag_field: 0, variants: [ Layout { fields: Arbitrary { offsets: [ Size(2 bytes), ], memory_index: [ 0, ], }, variants: Single { index: 0, }, abi: Aggregate { sized: true, }, largest_niche: None, align: AbiAndPrefAlign { abi: Align(2 bytes), pref: $PREF_ALIGN, }, size: Size(2 bytes), }, Layout { fields: Arbitrary { offsets: [ Size(4 bytes), ], memory_index: [ 0, ], }, variants: Single { index: 1, }, abi: Aggregate { sized: true, }, largest_niche: None, align: AbiAndPrefAlign { abi: Align(4 bytes), pref: $PREF_ALIGN, }, size: Size(4 bytes), }, Layout { fields: Arbitrary { offsets: [ Size(1 bytes), ], memory_index: [ 0, ], }, variants: Single { index: 2, }, abi: Aggregate { sized: true, }, largest_niche: Some( Niche { offset: Size(1 bytes), value: Int( I8, false, ), valid_range: 0..=1, }, ), align: AbiAndPrefAlign { abi: Align(1 bytes), pref: $PREF_ALIGN, }, size: Size(2 bytes), }, ], }, abi: Aggregate { sized: true, }, largest_niche: Some( Niche { offset: Size(0 bytes), value: Int( I8, false, ), valid_range: 0..=2, }, ), align: AbiAndPrefAlign { abi: Align(4 bytes), pref: $PREF_ALIGN, }, size: Size(4 bytes), } --> $DIR/zero-sized-array-enum-niche.rs:21:1 | LL | enum MultipleAlignments { | ^^^^^^^^^^^^^^^^^^^^^^^ error: layout_of(std::result::Result<[u32; 0], Packed<std::num::NonZeroU16>>) = Layout { fields: Arbitrary { offsets: [ Size(0 bytes), ], memory_index: [ 0, ], }, variants: Multiple { tag: Initialized { value: Int( I8, false, ), valid_range: 0..=1, }, tag_encoding: Direct, tag_field: 0, variants: [ Layout { fields: Arbitrary { offsets: [ Size(4 bytes), ], memory_index: [ 0, ], }, variants: Single { index: 0, }, abi: Aggregate { sized: true, }, largest_niche: None, align: AbiAndPrefAlign { abi: Align(4 bytes), pref: $PREF_ALIGN, }, size: Size(4 bytes), }, Layout { fields: Arbitrary { offsets: [ Size(1 bytes), ], memory_index: [ 0, ], }, variants: Single { index: 1, }, abi: Aggregate { sized: true, }, largest_niche: Some( Niche { offset: Size(1 bytes), value: Int( I16, false, ), valid_range: 1..=65535, }, ), align: AbiAndPrefAlign { abi: Align(1 bytes), pref: $PREF_ALIGN, }, size: Size(3 bytes), }, ], }, abi: Aggregate { sized: true, }, largest_niche: Some( Niche { offset: Size(0 bytes), value: Int( I8, false, ), valid_range: 0..=1, }, ), align: AbiAndPrefAlign { abi: Align(4 bytes), pref: $PREF_ALIGN, }, size: Size(4 bytes), } --> $DIR/zero-sized-array-enum-niche.rs:37:1 | LL | type NicheLosesToTagged = Result<[u32; 0], Packed<std::num::NonZeroU16>>; | ^^^^^^^^^^^^^^^^^^^^^^^ error: layout_of(std::result::Result<[u32; 0], Packed<U16IsZero>>) = Layout { fields: Arbitrary { offsets: [ Size(0 bytes), ], memory_index: [ 0, ], }, variants: Multiple { tag: Initialized { value: Int( I16, false, ), valid_range: 0..=1, }, tag_encoding: Niche { dataful_variant: 1, niche_variants: 0..=0, niche_start: 1, }, tag_field: 0, variants: [ Layout { fields: Arbitrary { offsets: [ Size(0 bytes), ], memory_index: [ 0, ], }, variants: Single { index: 0, }, abi: Aggregate { sized: true, }, largest_niche: None, align: AbiAndPrefAlign { abi: Align(4 bytes), pref: $PREF_ALIGN, }, size: Size(0 bytes), }, Layout { fields: Arbitrary { offsets: [ Size(0 bytes), ], memory_index: [ 0, ], }, variants: Single { index: 1, }, abi: Aggregate { sized: true, }, largest_niche: Some( Niche { offset: Size(0 bytes), value: Int( I16, false, ), valid_range: 0..=0, }, ), align: AbiAndPrefAlign { abi: Align(1 bytes), pref: $PREF_ALIGN, }, size: Size(2 bytes), }, ], }, abi: Aggregate { sized: true, }, largest_niche: Some( Niche { offset: Size(0 bytes), value: Int( I16, false, ), valid_range: 0..=1, }, ), align: AbiAndPrefAlign { abi: Align(4 bytes), pref: $PREF_ALIGN, }, size: Size(4 bytes), } --> $DIR/zero-sized-array-enum-niche.rs:44:1 | LL | type NicheWinsOverTagged = Result<[u32; 0], Packed<U16IsZero>>; | ^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 4 previous errors </ins>