Question:
The following code sometimes causes a data race to be reported by Miri: Miri reports: The deallocation occurs when returns and its local variable gets deallocated. The read occurs here (I am fairly sure): This reads the contents of the field after the . But the moment the is done, the memory backing might be deallocated. That makes the read a potential use-after-free, and a race with the deallocation. Cc
The following patch fixes this: However, even with this patch, we still run into a problem like the gets deallocated while still runs. This is still UB even after my proposed because contains a field that is not inside an (the field). So that part of what points to is expected to stay live for the entire duration of the function call. Another alternative might be to put the entire into an rather than allocating it on the stack frame of .
Going to attach this to the 1.63 milestone, since this is in a newly stabilized feature -- we may wish to back out stabilization for a cycle or backport fixes.
Re-opening for beta backport.
The fix was backported in .

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> } }


Question:
This bug has already been identified but I wanted to open an issue for it. The following code should print first. () I believe there is an unanswered question: Should temporaries be dropped before the block? said this issue is being investigated at <!-- TRIAGEBOTSTART --<!-- TRIAGEBOTASSIGNSTART --This issue has been assigned to via . <!-- TRIAGEBOTASSIGNDATASTARTTRIAGEBOTASSIGNDATAEND --<!-- TRIAGEBOTASSIGNEND --<!-- TRIAGEBOTEND --
assign (I'm pretty sure you are working on this at )
I think they should, since this would be consistent with if/else. ()
Originally reported in: There are also some variations of it: I've made a little test case for it that doesn't rely on printf but instead can be ran as a run-pass test:
I believe the two tests in shall be comparing to . The reason is that actually extends the temporary to the end of the block at the moment, which is the desired outcome. cc
good point. What's important is that both the let-else and let-else free version are doing the same thing. Maybe one can add an additional block around the and then add a second to ensure it dropped after the block terminated? Edit: nvm, it's not possible as the .stdout files are for compiler output, not runtime output of the tests.
<details<summaryHere's another way to test, using Rc</summary</details
It might also be an idea to add a test for the inconsistency between if and if let: (no matter which side of the divide let else is on, make sure that there is no breakage of it in the future)
The above test for "drop before else block" failed on master. Reopening.
Allow me to notify that I am currently looking at how to drop values earlier inside the block.
So I was rebasing onto latest master and I wanted to share this as part of the diff: ! There has been great improvement by but it was no full fix, especially this still post-drops: prints: If you remove the it works now thanks to .
I think this is because the borrowed is assigned with extended lifetime. only breaks out towards the remainder scope. It turns out this is not sufficient. I will try to break out to further one scope up, say the parent remainder scope of the current one. I feel that this should suffice.
This is correct behavior I think. Anything accessible in the pattern needs to be in scope till the end of the block. Consider you can write . It shouldn't depend on whether the pattern matches since that is not known at compile time generally.
yeah it may not be dropped before the code after the construct terminates, but it can be dropped before the block.
I see. I think this is the non-temporary case, when the pattern doesn't match.

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


Question:
This bug has already been identified but I wanted to open an issue for it. The following code should print first. () I believe there is an unanswered question: Should temporaries be dropped before the block? said this issue is being investigated at <!-- TRIAGEBOTSTART --<!-- TRIAGEBOTASSIGNSTART --This issue has been assigned to via . <!-- TRIAGEBOTASSIGNDATASTARTTRIAGEBOTASSIGNDATAEND --<!-- TRIAGEBOTASSIGNEND --<!-- TRIAGEBOTEND --
assign (I'm pretty sure you are working on this at )
I think they should, since this would be consistent with if/else. ()
Originally reported in: There are also some variations of it: I've made a little test case for it that doesn't rely on printf but instead can be ran as a run-pass test:
I believe the two tests in shall be comparing to . The reason is that actually extends the temporary to the end of the block at the moment, which is the desired outcome. cc
good point. What's important is that both the let-else and let-else free version are doing the same thing. Maybe one can add an additional block around the and then add a second to ensure it dropped after the block terminated? Edit: nvm, it's not possible as the .stdout files are for compiler output, not runtime output of the tests.
<details<summaryHere's another way to test, using Rc</summary</details
It might also be an idea to add a test for the inconsistency between if and if let: (no matter which side of the divide let else is on, make sure that there is no breakage of it in the future)
The above test for "drop before else block" failed on master. Reopening.
Allow me to notify that I am currently looking at how to drop values earlier inside the block.
So I was rebasing onto latest master and I wanted to share this as part of the diff: ! There has been great improvement by but it was no full fix, especially this still post-drops: prints: If you remove the it works now thanks to .
I think this is because the borrowed is assigned with extended lifetime. only breaks out towards the remainder scope. It turns out this is not sufficient. I will try to break out to further one scope up, say the parent remainder scope of the current one. I feel that this should suffice.
This is correct behavior I think. Anything accessible in the pattern needs to be in scope till the end of the block. Consider you can write . It shouldn't depend on whether the pattern matches since that is not known at compile time generally.
yeah it may not be dropped before the code after the construct terminates, but it can be dropped before the block.
I see. I think this is the non-temporary case, when the pattern doesn't match.

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.


Question:
This bug has already been identified but I wanted to open an issue for it. The following code should print first. () I believe there is an unanswered question: Should temporaries be dropped before the block? said this issue is being investigated at <!-- TRIAGEBOTSTART --<!-- TRIAGEBOTASSIGNSTART --This issue has been assigned to via . <!-- TRIAGEBOTASSIGNDATASTARTTRIAGEBOTASSIGNDATAEND --<!-- TRIAGEBOTASSIGNEND --<!-- TRIAGEBOTEND --
assign (I'm pretty sure you are working on this at )
I think they should, since this would be consistent with if/else. ()
Originally reported in: There are also some variations of it: I've made a little test case for it that doesn't rely on printf but instead can be ran as a run-pass test:
I believe the two tests in shall be comparing to . The reason is that actually extends the temporary to the end of the block at the moment, which is the desired outcome. cc
good point. What's important is that both the let-else and let-else free version are doing the same thing. Maybe one can add an additional block around the and then add a second to ensure it dropped after the block terminated? Edit: nvm, it's not possible as the .stdout files are for compiler output, not runtime output of the tests.
<details<summaryHere's another way to test, using Rc</summary</details
It might also be an idea to add a test for the inconsistency between if and if let: (no matter which side of the divide let else is on, make sure that there is no breakage of it in the future)
The above test for "drop before else block" failed on master. Reopening.
Allow me to notify that I am currently looking at how to drop values earlier inside the block.
So I was rebasing onto latest master and I wanted to share this as part of the diff: ! There has been great improvement by but it was no full fix, especially this still post-drops: prints: If you remove the it works now thanks to .
I think this is because the borrowed is assigned with extended lifetime. only breaks out towards the remainder scope. It turns out this is not sufficient. I will try to break out to further one scope up, say the parent remainder scope of the current one. I feel that this should suffice.
This is correct behavior I think. Anything accessible in the pattern needs to be in scope till the end of the block. Consider you can write . It shouldn't depend on whether the pattern matches since that is not known at compile time generally.
yeah it may not be dropped before the code after the construct terminates, but it can be dropped before the block.
I see. I think this is the non-temporary case, when the pattern doesn't match.

} 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);


Question:
This bug has already been identified but I wanted to open an issue for it. The following code should print first. () I believe there is an unanswered question: Should temporaries be dropped before the block? said this issue is being investigated at <!-- TRIAGEBOTSTART --<!-- TRIAGEBOTASSIGNSTART --This issue has been assigned to via . <!-- TRIAGEBOTASSIGNDATASTARTTRIAGEBOTASSIGNDATAEND --<!-- TRIAGEBOTASSIGNEND --<!-- TRIAGEBOTEND --
assign (I'm pretty sure you are working on this at )
I think they should, since this would be consistent with if/else. ()
Originally reported in: There are also some variations of it: I've made a little test case for it that doesn't rely on printf but instead can be ran as a run-pass test:
I believe the two tests in shall be comparing to . The reason is that actually extends the temporary to the end of the block at the moment, which is the desired outcome. cc
good point. What's important is that both the let-else and let-else free version are doing the same thing. Maybe one can add an additional block around the and then add a second to ensure it dropped after the block terminated? Edit: nvm, it's not possible as the .stdout files are for compiler output, not runtime output of the tests.
<details<summaryHere's another way to test, using Rc</summary</details
It might also be an idea to add a test for the inconsistency between if and if let: (no matter which side of the divide let else is on, make sure that there is no breakage of it in the future)
The above test for "drop before else block" failed on master. Reopening.
Allow me to notify that I am currently looking at how to drop values earlier inside the block.
So I was rebasing onto latest master and I wanted to share this as part of the diff: ! There has been great improvement by but it was no full fix, especially this still post-drops: prints: If you remove the it works now thanks to .
I think this is because the borrowed is assigned with extended lifetime. only breaks out towards the remainder scope. It turns out this is not sufficient. I will try to break out to further one scope up, say the parent remainder scope of the current one. I feel that this should suffice.
This is correct behavior I think. Anything accessible in the pattern needs to be in scope till the end of the block. Consider you can write . It shouldn't depend on whether the pattern matches since that is not known at compile time generally.
yeah it may not be dropped before the code after the construct terminates, but it can be dropped before the block.
I see. I think this is the non-temporary case, when the pattern doesn't match.

<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>


Question:
This bug has already been identified but I wanted to open an issue for it. The following code should print first. () I believe there is an unanswered question: Should temporaries be dropped before the block? said this issue is being investigated at <!-- TRIAGEBOTSTART --<!-- TRIAGEBOTASSIGNSTART --This issue has been assigned to via . <!-- TRIAGEBOTASSIGNDATASTARTTRIAGEBOTASSIGNDATAEND --<!-- TRIAGEBOTASSIGNEND --<!-- TRIAGEBOTEND --
assign (I'm pretty sure you are working on this at )
I think they should, since this would be consistent with if/else. ()
Originally reported in: There are also some variations of it: I've made a little test case for it that doesn't rely on printf but instead can be ran as a run-pass test:
I believe the two tests in shall be comparing to . The reason is that actually extends the temporary to the end of the block at the moment, which is the desired outcome. cc
good point. What's important is that both the let-else and let-else free version are doing the same thing. Maybe one can add an additional block around the and then add a second to ensure it dropped after the block terminated? Edit: nvm, it's not possible as the .stdout files are for compiler output, not runtime output of the tests.
<details<summaryHere's another way to test, using Rc</summary</details
It might also be an idea to add a test for the inconsistency between if and if let: (no matter which side of the divide let else is on, make sure that there is no breakage of it in the future)
The above test for "drop before else block" failed on master. Reopening.
Allow me to notify that I am currently looking at how to drop values earlier inside the block.
So I was rebasing onto latest master and I wanted to share this as part of the diff: ! There has been great improvement by but it was no full fix, especially this still post-drops: prints: If you remove the it works now thanks to .
I think this is because the borrowed is assigned with extended lifetime. only breaks out towards the remainder scope. It turns out this is not sufficient. I will try to break out to further one scope up, say the parent remainder scope of the current one. I feel that this should suffice.
This is correct behavior I think. Anything accessible in the pattern needs to be in scope till the end of the block. Consider you can write . It shouldn't depend on whether the pattern matches since that is not known at compile time generally.
yeah it may not be dropped before the code after the construct terminates, but it can be dropped before the block.
I see. I think this is the non-temporary case, when the pattern doesn't match.

// 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);


Question:
This bug has already been identified but I wanted to open an issue for it. The following code should print first. () I believe there is an unanswered question: Should temporaries be dropped before the block? said this issue is being investigated at <!-- TRIAGEBOTSTART --<!-- TRIAGEBOTASSIGNSTART --This issue has been assigned to via . <!-- TRIAGEBOTASSIGNDATASTARTTRIAGEBOTASSIGNDATAEND --<!-- TRIAGEBOTASSIGNEND --<!-- TRIAGEBOTEND --
assign (I'm pretty sure you are working on this at )
I think they should, since this would be consistent with if/else. ()
Originally reported in: There are also some variations of it: I've made a little test case for it that doesn't rely on printf but instead can be ran as a run-pass test:
I believe the two tests in shall be comparing to . The reason is that actually extends the temporary to the end of the block at the moment, which is the desired outcome. cc
good point. What's important is that both the let-else and let-else free version are doing the same thing. Maybe one can add an additional block around the and then add a second to ensure it dropped after the block terminated? Edit: nvm, it's not possible as the .stdout files are for compiler output, not runtime output of the tests.
<details<summaryHere's another way to test, using Rc</summary</details
It might also be an idea to add a test for the inconsistency between if and if let: (no matter which side of the divide let else is on, make sure that there is no breakage of it in the future)
The above test for "drop before else block" failed on master. Reopening.
Allow me to notify that I am currently looking at how to drop values earlier inside the block.
So I was rebasing onto latest master and I wanted to share this as part of the diff: ! There has been great improvement by but it was no full fix, especially this still post-drops: prints: If you remove the it works now thanks to .
I think this is because the borrowed is assigned with extended lifetime. only breaks out towards the remainder scope. It turns out this is not sufficient. I will try to break out to further one scope up, say the parent remainder scope of the current one. I feel that this should suffice.
This is correct behavior I think. Anything accessible in the pattern needs to be in scope till the end of the block. Consider you can write . It shouldn't depend on whether the pattern matches since that is not known at compile time generally.
yeah it may not be dropped before the code after the construct terminates, but it can be dropped before the block.
I see. I think this is the non-temporary case, when the pattern doesn't match.

} } <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> }


Question:
<!-- Thank you for finding an Internal Compiler Error! If possible, try to provide a minimal verifiable example. You can read "Rust Bug Minimization Patterns" for how to create smaller examples. --After more testing, this only reproduces consistently under cmd, as other Windows shells have diverging behavior about what it means to close a pipe via program termination does not panic, but this appears to be not because of rustc doing anything different, but because finishes writing to stdout before it is closed. Note: never occurs under , as rustc sets to . <details<summary<coderustcdriver::setsigpipehandler</code </details<!-- If you're using the stable version of the compiler, you should also check if the bug also exists in the beta or nightly versions. --: <!-- Include a backtrace in the code block by setting in your environment. E.g. . --<details<summary<strongBacktrace</strong</summary<p</p</details
label +O-Windows
This is a consequence of using the print series of macros. They ignore errors but otherwise they're designed to panic on I/O errors. The panic could be handled in a global . Or by using and handling the error in place.
rustc explicitly chooses to override the startup code (which installs a SIGPIPE handler of SIGIGN) and reinstall a handler of SIGDFL: This means that rustc halts via SIGPIPE on POSIXy platforms and a broken pipe. To match this behavior on non-POSIX platforms, rustc should probably avoid the family of macros, and use a custom version which s on . What makes this interesting to me is that does terminate quietly on Windows in the face of a broken pipe; it's only style help printing which ICEs. The Zulip thread about is highly relevant here. The provides a platform-specific (a la ) to choose the SIGPIPE behavior between inherit/SIGDFL/SIG_IGN.
Oh, that is interesting and a bit of an odd difference. Should be easily fixed though? I mean if rustc already has code to print without ICEs.
<details<summary<code--help</code </details<details<summary<code-C help</code </detailsThere's nothing different going on here; is used in both paths. Instead, what I think is happening is buffering. I wrote a little test program: And running it I get (All runs here are done in the Windows Terminal terminal emulator with a shell of nushell, which as of 0.64.0 is using cmd to run non-builtin commands. I did a smaller number of tests directly in cmd when I realized that this may be impacting the results, and observed equivalent behavior.) The number of s I get is variable, though: I've seen as low as and as high as . (I even saw with in nushell...) <details<summarySome other runs with <eminteresting</emqualities</summaryThese commands were run directly into a cmd shell, still with Windows Terminal as the terminal emulator: In the limited trials I did, I didn't see any skipped iterations directly in cmd. </detailsSomething interesting is going on here: if you'll note, the highlighted example is missing and , despite stderr locking saying that this shouldn't be possible. This isn't a one-off error or just copy/paste error, either; this kind of data loss happened multiple times (see above dropdown for some cherry-picked trial runs). In any case, changing the loop to results in a quiet exit every time. The reason exits quietly thus seems to be not because that code path is handling , but because Windows isn't returning a broken pipe error quickly. Now, I realized after collecting most of the above data that the shell may be impacting the result... Observed differences: nushell turns into roughly . This is why results in a broken pipe. cmd and pwsh parse and do command lookup before executing the first part of a pipeline. This means e.g. <subpwsh</subfails before calling at all. pwsh is really an odd one out here... (this continues infinitely.) Eerily, it seems that pwsh treats a pipe-to-finished-program as an infinite sink, not a broken pipe! This means that in pwsh, and inputting will hang until you CTRL-C the pipeline. In cmd, nushell-on-cmd, and Git Bash For completeness sake, under Git Bash (a MINGW64), consistently gives . Additionally, under all tested shells (Git Bash, pwsh, cmd, nushell-on-cmd), doing the equivalent of and CTRL-Cing the pipeline exited without a panic, as did the equivalent of [^8]. (As opposed to on a properly POSIX shell with SIGPIPE, where AIUI non-final entries in the pipeline get SIGPIPE and continue running if they SIGIGN it, rather than receiving SIGINT? Leading to panics?) [^8]: New info! Most of the time in Git Bash hangs until CTRL-C exits without a panic. ( is not accepted by this pipeline.) But sometimes when I've just run with piping to (with or without ) will result in , and once I even just got with no more info. (I'm still running the same pc-windows-msvc build from MINGW.)

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);


Question:
There are many examples where produces worse code than it should (worse than you'd write by hand, at the very least, and in the case of slices, worse then I think it needs to). You can often work around this by going through a pointer, although this is surprising, and not always an option (for example, it is not an option for users of ). Here's a minimal example extracted from some real code but massaged somewhat: For posterity, the generated code here currently is: (And here is actually better than I was getting in my real code, where it ended up as a branch. That said, this is bad enough to demonstrate the problem). I guess this is because gets passed a which itself may not be aligned to (for example, can't be satisfied). That problem is avoided by going through u8 (as is done with ), since that should always succeed (for non-const which isn't relevant here), and hits the special case to fix ). However, in this case the compiler should know that the pointer is aligned to 4, since it comes from a slice (and the pointer does end up with in the LLVM). I think this means there's no actual reason that must be less efficient than (unless I'm missing something), and the issue is just that our impl doesn't have a fast path for this situation. That said, maybe I'm wrong, and the generated code is bad for some other reason. Either way, it would be very nice for this to do better for the slice use case -- as I mentioned, going through is not viable when is invoked from an API like (I suspect if we can't fix it for , we probably could work around it in more directly, but it would be better to do it in if possible. That said, if I'm wrong about why this is happening, all bets are certainly off). See also , which seems related (and my hunch is that it will be fixed if we fix this issue, at least in this case). CC since . P.S. Sorry that this is a bit of a rambling issue. <!-- TRIAGEBOTSTART --<!-- TRIAGEBOTASSIGNSTART --<!-- TRIAGEBOTASSIGNDATASTARTTRIAGEBOTASSIGNDATAEND --<!-- TRIAGEBOTASSIGNEND --<!-- TRIAGEBOTEND --
Saw, got interested. Some drive-by notes: is exactly the codegen plus an to multiply it by the stride. which is used to gate is necessarily not constant folded. Interestingly, (edit: forgot "removing") the fast path thus seems to and . Everything not derived from successfully constant folds. This , even at -O1, with no changes to . (EDIT: on mobile and can't edit on godbolt, but: the linked code implements incorrectly as where it should be . In this case this does not change the results; the UB results in the optimizer assuming the slice is length zero but this has no impact on the pointer aligning.) I figured this out while trying and failing to add a fast path for . I tried delegating inside which I couldn't get to work, then had the idea to just do it from the outside. Even inside , a is correctly constant folded, so it should be possible to improve perf internally, but I couldn't figure out how to add a correct fast path for that case.
Thanks for the report. I think I have an idea how this can be improved, but I need to think about it a little further to see if it really is correct in all instances and to evaluate if it the tradeoffs in code quality are going to be positive enough to warrant a change I’m thinking of. claim

// 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:


Question:
There are many examples where produces worse code than it should (worse than you'd write by hand, at the very least, and in the case of slices, worse then I think it needs to). You can often work around this by going through a pointer, although this is surprising, and not always an option (for example, it is not an option for users of ). Here's a minimal example extracted from some real code but massaged somewhat: For posterity, the generated code here currently is: (And here is actually better than I was getting in my real code, where it ended up as a branch. That said, this is bad enough to demonstrate the problem). I guess this is because gets passed a which itself may not be aligned to (for example, can't be satisfied). That problem is avoided by going through u8 (as is done with ), since that should always succeed (for non-const which isn't relevant here), and hits the special case to fix ). However, in this case the compiler should know that the pointer is aligned to 4, since it comes from a slice (and the pointer does end up with in the LLVM). I think this means there's no actual reason that must be less efficient than (unless I'm missing something), and the issue is just that our impl doesn't have a fast path for this situation. That said, maybe I'm wrong, and the generated code is bad for some other reason. Either way, it would be very nice for this to do better for the slice use case -- as I mentioned, going through is not viable when is invoked from an API like (I suspect if we can't fix it for , we probably could work around it in more directly, but it would be better to do it in if possible. That said, if I'm wrong about why this is happening, all bets are certainly off). See also , which seems related (and my hunch is that it will be fixed if we fix this issue, at least in this case). CC since . P.S. Sorry that this is a bit of a rambling issue. <!-- TRIAGEBOTSTART --<!-- TRIAGEBOTASSIGNSTART --<!-- TRIAGEBOTASSIGNDATASTARTTRIAGEBOTASSIGNDATAEND --<!-- TRIAGEBOTASSIGNEND --<!-- TRIAGEBOTEND --
Saw, got interested. Some drive-by notes: is exactly the codegen plus an to multiply it by the stride. which is used to gate is necessarily not constant folded. Interestingly, (edit: forgot "removing") the fast path thus seems to and . Everything not derived from successfully constant folds. This , even at -O1, with no changes to . (EDIT: on mobile and can't edit on godbolt, but: the linked code implements incorrectly as where it should be . In this case this does not change the results; the UB results in the optimizer assuming the slice is length zero but this has no impact on the pointer aligning.) I figured this out while trying and failing to add a fast path for . I tried delegating inside which I couldn't get to work, then had the idea to just do it from the outside. Even inside , a is correctly constant folded, so it should be possible to improve perf internally, but I couldn't figure out how to add a correct fast path for that case.
Thanks for the report. I think I have an idea how this can be improved, but I need to think about it a little further to see if it really is correct in all instances and to evaluate if it the tradeoffs in code quality are going to be positive enough to warrant a change I’m thinking of. claim

} } <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:


Question:
There are many examples where produces worse code than it should (worse than you'd write by hand, at the very least, and in the case of slices, worse then I think it needs to). You can often work around this by going through a pointer, although this is surprising, and not always an option (for example, it is not an option for users of ). Here's a minimal example extracted from some real code but massaged somewhat: For posterity, the generated code here currently is: (And here is actually better than I was getting in my real code, where it ended up as a branch. That said, this is bad enough to demonstrate the problem). I guess this is because gets passed a which itself may not be aligned to (for example, can't be satisfied). That problem is avoided by going through u8 (as is done with ), since that should always succeed (for non-const which isn't relevant here), and hits the special case to fix ). However, in this case the compiler should know that the pointer is aligned to 4, since it comes from a slice (and the pointer does end up with in the LLVM). I think this means there's no actual reason that must be less efficient than (unless I'm missing something), and the issue is just that our impl doesn't have a fast path for this situation. That said, maybe I'm wrong, and the generated code is bad for some other reason. Either way, it would be very nice for this to do better for the slice use case -- as I mentioned, going through is not viable when is invoked from an API like (I suspect if we can't fix it for , we probably could work around it in more directly, but it would be better to do it in if possible. That said, if I'm wrong about why this is happening, all bets are certainly off). See also , which seems related (and my hunch is that it will be fixed if we fix this issue, at least in this case). CC since . P.S. Sorry that this is a bit of a rambling issue. <!-- TRIAGEBOTSTART --<!-- TRIAGEBOTASSIGNSTART --<!-- TRIAGEBOTASSIGNDATASTARTTRIAGEBOTASSIGNDATAEND --<!-- TRIAGEBOTASSIGNEND --<!-- TRIAGEBOTEND --
Saw, got interested. Some drive-by notes: is exactly the codegen plus an to multiply it by the stride. which is used to gate is necessarily not constant folded. Interestingly, (edit: forgot "removing") the fast path thus seems to and . Everything not derived from successfully constant folds. This , even at -O1, with no changes to . (EDIT: on mobile and can't edit on godbolt, but: the linked code implements incorrectly as where it should be . In this case this does not change the results; the UB results in the optimizer assuming the slice is length zero but this has no impact on the pointer aligning.) I figured this out while trying and failing to add a fast path for . I tried delegating inside which I couldn't get to work, then had the idea to just do it from the outside. Even inside , a is correctly constant folded, so it should be possible to improve perf internally, but I couldn't figure out how to add a correct fast path for that case.
Thanks for the report. I think I have an idea how this can be improved, but I need to think about it a little further to see if it really is correct in all instances and to evaluate if it the tradeoffs in code quality are going to be positive enough to warrant a change I’m thinking of. claim

// ` 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`.


Question:
There are many examples where produces worse code than it should (worse than you'd write by hand, at the very least, and in the case of slices, worse then I think it needs to). You can often work around this by going through a pointer, although this is surprising, and not always an option (for example, it is not an option for users of ). Here's a minimal example extracted from some real code but massaged somewhat: For posterity, the generated code here currently is: (And here is actually better than I was getting in my real code, where it ended up as a branch. That said, this is bad enough to demonstrate the problem). I guess this is because gets passed a which itself may not be aligned to (for example, can't be satisfied). That problem is avoided by going through u8 (as is done with ), since that should always succeed (for non-const which isn't relevant here), and hits the special case to fix ). However, in this case the compiler should know that the pointer is aligned to 4, since it comes from a slice (and the pointer does end up with in the LLVM). I think this means there's no actual reason that must be less efficient than (unless I'm missing something), and the issue is just that our impl doesn't have a fast path for this situation. That said, maybe I'm wrong, and the generated code is bad for some other reason. Either way, it would be very nice for this to do better for the slice use case -- as I mentioned, going through is not viable when is invoked from an API like (I suspect if we can't fix it for , we probably could work around it in more directly, but it would be better to do it in if possible. That said, if I'm wrong about why this is happening, all bets are certainly off). See also , which seems related (and my hunch is that it will be fixed if we fix this issue, at least in this case). CC since . P.S. Sorry that this is a bit of a rambling issue. <!-- TRIAGEBOTSTART --<!-- TRIAGEBOTASSIGNSTART --<!-- TRIAGEBOTASSIGNDATASTARTTRIAGEBOTASSIGNDATAEND --<!-- TRIAGEBOTASSIGNEND --<!-- TRIAGEBOTEND --
Saw, got interested. Some drive-by notes: is exactly the codegen plus an to multiply it by the stride. which is used to gate is necessarily not constant folded. Interestingly, (edit: forgot "removing") the fast path thus seems to and . Everything not derived from successfully constant folds. This , even at -O1, with no changes to . (EDIT: on mobile and can't edit on godbolt, but: the linked code implements incorrectly as where it should be . In this case this does not change the results; the UB results in the optimizer assuming the slice is length zero but this has no impact on the pointer aligning.) I figured this out while trying and failing to add a fast path for . I tried delegating inside which I couldn't get to work, then had the idea to just do it from the outside. Even inside , a is correctly constant folded, so it should be possible to improve perf internally, but I couldn't figure out how to add a correct fast path for that case.
Thanks for the report. I think I have an idea how this can be improved, but I need to think about it a little further to see if it really is correct in all instances and to evaluate if it the tradeoffs in code quality are going to be positive enough to warrant a change I’m thinking of. claim

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;


Question:
There are many examples where produces worse code than it should (worse than you'd write by hand, at the very least, and in the case of slices, worse then I think it needs to). You can often work around this by going through a pointer, although this is surprising, and not always an option (for example, it is not an option for users of ). Here's a minimal example extracted from some real code but massaged somewhat: For posterity, the generated code here currently is: (And here is actually better than I was getting in my real code, where it ended up as a branch. That said, this is bad enough to demonstrate the problem). I guess this is because gets passed a which itself may not be aligned to (for example, can't be satisfied). That problem is avoided by going through u8 (as is done with ), since that should always succeed (for non-const which isn't relevant here), and hits the special case to fix ). However, in this case the compiler should know that the pointer is aligned to 4, since it comes from a slice (and the pointer does end up with in the LLVM). I think this means there's no actual reason that must be less efficient than (unless I'm missing something), and the issue is just that our impl doesn't have a fast path for this situation. That said, maybe I'm wrong, and the generated code is bad for some other reason. Either way, it would be very nice for this to do better for the slice use case -- as I mentioned, going through is not viable when is invoked from an API like (I suspect if we can't fix it for , we probably could work around it in more directly, but it would be better to do it in if possible. That said, if I'm wrong about why this is happening, all bets are certainly off). See also , which seems related (and my hunch is that it will be fixed if we fix this issue, at least in this case). CC since . P.S. Sorry that this is a bit of a rambling issue. <!-- TRIAGEBOTSTART --<!-- TRIAGEBOTASSIGNSTART --<!-- TRIAGEBOTASSIGNDATASTARTTRIAGEBOTASSIGNDATAEND --<!-- TRIAGEBOTASSIGNEND --<!-- TRIAGEBOTEND --
Saw, got interested. Some drive-by notes: is exactly the codegen plus an to multiply it by the stride. which is used to gate is necessarily not constant folded. Interestingly, (edit: forgot "removing") the fast path thus seems to and . Everything not derived from successfully constant folds. This , even at -O1, with no changes to . (EDIT: on mobile and can't edit on godbolt, but: the linked code implements incorrectly as where it should be . In this case this does not change the results; the UB results in the optimizer assuming the slice is length zero but this has no impact on the pointer aligning.) I figured this out while trying and failing to add a fast path for . I tried delegating inside which I couldn't get to work, then had the idea to just do it from the outside. Even inside , a is correctly constant folded, so it should be possible to improve perf internally, but I couldn't figure out how to add a correct fast path for that case.
Thanks for the report. I think I have an idea how this can be improved, but I need to think about it a little further to see if it really is correct in all instances and to evaluate if it the tradeoffs in code quality are going to be positive enough to warrant a change I’m thinking of. claim

} #[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;


Question:
There are many examples where produces worse code than it should (worse than you'd write by hand, at the very least, and in the case of slices, worse then I think it needs to). You can often work around this by going through a pointer, although this is surprising, and not always an option (for example, it is not an option for users of ). Here's a minimal example extracted from some real code but massaged somewhat: For posterity, the generated code here currently is: (And here is actually better than I was getting in my real code, where it ended up as a branch. That said, this is bad enough to demonstrate the problem). I guess this is because gets passed a which itself may not be aligned to (for example, can't be satisfied). That problem is avoided by going through u8 (as is done with ), since that should always succeed (for non-const which isn't relevant here), and hits the special case to fix ). However, in this case the compiler should know that the pointer is aligned to 4, since it comes from a slice (and the pointer does end up with in the LLVM). I think this means there's no actual reason that must be less efficient than (unless I'm missing something), and the issue is just that our impl doesn't have a fast path for this situation. That said, maybe I'm wrong, and the generated code is bad for some other reason. Either way, it would be very nice for this to do better for the slice use case -- as I mentioned, going through is not viable when is invoked from an API like (I suspect if we can't fix it for , we probably could work around it in more directly, but it would be better to do it in if possible. That said, if I'm wrong about why this is happening, all bets are certainly off). See also , which seems related (and my hunch is that it will be fixed if we fix this issue, at least in this case). CC since . P.S. Sorry that this is a bit of a rambling issue. <!-- TRIAGEBOTSTART --<!-- TRIAGEBOTASSIGNSTART --<!-- TRIAGEBOTASSIGNDATASTARTTRIAGEBOTASSIGNDATAEND --<!-- TRIAGEBOTASSIGNEND --<!-- TRIAGEBOTEND --
Saw, got interested. Some drive-by notes: is exactly the codegen plus an to multiply it by the stride. which is used to gate is necessarily not constant folded. Interestingly, (edit: forgot "removing") the fast path thus seems to and . Everything not derived from successfully constant folds. This , even at -O1, with no changes to . (EDIT: on mobile and can't edit on godbolt, but: the linked code implements incorrectly as where it should be . In this case this does not change the results; the UB results in the optimizer assuming the slice is length zero but this has no impact on the pointer aligning.) I figured this out while trying and failing to add a fast path for . I tried delegating inside which I couldn't get to work, then had the idea to just do it from the outside. Even inside , a is correctly constant folded, so it should be possible to improve perf internally, but I couldn't figure out how to add a correct fast path for that case.
Thanks for the report. I think I have an idea how this can be improved, but I need to think about it a little further to see if it really is correct in all instances and to evaluate if it the tradeoffs in code quality are going to be positive enough to warrant a change I’m thinking of. claim

} #[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>.


Question:
There are many examples where produces worse code than it should (worse than you'd write by hand, at the very least, and in the case of slices, worse then I think it needs to). You can often work around this by going through a pointer, although this is surprising, and not always an option (for example, it is not an option for users of ). Here's a minimal example extracted from some real code but massaged somewhat: For posterity, the generated code here currently is: (And here is actually better than I was getting in my real code, where it ended up as a branch. That said, this is bad enough to demonstrate the problem). I guess this is because gets passed a which itself may not be aligned to (for example, can't be satisfied). That problem is avoided by going through u8 (as is done with ), since that should always succeed (for non-const which isn't relevant here), and hits the special case to fix ). However, in this case the compiler should know that the pointer is aligned to 4, since it comes from a slice (and the pointer does end up with in the LLVM). I think this means there's no actual reason that must be less efficient than (unless I'm missing something), and the issue is just that our impl doesn't have a fast path for this situation. That said, maybe I'm wrong, and the generated code is bad for some other reason. Either way, it would be very nice for this to do better for the slice use case -- as I mentioned, going through is not viable when is invoked from an API like (I suspect if we can't fix it for , we probably could work around it in more directly, but it would be better to do it in if possible. That said, if I'm wrong about why this is happening, all bets are certainly off). See also , which seems related (and my hunch is that it will be fixed if we fix this issue, at least in this case). CC since . P.S. Sorry that this is a bit of a rambling issue. <!-- TRIAGEBOTSTART --<!-- TRIAGEBOTASSIGNSTART --<!-- TRIAGEBOTASSIGNDATASTARTTRIAGEBOTASSIGNDATAEND --<!-- TRIAGEBOTASSIGNEND --<!-- TRIAGEBOTEND --
Saw, got interested. Some drive-by notes: is exactly the codegen plus an to multiply it by the stride. which is used to gate is necessarily not constant folded. Interestingly, (edit: forgot "removing") the fast path thus seems to and . Everything not derived from successfully constant folds. This , even at -O1, with no changes to . (EDIT: on mobile and can't edit on godbolt, but: the linked code implements incorrectly as where it should be . In this case this does not change the results; the UB results in the optimizer assuming the slice is length zero but this has no impact on the pointer aligning.) I figured this out while trying and failing to add a fast path for . I tried delegating inside which I couldn't get to work, then had the idea to just do it from the outside. Even inside , a is correctly constant folded, so it should be possible to improve perf internally, but I couldn't figure out how to add a correct fast path for that case.
Thanks for the report. I think I have an idea how this can be improved, but I need to think about it a little further to see if it really is correct in all instances and to evaluate if it the tradeoffs in code quality are going to be positive enough to warrant a change I’m thinking of. claim

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();


Question:
There are many examples where produces worse code than it should (worse than you'd write by hand, at the very least, and in the case of slices, worse then I think it needs to). You can often work around this by going through a pointer, although this is surprising, and not always an option (for example, it is not an option for users of ). Here's a minimal example extracted from some real code but massaged somewhat: For posterity, the generated code here currently is: (And here is actually better than I was getting in my real code, where it ended up as a branch. That said, this is bad enough to demonstrate the problem). I guess this is because gets passed a which itself may not be aligned to (for example, can't be satisfied). That problem is avoided by going through u8 (as is done with ), since that should always succeed (for non-const which isn't relevant here), and hits the special case to fix ). However, in this case the compiler should know that the pointer is aligned to 4, since it comes from a slice (and the pointer does end up with in the LLVM). I think this means there's no actual reason that must be less efficient than (unless I'm missing something), and the issue is just that our impl doesn't have a fast path for this situation. That said, maybe I'm wrong, and the generated code is bad for some other reason. Either way, it would be very nice for this to do better for the slice use case -- as I mentioned, going through is not viable when is invoked from an API like (I suspect if we can't fix it for , we probably could work around it in more directly, but it would be better to do it in if possible. That said, if I'm wrong about why this is happening, all bets are certainly off). See also , which seems related (and my hunch is that it will be fixed if we fix this issue, at least in this case). CC since . P.S. Sorry that this is a bit of a rambling issue. <!-- TRIAGEBOTSTART --<!-- TRIAGEBOTASSIGNSTART --<!-- TRIAGEBOTASSIGNDATASTARTTRIAGEBOTASSIGNDATAEND --<!-- TRIAGEBOTASSIGNEND --<!-- TRIAGEBOTEND --
Saw, got interested. Some drive-by notes: is exactly the codegen plus an to multiply it by the stride. which is used to gate is necessarily not constant folded. Interestingly, (edit: forgot "removing") the fast path thus seems to and . Everything not derived from successfully constant folds. This , even at -O1, with no changes to . (EDIT: on mobile and can't edit on godbolt, but: the linked code implements incorrectly as where it should be . In this case this does not change the results; the UB results in the optimizer assuming the slice is length zero but this has no impact on the pointer aligning.) I figured this out while trying and failing to add a fast path for . I tried delegating inside which I couldn't get to work, then had the idea to just do it from the outside. Even inside , a is correctly constant folded, so it should be possible to improve perf internally, but I couldn't figure out how to add a correct fast path for that case.
Thanks for the report. I think I have an idea how this can be improved, but I need to think about it a little further to see if it really is correct in all instances and to evaluate if it the tradeoffs in code quality are going to be positive enough to warrant a change I’m thinking of. claim

<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>


Question:
<!-- Thank you for filing a bug report! Please provide a short summary of the bug, along with any information you feel relevant to replicating the bug. If you cannot produce a minimal reproduction case (something that would work in isolation), please provide the steps or even link to a repository that causes the problematic output to occur. --There are two crates here, and (which depends on ). library: consumer: The current output is (when running on ): <!-- The following is not always necessary. --Ideally the output should look like: We should not be suggesting private types to users, since macro hygiene does mean they can't write This is on the latest nightly, This was ran into in
this only happens with items and not ?
I included examples of and , and it seems to happen for both (see: )
Oh, my bad. I didn't read closely, my eyes just went directly to the macro item. Sorry. :sweat_smile:
For the record, this is not a macro problem. See that does not use macros: /// fn needsmeow<T: playground::Meow(t: T) {} /// fn main() { needsmeow(1usize); } /// (uses a doc comment to emulate two crates on playground) The reason is that  does not do any filtering for type privacy.

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


Question:
<!-- Thank you for filing a bug report! Please provide a short summary of the bug, along with any information you feel relevant to replicating the bug. If you cannot produce a minimal reproduction case (something that would work in isolation), please provide the steps or even link to a repository that causes the problematic output to occur. --There are two crates here, and (which depends on ). library: consumer: The current output is (when running on ): <!-- The following is not always necessary. --Ideally the output should look like: We should not be suggesting private types to users, since macro hygiene does mean they can't write This is on the latest nightly, This was ran into in
this only happens with items and not ?
I included examples of and , and it seems to happen for both (see: )
Oh, my bad. I didn't read closely, my eyes just went directly to the macro item. Sorry. :sweat_smile:
For the record, this is not a macro problem. See that does not use macros: /// fn needsmeow<T: playground::Meow(t: T) {} /// fn main() { needsmeow(1usize); } /// (uses a doc comment to emulate two crates on playground) The reason is that  does not do any filtering for type privacy.

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


Question:
<!-- Thank you for filing a bug report! Please provide a short summary of the bug, along with any information you feel relevant to replicating the bug. If you cannot produce a minimal reproduction case (something that would work in isolation), please provide the steps or even link to a repository that causes the problematic output to occur. --There are two crates here, and (which depends on ). library: consumer: The current output is (when running on ): <!-- The following is not always necessary. --Ideally the output should look like: We should not be suggesting private types to users, since macro hygiene does mean they can't write This is on the latest nightly, This was ran into in
this only happens with items and not ?
I included examples of and , and it seems to happen for both (see: )
Oh, my bad. I didn't read closely, my eyes just went directly to the macro item. Sorry. :sweat_smile:
For the record, this is not a macro problem. See that does not use macros: /// fn needsmeow<T: playground::Meow(t: T) {} /// fn main() { needsmeow(1usize); } /// (uses a doc comment to emulate two crates on playground) The reason is that  does not do any filtering for type privacy.

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


Question:
<!-- Thank you for filing a bug report! Please provide a short summary of the bug, along with any information you feel relevant to replicating the bug. If you cannot produce a minimal reproduction case (something that would work in isolation), please provide the steps or even link to a repository that causes the problematic output to occur. --There are two crates here, and (which depends on ). library: consumer: The current output is (when running on ): <!-- The following is not always necessary. --Ideally the output should look like: We should not be suggesting private types to users, since macro hygiene does mean they can't write This is on the latest nightly, This was ran into in
this only happens with items and not ?
I included examples of and , and it seems to happen for both (see: )
Oh, my bad. I didn't read closely, my eyes just went directly to the macro item. Sorry. :sweat_smile:
For the record, this is not a macro problem. See that does not use macros: /// fn needsmeow<T: playground::Meow(t: T) {} /// fn main() { needsmeow(1usize); } /// (uses a doc comment to emulate two crates on playground) The reason is that  does not do any filtering for type privacy.

&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| {


Question:
<!-- Thank you for filing a bug report! Please provide a short summary of the bug, along with any information you feel relevant to replicating the bug. If you cannot produce a minimal reproduction case (something that would work in isolation), please provide the steps or even link to a repository that causes the problematic output to occur. --There are two crates here, and (which depends on ). library: consumer: The current output is (when running on ): <!-- The following is not always necessary. --Ideally the output should look like: We should not be suggesting private types to users, since macro hygiene does mean they can't write This is on the latest nightly, This was ran into in
this only happens with items and not ?
I included examples of and , and it seems to happen for both (see: )
Oh, my bad. I didn't read closely, my eyes just went directly to the macro item. Sorry. :sweat_smile:
For the record, this is not a macro problem. See that does not use macros: /// fn needsmeow<T: playground::Meow(t: T) {} /// fn main() { needsmeow(1usize); } /// (uses a doc comment to emulate two crates on playground) The reason is that  does not do any filtering for type privacy.

|| 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); }


Question:
<!-- Thank you for filing a bug report! Please provide a short summary of the bug, along with any information you feel relevant to replicating the bug. If you cannot produce a minimal reproduction case (something that would work in isolation), please provide the steps or even link to a repository that causes the problematic output to occur. --There are two crates here, and (which depends on ). library: consumer: The current output is (when running on ): <!-- The following is not always necessary. --Ideally the output should look like: We should not be suggesting private types to users, since macro hygiene does mean they can't write This is on the latest nightly, This was ran into in
this only happens with items and not ?
I included examples of and , and it seems to happen for both (see: )
Oh, my bad. I didn't read closely, my eyes just went directly to the macro item. Sorry. :sweat_smile:
For the record, this is not a macro problem. See that does not use macros: /// fn needsmeow<T: playground::Meow(t: T) {} /// fn main() { needsmeow(1usize); } /// (uses a doc comment to emulate two crates on playground) The reason is that  does not do any filtering for type privacy.

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


Question:
<!-- Thank you for filing a bug report! Please provide a short summary of the bug, along with any information you feel relevant to replicating the bug. If you cannot produce a minimal reproduction case (something that would work in isolation), please provide the steps or even link to a repository that causes the problematic output to occur. --There are two crates here, and (which depends on ). library: consumer: The current output is (when running on ): <!-- The following is not always necessary. --Ideally the output should look like: We should not be suggesting private types to users, since macro hygiene does mean they can't write This is on the latest nightly, This was ran into in
this only happens with items and not ?
I included examples of and , and it seems to happen for both (see: )
Oh, my bad. I didn't read closely, my eyes just went directly to the macro item. Sorry. :sweat_smile:
For the record, this is not a macro problem. See that does not use macros: /// fn needsmeow<T: playground::Meow(t: T) {} /// fn main() { needsmeow(1usize); } /// (uses a doc comment to emulate two crates on playground) The reason is that  does not do any filtering for type privacy.

<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>


Question:
<!-- Thank you for filing a bug report! Please provide a short summary of the bug, along with any information you feel relevant to replicating the bug. If you cannot produce a minimal reproduction case (something that would work in isolation), please provide the steps or even link to a repository that causes the problematic output to occur. --There are two crates here, and (which depends on ). library: consumer: The current output is (when running on ): <!-- The following is not always necessary. --Ideally the output should look like: We should not be suggesting private types to users, since macro hygiene does mean they can't write This is on the latest nightly, This was ran into in
this only happens with items and not ?
I included examples of and , and it seems to happen for both (see: )
Oh, my bad. I didn't read closely, my eyes just went directly to the macro item. Sorry. :sweat_smile:
For the record, this is not a macro problem. See that does not use macros: /// fn needsmeow<T: playground::Meow(t: T) {} /// fn main() { needsmeow(1usize); } /// (uses a doc comment to emulate two crates on playground) The reason is that  does not do any filtering for type privacy.

<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>


Question:
<!-- Thank you for filing a bug report! Please provide a short summary of the bug, along with any information you feel relevant to replicating the bug. If you cannot produce a minimal reproduction case (something that would work in isolation), please provide the steps or even link to a repository that causes the problematic output to occur. --There are two crates here, and (which depends on ). library: consumer: The current output is (when running on ): <!-- The following is not always necessary. --Ideally the output should look like: We should not be suggesting private types to users, since macro hygiene does mean they can't write This is on the latest nightly, This was ran into in
this only happens with items and not ?
I included examples of and , and it seems to happen for both (see: )
Oh, my bad. I didn't read closely, my eyes just went directly to the macro item. Sorry. :sweat_smile:
For the record, this is not a macro problem. See that does not use macros: /// fn needsmeow<T: playground::Meow(t: T) {} /// fn main() { needsmeow(1usize); } /// (uses a doc comment to emulate two crates on playground) The reason is that  does not do any filtering for type privacy.

&'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


Question:
Any methods of container types (12 in Rust 1,62.1, including duplicated ones in ), and maybe corresponding methods. - - - - ... and corresponding methods in . Documentations for describes "If the capacity overflows, or the allocator reports a failure, then an error is returned.". However, it does not give any guarantees about whether the content is preserved or not on that error. This guarantee (or absence of the guarantee) should be explicitly described.
At least for (and -based things like ) it's obvious to guarantee that, so we might as well. (Thank you, Rust, for insisting on move-is- so we don't have the "what if the move constructor panics" problems like C++.) I think it'll need to be to hashbrown before we can consider promising it for /, through -- there's no guarantee in <https://rust-
Modulo needing to add it in hashbrown, shall we see if we have consensus to say that failures preserve the current contents? merge
Team member has proposed to merge this. The next step is review by the rest of the tagged team members: [x] [x] [x] [x] [ ] [x] No concerns currently listed. Once a majority of reviewers approve (and at most 2 approvals are outstanding), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up! See for info about what commands tagged team members can give me.
:bell: This is now entering its final comment period, as per the . :bell:
The final comment period, with a disposition to merge, as per the , is now complete. As the automated representative of the governance process, I would like to thank the author for their work and everyone else who contributed. This will be merged soon.
Please feel free to post a PR for this.
Created a PR for this.

/// 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 ///


Question:
Any methods of container types (12 in Rust 1,62.1, including duplicated ones in ), and maybe corresponding methods. - - - - ... and corresponding methods in . Documentations for describes "If the capacity overflows, or the allocator reports a failure, then an error is returned.". However, it does not give any guarantees about whether the content is preserved or not on that error. This guarantee (or absence of the guarantee) should be explicitly described.
At least for (and -based things like ) it's obvious to guarantee that, so we might as well. (Thank you, Rust, for insisting on move-is- so we don't have the "what if the move constructor panics" problems like C++.) I think it'll need to be to hashbrown before we can consider promising it for /, through -- there's no guarantee in <https://rust-
Modulo needing to add it in hashbrown, shall we see if we have consensus to say that failures preserve the current contents? merge
Team member has proposed to merge this. The next step is review by the rest of the tagged team members: [x] [x] [x] [x] [ ] [x] No concerns currently listed. Once a majority of reviewers approve (and at most 2 approvals are outstanding), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up! See for info about what commands tagged team members can give me.
:bell: This is now entering its final comment period, as per the . :bell:
The final comment period, with a disposition to merge, as per the , is now complete. As the automated representative of the governance process, I would like to thank the author for their work and everyone else who contributed. This will be merged soon.
Please feel free to post a PR for this.
Created a PR for this.

/// 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 ///


Question:
Any methods of container types (12 in Rust 1,62.1, including duplicated ones in ), and maybe corresponding methods. - - - - ... and corresponding methods in . Documentations for describes "If the capacity overflows, or the allocator reports a failure, then an error is returned.". However, it does not give any guarantees about whether the content is preserved or not on that error. This guarantee (or absence of the guarantee) should be explicitly described.
At least for (and -based things like ) it's obvious to guarantee that, so we might as well. (Thank you, Rust, for insisting on move-is- so we don't have the "what if the move constructor panics" problems like C++.) I think it'll need to be to hashbrown before we can consider promising it for /, through -- there's no guarantee in <https://rust-
Modulo needing to add it in hashbrown, shall we see if we have consensus to say that failures preserve the current contents? merge
Team member has proposed to merge this. The next step is review by the rest of the tagged team members: [x] [x] [x] [x] [ ] [x] No concerns currently listed. Once a majority of reviewers approve (and at most 2 approvals are outstanding), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up! See for info about what commands tagged team members can give me.
:bell: This is now entering its final comment period, as per the . :bell:
The final comment period, with a disposition to merge, as per the , is now complete. As the automated representative of the governance process, I would like to thank the author for their work and everyone else who contributed. This will be merged soon.
Please feel free to post a PR for this.
Created a PR for this.

/// 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 ///


Question:
Any methods of container types (12 in Rust 1,62.1, including duplicated ones in ), and maybe corresponding methods. - - - - ... and corresponding methods in . Documentations for describes "If the capacity overflows, or the allocator reports a failure, then an error is returned.". However, it does not give any guarantees about whether the content is preserved or not on that error. This guarantee (or absence of the guarantee) should be explicitly described.
At least for (and -based things like ) it's obvious to guarantee that, so we might as well. (Thank you, Rust, for insisting on move-is- so we don't have the "what if the move constructor panics" problems like C++.) I think it'll need to be to hashbrown before we can consider promising it for /, through -- there's no guarantee in <https://rust-
Modulo needing to add it in hashbrown, shall we see if we have consensus to say that failures preserve the current contents? merge
Team member has proposed to merge this. The next step is review by the rest of the tagged team members: [x] [x] [x] [x] [ ] [x] No concerns currently listed. Once a majority of reviewers approve (and at most 2 approvals are outstanding), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up! See for info about what commands tagged team members can give me.
:bell: This is now entering its final comment period, as per the . :bell:
The final comment period, with a disposition to merge, as per the , is now complete. As the automated representative of the governance process, I would like to thank the author for their work and everyone else who contributed. This will be merged soon.
Please feel free to post a PR for this.
Created a PR for this.

/// 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. ///


Question:
Any methods of container types (12 in Rust 1,62.1, including duplicated ones in ), and maybe corresponding methods. - - - - ... and corresponding methods in . Documentations for describes "If the capacity overflows, or the allocator reports a failure, then an error is returned.". However, it does not give any guarantees about whether the content is preserved or not on that error. This guarantee (or absence of the guarantee) should be explicitly described.
At least for (and -based things like ) it's obvious to guarantee that, so we might as well. (Thank you, Rust, for insisting on move-is- so we don't have the "what if the move constructor panics" problems like C++.) I think it'll need to be to hashbrown before we can consider promising it for /, through -- there's no guarantee in <https://rust-
Modulo needing to add it in hashbrown, shall we see if we have consensus to say that failures preserve the current contents? merge
Team member has proposed to merge this. The next step is review by the rest of the tagged team members: [x] [x] [x] [x] [ ] [x] No concerns currently listed. Once a majority of reviewers approve (and at most 2 approvals are outstanding), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up! See for info about what commands tagged team members can give me.
:bell: This is now entering its final comment period, as per the . :bell:
The final comment period, with a disposition to merge, as per the , is now complete. As the automated representative of the governance process, I would like to thank the author for their work and everyone else who contributed. This will be merged soon.
Please feel free to post a PR for this.
Created a PR for this.

/// 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 ///


Question:
master as of . <!-- If you're using the stable version of the compiler, you should also check if the bug also exists in the beta or nightly versions. --: That's commit built with this : Note that this assertion is under : <details<summary<strongFull output</strong</summary<p</p</details
cc since you re-enabled these assertions in .
Here's a smaller reproducer: <details<summary<strongOutput</strong</summary<p</p</detailsNote that has size 1 and align 8, which seems really bad...
This can make safe misaligned references:
WG-prioritization assigning priority (). label -I-prioritize +P-critical
Looks like the assertions found a bug in layout computation? The assertions are right I think, and that layout seems very bogus. Layout was wrong already before my PR, we just didn't catch the problem.
Yeah, now that I've explored further, I agree the assertions caught a real layout bug. AIUI, the size should always be a multiple of the alignment, since we don't have separate notions of size and stride. For testing older , I simplified it to this: Up to Rust 1.23, size and align were 8, and then 1.24 dropped to size 1. That corresponds to the time that niche layout was implemented, (and dropped from 1.23 in ), although I didn't bisect that specifically. cc
Ahh, zero-sized aligned types, the bane of layout algorithms... Likely needs an audit for everywhere we special-case ZSTs and maybe just make them require align == 1 for now. In this specific case though, we could keep the niche optimization, just need to increase align of the whole type to the max (across all variants and their fields ). Then it will look like an , i.e. one byte for the either--or- plus 7 padding bytes.
I don't understand how you could require that -- must still be aligned like , right?.
Yes, and for that reason is not a ZST for many . In the WG-UCG we started using the term "1-ZST" to refer to types with size 0 and alignment 1; that's really what we usually mean when we say "ZST".
Oh I see, the places looking for ZSTs should be looking for "1-ZST", where they want no effect on layout.
Sorry for the confusion - when I wrote: I should've been more specific instead of "them", and phrased it more like: (or used the 1-ZST naming, but I completely forgot we had that) <hr/<details<summary(click to open old notes, ended up tracking down the bug and was wrong)</summaryThose special cases, FWIW, are almost always ignoring ZSTs in a larger aggregate (which is also something I should've been clearer about). For example, the one you're hitting for is most likely deciding¹ that the variant is "dataless"/C-like because it contains only ZSTs, and the layout ends up being based² off of the only "dataful" variant (), i.e. somewhere between and , without considering the "dataless" variants at all³. But I wanted to be sure I was accurate, so I checked: ¹ correct, though it only cares about being "dataful", and ignores ZST-only variants ² correct-ish (see below), e.g. this part: ( is the "dataful" variant, i.e. ) ³ wait, I'm wrong? we totally seem to be combining variant alignments: </details<hr/Oh, whoops, misunderstood the bug: alignment is so we do in fact consider for alignment, but we never adjust the size to be a multiple of alignment. ( is what I used to inspect the layout, pretty useful - also, could be replaced by in the minimal repro, just noticed) <hr/This is what tagged union layout does, after it merges variants' /: To fix the niche-filling codepath, you need to duplicate the above, just after  However, be careful around : will need to also have an guard (as have expected align/sizes determined entirely by the value and the target spec). The fact that isn't checked today for is technically wrong AFAICT, but I can't think of a way to actually exploit being larger than what is required for a specific . <sub(AFAICT the LLVM backend always makes alignments explicit where possible, and always inserts explicit padding where there are any gaps between fields, the only way any of that could go wrong is if Rust alignment was less* than the LLVM alignment for the same type, but only the opposite can be true as LLVM has less information and we also force packed LLVM types at the slightest hint of underaligned fields)</sub
Here's what I've got so far: For the example of , it ends up using a direct tag instead, still . The original build also passes all assertions with this change.
Ah, does it hit the check between niche-filling and tag, that IIRC might prefer tag (if the sizes are equal) because it likely results in better codegen/more niches in many cases? Looks like it: In that case, it should get the same layout as s/bool/u8/ (since it ends up not using the niche). , I was able to confirm that in such a tagged union gets placed at the very end (i.e. offset 8 in a size 8 type), which matches the respective handwritten struct, so I'm happy to say that all looks good. <hr/We might still want a test where the tagged form doesn't happen. I was able to emulate the fix described above by making the "cross-pollinate" alignments: Such a definition shouldn't be notably different from (modulo bugs like this very one). (note that you can't just use to emulate the fix, as that will put padding inside variant fields, instead of in between them, which can pessimize tagged layouts) With that, I was able to : Relevant parts of the output (cleaned up a bit): To be clear, you should be able to get those exact results with instead of , on your branch.
That's a nice test, thanks! And indeed, nightly currently makes those both niche with , while my branch makes them direct and niche respectively with .

.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) => {