-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
regression: "self" in writeln! drops too early #99684
Comments
Yeah this would be #96455; it's the "needs_late_drop" due to "custom signature for write_fmt" situation described in #96455 (comment). https://docs.rs/async-std/latest/async_std/io/prelude/trait.WriteExt.html#method.write_fmt fn write_fmt<'a>(&'a mut self, fmt: Arguments<'_>) -> WriteFmtFuture<'a, Self>
where
Self: Unpin; IMO this signature obviously doesn't fit the "contrived" characterization I made about custom write_fmt signatures in that comment, so I think it would be reasonable to revert to late drop for the writer argument of @rust-lang/libs-api |
Sadly I think that combination might be impossible to implement (from a brief attempt) and we'll end up forced to drop the format args of |
Some nonworking implementations: // Causes $dst to be moved, so you can only write to it once.
// The correct behavior requires an autoref on $dst.
macro_rules! write {
($dst:expr, $($arg:tt)*) => {
match $dst {
mut dst => {
let result = dst.write_fmt($crate::format_args!($($arg)*));
result
}
}
};
} // Does not work if $dst is declared `f: &mut W` where the reference is mutable
// but the binding is not.
macro_rules! write {
($dst:expr, $($arg:tt)*) => {
match &mut $dst {
dst => {
let result = dst.write_fmt($crate::format_args!($($arg)*));
result
}
}
};
} // Does not work if $arg references $dst such as `write!(w, "{}", w.foo)`, nor (less
// importantly) if $dst contains a return or break or continue or await.
macro_rules! write {
($dst:expr, $($arg:tt)*) => {
match |args: $crate::fmt::Arguments<'_>| $dst.write_fmt(args) {
mut write_fmt => {
let result = write_fmt($crate::format_args!($($arg)*));
result
}
}
};
} |
I put up a revert for now in #99689. |
Hmm, |
If autoref is all that's needed to make #![feature(adt_const_params)]
#![allow(incomplete_features)]
// Everything inside this module is perma-unstable.
pub mod autoref {
// See below how this is used as a const-generic parameter.
#[derive(Eq, PartialEq)]
pub struct UnstableMethodSeal;
pub trait AutoRef {
// Not the best name but trying to avoid conflicts as much as possible.
#[inline(always)]
fn __rustc_unstable_auto_ref_mut_helper<
// This is the clever bit: no stable method could have this parameter:
const _SEAL: UnstableMethodSeal,
>(&mut self) -> &mut Self { self }
}
impl<T: ?Sized> AutoRef for T {}
// Appropriate allow_internal_unstable attributes go here:
#[macro_export]
macro_rules! autoref_mut {
($x:expr) => {{
use $crate::autoref::AutoRef as _;
$x.__rustc_unstable_auto_ref_mut_helper::<{$crate::autoref::UnstableMethodSeal}>()
}}
}
}
// Tests:
fn assert_ref_mut<T: ?Sized>(_: &mut T) {}
pub fn by_value<T>(mut x: T, f: impl FnOnce() -> T) {
assert_ref_mut(autoref_mut!(x));
assert_ref_mut(autoref_mut!(f()));
}
pub fn by_ref<T: ?Sized>(r: &mut T, f: impl FnOnce(&()) -> &mut T) {
assert_ref_mut(autoref_mut!(r));
assert_ref_mut(autoref_mut!(f(&())));
} A type parameter could be used instead of a For completeness, I believe the macro_rules! write {
($dst:expr, $($arg:tt)*) => {
match autoref_mut!($dst) {
dst => {
let result = dst.write_fmt($crate::format_args!($($arg)*));
result
}
}
};
} |
@eddyb I tried coding up your suggestion (ddaa6e7b498e81ca93a667e7ebdbca677d048a50) but it failed write!(
writer,
"{}{} ",
writer.bold().paint(meta.target()),
writer.dimmed().paint(":")
)?; error[E0502]: cannot borrow `writer` as immutable because it is also borrowed as mutable
--> github.aaakk.us.kg-1ecc6299db9ec823/tracing-subscriber-0.3.3/src/fmt/format/mod.rs:925:17
|
925 | writer.bold().paint(meta.target()),
| ^^^^^^^^^^^^^ immutable borrow occurs here
|
::: library/core/src/lib.rs:268:13
|
268 | $x.__rustc_unstable_auto_ref_mut_helper::<{ $crate::autoref::UnstableMethodSeal }>()
| ------------------------------------------------------------------------------------ mutable borrow occurs here
|
::: library/core/src/macros/mod.rs:503:34
|
503 | let result = dst.write_fmt($crate::format_args!($($arg)*));
| --------- mutable borrow later used by call In the T-lang zulip I floated the idea of let mut myvec = Vec::new();
iterator.for_each(myvec.fn push); // for_each(|element| myvec.push(element)), but better Then: match $dst.fn write_fmt {
write_fmt => {
let result = write_fmt(format_args!($($arg)*));
result
}
} NLL would need to know that $dst is not mutably borrowed until If we ever end up doing qualified paths in method syntax ( You mentioned "just make |
@dtolnay If the language had `let autoref = ..., would that help? (We've talked about that in @rust-lang/lang.) |
Meanwhile, applying the revert seems fine as a fix for now. |
Yes Josh, I believe that could be defined in such a way as to work here. It'd be: #[macro_export]
macro_rules! write {
($dst:expr, $($arg:tt)*) => {
match $dst {
autoref dst => {
let result = dst.write_fmt($crate::format_args!($($arg)*));
result
}
}
};
} but it potentially gets weirder with borrowchecking than hypothetical write!(
writer,
"{}{} ",
writer.bold().paint(meta.target()),
writer.dimmed().paint(":")
)?; $dst can't turn into an exclusive reference "too early". |
Yeah, I almost suggested something like But that worst-case example is an example of "two-phase borrows" (aka 2Φ), so really you'd want: #[macro_export]
macro_rules! write {
($dst:expr, $($arg:tt)*) => {
match $dst {
k#autoref2Φ mut dst => {
let result = dst.write_fmt($crate::format_args!($($arg)*));
result
}
}
};
} cc @rust-lang/wg-nll (Personally I prefer if a low-level perma-unstable tool like this was explicit about the powers it tapped into, but maybe it's not that huge of a deal?) |
Revert `write!` and `writeln!` to late drop temporaries Closes (on master, but not on beta) rust-lang#99684 by reverting the `write!` and `writeln!` parts of rust-lang#96455. argument position | before<br>rust-lang#94868 | after<br>rust-lang#94868 | after<br>rust-lang#96455 | after<br>this PR | desired<br>(unimplementable) --- |:---:|:---:|:---:|:---:|:---: `write!($tmp, "…", …)` | **⸺late** | **⸺late** | *early⸺* | **⸺late** | **⸺late** `write!(…, "…", $tmp)` | **⸺late** | **⸺late** | *early⸺* | **⸺late** | *early⸺* `writeln!($tmp, "…", …)` | **⸺late** | **⸺late** | *early⸺* | **⸺late** | **⸺late** `writeln!(…, "…", $tmp)` | **⸺late** | **⸺late** | *early⸺* | **⸺late** | *early⸺* `print!("…", $tmp)` | **⸺late** | **⸺late** | *early⸺* | *early⸺* | *early⸺* `println!("…", $tmp)` | *early⸺* | **⸺late** | *early⸺* | *early⸺* | *early⸺* `eprint!("…", $tmp)` | **⸺late** | **⸺late** | *early⸺* | *early⸺* | *early⸺* `eprintln!("…", $tmp)` | *early⸺* | **⸺late**| *early⸺* | *early⸺* | *early⸺* `panic!("…", $tmp)` | *early⸺* | *early⸺* | *early⸺* | *early⸺* | *early⸺* "Late drop" refers to dropping temporaries at the nearest semicolon **outside** of the macro invocation. "Early drop" refers to dropping temporaries inside of the macro invocation.
Note that there was some more discussion about this over Zulip. My personal short-term solution, here, barring two-phased borrows, would be to have a helper extension method to take care of the necessary autoref, thence yielding the following key macro rule: // key macro rule
($dst:expr, $($arg:tt)*) => ({
use $crate::ByRef as _;
match $dst.__by_ref() {
dst => {
let result = dst.write_fmt($crate::format_args!($($arg)*));
result
}
}
}); See the post in question for more info. Retro-compatiblity with two-phased borrowSeems to be the remaining thorny point:
|
@danielhenrymantilla Isn't that just like my earlier suggestion in #99684 (comment), except without the protection from users defining e.g. inherent |
@eddyb yeah, totally1, sorry for the redundant suggestion 🙇, I somehow scrolled past your post.
Footnotes
|
In https://crater-reports.s3.amazonaws.com/beta-1.63-2/beta-2022-07-16/reg/cabot-0.7.1/log.txt, it looks like the
stderr()
temporary isn't living long enough with the changes after #96455. However, I've not actually bisected or investigated thoroughly to confirm this -- it seems likely that this is the expected breakage noted in the FCP comment here (#96455 (comment)), though.cc @dtolnay
The text was updated successfully, but these errors were encountered: