-
Notifications
You must be signed in to change notification settings - Fork 12.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Remove drop order twist of && and || and make them associative #103293
Conversation
r? @wesleywiser (rust-highfive has picked a reviewer for you, use r? to override) |
a602b97
to
6172afd
Compare
This comment has been minimized.
This comment has been minimized.
2ea77b0
to
0e70267
Compare
This is not quite right, for example this PR would break the following program. struct Thing;
impl Drop for Thing {
fn drop(&mut self) {}
}
struct Ref<'a>(&'a Thing);
impl<'a> Drop for Ref<'a> {
fn drop(&mut self) {}
}
fn main() {
let tmp;
let _ = match Thing {
ref thing => {
tmp = thing;
true
}
} && match Ref(tmp) {
_ => false,
};
} It seems weird but it's not that much weirder than things like https://github.com/dtolnay/syn/blob/1.0.102/src/data.rs#L289-L293, so it's hard for me to gauge how widespread this might be. |
Good point, I confirm this fails with the PR applied. Text edited. |
I think this is worth a crater run, and worth attempting even if we need to fix a couple of crates; this is a substantial improvement to understandability. With this change, will that also make let chains able to use a consistent order? |
@joshtriplett indeed my investigation of how let chains work has motivated this change. The other behaviour that from my POV should be improved is the drop order of let temporaries regarding the else block. You can either choose consistency with The entire topic of what a good drop order would be would benefit from lang team input. I've made a longer description of behaviour and how I would like it to change here. I've edited the comment to reflect recent changes so it should be up to date. |
@bors try |
⌛ Trying commit 0e70267b50790094287a0d6711fab2849b9fe948 with merge 51ad93974f526eaf4ede64932870490e6c96e4fd... |
☀️ Try build successful - checks-actions |
@craterbot check |
👌 Experiment ℹ️ Crater is a tool to run experiments across parts of the Rust ecosystem. Learn more |
🚧 Experiment ℹ️ Crater is a tool to run experiments across parts of the Rust ecosystem. Learn more |
🎉 Experiment
|
So it seems that there is one build failure due to the drop order changes. It occurs in a match guard in Relevant log excerpt:
This is the relevant line on github. I don't really understand how this change can cause the build failure, as name is a const reference. All the other involved variables are references too. I don't get why the borrow should later be used. It seems like a bug in rustc to me, that I've missed something when writing my PR. It might be interesting to create a minimum reproducing example. For the other failures, most of them are either in proc macros or build.rs failures. I need to investigate more closely, but a lot of crate failures are due to libc's build.rs failing:
The code in libc doesn't use any && or || except for one place where it uses ||. I'm not sure what is going on here. I think though that many more crates depend on |
So a minimum reproducing example is fn main() {
fn is_empty(s: &str) -> bool {
s.is_empty()
}
// Doesn't fail if provided with this
//let check_val: fn(&str) -> bool = |v| is_empty(v);
let check_val = |v| is_empty(v);
let n = "Hi";
if check_val(&n.to_string()) || check_val("Hello") {}
}
And yes, on current Rust it fails if you add a check_val(&n.to_string());
check_val("Hello"); There seems to be something going on in the auto-generation of The other errors I still need to investigate. |
This is not code I am familiar with, I cannot review this.
r? compiler
|
For some reason your checkbox is unchecked, and seems like the rfcbot never actually checked it. A bug in rfcbot? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This PR is changing the behaviour of mixed &&
and ||
chains as well. Please adjust a test to exercise those scenarios as well.
Especially keep in mind the fact that operator precedence for &&
and ||
is different, so a && b || c && d
has a different evaluation order compared to e.g. a && (b || c) && d
. I believe with this change the difference cannot be observed either way, though.
src/test/ui/drop/issue-103107.rs
Outdated
@@ -0,0 +1,37 @@ | |||
// run-pass |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For this test to make sense as a run-pass test, I think it would need to assert something at the runtime. However it doesn't and we already have a test that does that (src/test/ui/drop/drop_order.rs
), so I think this should be changed to a check-pass
test.
let _ = self.option_loud_drop(7).is_none() // 1 | ||
|| self.option_loud_drop(8).is_none() // 2 | ||
|| self.option_loud_drop(9).is_none(); // 3 | ||
self.print(10); // 4 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the purpose of this print? Is it testing that the operands to the expression above are dropped before the next statement? Could you add a comment to that effect?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it testing that the operands to the expression above are dropped before the next statement?
Yes. I can add a comment that explains this, but note that printing in the body is done with tests for other constructs in the file too.
// If the LHS is not another binop itself of the same kind as ours, | ||
// we also mark it as terminating, so that in && or || chains, | ||
// the temporaries are dropped in order instead of the very first | ||
// being dropped last. For the Let exception, see below. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Style nit: it is easier to understand the compound comment if it is split and placed directly above the relevant branches.
This applies to the pre-existing comment at the top of this body as well.
ref r, | ||
) => { | ||
// For shortcircuiting operators, mark the RHS as a terminating | ||
// scope since it only executes conditionally. | ||
|
||
// If the LHS is not another binop itself of the same kind as ours, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps
// If the LHS is not another binop itself of the same kind as ours, | |
// If the LHS is not another binop itself of the same kind as the current expression, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FWIW there are a couple of implied contexts that aren’t necessarily immediately obvious. For example, why is it that only the LHS matters, and not RHS (because LAnd/LOr binary ops are left-associative.) The double negative makes this somewhat difficult to understand as well.
Something like the following might be easier to grok, perhaps?
We want to make sure that operands in expressions like
a && b && c
anda || b || c
are dropped in order. These operators are left-associative, so in order to achieve the desired behaviour we don’t want to terminate the LHS expression, if LHS is the same kind of binary operation as the current expression.
At least that’s what I understand this code to be doing.
Previously a short circuiting && chain would drop the first element after all the other elements, and otherwise follow evaluation order, so code like: f(1).g() && f(2).g() && f(3).g() && f(4).g() would drop the temporaries in the order 2,3,4,1. This made && and || non-associative regarding drop order, so adding ()'s to the expression would change drop order: f(1).g() && (f(2).g() && f(3).g()) && f(4).g() for example would drop in the order 3,2,4,1. As, except for the bool result, there is no data returned by the sub-expressions of the short circuiting binops, we can safely discard of any temporaries created by the sub-expr. Previously, code was already putting the rhs's into terminating scopes, but missed it for the lhs's. This commit addresses this "twist". In the expression, we now also put the lhs into a terminating scope. The drop order for the above expressions is 1,2,3,4 now.
0e70267
to
604af02
Compare
This avoids creation of a terminating scope in chains that contain both && and ||, because also there we know that a terminating scope is not neccessary: all the chain members are already in such terminating scopes. Also add a mixed && / || test.
604af02
to
a59a2d3
Compare
@bors r+ |
☀️ Test successful - checks-actions |
Finished benchmarking commit (19c250a): comparison URL. Overall result: no relevant changes - no action needed@rustbot label: -perf-regression Instruction countThis benchmark run did not return any relevant results for this metric. Max RSS (memory usage)This benchmark run did not return any relevant results for this metric. CyclesThis benchmark run did not return any relevant results for this metric. |
Remove drop order twist of && and || and make them associative Previously a short circuiting binop chain (chain of && or ||s) would drop the temporaries created by the first element after all the other elements, and otherwise follow evaluation order. So `f(1).g() && f(2).g() && f(3).g() && f(4).g()` would drop the temporaries in the order `2,3,4,1`. This made `&&` and `||` non-associative regarding drop order. In other words, adding ()'s to the expression would change drop order: `f(1).g() && (f(2).g() && f(3).g()) && f(4).g()` for example would drop in the order `3,2,4,1`. As, except for the bool result, there is no data returned by the sub-expressions of the short circuiting binops, we can safely discard of any temporaries created by the sub-expr. Previously, code was already putting the rhs's into terminating scopes, but missed it for the lhs's. This commit addresses this "twist". We now also put the lhs into a terminating scope. The drop order of the above expressions becomes `1,2,3,4`. There might be code relying on the current order, and therefore I'd recommend doing a crater run to gauge the impact. I'd argue that such code is already quite wonky as it is one `foo() &&` addition away from breaking. ~~For the impact, I don't expect any *build* failures, as the compiler gets strictly more tolerant: shortening the lifetime of temporaries only expands the list of programs the compiler accepts as valid. There might be *runtime* failures caused by this change however.~~ Edit: both build and runtime failures are possible, e.g. see the example provided by dtolnay [below](rust-lang/rust#103293 (comment)). Edit2: the crater run has finished and [results](rust-lang/rust#103293 (comment)) are that there is only one build failure which is easy to fix with a +/- 1 line diff. I've included a testcase that now compiles thanks to this patch. The breakage is also limited to drop order relative to conditionals in the && chain: that is, in code like this: ```Rust let hello = foo().hi() && bar().world(); println!("hi"); ``` we already drop the temporaries of `foo().hi()` before we reach "hi". I'd ideally have this PR merged before let chains are stabilized. If this PR is taking too long, I'd love to have a more restricted version of this change limited to `&&`'s in let chains: the `&&`'s of such chains are quite special anyways as they accept `let` bindings, in there the `&&` is therefore more a part of the "if let chain" construct than a construct of its own. Fixes #103107 Status: waiting on [this accepted FCP](rust-lang/rust#103293 (comment)) finishing.
Pkgsrc changes: * Adjust patches and cargo checksums to new versions, but also one strange "mips" conditional. Upstream changes: Version 1.67.0 (2023-01-26) ========================== Language -------- - [Make `Sized` predicates coinductive, allowing cycles.] (rust-lang/rust#100386) - [`#[must_use]` annotations on `async fn` also affect the `Future::Output`.] (rust-lang/rust#100633) - [Elaborate supertrait obligations when deducing closure signatures.] (rust-lang/rust#101834) - [Invalid literals are no longer an error under `cfg(FALSE)`.] (rust-lang/rust#102944) - [Unreserve braced enum variants in value namespace.] (rust-lang/rust#103578) Compiler -------- - [Enable varargs support for calling conventions other than `C` or `cdecl`.] (rust-lang/rust#97971) - [Add new MIR constant propagation based on dataflow analysis.] (rust-lang/rust#101168) - [Optimize field ordering by grouping m\*2^n-sized fields with equivalently aligned ones.] (rust-lang/rust#102750) - [Stabilize native library modifier `verbatim`.] (rust-lang/rust#104360) Added and removed targets: - [Add a tier 3 target for PowerPC on AIX] (rust-lang/rust#102293), `powerpc64-ibm-aix`. - [Add a tier 3 target for the Sony PlayStation 1] (rust-lang/rust#102689), `mipsel-sony-psx`. - [Add tier 3 `no_std` targets for the QNX Neutrino RTOS] (rust-lang/rust#102701), `aarch64-unknown-nto-qnx710` and `x86_64-pc-nto-qnx710`. - [Remove tier 3 `linuxkernel` targets] (rust-lang/rust#104015) (not used by the actual kernel). Refer to Rust's [platform support page][platform-support-doc] for more information on Rust's tiered platform support. Libraries --------- - [Merge `crossbeam-channel` into `std::sync::mpsc`.] (rust-lang/rust#93563) - [Fix inconsistent rounding of 0.5 when formatted to 0 decimal places.] (rust-lang/rust#102935) - [Derive `Eq` and `Hash` for `ControlFlow`.] (rust-lang/rust#103084) - [Don't build `compiler_builtins` with `-C panic=abort`.] (rust-lang/rust#103786) Stabilized APIs --------------- - [`{integer}::checked_ilog`] (https://doc.rust-lang.org/stable/std/primitive.i32.html#method.checked_ilog) - [`{integer}::checked_ilog2`] (https://doc.rust-lang.org/stable/std/primitive.i32.html#method.checked_ilog2) - [`{integer}::checked_ilog10`] (https://doc.rust-lang.org/stable/std/primitive.i32.html#method.checked_ilog10) - [`{integer}::ilog`] (https://doc.rust-lang.org/stable/std/primitive.i32.html#method.ilog) - [`{integer}::ilog2`] (https://doc.rust-lang.org/stable/std/primitive.i32.html#method.ilog2) - [`{integer}::ilog10`] (https://doc.rust-lang.org/stable/std/primitive.i32.html#method.ilog10) - [`NonZeroU*::ilog2`] (https://doc.rust-lang.org/stable/std/num/struct.NonZeroU32.html#method.ilog2) - [`NonZeroU*::ilog10`] (https://doc.rust-lang.org/stable/std/num/struct.NonZeroU32.html#method.ilog10) - [`NonZero*::BITS`] (https://doc.rust-lang.org/stable/std/num/struct.NonZeroU32.html#associatedconstant.BITS) These APIs are now stable in const contexts: - [`char::from_u32`] (https://doc.rust-lang.org/stable/std/primitive.char.html#method.from_u32) - [`char::from_digit`] (https://doc.rust-lang.org/stable/std/primitive.char.html#method.from_digit) - [`char::to_digit`] (https://doc.rust-lang.org/stable/std/primitive.char.html#method.to_digit) - [`core::char::from_u32`] (https://doc.rust-lang.org/stable/core/char/fn.from_u32.html) - [`core::char::from_digit`] (https://doc.rust-lang.org/stable/core/char/fn.from_digit.html) Compatibility Notes ------------------- - [The layout of `repr(Rust)` types now groups m\*2^n-sized fields with equivalently aligned ones.] (rust-lang/rust#102750) This is intended to be an optimization, but it is also known to increase type sizes in a few cases for the placement of enum tags. As a reminder, the layout of `repr(Rust)` types is an implementation detail, subject to change. - [0.5 now rounds to 0 when formatted to 0 decimal places.] (rust-lang/rust#102935) This makes it consistent with the rest of floating point formatting that rounds ties toward even digits. - [Chains of `&&` and `||` will now drop temporaries from their sub-expressions in evaluation order, left-to-right.] (rust-lang/rust#103293) Previously, it was "twisted" such that the _first_ expression dropped its temporaries _last_, after all of the other expressions dropped in order. - [Underscore suffixes on string literals are now a hard error.] (rust-lang/rust#103914) This has been a future-compatibility warning since 1.20.0. - [Stop passing `-export-dynamic` to `wasm-ld`.] (rust-lang/rust#105405) - [`main` is now mangled as `__main_void` on `wasm32-wasi`.] (rust-lang/rust#105468) - [Cargo now emits an error if there are multiple registries in the configuration with the same index URL.] (rust-lang/cargo#10592) Internal Changes ---------------- These changes do not affect any public interfaces of Rust, but they represent significant improvements to the performance or internals of rustc and related tools. - [Rewrite LLVM's archive writer in Rust.] (rust-lang/rust#97485)
Pkgsrc changes: * Adjust patches (add & remove) and cargo checksums to new versions. * It's conceivable that the workaround for LLVM based NetBSD works even less in this version (ref. PKGSRC_HAVE_LIBCPP not having a corresponding patch anymore). Upstream changes: Version 1.68.2 (2023-03-28) =========================== - [Update the GitHub RSA host key bundled within Cargo] (rust-lang/cargo#11883). The key was [rotated by GitHub] (https://github.blog/2023-03-23-we-updated-our-rsa-ssh-host-key/) on 2023-03-24 after the old one leaked. - [Mark the old GitHub RSA host key as revoked] (rust-lang/cargo#11889). This will prevent Cargo from accepting the leaked key even when trusted by the system. - [Add support for `@revoked` and a better error message for `@cert-authority` in Cargo's SSH host key verification] (rust-lang/cargo#11635) Version 1.68.1 (2023-03-23) =========================== - [Fix miscompilation in produced Windows MSVC artifacts] (rust-lang/rust#109094) This was introduced by enabling ThinLTO for the distributed rustc which led to miscompilations in the resulting binary. Currently this is believed to be limited to the -Zdylib-lto flag used for rustc compilation, rather than a general bug in ThinLTO, so only rustc artifacts should be affected. - [Fix --enable-local-rust builds] (rust-lang/rust#109111) - [Treat `$prefix-clang` as `clang` in linker detection code] (rust-lang/rust#109156) - [Fix panic in compiler code] (rust-lang/rust#108162) Version 1.68.0 (2023-03-09) =========================== Language -------- - [Stabilize default_alloc_error_handler] (rust-lang/rust#102318) This allows usage of `alloc` on stable without requiring the definition of a handler for allocation failure. Defining custom handlers is still unstable. - [Stabilize `efiapi` calling convention.] (rust-lang/rust#105795) - [Remove implicit promotion for types with drop glue] (rust-lang/rust#105085) Compiler -------- - [Change `bindings_with_variant_name` to deny-by-default] (rust-lang/rust#104154) - [Allow .. to be parsed as let initializer] (rust-lang/rust#105701) - [Add `armv7-sony-vita-newlibeabihf` as a tier 3 target] (rust-lang/rust#105712) - [Always check alignment during compile-time const evaluation] (rust-lang/rust#104616) - [Disable "split dwarf inlining" by default.] (rust-lang/rust#106709) - [Add vendor to Fuchsia's target triple] (rust-lang/rust#106429) - [Enable sanitizers for s390x-linux] (rust-lang/rust#107127) Libraries --------- - [Loosen the bound on the Debug implementation of Weak.] (rust-lang/rust#90291) - [Make `std::task::Context` !Send and !Sync] (rust-lang/rust#95985) - [PhantomData layout guarantees] (rust-lang/rust#104081) - [Don't derive Debug for `OnceWith` & `RepeatWith`] (rust-lang/rust#104163) - [Implement DerefMut for PathBuf] (rust-lang/rust#105018) - [Add O(1) `Vec -> VecDeque` conversion guarantee] (rust-lang/rust#105128) - [Leak amplification for peek_mut() to ensure BinaryHeap's invariant is always met] (rust-lang/rust#105851) Stabilized APIs --------------- - [`{core,std}::pin::pin!`] (https://doc.rust-lang.org/stable/std/pin/macro.pin.html) - [`impl From<bool> for {f32,f64}`] (https://doc.rust-lang.org/stable/std/primitive.f32.html#impl-From%3Cbool%3E-for-f32) - [`std::path::MAIN_SEPARATOR_STR`] (https://doc.rust-lang.org/stable/std/path/constant.MAIN_SEPARATOR_STR.html) - [`impl DerefMut for PathBuf`] (https://doc.rust-lang.org/stable/std/path/struct.PathBuf.html#impl-DerefMut-for-PathBuf) These APIs are now stable in const contexts: - [`VecDeque::new`] (https://doc.rust-lang.org/stable/std/collections/struct.VecDeque.html#method.new) Cargo ----- - [Stabilize sparse registry support for crates.io] (rust-lang/cargo#11224) - [`cargo build --verbose` tells you more about why it recompiles.] (rust-lang/cargo#11407) - [Show progress of crates.io index update even `net.git-fetch-with-cli` option enabled] (rust-lang/cargo#11579) Misc ---- Compatibility Notes ------------------- - [Add `SEMICOLON_IN_EXPRESSIONS_FROM_MACROS` to future-incompat report] (rust-lang/rust#103418) - [Only specify `--target` by default for `-Zgcc-ld=lld` on wasm] (rust-lang/rust#101792) - [Bump `IMPLIED_BOUNDS_ENTAILMENT` to Deny + ReportNow] (rust-lang/rust#106465) - [`std::task::Context` no longer implements Send and Sync] (rust-lang/rust#95985) nternal Changes ---------------- These changes do not affect any public interfaces of Rust, but they represent significant improvements to the performance or internals of rustc and related tools. - [Encode spans relative to the enclosing item] (rust-lang/rust#84762) - [Don't normalize in AstConv] (rust-lang/rust#101947) - [Find the right lower bound region in the scenario of partial order relations] (rust-lang/rust#104765) - [Fix impl block in const expr] (rust-lang/rust#104889) - [Check ADT fields for copy implementations considering regions] (rust-lang/rust#105102) - [rustdoc: simplify JS search routine by not messing with lev distance] (rust-lang/rust#105796) - [Enable ThinLTO for rustc on `x86_64-pc-windows-msvc`] (rust-lang/rust#103591) - [Enable ThinLTO for rustc on `x86_64-apple-darwin`] (rust-lang/rust#103647) Version 1.67.0 (2023-01-26) ========================== Language -------- - [Make `Sized` predicates coinductive, allowing cycles.] (rust-lang/rust#100386) - [`#[must_use]` annotations on `async fn` also affect the `Future::Output`.] (rust-lang/rust#100633) - [Elaborate supertrait obligations when deducing closure signatures.] (rust-lang/rust#101834) - [Invalid literals are no longer an error under `cfg(FALSE)`.] (rust-lang/rust#102944) - [Unreserve braced enum variants in value namespace.] (rust-lang/rust#103578) Compiler -------- - [Enable varargs support for calling conventions other than `C` or `cdecl`.] (rust-lang/rust#97971) - [Add new MIR constant propagation based on dataflow analysis.] (rust-lang/rust#101168) - [Optimize field ordering by grouping m\*2^n-sized fields with equivalently aligned ones.] (rust-lang/rust#102750) - [Stabilize native library modifier `verbatim`.] (rust-lang/rust#104360) Added and removed targets: - [Add a tier 3 target for PowerPC on AIX] (rust-lang/rust#102293), `powerpc64-ibm-aix`. - [Add a tier 3 target for the Sony PlayStation 1] (rust-lang/rust#102689), `mipsel-sony-psx`. - [Add tier 3 `no_std` targets for the QNX Neutrino RTOS] (rust-lang/rust#102701), `aarch64-unknown-nto-qnx710` and `x86_64-pc-nto-qnx710`. - [Remove tier 3 `linuxkernel` targets] (rust-lang/rust#104015) (not used by the actual kernel). Refer to Rust's [platform support page][platform-support-doc] for more information on Rust's tiered platform support. Libraries --------- - [Merge `crossbeam-channel` into `std::sync::mpsc`.] (rust-lang/rust#93563) - [Fix inconsistent rounding of 0.5 when formatted to 0 decimal places.] (rust-lang/rust#102935) - [Derive `Eq` and `Hash` for `ControlFlow`.] (rust-lang/rust#103084) - [Don't build `compiler_builtins` with `-C panic=abort`.] (rust-lang/rust#103786) Stabilized APIs --------------- - [`{integer}::checked_ilog`] (https://doc.rust-lang.org/stable/std/primitive.i32.html#method.checked_ilog) - [`{integer}::checked_ilog2`] (https://doc.rust-lang.org/stable/std/primitive.i32.html#method.checked_ilog2) - [`{integer}::checked_ilog10`] (https://doc.rust-lang.org/stable/std/primitive.i32.html#method.checked_ilog10) - [`{integer}::ilog`] (https://doc.rust-lang.org/stable/std/primitive.i32.html#method.ilog) - [`{integer}::ilog2`] (https://doc.rust-lang.org/stable/std/primitive.i32.html#method.ilog2) - [`{integer}::ilog10`] (https://doc.rust-lang.org/stable/std/primitive.i32.html#method.ilog10) - [`NonZeroU*::ilog2`] (https://doc.rust-lang.org/stable/std/num/struct.NonZeroU32.html#method.ilog2) - [`NonZeroU*::ilog10`] (https://doc.rust-lang.org/stable/std/num/struct.NonZeroU32.html#method.ilog10) - [`NonZero*::BITS`] (https://doc.rust-lang.org/stable/std/num/struct.NonZeroU32.html#associatedconstant.BITS) These APIs are now stable in const contexts: - [`char::from_u32`] (https://doc.rust-lang.org/stable/std/primitive.char.html#method.from_u32) - [`char::from_digit`] (https://doc.rust-lang.org/stable/std/primitive.char.html#method.from_digit) - [`char::to_digit`] (https://doc.rust-lang.org/stable/std/primitive.char.html#method.to_digit) - [`core::char::from_u32`] (https://doc.rust-lang.org/stable/core/char/fn.from_u32.html) - [`core::char::from_digit`] (https://doc.rust-lang.org/stable/core/char/fn.from_digit.html) Compatibility Notes ------------------- - [The layout of `repr(Rust)` types now groups m\*2^n-sized fields with equivalently aligned ones.] (rust-lang/rust#102750) This is intended to be an optimization, but it is also known to increase type sizes in a few cases for the placement of enum tags. As a reminder, the layout of `repr(Rust)` types is an implementation detail, subject to change. - [0.5 now rounds to 0 when formatted to 0 decimal places.] (rust-lang/rust#102935) This makes it consistent with the rest of floating point formatting that rounds ties toward even digits. - [Chains of `&&` and `||` will now drop temporaries from their sub-expressions in evaluation order, left-to-right.] (rust-lang/rust#103293) Previously, it was "twisted" such that the _first_ expression dropped its temporaries _last_, after all of the other expressions dropped in order. - [Underscore suffixes on string literals are now a hard error.] (rust-lang/rust#103914) This has been a future-compatibility warning since 1.20.0. - [Stop passing `-export-dynamic` to `wasm-ld`.] (rust-lang/rust#105405) - [`main` is now mangled as `__main_void` on `wasm32-wasi`.] (rust-lang/rust#105468) - [Cargo now emits an error if there are multiple registries in the configuration with the same index URL.] (rust-lang/cargo#10592) Internal Changes ---------------- These changes do not affect any public interfaces of Rust, but they represent significant improvements to the performance or internals of rustc and related tools. - [Rewrite LLVM's archive writer in Rust.] (rust-lang/rust#97485)
Previously a short circuiting binop chain (chain of && or ||s) would drop the temporaries created by the first element after all the other elements, and otherwise follow evaluation order. So
f(1).g() && f(2).g() && f(3).g() && f(4).g()
would drop the temporaries in the order2,3,4,1
. This made&&
and||
non-associative regarding drop order. In other words, adding ()'s to the expression would change drop order:f(1).g() && (f(2).g() && f(3).g()) && f(4).g()
for example would drop in the order3,2,4,1
.As, except for the bool result, there is no data returned by the sub-expressions of the short circuiting binops, we can safely discard of any temporaries created by the sub-expr. Previously, code was already putting the rhs's into terminating scopes, but missed it for the lhs's.
This commit addresses this "twist". We now also put the lhs into a terminating scope. The drop order of the above expressions becomes
1,2,3,4
.There might be code relying on the current order, and therefore I'd recommend doing a crater run to gauge the impact. I'd argue that such code is already quite wonky as it is one
foo() &&
addition away from breaking.For the impact, I don't expect any build failures, as the compiler gets strictly more tolerant: shortening the lifetime of temporaries only expands the list of programs the compiler accepts as valid. There might be runtime failures caused by this change however.Edit: both build and runtime failures are possible, e.g. see the example provided by dtolnay below. Edit2: the crater run has finished and results are that there is only one build failure which is easy to fix with a +/- 1 line diff.I've included a testcase that now compiles thanks to this patch.
The breakage is also limited to drop order relative to conditionals in the && chain: that is, in code like this:
we already drop the temporaries of
foo().hi()
before we reach "hi".I'd ideally have this PR merged before let chains are stabilized. If this PR is taking too long, I'd love to have a more restricted version of this change limited to
&&
's in let chains: the&&
's of such chains are quite special anyways as they acceptlet
bindings, in there the&&
is therefore more a part of the "if let chain" construct than a construct of its own.Fixes #103107
Status: waiting on this accepted FCP finishing.